/* Screem: skel-plugin.c * * Copyright (C) 2004 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* add sitecopy functionality */ #include "common.h" #include "sites.h" #include "frontend.h" #include "screem-skel-plugin.h" /* setup your plugin here */ /* plugin name should only include a-zA-Z */ static const gchar *plugin_name = "ScreemSitecopy"; static const gchar *authors[] = { "Lee Mallabone", "David A Knight ", NULL }; static const gchar *displayed_name = N_( "Screem Upload Wizard" ); static const gchar *description = N_( "Sitecopy based upload wizard, using gnome-vfs" ); static const gchar *version = "2.0.0"; /* add any per instance data items here */ struct ScreemSkelPluginPrivate { GladeXML *xml; GtkWidget *dialog; struct site *site; volatile int in_critical_section; volatile int want_abort; sigjmp_buf abort_buf; sem_t *update_semaphore; pthread_t update_tid; gboolean prompting; gboolean closing; gint button; guint count; guint done_count; gfloat upload_total; gfloat uploaded_bytes; GTimeVal start_time; gfloat progress; GtkAction *action; }; static GMutex *wizard_lock; static ScreemPlugin *currentWiz; enum site_op { site_op_update, site_op_fetch, site_op_resync }; int my_abortable_transfer_wrapper(struct site *site, enum site_op operation ); void handle_abort( int sig ); void do_abort( ScreemPlugin *plugin ); void abort_site_update(GtkWidget * button, gpointer data); int fe_gtk_question(char *question, GnomeReplyCallback yes_action); void fe_login_clicked( GtkDialog *dialog, gint number ); void fe_login_close( GtkDialog *dialog ); static gboolean create_sitecopy_directory( void ); static void destroy_fnlists( struct site *site ); static void destroy_fnlist( struct fnlist *list ); /* the drivers */ extern const struct proto_driver vfs_driver; static struct site* screem_site_to_sitecopy_site( ScreemSite *site, gboolean show_alert ); static void upload_wizard_error( const gchar *message ); static void upload_wizard_message( const gchar *message ); static void upload_site( GtkAction *action, gpointer user_data ); static gboolean should_init( void ); static gboolean verify_site( struct site *site ); static void *update_thread( void *arg ); #define SITE_NOSITE 901 #define SITE_NONAME 920 #define SITE_NOSERVER 921 #define SITE_NOREMOTEDIR 924 #define SITE_NOLOCALDIR 925 #define SITE_ACCESSLOCALDIR 926 #define SITE_INVALIDPORT 927 #define SITE_NOMAINTAIN 928 #define SITE_NOREMOTEREL 929 #define SITE_NOLOCALREL 930 #define SITE_NOPERMS 931 #define SITE_NOSAFEOVER 932 #define SITE_NORENAMES 933 #define SITE_NOSAFETEMPUP 934 #define SITE_NOSERVERCERT 935 static int rcfile_verify(struct site *any_site); static void init_dialog( ScreemPlugin *plugin ); static void begin_upload( ScreemPlugin *plugin ); static void finish_upload( ScreemPlugin *plugin ); /** * setup: * * this function will be called once for each window, * you should add any actions / ui here, eg. * * screem_plugin_add_action( plugin, name, label, tip, stock_id, * callback, error ); * screem_plugin_add_menu( plugin, path, action, error ); * screem_plugin_add_toolbar( plugin, path, action, error ); * * * to insert text into the current page being edited your callbacks * should make use of * screem_plugin_get_cursor_position( plugin ) * screem_plugin_set_cursor_position( plugin, pos ) * screem_plugin_insert( plugin, pos, text, length, indent ) * **/ static gboolean setup( ScreemPlugin *plugin ) { GError *error; gboolean ret; ret = TRUE; if( ! wizard_lock ) { wizard_lock = g_mutex_new(); } if( ! screem_plugin_add_action( plugin, "UploadWizard", _( "Upload" ), _( "Upload the current Site" ), GTK_STOCK_JUMP_TO, G_CALLBACK( upload_site ), &error ) ) { ret = FALSE; g_print( "Add action failed: %s\n", error->message ); g_error_free( error ); } if( ret && ! screem_plugin_add_menu( plugin, "/Tools", "UploadWizard", &error ) ) { ret = FALSE; g_print( "Add menu failed: %s\n", error->message ); g_error_free( error ); } return ret; } /** * cleanup: * * this function will be called once for each window when * it is closed, you should cleanup any data items you * have in ScreemSkelPluginPrivate here **/ static void cleanup( ScreemSkelPluginPrivate *priv ) { } static struct site* screem_site_to_sitecopy_site( ScreemSite *ssite, gboolean show_alert ) { struct site *sitecopy_site; struct site_host *host; const gchar *string; GList *list; GList *tmp; struct fnlist *exclude; struct fnlist *ignores; const gchar* home; gchar *temp; GnomeVFSURI *uri; GnomeVFSURI *full; UploadMethods method; sitecopy_site = g_new0( struct site, 1 ); host = &sitecopy_site->server; /* the site name */ string = screem_site_get_name( ssite ); sitecopy_site->name = g_strdup( string ); string = screem_site_get_remote_url( ssite ); host->hostname = g_strdup( string ); host->username = g_strdup( screem_site_get_remote_user( ssite ) ); host->password = g_strdup( screem_site_get_remote_pass( ssite ) ); if( host->username && strlen( host->username ) == 0 ) host->username = NULL; if( host->password && strlen( host->password ) == 0 ) host->password = NULL; if( host->hostname ) { if( ( temp = strrchr( host->hostname, ':' ) ) ) { host->port = atoi( temp + 1 ); temp = g_strndup( host->hostname, temp - host->hostname ); g_free( host->hostname ); host->hostname = temp; } } /* the view url */ string = screem_site_get_http_url( ssite ); sitecopy_site->url = g_strdup( string ); /* get transfer options */ method = screem_site_get_remote_method( ssite ); sitecopy_site->protocol = siteproto_vfs; sitecopy_site->proto_string = "vfs"; sitecopy_site->driver = &vfs_driver; host->port = 0; g_free( host->hostname ); string = screem_site_get_remote_url( ssite ); if( method == UPLOAD_OTHER ) { host->hostname = g_strdup( string ); } else { host->hostname = g_strconcat( upload_schemes[ method ], string, NULL ); } switch( screem_site_get_permissions( ssite ) ) { case PERMS_EXEC: sitecopy_site->perms = sitep_exec; break; case PERMS_IGNORE: sitecopy_site->perms = sitep_ignore; break; case PERMS_ALL: sitecopy_site->perms = sitep_all; break; } switch( screem_site_get_symlinks( ssite ) ) { case SYM_IGNORE: sitecopy_site->symlinks = sitesym_ignore; break; case SYM_FOLLOW: sitecopy_site->symlinks = sitesym_follow; break; case SYM_MAINTAIN: sitecopy_site->symlinks = sitesym_maintain; break; } /* the remote path */ string = screem_site_get_remote_path( ssite ); sitecopy_site->remote_root_user = g_strdup( string ); sitecopy_site->remote_isrel = ( string[ 0 ] == '~' ); if( sitecopy_site->protocol == siteproto_vfs ) { sitecopy_site->remote_isrel = FALSE; } if( string[ 0 ] != '~' && string[ 0 ] != G_DIR_SEPARATOR ) { /* must start with ~ or / */ if( show_alert ) { upload_wizard_error( _("Remote path must being with ~ or /") ); } g_free( sitecopy_site ); return NULL; } /* vfs method */ g_free( sitecopy_site->remote_root_user ); uri = gnome_vfs_uri_new( host->hostname ); if( ! uri ) { if( show_alert ) { upload_wizard_error( _( "Invalid URI for remote host" ) ); } g_free( sitecopy_site ); return NULL; } if( host->port ) { gnome_vfs_uri_set_host_port( uri, host->port ); } full = gnome_vfs_uri_append_path( uri, string ); gnome_vfs_uri_unref( uri ); if( ! full ) { if( show_alert ) { upload_wizard_error( _( "Invalid URI for remote host" ) ); } } if( host->username ) { gnome_vfs_uri_set_user_name( full, host->username ); } sitecopy_site->remote_root_user = gnome_vfs_uri_to_string( full, 0 ); gnome_vfs_uri_unref( full ); /* the local pathname */ string = screem_site_get_pathname( ssite ); if( ! g_str_has_prefix( string, "file://" ) ) { if( show_alert ) upload_wizard_error( _( "The upload wizard can only be used with local sites" ) ); g_free( sitecopy_site ); return NULL; } temp = screem_get_local_site_path( ssite ); string = g_strconcat( temp, G_DIR_SEPARATOR_S, NULL ); g_free( temp ); sitecopy_site->local_root_user = string; sitecopy_site->local_isrel = ( string[ 0 ] == '~' ); if( string[ 0 ] != '~' && string[ 0 ] != G_DIR_SEPARATOR ) { /* should never occur, screem should not allow this case to happen */ if( show_alert ) upload_wizard_error( _("Local path must begin with ~ or /") ); g_free( sitecopy_site ); return NULL; } string = screem_site_get_pathname( ssite ); /* misc settings */ sitecopy_site->nodelete = screem_site_get_no_delete( ssite ); sitecopy_site->checkmoved = screem_site_get_check_moved( ssite ); sitecopy_site->nooverwrite = screem_site_get_no_overwrite( ssite ); /* setup excludes */ exclude = g_new0( struct fnlist, 1 ); exclude->haspath = FALSE; exclude->pattern = "*.screem"; exclude->prev = g_new0( struct fnlist, 1 ); exclude->prev->next = exclude; exclude = exclude->prev; exclude->haspath = FALSE; exclude->pattern = g_strdup( ".project.screem" ); for( tmp = list = screem_site_get_excludes( ssite ); list; list = list->next ){ struct fnlist *newexclude; gchar *pattern; newexclude = g_new0( struct fnlist, 1 ); if( ! strncmp( "glob:", (gchar*)list->data, strlen( "glob:" ) ) ) { newexclude->haspath = FALSE; pattern = ((gchar*)list->data) + strlen( "glob:" ); } else if( ! strncmp( "regex:", (gchar*)list->data, strlen( "regex:" ) ) ) { newexclude->haspath = FALSE; pattern = ((gchar*)list->data) + strlen( "regex: " ); /* don't support this yet */ g_free( newexclude ); newexclude = NULL; g_warning( "regex: patterns not supported yet\n" ); } else { newexclude->haspath = TRUE; pattern = ((gchar*)list->data) + strlen( string ); pattern ++; } if( newexclude ) { newexclude->pattern = g_strdup( pattern ); exclude->prev = newexclude; exclude->prev->next = exclude; exclude = exclude->prev; } g_free( list->data ); } g_list_free( tmp ); sitecopy_site->excludes = exclude; /* setup ignores */ for( ignores = NULL, tmp = list = screem_site_get_ignores( ssite ); list; list = list->next ) { struct fnlist *newignore; gchar *pattern; newignore = g_new0( struct fnlist, 1 ); if( ! strncmp( "glob:", (gchar*)list->data, strlen( "glob:" ) ) ) { newignore->haspath = FALSE; pattern = ((gchar*)list->data) + strlen( "glob:" ); } else if( ! strncmp( "regex:", (gchar*)list->data, strlen( "regex:" ) ) ) { newignore->haspath = FALSE; pattern = ((gchar*)list->data) + strlen( "regex: " ); /* don't support this yet */ g_free( newignore ); newignore = NULL; g_warning( "regex: patterns not supported yet\n" ); } else { newignore->haspath = TRUE; pattern = ((gchar*)list->data) + strlen( string ); pattern ++; } if( newignore ) { newignore->pattern = g_strdup( pattern ); if( ! ignores ) { ignores = newignore; } else { ignores->prev = newignore; ignores->prev->next = ignores; ignores = ignores->prev; } } g_free( list->data ); } g_list_free( tmp ); sitecopy_site->ignores = ignores; sitecopy_site->asciis = NULL; sitecopy_site->use_this = TRUE; /* FIXME: this should be an option */ sitecopy_site->state_method = state_timesize; sitecopy_site->stored_state_method = state_timesize; sitecopy_site->keep_going = TRUE; /* set info file path */ home = g_get_home_dir(); sitecopy_site->infofile = g_strconcat( home, G_DIR_SEPARATOR_S, ".sitecopy", G_DIR_SEPARATOR_S, sitecopy_site->name, NULL ); return sitecopy_site; } static void upload_site( GtkAction *action, gpointer user_data ) { ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; struct stat s; ScreemSite *site; GtkWidget *widget; struct site *scsite; gchar *tmp; struct site_file *files; guint count; plugin = SCREEM_PLUGIN( user_data ); init_dialog( plugin ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; site = screem_plugin_get_current_site( plugin ); if( screem_site_get_fake_flag( site ) ) { return; } /* create ~/.sitecopy if necessary */ if( ! create_sitecopy_directory() ) { return; } g_mutex_lock(wizard_lock); currentWiz = plugin; if( ! ( priv->site = screem_site_to_sitecopy_site( site,TRUE ) ) ) { g_mutex_unlock(wizard_lock); return; } scsite = priv->site; /* so we can get the plugin back in different places */ scsite->user_data = plugin; /* verify that the site is setup properly */ if( ! verify_site( scsite ) ) { g_free( scsite->infofile ); g_free( scsite ); priv->site = NULL; g_mutex_unlock(wizard_lock); return; } widget = glade_xml_get_widget( priv->xml, "from_text" ); gtk_label_set_text( GTK_LABEL( widget ), screem_site_get_pathname( site ) ); widget = glade_xml_get_widget( priv->xml, "to_text" ); tmp = g_strdup_printf( "%s %s %s", screem_site_get_remote_path( site ), /* pathname on host */ _( "on" ), scsite->server.hostname ); gtk_label_set_text( GTK_LABEL( widget ), tmp ); g_free( tmp ); widget = glade_xml_get_widget( priv->xml, "status_text" ); gtk_label_set_text( GTK_LABEL( widget ), "" ); widget = glade_xml_get_widget( priv->xml, "progressbar" ); gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR( widget ), 0.0 ); /* info file exist? */ if( stat( scsite->infofile, &s ) < 0 ) { /* Displays a dialog asking if the user wants to init or catchup their website. */ site_write_stored_state( scsite ); if( should_init() ) { site_initialize( scsite ); } else if ( (site_readfiles (scsite)) < 0 ) { upload_wizard_error(_("Could not retrieve information about your local files.")); g_free( scsite->infofile ); g_free( scsite ); priv->site = NULL; g_mutex_unlock(wizard_lock); return; } else { site_catchup( scsite ); site_write_stored_state( scsite ); /* If the site is up to date, we really don't need to upload it! */ g_free( scsite->infofile ); g_free( scsite ); priv->site = NULL; g_mutex_unlock(wizard_lock); return; } } scsite->files = NULL; scsite->files_tail = NULL; priv->action = action; g_object_set( G_OBJECT( action ), "sensitive", FALSE, NULL ); if( site_readfiles( scsite ) < 0 ) { /* couldn't get info on local site files */ upload_wizard_error( _("Couldn't retrieve information about local files") ); g_free( scsite->infofile ); g_free( scsite ); priv->site = NULL; g_object_set( G_OBJECT( action ), "sensitive", TRUE, NULL ); g_mutex_unlock(wizard_lock); return; } else if( ! scsite->remote_is_different ) { /* nothing needs uploading */ upload_wizard_message( _( "The remote site is already uptodate" ) ); g_free( scsite->infofile ); g_free( scsite ); priv->site = NULL; g_object_set( G_OBJECT( action ), "sensitive", TRUE, NULL ); g_mutex_unlock(wizard_lock); return; } /* reset transfer counters */ priv->upload_total = scsite->totalnew + scsite->totalchanged; priv->uploaded_bytes = 0.0; for( count = 0, files = scsite->files; files; files = files->next ) { if( files->diff != file_unchanged ) { count ++; } } priv->count = count; priv->done_count = 0; g_get_current_time( &priv->start_time ); sem_init(priv->update_semaphore, 0, 0); priv->closing = FALSE; pthread_create(&priv->update_tid, NULL, update_thread, plugin); /* all setup, display upload dialog */ if( ! GTK_WIDGET_VISIBLE( priv->dialog ) ) { screem_plugin_restore_from_session( plugin, priv->dialog ); } gtk_widget_show_all( priv->dialog ); gdk_window_raise( priv->dialog->window ); begin_upload( plugin ); } static gboolean verify_site( struct site *site ) { gint ret; gchar *message = NULL; switch( ( ret = rcfile_verify( site ) ) ) { case SITE_NONAME: message = _("No site name given"); break; case SITE_NOSERVER: message = _("No server name given."); break; case SITE_NOREMOTEDIR: message = _("No remote directory given."); break; case SITE_NOLOCALDIR: message = _("No local directory given."); break; case SITE_ACCESSLOCALDIR: message = _("The local dir couldn't be accessed."); break; case SITE_INVALIDPORT: message = _("There was a problem with the port number for this site."); break; case SITE_NOMAINTAIN: message = _("Sorry, the symbolic links option you chose is not supported\nby this transfer protocol. Please choose another option."); break; case SITE_NOREMOTEREL: if( site->remote_isrel ) message = _("This protocol does not support relative remote directories."); break; case SITE_NOPERMS: message = _("The protocol you are attempting to use does\nnot currently support maintaining permissions."); break; case SITE_NOLOCALREL: message = _("The local dir is invalid."); break; case 0: /* Site integrity is okay. */ break; default: message = _("There was an undetermined problem verifying the correctness of your site definition. Please report this to the maintainer."); break; } if( message ) { upload_wizard_error( message ); } return ( ret == 0 ); } static gboolean should_init() { GtkWidget *state_question; gint button; state_question = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("This appears to be the first time you have attempted to upload\nthis website. Does the site already exist on the server?") ); button = gtk_dialog_run( GTK_DIALOG( state_question ) ); gtk_widget_destroy( state_question ); return ( button == GTK_RESPONSE_NO ); } static void *update_thread( void *arg ) { ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; int ret; GtkWidget *widget; const gchar *message; plugin = SCREEM_PLUGIN( arg ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; pthread_detach(priv->update_tid); while( ! priv->closing ) { /* sleep */ sem_wait( priv->update_semaphore ); ret = my_abortable_transfer_wrapper(priv->site, site_op_update); gdk_threads_enter(); message = priv->site->last_error; if( ret == SITE_OK ) { site_write_stored_state( priv->site ); } else if( message ) { upload_wizard_error( message ); g_print( "RET: %i\n", ret ); } widget = glade_xml_get_widget( priv->xml, "status_text" ); gtk_label_set_text( GTK_LABEL( widget ), "" ); finish_upload( plugin ); gdk_threads_leave(); } g_object_set( G_OBJECT( priv->action ), "sensitive", TRUE, NULL ); g_mutex_unlock( wizard_lock ); return NULL; } /* GUI part */ static void upload_wizard_error( const gchar *message ) { gchar *temp; temp = g_strdup_printf( _( "Upload Wizard: %s\n" ), message ); screem_plugin_show_error( currentWiz, temp ); g_free( temp ); } static void upload_wizard_message( const gchar *message ) { gchar *temp; temp = g_strdup_printf( _( "Upload Wizard: %s\n" ), message ); screem_plugin_show_error( currentWiz, temp ); g_free( temp ); } /* callbacks */ static void begin_upload( ScreemPlugin *plugin ) { /* We don't want to begin again. Also, there doesn't seem * to be any way * to explictly terminate a pthread, so we * don't want the dialog to be closed while the upload would * carry on in the background. * We also don't want the user changing files while we're * doing an * upload, so we make the upload dialog modal. */ ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( plugin )->priv; gtk_window_set_modal( GTK_WINDOW( priv->dialog ), TRUE ); sem_post(priv->update_semaphore); } void screem_upload_wizard_close_dialog( GtkWidget *widget ) { GladeXML *xml; GtkWidget *update_dialog; ScreemPlugin *plugin; xml = glade_get_widget_tree( widget ); update_dialog = glade_xml_get_widget( xml, "update_dialog" ); plugin = SCREEM_PLUGIN( g_object_get_data( G_OBJECT(update_dialog), "plugin" ) ); finish_upload( plugin ); } static void finish_upload( ScreemPlugin *plugin ) { ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( plugin )->priv; priv->closing = TRUE; sem_post( priv->update_semaphore ); /* the update thread will terminate itself as the for loop is broken out of */ /* Free the memory created in site_readfiles. */ site_destroy( priv->site ); /* clean up */ g_free( priv->site ); priv->site = NULL; screem_plugin_store_in_session( plugin, priv->dialog ); gtk_widget_hide( priv->dialog ); } static void update_progressbar( ScreemPlugin *plugin ) { ScreemSkelPluginPrivate *priv; gchar *tmp; GtkWidget *bar; GTimeVal now; glong elapsed; gint rate; gint remaining; gchar *str; priv = SCREEM_SKEL_PLUGIN( plugin )->priv; g_get_current_time( &now ); elapsed = now.tv_sec - priv->start_time.tv_sec; if( elapsed == 0 ) { elapsed = 1; } rate = priv->uploaded_bytes / elapsed; remaining = 0; if( rate != 0 ) { remaining = ( priv->upload_total - priv->uploaded_bytes ) / rate; } if( remaining >= 3600 ) { str = g_strdup_printf( _( "(%d:%02d:%d Remaining)" ), remaining / 3600, ( remaining % 3600 ) / 60, ( remaining % 3600 ) % 60 ); } else { str = g_strdup_printf( _( "(%d:%02d Remaining)" ), remaining / 60, remaining % 60 ); } tmp = g_strdup_printf( "file: %i of %i %s", priv->done_count, priv->count, str ); bar = glade_xml_get_widget( priv->xml, "progressbar" ); gtk_progress_bar_set_text( GTK_PROGRESS_BAR( bar ), tmp ); g_free( tmp ); g_free( str ); gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR( bar ), ( priv->uploaded_bytes / (gfloat)priv->upload_total ) ); } /* Signal handler, ok how are we supposed to get the wizard struct here? */ void handle_abort( int sig ) { ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( currentWiz )->priv; if( priv->in_critical_section ) { /* Can't abort now, so remember we want to for later */ priv->want_abort = 1; } else { do_abort( currentWiz ); } } void abort_site_update(GtkWidget * button, gpointer data) { GladeXML *xml; GtkWidget *update_dialog; ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; GtkWidget *status_label; gchar *tmp; xml = glade_get_widget_tree( GTK_WIDGET( button ) ); update_dialog = glade_xml_get_widget( xml, "update_dialog" ); plugin = SCREEM_PLUGIN( g_object_get_data( G_OBJECT(update_dialog), "plugin" ) ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; status_label = glade_xml_get_widget( xml, "status_text" ); tmp = g_strconcat( "", _( "Aborting" ), "...", "", NULL ); gtk_label_set_markup(GTK_LABEL(status_label), tmp ); g_free( tmp ); /* Free the memory created in site_readfiles. * hmmm, should we be doing this at this point? - David */ site_destroy( priv->site ); pthread_kill(priv->update_tid, SIGUSR1); } int my_abortable_transfer_wrapper(struct site *site, enum site_op operation ) { int ret; ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; plugin = SCREEM_PLUGIN( site->user_data ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; signal( SIGUSR1, handle_abort ); if( !sigsetjmp( priv->abort_buf, 1 ) ) { /* Normal execution */ switch (operation) { case site_op_update: ret = site_update(site); break; case site_op_fetch: ret = site_fetch(site); break; case site_op_resync: ret = site_synch(site); break; default: ret = 0; break; } } else { /* The update was aborted */ ret = SITE_ABORTED; } signal( SIGUSR1, SIG_IGN ); return ret; } /* Actually abort the update */ void do_abort( ScreemPlugin *plugin ) { ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( plugin )->priv; priv->want_abort = 0; siglongjmp( priv->abort_buf, 1 ); } /********************************************************************/ /**** Implementations of all the functions defined in frontend.h ****/ /**** These functions should be considered 'private' and never ****/ /**** be called directly. ****/ /********************************************************************/ int fe_gtk_question(char *question, GnomeReplyCallback yes_action) { /* GtkWidget *question_window; gdk_threads_enter(); question_window = gnome_app_question(GNOME_APP(app), (const gchar *) question, yes_action, NULL); gtk_widget_show(question_window); gdk_threads_leave(); g_print( "QUESTION: %s\n", question );*/ return 1; } /* Enter critical section */ void fe_disable_abort(struct site *site) { ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; if( site->user_data ) { plugin = SCREEM_PLUGIN( site->user_data ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; priv->in_critical_section = 1; } } /* Leave critical section */ void fe_enable_abort(struct site *site) { ScreemPlugin *plugin; ScreemSkelPluginPrivate *priv; if( site->user_data ) { plugin = SCREEM_PLUGIN( site->user_data ); priv = SCREEM_SKEL_PLUGIN( plugin )->priv; priv->in_critical_section = 0; /* Carry out the abort if we were aborted while in the * critical section */ if( priv->want_abort ) { do_abort( plugin ); } } } void fe_warning(const char *description, const char *subject, const char *error) { gchar *temp; gdk_threads_enter(); temp = g_strdup_printf( _( "Upload Wizard: %s" ), description ); screem_plugin_show_message( currentWiz, temp ); gdk_threads_leave(); g_free( temp ); } void fe_verified(const char *fname, enum file_diff match) { } /** Informs the user when we're trying to (re)establish a connection * or lookup a hostname. */ void fe_connection( fe_status status, const char *info ) { GtkWidget *label; gchar *text = ""; ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( currentWiz )->priv; gdk_threads_enter(); label = glade_xml_get_widget( priv->xml, "status_text" ); switch( status ) { case fe_namelookup: text = g_strconcat( "", _("Looking up hostname: "), info, " ...", "", NULL ); break; case fe_connecting: text = g_strconcat( "", _("Attempting to connect "), " ...", "", NULL ); break; case fe_connected: text = g_strconcat( "", _("Connected "), "", NULL ); break; } gtk_label_set_markup( GTK_LABEL( label ), text ); g_free( text ); gdk_threads_leave(); } int fe_can_update( const struct site_file *file ) { return TRUE; } void fe_updating( const struct site_file *file ) { const gchar *action; gchar *file_status; gchar *tmp; GtkWidget *status_label; ScreemSkelPluginPrivate *priv; const gchar *filename; priv = SCREEM_SKEL_PLUGIN( currentWiz )->priv; gdk_threads_enter(); priv->done_count ++; status_label = glade_xml_get_widget( priv->xml, "status_text" ); action = NULL; file_status = NULL; filename = file_name( file ); if (file->type == file_dir) { if (file->diff == file_new) { action = _( "Creating \"%s\"" ); } else { action = _( "Deleting \"%s\"" ); } } else { switch (file->diff) { case file_changed: case file_new: action = _( "Copying \"%s\"" ); break; case file_deleted: action = _( "Deleting \"%s\"" ); break; case file_moved: action = _( "Moving \"%s\"" ); break; default: case file_unchanged: break; } } if( action ) { file_status = g_strdup_printf( action, filename ); tmp = g_strconcat( "", file_status, "", NULL ); gtk_label_set_markup( GTK_LABEL( status_label ), tmp ); g_free( tmp ); g_free( file_status ); } update_progressbar( currentWiz ); gdk_threads_leave(); } /* Once a file has been updated, any errors with it are recorded in the * error list, and the progress bar is reset. */ void fe_updated( const struct site_file *file, const int success, const char *error ) { gchar *temp; ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( currentWiz )->priv; gdk_threads_enter(); if( ! success ) { temp = g_strdup_printf( "Upload Wizard: %s: %s\n", file_name( file ), error ); screem_plugin_show_message( currentWiz, temp ); g_free( temp ); } priv->progress = 0; gdk_threads_leave(); } void fe_transfer_progress( off_t progress, off_t total ) { ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( currentWiz )->priv; gdk_threads_enter(); priv->uploaded_bytes += ((gfloat) progress) - priv->progress ; priv->progress = progress; update_progressbar( currentWiz ); gdk_threads_leave(); } /* These can be implemented if we ever want to use the respective * functionality. */ void fe_synching( const struct site_file *file ) { } void fe_synched( const struct site_file *file, const int success, const char *error ) { } void fe_setting_perms(const struct site_file *file) { } void fe_set_perms(const struct site_file *file, int success, const char *error) { } void fe_checksumming( const char *filename ) { } void fe_checksummed(const char *file, int success, const char *err) { } void fe_fetch_found(const struct site_file *file) { } int fe_login(fe_login_context ctx, const char *realm, const char *hostname, char *username, char *password) { return 0; } void fe_login_clicked( GtkDialog *dialog, gint number ) { } void fe_login_close( GtkDialog *dialog ) { } static gboolean create_sitecopy_directory() { const gchar *home; gchar *dir; struct stat s; home = g_get_home_dir(); dir = g_strconcat( home, G_DIR_SEPARATOR_S, ".sitecopy", NULL ); if( stat( dir, &s ) != 0 ) { if( errno != ENOENT ) { /* couldn't access it */ upload_wizard_error(_("Couldn't access ~/.sitecopy")); g_free( dir ); return FALSE; } /* it doesn't exist so lets make it */ if( mkdir( dir, S_IRWXU ) != 0 ) { /* couldn't make it */ upload_wizard_error(_("Couldn't create ~/.sitecopy")); g_free( dir ); return FALSE; } } g_free( dir ); return TRUE; } gboolean screem_site_get_sync_status( ScreemSite *ssite, GHashTable **table ) { struct site *site; struct site_file *files; struct file_state *file; gchar *filename; g_return_val_if_fail( ssite != NULL, FALSE ); g_return_val_if_fail( table != NULL, FALSE ); *table = NULL; if( screem_site_get_fake_flag( ssite ) ) { /* fake sites have no sync status */ return FALSE; } if( ! create_sitecopy_directory() ) { return FALSE; } site = screem_site_to_sitecopy_site( ssite, FALSE ); if( ! site ) { return FALSE; } if( ! verify_site( site ) ) { destroy_fnlists( site ); g_free( site->infofile ); g_free( site ); return FALSE; } if( site_readfiles( site ) < 0 ) { /* couldn't get info on local site files, this probably means the user hasn't attempted to upload yet */ destroy_fnlists( site ); g_free( site->infofile ); g_free( site ); return FALSE; } *table = g_hash_table_new( g_str_hash, g_str_equal ); /* ok we've got the info, and there are changes */ files = site->files; while( files ) { file = &files->local; if( file->filename ) { gchar *tmp; filename = file_full_local( file, site ); tmp = g_strconcat( "file://", filename, NULL ); free( filename ); /* add to hash table with the status */ g_hash_table_insert( *table, tmp, GINT_TO_POINTER( files->diff ) ); } files = files->next; } site_destroy( site ); destroy_fnlists( site ); g_free( site->infofile ); g_free( site ); return TRUE; } /* destroy the fnlists in the sitecopy site object, we don't want to free that pattern though */ static void destroy_fnlists( struct site *site ) { struct fnlist *list; list = site->excludes; destroy_fnlist( list ); list = site->ignores; destroy_fnlist( list ); list = site->asciis; destroy_fnlist( list ); } static void destroy_fnlist( struct fnlist *list ) { if( ! list ) return; for( ;; ) { if( list->next ) { list = list->next; g_free( list->prev ); } else { g_free( list ); break; } } } static int rcfile_verify(struct site *any_site) { const gchar *home; struct stat localst; char *temp; int ret; home = g_get_home_dir(); /* Protocol-specific checks first, since if a new protocol driver is used, * any of the other checks may be irrelevant. */ switch (any_site->protocol) { case siteproto_vfs: any_site->driver = &vfs_driver; break; case siteproto_unknown: default: return SITE_UNSUPPORTED; } /* Valid options check */ if (any_site->checkrenames && (any_site->state_method != state_checksum)) { return SITE_NORENAMES; } /* Check they specified everything in the rcfile */ if (any_site->server.hostname == NULL) { return SITE_NOSERVER; } if (any_site->remote_root_user == NULL) { return SITE_NOREMOTEDIR; } else if (any_site->local_root_user == NULL) { return SITE_NOLOCALDIR; } /* Need a home directory if we're using relative local root */ if (home == NULL && any_site->local_root) return SITE_NOLOCALREL; /* Can't use safe mode and nooverwrite mode */ if (any_site->safemode && any_site->nooverwrite) return SITE_NOSAFEOVER; if (any_site->safemode && any_site->tempupload) return SITE_NOSAFETEMPUP; if (any_site->remote_isrel) { any_site->remote_root = g_strdup(any_site->remote_root_user + 2); } else { any_site->remote_root = g_strdup(any_site->remote_root_user); } if (any_site->local_isrel) { /* We skip the first char ('~') of l_r_u */ any_site->local_root = g_strconcat(home, any_site->local_root_user + 1, NULL); } else { any_site->local_root = any_site->local_root_user; } /* Now check the local directory actually exists. * To do this, stat `/the/local/root/.', which will fail if the * can't read the directory or if it's a file not a directory */ temp = g_strconcat(any_site->local_root, ".", NULL); ret = stat(temp, &localst); free(temp); if (ret != 0) { return SITE_ACCESSLOCALDIR; } /* Assign default ports if they didn't bother to */ if (any_site->server.port == 0) { NE_DEBUG(DEBUG_RCFILE, "Lookup up default port:\n"); any_site->server.port = (*any_site->driver->get_server_port)(any_site); NE_DEBUG(DEBUG_RCFILE, "Using port: %d\n", any_site->server.port); } if (any_site->proxy.port == 0) { NE_DEBUG(DEBUG_RCFILE, "Lookup default proxy port...\n"); any_site->proxy.port = (*any_site->driver->get_proxy_port)(any_site); NE_DEBUG(DEBUG_RCFILE, "Using port %d\n", any_site->proxy.port); } /* TODO: ditto for proxy server */ return 0; } static void init_dialog( ScreemPlugin *plugin ) { ScreemSkelPluginPrivate *priv; priv = SCREEM_SKEL_PLUGIN( plugin )->priv; if( ! priv->xml ) { priv->xml = glade_xml_new( GLADE_PATH, "update_dialog", NULL ); priv->dialog = glade_xml_get_widget( priv->xml, "update_dialog" ); g_object_set_data( G_OBJECT( priv->dialog ), "plugin", plugin ); glade_xml_signal_autoconnect( priv->xml ); fe_initialize(); priv->update_semaphore = g_new( sem_t, 1 ); sem_init(priv->update_semaphore, 0, 0); priv->closing = FALSE; } } /* There should be no need to change any code below here */ enum { ARG_0 }; static void screem_skel_plugin_class_init( ScreemSkelPluginClass *klass ); static void screem_skel_plugin_init( ScreemSkelPlugin *skel_plugin ); static void screem_skel_plugin_finalize( GObject *object ); /* G Object stuff */ #define PARENT_TYPE SCREEM_TYPE_PLUGIN static gpointer parent_class; static void screem_skel_plugin_class_init( ScreemSkelPluginClass *klass ) { GObjectClass *object_class; object_class = G_OBJECT_CLASS( klass ); object_class->finalize = screem_skel_plugin_finalize; parent_class = g_type_class_peek_parent( klass ); } static void screem_skel_plugin_init( ScreemSkelPlugin *skel_plugin ) { skel_plugin->priv = g_new0( ScreemSkelPluginPrivate, 1 ); SCREEM_PLUGIN( skel_plugin )->setup = setup; } static void screem_skel_plugin_finalize( GObject *object ) { ScreemSkelPlugin *skel_plugin; ScreemSkelPluginPrivate *priv; skel_plugin = SCREEM_SKEL_PLUGIN( object ); priv = skel_plugin->priv; cleanup( priv ); g_free( priv ); G_OBJECT_CLASS( parent_class )->finalize( object ); } static GType screem_skel_plugin_get_type() { static GType type = 0; if( ! type ) { static const GTypeInfo info = { sizeof( ScreemSkelPluginClass ), NULL, /* base init */ NULL, /* base finalise */ (GClassInitFunc)screem_skel_plugin_class_init, NULL, /* class finalise */ NULL, /* class data */ sizeof( ScreemSkelPlugin ), 0, /* n_preallocs */ (GInstanceInitFunc)screem_skel_plugin_init }; type = g_type_register_static( PARENT_TYPE, plugin_name, &info, 0 ); } return type; } static ScreemSkelPlugin *screem_skel_plugin_new( void ) { ScreemSkelPlugin *skel_plugin; skel_plugin = SCREEM_SKEL_PLUGIN( g_object_new( SCREEM_TYPE_SKEL_PLUGIN, "name", plugin_name, NULL ) ); return skel_plugin; } G_MODULE_EXPORT void get_details( ScreemPluginDetails **ret ) { ScreemPluginDetails *details; details = g_new0( ScreemPluginDetails, 1 ); details->name = plugin_name; details->displayed_name = displayed_name; details->authors = authors; details->description = description; details->version = version; details->create = screem_skel_plugin_new; details->api_version = SCREEM_PLUGIN_REQUIRED_VERSION; *ret = details; }