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 }