5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
18 #include <sys/types.h>
19 #include "abook_curses.h"
36 extern abook_field_list *fields_list;
37 extern int fields_count;
40 * function declarations
44 * import filter prototypes
47 static int ldif_parse_file(FILE *handle);
48 static int mutt_parse_file(FILE *in);
49 static int pine_parse_file(FILE *in);
50 static int csv_parse_file(FILE *in);
51 static int allcsv_parse_file(FILE *in);
52 static int palmcsv_parse_file(FILE *in);
53 static int vcard_parse_file(FILE *in);
56 * export filter prototypes
59 static int ldif_export_database(FILE *out, struct db_enumerator e);
60 static int html_export_database(FILE *out, struct db_enumerator e);
61 static int pine_export_database(FILE *out, struct db_enumerator e);
62 static int csv_export_database(FILE *out, struct db_enumerator e);
63 static int allcsv_export_database(FILE *out, struct db_enumerator e);
64 static int palm_export_database(FILE *out, struct db_enumerator e);
65 static int vcard_export_database(FILE *out, struct db_enumerator e);
66 static int mutt_alias_export(FILE *out, struct db_enumerator e);
67 static int mutt_query_export_database(FILE *out, struct db_enumerator e);
68 static int elm_alias_export(FILE *out, struct db_enumerator e);
69 static int text_export_database(FILE *out, struct db_enumerator e);
70 static int spruce_export_database(FILE *out, struct db_enumerator e);
71 static int wl_export_database(FILE *out, struct db_enumerator e);
72 static int bsdcal_export_database(FILE *out, struct db_enumerator e);
73 static int custom_export_database(FILE *out, struct db_enumerator e);
76 * export filter item prototypes
79 void vcard_export_item(FILE *out, int item);
80 void muttq_print_item(FILE *file, int item);
81 void custom_print_item(FILE *out, int item);
84 * end of function declarations
87 struct abook_input_filter i_filters[] = {
88 { "abook", N_("abook native format"), parse_database },
89 { "ldif", N_("ldif / Netscape addressbook"), ldif_parse_file },
90 { "mutt", N_("mutt alias"), mutt_parse_file },
91 { "pine", N_("pine addressbook"), pine_parse_file },
92 { "csv", N_("comma separated values"), csv_parse_file },
93 { "allcsv", N_("comma separated values (all fields)"), allcsv_parse_file },
94 { "palmcsv", N_("Palm comma separated values"), palmcsv_parse_file },
95 { "vcard", N_("vCard file"), vcard_parse_file },
99 struct abook_output_filter e_filters[] = {
100 { "abook", N_("abook native format"), write_database },
101 { "ldif", N_("ldif / Netscape addressbook (.4ld)"), ldif_export_database },
102 { "vcard", N_("vCard 2 file"), vcard_export_database },
103 { "mutt", N_("mutt alias"), mutt_alias_export },
104 { "muttq", N_("mutt query format (internal use)"), mutt_query_export_database },
105 { "html", N_("html document"), html_export_database },
106 { "pine", N_("pine addressbook"), pine_export_database },
107 { "csv", N_("comma separated values"), csv_export_database },
108 { "allcsv", N_("comma separated values (all fields)"), allcsv_export_database },
109 { "palmcsv", N_("Palm comma separated values"), palm_export_database},
110 { "elm", N_("elm alias"), elm_alias_export },
111 { "text", N_("plain text"), text_export_database },
112 { "wl", N_("Wanderlust address book"), wl_export_database },
113 { "spruce", N_("Spruce address book"), spruce_export_database },
114 { "bsdcal", N_("BSD calendar"), bsdcal_export_database },
115 { "custom", N_("Custom format"), custom_export_database },
119 struct abook_output_item_filter u_filters[] = {
120 { "vcard", N_("vCard 2 file"), vcard_export_item },
121 { "muttq", N_("mutt alias"), muttq_print_item },
122 { "custom", N_("Custom format"), custom_print_item },
135 puts(_("input formats:"));
136 for(i=0; *i_filters[i].filtname ; i++)
137 printf("\t%s\t%s\n", i_filters[i].filtname,
138 gettext(i_filters[i].desc));
142 puts(_("output formats:"));
143 for(i=0; *e_filters[i].filtname ; i++)
144 printf("\t%s\t%s\n", e_filters[i].filtname,
145 gettext(e_filters[i].desc));
149 puts(_("query-compatible output formats:"));
150 for(i=0; *u_filters[i].filtname ; i++)
151 printf("\t%s\t%s\n", u_filters[i].filtname,
152 gettext(u_filters[i].desc));
158 number_of_output_filters()
162 for(i=0; *e_filters[i].filtname ; i++)
169 number_of_input_filters()
173 for(i=0; *i_filters[i].filtname ; i++)
182 char *username = getenv("USER");
183 struct passwd *pwent;
187 pwent = getpwnam(username);
189 if((tmp = xstrdup(pwent->pw_gecos)) == NULL)
190 return xstrdup(username);
192 rtn = sscanf(pwent->pw_gecos, "%[^,]", tmp);
193 if (rtn == EOF || rtn == 0) {
195 return xstrdup(username);
204 static int i_read_file(char *filename, int (*func) (FILE *in));
213 refresh_statusline();
214 headerline(_("import database"));
216 mvaddstr(3, 1, _("please select a filter"));
219 for(i=0; *i_filters[i].filtname ; i++)
220 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
221 i_filters[i].filtname,
222 gettext(i_filters[i].desc));
224 mvprintw(6 + i, 6, _("x -\tcancel"));
232 int tmp = db_n_items();
236 filter = getch() - 'a';
237 if(filter == 'x' - 'a' ||
238 filter >= number_of_input_filters() || filter < 0) {
243 mvaddstr(5+filter, 2, "->");
245 filename = ask_filename(_("Filename: "));
251 if(i_read_file(filename, i_filters[filter].func ))
252 statusline_msg(_("Error occured while opening the file"));
253 else if(tmp == db_n_items())
254 statusline_msg(_("File does not seem to be a valid addressbook"));
265 i_read_file(char *filename, int (*func) (FILE *in))
270 if( (in = abook_fopen( filename, "r" )) == NULL )
281 import_file(char filtname[FILTNAME_LEN], char *filename)
284 int tmp = db_n_items();
288 if(! strncasecmp(i_filters[i].filtname, filtname,
291 if(! *i_filters[i].filtname) {
301 // this is a special case for
302 // libvformat whose API expects a filename
303 if(!strcmp(filtname, "vcard")) {
304 if(!strcmp(filename, "-"))
305 ret = vcard_parse_file_libvformat("/dev/stdin");
307 ret = vcard_parse_file_libvformat(filename);
312 if(!strcmp(filename, "-")) {
314 if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode))
317 ret = (*i_filters[i].func) (stdin);
319 ret = i_read_file(filename, i_filters[i].func);
321 if(tmp == db_n_items())
331 static int e_write_file(char *filename,
332 int (*func) (FILE *in, struct db_enumerator e), int mode);
342 refresh_statusline();
343 headerline(_("export database"));
345 mvaddstr(3, 1, _("please select a filter"));
348 for(i = 0; *e_filters[i].filtname ; i++)
349 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
350 e_filters[i].filtname,
351 gettext(e_filters[i].desc));
353 mvprintw(6 + i, 6, _("x -\tcancel"));
360 int enum_mode = ENUM_ALL;
365 filter = getch() - 'a';
366 if(filter == 'x' - 'a' ||
367 filter >= number_of_output_filters() || filter < 0) {
372 mvaddstr(5 + filter, 2, "->");
374 if(selected_items()) {
375 switch(statusline_askchoice(
376 _("Export <a>ll, export <s>elected, or <c>ancel?"),
377 S_("keybindings:all/selected/cancel|asc"), 3)) {
381 enum_mode = ENUM_SELECTED;
391 filename = ask_filename(_("Filename: "));
397 if( e_write_file(filename, e_filters[filter].func, enum_mode))
398 statusline_msg(_("Error occured while exporting"));
406 struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) {
409 if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN))
411 if(!*u_filters[i].filtname) {
420 e_write_item(FILE *out, int item, void (*func) (FILE *in, int item))
426 e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e),
431 struct db_enumerator enumerator = init_db_enumerator(mode);
433 if((out = fopen(filename, "a")) == NULL)
439 ret = (*func) (out, enumerator);
447 fexport(char filtname[FILTNAME_LEN], FILE *handle, int enum_mode)
450 struct db_enumerator e = init_db_enumerator(enum_mode);
453 if(!strncasecmp(e_filters[i].filtname, filtname,
456 if(!*e_filters[i].filtname) {
462 return (e_filters[i].func) (handle, e);
468 export_file(char filtname[FILTNAME_LEN], char *filename)
470 const int mode = ENUM_ALL;
473 struct db_enumerator e = init_db_enumerator(mode);
476 if(!strncasecmp(e_filters[i].filtname, filtname,
479 if(!*e_filters[i].filtname) {
488 if(!strcmp(filename, "-"))
489 ret = (e_filters[i].func) (stdout, e);
491 ret = e_write_file(filename, e_filters[i].func, mode);
497 * end of common functions
506 static void ldif_fix_string(char *str);
508 #define LDIF_ITEM_FIELDS 16
510 typedef char *ldif_item[LDIF_ITEM_FIELDS];
512 static ldif_item ldif_field_names = {
524 "facsimiletelephonenumber",
528 "objectclass", /* this must be the last entry */
531 static int ldif_conv_table[LDIF_ITEM_FIELDS] = {
534 ADDRESS, /* "streetaddress" */
535 ADDRESS2, /* "streetaddress2" */
536 CITY, /* "locality" */
538 ZIP, /* "postalcode" */
539 COUNTRY, /* "countryname" */
540 PHONE, /* "homephone" */
541 NOTES, /* "description" */
543 FAX, /* "facsimiletelephonenumber" */
544 MOBILEPHONE, /* "cellphone" */
545 WORKPHONE, /* "xmozillaanyphone" */
546 NICK, /* "xmozillanickname" */
547 -1, /* "objectclass" */ /* this must be the last entry */
551 Handles multi-line strings.
552 If a string starts with a space, it's the continuation
553 of the previous line. Thus we need to always read ahead.
554 But for this to work with stdin, we need to stores the next
555 line for later use in case it's not a continuation of the
559 ldif_read_line(FILE *in, char **next_line)
565 // buf filled with the first line
569 buf = xstrdup(*next_line);
574 // if no line already read-ahead.
578 // this is not a continuation of what is already in buf
579 // store it for the next round
585 // starts with ' ': this is the continuation of buf
591 buf = strconcat(buf, ptr, NULL);
596 if(buf && *buf == '#' ) {
605 ldif_add_item(ldif_item li)
610 item = item_create();
612 for(i=0; i < LDIF_ITEM_FIELDS; i++) {
613 if(ldif_conv_table[i] >= 0 && li[i] && *li[i])
614 item_fput(item,ldif_conv_table[i],xstrdup(li[i]));
617 add_item2database(item);
619 for(i=0; i < LDIF_ITEM_FIELDS; i++)
626 ldif_convert(ldif_item item, char *type, char *value)
630 if(!strcmp(type, "dn")) {
635 for(i=0; i < LDIF_ITEM_FIELDS - 1; i++) {
636 if(!strcasecmp(ldif_field_names[i], type) && *value) {
637 item_fput(item, i, xstrdup(value));
644 ldif_parse_file(FILE *handle)
647 char *next_line = NULL;
652 memset(item, 0, sizeof(item));
655 line = ldif_read_line(handle, &next_line);
657 // EOF or empty lines: continue;
658 if(!line || *line == '\0') continue;
660 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
662 continue; /* just skip the errors */
665 ldif_fix_string(value);
667 ldif_convert(item, type, value);
670 } while ( !feof(handle) );
672 ldif_convert(item, "dn", "");
678 ldif_fix_string(char *str)
682 for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
683 str[i] = ( str[j] == (char)0xc3 ?
684 (char) str[++j] + (char) 0x40 :
695 * mutt alias import filter
701 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
705 abook_list *glist = NULL;
707 if( !(line = ptr = getaline(in)) )
708 return 1; /* error / EOF */
712 if(strncmp("alias", ptr, 5)) {
720 /* If the group option is used, save the groups */
724 for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
730 abook_list_append(&glist,xstrndup(start, end - start));
734 if(n_groups && groups)
735 *groups = abook_list_to_csv(glist);
737 abook_list_free(&glist);
745 *alias = xstrndup(start, end - start);
748 *rest = xstrdup(ptr);
755 mutt_fix_quoting(char *p)
775 mutt_parse_email(list_item item)
777 char *line = item_fget(item, NAME);
785 mutt_fix_quoting(line);
786 tmp = strconcat("From: ", line, NULL);
787 getname(tmp, &name, &email);
791 item_fput(item, NAME, name);
796 item_fput(item, EMAIL, email);
801 * this is completely broken
804 while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
805 tmp = strconcat("From: ", ++start, NULL);
806 getname(tmp, &name, &email);
811 tmp = strconcat(item[EMAIL], ",", email, NULL);
823 mutt_parse_file(FILE *in)
825 list_item item = item_create();
828 memset(item, 0, fields_count * sizeof(char *));
830 if(!mutt_read_line(in,
831 (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
832 (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
833 &item[field_id(NAME)]) )
834 mutt_parse_email(item);
841 add_item2database(item);
849 * end of mutt alias import filter
858 ldif_fput_type_and_value(FILE *out,char *type, char *value )
862 tmp = ldif_type_and_value(type, value, strlen(value));
870 ldif_export_database(FILE *out, struct db_enumerator e)
872 char email[MAX_EMAILSTR_LEN];
874 fprintf(out, "version: 1\n");
876 db_enumerate_items(e) {
879 get_first_email(email, e.item);
882 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
884 tmp = strdup_printf("cn=%s",db_name_get(e.item));
886 ldif_fput_type_and_value(out, "dn", tmp);
889 for(j = 0; j < LDIF_ITEM_FIELDS; j++) {
890 if(ldif_conv_table[j] >= 0) {
891 if(ldif_conv_table[j] == EMAIL) {
892 if(*email) // don't dump en empty email field
893 ldif_fput_type_and_value(out,
897 else if(db_fget(e.item,ldif_conv_table[j]))
898 ldif_fput_type_and_value(out,
901 ldif_conv_table[j]));
905 fprintf(out, "objectclass: top\n"
906 "objectclass: person\n\n");
913 * end of ldif export filter
920 static void html_export_write_head(FILE *out);
921 static void html_export_write_tail(FILE *out);
923 extern struct index_elem *index_elements;
926 html_print_emails(FILE *out, struct list_field *f)
928 abook_list *l = csv_to_abook_list(f->data);
930 for(; l; l = l->next) {
931 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
940 html_export_database(FILE *out, struct db_enumerator e)
943 struct index_elem *cur;
950 html_export_write_head(out);
952 db_enumerate_items(e) {
953 fprintf(out, "<tr>");
954 for(cur = index_elements; cur; cur = cur->next) {
955 if(cur->type != INDEX_FIELD)
958 get_list_field(e.item, cur, &f);
960 if(f.type == FIELD_EMAILS) {
961 fprintf(out, "<td>");
962 html_print_emails(out, &f);
963 fprintf(out, "</td>");
966 fprintf(out, "<td>%s</td>", safe_str(f.data));
969 fprintf(out, "</tr>\n");
972 html_export_write_tail(out);
978 html_export_write_head(FILE *out)
980 char *realname = get_real_name(), *str;
981 struct index_elem *cur;
983 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
984 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
986 fprintf(out, "\n</head>\n<body>\n");
987 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
988 fprintf(out, "<br><br>\n\n");
990 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
991 for(cur = index_elements; cur; cur = cur->next) {
992 if(cur->type != INDEX_FIELD)
995 get_field_info(cur->d.field.id, NULL, &str, NULL);
996 fprintf(out, "<th>%s</th>", str);
998 fprintf(out, "</tr>\n\n");
1004 html_export_write_tail(FILE *out)
1006 fprintf(out, "\n</table>\n");
1007 fprintf(out, "\n</body>\n</html>\n");
1011 * end of html export filter
1016 * pine addressbook import filter
1019 #define PINE_BUF_SIZE 2048
1022 pine_fixbuf(char *buf)
1026 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1027 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1031 pine_convert_emails(char *s)
1036 if(s == NULL || *s != '(')
1039 for(i = 0; s[i]; i++)
1042 if( ( tmp = strchr(s,')')) )
1045 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1046 if(i > MAX_LIST_ITEMS - 1) {
1054 pine_parse_buf(char *buf)
1059 char tmp[PINE_BUF_SIZE];
1061 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1063 item = item_create();
1065 for(i=0, last=0; !last ; i++) {
1066 if( !(end = strchr(start, '\t')) )
1069 len = last ? strlen(start) : (int) (end-start);
1070 len = min(len, PINE_BUF_SIZE - 1);
1072 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1073 && pine_conv_table[i] >= 0) {
1074 strncpy(tmp, start, len);
1077 item_fput(item, pine_conv_table[i],
1083 pine_convert_emails(item_fget(item, EMAIL));
1084 add_item2database(item);
1089 #define LINESIZE 1024
1092 pine_parse_file(FILE *in)
1094 char line[LINESIZE];
1099 fgets(line, LINESIZE, in);
1103 buf = xrealloc(buf, i*LINESIZE);
1106 fgets(line, LINESIZE, in);
1108 if(*ptr != ' ' || feof(in))
1122 pine_parse_buf(buf);
1131 * end of pine addressbook import filter
1136 * pine addressbook export filter
1138 * filter doesn't wrap the lines as it should but Pine seems to handle
1139 * created files without problems - JH
1143 pine_export_database(FILE *out, struct db_enumerator e)
1147 db_enumerate_items(e) {
1148 emails = db_email_get(e.item);
1149 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1150 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1151 safe_str(db_fget(e.item, NICK)),
1152 safe_str(db_name_get(e.item)),
1154 safe_str(db_fget(e.item, NOTES))
1163 * end of pine addressbook export filter
1172 * these files should be parsed according to a certain
1173 * lay out, or the default if layout is not given, at
1174 * the moment only default is done...
1177 #define CSV_COMMENT_CHAR '#'
1178 #define CSV_DUPLICATE_SEPARATOR " "
1179 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1181 static int csv_conv_table[] = {
1189 static int allcsv_conv_table[] = {
1208 static int palmcsv_conv_table[] = {
1209 NAME, /* Last name */
1210 NAME, /* First name */
1227 csv_convert_emails(char *s)
1235 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1236 if(i > MAX_LIST_ITEMS - 1) {
1244 csv_remove_quotes(char *s)
1246 char *copy, *trimmed;
1249 copy = trimmed = xstrdup(s);
1252 len = strlen(trimmed);
1253 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1258 trimmed[len - 1] = 0;
1260 trimmed = xstrdup(trimmed);
1270 csv_field_to_item(int *table_base, size_t table_size, int field)
1272 if(field < table_size)
1273 return field_id(table_base[field]);
1279 csv_store_item(list_item item, int i, char *s)
1281 char *newstr = NULL;
1286 if( !(newstr = csv_remove_quotes(s)) )
1290 if (item[i] != NULL) {
1291 char *oldstr = item[i];
1293 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1306 csv_is_valid_quote_end(char *p)
1314 else if(!ISSPACE(*p))
1322 csv_is_valid_quote_start(char *p)
1327 else if(!ISSPACE(*p))
1335 csv_parse_line(char *line, int *table_base, size_t table_size)
1339 bool in_quote = FALSE;
1342 item = item_create();
1344 for(p = start = line, field = 0; *p; p++) {
1346 if(csv_is_valid_quote_end(p))
1349 if ( (((p - start) / sizeof (char)) < 2 ) &&
1350 csv_is_valid_quote_start(p) )
1354 if(*p == ',' && !in_quote) {
1356 csv_store_item(item,
1357 csv_field_to_item(table_base,table_size,field),
1366 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1369 csv_convert_emails(item_fget(item, EMAIL));
1370 add_item2database(item);
1375 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1380 line = getaline(in);
1382 if(line && *line && *line != CSV_COMMENT_CHAR)
1383 csv_parse_line(line, conv_table, table_size);
1392 csv_parse_file(FILE *in)
1394 return csv_parse_file_common(in, csv_conv_table,
1395 CSV_TABLE_SIZE(csv_conv_table));
1399 allcsv_parse_file(FILE *in)
1401 return csv_parse_file_common(in, allcsv_conv_table,
1402 CSV_TABLE_SIZE(allcsv_conv_table));
1406 palmcsv_parse_file(FILE *in)
1408 return csv_parse_file_common(in, palmcsv_conv_table,
1409 CSV_TABLE_SIZE(palmcsv_conv_table));
1413 * end of csv import filter
1417 * vCard import filter
1420 static char *vcard_fields[] = {
1421 "FN", /* FORMATTED NAME */
1422 "EMAIL", /* EMAIL */
1423 "ADR", /* ADDRESS */
1424 "ADR", /* ADDRESS2 - not used */
1428 "ADR", /* COUNTRY */
1430 "TEL", /* WORKPHONE */
1432 "TEL", /* MOBILEPHONE */
1433 "NICKNAME", /* NICK */
1436 "N", /* NAME: special case/mapping in vcard_parse_line() */
1437 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1441 * mappings between vCard ADR field and abook's ADDRESS
1442 * see rfc2426 section 3.2.1
1444 static int vcard_address_fields[] = {
1445 -1, /* vCard(post office box) - not used */
1446 -1, /* vCard(the extended address) - not used */
1447 2, /* vCard(the street address) - ADDRESS */
1448 4, /* vCard(the locality) - CITY */
1449 5, /* vCard(the region) - STATE */
1450 6, /* vCard(the postal code) - ZIP */
1451 7 /* vCard(the country name) - COUNTRY */
1456 VCARD_KEY_ATTRIBUTE,
1461 vcard_get_line_element(char *line, int element)
1464 char *line_copy = 0;
1470 line_copy = xstrdup(line);
1472 /* change newline characters, if present, to end of string */
1473 for(i=0; line_copy[i]; i++) {
1474 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1475 line_copy[i] = '\0';
1480 /* separate key from value */
1481 for(i=0; line_copy[i]; i++) {
1482 if(line_copy[i] == ':') {
1483 line_copy[i] = '\0';
1485 value = &line_copy[i+1];
1490 /* separate key from key attributes */
1491 /* works for vCard 2 as well (automagically) */
1493 for(i=0; key[i]; i++) {
1496 key_attr = &key[i+1];
1505 result = xstrdup(key);
1507 case VCARD_KEY_ATTRIBUTE:
1509 result = xstrdup(key_attr);
1513 result = xstrdup(value);
1522 vcard_parse_email(list_item item, char *line)
1526 email = vcard_get_line_element(line, VCARD_VALUE);
1529 item[1] = strconcat(item[1], ",", email, 0);
1538 vcard_parse_address(list_item item, char *line)
1543 char *address_field;
1545 value = vcard_get_line_element(line, VCARD_VALUE);
1549 address_field = value;
1550 for(i=k=0; value[i]; i++) {
1551 if(value[i] == ';') {
1553 if(vcard_address_fields[k] >= 0) {
1554 item[vcard_address_fields[k]] = xstrdup(address_field);
1556 address_field = &value[i+1];
1558 if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1562 item[vcard_address_fields[k]] = xstrdup(address_field);
1567 vcard_parse_name(list_item item, char *line)
1569 // store the "N" field into "NAME" *if* no "FN:"
1570 // value has already been stored here
1574 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1575 // "N:" can be multivalued => replace ';' separators by ' '
1576 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1578 // http://www.daniweb.com/software-development/c/code/216919
1579 char *original = item[0], *p = original;
1582 if (*original != ' ' || trimmed) {
1583 trimmed = 1; *p++ = *original;
1585 } while(*original++);
1589 vcard_parse_phone(list_item item, char *line)
1591 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1592 char *value = vcard_get_line_element(line, VCARD_VALUE);
1594 /* set the standard number */
1595 if (!type) item_fput(item, PHONE, value);
1598 * see rfc2426 section 3.3.1
1599 * Note: we probably support both vCard 2 and 3
1602 if (strcasestr(type, "home") != NULL)
1603 item_fput(item, PHONE, xstrdup(value));
1604 else if (strcasestr(type, "work") != NULL)
1605 item_fput(item, WORKPHONE, xstrdup(value));
1606 else if (strcasestr(type, "fax") != NULL)
1607 item_fput(item, FAX, xstrdup(value));
1608 else if (strcasestr(type, "cell") != NULL)
1609 item_fput(item, MOBILEPHONE, xstrdup(value));
1617 vcard_parse_line(list_item item, char *line)
1622 for(i=0; vcard_fields[i]; i++) {
1623 key = vcard_fields[i];
1625 if(0 == strncmp(key, line, strlen(key))) {
1626 if(0 == strcmp(key, "EMAIL"))
1627 vcard_parse_email(item, line);
1629 vcard_parse_address(item, line);
1630 else if(0 == strcmp(key, "TEL"))
1631 vcard_parse_phone(item, line);
1632 else if(0 == strcmp(key, "N"))
1633 vcard_parse_name(item, line);
1635 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1642 vcard_parse_item(FILE *in)
1645 list_item item = item_create();
1648 line = getaline(in);
1650 if(line && !strncmp("END:VCARD", line, 9)) {
1655 vcard_parse_line(item, line);
1660 add_item2database(item);
1665 vcard_parse_file(FILE *in)
1670 line = getaline(in);
1672 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1674 vcard_parse_item(in);
1685 * end of vCard import filter
1689 * csv addressbook export filters
1692 #define CSV_LAST (-1)
1693 #define CSV_UNDEFINED (-2)
1694 #define CSV_SPECIAL(X) (-3 - (X))
1695 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1698 csv_export_common(FILE *out, struct db_enumerator e,
1699 int fields[], void (*special_func)(FILE *, int, int))
1703 db_enumerate_items(e) {
1704 for(i = 0; fields[i] != CSV_LAST; i++) {
1705 if(fields[i] == CSV_UNDEFINED)
1706 fprintf(out, "\"\"");
1707 else if(CSV_IS_SPECIAL(fields[i])) {
1709 (*special_func)(out, e.item, fields[i]);
1712 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1713 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1715 safe_str(database[e.item][field_idx(fields[i])])
1717 fprintf(out, "\"%s\"",
1718 safe_str(db_fget(e.item,fields[i])));
1720 if(fields[i + 1] != CSV_LAST)
1730 csv_export_database(FILE *out, struct db_enumerator e)
1732 int csv_export_fields[] = {
1741 csv_export_common(out, e, csv_export_fields, NULL);
1747 allcsv_export_database(FILE *out, struct db_enumerator e)
1750 * TODO: Should get these atomatically from abook_fileds
1753 int allcsv_export_fields[] = {
1775 fprintf(out, "\"NAME\",");
1776 fprintf(out, "\"EMAIL\",");
1777 fprintf(out, "\"ADDRESS\",");
1778 fprintf(out, "\"ADDRESS2\",");
1779 fprintf(out, "\"CITY\",");
1780 fprintf(out, "\"STATE\",");
1781 fprintf(out, "\"ZIP\",");
1782 fprintf(out, "\"COUNTRY\",");
1783 fprintf(out, "\"PHONE\",");
1784 fprintf(out, "\"WORKPHONE\",");
1785 fprintf(out, "\"FAX\",");
1786 fprintf(out, "\"MOBILEPHONE\",");
1787 fprintf(out, "\"NICK\",");
1788 fprintf(out, "\"URL\",");
1789 fprintf(out, "\"NOTES\",");
1790 fprintf(out, "\"ANNIVERSARY\",");
1791 fprintf(out, "\"GROUPS\"\n");
1793 csv_export_common(out, e, allcsv_export_fields, NULL);
1802 #define PALM_CSV_NAME CSV_SPECIAL(0)
1803 #define PALM_CSV_END CSV_SPECIAL(1)
1804 #define PALM_CSV_CAT CSV_SPECIAL(2)
1807 palm_split_and_write_name(FILE *out, char *name)
1813 if ( (p = strchr(name, ' ')) ) {
1817 fprintf(out, "\"%s\",\"" , p + 1);
1818 fwrite((void *)name, p - name, sizeof(char), out);
1821 fprintf(out, "\"%s\"", safe_str(name));
1826 palm_csv_handle_specials(FILE *out, int item, int field)
1830 palm_split_and_write_name(out, db_name_get(item));
1833 fprintf(out, "\"abook\"");
1836 fprintf(out, "\"0\"");
1844 palm_export_database(FILE *out, struct db_enumerator e)
1846 int palm_export_fields[] = {
1847 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1848 CSV_UNDEFINED, /* TITLE */
1849 CSV_UNDEFINED, /* COMPANY */
1850 WORKPHONE, /* WORK PHONE */
1851 PHONE, /* HOME PHONE */
1853 MOBILEPHONE, /* OTHER */
1855 ADDRESS, /* ADDRESS */
1859 COUNTRY, /* COUNTRY */
1860 NICK, /* DEFINED 1 */
1861 URL, /* DEFINED 2 */
1862 CSV_UNDEFINED, /* DEFINED 3 */
1863 CSV_UNDEFINED, /* DEFINED 4 */
1865 PALM_CSV_END, /* "0" */
1866 PALM_CSV_CAT, /* CATEGORY */
1870 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1876 * end of csv export filters
1880 * vCard 2 addressbook export filter
1884 vcard_export_database(FILE *out, struct db_enumerator e)
1886 db_enumerate_items(e)
1887 vcard_export_item(out, e.item);
1892 vcard_export_item(FILE *out, int item)
1896 abook_list *emails, *em;
1897 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
1898 safe_str(db_name_get(item)));
1900 name = get_surname(db_name_get(item));
1901 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
1902 if((db_name_get(item))[j] == ' ')
1905 fprintf(out, "N:%s;%.*s\r\n",
1908 safe_str(db_name_get(item))
1913 if(db_fget(item, ADDRESS))
1914 fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
1915 safe_str(db_fget(item, ADDRESS)),
1916 safe_str(db_fget(item, ADDRESS2)),
1917 safe_str(db_fget(item, CITY)),
1918 safe_str(db_fget(item, STATE)),
1919 safe_str(db_fget(item, ZIP)),
1920 safe_str(db_fget(item, COUNTRY))
1923 if(db_fget(item, PHONE))
1924 fprintf(out, "TEL;HOME:%s\r\n",
1925 db_fget(item, PHONE));
1926 if(db_fget(item, WORKPHONE))
1927 fprintf(out, "TEL;WORK:%s\r\n",
1928 db_fget(item, WORKPHONE));
1929 if(db_fget(item, FAX))
1930 fprintf(out, "TEL;FAX:%s\r\n",
1931 db_fget(item, FAX));
1932 if(db_fget(item, MOBILEPHONE))
1933 fprintf(out, "TEL;CELL:%s\r\n",
1934 db_fget(item, MOBILEPHONE));
1936 tmp = db_email_get(item);
1938 emails = csv_to_abook_list(tmp);
1940 for(em = emails; em; em = em->next)
1941 fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
1943 abook_list_free(&emails);
1947 if(db_fget(item, NOTES))
1948 fprintf(out, "NOTE:%s\r\n",
1949 db_fget(item, NOTES));
1950 if(db_fget(item, URL))
1951 fprintf(out, "URL:%s\r\n",
1952 db_fget(item, URL));
1954 fprintf(out, "END:VCARD\r\n\r\n");
1959 * end of vCard export filter
1964 * mutt alias export filter
1968 mutt_alias_genalias(int i)
1972 if(db_fget(i, NICK))
1973 return xstrdup(db_fget(i, NICK));
1975 tmp = xstrdup(db_name_get(i));
1977 if( ( pos = strchr(tmp, ' ') ) )
1986 * This function is a variant of abook_list_to_csv
1989 mutt_alias_gengroups(int i)
1991 char *groups, *res = NULL;
1992 char groupstr[7] = "-group ";
1993 abook_list *list, *tmp;
1995 groups = db_fget(i, GROUPS);
2000 list = csv_to_abook_list(groups);
2001 for(tmp = list; tmp; tmp = tmp->next) {
2003 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2004 res = strcpy(res, groupstr);
2006 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2008 strcat(res, groupstr);
2010 strcat(res, tmp->data);
2012 abook_list_free(&list);
2019 mutt_alias_export(FILE *out, struct db_enumerator e)
2021 char email[MAX_EMAIL_LEN];
2023 char *groups = NULL;
2024 int email_addresses;
2027 db_enumerate_items(e) {
2028 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2029 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2030 get_first_email(email, e.item);
2032 /* do not output contacts without email address */
2033 /* cause this does not make sense in mutt aliases */
2036 /* output first email address */
2037 fprintf(out,"alias ");
2039 fprintf(out, "%s ", groups);
2041 fprintf(out, "%s ", alias);
2042 fprintf(out, "%s <%s>\n",
2043 db_name_get(e.item),
2046 /* number of email addresses */
2047 email_addresses = 1;
2048 ptr = db_email_get(e.item);
2049 while (*ptr != '\0') {
2056 /* output other email addresses */
2057 while (email_addresses-- > 1) {
2058 roll_emails(e.item, ROTATE_RIGHT);
2059 get_first_email(email, e.item);
2060 fprintf(out,"alias ");
2062 fprintf(out, "%s ", groups);
2064 fprintf(out, "%s__%s ", alias, email);
2066 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2067 fprintf(out, "%s <%s>\n",
2068 db_name_get(e.item),
2071 roll_emails(e.item, ROTATE_RIGHT);
2080 void muttq_print_item(FILE *file, int item)
2082 abook_list *emails, *e;
2083 char *tmp = db_email_get(item);
2085 emails = csv_to_abook_list(tmp);
2088 for(e = emails; e; e = e->next) {
2089 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2090 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2092 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2095 abook_list_free(&emails);
2099 mutt_query_export_database(FILE *out, struct db_enumerator e)
2101 fprintf(out, "All items\n");
2102 db_enumerate_items(e)
2103 muttq_print_item(out, e.item);
2108 * end of mutt alias export filter
2113 * printable export filter
2118 text_write_address_us(FILE *out, int i) {
2119 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2121 if(db_fget(i, ADDRESS2))
2122 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2124 if(db_fget(i, CITY))
2125 fprintf(out, "\n%s", db_fget(i, CITY));
2127 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2130 if(db_fget(i, STATE)) {
2131 fprintf(out, "%s", db_fget(i, STATE));
2137 fprintf(out, "%s", db_fget(i, ZIP));
2140 if(db_fget(i, COUNTRY))
2141 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2146 text_write_address_uk(FILE *out, int i) {
2149 for(j = ADDRESS; j <= COUNTRY; j++)
2151 fprintf(out, "\n%s", db_fget(i, j));
2155 text_write_address_eu(FILE *out, int i) {
2156 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2158 if(db_fget(i, ADDRESS2))
2159 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2161 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2164 if(db_fget(i, ZIP)) {
2165 fprintf(out, "%s", db_fget(i, ZIP));
2166 if(db_fget(i, CITY))
2170 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2173 if(db_fget(i, STATE))
2174 fprintf(out, "\n%s", db_fget(i, STATE));
2176 if(db_fget(i, COUNTRY))
2177 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2181 text_export_database(FILE * out, struct db_enumerator e)
2183 abook_list *emails, *em;
2185 char *realname = get_real_name(), *str = NULL, *tmp;
2186 char *style = opt_get_str(STR_ADDRESS_STYLE);
2189 "-----------------------------------------\n%s's address book\n"
2190 "-----------------------------------------\n\n\n",
2194 db_enumerate_items(e) {
2196 "-----------------------------------------\n\n");
2197 fprintf(out, "%s", db_name_get(e.item));
2198 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2199 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2202 tmp = db_email_get(e.item);
2204 emails = csv_to_abook_list(tmp);
2207 for(em = emails; em; em = em->next)
2208 fprintf(out, "%s\n", em->data);
2210 abook_list_free(&emails);
2214 if(db_fget(e.item, ADDRESS)) {
2215 if(!safe_strcmp(style, "us")) /* US like */
2216 text_write_address_us(out, e.item);
2217 else if(!safe_strcmp(style, "uk")) /* UK like */
2218 text_write_address_uk(out, e.item);
2220 text_write_address_eu(out, e.item);
2225 if((db_fget(e.item, PHONE)) ||
2226 (db_fget(e.item, WORKPHONE)) ||
2227 (db_fget(e.item, FAX)) ||
2228 (db_fget(e.item, MOBILEPHONE))) {
2230 for(j = PHONE; j <= MOBILEPHONE; j++)
2231 if(db_fget(e.item, j)) {
2232 get_field_info(field_id(j),
2234 fprintf(out, "%s: %s\n", str,
2235 db_fget(e.item, j));
2239 if(db_fget(e.item, URL))
2240 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2241 if(db_fget(e.item, NOTES))
2242 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2247 fprintf(out, "-----------------------------------------\n");
2253 * end of printable export filter
2257 * elm alias export filter
2261 elm_alias_export(FILE *out, struct db_enumerator e)
2263 char email[MAX_EMAIL_LEN];
2266 db_enumerate_items(e) {
2267 alias = mutt_alias_genalias(e.item);
2268 get_first_email(email, e.item);
2269 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2277 * end of elm alias export filter
2282 * Spruce export filter
2286 spruce_export_database (FILE *out, struct db_enumerator e)
2288 char email[MAX_EMAIL_LEN];
2290 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2292 db_enumerate_items(e) {
2293 get_first_email(email, e.item);
2294 if(strcmp(email, "")) {
2295 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2297 db_name_get(e.item),
2299 safe_str(db_fget(e.item, NOTES))
2304 fprintf (out, "# End of address book file.\n");
2310 * end of Spruce export filter
2314 * wanderlust addressbook export filter
2318 wl_export_database(FILE *out, struct db_enumerator e)
2320 char email[MAX_EMAIL_LEN];
2322 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2323 db_enumerate_items(e) {
2324 get_first_email(email, e.item);
2327 "%s\t\"%s\"\t\"%s\"\n",
2329 safe_str(db_fget(e.item, NICK)),
2330 safe_str(db_name_get(e.item))
2335 fprintf (out, "\n# End of address book file.\n");
2341 * end of wanderlust addressbook export filter
2345 * BSD calendar export filter
2349 bsdcal_export_database(FILE *out, struct db_enumerator e)
2351 db_enumerate_items(e) {
2352 int year, month = 0, day = 0;
2353 char *anniversary = db_fget(e.item, ANNIVERSARY);
2356 if(!parse_date_string(anniversary, &day, &month, &year))
2360 _("%02d/%02d\tAnniversary of %s\n"),
2363 safe_str(db_name_get(e.item))
2371 // see enum field_types @database.h
2372 static char *conv_table[] = {
2393 find_field_enum(char *s) {
2395 while (conv_table[i]) {
2396 if(!safe_strcmp(conv_table[i], s))
2404 /* Convert a string with named placeholders to
2405 a *printf() compatible string.
2406 Stores the abook field values into ft. */
2408 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2410 if(! fmt_string || ! ft) {
2411 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2415 char *p, *start, *field_name = NULL;
2421 p = strchr(start, '}');
2423 fprintf(stderr, _("parse_custom_format: invalid format\n"));
2426 strcat(fmt_string, "%s");
2427 field_name = strndup(start, (size_t)(p-start));
2428 *ft = find_field_enum(field_name);
2430 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2438 p = strchr(start, '{');
2440 strncat(fmt_string, start, (size_t)(p-start));
2444 strncat( fmt_string,
2446 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2455 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2458 // used to store the format string from --outformatstr when "custom" format is used
2459 // default value overriden in export_file()
2460 extern char *parsed_custom_format;
2461 extern enum field_types *custom_format_fields;
2463 /* wrapper for custom_export_item:
2464 1) avoid messing with extern pointer
2466 3) follow the prototype needed for an abook_output_item_filter entry */
2468 custom_print_item(FILE *out, int item)
2471 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2476 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2480 // if the first character is '!':
2481 // we first check that all fields exist before continuing
2483 enum field_types *ftp = ft;
2485 if(! db_fget(item, *ft) )
2494 if(!strncmp(fmt, "%s", 2)) {
2495 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2498 } else if (*ft == 66) {
2499 fprintf(out, "%s", fmt);
2502 p = strchr(fmt, '%');
2504 q = strndup(fmt, (size_t)(p-fmt));
2505 fprintf(out, "%s", q);
2510 fprintf(out, "%s", fmt);
2519 // used to store the format string from --outformatstr when "custom" format is used
2520 // default value overriden from abook.c
2521 extern char custom_format[FORMAT_STRING_LEN];
2524 custom_export_database(FILE *out, struct db_enumerator e)
2526 char *format_string =
2527 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2529 enum field_types *ft =
2530 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2532 parse_custom_format(custom_format, format_string, ft);
2534 db_enumerate_items(e) {
2535 if(custom_export_item(out, e.item, format_string, ft) == 0)
2542 * end of BSD calendar export filter