/* $Id$ Copyright (C) 2007-2009 tooar This file is part of emelFM2. emelFM2 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 3, or (at your option) any later version. emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file plugins/optional/e2p_gvfs.c @brief virtual-file-system operations using gio library */ /** \page gvfs working with the gvfs virtual file system ToDo - describe how this happens Not implemented yet. */ /* TODO figure out the various ways to get a GFile copy flags properties for file-info dialog read/write block functions ? (streamed) read/write from/to local stream ? [de]monitoring items works for vdirs ? if so, implement it readlink func - immediate or ultimate more funcs use progress monitoring ? check-ability function functions iface, having regard to func arguments - incl conform retvals check sizes assumed by gvfs for some properties to be changed check links-traversal when checking for modifiable properties user-warnings here or upstream ? function to check and mount if needed all segments of a VPATH */ #include "emelfm2.h" #ifdef E2_VFS #include #include #include #include // #include // #include // #include // #include #include "e2_vfs.h" // #include "e2_fs.h" // #include "e2_utf8.h" // #include "e2_dialog.h" // #include "e2_option_tree.h" #include "e2_action.h" #include "e2_password_dialog.h" #include "e2_plugins.h" //data for mount operation "ask-password" cb dialog typedef struct _E2_GVFSDlgData { GtkWidget *dialog; GtkWidget *domainentry; GtkWidget *userentry; GtkWidget *anoncheck; GtkWidget *singleradio; GtkWidget *sessionradio; GtkWidget *retainradio; E2_PWDataRuntime *pwrt; GPasswordFlags flags; // DialogButtons choice; } E2_GVFSDlgData; //data for mount operation "get-choice" cb dialog typedef struct _E2_GVFSDlg2Data { GtkWidget *dialog; GtkWidget *combo; // GList *radiobuttons; CHECKME use radio instead of combo for choices < 4, say? // DialogButtons choice; } E2_GVFSDlg2Data; //attribute labels array enumerator, must conform to statbuf_attrib_text[] enum { E2V_STD_TYPE, //"std:type" E2V_STD_SIZE, //"std:size" E2V_STD_SYMLINK_TARGET, //"std:symlink_target" E2V_STD_MTIME, //time:modified" E2V_STD_MTIME_USEC, //"time:modified_usec" E2V_UNIX_DEVICE, //"unix:device" E2V_UNIX_INODE, //"unix:inode" E2V_UNIX_MODE, //"unix:mode" E2V_UNIX_NLINK, //"unix:nlink" E2V_UNIX_UID, //"unix:uid" E2V_UNIX_GID, //"unix:gid" E2V_UNIX_RDEV, //"unix:rdev" E2V_UNIX_BLOCK_SIZE, //"unix:block_size" E2V_UNIX_BLOCKS, //"unix:blocks" E2V_UNIX_ATIME, //"time:access" E2V_UNIX_ATIME_USEC, //"time:access_usec" E2V_UNIX_CTIME, //"time:changed" E2V_UNIX_CTIME_USEC, //"time:changed_usec" E2V_OWNER_USER, //"owner:user" E2V_OWNER_GROUP, //"owner:group" E2V_MAX_ATTRIBS } E2_GVFS_StatbufAttrib; static const gchar *statbuf_attrib_text [] = { G_FILE_ATTRIBUTE_STD_TYPE, //"std:type" G_FILE_ATTRIBUTE_STD_SIZE, //"std:size" G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET, //"std:symlink_target" G_FILE_ATTRIBUTE_TIME_MODIFIED, //"time:modified" G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, //"time:modified_usec" G_FILE_ATTRIBUTE_UNIX_DEVICE, //"unix:device" G_FILE_ATTRIBUTE_UNIX_INODE, //"unix:inode" G_FILE_ATTRIBUTE_UNIX_MODE, //"unix:mode" G_FILE_ATTRIBUTE_UNIX_NLINK, //"unix:nlink" G_FILE_ATTRIBUTE_UNIX_UID, //"unix:uid" G_FILE_ATTRIBUTE_UNIX_GID, //"unix:gid" G_FILE_ATTRIBUTE_UNIX_RDEV, //"unix:rdev" G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, //"unix:block_size" G_FILE_ATTRIBUTE_UNIX_BLOCKS, //"unix:blocks" G_FILE_ATTRIBUTE_TIME_ACCESS, //"time:access" G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, //"time:access_usec" G_FILE_ATTRIBUTE_TIME_CHANGED, //"time:changed" G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, //"time:changed_usec" G_FILE_ATTRIBUTE_OWNER_USER, //"owner:user" G_FILE_ATTRIBUTE_OWNER_GROUP //"owner:group" }; static E2_FsFuncs gvfsiface = {NULL}; static gboolean _e2_gvfs_chmod (VPATH *localpath, mode_t mode, E2_VFSMonitor *opdata); /*******************/ /**** utilities ****/ /*******************/ /** @brief check if @a localpath is mounted @param localpath pointer to data for the item @return TRUE if mounted */ static gboolean _e2_gvfs_check_mounted (VPATH *localpath) { //FIXME properly check native paths in mtab etc if (localpath->spacedata == NULL) return TRUE; //local place doesn't need mounting FIXME unless it's removable etc if (localpath->spacedata->mountstate == E2PLACE_MOUNTED) return TRUE; // if (localpath->spacedata->mountstate == E2PLACE_MOUNTING) // do something like tell the user to try again soon return FALSE; } //UNUSED Make this mount needed places, not just check /** @brief check if parent place of @a localpath, i.e. all path segments, are mounted @param localpath pointer to data for the item @return TRUE if mounted */ static gboolean _e2_gvfs_check_parent_mounted (VPATH *localpath) { gchar *checkpath = g_strdup (localpath->localpath); PlaceInfo *spacedata = localpath->spacedata->parentplace; //iterate up the path while (spacedata != NULL) { //FIXME loop logic if (e2_utils_get_parent_path (checkpath, TRUE)) { VPATH ddata; ddata.locapath = checkpath; ddata.spacedata = spacedata; if (!_e2_gvfs_check_mounted (&ddata)) { g_free (checkpath); return FALSE; } spacedata = spacedata->parentplace; } } g_free (checkpath); return TRUE; } /** @brief grab a vfs-handle for @a localpath @param localpath pointer to data for the item @return the handle, NULL after error */ static GFile *_e2_gvfs_get_file (VPATH *localpath) { if (!_e2_gvfs_check_mounted (localpath)) //CHECKME ancestors too ? { //ERROR MSG ? return NULL; } //construct stuff from localpath->spacedata GFile *file = g_file_new_for_path (VPCSTR(localpath)); /* from native filename: GFile *file = g_file_new_for_path (const char *path); from uri: GFile *file = g_file_new_for_uri (const char *uri); from commandline argument (absolute path, cwd relative path or uri): GFile *file = g_file_new_for_commandline_arg (const char *arg); from a filename/uri input entry (a utf8 string, filename, uri or iri): GFile *file = g_file_parse_name (const char *parse_name); */ if (file != NULL) { //CHECKME mount parent of file if needed } return file; } /** @brief get information, in accord with @a attributes and @a flags, about @a item @param item pointer to gvfs data for the item @param attributes string describing the desired attributes, NULL (= "*"), "*" or comma-separated series of members of attrib_text[] @param flags bitflags dictating how the data is to be retrieved @param cancellable a cancellable which may be invoked to terminate this process @param error pointer to store for any error data @return pointer to inforamtion data struct, or NULL upon error */ static GFileInfo *_e2_gvfs_get_info (GFile *item, gchar *attributes, GFileGetInfoFlags flags, GCancellable *cancellable, GError **error) { //E2_VFSTMP mounting ? error reporting if (item == NULL) { // set_errno (EINVAL); return NULL; } if (attributes == NULL) attributes = "*"; GError *localerr = NULL; if (error == NULL) error = &localerr; else *error = NULL; GFileInfo *info = g_file_get_info (item, attributes, flags, cancellable, error); //CHECK NULL returned after cancellation ? if (info == NULL) { printd (ERROR, "Error getting info: %s", (*error)->message); //WARN user if (localerr != NULL) g_error_free (localerr); return NULL; } return info; } /** @brief get size of @a localpath @param localpath @param cancellable a cancellable which may be invoked to terminate this process @param error pointer to store for any error data @return the size, -1 on error */ static size_t _e2_gvfs_get_size (VPATH *localpath, GCancellable *cancellable, GError **error) { GFile *item = _e2_gvfs_get_file (localpath); if (item == NULL) return (size_t) -1; gchar *attributes = statbuf_attrib_text [E2V_STD_SIZE]; GFileGetInfoFlags flags = G_FILE_GET_INFO_NOFOLLOW_SYMLINKS; GFileInfo *iteminfo = _e2_gvfs_get_info (item, attributes, flags, cancellable, error); if (iteminfo == NULL) { g_object_unref (item); return (size_t) -1; } size_t size = (size_t)g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_STD_SIZE]); g_object_unref (item); g_object_unref (iteminfo); return size; } /** @brief check access rights of @a localpath @param localpath string describing item to be interrogated @param statusflags combination of F_OK, R_OK, W_OK and/or X_OK @param nofollow_symlinks TRUE to behave like lstat(), FALSE to behave like stat() @param cancellable a cancellable which may be invoked to terminate this process @param error pointer to store for any error data @return TRUE if all tests are passed, FALSE after any test failure or error */ static gboolean _e2_gvfs_get_rights (VPATH *localpath, gint statusflags, gboolean nofollow_symlinks, GCancellable *cancellable, GError **error) { gchar *attributes = NULL; if (statusflags == F_OK) attributes = g_strdup (statbuf_attrib_text [E2V_STD_SIZE]); else { gchar *tmp; if (statusflags & R_OK) attributes = g_strdup (G_FILE_ATTRIBUTE_ACCESS_CAN_READ); if (statusflags & W_OK) { if (attributes == NULL) attributes = g_strdup (G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); else { tmp = attributes; attributes = g_strconcat (attributes, ",", G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, NULL); g_free (tmp); } } if (statusflags & X_OK) { if (attributes == NULL) attributes = g_strdup (G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); else { tmp = attributes; attributes = g_strconcat (attributes, ",", G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, NULL); g_free (tmp); } } } if (attributes == NULL) return FALSE; GFile *item = _e2_gvfs_get_file (localpath); if (item == NULL) { // set_errno (EINVAL); g_free (attributes); return FALSE; } //CHECKME correct flags ? GFileGetInfoFlags flags = 0; if (nofollow_symlinks) flags |= G_FILE_GET_INFO_NOFOLLOW_SYMLINKS; GFileInfo *iteminfo = _e2_gvfs_get_info (item, attributes, flags, cancellable, error); g_free (attributes); g_object_unref (item); if (iteminfo == NULL) return FALSE; gboolean retval = TRUE; if (retval && statusflags == F_OK) { if (!g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_STD_SIZE])) retval = FALSE; } if (statusflags & R_OK) { if (!g_file_info_get_attribute_boolean (iteminfo, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) retval = FALSE; } if (retval && statusflags & W_OK) { if (!g_file_info_get_attribute_boolean (iteminfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) retval = FALSE; } if (retval && statusflags & X_OK) { if (!g_file_info_get_attribute_boolean (iteminfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) retval = FALSE; } g_object_unref (iteminfo); return retval; } /** @brief populate @a buf with full or approximate stat data for @a localpath @param localpath string describing item to be interrogated @param buf pointer to struct stat to be filled @param nofollow_symlinks TRUE to behave like lstat(), FALSE to behave like stat() @param cancellable a cancellable which may be invoked to terminate this process @param error pointer to store for any error data @return TRUE if successful, FALSE after error, in which case errno will have been set */ //FIXME error setting static gboolean _e2_gvfs_fill_statbuf (VPATH *localpath, struct stat *buf, gboolean nofollow_symlinks, GCancellable *cancellable, GError **error) { GFile *item = _e2_gvfs_get_file (localpath); if (item == NULL) { // set_errno (EINVAL); return FALSE; } //CHECKME correct flags ? GFileGetInfoFlags flags = 0; if (nofollow_symlinks) flags |= G_FILE_GET_INFO_NOFOLLOW_SYMLINKS; // gchar *attributes = "*"; gchar *attributes = g_strconcat ( statbuf_attrib_text [E2V_UNIX_DEVICE], ",", statbuf_attrib_text [E2V_UNIX_RDEV], ",", statbuf_attrib_text [E2V_UNIX_INODE], ",", statbuf_attrib_text [E2V_UNIX_MODE], ",", statbuf_attrib_text [E2V_UNIX_NLINK], ",", statbuf_attrib_text [E2V_UNIX_UID], ",", statbuf_attrib_text [E2V_UNIX_GID], ",", statbuf_attrib_text [E2V_STD_SIZE], ",", statbuf_attrib_text [E2V_UNIX_BLOCK_SIZE], ",", statbuf_attrib_text [E2V_UNIX_BLOCKS], ",", statbuf_attrib_text [E2V_UNIX_ATIME], ",", statbuf_attrib_text [E2V_UNIX_ATIME_USEC], ",", statbuf_attrib_text [E2V_STD_MTIME], ",", statbuf_attrib_text [E2V_STD_MTIME_USEC], ",", statbuf_attrib_text [E2V_UNIX_CTIME], ",", statbuf_attrib_text [E2V_UNIX_CTIME_USEC], NULL); GFileInfo *iteminfo = _e2_gvfs_get_info (item, attributes, flags, cancellable, error); g_free (attributes); g_object_unref (item); if (iteminfo == NULL) return FALSE; gboolean success; GFileType type = g_file_info_get_file_type (iteminfo); switch (type) { case G_FILE_TYPE_REGULAR: case G_FILE_TYPE_DIRECTORY: case G_FILE_TYPE_SYMBOLIC_LINK: case G_FILE_TYPE_SHORTCUT: case G_FILE_TYPE_SPECIAL: success = TRUE; break; default: success = FALSE; break; } if (success) { memset (buf, 0, sizeof (struct stat)); /* gchar **attributes = g_file_info_list_attributes (iteminfo, NULL); gint i; for (i = 0; attributes[i] != NULL; i++) { gchar *value = g_file_info_get_attribute_as_string (iteminfo, attributes[i]); g_print (" %s: %s\n", attributes[i], value); g_free (value); } g_strfreev (attributes); std:size: 16323 std:mtime: 1175109403 std:mtime_usec: 0 std:flags: 0 std:access_rights: 6 std:access_rights_mask: 14 std:display_name: e2_fs_walk.c std:edit_name: e2_fs_walk.c std:content_type: text/x-csrc unix:device: 835 unix:inode: 599401 unix:mode: 33188 unix:nlink: 1 unix:uid: 501 unix:gid: 501 unix:rdev: 0 unix:block_size: 4096 unix:blocks: 32 unix:atime: 1177024612 unix:atime_usec: 0 unix:ctime: 1175109403 unix:ctime_usec: 0 */ buf->st_dev = (dev_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_DEVICE]); buf->__st_ino = buf->st_ino = (ino_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_INODE]); buf->st_mode = (mode_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_MODE]); buf->st_nlink = g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_NLINK]); buf->st_uid = (uid_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_UID]); buf->st_gid = (gid_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_GID]); buf->st_rdev = (dev_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_RDEV]); buf->st_size = g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_STD_SIZE]); buf->st_blksize = g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_BLOCK_SIZE]); buf->st_blocks = g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_BLOCKS]); //#ifdef _HAVE_STAT64_NSEC FIXME this check buf->st_atim.tv_sec = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_ATIME]); buf->st_atim.tv_nsec = (time_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_ATIME_USEC]) * 1000; buf->st_mtim.tv_sec = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_STD_MTIME]); buf->st_mtim.tv_nsec = (time_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_STD_MTIME_USEC]) * 1000; buf->st_ctim.tv_sec = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_CTIME]); buf->st_ctim.tv_nsec = (time_t) g_file_info_get_attribute_uint32 (iteminfo, statbuf_attrib_text [E2V_UNIX_CTIME_USEC]) * 1000; /*#else buf->st_atime = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_ATIME]); buf->st_mtime = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_STD_MTIME]); buf->st_ctime = (time_t) g_file_info_get_attribute_uint64 (iteminfo, statbuf_attrib_text [E2V_UNIX_CTIME]); #endif */ } g_object_unref (iteminfo); return success; } /** @brief determine whether @a localpath is a directory or a link to one @param localpath pointer to virtual path data for item to check @return TRUE if it is a directory */ static gboolean _e2_gvfs_is_dir (VPATH *localpath, GCancellable *cancellable, GError **error) { gboolean retval; GFile *item = _e2_gvfs_get_file (localpath); if (item != NULL) { GFileInfo *info = _e2_gvfs_get_info (item, statbuf_attrib_text [E2V_STD_TYPE], 0, cancellable, error); if (info != NULL) { retval = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY); g_object_unref (G_OBJECT (info)); } else retval = FALSE; g_object_unref (G_OBJECT (item)); } else retval = FALSE; return retval; } /***************/ /** callbacks **/ /***************/ /** @brief gvfs callback function to display task progress @param current_bytecount the no. of bytes processed sofar @param total_bytecount the totial no. of bytes @param opdata pointer to operation data struct @return */ static void _e2_gvfs_show_progress ( goffset current_bytecount, goffset total_bytecount, E2_VFSMonitor *opdata) { printd (DEBUG, "progress %"PRIu64"/%"PRIu64, current_bytecount, total_bytecount); g_mutex_lock (opdata->mutx); if (opdata->progress_dialog != NULL && opdata->progbar != NULL) { gdouble fraction = (gdouble) current_bytecount / total_bytecount; //deal with rounding errors if (fraction > 1.0) fraction = 1.0; //CHECKME BGL closed ? gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (opdata->progbar), fraction); if (opdata->progfmt != NULL) { guint percent = (guint) fraction * 100; gchar *progresstext = g_strdup_printf (opdata->progfmt, percent); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (opdata->progbar), progresstext); g_free (progresstext); } } g_mutex_unlock (opdata->mutx); } /* * @brief "toggled" signal callback for each of password-scope radio-buttons in get-data dialog @param togglebutton the object which was changed @param rt pointer to dialog data struct @return */ /*UNUSED static void _e2_gvfs_pwscope_toggled_cb (GtkToggleButton *togglebutton, E2_GVFSDlgData *rt) { if (gtk_toggle_button_get_active (togglebutton)) { if (togglebutton == GTK_TOGGLE_BUTTON (rt->sessionradio)) rt->pwscope = G_PASSWORD_SAVE_FOR_SESSION; else if (togglebutton == GTK_TOGGLE_BUTTON (rt->retainradio)) rt->pwscope = G_PASSWORD_SAVE_PERMANENTLY; else rt->pwscope = G_PASSWORD_SAVE_NEVER; } } */ /** @brief "toggled" signal callback for anonymous-user check-button in get-data dialog @param togglebutton the object which was changed @param rt pointer to dialog data struct @return */ static void _e2_gvfs_anonuser_toggled_cb (GtkToggleButton *togglebutton, E2_GVFSDlgData *rt) { gboolean state = gtk_toggle_button_get_active (togglebutton); gtk_widget_set_sensitive (rt.userentry, !state); } /** @brief "ask-password" signal callback to get login-data @param op mount operation object @param message a "header" message to display to the user @param default_user current user, utf-8? string @param default_domain current domain, utf-8? string @param flags indicators of which data is needed @param opdata pointer to operation data struct specified when callback was connected @return */ static void _e2_gvfs_get_mount_data_cb (GMountOperation *op, const gchar *message, const gchar *default_user, const gchar *default_domain, GPasswordFlags flags, E2_VFSMonitor *opdata) { //CHECKME encoding of string args ? GtkWidget *wid, *dialog_vbox, *hbox; E2_GVFSDlgData rt; rt.dialog = e2_dialog_create (GTK_STOCK_DIALOG_QUESTION, message, _("mount-operation details"), DUMMY_RESPONSE_CB, NULL); rt.flags = flags; dialog_vbox = #ifdef USE_GTK2_14 gtk_dialog_get_content_area (GTK_DIALOG (rt.dialog)); #else GTK_DIALOG (rt.dialog)->vbox; #endif wid = e2_widget_add_mid_label (vbox, _("Domain"), 0, FALSE, E2_PADDING_SMALL); //L, R padding rt.domainentry = e2_widget_add_entry (vbox, (gchar *)default_domain, TRUE, FALSE); if ((flags & G_PASSWORD_FLAGS_NEED_DOMAIN) == 0) { gtk_widget_set_sensitive (wid, FALSE); gtk_widget_set_sensitive (rt.domainentry, FALSE); } else { #ifdef USE_GTK2_12TIPS gtk_widget_set_tooltip_text ( #else e2_widget_set_tooltip (NULL, #endif rt.domainentry, "FIXME explain domain"); //FIXME explain domain } #ifdef E2_ASSISTED e2_widget_set_label_relations (wid, rt.domainentry); #endif wid = e2_widget_add_mid_label (vbox, _("User"), 0, FALSE, E2_PADDING_SMALL); //L, R padding if (flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) { hbox = gtk_hbox_new (FALSE, E2_PADDING); GTK_WIDGET_SHOW (hbox); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING); rt.anoncheck = e2_button_add_toggle (hbox, TRUE, TRUE, _("_Anonymous user"), NULL, FALSE, E2_PADDING_SMALL, (gpointer) _e2_gvfs_anonuser_toggled_cb, &rt); rt.userentry = e2_widget_add_entry (hbox, (gchar *)default_user, TRUE, FALSE); gtk_widget_set_sensitive (rt.userentry, FALSE;); } else { rt.userentry = e2_widget_add_entry (vbox, (gchar *)default_user, TRUE, FALSE); rt.anoncheck = NULL; } #ifdef E2_ASSISTED e2_widget_set_label_relations (wid, rt.userentry); #endif if ((flags & G_PASSWORD_FLAGS_NEED_USERNAME) == 0) { gtk_widget_set_sensitive (wid, FALSE); gtk_widget_set_sensitive (rt.userentry, FALSE); if (rt.anoncheck != NULL) gtk_widget_set_sensitive (rt.anoncheck, FALSE); } gchar *newpw = NULL; rt.pwrt = e2_password_dialog_setup (vbox, FALSE, /*FALSE,*/ _("Password"), &newpw); //CHECKME what about local cacheing of p/w ? if (flags & G_ASK_PASSWORD_SAVING_SUPPORTED) { hbox = gtk_hbox_new (FALSE, E2_PADDING); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, E2_PADDING); //to avoid premature "toggle" callbacks, we group the buttons after setup rt.singleradio = e2_button_add_radio (hbox, _("_Each-time password"), NULL, FALSE, TRUE, 0, NULL, NULL);//(gpointer) _e2_gvfs_pwscope_toggled_cb, &rt); rt.sessionradio = e2_button_add_radio (hbox, _("_Session password"), NULL, TRUE, TRUE, 0, NULL, NULL);//(gpointer) _e2_gvfs_pwscope_toggled_cb, &rt); rt.retainradio = e2_button_add_radio (hbox, _("_Remembered password"), NULL, FALSE, TRUE, 0, NULL, NULL);//(gpointer) _e2_gvfs_pwscope_toggled_cb, &rt); GSList *grp = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rt.singleradio)); gtk_radio_button_set_group (GTK_RADIO_BUTTON (rt.sessionradio), grp); grp = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rt.singleradio)); gtk_radio_button_set_group (GTK_RADIO_BUTTON (rt.retainradio), grp); GTK_WIDGET_SHOW_ALL (hbox); } else { rt.singleradio = NULL; rt.sessionradio = NULL; rt.retainradio = NULL; } if ((flags & G_PASSWORD_FLAGS_NEED_PASSWORD) == 0) { gtk_widget_set_sensitive (rt.pwrt->pwentry1, FALSE); if (rt.singleradio != NULL) { gtk_widget_set_sensitive (rt.singleradio, FALSE); gtk_widget_set_sensitive (rt.sessionradio, FALSE); gtk_widget_set_sensitive (rt.retainradio, FALSE); } } e2_dialog_show (rt.dialog, app.main_window, 0, &E2_BUTTON_CANCEL, &E2_BUTTON_APPLY, NULL); if (flags & G_PASSWORD_FLAGS_NEED_DOMAIN) { gtk_editable_select_region (GTK_EDITABLE (rt.domainentry), 0, -1); gtk_widget_grab_focus (rt.domainentry); } else if (flags & G_PASSWORD_FLAGS_NEED_USERNAME) { gtk_editable_select_region (GTK_EDITABLE (rt.userentry), 0, -1); gtk_widget_grab_focus (rt.userentry); } else if (flags & G_PASSWORD_FLAGS_NEED_PASSWORD) { gtk_widget_grab_focus (rt.pwrt->pwentry1); } DialogButtons choice; more: choice = e2_dialog_wait (rt.dialog, TRUE, FALSE, FALSE, TRUE); CHECKME TRUE maincontext if (choice == OK && (flags & G_PASSWORD_FLAGS_NEED_PASSWORD) && !e2_password_dialog_confirm (rt.pwrt)) goto more; //keep the dialog going GMountOperationResult retval; if (choice == OK) { const gchar *s; retval = G_MOUNT_OPERATION_HANDLED; if (flags & G_PASSWORD_FLAGS_NEED_USERNAME) { if (rt.anoncheck != NULL && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt.anoncheck))) { g_mount_operation_set_anonymous (op, TRUE); } else { s = gtk_entry_get_text (GTK_ENTRY (rt.userentry)); if (s == NULL || *s == '\0') retval = G_MOUNT_OPERATION_UNHANDLED; else { g_mount_operation_set_username (op, s); //encoding ? g_mount_operation_set_anonymous (op, FALSE); } } } if (flags & G_PASSWORD_FLAGS_NEED_DOMAIN) { s = gtk_entry_get_text (GTK_ENTRY (rt.domainentry)); if (s == NULL || *s == '\0') retval = G_MOUNT_OPERATION_UNHANDLED; else g_mount_operation_set_domain (op, s); //encoding ? } if (flags & G_PASSWORD_FLAGS_NEED_PASSWORD) { if (newpw == NULL || *newpw == '\0') retval = G_MOUNT_OPERATION_UNHANDLED; else { g_mount_operation_set_password (op, newpw); //can be NULL? //CHECKME local cacheing of p/w as well or instead ? if (flags & G_ASK_PASSWORD_SAVING_SUPPORTED) { GPasswordSave scope; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt.sessionradio))) scope = G_PASSWORD_SAVE_FOR_SESSION; else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt.retainradio))) scope = G_PASSWORD_SAVE_PERMANENTLY; else scope = G_PASSWORD_SAVE_NEVER; g_mount_operation_set_password_save (op, scope); } } } } else { //CHECKME cancel here ? if (opdata->c != NULL) g_cancellable_cancel (opdata->c); //CHECKME cleanup any timer here or elsewhere, without timer race if (opdata->timer_id > 0) { guint tmp = opdata->timer_id; opdata->timer_id = 0; g_source_remove (tmp); } //CHECKME cleanup here or elsewhere ? if (GTK_IS_WIDGET (opdata->slow_dialog)) { gtk_widget_destroy (opdata->slow_dialog); opdata->slow_dialog = NULL; } if (GTK_IS_WIDGET (opdata->progress_dialog)) { gtk_widget_destroy (opdata->progress_dialog); opdata->progress_dialog = NULL; } retval = G_MOUNT_OPERATION_ABORTED; } if (GTK_IS_WIDGET (rt.dialog)) gtk_widget_destroy (rt.dialog); if (flags & G_PASSWORD_FLAGS_NEED_PASSWORD) { e2_password_dialog_backup (rt.pwrt); //backup static stuff // DEALLOCATE (E2_PWDataRuntime, rt.pwrt); //the p/w data was allocated } //we always show password widgets, so always cleanup DEALLOCATE (E2_PWDataRuntime, rt.pwrt); //the p/w data was allocated if (newpw != NULL) g_free (newpw); g_mount_operation_reply (op, retval); } /** @brief "ask-question" signal callback to get mount-option @param op mount operation object @param message a "header" message to display to the user @param choices string-array of choices for the user @param opdata pointer to operation data structr specified when callback was connected @return */ static void _e2_gvfs_get_mount_choice_cb (GMountOperation *op, const gchar *message, GStrv *choices, E2_VFSMonitor *opdata) { //VFS_TMP //CHECKME message and choice-string(s) encoding GStrv *item; E2_GVFSDlg2Data rt; rt.dialog = e2_dialog_create (GTK_STOCK_DIALOG_QUESTION, message, _("mount-operation details"), DUMMY_RESPONSE_CB, NULL); GtkWidget *dialog_vbox = #ifdef USE_GTK2_14 gtk_dialog_get_content_area (GTK_DIALOG (rt.dialog)); #else GTK_DIALOG (rt.dialog)->vbox; #endif GList *history = NULL; for (item = choices; *item != NULL; item++) { if (**item != '\0') history = g_list_append (history, *item); } rt.combo = e2_combobox_add (vbox, FALSE, E2_PADDING, NULL, NULL, &history, E2_COMBOBOX_CYCLE_HISTORY); if (history != NULL) g_list_free (history); rt.choice = NO_TO_ALL; //default in case of abort //CHECKME do not set an active item ? gtk_combo_box_set_active (GTK_COMBO_BOX (rt.combo), 0); e2_dialog_show (rt.dialog, app.main_window, 0, &E2_BUTTON_CANCEL, &E2_BUTTON_APPLY, NULL); gtk_widget_grab_focus (rt.combo); DialogButtons choice = e2_dialog_wait (rt.dialog, TRUE, FALSE, TRUE); CHECKME TRUE maincontext GMountOperationResult retval; if (choice == OK) { gint value = gtk_combo_box_get_active (GTK_COMBO_BOX (rt.combo)); if (value != -1) { g_mount_operation_set_choice (op, value); retval = G_MOUNT_OPERATION_HANDLED; } else retval = G_MOUNT_OPERATION_UNHANDLED; } else { //CHECKME cancel here ? if (opdata->c != NULL) g_cancellable_cancel (opdata->c); //CHECKME cleanup any timer here or elsewhere, without timer race if (opdata->timer_id > 0) { guint tmp = opdata->timer_id; opdata->timer_id = 0; g_source_remove (tmp); } //CHECKME cleanup here or elsewhere ? if (GTK_IS_WIDGET (opdata->slow_dialog)) { gtk_widget_destroy (opdata->slow_dialog); opdata->slow_dialog = NULL; } if (GTK_IS_WIDGET (opdata->progress_dialog)) { gtk_widget_destroy (opdata->progress_dialog); opdata->progress_dialog = NULL; } retval = G_MOUNT_OPERATION_ABORTED; } if (GTK_IS_WIDGET (rt.dialog)) gtk_widget_destroy (rt.dialog); g_mount_operation_reply (op, retval); } /** @brief "reply" signal callback issued upon a g_mount_operation_reply() FIXME do relevant things when aborted @param op mount operation object @param result status enumerator returned by the prior 'request' callback @param opdata pointer to operation data structr specified when callback was connected @return */ static void _e2_gvfs_mount_reply_cb (GMountOperation *op, GMountOperationResult result, E2_VFSMonitor *opdata) { if (result != G_MOUNT_OPERATION_HANDLED) { //VFS_TMP /* //CHECKME cancel here ? if (opdata->c != NULL) g_cancellable_cancel (opdata->c); //CHECKME cleanup any timer here or elsewhere, without timer race if (opdata->timer_id > 0) { guint tmp = opdata->timer_id; opdata->timer_id = 0; g_source_remove (tmp); } //CHECKME cleanup here or elsewhere ? if (GTK_IS_WIDGET (opdata->slow_dialog)) { gtk_widget_destroy (opdata->slow_dialog); opdata->slow_dialog = NULL; } if (GTK_IS_WIDGET (opdata->progress_dialog)) { gtk_widget_destroy (opdata->progress_dialog); opdata->progress_dialog = NULL; } some warning to user ? */ } } /** @brief @param object the GFile that has been unmounted @param res @param opdata pointer to operation data struct @return */ static void _e2_gvfs_unmount_mountable_done_cb (GObject *object, GAsyncResult *res, E2_VFSMonitor *opdata) { GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //VFS_TMP //FIXME interrogate res before finishing if (!g_file_unmount_mountable_finish (G_FILE (object), res, opdata->error)) { printd (WARN, "Error unmounting location: %s", (*opdata->error)->message); gchar *name = g_file_get_parse_name (G_FILE (object)); gchar *msg = g_strdup_printf (_("Cannot unmount '%s' - %s"), name, (*opdata->error)->message); g_free (name); if (localerr != NULL) g_error_free (localerr); e2_output_print_error (msg, TRUE); } //CHECKME g_object_unref (object); } /** @brief completion callback for async mount-mountable operation @param object the GFile that has been mounted @param res @param opdata pointer to operation data struct @return */ static void _e2_gvfs_mount_mountable_done_cb (GObject *object, GAsyncResult *res, E2_VFSMonitor *opdata) { GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; GFile *target = g_file_mount_mountable_finish (G_FILE (object), res, opdata->error); if (target == NULL) { //FIXME some->spacedata->mountstate = E2PLACE_UNMOUNTED; printd (WARN, "Error mounting location: %s", (*opdata->error)->message); gchar *name = g_file_get_parse_name (G_FILE (object)); gchar *msg = g_strdup_printf (_("Cannot mount '%s' - %s"), name, (*opdata->error)->message); g_free (name); if (localerr != NULL) g_error_free (localerr); e2_output_print_error (msg, TRUE); } else { g_object_unref (G_OBJECT (target)); //FIXME some->spacedata->mountstate = E2PLACE_MOUNTED; } //CHECKME g_object_unref (object); } /** @brief completion callback for async mount-other (not mountable) operation @param object the GFile that has been mounted @param res @param opdata pointer to operation data struct @return */ static void _e2_gvfs_mount_done_cb (GObject *object, GAsyncResult *res, E2_VFSMonitor *opdata) { GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean succeeded = g_mount_for_location_finish (G_FILE (object), res, opdata->error); if (succeeded) { //FIXME some->spacedata->mountstate = E2PLACE_MOUNTED; } else { printd (WARN, "Error mounting location: %s", (*opdata->error)->message); //FIXME some->spacedata->mountstate = E2PLACE_UNMOUNTED; gchar *name = g_file_get_parse_name (G_FILE (object)); gchar *msg = g_strdup_printf (_("Cannot mount %s - %s"), name, (*opdata->error)->message); g_free (name); if (localerr != NULL) g_error_free (localerr); e2_output_print_error (msg, TRUE); } } /*************************/ /** interface functions **/ /*************************/ /** @brief mount place represented by @a localpath Any error message expects BGL open @param localpath pointer to virtual path, with localised string @param opdata pointer to operation data struct @return TRUE if completed successfully */ static gboolean _e2_gvfs_mount (VPATH *localpath, E2_VFSMonitor *opdata) { if (_e2_gvfs_check_mounted (localpath)) return TRUE; //nothing to do if (localpath->spacedata->mountstate == E2PLACE_MOUNTING) return TRUE; //don't do it again //E2_VFSTMP what does file path need to be GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; //determine file type, hence type of mount operation GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, //G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ? opdata->c, opdata->error); if (info == NULL) { //handle error [or cancellation ??] gchar *name = g_file_get_parse_name (file); gchar *msg = g_strdup_printf (_("Cannot mount %s - %s"), name, (*opdata->error)->message); g_free (name); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); if (localerr != NULL) g_error_free (localerr); g_object_unref (G_OBJECT (file)); return FALSE; } GMountOperation *op = g_mount_operation_new (); //FIXME if we can get from history-data any mount parameter(s), set them now //g_mount_operation_set_domain (op, const gchar *domain); //g_mount_operation_set_username (op, s); //encoding ? //g_mount_operation_set_anonymous (op, gboolean anonymous); //g_mount_operation_set_password (op, const gchar *password); //g_mount_operation_set_password_save (op, G_PASSWORD_SAVE_FOR_SESSION); g_signal_connect (G_OBJECT (op), "ask-password", G_CALLBACK (_e2_gvfs_get_mount_data_cb), opdata); g_signal_connect (G_OBJECT (op), "ask-question", G_CALLBACK (_e2_gvfs_get_mount_choice_cb), opdata); g_signal_connect (G_OBJECT (op), "reply", G_CALLBACK (_e2_gvfs_mount_reply_cb), opdata); localpath->spacedata->mountstate = E2PLACE_MOUNTING; //start async mount to suit the type of file //(G_FILE_TYPE_MOUNTABLE represents a mountable location) if (g_file_info_get_file_type (info) == G_FILE_TYPE_MOUNTABLE) g_file_mount_mountable (file, op, opdata->c, (GAsyncReadyCallback)_e2_gvfs_mount_mountable_done_cb, opdata); else g_mount_for_location (file, op, opdata->c, (GAsyncReadyCallback)_e2_gvfs_mount_done_cb, opdata); return TRUE; } /** @brief unmount place represented by @a spacedata Any error message expects BGL open @param spacedata pointer to data for place to unmount @return TRUE if completed successfully */ static gboolean _e2_gvfs_unmount (VPATH *localpath, E2_VFSMonitor *opdata) { if (localpath->spacedata->mountstate == E2PLACE_UNMOUNTED) return TRUE; //nothing to do //E2_VFSTMP what does file path need to be GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GMount *mount = ; if (g_mount_can_unmount (mount)) { //determine file type, hence type of mount operation GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, //G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ? opdata->c, opdata->error); if (info == NULL) { //handle error [or cancellation ??] gchar *name = g_file_get_parse_name (file); gchar *msg = g_strdup_printf (_("Cannot unmount %s - %s"), name, (*opdata->error)->message); g_free (name); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); if (localerr != NULL) g_error_free (localerr); g_object_unref (G_OBJECT (file)); return FALSE; } //start unmount to suit the type of mount if (g_file_info_get_file_type (info) == G_FILE_TYPE_MOUNTABLE) g_file_unmount_mountable (file, opdata->c, (GAsyncReadyCallback)_e2_gvfs_unmount_mountable_done_cb, opdata); else g_mount_unmount (mount, G_MOUNT_UNMOUNT_NONE, opdata->c, (GAsyncReadyCallback)_e2_gvfs_unmount_done_cb, opdata); return TRUE; } else { //FIXME warn user return FALSE; } } /** @brief like stat(), populate @a buf with full or approximate stat data for @a localpath @param localpath pointer to virtual path data for item to check @param statbuf pointer to struct stat to fill with data about @a localpath @param opdata pointer to operation parameters @return 0 if successful, -1 after error, in which case errno will have been set */ static gint _e2_gvfs_stat (VPATH *localpath, struct stat *statbuf, E2_VFSMonitor *opdata) { return ((_e2_gvfs_fill_statbuf (localpath, statbuf, FALSE, opdata->c, opdata->error)) ? 0 : -1); } /** @brief like lstat(), populate @a buf with full or approximate stat data for @a localpath, without traversing a link @param localpath pointer to virtual path data for item to check @param statbuf pointer to struct stat to fill with data about @a localpath @param opdata pointer to operation parameters @return 0 if successful, -1 after error, in which case errno will have been set */ static gint _e2_gvfs_lstat (VPATH *localpath, struct stat *statbuf, E2_VFSMonitor *opdata) { return ((_e2_gvfs_fill_statbuf (localpath, statbuf, TRUE, opdata->c, opdata->error)) ? 0 : -1); } /** @brief like access(), check whether @a localpath meets all conditions flagged in @a how Like access(), this func looks through links @param localpath pointer to virtual path data for item to check @param how bit flags, one or more of F_OK, R_OK, W_OK, X_OK @param opdata pointer to operation parameters @return 0 if all tests are satisfied, -1 if any fails */ static gint _e2_gvfs_access (VPATH *localpath, gint how, E2_VFSMonitor *opdata) { return ((_e2_gvfs_get_rights (localpath, how, TRUE, opdata->c, opdata->error)) ? 0 : -1); } /** @brief like access(), check whether @a localpath meets all conditions flagged in @a how This func does not look through links @param localpath pointer to virtual path data for item to check @param how bit flags, one or more of F_OK, R_OK, W_OK, X_OK @param opdata pointer to operation parameters @return 0 if all tests are satisfied, -1 if any fails */ static gint _e2_gvfs_laccess (VPATH *localpath, gint how, E2_VFSMonitor *opdata) { return ((_e2_gvfs_get_rights (localpath, how, FALSE, opdata->c, opdata->error)) ? 0 : -1); } //FIXME a function arg which sends the file into local workplace on disk /** @brief read file @a localpath into memory Note the file needs to be size-limited to what can be represented by a gulong, i.e. to ULONG_MAX. This is to allow allocation by g_..., to simplify later cleanups (not supposed to mix glib and glibc mamaory-management) Memory is allocated by g_malloc, not malloc, and needs to be cleared by g_free, not free Error messages expect BGL open/off @param localpath virtual path of file to be read, with localised string @param contents store for ptr start of the allocated contents @param contlength store for no. of bytes written, or NULL if not interested @param string TRUE to add a trailing 0 if that's missing @param opdata pointer to operation parameters @return TRUE if @a localpath was read successfully */ static gboolean _e2_gvfs_get_file_contents (VPATH *localpath, gpointer *contents, /*ssize_t*/ gulong *contlength, gboolean string, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; gchar *msg; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //CHECKME gvfs utility function // if (!g_file_load_contents (file, opdata->c, (gchar **)contents, // gsize *contlength, opdata->error) //CHECKME progress reporting ? GInputStream *in = (GInputStream *) g_file_read (file, opdata->c, opdata->error); if (in == NULL) { printd (WARN, "Error opening virtual file %s", VPSTR (localpath)); //CHECKME localpath encoding ? msg = g_strdup_printf (_("Cannot open file %s - %s"), VPCSTR (localpath), (*opdata->error)->message), gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); g_object_unref (file); if (localerr != NULL) g_error_free (localerr); return FALSE; } gssize bsize = 1024; gpointer buffer = g_try_malloc ((gulong) bsize); if (buffer == NULL) { gdk_threads_enter(); e2_utils_memory_error(); gdk_threads_leave(); g_object_unref (file); g_input_stream_close (in, NULL, opdata->error); if (localerr != NULL) g_error_free (localerr); return FALSE; } gboolean retval = TRUE; gpointer store = buffer; gssize bytes_read, total = 0; while (TRUE) { bytes_read = g_input_stream_read (in, store, bsize, opdata->c, opdata->error); if (bytes_read >= 0) { if (bytes_read == bsize) { //full buffer total += bsize; buffer = g_try_realloc (buffer, total + bsize); if (buffer == NULL) { gdk_threads_enter(); e2_utils_memory_error(); gdk_threads_leave(); total = 0; retval = FALSE; break; } store = buffer + total; } else { //empty or partial buffer = finished reading total += bytes_read; gchar *e = NULL; if (string) { //ensure a trailer e = (gchar *)store + bytes_read - sizeof (gchar); if (*e != '\0') { e++; *e = '\0'; total++; } else e = NULL; } if (bytes_read > 0 || e != NULL) { buffer = g_try_realloc (buffer, total); //trim the alloccation if (buffer == NULL) { gdk_threads_enter(); e2_utils_memory_error(); gdk_threads_leave(); total = 0; retval = FALSE; } } if (e != NULL) total--; break; } } else //<0 { printd (WARN, "Error reading streamed virtual file %s", localpath); //CHECKME localpath encoding ? msg = g_strdup_printf (_("Error reading file %s - %s"), VPSTR (localpath), (*opdata->error)->message), gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); //total unchanged, buffer size unchanged, no trailer handling if (localerr != NULL) g_error_free (localerr); retval = FALSE; break; } } if (!g_input_stream_close (in, opdata->c, opdata->error)) { printd (WARN, "Error closing virtual file %s", localpath); //CHECKME localpath encoding ? msg = g_strdup_printf (_("Cannot close file %s - %s"), VPSTR (localpath), (*opdata->error)->message), gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); //retval, total unchanged if (localerr != NULL) g_error_free (localerr); } if (contlength != NULL) *contlength = (gulong)total; g_object_unref (file); g_free (buffer); return retval; } //FIXME a function arg which gets from file on local disk /** @brief write whole file @a localpath, from memory starting at @a contents No overwrite checking is done @param localpath virtual path of file to be write, with localised string @param contents ptr to the start of the data to save @param contlength byte-length of data to save @param mode permissions to apply, or -1 for default @param opdata pointer to operation parameters @return TRUE if @a locapath was written successfully */ static gboolean _e2_gvfs_set_file_contents (VPATH *localpath, gpointer contents, size_t contlength, mode_t mode, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gchar *msg; GOutputStream *out; gboolean newfile; if (_e2_gvfs_access (localpath, F_OK, opdata)) { newfile = TRUE; out = (GOutputStream *) g_file_create (file, opdata->c, opdata->error); } else { newfile = FALSE; gchar *etag = NULL; //CHECK API doco out = (GOutputStream *) g_file_replace (file, etag, FALSE, opdata->c, opdata->error); //CHECKME gvfs utility function // if (!g_file_replace_contents (file, (gchar *)contents, (gsize)contlength, // etag, gboolean make_backup, opdata->c, opdata->error)) //CHECKME progress reporting ? } if (out == NULL) { printd (WARN, "Error opening virtual file %s", VPSTR(localpath)); //CHECKME localpath encoding ? e2_fs_error_local () ? msg = g_strdup_printf (_("Cannot open file %s - %s"), VPSTR (localpath), (*opdata->error)->message); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); g_object_unref (file); if (localerr != NULL) g_error_free (localerr); return FALSE; } gboolean retval = TRUE; ssize_t written; size_t rest = contlength; gpointer from = contents; while (rest > 0) { written = g_output_stream_write (out, from, rest, opdata->c, opdata->error); if (written < 0) { printd (WARN, "Error writing streamed virtual file %s", VPSTR (localpath)); //CHECKME localpath encoding ? e2_fs_error_local () ? msg = g_strdup_printf (_("Error writing file %s - %s"), VPSTR (localpath), (*opdata->error)->message); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); retval = FALSE; break; } rest -= written; from += written; } g_object_unref (file); if (!g_output_stream_close (out, opdata->c, opdata->error)) { printd (WARN, "Error closing virtual file %s", VPSTR (localpath)); //CHECKME localpath encoding ? msg = g_strdup_printf (_("Cannot close file %s - %s"), VPSTR (localpath), (*opdata->error)->message); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); //retval unchanged if (localerr != NULL) g_error_free (localerr); } if (newfile) _e2_gvfs_chmod (localpath, 0644, opdata); //FIXME do this mode better return retval; } /** @brief construct a list of all items in directory @a localpath @param localpath pointer to virtual path data for dir to read @param filterfunc function to call for each processed item, or NULL to append item's name to list @param cb_data pointer to data to send to @a callback @param free_data_func function for cleaning @a cb_data, or NULL @param opdata pointer to operation parameters @return list of itemnames, may be NULL, or a pointerised small positive error code */ static gpointer _e2_gvfs_dir_foreach (VPATH *localpath, gpointer filterfunc, gpointer cb_data, GDestroyNotify free_data_func, E2_VFSMonitor *opdata) { if (!_e2_gvfs_is_dir (localpath, opdata->c, opdata->error)) return FALSE; GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return GUINT_TO_POINTER (E2DREAD_DNR); GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; GFileEnumerator *enumerator = g_file_enumerate_children (file, NULL, 0, NULL, opdata->error); if (enumerator == NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); return GUINT_TO_POINTER (E2DREAD_NS); } GFileInfo *info; const gchar *itemname; GList *entries = NULL; gboolean (*processor) (VPATH *, const gchar *, GList **, gpointer) = filterfunc; while ((info = g_file_enumerator_next_file (enumerator, NULL, opdata->error)) != NULL) { itemname = g_file_info_get_name (info); if (itemname != NULL) { //CHECKME append "/" to dirs NO //CHECKME name encoding if (itemname[0] != '.' || itemname[1] != '\0') { if (processor != NULL) { if (!processor (localpath, itemname, &entries, cb_data)) break; } else entries = g_list_append (entries, g_strdup (itemname)); } } g_object_unref (info); } g_object_unref (file); if (free_data_func != NULL) free_data_func (cb_data); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); e2_list_free_with_data (&entries); return GUINT_TO_POINTER (E2DREAD_NS); } if (!g_file_enumerator_stop (enumerator, NULL, opdata->error)) { printd (DEBUG, "Error stopping enumerator: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); e2_list_free_with_data (&entries); return GUINT_TO_POINTER (E2DREAD_NS); } return ((gpointer )entries); } /** @brief make a virtual directory @a localpath @param localpath pointer to virtual path data for dir to make, with path as localised string @param mode permissions to give to the new directory @param opdata pointer to operation parameters @return TRUE if @a locapath was created successfully */ static gboolean _e2_gvfs_make_directory (VPATH *localpath, mode_t mode, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean retval = g_file_make_directory (file, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (file); return retval; } /** @brief copy item @a src to @a dest @param src pointer to virtual path data for item to copy, with path as localised string @param dest pointer to virtual path data for copied item, with path as localised string @param with_progress TRUE to show a progress-bar @param opdata pointer to operation parameters @return TRUE if @a src was copied successfully */ static gboolean _e2_gvfs_copy (VPATH *src, VPATH *dest, gboolean with_progress, E2_VFSMonitor *opdata) { GFile *sfile = _e2_gvfs_get_file (src); if (sfile == NULL) return FALSE; GFile *dfile = _e2_gvfs_get_file (dest); if (dfile == NULL) { g_object_unref (sfile); return FALSE; } // flags = 0; // if (backup) // flags |= G_FILE_COPY_BACKUP; // if (!interactive) // flags |= G_FILE_COPY_OVERWRITE; GFileCopyFlags flags = G_FILE_COPY_OVERWRITE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean retval = g_file_copy (sfile, dfile, flags, opdata->c, (with_progress) ? (GFileProgressCallback)_e2_gvfs_show_progress:NULL, opdata, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (sfile); g_object_unref (dfile); return retval; } /** @brief move item @a src to @a dest @param src pointer to virtual path data for item to move, with path as localised string @param dest pointer to virtual path data for moved item, with path as localised string @param with_progress TRUE to show a progress-bar @param opdata pointer to operation parameters @return TRUE if @a src was moved successfully */ static gboolean _e2_gvfs_move (VPATH *src, VPATH *dest, gboolean with_progress, E2_VFSMonitor *opdata) { GFile *sfile = _e2_gvfs_get_file (src); if (sfile == NULL) return FALSE; GFile *dfile = _e2_gvfs_get_file (dest); if (dfile == NULL) { g_object_unref (sfile); return FALSE; } GFileCopyFlags flags = G_FILE_COPY_OVERWRITE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean retval = g_file_move (sfile, dfile, flags, opdata->c, (with_progress) ? (GFileProgressCallback)_e2_gvfs_show_progress:NULL, opdata, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (sfile); g_object_unref (dfile); return retval; } /** @brief move item @a src to @a dest @param src pointer to virtual path data for item to move, with path as localised string @param dest pointer to virtual path data for moved item, with path as localised string @param with_progress TRUE to show a progress-bar @param opdata pointer to operation parameters @return TRUE if @a old was renamed successfully */ static gboolean _e2_gvfs_rename (VPATH *old, VPATH *new, E2_VFSMonitor *opdata) { if (old->spacedata != new->spacedata) //or just ignore new spacedata return FALSE; GFile *file = _e2_gvfs_get_file (old); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //E2_VFSTMP links ? GFileAttributeInfoList *atts = g_file_query_settable_attributes (file, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, old, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); g_object_unref (file); return FALSE; } gboolean retval; if (g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_STD_NAME) != NULL) //can rename { // retval = _e2_gvfs_move (old, new, FALSE, opdata); GFileAttributeValue newatt; newatt.type = G_FILE_ATTRIBUTE_TYPE_BYTE_STRING; newatt.u.string = VPSTR (new); retval = g_file_set_attribute (file, G_FILE_ATTRIBUTE_STD_NAME, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, old, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; g_object_unref (file); g_file_attribute_info_list_free (atts); return retval; } /** @brief create symlink to @a target, having name @a name @param target pointer to virtual path data for item to be the target, with path as localised string @param name pointer to virtual path data for the name of the link, with path as localised string @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_symlink (VPATH *target, VPATH *name, E2_VFSMonitor *opdata) { //matching space for target & name is checked upstream ? GFile *file = _e2_gvfs_get_file (target); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean retval = g_file_make_symbolic_link (file, VPCSTR(name), opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, target, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (file); return retval; } /** @brief determine the immediate or ultimate target of symlink to @a localpath @param localpath pointer to virtual path data for item to be processed, with path as localised string @param firtstarget TRUE to get immediate target, FALSE to resolve any subsequent paths as far as possible @param namebuf pointer to store for allocated target name string, whatever is discovered should be localised @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_readlink (VPATH *localpath, gboolean firsttarget, gchar **namebuf, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; *namebuf = NULL; gboolean retval = FALSE; //E2_VFSTMP if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (file); return retval; } /** @brief change permissions of @a localpath, or its target if it's a link @param localpath pointer to virtual path data for item to be processed, with path as localised string @param mode permissions to be set for @a localpath @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_chmod (VPATH *localpath, mode_t mode, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //E2_VFSTMP what about links - ignore them ? GFileAttributeInfoList *atts = g_file_query_settable_attributes (file, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); g_object_unref (file); return FALSE; } gboolean retval; if (g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_UNIX_MODE) != NULL) //can set mode { GFileAttributeValue newatt; //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT32; newatt.u.uint32 = (guint32) mode; //CHECK is this always really 32 bits retval = g_file_set_attribute (file, G_FILE_ATTRIBUTE_UNIX_MODE, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; g_object_unref (file); g_file_attribute_info_list_free (atts); return retval; } /** @brief change owners of @a localpath, or its target if it's a link @param localpath pointer to virtual path data for item to be processed, with path as localised string @param owner numerical value of new owner or -1 to ignore @param group numerical value of new group or -1 to ignore @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_chown (VPATH *localpath, uid_t owner, gid_t group, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //E2_VFSTMP make this traverse links GFileAttributeInfoList *atts = g_file_query_settable_attributes (file, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); g_object_unref (file); return FALSE; } GFileAttributeValue newatt; gboolean retval; if (owner != (uid_t) -1 && g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_UNIX_UID) != NULL) //can set uid { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT32; newatt.u.uint32 = (guint32) owner; //CHECK is this always really 32 bits retval = g_file_set_attribute (file, G_FILE_ATTRIBUTE_UNIX_UID, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; if (group != (gid_t) -1 && g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_UNIX_GID) != NULL) //can set gid { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT32; newatt.u.uint32 = (guint32) group; //CHECK is this always really 32 bits retval = retval && g_file_set_attribute (file, G_FILE_ATTRIBUTE_UNIX_GID, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); printd (DEBUG, "Error: %s", (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; g_object_unref (file); g_file_attribute_info_list_free (atts); return retval; } /** @brief change owners of @a localpath @param localpath pointer to virtual path data for item to be processed, with path as localised string @param owner numerical value of new owner or -1 to ignore @param group numerical value of new group or -1 to ignore @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_lchown (VPATH *localpath, uid_t owner, gid_t group, E2_VFSMonitor *opdata) //?? valid for vfs ? { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; //E2_VFSTMP make this not traverse links GFileAttributeInfoList *atts = g_file_query_settable_attributes (file, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); g_object_unref (file); return FALSE; } GFileAttributeValue newatt; gboolean retval; if (owner != (uid_t) -1 && g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_UNIX_UID) != NULL) //can set uid { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT32; newatt.u.uint32 = (guint32) owner; //CHECK is this always really 32 bits retval = g_file_set_attribute (file, G_FILE_ATTRIBUTE_UNIX_UID, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; if (group != (gid_t) -1 && g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_UNIX_GID) != NULL) //can set gid { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT32; newatt.u.uint32 = (guint32) group; //CHECK is this always really 32 bits retval = retval && g_file_set_attribute (file, G_FILE_ATTRIBUTE_UNIX_GID, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; g_object_unref (file); g_file_attribute_info_list_free (atts); return retval; } /** @brief change atime and/or mtime of @a localpath @param localpath pointer to virtual path data for item to be processed, with path as localised string @param tb pointer to times data struct, NULL to use current syatem time @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_touch (VPATH *localpath, const struct utimbuf *tb, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; GFileAttributeInfoList *atts = g_file_query_settable_attributes (file, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); g_object_unref (file); return FALSE; } struct utimbuf now; if (tb == NULL) { now.actime = now.modtime = time (NULL); tb = &now; } //E2_VFSTMP this is suppoesed to traverse links - does it ? gboolean retval; GFileAttributeValue newatt; if (g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_TIME_ACCESS) != NULL) //can set atime { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT64; newatt.u.uint64 = (guint64) tb->actime; //CHECK is this always really 64 bits retval = g_file_set_attribute (file, G_FILE_ATTRIBUTE_TIME_ACCESS, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; if (g_file_attribute_info_list_lookup (atts, G_FILE_ATTRIBUTE_TIME_MODIFIED) != NULL) //can set mtime { //E2_VFSTMP newatt.type = G_FILE_ATTRIBUTE_TYPE_UINT64; newatt.u.uint64 = (guint64) tb->modtime; //CHECK is this always really 64 bits retval = retval && g_file_set_attribute (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, &newatt, G_FILE_ATTRIBUTE_FLAGS_NONE, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } } else retval = FALSE; g_object_unref (file); g_file_attribute_info_list_free (atts); return retval; } /** @brief delete @a localpath @param localpath pointer to virtual path data for item to be processed, with path as localised string @param opdata pointer to operation parameters @return TRUE if the task was completed successfully */ static gboolean _e2_gvfs_delete (VPATH *localpath, E2_VFSMonitor *opdata) { GFile *file = _e2_gvfs_get_file (localpath); if (file == NULL) return FALSE; GError *localerr = NULL; if (opdata->error == NULL) opdata->error = &localerr; *opdata->error = NULL; gboolean retval = g_file_delete (file, opdata->c, opdata->error); if (*opdata->error != NULL) { printd (DEBUG, "Error: %s", (*opdata->error)->message); //WARN USER // e2_fs_error_local (fmt, localpath, (*opdata->error)->message); if (localerr != NULL) g_error_free (localerr); } g_object_unref (file); return retval; } /** @brief determine whether this plugin can handle an operation characterised by the protocol in @a spacedata and operation @a optype @param localpath pointer to virtual path data for item to be processed, with path as localised string @param opdata pointer to operation parameters @return TRUE if the task can be performed */ static gboolean _e2_gvfs_check_ability (PlaceInfo *spacedata, guint optype) //check if the library supports a nominated item-type/task combination { //E2_VFSTMP // g_vfs_get_supported_uri_schemes (GVfs *vfs); return TRUE; } /*static gboolean _e2_gvfs_precall_func (void) { //E2_VFSTMP return FALSE; } */ /*static gboolean _e2_gvfs_postcall_func (void) { //E2_VFSTMP return FALSE; } */ /** @brief plugin initialization function, called by main program @param p ptr to plugin data struct @return TRUE if the initialization succeeds, else FALSE */ gboolean init_plugin (Plugin *p) { #define ANAME "gvfs" p->signature = ANAME VERSION; p->menu_name = ""; p->description = _("Enable vfs functionality using gio/gvfs libraries"); p->icon = ""; if (p->action == NULL) { //there is no user-initated action for this plugin //do-nothing-action, in case the user puts plugin into menu gchar *action_name = g_strconcat(_A(1),".",_A(121),NULL); p->action = e2_plugins_action_register (action_name, E2_ACTION_TYPE_ITEM, e2_action_inaction, NULL, FALSE, 0, NULL); gvfsiface.mount = _e2_gvfs_mount; gvfsiface.unmount = _e2_gvfs_unmount; //gvfsiface.monitor = _e2_gvfs_monitor_item; NOT YET SUPPORTED //gvfsiface.demonitor = _e2_gvfs_demonitor_item; gvfsiface.stat = _e2_gvfs_stat; //looks through links gvfsiface.lstat = _e2_gvfs_lstat; //?? valid for vfs ? // gpointer //fstat; //?? valid for vfs ? gvfsiface.access = _e2_gvfs_access; //looks through links gvfsiface.laccess = _e2_gvfs_laccess; //no look through links // gvfsiface.open; // gpointer fopen; invalid without handles (local = open stream) // gvfsiface.close; // gpointer fclose; invalid without handles (local = close stream) gvfsiface.readfile = _e2_gvfs_get_file_contents; //aka get // gvfsiface.readblock; //get a buffer's worth (covers sniff) gvfsiface.writefile = _e2_gvfs_set_file_contents; //aka put // gvfsiface.writeblock = _e2_gvfs_; //write a buffer's worth // gvfsiface.writestring = _e2_gvfs_; //write string // gpointer chdir; //?? set the "default" dir in some other place // gvfsiface.opendir = _e2_gvfs_; // gvfsiface.closedir = _e2_gvfs_; gvfsiface.readdir = _e2_gvfs_dir_foreach; //enumerate dir contents into a list gvfsiface.mkdir = _e2_gvfs_make_directory; gvfsiface.copy = _e2_gvfs_copy; gvfsiface.move = _e2_gvfs_move; gvfsiface.rename = _e2_gvfs_rename; gvfsiface.symlink = _e2_gvfs_symlink; gvfsiface.readlink = _e2_gvfs_readlink; gvfsiface.chmod = _e2_gvfs_chmod; gvfsiface.chown = _e2_gvfs_chown; gvfsiface.lchown = _e2_gvfs_lchown; //?? valid for vfs ? gvfsiface.touch = _e2_gvfs_touch; gvfsiface.delete = _e2_gvfs_delete; //cover unlink, remove gvfsiface.check = _e2_gvfs_check_ability; //check if the library supports a nominated item-type/task combination //gvfsiface.prefunc = _e2_gvfs_precall_func; //gvfsiface.postfunc = _e2_gvfs_postcall_func; // do other init stuff vfuncs [E2_LIBGVFS] = &gvfsiface; //signal we're ready to roll return TRUE; } return FALSE; } /** @brief cleanup transient things for this plugin @param p pointer to data struct for the plugin @return TRUE if all cleanups were completed */ gboolean clean_plugin (Plugin *p) { vfuncs [E2_LIBGVFS] = NULL; gchar *action_name = g_strconcat (_A(1),".",_A(121),NULL); e2_plugins_action_unregister (action_name); g_free (action_name); //do other stuff return TRUE; } #endif //def E2_VFS