Hi all, I have probably reinvented the wheel here, however I thought it may be useful if I share the modifications I have made to the multisync backup plugin.
My intention was to synchronise calenders/contacts/tasks on several computers running evolution, laptop <-> desktop etc... This modified version of the backup plugin stores the entries in exactly the same way as before, however it can detect when new files are added to the directory and when files are removed/modified (based upon the modification time stamp). This enables several computers to sync to the same directory either by NFS or using software such as Unison to keep a local copy of the backup directory in sync with remote copies. I have been testing this for a day or two, and it appears to be working flawlessly for contacts and calendars. The handling of task data seems to be a little broken, however I believe this not to be a fault of this plugin. Since the backup directory no-longer contains the backup_entries file, synchronising multiple backups is much simplified, and it enables many people to share calendars/contacts/tasks using the file-system, or whatever directory level synchronisation method is preferred. All comments welcome! Regards, -- Craig Shelley EMail: [EMAIL PROTECTED] Jabber: [EMAIL PROTECTED]
/* MultiSync Backup Plugin - Backup your PIM data Copyright (C) 2002-2003 Bo Lincoln <[EMAIL PROTECTED]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * $Id: backup_plugin.c,v 1.9 2003/08/26 20:05:49 lincoln Exp $ */ #include <stdlib.h> #include <glib.h> #include <string.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <time.h> #include <unistd.h> #include <gmodule.h> #include <gtk/gtkmain.h> #include <gtk/gtksignal.h> #include <multisync.h> #include "backup_plugin.h" #include "gui.h" #define BACKUPFILE "backup" void backup_load_state(backup_connection *conn) { char *filename; FILE *f; char line[256]; filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair), (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"), BACKUPFILE); if ((f = fopen(filename, "r"))) { while (fgets(line, 256, f)) { char prop[128], data[256]; if (sscanf(line, "%128s = %256[^\n]", prop, data) == 2) { if (!strcmp(prop, "backupdir")) { conn->backupdir = g_strdup(data); } if (!strcmp(prop, "rebackupall")) { if (!strcmp(data, "yes")) conn->rebackupall = TRUE; else conn->rebackupall = FALSE; } if (!strcmp(prop, "harddelete")) { if (!strcmp(data, "yes")) conn->harddelete = TRUE; else conn->harddelete = FALSE; } } } fclose(f); } g_free(filename); } void backup_save_state(backup_connection *conn) { FILE *f; char *filename; filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair), (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"), BACKUPFILE); if ((f = fopen(filename, "w"))) { if (conn->backupdir) fprintf(f, "backupdir = %s\n", conn->backupdir); fprintf(f, "rebackupall = %s\n", conn->rebackupall?"yes":"no"); fprintf(f, "harddelete = %s\n", conn->harddelete?"yes":"no"); fclose(f); } g_free(filename); } //Return a line of data from a vCARD, vCALENDAR etc. Free the string // using g_free(). char* backup_get_entry_data(char *card, char *key) { char *pos = card; int l = strlen(key); while (pos) { if (!strncmp(pos, key, l) && (pos[l] == ':' || pos[l] == ';')) { char *start, *end; start = strstr(pos+l, ":"); if (start) { start++; end = strstr(start, "\n"); if (!end) end = card+strlen(card); if (*(end-1) == '\r') end--; return(g_strndup(start, end-start)); } } pos = strstr(pos, "\n"); if (pos) pos += 1; } return(NULL); } #define BACKUPENTRYFILE "backup_entries" void backup_load_entries(backup_connection *conn) { char *filename; FILE *f; char line[512]; /* If the backupdir hasn't been configured, skip this function */ if(conn->backupdir == NULL) return; filename = g_strdup_printf ("%s/%s", sync_get_datapath(conn->sync_pair), BACKUPENTRYFILE); if ((f = fopen(filename, "r"))) { while (fgets(line, 512, f)) { char uid[256]; int objtype, mod_time; if (sscanf(line, "%d %u %256s", &objtype, &mod_time, uid) >= 3) { backup_object *entry = g_malloc(sizeof(backup_object)); g_assert(entry); entry->uid = g_strdup(uid); entry->object_type = objtype; entry->mod_time = mod_time; conn->entries = g_list_append(conn->entries, entry); } } fclose(f); } g_free(filename); } void backup_save_entries(backup_connection *conn) { char *filename; FILE *f; filename = g_strdup_printf ("%s/%s", sync_get_datapath(conn->sync_pair), BACKUPENTRYFILE); if ((f = fopen(filename, "w"))) { GList *l; for (l=conn->entries; l; l = l->next) { int changetype; int objtype; backup_object *entry; entry = l->data; objtype = entry->object_type; changetype = entry->type; fprintf(f, "%d %u %s\n", objtype, entry->mod_time, entry->uid); } fclose(f); } g_free(filename); } void backup_free_entries(backup_connection *conn) { while (conn->entries) { GList *entries; backup_object *entry; entries = g_list_first(conn->entries); if (entries->data) { entry = entries->data; if (entry->uid) g_free(entry->uid); g_free(entry); } conn->entries = g_list_remove_link(conn->entries, entries); } } gboolean backup_do_connect(gpointer data) { backup_connection *conn = data; struct stat statbuf; if (!conn->backupdir) { backup_show_msg("Backup plugin: Please set the backup directory\nin the backup options first."); goto err; } if (stat(conn->backupdir, &statbuf) == -1) { if (mkdir(conn->backupdir, 0700)) goto err; } backup_load_entries(conn); sync_set_requestdone(conn->sync_pair); return(FALSE); err: sync_set_requestfailed(conn->sync_pair); return(FALSE); } backup_connection* sync_connect(sync_pair* handle, connection_type type, sync_object_type object_types) { backup_connection *conn; conn = g_malloc0(sizeof(backup_connection)); conn->sync_pair = handle; conn->conntype = type; backup_load_state(conn); g_idle_add(backup_do_connect, conn); return(conn); } void backup_free_connection(backup_connection *conn) { if (conn) { backup_free_entries(conn); if (conn->backupdir) g_free(conn->backupdir); g_free(conn); } } void sync_disconnect(backup_connection *conn) { sync_pair *sync_pair = conn->sync_pair; backup_free_connection(conn); sync_set_requestdone(sync_pair); } typedef struct { backup_connection *conn; sync_object_type newdbs; } backup_get_changes_arg; gboolean backup_do_get_changes(gpointer data) { backup_get_changes_arg *arg = data; backup_connection *conn = arg->conn; int t; GList *changes = NULL; change_info *chinfo; sync_object_type newdbs = arg->newdbs; g_free(arg); if (newdbs) { gboolean ask = FALSE; for (t = 0; t < g_list_length(conn->entries); t++) { backup_object *entry = g_list_nth_data(conn->entries, t); if (newdbs & entry->object_type) ask = TRUE; } if (ask) { if (!backup_show_question("One or more of the other side databases\nseem to have been reset.\nWould you like to restore the data from backup?")) newdbs = 0; } } DIR *d = opendir(conn->backupdir); if (d) { struct dirent *direntry; while (direntry=readdir(d)) { char * fname = g_strdup_printf("%s/%s", conn->backupdir, direntry->d_name); struct stat statbuf; if (!stat(fname, &statbuf)) { if (S_ISREG(statbuf.st_mode)) { /*Try to find the entry for this file*/ backup_object *found_entry=NULL; for (t = 0; t < g_list_length(conn->entries); t++) { backup_object *entry = g_list_nth_data(conn->entries, t); if (strcmp(direntry->d_name, entry->uid) == 0) { found_entry = entry; break; } } /* If no entry was found for this file*/ if (!found_entry) { /* New file found */ printf("BACKUP: New file %s\n", fname); backup_object *entry = g_malloc(sizeof(backup_object)); g_assert(entry); entry->uid = g_strdup(direntry->d_name); entry->mod_time = (int) statbuf.st_mtime; conn->entries = g_list_append(conn->entries, entry); /* Load the changes */ changed_object *change = g_malloc0(sizeof(changed_object)); change->comp = g_malloc0(statbuf.st_size+1); FILE *f; if ((f = fopen(fname, "r"))) { fread(change->comp, 1, statbuf.st_size, f); fclose(f); } if (strncmp(change->comp, "BEGIN:VCALENDAR", 15) == 0 && strlen(change->comp) > 30) { int i; entry->object_type = SYNC_OBJECT_TYPE_UNKNOWN; for (i=15; i<20 && change->comp[i] != 'B'; i++); if (strncmp(change->comp + i, "BEGIN:VEVENT", 12) == 0) entry->object_type = SYNC_OBJECT_TYPE_CALENDAR; if (strncmp(change->comp + i, "BEGIN:VTODO", 11) == 0) entry->object_type = SYNC_OBJECT_TYPE_TODO; } if (strncmp(change->comp, "BEGIN:VCARD", 11) == 0) entry->object_type = SYNC_OBJECT_TYPE_PHONEBOOK; printf("BACKUP: type = %i\n", entry->object_type); change->uid = g_strdup(entry->uid); change->change_type = SYNC_OBJ_ADDED; change->object_type = entry->object_type; changes = g_list_append(changes, change); } else { /* Existing entry, check modification time*/ if (found_entry->mod_time != (int) statbuf.st_mtime) { printf("BACKUP: File modified %s type = %i\n", fname, found_entry->object_type); found_entry->mod_time = (int) statbuf.st_mtime; /* Load the changes */ changed_object *change = g_malloc0(sizeof(changed_object)); FILE *f = NULL; change->comp = g_malloc0(statbuf.st_size+1); if ((f = fopen(fname, "r"))) { fread(change->comp, 1, statbuf.st_size, f); fclose(f); } change->uid = g_strdup(found_entry->uid); change->change_type = SYNC_OBJ_MODIFIED; change->object_type = found_entry->object_type; changes = g_list_append(changes, change); } } } } g_free(fname); } closedir(d); } GList *entries = g_list_first(conn->entries); while(entries) { backup_object *entry = entries->data; entries = g_list_next(entries); if (entry) { char * fname = g_strdup_printf("%s/%s", conn->backupdir, entry->uid); struct stat statbuf; if (stat(fname, &statbuf)) { printf("BACKUP: File deleted %s type = %i\n", fname, entry->object_type); changed_object *change = g_malloc0(sizeof(changed_object)); change->uid = g_strdup(entry->uid); change->change_type = SYNC_OBJ_HARDDELETED; change->object_type = entry->object_type; changes = g_list_append(changes, change); conn->entries = g_list_remove(conn->entries, entry); if (entry->uid) { g_free(entry->uid); g_free(entry); } g_free(fname); } } } printf("BACKUP: Found %i changes.\n", g_list_length(changes)); chinfo = g_malloc0(sizeof(change_info)); chinfo->changes = changes; chinfo->newdbs = 0; sync_set_requestdata(chinfo, conn->sync_pair); return(FALSE); } void get_changes(backup_connection* conn, sync_object_type newdbs) { backup_get_changes_arg *arg; if (conn->rebackupall) { change_info *chinfo = g_malloc0(sizeof(change_info)); chinfo->newdbs = SYNC_OBJECT_TYPE_ANY; sync_set_requestdata(chinfo, conn->sync_pair); backup_free_entries(conn); backup_save_state(conn); return; } arg = g_malloc0(sizeof(backup_get_changes_arg)); arg->conn = conn; arg->newdbs = newdbs; // g_idle_add(backup_do_get_changes, arg); backup_do_get_changes(arg); } void sync_done(backup_connection *conn, gboolean success) { if (success) { int t; for (t = 0; t < g_list_length(conn->entries); t++) { backup_object *entry = g_list_nth_data(conn->entries, t); if (entry && (entry->type == BACKUP_TYPE_RESTORE || entry->type == BACKUP_TYPE_REBACKUP)) entry->type = BACKUP_TYPE_SAVED; } if (conn->rebackupall) { conn->rebackupall = FALSE; backup_save_state(conn); } backup_save_entries(conn); } sync_set_requestdone(conn->sync_pair); } void backup_hard_delete(backup_connection *conn, backup_object *entry) { char *fname; if (entry) { conn->entries = g_list_remove(conn->entries, entry); fname = g_strdup_printf("%s/%s", conn->backupdir, entry->uid); unlink(fname); g_free(fname); if (entry->uid) g_free(entry->uid); g_free(entry); } } void backup_modify_or_delete(backup_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen, gboolean softdelete) { char *luid = NULL; backup_object *entry = NULL; int t; if (!uid && !comp) { sync_set_requestfailed(conn->sync_pair); return; } if (uid) luid = g_strdup(uid); if (!luid) { // Generate a UID int count = 0; while (!luid) { char *fname; struct stat statbuf; luid = g_strdup_printf("multisync%d-%d", (int) time(NULL), count); fname = g_strdup_printf("%s/%s", conn->backupdir, luid); if (!stat(fname, &statbuf)) { count++; g_free(luid); luid = NULL; } g_free(fname); } } for (t = 0; t < g_list_length(conn->entries); t++) { backup_object *e; e = g_list_nth_data(conn->entries, t); if (e->uid && !strcmp(e->uid, luid)) { entry = e; } } if (!entry && uid) { // Non-existing UID given sync_set_requestfailed(conn->sync_pair); return; } if (!entry) { // If no entry is found, add it entry = g_malloc0(sizeof(backup_object)); entry->uid = g_strdup(luid); conn->entries = g_list_append(conn->entries, entry); } entry->object_type = objtype; if (comp) entry->type = BACKUP_TYPE_SAVED; else { entry->type = BACKUP_TYPE_DELETED; } if (conn->harddelete && !comp) backup_hard_delete(conn, entry); backup_save_entries(conn); if (comp) { // Save the data char *fname, *objtypestr; FILE *f; fname = g_strdup_printf("%s/%s", conn->backupdir, luid); //objtypestr = g_strdup_printf("%u\n", entry->object_type); if ((f = fopen(fname, "w"))) { // fputs(objtypestr, f); fputs(comp, f); fclose(f); } struct stat statbuf; if (!stat(fname, &statbuf)) { entry->mod_time = (int) statbuf.st_mtime; } //g_free(objtypestr); g_free(fname); } if (!uid && uidret) { // Return our generated LUID strncpy(uidret, luid, *uidretlen); *uidretlen = strlen(luid); } g_free(luid); sync_set_requestdone(conn->sync_pair); } void syncobj_modify(backup_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen) { backup_modify_or_delete(conn, comp, uid, objtype, uidret, uidretlen, FALSE); } void syncobj_delete(backup_connection *conn, char *uid, sync_object_type objtype, int softdelete) { backup_modify_or_delete(conn, NULL, uid, objtype, NULL, NULL, softdelete); } // Return TRUE if this client does not have to be polled // (i.e. can be constantly connected) gboolean always_connected() { return(TRUE); } char* short_name() { return("backup-plugin"); } char* long_name() { return("Backup"); } // Return the types of objects that this client handle sync_object_type object_types() { return(SYNC_OBJECT_TYPE_ANY); } void plugin_init(void) { } char* plugin_info(void) { return("This plugin keeps a backup of your data. You can manage the stored entries is the plugin options (above)."); } int plugin_API_version(void) { return(3); }
#ifndef BACKUP_PLUGIN_H #define BACKUP_PLUGIN_H #include <multisync.h> typedef enum { BACKUP_TYPE_SAVED = 1, BACKUP_TYPE_DELETED = 2, BACKUP_TYPE_RESTORE = 3, BACKUP_TYPE_REBACKUP = 4 } backup_type; typedef struct { char *uid; backup_type type; sync_object_type object_type; int mod_time; } backup_object; typedef struct { client_connection commondata; sync_pair *sync_pair; connection_type conntype; char *backupdir; GList *entries; // The backup entries we have gboolean rebackupall; gboolean harddelete; } backup_connection; void backup_load_state(backup_connection *conn); void backup_save_state(backup_connection *conn); void backup_load_entries(backup_connection *conn); char* backup_get_entry_data(char *card, char *key); void backup_save_entries(backup_connection *conn); void backup_free_entries(backup_connection *conn); void backup_hard_delete(backup_connection *conn, backup_object *entry); void backup_modify_or_delete(backup_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen, gboolean softdelete); void backup_free_connection(backup_connection *conn); gboolean backup_do_connect(gpointer data); backup_connection* sync_connect(sync_pair* handle, connection_type type, sync_object_type object_types); void sync_disconnect(backup_connection *conn); void sync_done(backup_connection *conn, gboolean success); void syncobj_modify(backup_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen); void syncobj_delete(backup_connection *conn, char *uid, sync_object_type objtype, int softdelete); gboolean backup_do_get_changes(gpointer data); void get_changes(backup_connection* conn, sync_object_type newdbs); gboolean always_connected(void); char* short_name(void); char* long_name(void); sync_object_type object_types(void); void plugin_init(void); char* plugin_info(void); #endif
signature.asc
Description: This is a digitally signed message part
Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________ Multisync-devel mailing list Multisync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/multisync-devel