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 if(!li[LDIF_ITEM_FIELDS -1])
616 for(i=0; i < LDIF_ITEM_FIELDS; i++) {
617 if(ldif_conv_table[i] >= 0 && li[i] && *li[i])
618 item_fput(item,ldif_conv_table[i],xstrdup(li[i]));
621 add_item2database(item);
624 for(i=0; i < LDIF_ITEM_FIELDS; i++)
631 ldif_convert(ldif_item item, char *type, char *value)
635 if(!strcmp(type, "dn")) {
640 for(i=0; i < LDIF_ITEM_FIELDS - 1; i++) {
641 if(!strcasecmp(ldif_field_names[i], type) && *value) {
642 item_fput(item, i, xstrdup(value));
649 ldif_parse_file(FILE *handle)
652 char *next_line = NULL;
657 memset(item, 0, sizeof(item));
660 line = ldif_read_line(handle, &next_line);
662 // EOF or empty lines: continue;
663 if(!line || *line == '\0') continue;
665 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
667 continue; /* just skip the errors */
670 ldif_fix_string(value);
672 ldif_convert(item, type, value);
675 } while ( !feof(handle) );
677 ldif_convert(item, "dn", "");
683 ldif_fix_string(char *str)
687 for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
688 str[i] = ( str[j] == (char)0xc3 ?
689 (char) str[++j] + (char) 0x40 :
700 * mutt alias import filter
706 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
710 abook_list *glist = NULL;
712 if( !(line = ptr = getaline(in)) )
713 return 1; /* error / EOF */
717 if(strncmp("alias", ptr, 5)) {
725 /* If the group option is used, save the groups */
729 for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
735 abook_list_append(&glist,xstrndup(start, end - start));
739 if(n_groups && groups)
740 *groups = abook_list_to_csv(glist);
742 abook_list_free(&glist);
750 *alias = xstrndup(start, end - start);
753 *rest = xstrdup(ptr);
760 mutt_fix_quoting(char *p)
780 mutt_parse_email(list_item item)
782 char *line = item_fget(item, NAME);
790 mutt_fix_quoting(line);
791 tmp = strconcat("From: ", line, NULL);
792 getname(tmp, &name, &email);
796 item_fput(item, NAME, name);
801 item_fput(item, EMAIL, email);
806 * this is completely broken
809 while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
810 tmp = strconcat("From: ", ++start, NULL);
811 getname(tmp, &name, &email);
816 tmp = strconcat(item[EMAIL], ",", email, NULL);
828 mutt_parse_file(FILE *in)
830 list_item item = item_create();
833 memset(item, 0, fields_count * sizeof(char *));
835 if(!mutt_read_line(in,
836 (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
837 (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
838 &item[field_id(NAME)]) )
839 mutt_parse_email(item);
846 add_item2database(item);
854 * end of mutt alias import filter
863 ldif_fput_type_and_value(FILE *out,char *type, char *value )
867 tmp = ldif_type_and_value(type, value, strlen(value));
875 ldif_export_database(FILE *out, struct db_enumerator e)
877 char email[MAX_EMAILSTR_LEN];
879 fprintf(out, "version: 1\n");
881 db_enumerate_items(e) {
884 get_first_email(email, e.item);
887 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
889 tmp = strdup_printf("cn=%s",db_name_get(e.item));
891 ldif_fput_type_and_value(out, "dn", tmp);
894 for(j = 0; j < LDIF_ITEM_FIELDS; j++) {
895 if(ldif_conv_table[j] >= 0) {
896 if(ldif_conv_table[j] == EMAIL) {
897 if(*email) // don't dump en empty email field
898 ldif_fput_type_and_value(out,
902 else if(db_fget(e.item,ldif_conv_table[j]))
903 ldif_fput_type_and_value(out,
906 ldif_conv_table[j]));
910 fprintf(out, "objectclass: top\n"
911 "objectclass: person\n\n");
918 * end of ldif export filter
925 static void html_export_write_head(FILE *out);
926 static void html_export_write_tail(FILE *out);
928 extern struct index_elem *index_elements;
931 html_print_emails(FILE *out, struct list_field *f)
933 abook_list *l = csv_to_abook_list(f->data);
935 for(; l; l = l->next) {
936 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
945 html_export_database(FILE *out, struct db_enumerator e)
948 struct index_elem *cur;
955 html_export_write_head(out);
957 db_enumerate_items(e) {
958 fprintf(out, "<tr>");
959 for(cur = index_elements; cur; cur = cur->next) {
960 if(cur->type != INDEX_FIELD)
963 get_list_field(e.item, cur, &f);
965 if(f.type == FIELD_EMAILS) {
966 fprintf(out, "<td>");
967 html_print_emails(out, &f);
968 fprintf(out, "</td>");
971 fprintf(out, "<td>%s</td>", safe_str(f.data));
974 fprintf(out, "</tr>\n");
977 html_export_write_tail(out);
983 html_export_write_head(FILE *out)
985 char *realname = get_real_name(), *str;
986 struct index_elem *cur;
988 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
989 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
991 fprintf(out, "\n</head>\n<body>\n");
992 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
993 fprintf(out, "<br><br>\n\n");
995 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
996 for(cur = index_elements; cur; cur = cur->next) {
997 if(cur->type != INDEX_FIELD)
1000 get_field_info(cur->d.field.id, NULL, &str, NULL);
1001 fprintf(out, "<th>%s</th>", str);
1003 fprintf(out, "</tr>\n\n");
1009 html_export_write_tail(FILE *out)
1011 fprintf(out, "\n</table>\n");
1012 fprintf(out, "\n</body>\n</html>\n");
1016 * end of html export filter
1021 * pine addressbook import filter
1024 #define PINE_BUF_SIZE 2048
1027 pine_fixbuf(char *buf)
1031 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1032 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1036 pine_convert_emails(char *s)
1041 if(s == NULL || *s != '(')
1044 for(i = 0; s[i]; i++)
1047 if( ( tmp = strchr(s,')')) )
1050 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1051 if(i > MAX_LIST_ITEMS - 1) {
1059 pine_parse_buf(char *buf)
1064 char tmp[PINE_BUF_SIZE];
1066 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1068 item = item_create();
1070 for(i=0, last=0; !last ; i++) {
1071 if( !(end = strchr(start, '\t')) )
1074 len = last ? strlen(start) : (int) (end-start);
1075 len = min(len, PINE_BUF_SIZE - 1);
1077 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1078 && pine_conv_table[i] >= 0) {
1079 strncpy(tmp, start, len);
1082 item_fput(item, pine_conv_table[i],
1088 pine_convert_emails(item_fget(item, EMAIL));
1089 add_item2database(item);
1094 #define LINESIZE 1024
1097 pine_parse_file(FILE *in)
1099 char line[LINESIZE];
1104 fgets(line, LINESIZE, in);
1108 buf = xrealloc(buf, i*LINESIZE);
1111 fgets(line, LINESIZE, in);
1113 if(*ptr != ' ' || feof(in))
1127 pine_parse_buf(buf);
1136 * end of pine addressbook import filter
1141 * pine addressbook export filter
1143 * filter doesn't wrap the lines as it should but Pine seems to handle
1144 * created files without problems - JH
1148 pine_export_database(FILE *out, struct db_enumerator e)
1152 db_enumerate_items(e) {
1153 emails = db_email_get(e.item);
1154 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1155 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1156 safe_str(db_fget(e.item, NICK)),
1157 safe_str(db_name_get(e.item)),
1159 safe_str(db_fget(e.item, NOTES))
1168 * end of pine addressbook export filter
1177 * these files should be parsed according to a certain
1178 * lay out, or the default if layout is not given, at
1179 * the moment only default is done...
1182 #define CSV_COMMENT_CHAR '#'
1183 #define CSV_DUPLICATE_SEPARATOR " "
1184 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1186 static int csv_conv_table[] = {
1194 static int allcsv_conv_table[] = {
1213 static int palmcsv_conv_table[] = {
1214 NAME, /* Last name */
1215 NAME, /* First name */
1232 csv_convert_emails(char *s)
1240 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1241 if(i > MAX_LIST_ITEMS - 1) {
1249 csv_remove_quotes(char *s)
1251 char *copy, *trimmed;
1254 copy = trimmed = xstrdup(s);
1257 len = strlen(trimmed);
1258 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1263 trimmed[len - 1] = 0;
1265 trimmed = xstrdup(trimmed);
1275 csv_field_to_item(int *table_base, size_t table_size, int field)
1277 if(field < table_size)
1278 return field_id(table_base[field]);
1284 csv_store_item(list_item item, int i, char *s)
1286 char *newstr = NULL;
1291 if( !(newstr = csv_remove_quotes(s)) )
1295 if (item[i] != NULL) {
1296 char *oldstr = item[i];
1298 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1311 csv_is_valid_quote_end(char *p)
1319 else if(!ISSPACE(*p))
1327 csv_is_valid_quote_start(char *p)
1332 else if(!ISSPACE(*p))
1340 csv_parse_line(char *line, int *table_base, size_t table_size)
1344 bool in_quote = FALSE;
1347 item = item_create();
1349 for(p = start = line, field = 0; *p; p++) {
1351 if(csv_is_valid_quote_end(p))
1354 if ( (((p - start) / sizeof (char)) < 2 ) &&
1355 csv_is_valid_quote_start(p) )
1359 if(*p == ',' && !in_quote) {
1361 csv_store_item(item,
1362 csv_field_to_item(table_base,table_size,field),
1371 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1374 csv_convert_emails(item_fget(item, EMAIL));
1375 add_item2database(item);
1380 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1385 line = getaline(in);
1387 if(line && *line && *line != CSV_COMMENT_CHAR)
1388 csv_parse_line(line, conv_table, table_size);
1397 csv_parse_file(FILE *in)
1399 return csv_parse_file_common(in, csv_conv_table,
1400 CSV_TABLE_SIZE(csv_conv_table));
1404 allcsv_parse_file(FILE *in)
1406 return csv_parse_file_common(in, allcsv_conv_table,
1407 CSV_TABLE_SIZE(allcsv_conv_table));
1411 palmcsv_parse_file(FILE *in)
1413 return csv_parse_file_common(in, palmcsv_conv_table,
1414 CSV_TABLE_SIZE(palmcsv_conv_table));
1418 * end of csv import filter
1422 * vCard import filter
1425 static char *vcard_fields[] = {
1426 "FN", /* FORMATTED NAME */
1427 "EMAIL", /* EMAIL */
1428 "ADR", /* ADDRESS */
1429 "ADR", /* ADDRESS2 - not used */
1433 "ADR", /* COUNTRY */
1435 "TEL", /* WORKPHONE */
1437 "TEL", /* MOBILEPHONE */
1438 "NICKNAME", /* NICK */
1441 "N", /* NAME: special case/mapping in vcard_parse_line() */
1442 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1446 * mappings between vCard ADR field and abook's ADDRESS
1447 * see rfc2426 section 3.2.1
1449 static int vcard_address_fields[] = {
1450 -1, /* vCard(post office box) - not used */
1451 -1, /* vCard(the extended address) - not used */
1452 2, /* vCard(the street address) - ADDRESS */
1453 4, /* vCard(the locality) - CITY */
1454 5, /* vCard(the region) - STATE */
1455 6, /* vCard(the postal code) - ZIP */
1456 7 /* vCard(the country name) - COUNTRY */
1461 VCARD_KEY_ATTRIBUTE,
1466 vcard_get_line_element(char *line, int element)
1469 char *line_copy = 0;
1475 line_copy = xstrdup(line);
1477 /* change newline characters, if present, to end of string */
1478 for(i=0; line_copy[i]; i++) {
1479 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1480 line_copy[i] = '\0';
1485 /* separate key from value */
1486 for(i=0; line_copy[i]; i++) {
1487 if(line_copy[i] == ':') {
1488 line_copy[i] = '\0';
1490 value = &line_copy[i+1];
1495 /* separate key from key attributes */
1496 /* works for vCard 2 as well (automagically) */
1498 for(i=0; key[i]; i++) {
1501 key_attr = &key[i+1];
1510 result = xstrdup(key);
1512 case VCARD_KEY_ATTRIBUTE:
1514 result = xstrdup(key_attr);
1518 result = xstrdup(value);
1527 vcard_parse_email(list_item item, char *line)
1531 email = vcard_get_line_element(line, VCARD_VALUE);
1534 item[1] = strconcat(item[1], ",", email, 0);
1543 vcard_parse_address(list_item item, char *line)
1548 char *address_field;
1550 value = vcard_get_line_element(line, VCARD_VALUE);
1554 address_field = value;
1555 for(i=k=0; value[i]; i++) {
1556 if(value[i] == ';') {
1558 if(vcard_address_fields[k] >= 0) {
1559 item[vcard_address_fields[k]] = xstrdup(address_field);
1561 address_field = &value[i+1];
1563 if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1567 item[vcard_address_fields[k]] = xstrdup(address_field);
1572 vcard_parse_name(list_item item, char *line)
1574 // store the "N" field into "NAME" *if* no "FN:"
1575 // value has already been stored here
1579 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1580 // "N:" can be multivalued => replace ';' separators by ' '
1581 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1583 // http://www.daniweb.com/software-development/c/code/216919
1584 char *original = item[0], *p = original;
1587 if (*original != ' ' || trimmed) {
1588 trimmed = 1; *p++ = *original;
1590 } while(*original++);
1594 vcard_parse_phone(list_item item, char *line)
1596 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1597 char *value = vcard_get_line_element(line, VCARD_VALUE);
1599 /* set the standard number */
1600 if (!type) item_fput(item, PHONE, value);
1603 * see rfc2426 section 3.3.1
1604 * Note: we probably support both vCard 2 and 3
1607 if (strcasestr(type, "home") != NULL)
1608 item_fput(item, PHONE, xstrdup(value));
1609 else if (strcasestr(type, "work") != NULL)
1610 item_fput(item, WORKPHONE, xstrdup(value));
1611 else if (strcasestr(type, "fax") != NULL)
1612 item_fput(item, FAX, xstrdup(value));
1613 else if (strcasestr(type, "cell") != NULL)
1614 item_fput(item, MOBILEPHONE, xstrdup(value));
1622 vcard_parse_line(list_item item, char *line)
1627 for(i=0; vcard_fields[i]; i++) {
1628 key = vcard_fields[i];
1630 if(0 == strncmp(key, line, strlen(key))) {
1631 if(0 == strcmp(key, "EMAIL"))
1632 vcard_parse_email(item, line);
1634 vcard_parse_address(item, line);
1635 else if(0 == strcmp(key, "TEL"))
1636 vcard_parse_phone(item, line);
1637 else if(0 == strcmp(key, "N"))
1638 vcard_parse_name(item, line);
1640 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1647 vcard_parse_item(FILE *in)
1650 list_item item = item_create();
1653 line = getaline(in);
1655 if(line && !strncmp("END:VCARD", line, 9)) {
1660 vcard_parse_line(item, line);
1665 add_item2database(item);
1670 vcard_parse_file(FILE *in)
1675 line = getaline(in);
1677 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1679 vcard_parse_item(in);
1690 * end of vCard import filter
1694 * csv addressbook export filters
1697 #define CSV_LAST (-1)
1698 #define CSV_UNDEFINED (-2)
1699 #define CSV_SPECIAL(X) (-3 - (X))
1700 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1703 csv_export_common(FILE *out, struct db_enumerator e,
1704 int fields[], void (*special_func)(FILE *, int, int))
1708 db_enumerate_items(e) {
1709 for(i = 0; fields[i] != CSV_LAST; i++) {
1710 if(fields[i] == CSV_UNDEFINED)
1711 fprintf(out, "\"\"");
1712 else if(CSV_IS_SPECIAL(fields[i])) {
1714 (*special_func)(out, e.item, fields[i]);
1717 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1718 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1720 safe_str(database[e.item][field_idx(fields[i])])
1722 fprintf(out, "\"%s\"",
1723 safe_str(db_fget(e.item,fields[i])));
1725 if(fields[i + 1] != CSV_LAST)
1735 csv_export_database(FILE *out, struct db_enumerator e)
1737 int csv_export_fields[] = {
1746 csv_export_common(out, e, csv_export_fields, NULL);
1752 allcsv_export_database(FILE *out, struct db_enumerator e)
1755 * TODO: Should get these atomatically from abook_fileds
1758 int allcsv_export_fields[] = {
1780 fprintf(out, "\"NAME\",");
1781 fprintf(out, "\"EMAIL\",");
1782 fprintf(out, "\"ADDRESS\",");
1783 fprintf(out, "\"ADDRESS2\",");
1784 fprintf(out, "\"CITY\",");
1785 fprintf(out, "\"STATE\",");
1786 fprintf(out, "\"ZIP\",");
1787 fprintf(out, "\"COUNTRY\",");
1788 fprintf(out, "\"PHONE\",");
1789 fprintf(out, "\"WORKPHONE\",");
1790 fprintf(out, "\"FAX\",");
1791 fprintf(out, "\"MOBILEPHONE\",");
1792 fprintf(out, "\"NICK\",");
1793 fprintf(out, "\"URL\",");
1794 fprintf(out, "\"NOTES\",");
1795 fprintf(out, "\"ANNIVERSARY\",");
1796 fprintf(out, "\"GROUPS\"\n");
1798 csv_export_common(out, e, allcsv_export_fields, NULL);
1807 #define PALM_CSV_NAME CSV_SPECIAL(0)
1808 #define PALM_CSV_END CSV_SPECIAL(1)
1809 #define PALM_CSV_CAT CSV_SPECIAL(2)
1812 palm_split_and_write_name(FILE *out, char *name)
1818 if ( (p = strchr(name, ' ')) ) {
1822 fprintf(out, "\"%s\",\"" , p + 1);
1823 fwrite((void *)name, p - name, sizeof(char), out);
1826 fprintf(out, "\"%s\"", safe_str(name));
1831 palm_csv_handle_specials(FILE *out, int item, int field)
1835 palm_split_and_write_name(out, db_name_get(item));
1838 fprintf(out, "\"abook\"");
1841 fprintf(out, "\"0\"");
1849 palm_export_database(FILE *out, struct db_enumerator e)
1851 int palm_export_fields[] = {
1852 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1853 CSV_UNDEFINED, /* TITLE */
1854 CSV_UNDEFINED, /* COMPANY */
1855 WORKPHONE, /* WORK PHONE */
1856 PHONE, /* HOME PHONE */
1858 MOBILEPHONE, /* OTHER */
1860 ADDRESS, /* ADDRESS */
1864 COUNTRY, /* COUNTRY */
1865 NICK, /* DEFINED 1 */
1866 URL, /* DEFINED 2 */
1867 CSV_UNDEFINED, /* DEFINED 3 */
1868 CSV_UNDEFINED, /* DEFINED 4 */
1870 PALM_CSV_END, /* "0" */
1871 PALM_CSV_CAT, /* CATEGORY */
1875 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1881 * end of csv export filters
1885 * vCard 2 addressbook export filter
1889 vcard_export_database(FILE *out, struct db_enumerator e)
1891 db_enumerate_items(e)
1892 vcard_export_item(out, e.item);
1897 vcard_export_item(FILE *out, int item)
1901 abook_list *emails, *em;
1902 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
1903 safe_str(db_name_get(item)));
1905 name = get_surname(db_name_get(item));
1906 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
1907 if((db_name_get(item))[j] == ' ')
1910 fprintf(out, "N:%s;%.*s\r\n",
1913 safe_str(db_name_get(item))
1918 if(db_fget(item, ADDRESS))
1919 fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
1920 safe_str(db_fget(item, ADDRESS)),
1921 safe_str(db_fget(item, ADDRESS2)),
1922 safe_str(db_fget(item, CITY)),
1923 safe_str(db_fget(item, STATE)),
1924 safe_str(db_fget(item, ZIP)),
1925 safe_str(db_fget(item, COUNTRY))
1928 if(db_fget(item, PHONE))
1929 fprintf(out, "TEL;HOME:%s\r\n",
1930 db_fget(item, PHONE));
1931 if(db_fget(item, WORKPHONE))
1932 fprintf(out, "TEL;WORK:%s\r\n",
1933 db_fget(item, WORKPHONE));
1934 if(db_fget(item, FAX))
1935 fprintf(out, "TEL;FAX:%s\r\n",
1936 db_fget(item, FAX));
1937 if(db_fget(item, MOBILEPHONE))
1938 fprintf(out, "TEL;CELL:%s\r\n",
1939 db_fget(item, MOBILEPHONE));
1941 tmp = db_email_get(item);
1943 emails = csv_to_abook_list(tmp);
1945 for(em = emails; em; em = em->next)
1946 fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
1948 abook_list_free(&emails);
1952 if(db_fget(item, NOTES))
1953 fprintf(out, "NOTE:%s\r\n",
1954 db_fget(item, NOTES));
1955 if(db_fget(item, URL))
1956 fprintf(out, "URL:%s\r\n",
1957 db_fget(item, URL));
1959 fprintf(out, "END:VCARD\r\n\r\n");
1964 * end of vCard export filter
1969 * mutt alias export filter
1973 mutt_alias_genalias(int i)
1977 if(db_fget(i, NICK))
1978 return xstrdup(db_fget(i, NICK));
1980 tmp = xstrdup(db_name_get(i));
1982 if( ( pos = strchr(tmp, ' ') ) )
1991 * This function is a variant of abook_list_to_csv
1994 mutt_alias_gengroups(int i)
1996 char *groups, *res = NULL;
1997 char groupstr[7] = "-group ";
1998 abook_list *list, *tmp;
2000 groups = db_fget(i, GROUPS);
2005 list = csv_to_abook_list(groups);
2006 for(tmp = list; tmp; tmp = tmp->next) {
2008 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2009 res = strcpy(res, groupstr);
2011 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2013 strcat(res, groupstr);
2015 strcat(res, tmp->data);
2017 abook_list_free(&list);
2024 mutt_alias_export(FILE *out, struct db_enumerator e)
2026 char email[MAX_EMAIL_LEN];
2028 char *groups = NULL;
2029 int email_addresses;
2032 db_enumerate_items(e) {
2033 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2034 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2035 get_first_email(email, e.item);
2037 /* do not output contacts without email address */
2038 /* cause this does not make sense in mutt aliases */
2041 /* output first email address */
2042 fprintf(out,"alias ");
2044 fprintf(out, "%s ", groups);
2046 fprintf(out, "%s ", alias);
2047 fprintf(out, "%s <%s>\n",
2048 db_name_get(e.item),
2051 /* number of email addresses */
2052 email_addresses = 1;
2053 ptr = db_email_get(e.item);
2054 while (*ptr != '\0') {
2061 /* output other email addresses */
2062 while (email_addresses-- > 1) {
2063 roll_emails(e.item, ROTATE_RIGHT);
2064 get_first_email(email, e.item);
2065 fprintf(out,"alias ");
2067 fprintf(out, "%s ", groups);
2069 fprintf(out, "%s__%s ", alias, email);
2071 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2072 fprintf(out, "%s <%s>\n",
2073 db_name_get(e.item),
2076 roll_emails(e.item, ROTATE_RIGHT);
2085 void muttq_print_item(FILE *file, int item)
2087 abook_list *emails, *e;
2088 char *tmp = db_email_get(item);
2090 emails = csv_to_abook_list(tmp);
2093 for(e = emails; e; e = e->next) {
2094 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2095 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2097 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2100 abook_list_free(&emails);
2104 mutt_query_export_database(FILE *out, struct db_enumerator e)
2106 fprintf(out, "All items\n");
2107 db_enumerate_items(e)
2108 muttq_print_item(out, e.item);
2113 * end of mutt alias export filter
2118 * printable export filter
2123 text_write_address_us(FILE *out, int i) {
2124 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2126 if(db_fget(i, ADDRESS2))
2127 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2129 if(db_fget(i, CITY))
2130 fprintf(out, "\n%s", db_fget(i, CITY));
2132 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2135 if(db_fget(i, STATE)) {
2136 fprintf(out, "%s", db_fget(i, STATE));
2142 fprintf(out, "%s", db_fget(i, ZIP));
2145 if(db_fget(i, COUNTRY))
2146 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2151 text_write_address_uk(FILE *out, int i) {
2154 for(j = ADDRESS; j <= COUNTRY; j++)
2156 fprintf(out, "\n%s", db_fget(i, j));
2160 text_write_address_eu(FILE *out, int i) {
2161 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2163 if(db_fget(i, ADDRESS2))
2164 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2166 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2169 if(db_fget(i, ZIP)) {
2170 fprintf(out, "%s", db_fget(i, ZIP));
2171 if(db_fget(i, CITY))
2175 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2178 if(db_fget(i, STATE))
2179 fprintf(out, "\n%s", db_fget(i, STATE));
2181 if(db_fget(i, COUNTRY))
2182 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2186 text_export_database(FILE * out, struct db_enumerator e)
2188 abook_list *emails, *em;
2190 char *realname = get_real_name(), *str = NULL, *tmp;
2191 char *style = opt_get_str(STR_ADDRESS_STYLE);
2194 "-----------------------------------------\n%s's address book\n"
2195 "-----------------------------------------\n\n\n",
2199 db_enumerate_items(e) {
2201 "-----------------------------------------\n\n");
2202 fprintf(out, "%s", db_name_get(e.item));
2203 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2204 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2207 tmp = db_email_get(e.item);
2209 emails = csv_to_abook_list(tmp);
2212 for(em = emails; em; em = em->next)
2213 fprintf(out, "%s\n", em->data);
2215 abook_list_free(&emails);
2219 if(db_fget(e.item, ADDRESS)) {
2220 if(!safe_strcmp(style, "us")) /* US like */
2221 text_write_address_us(out, e.item);
2222 else if(!safe_strcmp(style, "uk")) /* UK like */
2223 text_write_address_uk(out, e.item);
2225 text_write_address_eu(out, e.item);
2230 if((db_fget(e.item, PHONE)) ||
2231 (db_fget(e.item, WORKPHONE)) ||
2232 (db_fget(e.item, FAX)) ||
2233 (db_fget(e.item, MOBILEPHONE))) {
2235 for(j = PHONE; j <= MOBILEPHONE; j++)
2236 if(db_fget(e.item, j)) {
2237 get_field_info(field_id(j),
2239 fprintf(out, "%s: %s\n", str,
2240 db_fget(e.item, j));
2244 if(db_fget(e.item, URL))
2245 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2246 if(db_fget(e.item, NOTES))
2247 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2252 fprintf(out, "-----------------------------------------\n");
2258 * end of printable export filter
2262 * elm alias export filter
2266 elm_alias_export(FILE *out, struct db_enumerator e)
2268 char email[MAX_EMAIL_LEN];
2271 db_enumerate_items(e) {
2272 alias = mutt_alias_genalias(e.item);
2273 get_first_email(email, e.item);
2274 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2282 * end of elm alias export filter
2287 * Spruce export filter
2291 spruce_export_database (FILE *out, struct db_enumerator e)
2293 char email[MAX_EMAIL_LEN];
2295 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2297 db_enumerate_items(e) {
2298 get_first_email(email, e.item);
2299 if(strcmp(email, "")) {
2300 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2302 db_name_get(e.item),
2304 safe_str(db_fget(e.item, NOTES))
2309 fprintf (out, "# End of address book file.\n");
2315 * end of Spruce export filter
2319 * wanderlust addressbook export filter
2323 wl_export_database(FILE *out, struct db_enumerator e)
2325 char email[MAX_EMAIL_LEN];
2327 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2328 db_enumerate_items(e) {
2329 get_first_email(email, e.item);
2332 "%s\t\"%s\"\t\"%s\"\n",
2334 safe_str(db_fget(e.item, NICK)),
2335 safe_str(db_name_get(e.item))
2340 fprintf (out, "\n# End of address book file.\n");
2346 * end of wanderlust addressbook export filter
2350 * BSD calendar export filter
2354 bsdcal_export_database(FILE *out, struct db_enumerator e)
2356 db_enumerate_items(e) {
2357 int year, month = 0, day = 0;
2358 char *anniversary = db_fget(e.item, ANNIVERSARY);
2361 if(!parse_date_string(anniversary, &day, &month, &year))
2365 _("%02d/%02d\tAnniversary of %s\n"),
2368 safe_str(db_name_get(e.item))
2376 // see enum field_types @database.h
2377 static char *conv_table[] = {
2398 find_field_enum(char *s) {
2400 while (conv_table[i]) {
2401 if(!safe_strcmp(conv_table[i], s))
2409 /* Convert a string with named placeholders to
2410 a *printf() compatible string.
2411 Stores the abook field values into ft. */
2413 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2415 if(! fmt_string || ! ft) {
2416 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2420 char *p, *start, *field_name = NULL;
2426 p = strchr(start, '}');
2428 fprintf(stderr, _("parse_custom_format: invalid format\n"));
2431 strcat(fmt_string, "%s");
2432 field_name = strndup(start, (size_t)(p-start));
2433 *ft = find_field_enum(field_name);
2435 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2443 p = strchr(start, '{');
2445 strncat(fmt_string, start, (size_t)(p-start));
2449 strncat( fmt_string,
2451 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2460 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2463 // used to store the format string from --outformatstr when "custom" format is used
2464 // default value overriden in export_file()
2465 extern char *parsed_custom_format;
2466 extern enum field_types *custom_format_fields;
2468 /* wrapper for custom_export_item:
2469 1) avoid messing with extern pointer
2471 3) follow the prototype needed for an abook_output_item_filter entry */
2473 custom_print_item(FILE *out, int item)
2476 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2481 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2485 // if the first character is '!':
2486 // we first check that all fields exist before continuing
2488 enum field_types *ftp = ft;
2490 if(! db_fget(item, *ft) )
2499 if(!strncmp(fmt, "%s", 2)) {
2500 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2503 } else if (*ft == 66) {
2504 fprintf(out, "%s", fmt);
2507 p = strchr(fmt, '%');
2509 q = strndup(fmt, (size_t)(p-fmt));
2510 fprintf(out, "%s", q);
2515 fprintf(out, "%s", fmt);
2524 // used to store the format string from --outformatstr when "custom" format is used
2525 // default value overriden from abook.c
2526 extern char custom_format[FORMAT_STRING_LEN];
2529 custom_export_database(FILE *out, struct db_enumerator e)
2531 char *format_string =
2532 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2534 enum field_types *ft =
2535 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2537 parse_custom_format(custom_format, format_string, ft);
2539 db_enumerate_items(e) {
2540 if(custom_export_item(out, e.item, format_string, ft) == 0)
2547 * end of BSD calendar export filter