/* * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "mountoperation.h" #include // for _() #include #include #include #include "mountoperationpassworddialog_p.h" #include "mountoperationquestiondialog_p.h" #include "ui_mount-operation-password.h" namespace Fm { MountOperation::MountOperation(bool interactive, QWidget* parent): QObject(parent), op(g_mount_operation_new()), cancellable_(g_cancellable_new()), running(false), interactive_(interactive), eventLoop(nullptr), autoDestroy_(true) { g_signal_connect(op, "ask-password", G_CALLBACK(onAskPassword), this); g_signal_connect(op, "ask-question", G_CALLBACK(onAskQuestion), this); // g_signal_connect(op, "reply", G_CALLBACK(onReply), this); #if GLIB_CHECK_VERSION(2, 20, 0) g_signal_connect(op, "aborted", G_CALLBACK(onAbort), this); #endif #if GLIB_CHECK_VERSION(2, 22, 0) g_signal_connect(op, "show-processes", G_CALLBACK(onShowProcesses), this); #endif #if GLIB_CHECK_VERSION(2, 34, 0) g_signal_connect(op, "show-unmount-progress", G_CALLBACK(onShowUnmountProgress), this); #endif } MountOperation::~MountOperation() { qDebug("delete MountOperation"); if(cancellable_) { cancel(); g_object_unref(cancellable_); } if(eventLoop) { // if wait() is called to block the main loop, but the event loop is still running // NOTE: is this possible? eventLoop->exit(1); } if(op) { g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskPassword), this); g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskQuestion), this); #if GLIB_CHECK_VERSION(2, 20, 0) g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAbort), this); #endif #if GLIB_CHECK_VERSION(2, 22, 0) g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowProcesses), this); #endif #if GLIB_CHECK_VERSION(2, 34, 0) g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowUnmountProgress), this); #endif g_object_unref(op); } // qDebug("MountOperation deleted"); } void MountOperation::onAbort(GMountOperation* /*_op*/, MountOperation* /*pThis*/) { } void MountOperation::onAskPassword(GMountOperation* /*_op*/, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis) { qDebug("ask password"); MountOperationPasswordDialog dlg(pThis, flags); dlg.setMessage(QString::fromUtf8(message)); dlg.setDefaultUser(QString::fromUtf8(default_user)); dlg.setDefaultDomain(QString::fromUtf8(default_domain)); dlg.exec(); } void MountOperation::onAskQuestion(GMountOperation* /*_op*/, gchar* message, GStrv choices, MountOperation* pThis) { qDebug("ask question"); MountOperationQuestionDialog dialog(pThis, message, choices); dialog.exec(); } /* void MountOperation::onReply(GMountOperation* _op, GMountOperationResult result, MountOperation* pThis) { qDebug("reply"); } */ void MountOperation::onShowProcesses(GMountOperation* /*_op*/, gchar* /*message*/, GArray* /*processes*/, GStrv /*choices*/, MountOperation* /*pThis*/) { qDebug("show processes"); } void MountOperation::onShowUnmountProgress(GMountOperation* /*_op*/, gchar* /*message*/, gint64 /*time_left*/, gint64 /*bytes_left*/, MountOperation* /*pThis*/) { qDebug("show unmount progress"); } void MountOperation::onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { if(*pThis) { GError* error = nullptr; g_mount_eject_with_operation_finish(mount, res, &error); (*pThis)->handleFinish(error); } delete pThis; } void MountOperation::onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { if(*pThis) { GError* error = nullptr; g_volume_eject_with_operation_finish(volume, res, &error); (*pThis)->handleFinish(error); } delete pThis; } void MountOperation::onMountFileFinished(GFile* file, GAsyncResult* res, QPointer< MountOperation >* pThis) { if(*pThis) { GError* error = nullptr; g_file_mount_enclosing_volume_finish(file, res, &error); (*pThis)->handleFinish(error); } delete pThis; } void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { if(*pThis) { GError* error = nullptr; g_volume_mount_finish(volume, res, &error); (*pThis)->handleFinish(error); } delete pThis; } void MountOperation::onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { if(*pThis) { GError* error = nullptr; g_mount_unmount_with_operation_finish(mount, res, &error); (*pThis)->handleFinish(error); } delete pThis; } void MountOperation::handleFinish(GError* error) { qDebug("operation finished: %p", static_cast(error)); if(error) { bool showError = interactive_; if(error->domain == G_IO_ERROR) { if(error->code == G_IO_ERROR_FAILED) { // Generate a more human-readable error message instead of using a gvfs one. // The original error message is something like: // Error unmounting: umount exited with exit code 1: // helper failed with: umount: only root can unmount // UUID=18cbf00c-e65f-445a-bccc-11964bdea05d from /media/sda4 */ // Why they pass this back to us? This is not human-readable for the users at all. if(strstr(error->message, "only root can ")) { g_free(error->message); error->message = g_strdup(_("Only system administrators have the permission to do this.")); } } else if(error->code == G_IO_ERROR_FAILED_HANDLED) { showError = false; } } if(showError) { QMessageBox::critical(nullptr, QObject::tr("Error"), QString::fromUtf8(error->message)); } } Q_EMIT finished(error); if(eventLoop) { // if wait() is called to block the main loop eventLoop->exit(error != nullptr ? 1 : 0); eventLoop = nullptr; } if(error) { g_error_free(error); } // free ourself here!! if(autoDestroy_) { deleteLater(); } } void MountOperation::prepareUnmount(GMount* mount) { /* ensure that CWD is not on the mounted filesystem. */ char* cwd_str = g_get_current_dir(); GFile* cwd = g_file_new_for_path(cwd_str); GFile* root = g_mount_get_root(mount); g_free(cwd_str); /* FIXME: This cannot cover 100% cases since symlinks are not checked. * There may be other cases that cwd is actually under mount root * but checking prefix is not enough. We already did our best, though. */ if(g_file_has_prefix(cwd, root)) { g_chdir("/"); } g_object_unref(cwd); g_object_unref(root); } // block the operation used an internal QEventLoop and returns // only after the whole operation is finished. bool MountOperation::wait() { QEventLoop loop; eventLoop = &loop; int exitCode = loop.exec(); return exitCode == 0 ? true : false; } } // namespace Fm