diff options
author | Damon Chaplin <damon@ximian.com> | 2002-02-09 09:03:32 +0800 |
---|---|---|
committer | Damon Chaplin <damon@src.gnome.org> | 2002-02-09 09:03:32 +0800 |
commit | bf5ad102a4af562b220c3f68360031294cf1e758 (patch) | |
tree | abd7a9b00d8265bc3dcdb79015a887d80185d164 /calendar/importers/icalendar-importer.c | |
parent | 3c81b8f46ccc17cd5e8fe7dd453d99cca756c7f1 (diff) | |
download | gsoc2013-evolution-bf5ad102a4af562b220c3f68360031294cf1e758.tar.gz gsoc2013-evolution-bf5ad102a4af562b220c3f68360031294cf1e758.tar.zst gsoc2013-evolution-bf5ad102a4af562b220c3f68360031294cf1e758.zip |
added vCalendar importer and intelligent GnomeCalendar importer code here,
2002-02-08 Damon Chaplin <damon@ximian.com>
* importers/icalendar-importer.c: added vCalendar importer and
intelligent GnomeCalendar importer code here, as it shares a lot of
code with the iCalendar importer.
NOTE: check_folder_type() needs to be finished at some point.
It needs a new shell Corba call so it can decide whether to import
events or tasks into the folder. Currently it just imports both.
* importers/main.c (importer_factory_fn): create vCalendar importer
or GnomeCalendar importer if required.
* importers/evolution-calendar-importer.h: added declarations for
creating a vCalendar importer and intelligent Gnome Calendar importer.
* importers/Makefile.am: added -DEVOLUTION_SOUNDDIR so the importer
knows what filename to use for audio alarms in vCalendar files.
Added libicalvcal-evolution to LDADD.
* importers/GNOME_Evolution_Calendar_Importer.oaf.in: added vCalendar
importer and intelligent Gnome Calendar importer.
* gui/comp-util.c (cal_comp_util_compare_event_timezones): return TRUE
if the event uses UTC. We don't want to flag all events from Outlook,
which use UTC.
svn path=/trunk/; revision=15628
Diffstat (limited to 'calendar/importers/icalendar-importer.c')
-rw-r--r-- | calendar/importers/icalendar-importer.c | 646 |
1 files changed, 544 insertions, 102 deletions
diff --git a/calendar/importers/icalendar-importer.c b/calendar/importers/icalendar-importer.c index 262c925b74..6a7a6f363f 100644 --- a/calendar/importers/icalendar-importer.c +++ b/calendar/importers/icalendar-importer.c @@ -23,17 +23,42 @@ #include <sys/types.h> #include <fcntl.h> #include <gtk/gtksignal.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkmain.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-util.h> +#include <bonobo/bonobo-control.h> +#include <gal/util/e-unicode-i18n.h> #include <cal-client.h> #include <importer/evolution-importer.h> +#include <importer/evolution-intelligent-importer.h> #include <importer/GNOME_Evolution_Importer.h> +#include "icalvcal.h" #include "evolution-calendar-importer.h" +/* We timeout after 2 minutes, when opening the folders. */ +#define IMPORTER_TIMEOUT_SECONDS 120 + + typedef struct { CalClient *client; + CalClient *tasks_client; EvolutionImporter *importer; icalcomponent *icalcomp; + gboolean folder_contains_events; + gboolean folder_contains_tasks; } ICalImporter; +typedef struct { + gboolean do_calendar; + gboolean do_tasks; +} ICalIntelligentImporter; + +/* + * Functions shared by iCalendar & vCalendar importer. + */ + static void importer_destroy_cb (GtkObject *object, gpointer user_data) { @@ -42,27 +67,27 @@ importer_destroy_cb (GtkObject *object, gpointer user_data) g_return_if_fail (ici != NULL); gtk_object_unref (GTK_OBJECT (ici->client)); + gtk_object_unref (GTK_OBJECT (ici->tasks_client)); if (ici->icalcomp != NULL) icalcomponent_free (ici->icalcomp); g_free (ici); } -static gboolean -support_format_fn (EvolutionImporter *importer, - const char *filename, - void *closure) + +/* This reads in an entire file and returns it. It returns NULL on error. + The returned string should be freed. */ +static char* +read_file (const char *filename) { - int fd; + int fd, n; GString *str; - icalcomponent *icalcomp; - int n; char buffer[2049]; - gboolean ret = TRUE; + gboolean error = FALSE; /* read file contents */ fd = open (filename, O_RDONLY); if (fd == -1) - return FALSE; + return NULL; str = g_string_new (""); while (1) { @@ -70,134 +95,159 @@ support_format_fn (EvolutionImporter *importer, n = read (fd, buffer, sizeof (buffer) - 1); if (n > 0) { str = g_string_append (str, buffer); - } - else if (n == 0) + } else if (n == 0) { break; - else { - ret = FALSE; + } else { + error = TRUE; break; } } close (fd); - /* parse the file */ - if (ret) { - icalcomp = icalparser_parse_string (str->str); - if (icalcomp) - icalcomponent_free (icalcomp); + if (error) { + g_string_free (str, FALSE); + return NULL; + } else { + gchar *retval = str->str; + g_string_free (str, FALSE); + return retval; + } +} + + +/* Returns the URI to load given a folder path. Currently this is just a full + pathname. The returned string should be freed. */ +static char* +get_uri_from_folder_path (const char *folderpath) +{ + const char *name; + char *parent; + + if (folderpath == NULL || *folderpath == '\0') { + return g_strdup_printf ("%s/evolution/local/Calendar/calendar.ics", + g_get_home_dir ()); + } + + name = strrchr (folderpath, '/'); + if (name == NULL || name == folderpath) { + parent = "evolution/local/"; + if (folderpath[0] == '/') + name = folderpath + 1; else - ret = FALSE; + name = folderpath; + } else { + name += 1; + parent = "evolution/local/Calendar/subfolders/"; } - g_string_free (str, TRUE); + return g_strdup_printf ("%s/%s%s/calendar.ics", g_get_home_dir (), + parent, name); +} - return ret; + +/* Determine whether it is a calendar or tasks folder, or both. If it starts + with 'file:' we know it is a local folder so we see if it ends with + calendar.ics or tasks.ics. For remote folders (i.e. Exchange folders at + present), we import both calendar events and tasks into it. */ +static void +check_folder_type (ICalImporter *ici, + const char *folderpath) +{ + ici->folder_contains_events = TRUE; + ici->folder_contains_tasks = TRUE; + + /* FIXME: Finish. */ } -static gboolean -load_file_fn (EvolutionImporter *importer, - const char *filename, - const char *folderpath, - void *closure) + +/* This removes all components except VEVENTs and VTIMEZONEs from the toplevel + icalcomponent, and returns a GList of the VTODO components. */ +static GList* +prepare_events (icalcomponent *icalcomp) { - int fd; - GString *str; - icalcomponent *icalcomp; - int n; - char buffer[2049]; - char *uri_str; - gboolean ret = TRUE; - ICalImporter *ici = (ICalImporter *) closure; + icalcomponent *subcomp, *next_subcomp; + GList *vtodos = NULL; - g_return_val_if_fail (ici != NULL, FALSE); + subcomp = icalcomponent_get_first_component (icalcomp, + ICAL_ANY_COMPONENT); + while (subcomp) { + icalcomponent_kind child_kind = icalcomponent_isa (subcomp); + next_subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT); + if (child_kind != ICAL_VEVENT_COMPONENT + && child_kind != ICAL_VTIMEZONE_COMPONENT) { - if (folderpath == NULL || *folderpath == '\0') - uri_str = g_strdup_printf ("%s/evolution/local/Calendar/calendar.ics", - g_get_home_dir ()); - else { - char *name; - char *parent; - - name = strrchr (folderpath, '/'); - if (name == NULL || name == folderpath) { - parent = g_strdup ("evolution/local/"); - if (folderpath[0] == '/') - name = folderpath + 1; + icalcomponent_remove_component (icalcomp, + subcomp); + if (child_kind == ICAL_VTODO_COMPONENT) + vtodos = g_list_prepend (vtodos, subcomp); else - name = folderpath; - } - else { - name += 1; - parent = g_strdup ("evolution/local/Calendar/subfolders/"); + icalcomponent_free (subcomp); } - uri_str = g_strdup_printf ("%s/%s%s/calendar.ics", g_get_home_dir (), - parent, name); + subcomp = next_subcomp; } - /* read file contents */ - fd = open (filename, O_RDONLY); - if (fd == -1) - return FALSE; + return vtodos; +} - str = g_string_new (""); - while (1) { - memset (buffer, 0, sizeof(buffer)); - n = read (fd, buffer, sizeof (buffer) - 1); - if (n > 0) { - str = g_string_append (str, buffer); - } - else if (n == 0) - break; - else { - ret = FALSE; - break; - } - } - close (fd); +/* This removes all components except VTODOs and VTIMEZONEs from the toplevel + icalcomponent, and adds the given list of VTODO components. The list is + freed afterwards. */ +static void +prepare_tasks (icalcomponent *icalcomp, GList *vtodos) +{ + icalcomponent *subcomp, *next_subcomp; + GList *elem; - /* parse the file */ - if (ret) { - icalcomp = icalparser_parse_string (str->str); - if (icalcomp) { - if (!cal_client_open_calendar (ici->client, uri_str, TRUE)) - ret = FALSE; - else - ici->icalcomp = icalcomp; + subcomp = icalcomponent_get_first_component (icalcomp, + ICAL_ANY_COMPONENT); + while (subcomp) { + icalcomponent_kind child_kind = icalcomponent_isa (subcomp); + next_subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT); + if (child_kind != ICAL_VTODO_COMPONENT + && child_kind != ICAL_VTIMEZONE_COMPONENT) { + icalcomponent_remove_component (icalcomp, subcomp); + icalcomponent_free (subcomp); } - else - ret = FALSE; + subcomp = next_subcomp; } - g_string_free (str, TRUE); - g_free (uri_str); - - return ret; + for (elem = vtodos; elem; elem = elem->next) { + icalcomponent_add_component (icalcomp, elem->data); + } + g_list_free (vtodos); } + static void process_item_fn (EvolutionImporter *importer, CORBA_Object listener, void *closure, CORBA_Environment *ev) { - CalClientLoadState state; + CalClientLoadState state, tasks_state; ICalImporter *ici = (ICalImporter *) closure; + GNOME_Evolution_ImporterListener_ImporterResult result; + + result = GNOME_Evolution_ImporterListener_OK; g_return_if_fail (ici != NULL); g_return_if_fail (IS_CAL_CLIENT (ici->client)); g_return_if_fail (ici->icalcomp != NULL); state = cal_client_get_load_state (ici->client); - if (state == CAL_CLIENT_LOAD_LOADING) { + tasks_state = cal_client_get_load_state (ici->tasks_client); + if (state == CAL_CLIENT_LOAD_LOADING + || tasks_state == CAL_CLIENT_LOAD_LOADING) { GNOME_Evolution_ImporterListener_notifyResult ( listener, GNOME_Evolution_ImporterListener_NOT_READY, TRUE, ev); return; } - else if (state != CAL_CLIENT_LOAD_LOADED) { + else if (state != CAL_CLIENT_LOAD_LOADED + || tasks_state != CAL_CLIENT_LOAD_LOADED) { GNOME_Evolution_ImporterListener_notifyResult ( listener, GNOME_Evolution_ImporterListener_UNSUPPORTED_OPERATION, @@ -205,20 +255,98 @@ process_item_fn (EvolutionImporter *importer, return; } - /* import objects into the given client */ - if (!cal_client_update_objects (ici->client, ici->icalcomp)) { - g_warning ("Could not update objects"); - GNOME_Evolution_ImporterListener_notifyResult ( - listener, - GNOME_Evolution_ImporterListener_BAD_DATA, - FALSE, ev); + /* If the folder contains events & tasks we can just import everything + into it. If it contains just events, we have to strip out the + VTODOs and import them into the default tasks folder. If the folder + contains just tasks, we strip out the VEVENTs, which do not get + imported at all. */ + if (ici->folder_contains_events && ici->folder_contains_tasks) { + if (!cal_client_update_objects (ici->client, ici->icalcomp)) + result = GNOME_Evolution_ImporterListener_BAD_DATA; + } else if (ici->folder_contains_events) { + GList *vtodos = prepare_events (ici->icalcomp); + if (!cal_client_update_objects (ici->client, ici->icalcomp)) + result = GNOME_Evolution_ImporterListener_BAD_DATA; + + prepare_tasks (ici->icalcomp, vtodos); + if (!cal_client_update_objects (ici->tasks_client, + ici->icalcomp)) + result = GNOME_Evolution_ImporterListener_BAD_DATA; + } else { + prepare_tasks (ici->icalcomp, NULL); + if (!cal_client_update_objects (ici->client, ici->icalcomp)) + result = GNOME_Evolution_ImporterListener_BAD_DATA; } - else { - GNOME_Evolution_ImporterListener_notifyResult ( - listener, - GNOME_Evolution_ImporterListener_OK, - FALSE, ev); + + GNOME_Evolution_ImporterListener_notifyResult (listener, result, FALSE, + ev); +} + + +/* + * iCalendar importer functions. + */ + +static gboolean +support_format_fn (EvolutionImporter *importer, + const char *filename, + void *closure) +{ + char *contents; + icalcomponent *icalcomp; + gboolean ret = FALSE; + + contents = read_file (filename); + + /* parse the file */ + if (contents) { + icalcomp = icalparser_parse_string (contents); + if (icalcomp) { + icalcomponent_free (icalcomp); + ret = TRUE; + } + } + + g_free (contents); + + return ret; +} + +static gboolean +load_file_fn (EvolutionImporter *importer, + const char *filename, + const char *folderpath, + void *closure) +{ + char *uri_str, *contents; + gboolean ret = FALSE; + ICalImporter *ici = (ICalImporter *) closure; + + g_return_val_if_fail (ici != NULL, FALSE); + + uri_str = get_uri_from_folder_path (folderpath); + check_folder_type (ici, folderpath); + + contents = read_file (filename); + + /* parse the file */ + if (contents) { + icalcomponent *icalcomp; + + icalcomp = icalparser_parse_string (contents); + if (icalcomp) { + if (cal_client_open_calendar (ici->client, uri_str, TRUE) + && cal_client_open_default_tasks (ici->tasks_client, FALSE)) { + ici->icalcomp = icalcomp; + ret = TRUE; + } + } } + + g_free (contents); + g_free (uri_str); + + return ret; } BonoboObject * @@ -228,6 +356,7 @@ ical_importer_new (void) ici = g_new0 (ICalImporter, 1); ici->client = cal_client_new (); + ici->tasks_client = cal_client_new (); ici->icalcomp = NULL; ici->importer = evolution_importer_new (support_format_fn, load_file_fn, @@ -239,3 +368,316 @@ ical_importer_new (void) return BONOBO_OBJECT (ici->importer); } + + + +/* + * vCalendar importer functions. + */ + +static gboolean +vcal_support_format_fn (EvolutionImporter *importer, + const char *filename, + void *closure) +{ + char *contents; + gboolean ret = FALSE; + + contents = read_file (filename); + + /* parse the file */ + if (contents) { + VObject *vcal; + + vcal = Parse_MIME (contents, strlen (contents)); + + if (vcal) { + icalcomponent *icalcomp; + + icalcomp = icalvcal_convert (vcal); + + if (icalcomp) { + icalcomponent_free (icalcomp); + ret = TRUE; + } + + cleanVObject (vcal); + } + } + + g_free (contents); + + return ret; +} + +/* This tries to load in a vCalendar file and convert it to an icalcomponent. + It returns NULL on failure. */ +static icalcomponent* +load_vcalendar_file (const char *filename) +{ + icalvcal_defaults defaults = { 0 }; + icalcomponent *icalcomp = NULL; + char *contents; + + defaults.alarm_audio_url = "file://" EVOLUTION_SOUNDDIR "/default_alarm.wav"; + defaults.alarm_audio_fmttype = "audio/x-wav"; + defaults.alarm_description = (char*) U_("Reminder!!"); + + contents = read_file (filename); + + /* parse the file */ + if (contents) { + VObject *vcal; + + vcal = Parse_MIME (contents, strlen (contents)); + + if (vcal) { + icalcomp = icalvcal_convert_with_defaults (vcal, + &defaults); + cleanVObject (vcal); + } + } + + g_free (contents); + + return icalcomp; +} + +static gboolean +vcal_load_file_fn (EvolutionImporter *importer, + const char *filename, + const char *folderpath, + void *closure) +{ + char *uri_str; + gboolean ret = FALSE; + ICalImporter *ici = (ICalImporter *) closure; + icalcomponent *icalcomp; + + g_return_val_if_fail (ici != NULL, FALSE); + + uri_str = get_uri_from_folder_path (folderpath); + check_folder_type (ici, folderpath); + + icalcomp = load_vcalendar_file (filename); + if (icalcomp) { + if (cal_client_open_calendar (ici->client, uri_str, TRUE) + && cal_client_open_default_tasks (ici->tasks_client, FALSE)) { + ici->icalcomp = icalcomp; + ret = TRUE; + } + } + + g_free (uri_str); + + return ret; +} + +BonoboObject * +vcal_importer_new (void) +{ + ICalImporter *ici; + + ici = g_new0 (ICalImporter, 1); + ici->client = cal_client_new (); + ici->tasks_client = cal_client_new (); + ici->icalcomp = NULL; + ici->importer = evolution_importer_new (vcal_support_format_fn, + vcal_load_file_fn, + process_item_fn, + NULL, + ici); + gtk_signal_connect (GTK_OBJECT (ici->importer), "destroy", + GTK_SIGNAL_FUNC (importer_destroy_cb), ici); + + return BONOBO_OBJECT (ici->importer); +} + + + + + + +static void +gnome_calendar_importer_destroy_cb (GtkObject *object, gpointer user_data) +{ + ICalIntelligentImporter *ici = (ICalIntelligentImporter *) user_data; + + g_return_if_fail (ici != NULL); + + g_free (ici); +} + + + +static gboolean +gnome_calendar_can_import_fn (EvolutionIntelligentImporter *ii, + void *closure) +{ + char *filename; + gboolean gnome_calendar_exists; + + filename = gnome_util_home_file ("user-cal.vcf"); + gnome_calendar_exists = g_file_exists (filename); + g_free (filename); + + return gnome_calendar_exists; +} + + +static void +gnome_calendar_import_data_fn (EvolutionIntelligentImporter *ii, + void *closure) +{ + ICalIntelligentImporter *ici = closure; + icalcomponent *icalcomp = NULL; + char *filename; + GList *vtodos; + CalClient *calendar_client = NULL, *tasks_client = NULL; + int t; + + /* If neither is selected, just return. */ + if (!ici->do_calendar && !ici->do_tasks) { + return; + } + + /* Try to open the default calendar & tasks folders. */ + if (ici->do_calendar) { + calendar_client = cal_client_new (); + if (!cal_client_open_default_calendar (calendar_client, FALSE)) + goto out; + } + + if (ici->do_tasks) { + tasks_client = cal_client_new (); + if (!cal_client_open_default_tasks (tasks_client, FALSE)) + goto out; + } + + /* Load the Gnome Calendar file and convert to iCalendar. */ + filename = gnome_util_home_file ("user-cal.vcf"); + icalcomp = load_vcalendar_file (filename); + g_free (filename); + + /* If we couldn't load the file, just return. FIXME: Error message? */ + if (!icalcomp) + goto out; + + /* + * Import the calendar events into the default calendar folder. + */ + vtodos = prepare_events (icalcomp); + + /* Wait for client to finish opening the calendar & tasks folders. */ + for (t = 0; t < IMPORTER_TIMEOUT_SECONDS; t++) { + CalClientLoadState calendar_state, tasks_state; + + calendar_state = tasks_state = CAL_CLIENT_LOAD_LOADED; + + /* We need this so the CalClient gets notified that the + folder is opened, via Corba. */ + while (gtk_events_pending ()) + gtk_main_iteration (); + + if (ici->do_calendar) + calendar_state = cal_client_get_load_state (calendar_client); + + if (ici->do_tasks) + tasks_state = cal_client_get_load_state (tasks_client); + + if (calendar_state == CAL_CLIENT_LOAD_LOADED + && tasks_state == CAL_CLIENT_LOAD_LOADED) + break; + + sleep (1); + } + + /* If we timed out, just return. */ + if (t == IMPORTER_TIMEOUT_SECONDS) + goto out; + + /* Import the calendar events. */ + /* FIXME: What do intelligent importers do about errors? */ + if (ici->do_calendar) + cal_client_update_objects (calendar_client, icalcomp); + + + /* + * Import the tasks into the default tasks folder. + */ + prepare_tasks (icalcomp, vtodos); + if (ici->do_tasks) + cal_client_update_objects (tasks_client, icalcomp); + + out: + if (icalcomp) + icalcomponent_free (icalcomp); + if (calendar_client) + gtk_object_unref (GTK_OBJECT (calendar_client)); + if (tasks_client) + gtk_object_unref (GTK_OBJECT (tasks_client)); +} + + +/* Fun with aggregation */ +static void +checkbox_toggle_cb (GtkToggleButton *tb, + gboolean *do_item) +{ + *do_item = gtk_toggle_button_get_active (tb); +} + +static BonoboControl * +create_checkboxes_control (ICalIntelligentImporter *ici) +{ + GtkWidget *hbox, *calendar_checkbox, *tasks_checkbox; + BonoboControl *control; + + hbox = gtk_hbox_new (FALSE, 2); + + calendar_checkbox = gtk_check_button_new_with_label (_("Calendar Events")); + gtk_signal_connect (GTK_OBJECT (calendar_checkbox), "toggled", + GTK_SIGNAL_FUNC (checkbox_toggle_cb), + &ici->do_calendar); + gtk_box_pack_start (GTK_BOX (hbox), calendar_checkbox, + FALSE, FALSE, 0); + + tasks_checkbox = gtk_check_button_new_with_label (_("Tasks")); + gtk_signal_connect (GTK_OBJECT (tasks_checkbox), "toggled", + GTK_SIGNAL_FUNC (checkbox_toggle_cb), + &ici->do_tasks); + gtk_box_pack_start (GTK_BOX (hbox), tasks_checkbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + control = bonobo_control_new (hbox); + return control; +} + +BonoboObject * +gnome_calendar_importer_new (void) +{ + EvolutionIntelligentImporter *importer; + ICalIntelligentImporter *ici; + BonoboControl *control; + char *message = N_("Evolution has found Gnome Calendar files.\n" + "Would you like to import them into Evolution?"); + + ici = g_new0 (ICalIntelligentImporter, 1); + + importer = evolution_intelligent_importer_new (gnome_calendar_can_import_fn, + gnome_calendar_import_data_fn, + _("Gnome Calendar"), + _(message), + ici); + + + gtk_signal_connect (GTK_OBJECT (importer), "destroy", + GTK_SIGNAL_FUNC (gnome_calendar_importer_destroy_cb), ici); + + control = create_checkboxes_control (ici); + bonobo_object_add_interface (BONOBO_OBJECT (importer), + BONOBO_OBJECT (control)); + + return BONOBO_OBJECT (importer); +} |