aboutsummaryrefslogblamecommitdiffstats
path: root/plugins/ipod-sync/sync.c
blob: b826dca5f38e9027fa842f6ac63c50e96b8eb990 (plain) (tree)








































                                                                 
                              


                                                                                                                                
 





                                                                                                                 
 






























































                                                                         

                            











                                                                  
         

















                                                        
 















                                                                                                                           
 


                                     
 









                                                                               
 



                                                                        
 
                                             

                        




                                          
 

                                                
         



















                                                                                      
 
                                  
 




                                                             
 




















                                                                                                                         
 










                                                                                                              
 
















                                                           
 






                                                           
 








                                                                                                                                
 





                                                                  
 






                                                                        
 














                                        
 


                                                            
 


                                                            
 
























                                                                       
 


                                   
 






















                                                                      
 






                     
 




                                          
 








                                    
/*
 * 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;
}