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);
886 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
888 ldif_fput_type_and_value(out, "dn", tmp);
891 for(j = 0; j < LDIF_ITEM_FIELDS; j++) {
892 if(ldif_conv_table[j] >= 0) {
893 if(ldif_conv_table[j] == EMAIL)
894 ldif_fput_type_and_value(out,
895 ldif_field_names[j], email);
896 else if(db_fget(e.item,ldif_conv_table[j]))
897 ldif_fput_type_and_value(out,
900 ldif_conv_table[j]));
904 fprintf(out, "objectclass: top\n"
905 "objectclass: person\n\n");
912 * end of ldif export filter
919 static void html_export_write_head(FILE *out);
920 static void html_export_write_tail(FILE *out);
922 extern struct index_elem *index_elements;
925 html_print_emails(FILE *out, struct list_field *f)
927 abook_list *l = csv_to_abook_list(f->data);
929 for(; l; l = l->next) {
930 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
939 html_export_database(FILE *out, struct db_enumerator e)
942 struct index_elem *cur;
949 html_export_write_head(out);
951 db_enumerate_items(e) {
952 fprintf(out, "<tr>");
953 for(cur = index_elements; cur; cur = cur->next) {
954 if(cur->type != INDEX_FIELD)
957 get_list_field(e.item, cur, &f);
959 if(f.type == FIELD_EMAILS) {
960 fprintf(out, "<td>");
961 html_print_emails(out, &f);
962 fprintf(out, "</td>");
965 fprintf(out, "<td>%s</td>", safe_str(f.data));
968 fprintf(out, "</tr>\n");
971 html_export_write_tail(out);
977 html_export_write_head(FILE *out)
979 char *realname = get_real_name(), *str;
980 struct index_elem *cur;
982 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
983 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
985 fprintf(out, "\n</head>\n<body>\n");
986 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
987 fprintf(out, "<br><br>\n\n");
989 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
990 for(cur = index_elements; cur; cur = cur->next) {
991 if(cur->type != INDEX_FIELD)
994 get_field_info(cur->d.field.id, NULL, &str, NULL);
995 fprintf(out, "<th>%s</th>", str);
997 fprintf(out, "</tr>\n\n");
1003 html_export_write_tail(FILE *out)
1005 fprintf(out, "\n</table>\n");
1006 fprintf(out, "\n</body>\n</html>\n");
1010 * end of html export filter
1015 * pine addressbook import filter
1018 #define PINE_BUF_SIZE 2048
1021 pine_fixbuf(char *buf)
1025 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1026 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1030 pine_convert_emails(char *s)
1035 if(s == NULL || *s != '(')
1038 for(i = 0; s[i]; i++)
1041 if( ( tmp = strchr(s,')')) )
1044 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1045 if(i > MAX_LIST_ITEMS - 1) {
1053 pine_parse_buf(char *buf)
1058 char tmp[PINE_BUF_SIZE];
1060 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1062 item = item_create();
1064 for(i=0, last=0; !last ; i++) {
1065 if( !(end = strchr(start, '\t')) )
1068 len = last ? strlen(start) : (int) (end-start);
1069 len = min(len, PINE_BUF_SIZE - 1);
1071 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1072 && pine_conv_table[i] >= 0) {
1073 strncpy(tmp, start, len);
1076 item_fput(item, pine_conv_table[i],
1082 pine_convert_emails(item_fget(item, EMAIL));
1083 add_item2database(item);
1088 #define LINESIZE 1024
1091 pine_parse_file(FILE *in)
1093 char line[LINESIZE];
1098 fgets(line, LINESIZE, in);
1102 buf = xrealloc(buf, i*LINESIZE);
1105 fgets(line, LINESIZE, in);
1107 if(*ptr != ' ' || feof(in))
1121 pine_parse_buf(buf);
1130 * end of pine addressbook import filter
1135 * pine addressbook export filter
1137 * filter doesn't wrap the lines as it should but Pine seems to handle
1138 * created files without problems - JH
1142 pine_export_database(FILE *out, struct db_enumerator e)
1146 db_enumerate_items(e) {
1147 emails = db_email_get(e.item);
1148 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1149 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1150 safe_str(db_fget(e.item, NICK)),
1151 safe_str(db_name_get(e.item)),
1153 safe_str(db_fget(e.item, NOTES))
1162 * end of pine addressbook export filter
1171 * these files should be parsed according to a certain
1172 * lay out, or the default if layout is not given, at
1173 * the moment only default is done...
1176 #define CSV_COMMENT_CHAR '#'
1177 #define CSV_DUPLICATE_SEPARATOR " "
1178 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1180 static int csv_conv_table[] = {
1188 static int allcsv_conv_table[] = {
1207 static int palmcsv_conv_table[] = {
1208 NAME, /* Last name */
1209 NAME, /* First name */
1226 csv_convert_emails(char *s)
1234 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1235 if(i > MAX_LIST_ITEMS - 1) {
1243 csv_remove_quotes(char *s)
1245 char *copy, *trimmed;
1248 copy = trimmed = xstrdup(s);
1251 len = strlen(trimmed);
1252 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1257 trimmed[len - 1] = 0;
1259 trimmed = xstrdup(trimmed);
1269 csv_field_to_item(int *table_base, size_t table_size, int field)
1271 if(field < table_size)
1272 return field_id(table_base[field]);
1278 csv_store_item(list_item item, int i, char *s)
1280 char *newstr = NULL;
1285 if( !(newstr = csv_remove_quotes(s)) )
1289 if (item[i] != NULL) {
1290 char *oldstr = item[i];
1292 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1305 csv_is_valid_quote_end(char *p)
1313 else if(!ISSPACE(*p))
1321 csv_is_valid_quote_start(char *p)
1326 else if(!ISSPACE(*p))
1334 csv_parse_line(char *line, int *table_base, size_t table_size)
1338 bool in_quote = FALSE;
1341 item = item_create();
1343 for(p = start = line, field = 0; *p; p++) {
1345 if(csv_is_valid_quote_end(p))
1348 if ( (((p - start) / sizeof (char)) < 2 ) &&
1349 csv_is_valid_quote_start(p) )
1353 if(*p == ',' && !in_quote) {
1355 csv_store_item(item,
1356 csv_field_to_item(table_base,table_size,field),
1365 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1368 csv_convert_emails(item_fget(item, EMAIL));
1369 add_item2database(item);
1374 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1379 line = getaline(in);
1381 if(line && *line && *line != CSV_COMMENT_CHAR)
1382 csv_parse_line(line, conv_table, table_size);
1391 csv_parse_file(FILE *in)
1393 return csv_parse_file_common(in, csv_conv_table,
1394 CSV_TABLE_SIZE(csv_conv_table));
1398 allcsv_parse_file(FILE *in)
1400 return csv_parse_file_common(in, allcsv_conv_table,
1401 CSV_TABLE_SIZE(allcsv_conv_table));
1405 palmcsv_parse_file(FILE *in)
1407 return csv_parse_file_common(in, palmcsv_conv_table,
1408 CSV_TABLE_SIZE(palmcsv_conv_table));
1412 * end of csv import filter
1416 * vCard import filter
1419 static char *vcard_fields[] = {
1420 "FN", /* FORMATTED NAME */
1421 "EMAIL", /* EMAIL */
1422 "ADR", /* ADDRESS */
1423 "ADR", /* ADDRESS2 - not used */
1427 "ADR", /* COUNTRY */
1429 "TEL", /* WORKPHONE */
1431 "TEL", /* MOBILEPHONE */
1432 "NICKNAME", /* NICK */
1435 "N", /* NAME: special case/mapping in vcard_parse_line() */
1436 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1440 * mappings between vCard ADR field and abook's ADDRESS
1441 * see rfc2426 section 3.2.1
1443 static int vcard_address_fields[] = {
1444 -1, /* vCard(post office box) - not used */
1445 -1, /* vCard(the extended address) - not used */
1446 2, /* vCard(the street address) - ADDRESS */
1447 4, /* vCard(the locality) - CITY */
1448 5, /* vCard(the region) - STATE */
1449 6, /* vCard(the postal code) - ZIP */
1450 7 /* vCard(the country name) - COUNTRY */
1455 VCARD_KEY_ATTRIBUTE,
1460 vcard_get_line_element(char *line, int element)
1463 char *line_copy = 0;
1469 line_copy = xstrdup(line);
1471 /* change newline characters, if present, to end of string */
1472 for(i=0; line_copy[i]; i++) {
1473 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1474 line_copy[i] = '\0';
1479 /* separate key from value */
1480 for(i=0; line_copy[i]; i++) {
1481 if(line_copy[i] == ':') {
1482 line_copy[i] = '\0';
1484 value = &line_copy[i+1];
1489 /* separate key from key attributes */
1490 /* works for vCard 2 as well (automagically) */
1492 for(i=0; key[i]; i++) {
1495 key_attr = &key[i+1];
1504 result = xstrdup(key);
1506 case VCARD_KEY_ATTRIBUTE:
1508 result = xstrdup(key_attr);
1512 result = xstrdup(value);
1521 vcard_parse_email(list_item item, char *line)
1525 email = vcard_get_line_element(line, VCARD_VALUE);
1528 item[1] = strconcat(item[1], ",", email, 0);
1537 vcard_parse_address(list_item item, char *line)
1542 char *address_field;
1544 value = vcard_get_line_element(line, VCARD_VALUE);
1548 address_field = value;
1549 for(i=k=0; value[i]; i++) {
1550 if(value[i] == ';') {
1552 if(vcard_address_fields[k] >= 0) {
1553 item[vcard_address_fields[k]] = xstrdup(address_field);
1555 address_field = &value[i+1];
1557 if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1561 item[vcard_address_fields[k]] = xstrdup(address_field);
1566 vcard_parse_name(list_item item, char *line)
1568 // store the "N" field into "NAME" *if* no "FN:"
1569 // value has already been stored here
1573 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1574 // "N:" can be multivalued => replace ';' separators by ' '
1575 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1577 // http://www.daniweb.com/software-development/c/code/216919
1578 char *original = item[0], *p = original;
1581 if (*original != ' ' || trimmed) {
1582 trimmed = 1; *p++ = *original;
1584 } while(*original++);
1588 vcard_parse_phone(list_item item, char *line)
1590 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1591 char *value = vcard_get_line_element(line, VCARD_VALUE);
1593 /* set the standard number */
1594 if (!type) item_fput(item, PHONE, value);
1597 * see rfc2426 section 3.3.1
1598 * Note: we probably support both vCard 2 and 3
1601 if (strcasestr(type, "home") != NULL)
1602 item_fput(item, PHONE, xstrdup(value));
1603 else if (strcasestr(type, "work") != NULL)
1604 item_fput(item, WORKPHONE, xstrdup(value));
1605 else if (strcasestr(type, "fax") != NULL)
1606 item_fput(item, FAX, xstrdup(value));
1607 else if (strcasestr(type, "cell") != NULL)
1608 item_fput(item, MOBILEPHONE, xstrdup(value));
1616 vcard_parse_line(list_item item, char *line)
1621 for(i=0; vcard_fields[i]; i++) {
1622 key = vcard_fields[i];
1624 if(0 == strncmp(key, line, strlen(key))) {
1625 if(0 == strcmp(key, "EMAIL"))
1626 vcard_parse_email(item, line);
1628 vcard_parse_address(item, line);
1629 else if(0 == strcmp(key, "TEL"))
1630 vcard_parse_phone(item, line);
1631 else if(0 == strcmp(key, "N"))
1632 vcard_parse_name(item, line);
1634 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1641 vcard_parse_item(FILE *in)
1644 list_item item = item_create();
1647 line = getaline(in);
1649 if(line && !strncmp("END:VCARD", line, 9)) {
1654 vcard_parse_line(item, line);
1659 add_item2database(item);
1664 vcard_parse_file(FILE *in)
1669 line = getaline(in);
1671 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1673 vcard_parse_item(in);
1684 * end of vCard import filter
1688 * csv addressbook export filters
1691 #define CSV_LAST (-1)
1692 #define CSV_UNDEFINED (-2)
1693 #define CSV_SPECIAL(X) (-3 - (X))
1694 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1697 csv_export_common(FILE *out, struct db_enumerator e,
1698 int fields[], void (*special_func)(FILE *, int, int))
1702 db_enumerate_items(e) {
1703 for(i = 0; fields[i] != CSV_LAST; i++) {
1704 if(fields[i] == CSV_UNDEFINED)
1705 fprintf(out, "\"\"");
1706 else if(CSV_IS_SPECIAL(fields[i])) {
1708 (*special_func)(out, e.item, fields[i]);
1711 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1712 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1714 safe_str(database[e.item][field_idx(fields[i])])
1716 fprintf(out, "\"%s\"",
1717 safe_str(db_fget(e.item,fields[i])));
1719 if(fields[i + 1] != CSV_LAST)
1729 csv_export_database(FILE *out, struct db_enumerator e)
1731 int csv_export_fields[] = {
1740 csv_export_common(out, e, csv_export_fields, NULL);
1746 allcsv_export_database(FILE *out, struct db_enumerator e)
1749 * TODO: Should get these atomatically from abook_fileds
1752 int allcsv_export_fields[] = {
1774 fprintf(out, "\"NAME\",");
1775 fprintf(out, "\"EMAIL\",");
1776 fprintf(out, "\"ADDRESS\",");
1777 fprintf(out, "\"ADDRESS2\",");
1778 fprintf(out, "\"CITY\",");
1779 fprintf(out, "\"STATE\",");
1780 fprintf(out, "\"ZIP\",");
1781 fprintf(out, "\"COUNTRY\",");
1782 fprintf(out, "\"PHONE\",");
1783 fprintf(out, "\"WORKPHONE\",");
1784 fprintf(out, "\"FAX\",");
1785 fprintf(out, "\"MOBILEPHONE\",");
1786 fprintf(out, "\"NICK\",");
1787 fprintf(out, "\"URL\",");
1788 fprintf(out, "\"NOTES\",");
1789 fprintf(out, "\"ANNIVERSARY\",");
1790 fprintf(out, "\"GROUPS\"\n");
1792 csv_export_common(out, e, allcsv_export_fields, NULL);
1801 #define PALM_CSV_NAME CSV_SPECIAL(0)
1802 #define PALM_CSV_END CSV_SPECIAL(1)
1803 #define PALM_CSV_CAT CSV_SPECIAL(2)
1806 palm_split_and_write_name(FILE *out, char *name)
1812 if ( (p = strchr(name, ' ')) ) {
1816 fprintf(out, "\"%s\",\"" , p + 1);
1817 fwrite((void *)name, p - name, sizeof(char), out);
1820 fprintf(out, "\"%s\"", safe_str(name));
1825 palm_csv_handle_specials(FILE *out, int item, int field)
1829 palm_split_and_write_name(out, db_name_get(item));
1832 fprintf(out, "\"abook\"");
1835 fprintf(out, "\"0\"");
1843 palm_export_database(FILE *out, struct db_enumerator e)
1845 int palm_export_fields[] = {
1846 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1847 CSV_UNDEFINED, /* TITLE */
1848 CSV_UNDEFINED, /* COMPANY */
1849 WORKPHONE, /* WORK PHONE */
1850 PHONE, /* HOME PHONE */
1852 MOBILEPHONE, /* OTHER */
1854 ADDRESS, /* ADDRESS */
1858 COUNTRY, /* COUNTRY */
1859 NICK, /* DEFINED 1 */
1860 URL, /* DEFINED 2 */
1861 CSV_UNDEFINED, /* DEFINED 3 */
1862 CSV_UNDEFINED, /* DEFINED 4 */
1864 PALM_CSV_END, /* "0" */
1865 PALM_CSV_CAT, /* CATEGORY */
1869 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1875 * end of csv export filters
1879 * vCard 2 addressbook export filter
1883 vcard_export_database(FILE *out, struct db_enumerator e)
1885 db_enumerate_items(e)
1886 vcard_export_item(out, e.item);
1891 vcard_export_item(FILE *out, int item)
1895 abook_list *emails, *em;
1896 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
1897 safe_str(db_name_get(item)));
1899 name = get_surname(db_name_get(item));
1900 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
1901 if((db_name_get(item))[j] == ' ')
1904 fprintf(out, "N:%s;%.*s\r\n",
1907 safe_str(db_name_get(item))
1912 if(db_fget(item, ADDRESS))
1913 fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
1914 safe_str(db_fget(item, ADDRESS)),
1915 safe_str(db_fget(item, ADDRESS2)),
1916 safe_str(db_fget(item, CITY)),
1917 safe_str(db_fget(item, STATE)),
1918 safe_str(db_fget(item, ZIP)),
1919 safe_str(db_fget(item, COUNTRY))
1922 if(db_fget(item, PHONE))
1923 fprintf(out, "TEL;HOME:%s\r\n",
1924 db_fget(item, PHONE));
1925 if(db_fget(item, WORKPHONE))
1926 fprintf(out, "TEL;WORK:%s\r\n",
1927 db_fget(item, WORKPHONE));
1928 if(db_fget(item, FAX))
1929 fprintf(out, "TEL;FAX:%s\r\n",
1930 db_fget(item, FAX));
1931 if(db_fget(item, MOBILEPHONE))
1932 fprintf(out, "TEL;CELL:%s\r\n",
1933 db_fget(item, MOBILEPHONE));
1935 tmp = db_email_get(item);
1937 emails = csv_to_abook_list(tmp);
1939 for(em = emails; em; em = em->next)
1940 fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
1942 abook_list_free(&emails);
1946 if(db_fget(item, NOTES))
1947 fprintf(out, "NOTE:%s\r\n",
1948 db_fget(item, NOTES));
1949 if(db_fget(item, URL))
1950 fprintf(out, "URL:%s\r\n",
1951 db_fget(item, URL));
1953 fprintf(out, "END:VCARD\r\n\r\n");
1958 * end of vCard export filter
1963 * mutt alias export filter
1967 mutt_alias_genalias(int i)
1971 if(db_fget(i, NICK))
1972 return xstrdup(db_fget(i, NICK));
1974 tmp = xstrdup(db_name_get(i));
1976 if( ( pos = strchr(tmp, ' ') ) )
1985 * This function is a variant of abook_list_to_csv
1988 mutt_alias_gengroups(int i)
1990 char *groups, *res = NULL;
1991 char groupstr[7] = "-group ";
1992 abook_list *list, *tmp;
1994 groups = db_fget(i, GROUPS);
1999 list = csv_to_abook_list(groups);
2000 for(tmp = list; tmp; tmp = tmp->next) {
2002 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2003 res = strcpy(res, groupstr);
2005 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2007 strcat(res, groupstr);
2009 strcat(res, tmp->data);
2011 abook_list_free(&list);
2018 mutt_alias_export(FILE *out, struct db_enumerator e)
2020 char email[MAX_EMAIL_LEN];
2022 char *groups = NULL;
2023 int email_addresses;
2026 db_enumerate_items(e) {
2027 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2028 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2029 get_first_email(email, e.item);
2031 /* do not output contacts without email address */
2032 /* cause this does not make sense in mutt aliases */
2035 /* output first email address */
2036 fprintf(out,"alias ");
2038 fprintf(out, "%s ", groups);
2040 fprintf(out, "%s ", alias);
2041 fprintf(out, "%s <%s>\n",
2042 db_name_get(e.item),
2045 /* number of email addresses */
2046 email_addresses = 1;
2047 ptr = db_email_get(e.item);
2048 while (*ptr != '\0') {
2055 /* output other email addresses */
2056 while (email_addresses-- > 1) {
2057 roll_emails(e.item, ROTATE_RIGHT);
2058 get_first_email(email, e.item);
2059 fprintf(out,"alias ");
2061 fprintf(out, "%s ", groups);
2063 fprintf(out, "%s__%s ", alias, email);
2065 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2066 fprintf(out, "%s <%s>\n",
2067 db_name_get(e.item),
2070 roll_emails(e.item, ROTATE_RIGHT);
2079 void muttq_print_item(FILE *file, int item)
2081 abook_list *emails, *e;
2082 char *tmp = db_email_get(item);
2084 emails = csv_to_abook_list(tmp);
2087 for(e = emails; e; e = e->next) {
2088 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2089 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2091 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2094 abook_list_free(&emails);
2098 mutt_query_export_database(FILE *out, struct db_enumerator e)
2100 fprintf(out, "All items\n");
2101 db_enumerate_items(e)
2102 muttq_print_item(out, e.item);
2107 * end of mutt alias export filter
2112 * printable export filter
2117 text_write_address_us(FILE *out, int i) {
2118 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2120 if(db_fget(i, ADDRESS2))
2121 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2123 if(db_fget(i, CITY))
2124 fprintf(out, "\n%s", db_fget(i, CITY));
2126 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2129 if(db_fget(i, STATE)) {
2130 fprintf(out, "%s", db_fget(i, STATE));
2136 fprintf(out, "%s", db_fget(i, ZIP));
2139 if(db_fget(i, COUNTRY))
2140 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2145 text_write_address_uk(FILE *out, int i) {
2148 for(j = ADDRESS; j <= COUNTRY; j++)
2150 fprintf(out, "\n%s", db_fget(i, j));
2154 text_write_address_eu(FILE *out, int i) {
2155 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2157 if(db_fget(i, ADDRESS2))
2158 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2160 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2163 if(db_fget(i, ZIP)) {
2164 fprintf(out, "%s", db_fget(i, ZIP));
2165 if(db_fget(i, CITY))
2169 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2172 if(db_fget(i, STATE))
2173 fprintf(out, "\n%s", db_fget(i, STATE));
2175 if(db_fget(i, COUNTRY))
2176 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2180 text_export_database(FILE * out, struct db_enumerator e)
2182 abook_list *emails, *em;
2184 char *realname = get_real_name(), *str = NULL, *tmp;
2185 char *style = opt_get_str(STR_ADDRESS_STYLE);
2188 "-----------------------------------------\n%s's address book\n"
2189 "-----------------------------------------\n\n\n",
2193 db_enumerate_items(e) {
2195 "-----------------------------------------\n\n");
2196 fprintf(out, "%s", db_name_get(e.item));
2197 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2198 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2201 tmp = db_email_get(e.item);
2203 emails = csv_to_abook_list(tmp);
2206 for(em = emails; em; em = em->next)
2207 fprintf(out, "%s\n", em->data);
2209 abook_list_free(&emails);
2213 if(db_fget(e.item, ADDRESS)) {
2214 if(!safe_strcmp(style, "us")) /* US like */
2215 text_write_address_us(out, e.item);
2216 else if(!safe_strcmp(style, "uk")) /* UK like */
2217 text_write_address_uk(out, e.item);
2219 text_write_address_eu(out, e.item);
2224 if((db_fget(e.item, PHONE)) ||
2225 (db_fget(e.item, WORKPHONE)) ||
2226 (db_fget(e.item, FAX)) ||
2227 (db_fget(e.item, MOBILEPHONE))) {
2229 for(j = PHONE; j <= MOBILEPHONE; j++)
2230 if(db_fget(e.item, j)) {
2231 get_field_info(field_id(j),
2233 fprintf(out, "%s: %s\n", str,
2234 db_fget(e.item, j));
2238 if(db_fget(e.item, URL))
2239 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2240 if(db_fget(e.item, NOTES))
2241 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2246 fprintf(out, "-----------------------------------------\n");
2252 * end of printable export filter
2256 * elm alias export filter
2260 elm_alias_export(FILE *out, struct db_enumerator e)
2262 char email[MAX_EMAIL_LEN];
2265 db_enumerate_items(e) {
2266 alias = mutt_alias_genalias(e.item);
2267 get_first_email(email, e.item);
2268 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2276 * end of elm alias export filter
2281 * Spruce export filter
2285 spruce_export_database (FILE *out, struct db_enumerator e)
2287 char email[MAX_EMAIL_LEN];
2289 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2291 db_enumerate_items(e) {
2292 get_first_email(email, e.item);
2293 if(strcmp(email, "")) {
2294 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2296 db_name_get(e.item),
2298 safe_str(db_fget(e.item, NOTES))
2303 fprintf (out, "# End of address book file.\n");
2309 * end of Spruce export filter
2313 * wanderlust addressbook export filter
2317 wl_export_database(FILE *out, struct db_enumerator e)
2319 char email[MAX_EMAIL_LEN];
2321 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2322 db_enumerate_items(e) {
2323 get_first_email(email, e.item);
2326 "%s\t\"%s\"\t\"%s\"\n",
2328 safe_str(db_fget(e.item, NICK)),
2329 safe_str(db_name_get(e.item))
2334 fprintf (out, "\n# End of address book file.\n");
2340 * end of wanderlust addressbook export filter
2344 * BSD calendar export filter
2348 bsdcal_export_database(FILE *out, struct db_enumerator e)
2350 db_enumerate_items(e) {
2351 int year, month = 0, day = 0;
2352 char *anniversary = db_fget(e.item, ANNIVERSARY);
2355 if(!parse_date_string(anniversary, &day, &month, &year))
2359 _("%02d/%02d\tAnniversary of %s\n"),
2362 safe_str(db_name_get(e.item))
2370 // see enum field_types @database.h
2371 static char *conv_table[] = {
2392 find_field_enum(char *s) {
2394 while (conv_table[i]) {
2395 if(!safe_strcmp(conv_table[i], s))
2403 /* Convert a string with named placeholders to
2404 a *printf() compatible string.
2405 Stores the abook field values into ft. */
2407 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2409 if(! fmt_string || ! ft) {
2410 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2414 char *p, *start, *field_name = NULL;
2420 p = strchr(start, '}');
2422 fprintf(stderr, _("parse_custom_format: invalid format\n"));
2425 strcat(fmt_string, "%s");
2426 field_name = strndup(start, (size_t)(p-start));
2427 *ft = find_field_enum(field_name);
2429 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2437 p = strchr(start, '{');
2439 strncat(fmt_string, start, (size_t)(p-start));
2443 strncat( fmt_string,
2445 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2454 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2457 // used to store the format string from --outformatstr when "custom" format is used
2458 // default value overriden in export_file()
2459 extern char *parsed_custom_format;
2460 extern enum field_types *custom_format_fields;
2462 /* wrapper for custom_export_item:
2463 1) avoid messing with extern pointer
2465 3) follow the prototype needed for an abook_output_item_filter entry */
2467 custom_print_item(FILE *out, int item)
2470 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2475 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2479 // if the first character is '!':
2480 // we first check that all fields exist before continuing
2482 enum field_types *ftp = ft;
2484 if(! db_fget(item, *ft) )
2493 if(!strncmp(fmt, "%s", 2)) {
2494 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2497 } else if (*ft == 66) {
2498 fprintf(out, "%s", fmt);
2501 p = strchr(fmt, '%');
2503 q = strndup(fmt, (size_t)(p-fmt));
2504 fprintf(out, "%s", q);
2509 fprintf(out, "%s", fmt);
2518 // used to store the format string from --outformatstr when "custom" format is used
2519 // default value overriden from abook.c
2520 extern char custom_format[FORMAT_STRING_LEN];
2523 custom_export_database(FILE *out, struct db_enumerator e)
2525 char *format_string =
2526 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2528 enum field_types *ft =
2529 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2531 parse_custom_format(custom_format, format_string, ft);
2533 db_enumerate_items(e) {
2534 if(custom_export_item(out, e.item, format_string, ft) == 0)
2541 * end of BSD calendar export filter