You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pcmanfm-qt-packaging/libfm-qt/mountoperation.cpp

228 lines
7.4 KiB

/*
Copyright (C) 2013 - 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mountoperation.h"
#include <glib/gi18n.h> // for _()
#include <QMessageBox>
#include <QPushButton>
#include <QEventLoop>
#include "mountoperationpassworddialog_p.h"
#include "mountoperationquestiondialog_p.h"
#include "ui_mount-operation-password.h"
namespace Fm {
MountOperation::MountOperation(bool interactive, QWidget* parent):
QObject(parent),
interactive_(interactive),
running(false),
op(g_mount_operation_new()),
cancellable_(g_cancellable_new()),
eventLoop(NULL),
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 = NULL;
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 = NULL;
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 = NULL;
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 = NULL;
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 = NULL;
g_mount_unmount_with_operation_finish(mount, res, &error);
(*pThis)->handleFinish(error);
}
delete pThis;
}
void MountOperation::handleFinish(GError* error) {
qDebug("operation finished: %p", 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(NULL, 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 != NULL ? 1 : 0);
eventLoop = NULL;
}
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