/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
* Copyright (C) 2004 Justin Wake <jwake@iinet.net.au>
*
*/
#include "config.h"
#include "evolution-ipod-sync.h"
#include <gtk/gtk.h>
#include <glib/gi18n-lib.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"
#define EMEMO_SOURCE_LIST "/apps/evolution/memos/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 (gchar *title, gchar *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 (gchar *title, gchar *error)
{
error_dialog (title, error);
gtk_main_quit ();
exit (EXIT_FAILURE);
}
static GSList *
get_source_uris_for_type (gchar *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, gint length)
{
gint 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, gchar *path, gchar *filename)
{
gchar *output_path;
gchar *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!"), g_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 ();
}
/* Attempt to export the memo list(s). */
static void
export_memos (void)
{
GSList *uris;
GString *data;
pulse ();
uris = get_source_uris_for_type (EMEMO_SOURCE_LIST);
pulse ();
data = uri_list_to_vcal_string (uris, E_CAL_SOURCE_TYPE_JOURNAL);
write_to_ipod (data, "/Calendars/", "evolution-memo.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 ();
if (ipod_info.memos == TRUE)
export_memos ();
pulse ();
sync ();
pulse ();
return;
}