/* Screem: fileops.c, * This file provides file/directory copying/moving/deletion * * Copyright (C) 1999, 2000 David A Knight * * 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 * * For contact information with the author of this source code please see * the AUTHORS file. If there is no AUTHORS file present then check the * about box under the help menu for a contact address */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fileops.h" #include "support.h" #include "screem-errors.h" #include "screem-file.h" #include "screem-file-progress-dialog.h" /* not nice including these, we need access to the ScreemSession * so we can keep track of the file selector window size */ #include "screem-application.h" #include "screem-window.h" /* yuk, needed for our hack on GnomeFileEntry as we can't * provide the ScreemWindow in glade dialogs to get access to the app */ extern ScreemApplication *app; static gchar *screem_default_dir = NULL; gboolean copy_file( const gchar *source, const gchar *dest, GnomeVFSXferOverwriteMode ovr_mode, ScreemFileOp cb, gpointer data ) { GnomeVFSURI *suri; GnomeVFSURI *duri; GnomeVFSResult result; gboolean ret = FALSE; g_return_val_if_fail( source != NULL, FALSE ); g_return_val_if_fail( dest != NULL, FALSE ); if( ovr_mode == GNOME_VFS_XFER_OVERWRITE_MODE_QUERY ) { if( ! overwrite( dest ) ) { return FALSE; } } ovr_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE; suri = gnome_vfs_uri_new( source ); if( ! suri ) { return FALSE; } duri = gnome_vfs_uri_new( dest ); if( ! duri ) { gnome_vfs_uri_unref( suri ); return FALSE; } result = gnome_vfs_xfer_uri( suri, duri, GNOME_VFS_XFER_DEFAULT, GNOME_VFS_XFER_ERROR_MODE_ABORT, ovr_mode, NULL, NULL ); if( result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_EOF ) { ret = TRUE; if( cb ) { cb( GNOME_VFS_MONITOR_EVENT_CREATED, dest, data ); } } gnome_vfs_uri_unref( duri ); gnome_vfs_uri_unref( suri ); return ret; } gboolean copy_dir( const gchar *source, const gchar *dest, gboolean move, ScreemFileOp cb, gpointer data ) { gchar *orig; gchar *new_path; gboolean made; GnomeVFSFileInfo *vfsinfo; GnomeVFSFileInfoOptions options; GnomeVFSResult result; GnomeVFSDirectoryHandle *handle; GnomeVFSFilePermissions perms; made = FALSE; vfsinfo = gnome_vfs_file_info_new(); options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS; result = gnome_vfs_get_file_info( source, vfsinfo, options ); if( result != GNOME_VFS_OK ) { gnome_vfs_file_info_unref( vfsinfo ); return FALSE; } perms = vfsinfo->permissions; result = gnome_vfs_get_file_info( dest, vfsinfo, options ); if( result != GNOME_VFS_OK ) { if( ! mkdir_recursive( dest, perms, cb, data ) ) { gnome_vfs_file_info_unref( vfsinfo ); return FALSE; } made = TRUE; if( cb ) { cb( GNOME_VFS_MONITOR_EVENT_CREATED, dest, data ); } } if( ! made && vfsinfo->type != GNOME_VFS_FILE_TYPE_DIRECTORY ) { gnome_vfs_file_info_unref( vfsinfo ); return FALSE; } gnome_vfs_file_info_unref( vfsinfo ); handle = NULL; result = gnome_vfs_directory_open( &handle, source, options ); if( result != GNOME_VFS_OK ) { return FALSE; } vfsinfo = gnome_vfs_file_info_new(); while( result == GNOME_VFS_OK ) { result = gnome_vfs_directory_read_next( handle, vfsinfo ); if( ( result == GNOME_VFS_OK ) && strcmp( "..", vfsinfo->name ) && strcmp( ".", vfsinfo->name ) ) { orig = g_strconcat( source, "/", vfsinfo->name, NULL ); new_path = g_strconcat( dest, "/", vfsinfo->name, NULL ); if( vfsinfo->type != GNOME_VFS_FILE_TYPE_DIRECTORY ) { if( move ) { move_file( orig, new_path, cb, data ); } else { copy_file( orig, new_path, GNOME_VFS_XFER_OVERWRITE_MODE_QUERY, cb, data ); } } else { copy_dir( orig, new_path, move, cb, data ); } g_free( orig ); g_free( new_path ); } } if( handle ) { gnome_vfs_directory_close( handle ); } if( move ) { delete_dir( source, cb, data ); } return TRUE; } gboolean move_file( const gchar *source, const gchar *dest, ScreemFileOp cb, gpointer data ) { gboolean ret; if( ( ! overwrite( dest ) ) || ( gnome_vfs_move( source, dest, TRUE ) != GNOME_VFS_OK ) ) { ret = FALSE; } else { ret = TRUE; cb( GNOME_VFS_MONITOR_EVENT_CREATED, dest, data ); cb( GNOME_VFS_MONITOR_EVENT_DELETED, source, data ); } return ret; } gboolean delete_file( const gchar *file, ScreemFileOp cb, gpointer data ) { gboolean ret; GnomeVFSResult result; ret = FALSE; result = gnome_vfs_unlink( file ); ret = ( result == GNOME_VFS_OK ); if( ret && cb ) { cb( GNOME_VFS_MONITOR_EVENT_DELETED, file, data ); } return ret; } gboolean delete_dir( const gchar *path, ScreemFileOp cb, gpointer data ) { GnomeVFSDirectoryHandle *handle; GnomeVFSFileInfoOptions options; GnomeVFSResult result; GnomeVFSFileInfo *info; gboolean ret; options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK; result = gnome_vfs_directory_open( &handle, path, options ); if( result != GNOME_VFS_OK) { return delete_file( path, cb, data ); } info = gnome_vfs_file_info_new(); while( result == GNOME_VFS_OK ) { result = gnome_vfs_directory_read_next( handle, info ); if( ( result == GNOME_VFS_OK ) && strcmp( ".", info->name ) && strcmp( "..", info->name ) ) { gchar *new_path = g_strdup_printf( "%s%c%s", path, G_DIR_SEPARATOR, info->name ); delete_dir( new_path, cb, data ); g_free( new_path ); } } gnome_vfs_file_info_unref( info ); gnome_vfs_directory_close( handle ); ret = ( gnome_vfs_remove_directory( path ) == GNOME_VFS_OK ); if( ret ) { if( cb ) { cb( GNOME_VFS_MONITOR_EVENT_DELETED, path, data ); } } return ret; } /* recusivley makes directories (if needed), starting at current path */ gboolean mkdir_recursive( const gchar *path, GnomeVFSFilePermissions perms, ScreemFileOp cb, gpointer data ) { GnomeVFSURI *uri; gboolean ret = FALSE; gboolean exists; uri = gnome_vfs_uri_new( path ); if( uri ) { ret = TRUE; exists = gnome_vfs_uri_exists( uri ); if( ( ! exists ) && gnome_vfs_uri_has_parent( uri ) ) { GnomeVFSURI *parent; gchar *parentname; parent = gnome_vfs_uri_get_parent( uri ); parentname = gnome_vfs_uri_to_string( parent, GNOME_VFS_URI_HIDE_NONE ); gnome_vfs_uri_unref( parent ); ret = mkdir_recursive( parentname, perms, cb, data ); g_free( parentname ); } if( ret && ! exists ) { GnomeVFSResult result; result = gnome_vfs_make_directory_for_uri( uri, perms ); if( result == GNOME_VFS_ERROR_FILE_EXISTS ) { /* ensure it is a directory */ GnomeVFSFileInfo *info; GnomeVFSFileInfoOptions options; info = gnome_vfs_file_info_new(); options = GNOME_VFS_FILE_INFO_DEFAULT; if( gnome_vfs_get_file_info_uri( uri, info, options ) == GNOME_VFS_OK && info->type ==GNOME_VFS_FILE_TYPE_DIRECTORY) { result = GNOME_VFS_OK; } gnome_vfs_file_info_unref( info ); } ret = ( result == GNOME_VFS_OK ); } gnome_vfs_uri_unref( uri ); } if( ret ) { if( cb ) { cb( GNOME_VFS_MONITOR_EVENT_CREATED, path, data ); } } return ret; } /* converts the given path into one relative to the passed root */ gchar *relative_path( const gchar *text, const gchar *root ) { GnomeVFSURI *uri; GnomeVFSURI *base; gchar *ret; GSList *uri_parents = NULL; GSList *base_parents = NULL; GSList *up; GSList *bp; GnomeVFSURI *parent; GString *temp; g_return_val_if_fail( text != NULL, NULL ); if( ! root || *root == '\0' || ! g_strncasecmp( "mailto:", text, strlen( "mailto:" ) ) || ! g_strncasecmp( "javascript:", text, strlen( "javascript:" ) ) ) { return g_strdup( text ); } uri = gnome_vfs_uri_new( text ); base = gnome_vfs_uri_new( root ); if( strcmp( gnome_vfs_uri_get_scheme( uri ), gnome_vfs_uri_get_scheme( base ) ) ) { gnome_vfs_uri_unref( uri ); gnome_vfs_uri_unref( base ); return g_strdup( text ); } /* get list of uri parents */ uri_parents = g_slist_prepend( NULL, uri ); for( parent = uri; gnome_vfs_uri_has_parent( parent ); ) { parent = gnome_vfs_uri_get_parent( parent ); uri_parents = g_slist_prepend( uri_parents, parent ); } /* get list of base parents */ base_parents = g_slist_prepend( NULL, base ); for( parent = base; gnome_vfs_uri_has_parent( parent ); ) { parent = gnome_vfs_uri_get_parent( parent ); base_parents = g_slist_prepend( base_parents, parent ); } up = uri_parents; bp = base_parents; while( up && bp ) { gchar *uri1; gchar *uri2; gboolean match; uri1 = gnome_vfs_uri_extract_short_name( (GnomeVFSURI*)up->data ); uri2 = gnome_vfs_uri_extract_short_name( (GnomeVFSURI*)bp->data ); match = ( ! strcmp( uri1, uri2 ) ); g_free( uri1 ); g_free( uri2 ); if( match ) { up = up->next; bp = bp->next; } else { break; } } /* up and bp should now be at the point where they differ */ if( bp ) { gint parents; parents = g_slist_length( bp ); temp = g_string_sized_new( parents * strlen( "../" ) ); for( ; parents > 0; -- parents ) { g_string_append( temp, "../" ); } } else { temp = g_string_new( "./" ); } for( ; up; up = up->next ) { gchar *tmp = gnome_vfs_uri_extract_short_name( (GnomeVFSURI*)up->data ); g_string_append( temp, tmp ); if( up->next ) { g_string_append_c( temp, '/' ); } g_free( tmp ); } ret = temp->str; g_string_free( temp, FALSE ); for( up = uri_parents; up; up = up->next ) { gnome_vfs_uri_unref( (GnomeVFSURI*) up->data ); } for( bp = base_parents; bp; bp = bp->next ) { gnome_vfs_uri_unref( (GnomeVFSURI*) bp->data ); } if( uri_parents ) { g_slist_free( uri_parents ); } if( base_parents ) { g_slist_free( base_parents ); } return ret; } /* converts the given relative path to a full pathname, we should be in the path that it is relative from before calling the function */ gchar *relative_to_full( const gchar *relPath, const gchar *base_uri ) { gchar *base; gchar *path = NULL; gchar *cwd; /* if no base uri get current local dir */ cwd = NULL; if( ! base_uri ) { cwd = g_get_current_dir(); if( ! cwd ) { cwd = g_strdup( "" ); } base_uri = cwd; } /* ensure base uri ends in / */ if( base_uri[ strlen( base_uri ) -1 ] != G_DIR_SEPARATOR ) { base = g_strconcat( base_uri, "/", NULL ); } else { base = g_strdup( base_uri ); } path = gnome_vfs_uri_make_full_from_relative( base, relPath ); g_free( base ); if( cwd ) { g_free( cwd ); } return path; } gboolean uri_exists( const gchar *filename, GnomeVFSResult *result ) { GnomeVFSFileInfo *info; GnomeVFSURI *uri; gboolean ret; uri = gnome_vfs_uri_new( filename ); if( ! result ) { ret = gnome_vfs_uri_exists( uri ); } else { info = gnome_vfs_file_info_new(); *result = gnome_vfs_get_file_info_uri( uri, info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS ); /* does dest already exist? */ ret = ( *result == GNOME_VFS_OK ); gnome_vfs_file_info_unref( info ); } gnome_vfs_uri_unref( uri ); return ret; } gboolean uri_accessible( const gchar *filename, GnomeVFSFilePermissions perms ) { gboolean ret; GnomeVFSFileInfo *info; GnomeVFSResult res; ret = FALSE; info = gnome_vfs_file_info_new(); res = gnome_vfs_get_file_info( filename, info, GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS | GNOME_VFS_FILE_INFO_FOLLOW_LINKS ); if( res == GNOME_VFS_OK ) { ret = info->permissions & perms; } gnome_vfs_file_info_unref( info ); return ret; } gboolean overwrite( const gchar *filename ) { GnomeVFSResult result; gboolean ret; ret = uri_exists( filename, &result ); if( ! ret ) { ret = TRUE; } else if( result == GNOME_VFS_OK ) { GtkWidget *widget; GtkWidget *area; GtkWidget *button; GList *children; GtkWidget *img; gchar *temp; gchar *tmp; gchar *basename; temp = gnome_vfs_unescape_string_for_display( filename ); basename = g_path_get_basename( temp ); tmp = g_markup_escape_text( basename, -1 ); g_free( basename ); g_free( temp ); widget = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _( "A file named \"%s\" already exists." ), tmp ); g_free( tmp ); gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( widget ), _( "Do you want to replace it with the one you are saving" ) ); gtk_dialog_add_buttons( GTK_DIALOG( widget ), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, _( "_Replace existing file" ), GTK_RESPONSE_OK, NULL ); area = GTK_DIALOG( widget )->action_area; children = gtk_container_get_children( GTK_CONTAINER( area ) ); button = GTK_WIDGET( children->data ); img = gtk_image_new_from_stock( GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON ); gtk_button_set_image( GTK_BUTTON( button ), img ); gtk_window_set_wmclass( GTK_WINDOW( widget ), "Screem", "overwrite_prompt" ); gtk_window_set_type_hint( GTK_WINDOW( widget ), GDK_WINDOW_TYPE_HINT_DIALOG ); ret = ( gtk_dialog_run( GTK_DIALOG( widget ) ) == GTK_RESPONSE_OK ); gtk_widget_destroy( widget ); } else if( result != GNOME_VFS_ERROR_NOT_FOUND ) { /* FIXME: report error reason */ ret = FALSE; } return ret; } /* does path1 match path2, or is it a subdir going from base_uri, path2 must be absolute */ gchar* paths_match( const gchar *base_uri, const gchar *path1, const gchar *path2 ) { gchar *abs_path1; abs_path1 = relative_to_full( path1, base_uri ); if( strncmp( abs_path1, path2, strlen( path2 ) ) ) { g_free( abs_path1 ); abs_path1 = NULL; } return abs_path1; } gchar *load_file( const gchar *path, gboolean *compress, gchar **charset, GError **error ) { gchar *ret; ScreemFile *file; ScreemFileProgressDialog *dialog; gchar *temp; guint64 size; const gchar *mime; file = screem_file_new_with_uri( path ); dialog = screem_file_progress_dialog_new( file ); screem_file_load( file ); screem_file_progress_dialog_run( dialog ); if( compress ) { *compress = (screem_file_get_compression_type( file ) != SCREEM_FILE_UNCOMPRESSED ); } ret = (gchar*)screem_file_get_data( file ); if( ret ) { size = screem_file_get_size( file ); /* FIXME: this will spot utf-16, use the default * screem charset, or the default system charset, * it would be nice to look for a which * gives the charset info, but that only works for * html files */ mime = screem_file_get_mime_type( file ); if( mime && ! strcmp( mime, "text/html" ) ) { /* look for meta */ } temp = screem_support_charset_convert( ret, size, charset ); ret = temp; } else { ret = NULL; if( error ) { *error = screem_file_get_error( file ); } } g_object_unref( dialog ); g_object_unref( file ); return ret; } gboolean save_file( const gchar *path, const gchar *data, GnomeVFSFilePermissions perms, gboolean compress, gboolean create_backup, GError **error ) { GConfClient *client; GnomeVFSURI *uri; GnomeVFSResult result; GnomeVFSHandle *handle; GnomeVFSHandle *rhandle; GnomeVFSFileSize wsize; gint len; GnomeVFSFileInfo *orig; GnomeVFSSetFileInfoMask orig_mask; gchar *dir; gchar *base; GnomeVFSFileInfo *info; gboolean can_write_dir; gchar *backup_uri; const gchar *ext; gchar *tmp; gboolean is_tmp_dir; gboolean renamed; gboolean restore; GnomeVFSResult restore_res; gchar buffer[ 8192 ]; GnomeVFSFileSize vfssize; uid_t user; GnomeVFSSetFileInfoMask mask; gboolean ret; /* AND requested backup creation state with user preference */ client = gconf_client_get_default(); create_backup &= gconf_client_get_bool( client, "/apps/screem/general/create_backups", NULL ); g_object_unref( client ); ret = FALSE; if( ! data ) { data = ""; } if( error ) { *error = NULL; } info = orig = NULL; dir = base = backup_uri = NULL; is_tmp_dir = FALSE; uri = gnome_vfs_uri_new( path ); /* can't proceed with an invalid uri */ if( ! uri ) { ret = FALSE; result = GNOME_VFS_ERROR_INVALID_URI; goto out; } /* no need for backups if the file doesn't already exist, * not using uri_exists() here as we want the info as well */ orig = gnome_vfs_file_info_new(); result = gnome_vfs_get_file_info( path, orig, GNOME_VFS_FILE_INFO_FOLLOW_LINKS | GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS ); if( result == GNOME_VFS_OK ) { /* file already exists */ orig_mask = GNOME_VFS_SET_FILE_INFO_OWNER; if( orig->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS ) { orig_mask |= GNOME_VFS_SET_FILE_INFO_PERMISSIONS; } /* follow symlink, is this correct behaviour ? */ if( GNOME_VFS_FILE_INFO_SYMLINK( orig ) ) { path = orig->symlink_name; gnome_vfs_uri_unref( uri ); uri = gnome_vfs_uri_new( path ); if( ! uri ) { ret = FALSE; result = GNOME_VFS_ERROR_INVALID_URI; goto out; } } } else { gnome_vfs_file_info_unref( orig ); orig = NULL; create_backup = FALSE; } /* can we write to the directory? */ dir = gnome_vfs_uri_to_string( uri, GNOME_VFS_URI_HIDE_NONE ); tmp = strrchr( dir, '/' ); if( tmp ) { *tmp = '\0'; info = gnome_vfs_file_info_new(); result = gnome_vfs_get_file_info( dir, info, GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS ); } if( ( ! tmp ) || result != GNOME_VFS_OK ) { ret = FALSE; goto out; } can_write_dir =( ( info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_ACCESS ) && ( info->permissions & GNOME_VFS_PERM_ACCESS_WRITABLE ) ); /* can we set the necessary permissions? */ if( can_write_dir && orig ) { result = gnome_vfs_set_file_info( path, orig, orig_mask ); } ext = "~"; base = gnome_vfs_uri_extract_short_name( uri ); renamed = FALSE; if( orig && ( ( ! create_backup ) || ( ! can_write_dir ) ) ) { /* can't write to the directory, or we don't * want a backup to be kept, make a backup copy * in TMPDIR, this gets erased if we * successfully save.*/ backup_uri = screem_create_tmp_file( base, ext, NULL ); tmp = g_strconcat( base, "-XXXXXX", NULL ); backup_uri = screem_create_tmp_file( tmp, NULL, NULL ); g_free( tmp ); is_tmp_dir = TRUE; result = GNOME_VFS_OK; if( ! copy_file( path, backup_uri, GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, NULL, NULL ) ) { /* failed to create backup */ result = GNOME_VFS_ERROR_IO; } } else if( orig && create_backup ) { /* create backup by renaming path */ tmp = g_strconcat( base, ext, NULL ); backup_uri = g_build_filename( dir, tmp, NULL ); /* erase if it already exists */ result = gnome_vfs_unlink( backup_uri ); if( result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_NOT_FOUND ) { g_free( orig->name ); orig->name = tmp; result = gnome_vfs_set_file_info( path, orig, GNOME_VFS_SET_FILE_INFO_NAME ); /* restore orig base name */ g_free( tmp ); orig->name = g_strdup( base ); renamed = TRUE; perms = orig->permissions; } } if( result != GNOME_VFS_OK ) { /* failed to create backup */ ret = FALSE; g_set_error( error, g_quark_from_string( SCREEM_SAVE_BACKUP_ERR ), (gint)result, gnome_vfs_result_to_string( result ) ); goto out; } /* add gzip mode if necessary - NOTE gzip method doesn't support * file creation currently so this will cause create to fail */ if( compress ) { gnome_vfs_uri_append_string( uri, "#gzip" ); } len = strlen( data ); wsize = 0; /* if the file didn't exist, or we renamed it to create the * backup we need to create/recreate the file */ if( renamed || ! orig ) { result = gnome_vfs_create_uri( &handle, uri, GNOME_VFS_OPEN_WRITE, FALSE, perms ); } else { /* not creating backup, or backup was a copy, * open and truncate */ result = gnome_vfs_open_uri( &handle, uri, GNOME_VFS_OPEN_WRITE ); } restore = FALSE; if( result != GNOME_VFS_OK ) { ret = FALSE; } else { /* truncate to 0 */ while( len > 0 && result == GNOME_VFS_OK ) { restore = TRUE; result = gnome_vfs_write( handle, data, len, &vfssize ); len -= vfssize; data += vfssize; wsize += vfssize; } gnome_vfs_truncate_handle( handle, wsize ); gnome_vfs_close( handle ); ret = ( result == GNOME_VFS_OK ); } /* set original file permissions on new file */ if( ret ) { mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS | GNOME_VFS_SET_FILE_INFO_OWNER; if( renamed ) { /* set original perms */ gnome_vfs_set_file_info( path, orig, mask ); } else if( orig ) { user = orig->uid; /* set backup file perms correctly, * * owned by current user, group as original */ orig->uid = getuid(); if( gnome_vfs_set_file_info( backup_uri, orig, mask ) != GNOME_VFS_OK ) { /* failed to set perms, set group perms * to same as other perms */ perms = orig->permissions; orig->permissions = ( perms & ( GNOME_VFS_PERM_USER_ALL | GNOME_VFS_PERM_OTHER_ALL ) ) | ( ( perms & GNOME_VFS_PERM_OTHER_ALL ) << 3 ); mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS; gnome_vfs_set_file_info( backup_uri, orig, mask ); orig->permissions = perms; } orig->uid = user; } goto out; } if( ! orig || ! restore ) { goto out; } /* restore from backup */ restore_res = GNOME_VFS_OK; if( renamed ) { /* erase if it already exists */ restore_res = gnome_vfs_unlink( path ); if( restore_res == GNOME_VFS_OK ) { restore_res = gnome_vfs_set_file_info( backup_uri, orig, GNOME_VFS_SET_FILE_INFO_NAME ); } } else { /* truncate path, open backup_uri, write */ rhandle = NULL; handle = NULL; restore_res = gnome_vfs_open( &handle, path, GNOME_VFS_OPEN_WRITE ); if( restore_res == GNOME_VFS_OK ) { restore_res = gnome_vfs_truncate_handle( handle, 0 ); } if( restore_res == GNOME_VFS_OK ) { restore_res = gnome_vfs_open( &rhandle, backup_uri, GNOME_VFS_OPEN_READ ); } if( restore_res == GNOME_VFS_OK ) { while( restore_res == GNOME_VFS_OK ) { vfssize = 0; restore_res = gnome_vfs_read( rhandle, buffer, 8192, &vfssize ); if( restore_res == GNOME_VFS_OK || ( restore_res == GNOME_VFS_ERROR_EOF && vfssize > 0 ) ) { restore_res = gnome_vfs_write( handle, buffer, vfssize, &vfssize ); } } if( restore_res == GNOME_VFS_ERROR_EOF ) { restore_res = GNOME_VFS_OK; } } if( handle ) { gnome_vfs_close( handle ); } if( rhandle ) { gnome_vfs_close( rhandle ); } } if( restore_res != GNOME_VFS_OK && error && ! *error ) { /* failed to restore backup */ g_set_error( error, g_quark_from_string( SCREEM_RESTORE_BACKUP_ERR ), (gint)restore_res, "%s\nFailed to restore from backup file: %s\n\nBackup is at %s", gnome_vfs_result_to_string( result ), gnome_vfs_result_to_string( restore_res ), backup_uri); /* prevent erasing of backup file if it is in TMPDIR */ is_tmp_dir = FALSE; } out: if( ( ! ret ) && error && ! *error ) { g_set_error( error, g_quark_from_string( SCREEM_SAVE_FILE_ERR ), (gint)result, gnome_vfs_result_to_string( result ) ); } /* erase backups to TMPDIR after we are done */ if( is_tmp_dir ) { gnome_vfs_unlink( backup_uri ); } if( dir ) { g_free( dir ); } if( base ) { g_free( base ); } if( backup_uri ) { g_free( backup_uri ); } if( orig ) { gnome_vfs_file_info_unref( orig ); } if( info ) { gnome_vfs_file_info_unref( info ); } if( uri ) { gnome_vfs_uri_unref( uri ); } return ret; } gboolean screem_uri_is_dir( const gchar *uri ) { GnomeVFSFileInfo *info; gboolean ret; ret = FALSE; info = gnome_vfs_file_info_new(); if( gnome_vfs_get_file_info( uri, info, GNOME_VFS_FILE_INFO_DEFAULT ) == GNOME_VFS_OK ) { ret = ( info->type == GNOME_VFS_FILE_TYPE_DIRECTORY ); } gnome_vfs_file_info_unref( info ); return ret; } /* pinched from Epiphany ephy-file-helpers.c + modified */ gchar *screem_create_tmp_file( const gchar *base, const gchar *ext, const gchar *tmpdir ) { gint fd; gchar *name; if( ! tmpdir ) { const gchar *tmp; tmp = g_getenv( "TMPDIR" ); if( ! tmp ) { #ifdef P_tmpdir tmp = P_tmpdir; #else tmp = "/tmp"; #endif } name = g_build_filename( tmp, base, NULL ); } else { name = g_build_filename( tmpdir, base, NULL ); } fd = g_mkstemp( name ); if( fd != -1 ) { unlink( name ); close( fd ); if( ext ) { gchar *tmp; tmp = g_strconcat( name, ".", ext, NULL ); g_free( name ); name = tmp; } } else { g_free( name ); name = NULL; } return name; } gchar *screem_get_dot_dir( void ) { const gchar *home; gchar *ret; home = g_get_home_dir(); ret = g_build_filename( home, ".screem", NULL ); mkdir_recursive( ret, GNOME_VFS_PERM_USER_ALL, NULL, NULL ); return ret; } static gint dummy_filter( const GnomeVFSFileInfo *info ) { return 0; } GSList *screem_vfs_scandir( const gchar *dir, gint (*filter)( const GnomeVFSFileInfo *info ), GCompareFunc compare, gboolean recurse ) { GSList *ret; GSList *rec; gchar *full; GnomeVFSDirectoryHandle *handle; GnomeVFSFileInfoOptions options; GnomeVFSFileInfo *info; GnomeVFSResult result; gboolean ignore; gchar *utf; ret = NULL; if( ! filter ) { filter = dummy_filter; } options = GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS; handle = NULL; result = gnome_vfs_directory_open( &handle, dir, options ); info = gnome_vfs_file_info_new(); while( result == GNOME_VFS_OK ) { result = gnome_vfs_directory_read_next( handle, info ); if( result == GNOME_VFS_OK ) { ignore = ( info->name[ 0 ] == '.' && ( info->name[ 1 ] == '\0' || ( info->name[ 1 ] == '.' && info->name[ 2 ] == '\0' ) ) ); if( ! g_utf8_validate( info->name, -1, NULL ) ) { utf = g_filename_to_utf8( info->name, -1, NULL, NULL, NULL ); } else { utf = g_strdup( info->name ); } if( ! filter( info ) ) { full = g_build_path( G_DIR_SEPARATOR_S, dir, utf, NULL ); ret = g_slist_prepend( ret, full ); } else if( recurse && ( ! ignore ) && info->mime_type && ( ! strcmp( "x-directory/normal", info->mime_type ) || ! strcmp( "inode/directory", info->mime_type ) ) ) { full = g_build_path( G_DIR_SEPARATOR_S, dir, utf, NULL ); rec = screem_vfs_scandir( full, filter, compare, recurse ); g_free( full ); ret = g_slist_concat( rec, ret ); } g_free( utf ); } gnome_vfs_file_info_clear( info ); } gnome_vfs_file_info_unref( info ); if( handle ) { gnome_vfs_directory_close( handle ); } if( ret && compare ) { ret = g_slist_sort( ret, compare ); ret = g_slist_reverse( ret ); } return ret; } static void screem_fileselect_update_preview( GtkFileChooser *file_chooser, gpointer data ) { GnomeThumbnailFactory *factory; GtkWidget *preview; const gchar *href; GnomeVFSFileInfo *info; GdkPixbuf *pixbuf; gchar *uri; factory = g_object_get_data( G_OBJECT( file_chooser ), "thumb_factory" ); preview = GTK_WIDGET( data ); href = gtk_file_chooser_get_preview_uri( file_chooser ); info = gnome_vfs_file_info_new(); pixbuf = NULL; if( gnome_vfs_get_file_info( href, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS ) == GNOME_VFS_OK ) { uri = gnome_thumbnail_factory_lookup( factory, href, info->mtime ); if( uri ) { pixbuf = gdk_pixbuf_new_from_file( uri, NULL ); g_free( uri ); } else { pixbuf = gnome_thumbnail_factory_generate_thumbnail( factory, href, info->mime_type ); if( pixbuf ) { gnome_thumbnail_factory_save_thumbnail( factory, pixbuf, href, info->mtime ); } } if( pixbuf ) { gtk_image_set_from_pixbuf( GTK_IMAGE( preview ), pixbuf ); g_object_unref( pixbuf ); } } gnome_vfs_file_info_unref( info ); gtk_file_chooser_set_preview_widget_active( file_chooser, pixbuf != NULL ); } GtkWidget *screem_file_dialog_create( const gchar *title, GtkWindow *parent, GtkFileChooserAction action, gboolean multi, const gchar *dir, const GSList *filters, gboolean preview ) { GtkWidget *dialog; GtkWidget *image; const gchar *button; GtkFileFilter *all; GnomeThumbnailFactory *factory; const gchar *role = "screem_file_selector"; button = GTK_STOCK_OPEN; if( action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ) { button = GTK_STOCK_SAVE; } dialog = gtk_file_chooser_dialog_new( title, parent, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, button, GTK_RESPONSE_ACCEPT, NULL ); gtk_window_set_role( GTK_WINDOW( dialog ), role ); gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( dialog ), FALSE ); gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( dialog), multi ); while( filters ) { gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( dialog ), filters->data ); filters = filters->next; } all = screem_get_file_filter( _( "All Files" ) ); gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( dialog ), all ); if( dir ) { gtk_file_chooser_set_current_folder_uri( GTK_FILE_CHOOSER( dialog ), dir ); } if( preview ) { image = gtk_image_new(); gtk_file_chooser_set_preview_widget( GTK_FILE_CHOOSER( dialog ), image ); g_signal_connect( GTK_FILE_CHOOSER( dialog ), "update-preview", G_CALLBACK( screem_fileselect_update_preview ), image ); gtk_file_chooser_set_use_preview_label( GTK_FILE_CHOOSER( dialog ), TRUE ); factory = gnome_thumbnail_factory_new( GNOME_THUMBNAIL_SIZE_LARGE ); g_object_set_data_full( G_OBJECT( dialog ), "thumb_factory", factory, (GDestroyNotify)g_object_unref ); } return dialog; } gchar *screem_fileselect( const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *dir, const GSList *filters, gboolean preview ) { ScreemSession *session; GtkWidget *dialog; gchar *ret; gchar *temp; gint x; gint y; gint w; gint h; session = screem_application_get_session( app ); ret = NULL; if( ! dir ) { dir = screem_default_dir; } dialog = screem_file_dialog_create( title, parent, action, FALSE, dir, filters, preview ); screem_session_restore_dialog( session, dialog ); if( gtk_dialog_run( GTK_DIALOG( dialog ) ) == GTK_RESPONSE_ACCEPT ) { ret = gtk_file_chooser_get_uri( GTK_FILE_CHOOSER( dialog ) ); if( ret ) { if( ! g_utf8_validate( ret, -1, NULL ) ) { temp = g_filename_to_utf8( ret, -1, NULL, NULL, NULL ); g_free( ret ); ret = temp; } } } if( dir == screem_default_dir ) { g_free( screem_default_dir ); screem_default_dir = gtk_file_chooser_get_current_folder_uri( GTK_FILE_CHOOSER( dialog ) ); } /* don't use screem_session_store_dialog(), we don't * want to store the width/height for save dialogs */ if( session ) { if( action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ) { w = h = -1; } else { gtk_window_get_size( GTK_WINDOW( dialog ), &w, &h ); } gtk_window_get_position( GTK_WINDOW( dialog ), &x, &y ); screem_session_set_dialog( session, gtk_window_get_role( GTK_WINDOW( dialog ) ), x, y, w, h ); } gtk_widget_destroy( dialog ); return ret; } GSList *screem_fileselect_multi( const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *dir, const GSList *filters, gboolean preview ) { ScreemSession *session; GtkWidget *dialog; GSList *ret; gchar *temp; GSList *tmp; gint x; gint y; gint w; gint h; session = screem_application_get_session( app ); ret = NULL; if( ! dir ) { dir = screem_default_dir; } dialog = screem_file_dialog_create( title, parent, action, TRUE, dir, filters, preview ); screem_session_restore_dialog( session, dialog ); if( gtk_dialog_run( GTK_DIALOG( dialog ) ) == GTK_RESPONSE_ACCEPT ) { ret = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( dialog ) ); for( tmp = ret; tmp; tmp = tmp->next ) { temp = tmp->data; if( ! g_utf8_validate( temp, -1, NULL ) ) { tmp->data = g_filename_to_utf8( temp, -1, NULL, NULL, NULL ); g_free( temp ); } } } if( dir == screem_default_dir ) { g_free( screem_default_dir ); screem_default_dir = gtk_file_chooser_get_current_folder_uri( GTK_FILE_CHOOSER( dialog ) ); } /* don't use screem_session_store_dialog(), we don't * want to store the width/height for save dialogs */ if( session ) { if( action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ) { w = h = -1; } else { gtk_window_get_size( GTK_WINDOW( dialog ), &w, &h ); } gtk_window_get_position( GTK_WINDOW( dialog ), &x, &y ); screem_session_set_dialog( session, gtk_window_get_role( GTK_WINDOW( dialog ) ), x, y, w, h ); } gtk_widget_destroy( dialog ); return ret; } gint screem_compare_file_age( const gchar *a, const gchar *b ) { GnomeVFSFileInfo *info; time_t atime; time_t btime; GnomeVFSFileInfoOptions options; info = gnome_vfs_file_info_new(); atime = btime = 0; options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS; if( gnome_vfs_get_file_info( a, info, options ) == GNOME_VFS_OK ) { atime = info->mtime; } if( gnome_vfs_get_file_info( b, info, options ) == GNOME_VFS_OK ) { btime = info->mtime; } gnome_vfs_file_info_unref( info ); return ( atime - btime ); } void screem_gtk_file_chooser_button_show( GtkWidget *widget ) { ScreemSession *session; session = screem_application_get_session( app ); screem_session_restore_dialog( session, widget ); } void screem_gtk_file_chooser_button_response( GtkWidget *widget, gint response ) { ScreemSession *session; GtkFileChooserAction action; gint w; gint h; gint x; gint y; action = gtk_file_chooser_get_action( GTK_FILE_CHOOSER( widget ) ); session = screem_application_get_session( app ); if( action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ) { w = h = -1; } else { gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( widget ), FALSE ); gtk_window_get_size( GTK_WINDOW( widget ), &w, &h ); } gtk_window_get_position( GTK_WINDOW( widget ), &x, &y ); screem_session_set_dialog( session, gtk_window_get_role( GTK_WINDOW( widget ) ), x, y, w, h ); }