[evolution-patches] Evolution-iPod Sync EPlugin



Hi,

Just over the weekend i was trying Justin Wakes Evolution-iPod Sync
bounty work, updated that to latest hal apis and made that a eplugin.

This is just a fun work over the weekend. It just works, and of course a
lot more can be done to this in future. Just r-click on the
e-source-list tree, and say Sync to iPod. it works. Strings/errors/etc
needs to be corrected further.

This can work well with 2.4.0 as well. Just need some comments on the
idea. If its ok, we can improve further.


-Srini.


Index: configure.in
===================================================================
RCS file: /cvs/gnome/evolution/configure.in,v
retrieving revision 1.846
diff -u -p -r1.846 configure.in
--- configure.in	8 Sep 2005 10:02:39 -0000	1.846
+++ configure.in	17 Oct 2005 04:33:43 -0000
@@ -1267,7 +1279,7 @@ EVO_SET_COMPILE_FLAGS(LIBSOUP, $LIBSOUP 
 AC_SUBST(LIBSOUP_CFLAGS)
 AC_SUBST(LIBSOUP_LIBS)
 
-EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 libglade-2.0 gnome-vfs-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 gnome-vfs-module-2.0 libgtkhtml-$GTKHTML_PACKAGE >= $GTKHTML_REQUIRED libebook-$EDS_PACKAGE >= $EDS_REQUIRED libecal-$EDS_PACKAGE >= $EDS_REQUIRED libedataserverui-$EDS_PACKAGE >= $EDS_REQUIRED)
+EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 libglade-2.0 gnome-vfs-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 gnome-vfs-module-2.0 libgtkhtml-$GTKHTML_PACKAGE >= $GTKHTML_REQUIRED libebook-$EDS_PACKAGE >= $EDS_REQUIRED libecal-$EDS_PACKAGE >= $EDS_REQUIRED libedataserverui-$EDS_PACKAGE >= $EDS_REQUIRED hal)
 AC_SUBST(EVOLUTION_CALENDAR_CFLAGS)
 AC_SUBST(EVOLUTION_CALENDAR_LIBS)
 
@@ -1414,7 +1426,7 @@ plugins_base="calendar-file calendar-htt
 
 plugins_standard="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task mark-calendar-offline audio-inline mailing-list-actions new-mail-notify default-mailer" 
 
-plugins_experimental="backup-restore folder-unsubscribe mail-to-meeting mail-remote prefer-plain save-attachments"
+plugins_experimental="backup-restore folder-unsubscribe mail-to-meeting mail-remote prefer-plain save-attachments ipod-sync"
 
 case x"$enable_plugins" in
 xno)
@@ -1709,6 +1721,7 @@ plugins/groupwise-account-setup/Makefile
 plugins/groupwise-features/Makefile
 plugins/mail-account-disable/Makefile
 plugins/sa-junk-plugin/Makefile
+plugins/ipod-sync/Makefile
 smime/Makefile
 smime/lib/Makefile
 smime/gui/Makefile
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ evolution-ipod-sync.c	2005-10-17 09:31:33.000000000 +0530
@@ -0,0 +1,84 @@
+/*
+ * evolution-ipod-sync.c - Evolution->Ipod synchronisation
+ *
+ * (C)2004 Justin Wake <jwake iinet net au>
+ *
+ * Licensed under the GNU GPL v2. See COPYING.
+ *
+ */
+
+#include "config.h"
+#include "evolution-ipod-sync.h"
+
+#include <gnome.h>
+#include <glade/glade.h>
+#include <libhal.h>
+
+char *  mount_point = NULL;
+LibHalContext *ctx;
+
+gboolean
+ipod_check_status (gboolean silent)
+{
+	LibHalContext *ctx;
+	DBusConnection *conn;
+	
+	if (check_hal () == FALSE)
+	{
+		if (!silent) {
+			GtkWidget *message = gtk_message_dialog_new_with_markup (
+											NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+											"<span weight=\"bold\" size=\"larger\">"
+											"Hardware Abstraction Layer not loaded"
+											"</span>\n\n"
+											"The \"hald\" service is required but not currently "
+											"running. Please enable the service and rerun this "
+											"program, or contact your system administrator.");
+
+			gtk_dialog_run (GTK_DIALOG (message));
+			gtk_widget_destroy (message);
+		}
+		return FALSE;
+			
+	}
+	
+	conn = dbus_bus_get (DBUS_BUS_SYSTEM, NULL);
+
+	ctx = libhal_ctx_new ();
+	libhal_ctx_set_dbus_connection (ctx, conn);
+	if (!libhal_ctx_init(ctx, NULL))
+		return FALSE;
+
+	
+	mount_point = find_ipod_mount_point (ctx);
+
+	if (mount_point == NULL) {
+		/* Either the iPod wasn't mounted when we started, or
+		 * it wasn't plugged in. Either way, we want to umount
+		 * the iPod when we finish syncing. */
+		if (!silent) {
+			GtkWidget *message = gtk_message_dialog_new_with_markup (
+											NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+											"<span weight=\"bold\" size=\"larger\">"
+											"Search for a iPod failed"
+											"</span>\n\n"
+											"Evolution could not find a iPod to synchronize with."
+											"Either it is not connected to the system or it is "
+											"not powered on.");
+
+			gtk_dialog_run (GTK_DIALOG (message));
+			gtk_widget_destroy (message);
+		}
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+char *
+ipod_get_mount ()
+{
+	return mount_point;
+}
+
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ evolution-ipod-sync.h	2005-10-17 09:32:01.000000000 +0530
@@ -0,0 +1,43 @@
+/*
+ * evolution-ipod-sync.h
+ *
+ * (C)2004 Justin Wake <jwake iinet net au>
+ *
+ * Licensed under the GNU GPL v2. See COPYING.
+ *
+ */
+
+#include "config.h"
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <libhal.h>
+#include <signal.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(String) gettext (String)
+# ifdef gettext_noop
+#  define N_(String) gettext_noop (String)
+# else
+#  define N_(String) (String)
+# endif
+#else
+# define _(String)
+# define N_(String) (String)
+#endif
+
+#ifdef EIS_DEBUG
+# define dbg(fmt,arg...) fprintf(stderr, "%s/%d: " fmt,__FILE__,__LINE__,##arg)
+#else
+# define dbg(fmt,arg...) do { } while(0)
+#endif
+
+#define warn(fmt,arg...) g_warning("%s/%d: " fmt,__FILE__,__LINE__,##arg)
+
+
+gboolean check_hal ();
+
+char *find_ipod_mount_point (LibHalContext *ctx);
+gboolean ipod_check_status (gboolean silent);
+char *ipod_get_mount ();
+
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ format-handler.h	2005-10-12 09:24:29.000000000 +0530
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Authors: Philip Van Hoof <pvanhoof gnome org>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public
+ *  License as published by the Free Software Foundation.
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <libedataserver/e-source.h>
+#include <libedataserverui/e-source-selector.h>
+#include <libecal/e-cal.h>
+#include <calendar/gui/e-cal-popup.h>
+
+typedef struct _FormatHandler FormatHandler;
+
+struct _FormatHandler
+{
+	gboolean isdefault;
+	const gchar *combo_label;
+	const gchar *filename_ext;
+	GtkWidget *options_widget;
+
+	gpointer data;
+
+	void (*save) (FormatHandler *handler, EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri);
+};
+
+FormatHandler *ical_format_handler_new (void);
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ ical-format.c	2005-10-12 09:20:19.000000000 +0530
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Authors: Rodrigo Moya <rodrigo novell com> 
+ *           Philip Van Hoof <pvanhoof gnome org>
+ *           
+ *  Copyright 2004 Novell, Inc. (www.novell.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public
+ *  License as published by the Free Software Foundation.
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <glib.h>
+#include <glib/gi18n.h>
+#ifdef USE_GTKFILECHOOSER
+#  include <gtk/gtkfilechooser.h>
+#  include <gtk/gtkfilechooserdialog.h>
+#else
+#  include <gtk/gtkfilesel.h>
+#endif
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtk.h>
+#include <libedataserver/e-source.h>
+#include <libedataserverui/e-source-selector.h>
+#include <libecal/e-cal.h>
+#include <libecal/e-cal-util.h>
+#include <calendar/gui/e-cal-popup.h>
+#include <calendar/common/authentication.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <string.h>
+
+#include "format-handler.h"
+
+static void
+display_error_message (GtkWidget *parent, const char *message)
+{
+	GtkWidget *dialog;
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, message);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+}
+
+static void
+do_save_calendar_ical (FormatHandler *handler, EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri)
+{
+	ESource *primary_source;
+	ECal *source_client;
+	GError *error = NULL;
+	GList *objects;
+	icalcomponent *top_level = NULL;
+
+	primary_source = e_source_selector_peek_primary_selection (target->selector);
+
+	if (!dest_uri)
+		return;
+
+	/* open source client */
+	source_client = auth_new_cal_from_source (primary_source, type);
+	if (!e_cal_open (source_client, TRUE, &error)) {
+		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error->message);
+		g_object_unref (source_client);
+		g_error_free (error);
+		return;
+	}
+
+	/* create destination file */
+	top_level = e_cal_util_new_top_level ();
+
+	error = NULL;
+	if (e_cal_get_object_list (source_client, "#t", &objects, &error)) {
+		GnomeVFSResult result;
+		GnomeVFSHandle *handle;
+
+		while (objects != NULL) {
+			icalcomponent *icalcomp = objects->data;
+			
+			icalcomponent_add_component (top_level, icalcomp);
+
+			/* remove item from the list */
+			objects = g_list_remove (objects, icalcomp);
+		}
+
+		/* save the file */
+		result = gnome_vfs_open (&handle, dest_uri, GNOME_VFS_OPEN_WRITE);
+		if (result != GNOME_VFS_OK) {
+			if ((result = gnome_vfs_create (&handle, dest_uri, GNOME_VFS_OPEN_WRITE,
+							TRUE, GNOME_VFS_PERM_USER_ALL)) != GNOME_VFS_OK) {
+				display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)),
+					       gnome_vfs_result_to_string (result));
+			}
+		}
+
+		if (result == GNOME_VFS_OK) {
+			char *ical_str;
+			GnomeVFSFileSize bytes_written;
+
+			ical_str = icalcomponent_as_ical_string (top_level);
+			if ((result = gnome_vfs_write (handle, (gconstpointer) ical_str, strlen (ical_str), &bytes_written))
+			    != GNOME_VFS_OK) {
+				display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)),
+						       gnome_vfs_result_to_string (result));
+			}
+
+			gnome_vfs_close (handle);
+		}
+	} else {
+		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error->message);
+		g_error_free (error);
+	}
+
+	/* terminate */
+	g_object_unref (source_client);
+	icalcomponent_free (top_level);
+}
+
+FormatHandler *ical_format_handler_new (void)
+{
+	FormatHandler *handler = g_new (FormatHandler, 1);
+
+	handler->isdefault = TRUE;
+	handler->combo_label = _("iCalendar format (.ics)");
+	handler->filename_ext = ".ics";
+	handler->options_widget = NULL;
+	handler->save = do_save_calendar_ical;
+	handler->data = NULL;
+
+	return handler;
+}
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ ipod.c	2005-10-17 09:32:31.000000000 +0530
@@ -0,0 +1,237 @@
+/*
+ * ipod.c - Find an iPod mount point using HAL
+ *
+ * (C)2004 Justin Wake <jwake iinet net au>
+ *
+ * Licensed under the GNU GPL v2. See COPYING.
+ *
+ */
+
+#include "config.h"
+#include "evolution-ipod-sync.h"
+#include <unistd.h>
+
+/**
+ * Ensure that HAL is running before we try to use it.
+ * From gnome-volume-manager's src/properties.c
+ */
+gboolean
+check_hal (void)
+{
+	LibHalContext *ctx;
+	char **devices;
+	int num;
+	DBusConnection *conn;
+	
+	conn = dbus_bus_get (DBUS_BUS_SYSTEM, NULL);
+
+	ctx = libhal_ctx_new ();
+	libhal_ctx_set_dbus_connection (ctx, conn);
+	if (!libhal_ctx_init(ctx, NULL))
+		return FALSE;
+	devices = libhal_get_all_devices (ctx, &num, NULL);
+	if (!devices)
+	{
+		libhal_ctx_shutdown (ctx, NULL);
+		return FALSE;
+	}
+	libhal_free_string_array (devices);
+
+	libhal_ctx_shutdown (ctx, NULL);
+	return TRUE;
+}
+
+#define MOUNT "/bin/mount"
+#define UMOUNT "/bin/umount"
+
+/**
+ * Try to mount a given device.
+ */
+static gboolean
+try_mount (char *device)
+{
+	char *argv[3];
+	GError *err = NULL;
+	gint exit_status;
+
+	argv[0] = MOUNT;
+	argv[1] = device;
+	argv[2] = NULL;
+
+	if (!g_spawn_sync (g_get_home_dir (), argv, NULL, 0, NULL, NULL, NULL,
+							 NULL, &exit_status, &err))
+	{
+		warn ("try_mount failed: %s", err->message);
+		return FALSE;
+	}
+
+	return (exit_status == 0);
+}
+
+/**
+ * Try to unmount a given device.
+ */
+gboolean
+try_umount (char *device)
+{
+	char *argv[3];
+	GError *err = NULL;
+	gint exit_status;
+
+	argv[0] = UMOUNT;
+	argv[1] = device;
+	argv[2] = NULL;
+
+	sync ();
+
+	if (!g_spawn_sync (g_get_home_dir (), argv, NULL, 0, NULL, NULL, NULL,
+							 NULL, &exit_status, &err))
+	{
+		warn ("try_umount failed: %s", err->message);
+		return FALSE;
+	}
+
+	return (exit_status == 0);
+}
+
+/**
+ * See if a given mount point contains an iPod.
+ *
+ * Do this by checking for the presence of an iTunes
+ * database at <mount_point>/iPod_Control/iTunes/.
+ */
+static gboolean
+is_ipod (char *mount_point)
+{
+	gboolean ret = FALSE;
+	
+	char *itunes_path;
+
+	itunes_path = g_build_path (G_DIR_SEPARATOR_S, mount_point,
+										 "iPod_Control", "iTunes",
+										 NULL);
+
+	if (!g_file_test (itunes_path, G_FILE_TEST_IS_DIR))
+		goto out;
+	
+	ret = TRUE;
+
+out:
+	g_free (itunes_path);
+	return ret;
+}
+
+/**
+ * Try to find a mount point for an iPod.
+ */
+char *
+find_ipod_mount_point (LibHalContext *ctx)
+{
+	char **apple_devices = NULL;
+	char **volumes = NULL;
+	char *udi, *udi2, *device, *fsusage, *mount_point = NULL;
+	char *retval = NULL;
+	int apple_count = 0;
+	int volume_count = 0;
+	int has_fs = 0;
+	int i, j;
+
+	/* First, we look for things made by Apple. */
+	apple_devices = libhal_manager_find_device_string_match (ctx,
+							"info.vendor",
+							"Apple",
+							&apple_count,
+							NULL);
+
+	for (i = 0; i < apple_count; i++)
+	{
+		udi = apple_devices[i];
+		
+		volumes = NULL;
+		volumes = libhal_manager_find_device_string_match (ctx,
+							"info.parent",
+							udi,
+							&volume_count,
+							NULL);
+
+		for (j = 0; j < volume_count; j++)
+		{
+			udi2 = volumes[j];
+
+			/* Only interested if it has a filesystem. */
+			has_fs = 0;
+			
+			if (!libhal_device_property_exists (ctx, udi2,
+								"volume.is_filesystem", NULL) ||
+				  !libhal_device_get_property_bool (ctx, udi2,
+				  				"volume.is_filesystem", NULL))
+			{
+				has_fs = 1;
+			}
+			
+			fsusage = libhal_device_get_property_string (ctx, udi2,
+				 				"volume.fsusage", NULL);
+
+			if (strncmp (fsusage, "filesystem", 10) == 0)
+			{
+				has_fs = 1;
+			}
+
+			libhal_free_string (fsusage);
+
+			if (has_fs == 0)
+				continue;
+				
+			device = libhal_device_get_property_string (ctx, udi2,
+									"block.device", NULL);
+
+			/* Let's see if it's mounted. */
+			if (!libhal_device_property_exists (ctx, udi2,
+								"volume.is_mounted", NULL) ||
+				 !libhal_device_get_property_bool (ctx, udi2,
+				 				"volume.is_mounted", NULL))
+			{
+				/* It isn't, so let's attempt to mount it */
+				if (device != NULL)
+				{
+					try_mount (device);	
+				}
+			}
+
+			if (!libhal_device_property_exists (ctx, udi2,
+								"volume.mount_point", NULL))
+			{
+				libhal_free_string (device);
+				continue;
+			}
+
+			libhal_free_string (device);
+			
+			mount_point = libhal_device_get_property_string (ctx, udi2,
+											"volume.mount_point", NULL);
+			
+			if (is_ipod (mount_point))
+			{
+				goto out;
+			}
+
+			libhal_free_string (mount_point);
+			mount_point = NULL;
+		}
+	}
+
+out:
+	if (volumes != NULL)
+		libhal_free_string_array (volumes);
+	
+	if (apple_devices != NULL)
+		libhal_free_string_array (apple_devices);
+	
+	if (mount_point != NULL)
+	{
+		retval = g_strdup (mount_point);
+		libhal_free_string (mount_point);
+	}
+	
+	return (retval);
+}
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ ipod-sync.c	2005-10-17 09:35:36.000000000 +0530
@@ -0,0 +1,186 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Authors: Srinivasa Ragavan <sragavan novell com>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public
+ *  License as published by the Free Software Foundation.
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* This is written from the save-calendar plugin and Justin Wakes evo-iPod
+ * sync code.
+ *
+ * This provides eplugin support to sync calendar/task/addressbook with iPod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libebook/e-book.h>
+#include <libebook/e-contact.h>
+
+#include <libedataserver/e-source.h>
+#include <libedataserverui/e-source-selector.h>
+#include <libecal/e-cal.h>
+#include <calendar/gui/e-cal-popup.h>
+#include <addressbook/gui/widgets/eab-popup.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <string.h>
+
+#include "format-handler.h"
+#include "evolution-ipod-sync.h"
+
+void org_gnome_sync_calendar (EPlugin *ep, ECalPopupTargetSource *target);
+void org_gnome_sync_tasks (EPlugin *ep, ECalPopupTargetSource *target);
+void org_gnome_sync_addressbook (EPlugin *ep, EABPopupTargetSource *target);
+
+
+static void
+display_error_message (GtkWidget *parent, const char *message)
+{
+	GtkWidget *dialog;
+
+	dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, message);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+}
+
+static void
+destination_save_addressbook  (EPlugin *ep, EABPopupTargetSource *target)
+{
+	EBook *book;
+	EBookQuery *query;
+	GList *contacts, *tmp;
+	ESource *primary_source;
+	gchar *uri;
+	char *dest_uri = NULL;
+	GnomeVFSResult result;
+	GnomeVFSHandle *handle;
+	char *mount = ipod_get_mount();
+
+	/* use g_file api here to build path*/
+	dest_uri = g_strdup_printf("%s/%s/%s", mount, "Contacts", "evolution.vcf");
+	g_free (mount);
+	
+	primary_source = e_source_selector_peek_primary_selection (target->selector);
+	uri = e_source_get_uri (primary_source);
+	
+	book = e_book_new_from_uri (uri, NULL);
+
+	if (!book
+	    || !e_book_open (book, TRUE, NULL)) {
+		g_warning ("Couldn't load addressbook %s", uri);
+		return;
+	}
+
+	/* Let us export some meaning full contacts */
+	query = e_book_query_any_field_contains ("");
+	e_book_get_contacts (book, query, &contacts, NULL);
+	e_book_query_unref (query);
+
+	result = gnome_vfs_open (&handle, dest_uri, GNOME_VFS_OPEN_WRITE);
+
+	if (result != GNOME_VFS_OK) {
+		if ((result = gnome_vfs_create (&handle, dest_uri, GNOME_VFS_OPEN_WRITE,
+						TRUE, GNOME_VFS_PERM_USER_ALL)) != GNOME_VFS_OK) {
+			display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)),
+				       gnome_vfs_result_to_string (result));
+		}
+	}
+
+	if (result == GNOME_VFS_OK) {
+		GnomeVFSFileSize bytes_written;
+
+		for (tmp = contacts; tmp; tmp=tmp->next) {
+				EContact *contact = tmp->data;
+				gchar *temp = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+				gchar *vcard;
+				
+				vcard = g_strconcat(temp, "\r\n", NULL);
+				if ((result = gnome_vfs_write (handle, (gconstpointer) vcard, strlen (vcard), &bytes_written))
+				    != GNOME_VFS_OK) {
+					display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)),
+							       gnome_vfs_result_to_string (result));
+				}
+
+				g_object_unref (contact);
+				g_free (temp);
+				g_free (vcard);
+		}
+	}
+
+	sync();
+
+	if (contacts != NULL)
+		g_list_free (contacts);
+
+	gnome_vfs_close (handle);
+	g_object_unref (book);
+	g_free (dest_uri);
+	g_free (uri);
+}
+
+static void 
+destination_save_cal (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type)
+{
+	FormatHandler *handler = NULL;
+	char *mount = ipod_get_mount();
+	char *dest_uri = NULL;
+
+	/* The available formathandlers */
+	handler= ical_format_handler_new ();
+
+	dest_uri = g_strdup_printf("%s/%s/%s", mount, "Calendars", (type==E_CAL_SOURCE_TYPE_EVENT)? "evolution-calendar.ics":"evolution-todo.ics");
+
+	handler->save (handler, ep, target, type, dest_uri);
+
+	sync();
+	
+	g_free (dest_uri);
+	g_free (mount);
+	g_free (handler);
+}
+
+void
+org_gnome_sync_calendar (EPlugin *ep, ECalPopupTargetSource *target)
+{
+	if (!ipod_check_status(FALSE))
+			return;
+
+	destination_save_cal (ep, target, E_CAL_SOURCE_TYPE_EVENT);
+}
+
+void
+org_gnome_sync_tasks (EPlugin *ep, ECalPopupTargetSource *target)
+{
+	if (!ipod_check_status(FALSE))
+			return;
+	
+	destination_save_cal (ep, target, E_CAL_SOURCE_TYPE_TODO);
+}
+
+
+void
+org_gnome_sync_addressbook (EPlugin *ep, EABPopupTargetSource *target)
+{
+	if (!ipod_check_status(FALSE))
+			return;
+
+	destination_save_addressbook (ep, target);
+}
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ Makefile.am	2005-10-15 13:15:32.000000000 +0530
@@ -0,0 +1,26 @@
+INCLUDES =						\
+	-I$(top_srcdir)					\
+	$(EVOLUTION_CALENDAR_CFLAGS)			\
+	-DDBUS_API_SUBJECT_TO_CHANGE
+
+ EVO_PLUGIN_RULE@
+
+plugin_DATA = org-gnome-ipod-sync-evolution.eplug
+plugin_LTLIBRARIES = liborg-gnome-ipod-sync-evolution.la
+
+liborg_gnome_ipod_sync_evolution_la_SOURCES = \
+	ipod-sync.c			\
+	ical-format.c			\
+	evolution-ipod-sync.c		\
+	ipod.c				\
+	format-handler.h
+
+liborg_gnome_ipod_sync_evolution_la_LDFLAGS = -module -avoid-version -lhal
+liborg_gnome_mark_all_read_la_LIBADD = 			\
+	$(EVOLUTION_CALENDAR_LIBS)
+
+
+EXTRA_DIST = org-gnome-ipod-sync-evolution.eplug.xml
+
+BUILT_SOURCES = $(plugin_DATA)
+CLEANFILES = $(BUILT_SOURCES)
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ org-gnome-ipod-sync-evolution.eplug.xml	2005-10-17 09:43:02.000000000 +0530
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<e-plugin-list>
+    <e-plugin id="org.gnome.evolution.ipod_sync" type="shlib" 
+	_name="iPod Synchronization"
+	    location="@PLUGINDIR@/liborg-gnome-ipod-sync-evolution.so"> 
+	    <author name="Srinivasa Ragavan" email="sragavan novell com"/>
+	    <_description>Synchronize the selected task/calendar/addressbook with Apple iPod</_description>
+	
+		<hook class="org.gnome.evolution.calendar.popup:1.0">
+			<menu id="org.gnome.evolution.tasks.source.popup" target="source">
+				<item type="item" path="20.sync_ipod.00." _label="Synchronize to iPod" icon="gnome-dev-ipod" activate="org_gnome_sync_tasks"/>
+			</menu>
+			<menu id="org.gnome.evolution.calendar.source.popup" target="source">
+				<item type="item" path="20.sync_ipod.00" _label="Synchronize to iPod" icon="gnome-dev-ipod" activate="org_gnome_sync_calendar"/>
+			</menu>
+		</hook>
+		<hook class="org.gnome.evolution.addressbook.popup:1.0">
+			<menu id="org.gnome.evolution.addressbook.source.popup" target="source">
+				<item type="item" path="20.sync_ipod.00." _label="Synchronize to iPod" icon="gnome-dev-ipod" activate="org_gnome_sync_addressbook"/>
+			</menu>
+		</hook>
+	</e-plugin>
+</e-plugin-list>
--- /dev/null	2005-10-17 13:27:23.204438750 +0530
+++ sync.c	2005-10-12 09:44:56.000000000 +0530
@@ -0,0 +1,414 @@
+/*
+ * sync.c
+ *
+ * (C)2004 Justin Wake <jwake iinet net au>
+ *
+ * Licensed under the GNU GPL v2. See COPYING.
+ *
+ */
+
+#include "config.h"
+#include "evolution-ipod-sync.h"
+#include <gnome.h>
+
+#include <libebook/e-book.h>
+#include <libebook/e-contact.h>
+#include <libecal/e-cal.h>
+#include <libical/ical.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define EBOOK_SOURCE_LIST "/apps/evolution/addressbook/sources"
+#define ECAL_SOURCE_LIST "/apps/evolution/calendar/sources"
+#define ETASK_SOURCE_LIST "/apps/evolution/tasks/sources"
+
+extern GtkWidget *progress_bar;
+extern IPod ipod_info;
+
+static void pulse (void)
+{
+	gtk_progress_bar_pulse (GTK_PROGRESS_BAR (progress_bar));
+	g_main_context_iteration (NULL, FALSE);
+}
+
+/**
+ * Something bad happened.
+ */
+static void error_dialog (char *title, char *error)
+{
+	GtkWidget *error_dlg = 
+			gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+											"<span weight=\"bold\" size=\"larger\">"
+											"%s</span>\n\n%s.", title, error);
+	
+	gtk_dialog_set_has_separator (GTK_DIALOG (error_dlg), FALSE);
+	gtk_container_set_border_width (GTK_CONTAINER (error_dlg), 5);
+	gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (error_dlg)->label),
+									  TRUE);
+	gtk_dialog_set_default_response (GTK_DIALOG (error_dlg),
+												GTK_RESPONSE_OK);
+	
+	gtk_dialog_run (GTK_DIALOG (error_dlg));
+	gtk_widget_destroy (error_dlg);
+}
+
+/**
+ * Something really bad happened.
+ */
+static void critical_error (char *title, char *error)
+{
+	error_dialog (title, error);
+	gtk_main_quit ();
+	exit (EXIT_FAILURE);
+}
+
+static GSList *
+get_source_uris_for_type (char *key)
+{
+	ESourceList *sources;
+	GSList		*groups;
+	GSList		*uris = NULL;
+	GSList		*item, *source;
+	sources = e_source_list_new_for_gconf_default (key);
+	groups = e_source_list_peek_groups (sources);
+
+	for (item = groups; item != NULL; item = item->next)
+	{
+		ESourceGroup *group;
+
+		g_assert (item->data != NULL);
+
+		group = E_SOURCE_GROUP (item->data);
+		for (source = e_source_group_peek_sources(group);
+			  source != NULL;
+			  source = source->next)
+		{
+			gchar *uri;
+			g_assert (source->data != NULL);
+			uri = e_source_get_uri (E_SOURCE (source->data));
+			uris = g_slist_append (uris, uri);
+		}
+	}
+
+	g_object_unref (sources);
+
+	return uris;
+}
+
+static void
+free_uri_list (GSList *uris)
+{
+	g_slist_foreach (uris, (GFunc)g_free, NULL);
+	g_slist_free (uris);
+}
+
+/**
+ * Force the data into little-endian output.
+ *
+ * Note: data must be of even length.
+ */
+static void
+force_little_endian (gunichar2 *data, int length)
+{
+	int i;
+	
+	/* We're big-endian? 
+	   (A little tidier than before) */
+	if (G_BYTE_ORDER == G_BIG_ENDIAN)
+	{
+		for (i = 0; i < length; i++)
+		{
+			gunichar2 c = data[i];
+
+			c = ((((guint16)(c) & 0xFF00) >> 8) |
+				  (((guint16)(c) & 0x00FF) << 8));
+
+			data[i] = c;
+		}
+	}	
+}
+
+/**
+ * Write a string of data to a file on the iPod.
+ *
+ * Will return if the write worked, otherwise will
+ * display an error dialog and end the program.
+ */
+static void
+write_to_ipod (GString *str, char *path, char *filename)
+{
+	char *output_path;
+	char *output_file;
+	FILE *f;
+	guchar		*utf8;
+	gunichar2	*utf16;
+	guchar		bom[2] = {0xFF, 0xFE};
+	int			i, count;
+	
+	output_path = g_build_path (G_DIR_SEPARATOR_S,
+										ipod_info.mount_point,
+										path, NULL);
+
+	if (!g_file_test (output_path, G_FILE_TEST_IS_DIR))
+	{
+		if (mkdir (output_path, 0777) != 0)
+			critical_error (_("No output directory!"),
+								 _("The output directory was not found on "
+								 	"iPod! Please ensure that iPod has been correctly "
+									"set up and try again."));
+	}
+
+	output_file = g_build_filename (output_path, filename, NULL);
+
+	g_free (output_path);
+	
+	f = fopen (output_file, "w");
+
+	g_free (output_file);
+	
+	if (f == NULL)
+	{
+		critical_error (_("Could not export data!"), strerror (errno));
+	}
+
+	/* Convert the input string into UTF16 */
+	utf8 = str->str;
+	if (g_utf8_validate (utf8, -1, NULL))
+	{
+		utf16 = g_utf8_to_utf16 (utf8, -1, NULL, NULL, NULL);
+		
+		/* Swap the bytes if we're big-endian so that the output
+		 * remains little-endian according to the BOM. */
+		force_little_endian (utf16, g_utf8_strlen (utf8, -1));
+	}
+	
+	count = 2 * g_utf8_strlen (utf8, -1);
+	
+	/* Write the BOM 
+	 * 0xFF 0xFE
+	 * UTF-16 Little Endian
+	 */
+	for (i = 0; i < 2; i++)
+		fwrite (&bom[i], 1, 1, f);
+	
+	if ((fwrite(utf16, count, 1, f) != 1) &&
+		 (count > 0))
+	{	
+		g_free (utf16);
+		fclose (f);
+		critical_error (_("Could not export data!"),
+							 _("Exporting data failed."));
+	}
+
+	g_free (utf16);
+	fclose (f);
+}
+
+static GString *
+uri_list_to_vcard_string (GSList *uris)
+{
+	GString 		*str = NULL;
+	EBook 		*book = NULL;
+	EBookQuery 	*qry = NULL;
+	GList			*contacts = NULL, *c = NULL;
+	GSList		*uri;
+
+	qry = e_book_query_field_exists (E_CONTACT_FILE_AS);
+	
+	str = g_string_new (NULL);
+	
+	for (uri = uris; uri != NULL; uri = uri->next)
+	{
+		g_assert (uri->data != NULL);
+
+		book = e_book_new_from_uri (uri->data, NULL);
+	
+		if (e_book_open (book, TRUE, NULL) == FALSE)
+		{
+			error_dialog (_("Could not open addressbook!"),
+							  _("Could not open the Evolution addressbook to export data."));
+
+			/* Maybe the next one will work. */
+			continue;
+		}
+
+		if (e_book_get_contacts (book, qry, &contacts, NULL) == FALSE)
+		{
+			/* Looks like this one is empty. */
+			g_object_unref (book);
+			continue;
+		}
+
+		/* Loop through the contacts, adding them to the string. */
+		for (c = contacts; c != NULL; c = c->next)
+		{
+			gchar *tmp;
+			EContact *contact = E_CONTACT (c->data);
+			
+			tmp = e_vcard_to_string (E_VCARD (contact),
+											 EVC_FORMAT_VCARD_30);
+
+			g_string_append (str, tmp);
+			g_string_append (str, "\r\n");
+			g_free (tmp);
+			g_object_unref (contact);
+		}
+
+		if (contacts != NULL)
+			g_list_free (contacts);
+		
+		g_object_unref (book);
+	}
+
+	/* Okay, all done. */
+	e_book_query_unref (qry);
+
+	return (str);
+}
+
+static GString *
+uri_list_to_vcal_string (GSList *uris, ECalSourceType type)
+{
+	GString 		*str = NULL;
+	ECal 			*cal = NULL;
+	icalcomponent *obj = NULL;
+	GList			*objects = NULL, *o = NULL;
+	GSList		*uri;
+	
+	str = g_string_new (NULL);
+
+	for (uri = uris; uri != NULL; uri = uri->next)
+	{
+		g_assert (uri->data != NULL);
+
+		cal = e_cal_new_from_uri (uri->data, type);
+		
+		if (e_cal_open (cal, TRUE, NULL) == FALSE)
+		{
+			error_dialog (_("Could not open calendar/todo!"),
+							  _("Could not open the Evolution calendar/todo list to export data."));
+
+			/* Maybe the next one will work. */
+			continue;
+		}
+
+		
+		e_cal_get_object_list (cal, "#t", &objects, NULL);
+
+		for (o = objects; o != NULL; o = o->next)
+		{
+			gchar *tmp;
+			icalcomponent *comp;
+		
+			g_assert (o->data != NULL);
+
+			comp = o->data;
+			tmp = e_cal_get_component_as_string (cal, comp);
+			g_string_append (str, tmp);
+			g_free (tmp);
+		}
+		
+		g_object_unref (cal);
+	}
+
+	/* Okay, all done. */
+
+	return (str);
+}
+
+/* Attempt to export the addressbook. */
+static void
+export_addressbook (void)
+{
+	GSList *uris;
+	GString *data;
+	pulse ();
+	
+	uris = get_source_uris_for_type (EBOOK_SOURCE_LIST);
+
+	pulse ();
+	
+	data = uri_list_to_vcard_string (uris);
+
+	write_to_ipod (data, "/Contacts/", "evolution.vcf");
+	
+	g_string_free (data, TRUE);
+
+	pulse ();
+
+	free_uri_list (uris);
+
+	pulse ();
+}
+
+/* Attempt to export the calendar(s). */
+static void
+export_calendar (void)
+{
+	GSList *uris;
+	GString *data;
+
+	pulse ();
+
+	uris = get_source_uris_for_type (ECAL_SOURCE_LIST);
+
+	pulse ();
+
+	data = uri_list_to_vcal_string (uris, E_CAL_SOURCE_TYPE_EVENT);
+
+	write_to_ipod (data, "/Calendars/", "evolution-cal.ics");
+	
+	g_string_free (data, TRUE);
+
+	free_uri_list (uris);
+	
+	pulse ();
+}
+
+/* Attempt to export the task list(s). */
+static void
+export_tasks (void)
+{
+	GSList *uris;
+	GString *data;
+
+	pulse ();
+
+	uris = get_source_uris_for_type (ETASK_SOURCE_LIST);
+
+	pulse ();
+
+	data = uri_list_to_vcal_string (uris, E_CAL_SOURCE_TYPE_TODO);
+
+	write_to_ipod (data, "/Calendars/", "evolution-todo.ics");
+
+	g_string_free (data, TRUE);
+
+	free_uri_list (uris);
+	
+	pulse ();
+}
+
+void
+export_to_ipod (void)
+{
+	pulse ();
+	
+	if (ipod_info.addressbook == TRUE)
+		export_addressbook ();
+
+	if (ipod_info.calendar == TRUE)
+		export_calendar ();
+	
+	if (ipod_info.tasks == TRUE)
+		export_tasks ();
+
+	pulse ();
+	sync ();
+	pulse ();
+	return;
+}
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]