/* * Evolution CSV and TAB importer * * 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 * * * Authors: * Devashish Sharma * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "e-util/e-import.h" #include "evolution-addressbook-importers.h" #define NOMAP -1 #define EVOLUTION_IMPORTER 3 #define MOZILLA_IMPORTER 2 #define OUTLOOK_IMPORTER 1 #define CSV_FILE_DELIMITER ',' #define TAB_FILE_DELIMITER '\t' typedef struct { EImport *import; EImportTarget *target; guint idle_id; gint state; FILE *file; gulong size; gint count; /* gint -> gint -- Column index in the CSV * file to an index in the known fields array. */ GHashTable *fields_map; EBook *book; GSList *contacts; } CSVImporter; static gint importer; static gchar delimiter; static void csv_import_done (CSVImporter *gci); typedef struct { const gchar *csv_attribute; EContactField contact_field; #define FLAG_HOME_ADDRESS 0x01 #define FLAG_WORK_ADDRESS 0x02 #define FLAG_OTHER_ADDRESS 0x04 #define FLAG_STREET 0x08 #define FLAG_CITY 0x10 #define FLAG_STATE 0x20 #define FLAG_POSTAL_CODE 0x40 #define FLAG_COUNTRY 0x80 #define FLAG_POBOX 0x70 #define FLAG_DATE_BDAY 0x03 #define FLAG_BIRTH_DAY 0x05 #define FLAG_BIRTH_YEAR 0x07 #define FLAG_BIRTH_MONTH 0x50 #define FLAG_DATE_ANNIVERSARY 0x30 #define FLAG_INVALID 0xff gint flags; }import_fields; static import_fields csv_fields_outlook[] = { {"Title", NOMAP}, {"First Name", E_CONTACT_GIVEN_NAME}, {"Middle Name", NOMAP}, {"Last Name", E_CONTACT_FAMILY_NAME}, {"Suffix", NOMAP}, {"Company", E_CONTACT_ORG}, {"Department", E_CONTACT_ORG_UNIT}, {"Job Title", E_CONTACT_TITLE}, {"Business Street", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET }, {"Business Street 2", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET }, {"Business Street 3", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET}, {"Business City", NOMAP, FLAG_WORK_ADDRESS|FLAG_CITY}, {"Business State", NOMAP, FLAG_WORK_ADDRESS|FLAG_STATE}, {"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS|FLAG_POSTAL_CODE}, {"Business Country", NOMAP, FLAG_WORK_ADDRESS|FLAG_COUNTRY}, {"Home Street", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home Street 2", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home Street 3", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home City", NOMAP, FLAG_HOME_ADDRESS|FLAG_CITY}, {"Home State", NOMAP, FLAG_HOME_ADDRESS|FLAG_STATE}, {"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS|FLAG_POSTAL_CODE}, {"Home Country", NOMAP, FLAG_HOME_ADDRESS|FLAG_COUNTRY}, {"Other Street", NOMAP, FLAG_OTHER_ADDRESS|FLAG_STREET}, {"Other Street 2", NOMAP, FLAG_OTHER_ADDRESS|FLAG_STREET}, {"Other Street 3", NOMAP, FLAG_OTHER_ADDRESS|FLAG_STREET}, {"Other City", NOMAP, FLAG_OTHER_ADDRESS|FLAG_CITY}, {"Other State", NOMAP, FLAG_OTHER_ADDRESS|FLAG_STATE}, {"Other Postal Code", NOMAP, FLAG_OTHER_ADDRESS|FLAG_POSTAL_CODE}, {"Other Country", NOMAP, FLAG_OTHER_ADDRESS|FLAG_COUNTRY}, {"Assistant's Phone", E_CONTACT_PHONE_ASSISTANT}, {"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX}, {"Business Phone", E_CONTACT_PHONE_BUSINESS}, {"Business Phone 2", E_CONTACT_PHONE_BUSINESS_2}, {"Callback", E_CONTACT_PHONE_CALLBACK}, {"Car Phone", E_CONTACT_PHONE_CAR}, {"Company Main Phone", E_CONTACT_PHONE_COMPANY}, {"Home Fax", E_CONTACT_PHONE_HOME_FAX}, {"Home Phone", E_CONTACT_PHONE_HOME}, {"Home Phone 2", E_CONTACT_PHONE_HOME_2}, {"ISDN", E_CONTACT_PHONE_ISDN}, {"Mobile Phone", E_CONTACT_PHONE_MOBILE}, {"Other Fax", E_CONTACT_PHONE_OTHER_FAX}, {"Other Phone", E_CONTACT_PHONE_OTHER}, {"Pager", E_CONTACT_PHONE_PAGER}, {"Primary Phone", E_CONTACT_PHONE_PRIMARY}, {"Radio Phone", E_CONTACT_PHONE_RADIO}, {"TTY/TDD Phone", E_CONTACT_PHONE_TTYTDD}, {"Telex", E_CONTACT_PHONE_TELEX}, {"Account", NOMAP}, {"Anniversary", NOMAP, FLAG_DATE_ANNIVERSARY}, {"Assistant's Name", E_CONTACT_ASSISTANT}, {"Billing Information", NOMAP}, {"Birthday", NOMAP, FLAG_DATE_BDAY}, {"Business Address PO Box", NOMAP, FLAG_WORK_ADDRESS|FLAG_POBOX}, {"Categories", E_CONTACT_CATEGORIES}, {"Children", NOMAP}, {"Directory Server", NOMAP}, {"E-mail Address", E_CONTACT_EMAIL_1}, {"E-mail Type", NOMAP}, {"E-mail Display Name", NOMAP}, {"E-mail 2 Address", E_CONTACT_EMAIL_2}, {"E-mail 2 Type", NOMAP}, {"E-mail 2 Display Name", NOMAP}, {"E-mail 3 Address", E_CONTACT_EMAIL_3}, {"E-mail 3 Type", NOMAP}, {"E-mail 3 Display Name", NOMAP}, {"Gender", NOMAP}, {"Government ID Number", NOMAP}, {"Hobby", NOMAP}, {"Home Address PO Box", NOMAP, FLAG_HOME_ADDRESS|FLAG_POBOX}, {"Initials", NOMAP}, {"Internet FREE/BUSY", E_CONTACT_FREEBUSY_URL}, {"Keywords", NOMAP}, {"Language", NOMAP}, {"Location", NOMAP}, {"Managers Name", E_CONTACT_MANAGER}, {"Mileage", NOMAP}, {"Notes", NOMAP}, {"Office Location", NOMAP}, {"Organizational ID Number", NOMAP}, {"Other Address PO Box", NOMAP, FLAG_OTHER_ADDRESS|FLAG_POBOX}, {"Priority", NOMAP}, {"Private", NOMAP}, {"Profession", NOMAP}, {"Referred By", NOMAP}, {"Senstivity", NOMAP}, {"Spouse", E_CONTACT_SPOUSE}, {"User 1", NOMAP}, {"User 2", NOMAP}, {"User 3", NOMAP}, {"User 4", NOMAP}, {"Web Page", E_CONTACT_HOMEPAGE_URL}, }; static import_fields csv_fields_mozilla[] = { {"First Name", E_CONTACT_GIVEN_NAME}, {"Last Name", E_CONTACT_FAMILY_NAME}, {"Display Name", NOMAP}, {"NickName", E_CONTACT_NICKNAME}, {"E-mail Address", E_CONTACT_EMAIL_1}, {"E-mail 2 Address", E_CONTACT_EMAIL_2}, {"Business Phone", E_CONTACT_PHONE_BUSINESS}, {"Home Phone", E_CONTACT_PHONE_HOME}, {"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX}, {"Pager", E_CONTACT_PHONE_PAGER}, {"Mobile Phone", E_CONTACT_PHONE_MOBILE}, {"Home Street", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home Street 2", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home City", NOMAP, FLAG_HOME_ADDRESS|FLAG_CITY}, {"Home State", NOMAP, FLAG_HOME_ADDRESS|FLAG_STATE}, {"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS|FLAG_POSTAL_CODE}, {"Home Country", NOMAP, FLAG_HOME_ADDRESS|FLAG_COUNTRY}, {"Business Street", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET }, {"Business Street 2", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET }, {"Business City", NOMAP, FLAG_WORK_ADDRESS|FLAG_CITY}, {"Business State", NOMAP, FLAG_WORK_ADDRESS|FLAG_STATE}, {"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS|FLAG_POSTAL_CODE}, {"Business Country", NOMAP, FLAG_WORK_ADDRESS|FLAG_COUNTRY}, {"Job Title", E_CONTACT_TITLE}, {"Department", E_CONTACT_ORG_UNIT}, {"Company", E_CONTACT_ORG}, {"Web Page", E_CONTACT_HOMEPAGE_URL}, {"Home Web Page", NOMAP}, {"Birth Year", NOMAP, FLAG_BIRTH_YEAR}, {"Birth Month", NOMAP,FLAG_BIRTH_MONTH}, {"Birth Day", NOMAP, FLAG_BIRTH_DAY}, {"Custom 1", NOMAP}, {"Custom 2", NOMAP}, {"Custom 3", NOMAP}, {"Custom 4", NOMAP}, {"Notes", NOMAP}, }; static import_fields csv_fields_evolution[] = { {"First Name", E_CONTACT_GIVEN_NAME}, {"Last Name", E_CONTACT_FAMILY_NAME}, {"id", NOMAP, FLAG_INVALID}, {"NickName", E_CONTACT_NICKNAME}, {"E-mail Address", E_CONTACT_EMAIL_1}, {"E-mail 2 Address", E_CONTACT_EMAIL_2}, {"E-mail 3 Address", E_CONTACT_EMAIL_3}, {"E-mail 4 Address", E_CONTACT_EMAIL_4}, {"Wants HTML", E_CONTACT_WANTS_HTML}, {"Business Phone", E_CONTACT_PHONE_BUSINESS}, {"Home Phone", E_CONTACT_PHONE_HOME}, {"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX}, {"Pager", E_CONTACT_PHONE_PAGER}, {"Mobile Phone", E_CONTACT_PHONE_MOBILE}, {"Home Street", NOMAP, FLAG_HOME_ADDRESS|FLAG_STREET}, {"Home Street 2", NOMAP, FLAG_INVALID}, {"Home City", NOMAP, FLAG_HOME_ADDRESS|FLAG_CITY}, {"Home State", NOMAP, FLAG_HOME_ADDRESS|FLAG_STATE}, {"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS|FLAG_POSTAL_CODE}, {"Home Country", NOMAP, FLAG_HOME_ADDRESS|FLAG_COUNTRY}, {"Business Street", NOMAP, FLAG_WORK_ADDRESS|FLAG_STREET }, {"Business Street 2", NOMAP, FLAG_INVALID }, {"Business City", NOMAP, FLAG_WORK_ADDRESS|FLAG_CITY}, {"Business State", NOMAP, FLAG_WORK_ADDRESS|FLAG_STATE}, {"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS|FLAG_POSTAL_CODE}, {"Business Country", NOMAP, FLAG_WORK_ADDRESS|FLAG_COUNTRY}, {"Job Title", E_CONTACT_TITLE}, {"Office", E_CONTACT_OFFICE}, {"Company", E_CONTACT_ORG}, {"Web Page", E_CONTACT_HOMEPAGE_URL}, {"Cal uri", E_CONTACT_CALENDAR_URI}, {"Birth Year", NOMAP, FLAG_BIRTH_YEAR}, {"Birth Month", NOMAP,FLAG_BIRTH_MONTH}, {"Birth Day", NOMAP, FLAG_BIRTH_DAY}, {"Notes", E_CONTACT_NOTE}, }; static void add_to_notes (EContact *contact, const gchar *field_text, gchar *val) { GString *new_text; if (!field_text || !val || !*val) return; new_text = g_string_new (e_contact_get_const (contact, E_CONTACT_NOTE)); if (strlen (new_text->str) != 0) new_text = g_string_append_c (new_text, '\n'); new_text = g_string_append (new_text, field_text); new_text = g_string_append_c (new_text, ':'); new_text = g_string_append (new_text, val); e_contact_set (contact, E_CONTACT_NOTE, new_text->str); g_string_free (new_text, TRUE); } /* @str: a date string in the format MM-DD-YYYY or MMDDYYYY */ static EContactDate* date_from_string (const gchar *str) { EContactDate* date; gint i = 0; g_return_val_if_fail (str != NULL, NULL); date = e_contact_date_new (); if (g_ascii_isdigit (str[i]) && g_ascii_isdigit (str[i+1])) { date->month = str[i] * 10 + str[i+1] - '0' * 11; i = i+3; } else { date->month = str[i] - '0' * 1; i = i+2; } if (g_ascii_isdigit (str[i]) && g_ascii_isdigit (str[i+1])) { date->day = str[i] * 10 + str[i+1] - '0' * 11; i = i+3; } else { date->day = str[i] - '0' * 1; i = i+2; } date->year = str[i] * 1000 + str[i+1] * 100 + str[i+2] * 10 + str[i+3] - '0' * 1111; return date; } static GString * parseNextValue (const gchar **pptr) { GString *value; const gchar *ptr = *pptr; g_return_val_if_fail (pptr != NULL, NULL); g_return_val_if_fail (*pptr != NULL, NULL); if (!*ptr || *ptr == '\n') return NULL; value = g_string_new (""); while (*ptr != delimiter) { if (*ptr == '\n') break; if (*ptr != '"') { g_string_append_unichar (value, g_utf8_get_char (ptr)); } else { ptr = g_utf8_next_char (ptr); while (*ptr && *ptr != '"') { g_string_append_unichar (value, g_utf8_get_char (ptr)); ptr = g_utf8_next_char (ptr); } if (!*ptr) break; } ptr = g_utf8_next_char (ptr); } if (*ptr != 0 && *ptr != '\n') ptr = g_utf8_next_char (ptr); *pptr = ptr; return value; } static GHashTable * map_fields (const gchar *header_line, gint pimporter) { import_fields *fields_array = NULL; gint n_fields = -1, idx, j; GString *value; GHashTable *fmap; const gchar *pptr = header_line; gboolean any_found = FALSE; if (pimporter == OUTLOOK_IMPORTER) { fields_array = csv_fields_outlook; n_fields = G_N_ELEMENTS (csv_fields_outlook); } else if (pimporter == EVOLUTION_IMPORTER) { fields_array = csv_fields_evolution; n_fields = G_N_ELEMENTS (csv_fields_evolution); } g_return_val_if_fail (fields_array != NULL, NULL); g_return_val_if_fail (n_fields > 0, NULL); fmap = g_hash_table_new (g_direct_hash, g_direct_equal); idx = 0; while (value = parseNextValue (&pptr), value != NULL) { for (j = 0; j < n_fields; j++) { if (g_ascii_strcasecmp (fields_array[j].csv_attribute, value->str) == 0) { g_hash_table_insert (fmap, GINT_TO_POINTER (idx), GINT_TO_POINTER (j + 1)); any_found = TRUE; break; } } if (j >= n_fields) g_hash_table_insert (fmap, GINT_TO_POINTER (idx), GINT_TO_POINTER (-1)); g_string_free (value, TRUE); idx++; } if (!any_found) { /* column names not in English? */ g_hash_table_destroy (fmap); fmap = NULL; } else { /* also add last index, to be always skipped */ g_hash_table_insert (fmap, GINT_TO_POINTER (idx), GINT_TO_POINTER (-1)); } return fmap; } static gboolean parseLine (CSVImporter *gci, EContact *contact, gchar *buf) { const gchar *pptr = buf, *field_text; gchar *do_free = NULL; GString *value; gint ii = 0, idx; gint flags = 0; gint contact_field; EContactAddress *home_address = NULL, *work_address = NULL, *other_address = NULL; EContactDate *bday = NULL; GString *home_street, *work_street, *other_street; home_street = g_string_new(""); work_street = g_string_new(""); other_street = g_string_new(""); home_address = g_new0 (EContactAddress, 1); work_address = g_new0 (EContactAddress, 1); other_address = g_new0 (EContactAddress, 1); bday = g_new0 (EContactDate, 1); if (!g_utf8_validate (pptr, -1, NULL)) { do_free = g_convert (pptr, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); pptr = do_free; } while (value = parseNextValue (&pptr), value != NULL) { contact_field = NOMAP; flags = FLAG_INVALID; field_text = NULL; idx = ii; if (gci->fields_map) { gpointer found = g_hash_table_lookup (gci->fields_map, GINT_TO_POINTER (idx)); if (!found) { g_warning ("%s: No map for index %d, skipping it", G_STRFUNC, idx); idx = -1; } else { idx = GPOINTER_TO_INT (found) - 1; } } if (importer == OUTLOOK_IMPORTER) { if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_outlook)) { contact_field = csv_fields_outlook[idx].contact_field; flags = csv_fields_outlook[idx].flags; field_text = csv_fields_outlook[idx].csv_attribute; } } else if (importer == MOZILLA_IMPORTER) { if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_mozilla)) { contact_field = csv_fields_mozilla[idx].contact_field; flags = csv_fields_mozilla[idx].flags; field_text = csv_fields_mozilla[idx].csv_attribute; } } else { if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_evolution)) { contact_field = csv_fields_evolution[idx].contact_field; flags = csv_fields_evolution[idx].flags; field_text = csv_fields_evolution[idx].csv_attribute; } } if (*value->str) { if (contact_field != NOMAP) { if (importer == OUTLOOK_IMPORTER || importer == MOZILLA_IMPORTER) { e_contact_set (contact, contact_field, value->str); } else { if (contact_field == E_CONTACT_WANTS_HTML) e_contact_set ( contact, contact_field, GINT_TO_POINTER ( g_ascii_strcasecmp ( value->str, "TRUE") == 0)); else e_contact_set (contact, contact_field, value->str); } } else { switch (flags) { case FLAG_HOME_ADDRESS|FLAG_STREET: if (strlen (home_street->str) != 0) { home_street = g_string_append(home_street, ",\n"); } home_street = g_string_append (home_street, value->str); break; case FLAG_HOME_ADDRESS|FLAG_CITY: home_address->locality = g_strdup (value->str); break; case FLAG_HOME_ADDRESS|FLAG_STATE: home_address->region = g_strdup (value->str); break; case FLAG_HOME_ADDRESS|FLAG_POSTAL_CODE: home_address->code = g_strdup (value->str); break; case FLAG_HOME_ADDRESS|FLAG_POBOX: home_address->po = g_strdup (value->str); break; case FLAG_HOME_ADDRESS|FLAG_COUNTRY: home_address->country = g_strdup (value->str); break; case FLAG_WORK_ADDRESS|FLAG_STREET: if (strlen (work_street->str) != 0) { work_street = g_string_append(work_street, ",\n"); } work_street = g_string_append (work_street, value->str); break; case FLAG_WORK_ADDRESS|FLAG_CITY: work_address->locality = g_strdup (value->str); break; case FLAG_WORK_ADDRESS|FLAG_STATE: work_address->region = g_strdup (value->str); break; case FLAG_WORK_ADDRESS|FLAG_POSTAL_CODE: work_address->code = g_strdup (value->str); break; case FLAG_WORK_ADDRESS|FLAG_POBOX: work_address->po = g_strdup (value->str); break; case FLAG_WORK_ADDRESS|FLAG_COUNTRY: work_address->country = g_strdup (value->str); break; case FLAG_OTHER_ADDRESS|FLAG_STREET: if (strlen (other_street->str) != 0) { other_street = g_string_append(other_street, ",\n"); } other_street = g_string_append (other_street, value->str); break; case FLAG_OTHER_ADDRESS|FLAG_CITY: other_address->locality = g_strdup (value->str); break; case FLAG_OTHER_ADDRESS|FLAG_STATE: other_address->region = g_strdup (value->str); break; case FLAG_OTHER_ADDRESS|FLAG_POSTAL_CODE: other_address->code = g_strdup (value->str); break; case FLAG_OTHER_ADDRESS|FLAG_POBOX: other_address->po = g_strdup (value->str); break; case FLAG_OTHER_ADDRESS|FLAG_COUNTRY: other_address->country = g_strdup (value->str); break; case FLAG_DATE_BDAY: e_contact_set ( contact, E_CONTACT_BIRTH_DATE, date_from_string (value->str)); break; case FLAG_DATE_ANNIVERSARY: e_contact_set ( contact, E_CONTACT_ANNIVERSARY, date_from_string (value->str)); break; case FLAG_BIRTH_DAY: bday->day = atoi (value->str); break; case FLAG_BIRTH_YEAR: bday->year = atoi (value->str); break; case FLAG_BIRTH_MONTH: bday->month = atoi (value->str); break; case FLAG_INVALID: break; default: add_to_notes (contact, field_text, value->str); } } } ii++; g_string_free (value, TRUE); } if (strlen (home_street->str) != 0) home_address->street = g_strdup (home_street->str); if (strlen (work_street->str) != 0) work_address->street = g_strdup (work_street->str); if (strlen (other_street->str) != 0) other_address->street = g_strdup (other_street->str); g_string_free (home_street, TRUE); g_string_free (work_street, TRUE); g_string_free (other_street, TRUE); if (home_address->locality || home_address->country || home_address->code || home_address->region || home_address->street) e_contact_set (contact, E_CONTACT_ADDRESS_HOME, home_address); if (work_address->locality || work_address->country || work_address->code || work_address->region || work_address->street) e_contact_set (contact, E_CONTACT_ADDRESS_WORK, work_address); if (other_address->locality || other_address->country || other_address->code || other_address->region || other_address->street) e_contact_set (contact, E_CONTACT_ADDRESS_OTHER, other_address); if (importer != OUTLOOK_IMPORTER) { if (bday->day || bday->year || bday->month) e_contact_set (contact, E_CONTACT_BIRTH_DATE, bday); } g_free (do_free); return TRUE; } static EContact * getNextCSVEntry (CSVImporter *gci, FILE *f) { EContact *contact = NULL; GString *line; GString *str; gchar *buf; gchar c; line = g_string_new(""); while (1) { c = fgetc (f); if (c == EOF) return NULL; if (c == '\n') { g_string_append_c (line, c); break; } if (c == '"') { g_string_append_c (line, c); c = fgetc (f); while (!feof (f) && c != '"') { g_string_append_c (line, c); c = fgetc (f); } g_string_append_c (line, c); } else g_string_append_c (line, c); } if (gci->count == 0 && importer != MOZILLA_IMPORTER) { gci->fields_map = map_fields (line->str, importer); g_string_free (line, TRUE); line = g_string_new(""); while (1) { c = fgetc (f); if (c == EOF) return NULL; if (c == '\n') { g_string_append_c (line, c); break; } if (c == '"') { g_string_append_c (line, c); c = fgetc (f); while (!feof (f) && c != '"') { g_string_append_c (line, c); c = fgetc (f); } g_string_append_c (line, c); } else g_string_append_c (line, c); } gci->count++; } str = g_string_new(""); str = g_string_append (str, line->str); g_string_free (line, TRUE); if (strlen (str->str) == 0) { g_string_free (str, TRUE); return NULL; } contact = e_contact_new (); buf = str->str; if (!parseLine (gci, contact, buf)) { g_object_unref (contact); return NULL; } gci->count++; g_string_free (str, TRUE); return contact; } static gboolean csv_import_contacts (gpointer d) { CSVImporter *gci = d; EContact *contact = NULL; while ((contact = getNextCSVEntry (gci, gci->file))) { e_book_add_contact (gci->book, contact, NULL); gci->contacts = g_slist_prepend (gci->contacts, contact); } if (contact == NULL) { gci->state = 1; } if (gci->state == 1) { csv_import_done (gci); return FALSE; } else { e_import_status ( gci->import, gci->target, _("Importing..."), ftell (gci->file) * 100 / gci->size); return TRUE; } } static void primary_selection_changed_cb (ESourceSelector *selector, EImportTarget *target) { g_datalist_set_data_full(&target->data, "csv-source", g_object_ref (e_source_selector_get_primary_selection (selector)), g_object_unref); } static GtkWidget * csv_getwidget (EImport *ei, EImportTarget *target, EImportImporter *im) { GtkWidget *vbox, *selector; ESource *primary; ESourceList *source_list; /* FIXME Better error handling */ if (!e_book_get_addressbooks (&source_list, NULL)) return NULL; vbox = gtk_vbox_new (FALSE, FALSE); selector = e_source_selector_new (source_list); e_source_selector_show_selection (E_SOURCE_SELECTOR (selector), FALSE); gtk_box_pack_start (GTK_BOX (vbox), selector, FALSE, TRUE, 6); primary = g_datalist_get_data(&target->data, "csv-source"); if (primary == NULL) { primary = e_source_list_peek_source_any (source_list); g_object_ref (primary); g_datalist_set_data_full(&target->data, "csv-source", primary, g_object_unref); } e_source_selector_set_primary_selection (E_SOURCE_SELECTOR (selector), primary); g_object_unref (source_list); g_signal_connect ( selector, "primary_selection_changed", G_CALLBACK (primary_selection_changed_cb), target); gtk_widget_show_all (vbox); return vbox; } static const gchar *supported_extensions[4] = { ".csv", ".tab" , ".txt", NULL }; static gboolean csv_supported (EImport *ei, EImportTarget *target, EImportImporter *im) { gchar *ext; gint i; EImportTargetURI *s; if (target->type != E_IMPORT_TARGET_URI) return FALSE; s = (EImportTargetURI *) target; if (s->uri_src == NULL) return TRUE; if (strncmp(s->uri_src, "file:///", 8) != 0) return FALSE; ext = strrchr (s->uri_src, '.'); if (ext == NULL) return FALSE; for (i = 0; supported_extensions[i] != NULL; i++) { if (g_ascii_strcasecmp (supported_extensions[i], ext) == 0) { if (i == 0) { delimiter = CSV_FILE_DELIMITER; } else { delimiter = TAB_FILE_DELIMITER; } return TRUE; } } return FALSE; } static void csv_import_done (CSVImporter *gci) { if (gci->idle_id) g_source_remove (gci->idle_id); fclose (gci->file); g_object_unref (gci->book); g_slist_foreach (gci->contacts, (GFunc) g_object_unref, NULL); g_slist_free (gci->contacts); if (gci->fields_map) g_hash_table_destroy (gci->fields_map); e_import_complete (gci->import, gci->target); g_object_unref (gci->import); g_free (gci); } static void book_loaded_cb (ESource *source, GAsyncResult *result, CSVImporter *gci) { gci->book = e_load_book_source_finish (source, result, NULL); if (gci->book == NULL) { csv_import_done (gci); return; } gci->idle_id = g_idle_add (csv_import_contacts, gci); } static void csv_import (EImport *ei, EImportTarget *target, EImportImporter *im) { CSVImporter *gci; ESource *source; gchar *filename; FILE *file; EImportTargetURI *s = (EImportTargetURI *) target; filename = g_filename_from_uri (s->uri_src, NULL, NULL); if (filename == NULL) { g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src); return; } file = g_fopen (filename, "r"); g_free (filename); if (file == NULL) { g_message("Can't open .csv file"); e_import_complete (ei, target); return; } gci = g_malloc0 (sizeof (*gci)); g_datalist_set_data(&target->data, "csv-data", gci); gci->import = g_object_ref (ei); gci->target = target; gci->file = file; gci->fields_map = NULL; gci->count = 0; fseek (file, 0, SEEK_END); gci->size = ftell (file); fseek (file, 0, SEEK_SET); source = g_datalist_get_data (&target->data, "csv-source"); e_load_book_source_async ( source, NULL, NULL, (GAsyncReadyCallback) book_loaded_cb, gci); } static void outlook_csv_import (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = OUTLOOK_IMPORTER; csv_import (ei, target, im); } static void mozilla_csv_import (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = MOZILLA_IMPORTER; csv_import (ei, target, im); } static void evolution_csv_import (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = EVOLUTION_IMPORTER; csv_import (ei, target, im); } static void csv_cancel (EImport *ei, EImportTarget *target, EImportImporter *im) { CSVImporter *gci = g_datalist_get_data(&target->data, "csv-data"); if (gci) gci->state = 1; } static GtkWidget * csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im) { GtkWidget *preview; GList *contacts = NULL; EContact *contact; EImportTargetURI *s = (EImportTargetURI *) target; gchar *filename; FILE *file; CSVImporter *gci; filename = g_filename_from_uri (s->uri_src, NULL, NULL); if (filename == NULL) { g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src); return NULL; } file = g_fopen (filename, "r"); g_free (filename); if (file == NULL) { g_message (G_STRLOC ": Can't open .csv file"); return NULL; } gci = g_malloc0 (sizeof (*gci)); gci->file = file; gci->fields_map = NULL; gci->count = 0; fseek (file, 0, SEEK_END); gci->size = ftell (file); fseek (file, 0, SEEK_SET); while (contact = getNextCSVEntry (gci, gci->file), contact != NULL) { contacts = g_list_prepend (contacts, contact); } contacts = g_list_reverse (contacts); preview = evolution_contact_importer_get_preview_widget (contacts); g_list_foreach (contacts, (GFunc) g_object_unref, NULL); g_list_free (contacts); fclose (file); g_free (gci); return preview; } static GtkWidget * outlook_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = OUTLOOK_IMPORTER; return csv_get_preview (ei, target, im); } static GtkWidget * mozilla_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = MOZILLA_IMPORTER; return csv_get_preview (ei, target, im); } static GtkWidget * evolution_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im) { importer = EVOLUTION_IMPORTER; return csv_get_preview (ei, target, im); } static EImportImporter csv_outlook_importer = { E_IMPORT_TARGET_URI, 0, csv_supported, csv_getwidget, outlook_csv_import, csv_cancel, outlook_csv_get_preview, }; static EImportImporter csv_mozilla_importer = { E_IMPORT_TARGET_URI, 0, csv_supported, csv_getwidget, mozilla_csv_import, csv_cancel, mozilla_csv_get_preview, }; static EImportImporter csv_evolution_importer = { E_IMPORT_TARGET_URI, 0, csv_supported, csv_getwidget, evolution_csv_import, csv_cancel, evolution_csv_get_preview, }; EImportImporter * evolution_csv_outlook_importer_peek (void) { csv_outlook_importer.name = _("Outlook CSV or Tab (.csv, .tab)"); csv_outlook_importer.description = _("Outlook CSV and Tab Importer"); return &csv_outlook_importer; } EImportImporter * evolution_csv_mozilla_importer_peek (void) { csv_mozilla_importer.name = _("Mozilla CSV or Tab (.csv, .tab)"); csv_mozilla_importer.description = _("Mozilla CSV and Tab Importer"); return &csv_mozilla_importer; } EImportImporter * evolution_csv_evolution_importer_peek (void) { csv_evolution_importer.name = _("Evolution CSV or Tab (.csv, .tab)"); csv_evolution_importer.description = _("Evolution CSV and Tab Importer"); return &csv_evolution_importer; }