evolution-3.6.4/addressbook/importers/evolution-csv-importer.c

No issues found

   1 /*
   2  * Evolution CSV and TAB importer
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *		Devashish Sharma <sdevashish@novell.com>
  20  *
  21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  22  *
  23  */
  24 
  25 #ifdef HAVE_CONFIG_H
  26 #include <config.h>
  27 #endif
  28 
  29 #include <stdio.h>
  30 #include <ctype.h>
  31 #include <string.h>
  32 
  33 #include <gtk/gtk.h>
  34 #include <glib/gi18n.h>
  35 #include <glib/gstdio.h>
  36 
  37 #include <libebook/libebook.h>
  38 #include <libedataserverui/libedataserverui.h>
  39 
  40 #include <shell/e-shell.h>
  41 #include <e-util/e-import.h>
  42 
  43 #include "evolution-addressbook-importers.h"
  44 
  45 #define NOMAP -1
  46 #define EVOLUTION_IMPORTER 3
  47 #define MOZILLA_IMPORTER 2
  48 #define OUTLOOK_IMPORTER 1
  49 #define CSV_FILE_DELIMITER ','
  50 #define TAB_FILE_DELIMITER '\t'
  51 
  52 typedef struct {
  53 	EImport *import;
  54 	EImportTarget *target;
  55 
  56 	guint idle_id;
  57 
  58 	gint state;
  59 	FILE *file;
  60 	gulong size;
  61 	gint count;
  62 
  63 	/* gint -> gint -- Column index in the CSV
  64 	 * file to an index in the known fields array. */
  65 	GHashTable *fields_map;
  66 
  67 	EBookClient *book_client;
  68 	GSList *contacts;
  69 } CSVImporter;
  70 
  71 static gint importer;
  72 static gchar delimiter;
  73 
  74 static void csv_import_done (CSVImporter *gci);
  75 
  76 typedef struct {
  77 	const gchar *csv_attribute;
  78 	EContactField contact_field;
  79 #define FLAG_HOME_ADDRESS  0x01
  80 #define FLAG_WORK_ADDRESS  0x02
  81 #define FLAG_OTHER_ADDRESS 0x04
  82 #define FLAG_STREET        0x08
  83 #define FLAG_CITY          0x10
  84 #define FLAG_STATE	   0x20
  85 #define FLAG_POSTAL_CODE   0x40
  86 #define FLAG_COUNTRY       0x80
  87 #define FLAG_POBOX         0x70
  88 #define FLAG_DATE_BDAY     0x03
  89 #define FLAG_BIRTH_DAY	   0x05
  90 #define FLAG_BIRTH_YEAR    0x07
  91 #define FLAG_BIRTH_MONTH   0x50
  92 #define FLAG_DATE_ANNIVERSARY 0x30
  93 #define FLAG_INVALID       0xff
  94 	gint flags;
  95 }import_fields;
  96 
  97 static import_fields csv_fields_outlook[] = {
  98 	{"Title", NOMAP},
  99 	{"First Name", E_CONTACT_GIVEN_NAME},
 100 	{"Middle Name", NOMAP},
 101 	{"Last Name", E_CONTACT_FAMILY_NAME},
 102 	{"Suffix", NOMAP},
 103 	{"Company", E_CONTACT_ORG},
 104 	{"Department", E_CONTACT_ORG_UNIT},
 105 	{"Job Title", E_CONTACT_TITLE},
 106 	{"Business Street", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET },
 107 	{"Business Street 2", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET },
 108 	{"Business Street 3", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET},
 109 	{"Business City", NOMAP, FLAG_WORK_ADDRESS | FLAG_CITY},
 110 	{"Business State", NOMAP, FLAG_WORK_ADDRESS | FLAG_STATE},
 111 	{"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS | FLAG_POSTAL_CODE},
 112 	{"Business Country", NOMAP, FLAG_WORK_ADDRESS | FLAG_COUNTRY},
 113 	{"Home Street", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 114 	{"Home Street 2", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 115 	{"Home Street 3", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 116 	{"Home City", NOMAP, FLAG_HOME_ADDRESS | FLAG_CITY},
 117 	{"Home State", NOMAP, FLAG_HOME_ADDRESS | FLAG_STATE},
 118 	{"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS | FLAG_POSTAL_CODE},
 119 	{"Home Country", NOMAP, FLAG_HOME_ADDRESS | FLAG_COUNTRY},
 120 	{"Other Street", NOMAP, FLAG_OTHER_ADDRESS | FLAG_STREET},
 121 	{"Other Street 2", NOMAP, FLAG_OTHER_ADDRESS | FLAG_STREET},
 122 	{"Other Street 3", NOMAP, FLAG_OTHER_ADDRESS | FLAG_STREET},
 123 	{"Other City", NOMAP, FLAG_OTHER_ADDRESS | FLAG_CITY},
 124 	{"Other State", NOMAP, FLAG_OTHER_ADDRESS | FLAG_STATE},
 125 	{"Other Postal Code", NOMAP, FLAG_OTHER_ADDRESS | FLAG_POSTAL_CODE},
 126 	{"Other Country", NOMAP, FLAG_OTHER_ADDRESS | FLAG_COUNTRY},
 127 	{"Assistant's Phone", E_CONTACT_PHONE_ASSISTANT},
 128 	{"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX},
 129 	{"Business Phone", E_CONTACT_PHONE_BUSINESS},
 130 	{"Business Phone 2", E_CONTACT_PHONE_BUSINESS_2},
 131 	{"Callback", E_CONTACT_PHONE_CALLBACK},
 132 	{"Car Phone", E_CONTACT_PHONE_CAR},
 133 	{"Company Main Phone", E_CONTACT_PHONE_COMPANY},
 134 	{"Home Fax", E_CONTACT_PHONE_HOME_FAX},
 135 	{"Home Phone", E_CONTACT_PHONE_HOME},
 136 	{"Home Phone 2", E_CONTACT_PHONE_HOME_2},
 137 	{"ISDN", E_CONTACT_PHONE_ISDN},
 138 	{"Mobile Phone", E_CONTACT_PHONE_MOBILE},
 139 	{"Other Fax", E_CONTACT_PHONE_OTHER_FAX},
 140 	{"Other Phone", E_CONTACT_PHONE_OTHER},
 141 	{"Pager", E_CONTACT_PHONE_PAGER},
 142 	{"Primary Phone", E_CONTACT_PHONE_PRIMARY},
 143 	{"Radio Phone", E_CONTACT_PHONE_RADIO},
 144 	{"TTY/TDD Phone", E_CONTACT_PHONE_TTYTDD},
 145 	{"Telex", E_CONTACT_PHONE_TELEX},
 146 	{"Account", NOMAP},
 147 	{"Anniversary", NOMAP, FLAG_DATE_ANNIVERSARY},
 148 	{"Assistant's Name", E_CONTACT_ASSISTANT},
 149 	{"Billing Information", NOMAP},
 150 	{"Birthday", NOMAP, FLAG_DATE_BDAY},
 151 	{"Business Address PO Box", NOMAP, FLAG_WORK_ADDRESS | FLAG_POBOX},
 152 	{"Categories", E_CONTACT_CATEGORIES},
 153 	{"Children", NOMAP},
 154 	{"Directory Server", NOMAP},
 155 	{"E-mail Address", E_CONTACT_EMAIL_1},
 156 	{"E-mail Type", NOMAP},
 157 	{"E-mail Display Name", NOMAP},
 158 	{"E-mail 2 Address", E_CONTACT_EMAIL_2},
 159 	{"E-mail 2 Type", NOMAP},
 160 	{"E-mail 2 Display Name", NOMAP},
 161 	{"E-mail 3 Address", E_CONTACT_EMAIL_3},
 162 	{"E-mail 3 Type", NOMAP},
 163 	{"E-mail 3 Display Name", NOMAP},
 164 	{"Gender", NOMAP},
 165 	{"Government ID Number", NOMAP},
 166 	{"Hobby", NOMAP},
 167 	{"Home Address PO Box", NOMAP, FLAG_HOME_ADDRESS | FLAG_POBOX},
 168 	{"Initials", NOMAP},
 169 	{"Internet FREE/BUSY", E_CONTACT_FREEBUSY_URL},
 170 	{"Keywords", NOMAP},
 171 	{"Language", NOMAP},
 172 	{"Location", NOMAP},
 173 	{"Managers Name", E_CONTACT_MANAGER},
 174 	{"Mileage", NOMAP},
 175 	{"Notes", NOMAP},
 176 	{"Office Location", NOMAP},
 177 	{"Organizational ID Number", NOMAP},
 178 	{"Other Address PO Box", NOMAP, FLAG_OTHER_ADDRESS | FLAG_POBOX},
 179 	{"Priority", NOMAP},
 180 	{"Private", NOMAP},
 181 	{"Profession", NOMAP},
 182 	{"Referred By", NOMAP},
 183 	{"Senstivity", NOMAP},
 184 	{"Spouse", E_CONTACT_SPOUSE},
 185 	{"User 1", NOMAP},
 186 	{"User 2", NOMAP},
 187 	{"User 3", NOMAP},
 188 	{"User 4", NOMAP},
 189 	{"Web Page", E_CONTACT_HOMEPAGE_URL},
 190 };
 191 
 192 static import_fields csv_fields_mozilla[] = {
 193 	{"First Name", E_CONTACT_GIVEN_NAME},
 194 	{"Last Name", E_CONTACT_FAMILY_NAME},
 195 	{"Display Name", NOMAP},
 196 	{"NickName", E_CONTACT_NICKNAME},
 197 	{"E-mail Address", E_CONTACT_EMAIL_1},
 198 	{"E-mail 2 Address", E_CONTACT_EMAIL_2},
 199 	{"Business Phone", E_CONTACT_PHONE_BUSINESS},
 200 	{"Home Phone", E_CONTACT_PHONE_HOME},
 201 	{"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX},
 202 	{"Pager", E_CONTACT_PHONE_PAGER},
 203 	{"Mobile Phone", E_CONTACT_PHONE_MOBILE},
 204 	{"Home Street", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 205 	{"Home Street 2", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 206 	{"Home City", NOMAP, FLAG_HOME_ADDRESS | FLAG_CITY},
 207 	{"Home State", NOMAP, FLAG_HOME_ADDRESS | FLAG_STATE},
 208 	{"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS | FLAG_POSTAL_CODE},
 209 	{"Home Country", NOMAP, FLAG_HOME_ADDRESS | FLAG_COUNTRY},
 210 	{"Business Street", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET },
 211 	{"Business Street 2", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET },
 212 	{"Business City", NOMAP, FLAG_WORK_ADDRESS | FLAG_CITY},
 213 	{"Business State", NOMAP, FLAG_WORK_ADDRESS | FLAG_STATE},
 214 	{"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS | FLAG_POSTAL_CODE},
 215 	{"Business Country", NOMAP, FLAG_WORK_ADDRESS | FLAG_COUNTRY},
 216 	{"Job Title", E_CONTACT_TITLE},
 217 	{"Department", E_CONTACT_ORG_UNIT},
 218 	{"Company", E_CONTACT_ORG},
 219 	{"Web Page", E_CONTACT_HOMEPAGE_URL},
 220 	{"Home Web Page", NOMAP},
 221 	{"Birth Year", NOMAP, FLAG_BIRTH_YEAR},
 222 	{"Birth Month", NOMAP,FLAG_BIRTH_MONTH},
 223 	{"Birth Day", NOMAP, FLAG_BIRTH_DAY},
 224 	{"Custom 1", NOMAP},
 225 	{"Custom 2", NOMAP},
 226 	{"Custom 3", NOMAP},
 227 	{"Custom 4", NOMAP},
 228 	{"Notes", NOMAP},
 229 
 230 };
 231 
 232 static import_fields csv_fields_evolution[] = {
 233 	{"First Name", E_CONTACT_GIVEN_NAME},
 234 	{"Last Name", E_CONTACT_FAMILY_NAME},
 235 	{"id", NOMAP, FLAG_INVALID},
 236 	{"NickName", E_CONTACT_NICKNAME},
 237 	{"E-mail Address", E_CONTACT_EMAIL_1},
 238 	{"E-mail 2 Address", E_CONTACT_EMAIL_2},
 239 	{"E-mail 3 Address", E_CONTACT_EMAIL_3},
 240 	{"E-mail 4 Address", E_CONTACT_EMAIL_4},
 241 	{"Wants HTML", E_CONTACT_WANTS_HTML},
 242 	{"Business Phone", E_CONTACT_PHONE_BUSINESS},
 243 	{"Home Phone", E_CONTACT_PHONE_HOME},
 244 	{"Business Fax", E_CONTACT_PHONE_BUSINESS_FAX},
 245 	{"Pager", E_CONTACT_PHONE_PAGER},
 246 	{"Mobile Phone", E_CONTACT_PHONE_MOBILE},
 247 	{"Home Street", NOMAP, FLAG_HOME_ADDRESS | FLAG_STREET},
 248 	{"Home Street 2", NOMAP, FLAG_INVALID},
 249 	{"Home City", NOMAP, FLAG_HOME_ADDRESS | FLAG_CITY},
 250 	{"Home State", NOMAP, FLAG_HOME_ADDRESS | FLAG_STATE},
 251 	{"Home Postal Code", NOMAP,FLAG_HOME_ADDRESS | FLAG_POSTAL_CODE},
 252 	{"Home Country", NOMAP, FLAG_HOME_ADDRESS | FLAG_COUNTRY},
 253 	{"Business Street", NOMAP, FLAG_WORK_ADDRESS | FLAG_STREET },
 254 	{"Business Street 2", NOMAP, FLAG_INVALID },
 255 	{"Business City", NOMAP, FLAG_WORK_ADDRESS | FLAG_CITY},
 256 	{"Business State", NOMAP, FLAG_WORK_ADDRESS | FLAG_STATE},
 257 	{"Business Postal Code", NOMAP, FLAG_WORK_ADDRESS | FLAG_POSTAL_CODE},
 258 	{"Business Country", NOMAP, FLAG_WORK_ADDRESS | FLAG_COUNTRY},
 259 	{"Job Title", E_CONTACT_TITLE},
 260 	{"Office", E_CONTACT_OFFICE},
 261 	{"Company", E_CONTACT_ORG},
 262 	{"Web Page", E_CONTACT_HOMEPAGE_URL},
 263 	{"Cal uri", E_CONTACT_CALENDAR_URI},
 264 	{"Birth Year", NOMAP, FLAG_BIRTH_YEAR},
 265 	{"Birth Month", NOMAP,FLAG_BIRTH_MONTH},
 266 	{"Birth Day", NOMAP, FLAG_BIRTH_DAY},
 267 	{"Notes", E_CONTACT_NOTE},
 268 };
 269 
 270 static void
 271 add_to_notes (EContact *contact,
 272               const gchar *field_text,
 273               gchar *val)
 274 {
 275 	GString *new_text;
 276 
 277 	if (!field_text || !val || !*val)
 278 		return;
 279 
 280 	new_text = g_string_new (e_contact_get_const (contact, E_CONTACT_NOTE));
 281 	if (strlen (new_text->str) != 0)
 282 		new_text = g_string_append_c (new_text, '\n');
 283 	new_text = g_string_append (new_text, field_text);
 284 	new_text = g_string_append_c (new_text, ':');
 285 	new_text = g_string_append (new_text, val);
 286 
 287 	e_contact_set (contact, E_CONTACT_NOTE, new_text->str);
 288 	g_string_free (new_text, TRUE);
 289 }
 290 
 291 /* @str: a date string in the format MM-DD-YYYY or MMDDYYYY */
 292 static EContactDate *
 293 date_from_string (const gchar *str)
 294 {
 295 	EContactDate * date;
 296 	gint i = 0;
 297 
 298 	g_return_val_if_fail (str != NULL, NULL);
 299 
 300 	date = e_contact_date_new ();
 301 
 302 	if (g_ascii_isdigit (str[i]) && g_ascii_isdigit (str[i + 1])) {
 303 		date->month = str[i] * 10 + str[i + 1] - '0' * 11;
 304 		i = i + 3;
 305 	}
 306 	else {
 307 		date->month = str[i] - '0' * 1;
 308 		i = i + 2;
 309 	}
 310 
 311 	if (g_ascii_isdigit (str[i]) && g_ascii_isdigit (str[i + 1])) {
 312 		date->day = str[i] * 10 + str[i + 1] - '0' * 11;
 313 		i = i + 3;
 314 	}
 315 	else {
 316 		date->day = str[i] - '0' * 1;
 317 		i = i + 2;
 318 	}
 319 	date->year = str[i] * 1000 + str[i + 1] * 100 +
 320 		str[i + 2] * 10 + str[i + 3] - '0' * 1111;
 321 
 322 	return date;
 323 }
 324 
 325 static GString *
 326 parseNextValue (const gchar **pptr)
 327 {
 328 	GString *value;
 329 	const gchar *ptr = *pptr;
 330 
 331 	g_return_val_if_fail (pptr != NULL, NULL);
 332 	g_return_val_if_fail (*pptr != NULL, NULL);
 333 
 334 	if (!*ptr || *ptr == '\n')
 335 		return NULL;
 336 
 337 	value = g_string_new ("");
 338 
 339 	while (*ptr != delimiter) {
 340 		if (*ptr == '\n')
 341 			break;
 342 		if (*ptr != '"') {
 343 			g_string_append_unichar (value, g_utf8_get_char (ptr));
 344 		} else {
 345 			ptr = g_utf8_next_char (ptr);
 346 			while (*ptr && *ptr != '"') {
 347 				g_string_append_unichar (value, g_utf8_get_char (ptr));
 348 				ptr = g_utf8_next_char (ptr);
 349 			}
 350 
 351 			if (!*ptr)
 352 				break;
 353 		}
 354 
 355 		ptr = g_utf8_next_char (ptr);
 356 	}
 357 
 358 	if (*ptr != 0 && *ptr != '\n')
 359 		ptr = g_utf8_next_char (ptr);
 360 
 361 	*pptr = ptr;
 362 
 363 	return value;
 364 }
 365 
 366 static GHashTable *
 367 map_fields (const gchar *header_line,
 368             gint pimporter)
 369 {
 370 	import_fields *fields_array = NULL;
 371 	gint n_fields = -1, idx, j;
 372 	GString *value;
 373 	GHashTable *fmap;
 374 	const gchar *pptr = header_line;
 375 	gboolean any_found = FALSE;
 376 
 377 	if (pimporter == OUTLOOK_IMPORTER) {
 378 		fields_array = csv_fields_outlook;
 379 		n_fields = G_N_ELEMENTS (csv_fields_outlook);
 380 	} else if (pimporter == EVOLUTION_IMPORTER) {
 381 		fields_array = csv_fields_evolution;
 382 		n_fields = G_N_ELEMENTS (csv_fields_evolution);
 383 	}
 384 
 385 	g_return_val_if_fail (fields_array != NULL, NULL);
 386 	g_return_val_if_fail (n_fields > 0, NULL);
 387 
 388 	fmap = g_hash_table_new (g_direct_hash, g_direct_equal);
 389 	idx = 0;
 390 	while (value = parseNextValue (&pptr), value != NULL) {
 391 		for (j = 0; j < n_fields; j++) {
 392 			if (g_ascii_strcasecmp (fields_array[j].csv_attribute, value->str) == 0) {
 393 				g_hash_table_insert (
 394 					fmap, GINT_TO_POINTER (idx),
 395 					GINT_TO_POINTER (j + 1));
 396 				any_found = TRUE;
 397 				break;
 398 			}
 399 		}
 400 
 401 		if (j >= n_fields)
 402 			g_hash_table_insert (fmap, GINT_TO_POINTER (idx), GINT_TO_POINTER (-1));
 403 
 404 		g_string_free (value, TRUE);
 405 		idx++;
 406 	}
 407 
 408 	if (!any_found) {
 409 		/* column names not in English? */
 410 		g_hash_table_destroy (fmap);
 411 		fmap = NULL;
 412 	} else {
 413 		/* also add last index, to be always skipped */
 414 		g_hash_table_insert (fmap, GINT_TO_POINTER (idx), GINT_TO_POINTER (-1));
 415 	}
 416 
 417 	return fmap;
 418 }
 419 
 420 static gboolean
 421 parseLine (CSVImporter *gci,
 422            EContact *contact,
 423            gchar *buf)
 424 {
 425 	const gchar *pptr = buf, *field_text;
 426 	gchar *do_free = NULL;
 427 	GString *value;
 428 	gint ii = 0, idx;
 429 	gint flags = 0;
 430 	gint contact_field;
 431 	EContactAddress *home_address = NULL, *work_address = NULL, *other_address = NULL;
 432 	EContactDate *bday = NULL;
 433 	GString *home_street, *work_street, *other_street;
 434 	home_street = g_string_new ("");
 435 	work_street = g_string_new ("");
 436 	other_street = g_string_new ("");
 437 	home_address = g_new0 (EContactAddress, 1);
 438 	work_address = g_new0 (EContactAddress, 1);
 439 	other_address = g_new0 (EContactAddress, 1);
 440 	bday = g_new0 (EContactDate, 1);
 441 
 442 	if (!g_utf8_validate (pptr, -1, NULL)) {
 443 		do_free = g_convert (pptr, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
 444 		pptr = do_free;
 445 	}
 446 
 447 	while (value = parseNextValue (&pptr), value != NULL) {
 448 		contact_field = NOMAP;
 449 		flags = FLAG_INVALID;
 450 		field_text = NULL;
 451 
 452 		idx = ii;
 453 		if (gci->fields_map) {
 454 			gpointer found;
 455 
 456 			found = g_hash_table_lookup (
 457 				gci->fields_map, GINT_TO_POINTER (idx));
 458 
 459 			if (found == NULL) {
 460 				g_warning ("%s: No map for index %d, skipping it", G_STRFUNC, idx);
 461 				idx = -1;
 462 			} else {
 463 				idx = GPOINTER_TO_INT (found) - 1;
 464 			}
 465 		}
 466 
 467 		if (importer == OUTLOOK_IMPORTER) {
 468 			if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_outlook)) {
 469 				contact_field = csv_fields_outlook[idx].contact_field;
 470 				flags = csv_fields_outlook[idx].flags;
 471 				field_text = csv_fields_outlook[idx].csv_attribute;
 472 			}
 473 		}
 474 		else if (importer == MOZILLA_IMPORTER) {
 475 			if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_mozilla)) {
 476 				contact_field = csv_fields_mozilla[idx].contact_field;
 477 				flags = csv_fields_mozilla[idx].flags;
 478 				field_text = csv_fields_mozilla[idx].csv_attribute;
 479 			}
 480 		}
 481 		else {
 482 			if (idx >= 0 && idx < G_N_ELEMENTS (csv_fields_evolution)) {
 483 				contact_field = csv_fields_evolution[idx].contact_field;
 484 				flags = csv_fields_evolution[idx].flags;
 485 				field_text = csv_fields_evolution[idx].csv_attribute;
 486 			}
 487 		}
 488 
 489 		if (*value->str) {
 490 			if (contact_field != NOMAP) {
 491 				if (importer == OUTLOOK_IMPORTER || importer == MOZILLA_IMPORTER) {
 492 					e_contact_set (contact, contact_field, value->str);
 493 				} else {
 494 					if (contact_field == E_CONTACT_WANTS_HTML)
 495 						e_contact_set (
 496 							contact, contact_field,
 497 							GINT_TO_POINTER (
 498 							g_ascii_strcasecmp (
 499 							value->str, "TRUE") == 0));
 500 					else
 501 						e_contact_set (contact, contact_field, value->str);
 502 				}
 503 			}
 504 			else {
 505 				switch (flags) {
 506 
 507 				case FLAG_HOME_ADDRESS | FLAG_STREET:
 508 					if (strlen (home_street->str) != 0) {
 509 						home_street = g_string_append (home_street, ",\n");
 510 					}
 511 					home_street = g_string_append (home_street, value->str);
 512 					break;
 513 				case FLAG_HOME_ADDRESS | FLAG_CITY:
 514 					home_address->locality = g_strdup (value->str);
 515 					break;
 516 				case FLAG_HOME_ADDRESS | FLAG_STATE:
 517 					home_address->region = g_strdup (value->str);
 518 					break;
 519 				case FLAG_HOME_ADDRESS | FLAG_POSTAL_CODE:
 520 					home_address->code = g_strdup (value->str);
 521 					break;
 522 				case FLAG_HOME_ADDRESS | FLAG_POBOX:
 523 					home_address->po = g_strdup (value->str);
 524 					break;
 525 				case FLAG_HOME_ADDRESS | FLAG_COUNTRY:
 526 					home_address->country = g_strdup (value->str);
 527 					break;
 528 
 529 				case FLAG_WORK_ADDRESS | FLAG_STREET:
 530 					if (strlen (work_street->str) != 0) {
 531 						work_street = g_string_append (work_street, ",\n");
 532 					}
 533 					work_street = g_string_append (work_street, value->str);
 534 					break;
 535 				case FLAG_WORK_ADDRESS | FLAG_CITY:
 536 					work_address->locality = g_strdup (value->str);
 537 					break;
 538 				case FLAG_WORK_ADDRESS | FLAG_STATE:
 539 					work_address->region = g_strdup (value->str);
 540 					break;
 541 				case FLAG_WORK_ADDRESS | FLAG_POSTAL_CODE:
 542 					work_address->code = g_strdup (value->str);
 543 					break;
 544 				case FLAG_WORK_ADDRESS | FLAG_POBOX:
 545 					work_address->po = g_strdup (value->str);
 546 					break;
 547 				case FLAG_WORK_ADDRESS | FLAG_COUNTRY:
 548 					work_address->country = g_strdup (value->str);
 549 					break;
 550 
 551 				case FLAG_OTHER_ADDRESS | FLAG_STREET:
 552 					if (strlen (other_street->str) != 0) {
 553 						other_street = g_string_append (other_street, ",\n");
 554 					}
 555 					other_street = g_string_append (other_street, value->str);
 556 					break;
 557 				case FLAG_OTHER_ADDRESS | FLAG_CITY:
 558 					other_address->locality = g_strdup (value->str);
 559 					break;
 560 				case FLAG_OTHER_ADDRESS | FLAG_STATE:
 561 					other_address->region = g_strdup (value->str);
 562 					break;
 563 				case FLAG_OTHER_ADDRESS | FLAG_POSTAL_CODE:
 564 					other_address->code = g_strdup (value->str);
 565 					break;
 566 				case FLAG_OTHER_ADDRESS | FLAG_POBOX:
 567 					other_address->po = g_strdup (value->str);
 568 					break;
 569 				case FLAG_OTHER_ADDRESS | FLAG_COUNTRY:
 570 					other_address->country = g_strdup (value->str);
 571 					break;
 572 
 573 				case FLAG_DATE_BDAY:
 574 					e_contact_set (
 575 						contact,
 576 						E_CONTACT_BIRTH_DATE,
 577 						date_from_string (value->str));
 578 					break;
 579 
 580 				case FLAG_DATE_ANNIVERSARY:
 581 					e_contact_set (
 582 						contact,
 583 						E_CONTACT_ANNIVERSARY,
 584 						date_from_string (value->str));
 585 					break;
 586 
 587 				case FLAG_BIRTH_DAY:
 588 					bday->day = atoi (value->str);
 589 					break;
 590 				case FLAG_BIRTH_YEAR:
 591 					bday->year = atoi (value->str);
 592 					break;
 593 				case FLAG_BIRTH_MONTH:
 594 					bday->month = atoi (value->str);
 595 					break;
 596 
 597 				case FLAG_INVALID:
 598 					break;
 599 
 600 				default:
 601 					add_to_notes (contact, field_text, value->str);
 602 
 603 				}
 604 			}
 605 		}
 606 		ii++;
 607 		g_string_free (value, TRUE);
 608 	}
 609 	if (strlen (home_street->str) != 0)
 610 		home_address->street = g_strdup (home_street->str);
 611 	if (strlen (work_street->str) != 0)
 612 		work_address->street = g_strdup (work_street->str);
 613 	if (strlen (other_street->str) != 0)
 614 		other_address->street = g_strdup (other_street->str);
 615 	g_string_free (home_street, TRUE);
 616 	g_string_free (work_street, TRUE);
 617 	g_string_free (other_street, TRUE);
 618 
 619 	if (home_address->locality || home_address->country ||
 620 	   home_address->code || home_address->region || home_address->street)
 621 		e_contact_set (contact, E_CONTACT_ADDRESS_HOME, home_address);
 622 	if (work_address->locality || work_address->country ||
 623 	   work_address->code || work_address->region || work_address->street)
 624 		e_contact_set (contact, E_CONTACT_ADDRESS_WORK, work_address);
 625 	if (other_address->locality || other_address->country ||
 626 	   other_address->code || other_address->region || other_address->street)
 627 		e_contact_set (contact, E_CONTACT_ADDRESS_OTHER, other_address);
 628 
 629 	if (importer !=  OUTLOOK_IMPORTER) {
 630 		if (bday->day || bday->year || bday->month)
 631 			e_contact_set (contact, E_CONTACT_BIRTH_DATE, bday);
 632 	}
 633 
 634 	g_free (do_free);
 635 
 636 	return TRUE;
 637 }
 638 
 639 static EContact *
 640 getNextCSVEntry (CSVImporter *gci,
 641                  FILE *f)
 642 {
 643 	EContact *contact = NULL;
 644 	GString  *line;
 645 	GString *str;
 646 	gchar *buf;
 647 	gchar c;
 648 
 649 	line = g_string_new ("");
 650 	while (1) {
 651 		c = fgetc (f);
 652 		if (c == EOF)
 653 			return NULL;
 654 		if (c == '\n') {
 655 			g_string_append_c (line, c);
 656 			break;
 657 		}
 658 		if (c == '"') {
 659 			g_string_append_c (line, c);
 660 			c = fgetc (f);
 661 			while (!feof (f) && c != '"') {
 662 				g_string_append_c (line, c);
 663 				c = fgetc (f);
 664 			}
 665 			g_string_append_c (line, c);
 666 		}
 667 		else
 668 			g_string_append_c (line, c);
 669 	}
 670 
 671 	if (gci->count == 0 && importer != MOZILLA_IMPORTER) {
 672 		gci->fields_map = map_fields (line->str, importer);
 673 		g_string_free (line, TRUE);
 674 		line = g_string_new ("");
 675 		while (1) {
 676 			c = fgetc (f);
 677 			if (c == EOF)
 678 				return NULL;
 679 			if (c == '\n') {
 680 				g_string_append_c (line, c);
 681 				break;
 682 			}
 683 			if (c == '"') {
 684 				g_string_append_c (line, c);
 685 				c = fgetc (f);
 686 				while (!feof (f) && c != '"') {
 687 					g_string_append_c (line, c);
 688 					c = fgetc (f);
 689 				}
 690 				g_string_append_c (line, c);
 691 			}
 692 			else
 693 				g_string_append_c (line, c);
 694 		}
 695 		gci->count++;
 696 	}
 697 
 698 	str = g_string_new ("");
 699 	str = g_string_append (str, line->str);
 700 
 701 	g_string_free (line, TRUE);
 702 
 703 	if (strlen (str->str) == 0) {
 704 		g_string_free (str, TRUE);
 705 		return NULL;
 706 	}
 707 
 708 	contact = e_contact_new ();
 709 
 710 	buf = str->str;
 711 
 712 	if (!parseLine (gci, contact, buf)) {
 713 		g_object_unref (contact);
 714 		return NULL;
 715 	}
 716 	gci->count++;
 717 
 718 	g_string_free (str, TRUE);
 719 
 720 	return contact;
 721 }
 722 
 723 static gboolean
 724 csv_import_contacts (gpointer d)
 725 {
 726 	CSVImporter *gci = d;
 727 	EContact *contact = NULL;
 728 
 729 	while ((contact = getNextCSVEntry (gci, gci->file))) {
 730 		gchar *uid = NULL;
 731 		if (e_book_client_add_contact_sync (gci->book_client, contact, &uid, NULL, NULL) && uid) {
 732 			e_contact_set (contact, E_CONTACT_UID, uid);
 733 			g_free (uid);
 734 		}
 735 		gci->contacts = g_slist_prepend (gci->contacts, contact);
 736 	}
 737 	if (contact == NULL) {
 738 		gci->state = 1;
 739 	}
 740 	if (gci->state == 1) {
 741 		csv_import_done (gci);
 742 		return FALSE;
 743 	}
 744 	else {
 745 		e_import_status (
 746 			gci->import, gci->target, _("Importing..."),
 747 			ftell (gci->file) * 100 / gci->size);
 748 		return TRUE;
 749 	}
 750 }
 751 
 752 static void
 753 primary_selection_changed_cb (ESourceSelector *selector,
 754                               EImportTarget *target)
 755 {
 756 	ESource *source;
 757 
 758 	source = e_source_selector_ref_primary_selection (selector);
 759 	g_return_if_fail (source != NULL);
 760 
 761 	g_datalist_set_data_full (
 762 		&target->data, "csv-source",
 763 		source, (GDestroyNotify) g_object_unref);
 764 }
 765 
 766 static GtkWidget *
 767 csv_getwidget (EImport *ei,
 768                EImportTarget *target,
 769                EImportImporter *im)
 770 {
 771 	EShell *shell;
 772 	GtkWidget *vbox, *selector;
 773 	ESourceRegistry *registry;
 774 	ESource *primary;
 775 	const gchar *extension_name;
 776 
 777 	vbox = gtk_vbox_new (FALSE, FALSE);
 778 
 779 	shell = e_shell_get_default ();
 780 	registry = e_shell_get_registry (shell);
 781 	extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
 782 	selector = e_source_selector_new (registry, extension_name);
 783 	e_source_selector_set_show_toggles (
 784 		E_SOURCE_SELECTOR (selector), FALSE);
 785 	gtk_box_pack_start (GTK_BOX (vbox), selector, FALSE, TRUE, 6);
 786 
 787 	primary = g_datalist_get_data (&target->data, "csv-source");
 788 	if (primary == NULL) {
 789 		GList *list;
 790 
 791 		list = e_source_registry_list_sources (registry, extension_name);
 792 		if (list != NULL) {
 793 			primary = g_object_ref (list->data);
 794 			g_datalist_set_data_full (
 795 				&target->data, "csv-source", primary,
 796 				(GDestroyNotify) g_object_unref);
 797 		}
 798 
 799 		g_list_free_full (list, (GDestroyNotify) g_object_unref);
 800 	}
 801 	e_source_selector_set_primary_selection (
 802 		E_SOURCE_SELECTOR (selector), primary);
 803 
 804 	g_signal_connect (
 805 		selector, "primary_selection_changed",
 806 		G_CALLBACK (primary_selection_changed_cb), target);
 807 
 808 	gtk_widget_show_all (vbox);
 809 
 810 	return vbox;
 811 }
 812 
 813 static const gchar *supported_extensions[4] = {
 814 	".csv", ".tab" , ".txt", NULL
 815 };
 816 
 817 static gboolean
 818 csv_supported (EImport *ei,
 819                EImportTarget *target,
 820                EImportImporter *im)
 821 {
 822 	gchar *ext;
 823 	gint i;
 824 	EImportTargetURI *s;
 825 
 826 	if (target->type != E_IMPORT_TARGET_URI)
 827 		return FALSE;
 828 
 829 	s = (EImportTargetURI *) target;
 830 	if (s->uri_src == NULL)
 831 		return TRUE;
 832 
 833 	if (strncmp (s->uri_src, "file:///", 8) != 0)
 834 		return FALSE;
 835 
 836 	ext = strrchr (s->uri_src, '.');
 837 	if (ext == NULL)
 838 		return FALSE;
 839 
 840 	for (i = 0; supported_extensions[i] != NULL; i++) {
 841 		if (g_ascii_strcasecmp (supported_extensions[i], ext) == 0) {
 842 			if (i == 0) {
 843 				delimiter = CSV_FILE_DELIMITER;
 844 			}
 845 			else {
 846 				delimiter = TAB_FILE_DELIMITER;
 847 			}
 848 			return TRUE;
 849 		}
 850 	}
 851 
 852 	return FALSE;
 853 }
 854 
 855 static void
 856 csv_import_done (CSVImporter *gci)
 857 {
 858 	if (gci->idle_id)
 859 		g_source_remove (gci->idle_id);
 860 
 861 	fclose (gci->file);
 862 	g_object_unref (gci->book_client);
 863 	g_slist_foreach (gci->contacts, (GFunc) g_object_unref, NULL);
 864 	g_slist_free (gci->contacts);
 865 
 866 	if (gci->fields_map)
 867 		g_hash_table_destroy (gci->fields_map);
 868 
 869 	e_import_complete (gci->import, gci->target);
 870 	g_object_unref (gci->import);
 871 
 872 	g_free (gci);
 873 }
 874 
 875 static void
 876 book_loaded_cb (GObject *source_object,
 877                 GAsyncResult *result,
 878                 gpointer user_data)
 879 {
 880 	ESource *source = E_SOURCE (source_object);
 881 	CSVImporter *gci = user_data;
 882 	EClient *client = NULL;
 883 
 884 	e_client_utils_open_new_finish (source, result, &client, NULL);
 885 
 886 	if (client == NULL) {
 887 		csv_import_done (gci);
 888 		return;
 889 	}
 890 
 891 	gci->book_client = E_BOOK_CLIENT (client);
 892 	gci->idle_id = g_idle_add (csv_import_contacts, gci);
 893 }
 894 
 895 static void
 896 csv_import (EImport *ei,
 897             EImportTarget *target,
 898             EImportImporter *im)
 899 {
 900 	CSVImporter *gci;
 901 	ESource *source;
 902 	gchar *filename;
 903 	FILE *file;
 904 	EImportTargetURI *s = (EImportTargetURI *) target;
 905 
 906 	filename = g_filename_from_uri (s->uri_src, NULL, NULL);
 907 	if (filename == NULL) {
 908 		g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
 909 		return;
 910 	}
 911 
 912 	file = g_fopen (filename, "r");
 913 	g_free (filename);
 914 	if (file == NULL) {
 915 		g_message ("Can't open .csv file");
 916 		e_import_complete (ei, target);
 917 		return;
 918 	}
 919 
 920 	gci = g_malloc0 (sizeof (*gci));
 921 	g_datalist_set_data (&target->data, "csv-data", gci);
 922 	gci->import = g_object_ref (ei);
 923 	gci->target = target;
 924 	gci->file = file;
 925 	gci->fields_map = NULL;
 926 	gci->count = 0;
 927 	fseek (file, 0, SEEK_END);
 928 	gci->size = ftell (file);
 929 	fseek (file, 0, SEEK_SET);
 930 
 931 	source = g_datalist_get_data (&target->data, "csv-source");
 932 
 933 	e_client_utils_open_new (
 934 		source, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
 935 		book_loaded_cb, gci);
 936 }
 937 
 938 static void
 939 outlook_csv_import (EImport *ei,
 940                     EImportTarget *target,
 941                     EImportImporter *im)
 942 {
 943 	importer = OUTLOOK_IMPORTER;
 944 	csv_import (ei, target, im);
 945 }
 946 
 947 static void
 948 mozilla_csv_import (EImport *ei,
 949                     EImportTarget *target,
 950                     EImportImporter *im)
 951 {
 952 	importer = MOZILLA_IMPORTER;
 953 	csv_import (ei, target, im);
 954 }
 955 
 956 static void
 957 evolution_csv_import (EImport *ei,
 958                       EImportTarget *target,
 959                       EImportImporter *im)
 960 {
 961 	importer = EVOLUTION_IMPORTER;
 962 	csv_import (ei, target, im);
 963 }
 964 
 965 static void
 966 csv_cancel (EImport *ei,
 967             EImportTarget *target,
 968             EImportImporter *im)
 969 {
 970 	CSVImporter *gci = g_datalist_get_data (&target->data, "csv-data");
 971 
 972 	if (gci)
 973 		gci->state = 1;
 974 }
 975 
 976 static GtkWidget *
 977 csv_get_preview (EImport *ei,
 978                  EImportTarget *target,
 979                  EImportImporter *im)
 980 {
 981 	GtkWidget *preview;
 982 	GSList *contacts = NULL;
 983 	EContact *contact;
 984 	EImportTargetURI *s = (EImportTargetURI *) target;
 985 	gchar *filename;
 986 	FILE *file;
 987 	CSVImporter *gci;
 988 
 989 	filename = g_filename_from_uri (s->uri_src, NULL, NULL);
 990 	if (filename == NULL) {
 991 		g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
 992 		return NULL;
 993 	}
 994 
 995 	file = g_fopen (filename, "r");
 996 	g_free (filename);
 997 	if (file == NULL) {
 998 		g_message (G_STRLOC ": Can't open .csv file");
 999 		return NULL;
1000 	}
1001 
1002 	gci = g_malloc0 (sizeof (*gci));
1003 	gci->file = file;
1004 	gci->fields_map = NULL;
1005 	gci->count = 0;
1006 	fseek (file, 0, SEEK_END);
1007 	gci->size = ftell (file);
1008 	fseek (file, 0, SEEK_SET);
1009 
1010 	while (contact = getNextCSVEntry (gci, gci->file), contact != NULL) {
1011 		contacts = g_slist_prepend (contacts, contact);
1012 	}
1013 
1014 	contacts = g_slist_reverse (contacts);
1015 	preview = evolution_contact_importer_get_preview_widget (contacts);
1016 
1017 	e_client_util_free_object_slist (contacts);
1018 	fclose (file);
1019 	g_free (gci);
1020 
1021 	return preview;
1022 }
1023 
1024 static GtkWidget *
1025 outlook_csv_get_preview (EImport *ei,
1026                          EImportTarget *target,
1027                          EImportImporter *im)
1028 {
1029 	importer = OUTLOOK_IMPORTER;
1030 	return csv_get_preview (ei, target, im);
1031 }
1032 
1033 static GtkWidget *
1034 mozilla_csv_get_preview (EImport *ei,
1035                          EImportTarget *target,
1036                          EImportImporter *im)
1037 {
1038 	importer = MOZILLA_IMPORTER;
1039 	return csv_get_preview (ei, target, im);
1040 }
1041 
1042 static GtkWidget *
1043 evolution_csv_get_preview (EImport *ei,
1044                            EImportTarget *target,
1045                            EImportImporter *im)
1046 {
1047 	importer = EVOLUTION_IMPORTER;
1048 	return csv_get_preview (ei, target, im);
1049 }
1050 
1051 static EImportImporter csv_outlook_importer = {
1052 	E_IMPORT_TARGET_URI,
1053 	0,
1054 	csv_supported,
1055 	csv_getwidget,
1056 	outlook_csv_import,
1057 	csv_cancel,
1058 	outlook_csv_get_preview,
1059 };
1060 
1061 static EImportImporter csv_mozilla_importer = {
1062 	E_IMPORT_TARGET_URI,
1063 	0,
1064 	csv_supported,
1065 	csv_getwidget,
1066 	mozilla_csv_import,
1067 	csv_cancel,
1068 	mozilla_csv_get_preview,
1069 };
1070 
1071 static EImportImporter csv_evolution_importer = {
1072 	E_IMPORT_TARGET_URI,
1073 	0,
1074 	csv_supported,
1075 	csv_getwidget,
1076 	evolution_csv_import,
1077 	csv_cancel,
1078 	evolution_csv_get_preview,
1079 };
1080 
1081 EImportImporter *
1082 evolution_csv_outlook_importer_peek (void)
1083 {
1084 	csv_outlook_importer.name = _("Outlook Contacts CSV or Tab (.csv, .tab)");
1085 	csv_outlook_importer.description = _("Outlook Contacts CSV and Tab Importer");
1086 
1087 	return &csv_outlook_importer;
1088 }
1089 
1090 EImportImporter *
1091 evolution_csv_mozilla_importer_peek (void)
1092 {
1093 	csv_mozilla_importer.name = _("Mozilla Contacts CSV or Tab (.csv, .tab)");
1094 	csv_mozilla_importer.description = _("Mozilla Contacts CSV and Tab Importer");
1095 
1096 	return &csv_mozilla_importer;
1097 }
1098 
1099 EImportImporter *
1100 evolution_csv_evolution_importer_peek (void)
1101 {
1102 	csv_evolution_importer.name = _("Evolution Contacts CSV or Tab (.csv, .tab)");
1103 	csv_evolution_importer.description = _("Evolution Contacts CSV and Tab Importer");
1104 
1105 	return &csv_evolution_importer;
1106 }