]> git.deb.at Git - pkg/abook.git/blob - filter.c
Upload 0.6.1-2 to unstable
[pkg/abook.git] / filter.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #define _GNU_SOURCE
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <pwd.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include "abook_curses.h"
20 #include "filter.h"
21 #include "abook.h"
22 #include "database.h"
23 #include "edit.h"
24 #include "gettext.h"
25 #include "list.h"
26 #include "misc.h"
27 #include "options.h"
28 #include "ui.h"
29 #include "xmalloc.h"
30 #include <assert.h>
31
32 #ifdef HAVE_VFORMAT
33 #include "vcard.h"
34 #endif
35
36 extern abook_field_list *fields_list;
37 extern int fields_count;
38 // see also enum field_types @database.h
39 extern abook_field standard_fields[];
40
41 /*
42  * function declarations
43  */
44
45 /*
46  * import filter prototypes
47  */
48
49 static int      ldif_parse_file(FILE *handle);
50 static int      mutt_parse_file(FILE *in);
51 static int      pine_parse_file(FILE *in);
52 static int      csv_parse_file(FILE *in);
53 static int      allcsv_parse_file(FILE *in);
54 static int      palmcsv_parse_file(FILE *in);
55 static int      vcard_parse_file(FILE *in);
56
57 /*
58  * export filter prototypes
59  */
60
61 static int      ldif_export_database(FILE *out, struct db_enumerator e);
62 static int      html_export_database(FILE *out, struct db_enumerator e);
63 static int      pine_export_database(FILE *out, struct db_enumerator e);
64 static int      csv_export_database(FILE *out, struct db_enumerator e);
65 static int      allcsv_export_database(FILE *out, struct db_enumerator e);
66 static int      palm_export_database(FILE *out, struct db_enumerator e);
67 static int      vcard_export_database(FILE *out, struct db_enumerator e);
68 static int      mutt_alias_export(FILE *out, struct db_enumerator e);
69 static int      mutt_query_export_database(FILE *out, struct db_enumerator e);
70 static int      elm_alias_export(FILE *out, struct db_enumerator e);
71 static int      text_export_database(FILE *out, struct db_enumerator e);
72 static int      spruce_export_database(FILE *out, struct db_enumerator e);
73 static int      wl_export_database(FILE *out, struct db_enumerator e);
74 static int      bsdcal_export_database(FILE *out, struct db_enumerator e);
75 static int      custom_export_database(FILE *out, struct db_enumerator e);
76
77 /*
78  * export filter item prototypes
79  */
80
81 void vcard_export_item(FILE *out, int item);
82 void muttq_print_item(FILE *file, int item);
83 void custom_print_item(FILE *out, int item);
84
85 /*
86  * end of function declarations
87  */
88
89 struct abook_input_filter i_filters[] = {
90         { "abook", N_("abook native format"), parse_database },
91         { "ldif", N_("ldif / Netscape addressbook"), ldif_parse_file },
92         { "mutt", N_("mutt alias"), mutt_parse_file },
93         { "pine", N_("pine addressbook"), pine_parse_file },
94         { "csv", N_("comma separated values"), csv_parse_file },
95         { "allcsv", N_("comma separated values (all fields)"), allcsv_parse_file },
96         { "palmcsv", N_("Palm comma separated values"), palmcsv_parse_file },
97         { "vcard", N_("vCard file"), vcard_parse_file },
98         { "\0", NULL, NULL }
99 };
100
101 struct abook_output_filter e_filters[] = {
102         { "abook", N_("abook native format"), write_database },
103         { "ldif", N_("ldif / Netscape addressbook (.4ld)"), ldif_export_database },
104         { "vcard", N_("vCard 2 file"), vcard_export_database },
105         { "mutt", N_("mutt alias"), mutt_alias_export },
106         { "muttq", N_("mutt query format (internal use)"), mutt_query_export_database },
107         { "html", N_("html document"), html_export_database },
108         { "pine", N_("pine addressbook"), pine_export_database },
109         { "csv", N_("comma separated values"), csv_export_database },
110         { "allcsv", N_("comma separated values (all fields)"), allcsv_export_database },
111         { "palmcsv", N_("Palm comma separated values"), palm_export_database},
112         { "elm", N_("elm alias"), elm_alias_export },
113         { "text", N_("plain text"), text_export_database },
114         { "wl", N_("Wanderlust address book"), wl_export_database },
115         { "spruce", N_("Spruce address book"), spruce_export_database },
116         { "bsdcal", N_("BSD calendar"), bsdcal_export_database },
117         { "custom", N_("Custom format"), custom_export_database },
118         { "\0", NULL, NULL }
119 };
120
121 struct abook_output_item_filter u_filters[] = {
122         { "vcard", N_("vCard 2 file"), vcard_export_item },
123         { "muttq", N_("mutt alias"), muttq_print_item },
124         { "custom", N_("Custom format"), custom_print_item },
125         { "\0", NULL }
126 };
127
128 /*
129  * common functions
130  */
131
132 void
133 print_filters()
134 {
135         int i;
136
137         puts(_("input formats:"));
138         for(i=0; *i_filters[i].filtname ; i++)
139                 printf("\t%s\t%s\n", i_filters[i].filtname,
140                         gettext(i_filters[i].desc));
141
142         putchar('\n');
143
144         puts(_("output formats:"));
145         for(i=0; *e_filters[i].filtname ; i++)
146                 printf("\t%s\t%s\n", e_filters[i].filtname,
147                         gettext(e_filters[i].desc));
148
149         putchar('\n');
150
151         puts(_("query-compatible output formats:"));
152         for(i=0; *u_filters[i].filtname ; i++)
153                 printf("\t%s\t%s\n", u_filters[i].filtname,
154                         gettext(u_filters[i].desc));
155
156         putchar('\n');
157 }
158
159 static int
160 number_of_output_filters()
161 {
162         int i;
163
164         for(i=0; *e_filters[i].filtname ; i++)
165                 ;
166
167         return i;
168 }
169
170 static int
171 number_of_input_filters()
172 {
173         int i;
174
175         for(i=0; *i_filters[i].filtname ; i++)
176                 ;
177
178         return i;
179 }
180
181 static char *
182 get_real_name()
183 {
184         char *username = getenv("USER");
185         struct passwd *pwent;
186         int rtn;
187         char *tmp;
188
189         pwent = getpwnam(username);
190
191         if((tmp = xstrdup(pwent->pw_gecos)) == NULL)
192                 return xstrdup(username);
193
194         rtn = sscanf(pwent->pw_gecos, "%[^,]", tmp);
195         if (rtn == EOF || rtn == 0) {
196                 free(tmp);
197                 return xstrdup(username);
198         } else
199                 return tmp;
200 }
201
202 /*
203  * import
204  */
205
206 static int              i_read_file(char *filename, int (*func) (FILE *in));
207
208 static void
209 import_screen()
210 {
211         int i;
212
213         clear();
214
215         refresh_statusline();
216         headerline(_("import database"));
217
218         mvaddstr(3, 1, _("please select a filter"));
219
220
221         for(i=0; *i_filters[i].filtname ; i++)
222                 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
223                         i_filters[i].filtname,
224                         gettext(i_filters[i].desc));
225
226         mvprintw(6 + i, 6, _("x -\tcancel"));
227 }
228
229 int
230 import_database()
231 {
232         int filter;
233         char *filename;
234         int tmp = db_n_items();
235
236         import_screen();
237
238         filter = getch() - 'a';
239         if(filter == 'x' - 'a' ||
240                 filter >= number_of_input_filters() || filter < 0) {
241                 refresh_screen();
242                 return 1;
243         }
244
245         mvaddstr(5+filter, 2, "->");
246
247         filename = ask_filename(_("Filename: "));
248         if(!filename) {
249                 refresh_screen();
250                 return 2;
251         }
252
253         if(i_read_file(filename, i_filters[filter].func ))
254                 statusline_msg(_("Error occured while opening the file"));
255         else if(tmp == db_n_items())
256                 statusline_msg(_("File does not seem to be a valid addressbook"));
257
258         refresh_screen();
259         free(filename);
260
261         return 0;
262 }
263
264
265
266 static int
267 i_read_file(char *filename, int (*func) (FILE *in))
268 {
269         FILE *in;
270         int ret = 0;
271
272         if( (in = abook_fopen( filename, "r" )) == NULL )
273                 return 1;
274
275         ret = (*func) (in);
276
277         fclose(in);
278
279         return ret;
280 }
281
282 int
283 import_file(char filtname[FILTNAME_LEN], char *filename)
284 {
285         int i;
286         int tmp = db_n_items();
287         int ret = 0;
288
289         for(i=0;; i++) {
290                 if(! strncasecmp(i_filters[i].filtname, filtname,
291                                         FILTNAME_LEN) )
292                         break;
293                 if(! *i_filters[i].filtname) {
294                         i = -1;
295                         break;
296                 }
297         }
298
299         if(i < 0)
300                 return -1;
301
302 #ifdef HAVE_VFORMAT
303         // this is a special case for
304         // libvformat whose API expects a filename
305         if(!strcmp(filtname, "vcard")) {
306           if(!strcmp(filename, "-"))
307             ret = vcard_parse_file_libvformat("/dev/stdin");
308           else
309             ret = vcard_parse_file_libvformat(filename);
310         }
311         else
312 #endif
313
314         if(!strcmp(filename, "-")) {
315                 struct stat s;
316                 if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode))
317                         ret = 1;
318                 else
319                         ret = (*i_filters[i].func) (stdin);
320         } else
321                 ret =  i_read_file(filename, i_filters[i].func);
322
323         if(tmp == db_n_items())
324                 ret = 1;
325
326         return ret;
327 }
328
329 /*
330  * export
331  */
332
333 static int e_write_file(char *filename,
334                 int (*func) (FILE *in, struct db_enumerator e), int mode);
335
336 static void
337 export_screen()
338 {
339         int i;
340
341         clear();
342
343
344         refresh_statusline();
345         headerline(_("export database"));
346
347         mvaddstr(3, 1, _("please select a filter"));
348
349
350         for(i = 0; *e_filters[i].filtname ; i++)
351                 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
352                         e_filters[i].filtname,
353                         gettext(e_filters[i].desc));
354
355         mvprintw(6 + i, 6, _("x -\tcancel"));
356 }
357
358 int
359 export_database()
360 {
361         int filter;
362         int enum_mode = ENUM_ALL;
363         char *filename;
364
365         export_screen();
366
367         filter = getch() - 'a';
368         if(filter == 'x' - 'a' ||
369                 filter >= number_of_output_filters() || filter < 0) {
370                 refresh_screen();
371                 return 1;
372         }
373
374         mvaddstr(5 + filter, 2, "->");
375
376         if(selected_items()) {
377                 switch(statusline_askchoice(
378                         _("Export <a>ll, export <s>elected, or <c>ancel?"),
379                         S_("keybindings:all/selected/cancel|asc"), 3)) {
380                         case 1:
381                                 break;
382                         case 2:
383                                 enum_mode = ENUM_SELECTED;
384                                 break;
385                         case 0:
386                         case 3:
387                                 refresh_screen();
388                                 return 1;
389                 }
390                 clear_statusline();
391         }
392
393         filename = ask_filename(_("Filename: "));
394         if(!filename) {
395                 refresh_screen();
396                 return 2;
397         }
398
399         if( e_write_file(filename, e_filters[filter].func, enum_mode))
400                 statusline_msg(_("Error occured while exporting"));
401
402         refresh_screen();
403         free(filename);
404
405         return 0;
406 }
407
408 struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) {
409         int i;
410         for(i=0;; i++) {
411                 if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN))
412                   break;
413                 if(!*u_filters[i].filtname) {
414                   i = -1;
415                   break;
416                 }
417         }
418         return u_filters[i];
419 }
420
421 void
422 e_write_item(FILE *out, int item, void (*func) (FILE *in, int item))
423 {
424   (*func) (out, item);
425 }
426
427 static int
428 e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e),
429                 int mode)
430 {
431         FILE *out;
432         int ret = 0;
433         struct db_enumerator enumerator = init_db_enumerator(mode);
434
435         if((out = fopen(filename, "a")) == NULL)
436                 return 1;
437
438         if(ftell(out))
439                 return 1;
440
441         ret = (*func) (out, enumerator);
442
443         fclose(out);
444
445         return ret;
446 }
447
448 int
449 fexport(char filtname[FILTNAME_LEN], FILE *handle, int enum_mode)
450 {
451         int i;
452         struct db_enumerator e = init_db_enumerator(enum_mode);
453
454         for(i=0;; i++) {
455                 if(!strncasecmp(e_filters[i].filtname, filtname,
456                                         FILTNAME_LEN))
457                         break;
458                 if(!*e_filters[i].filtname) {
459                         i = -1;
460                         break;
461                 }
462         }
463
464         return (e_filters[i].func) (handle, e);
465 }
466
467
468
469 int
470 export_file(char filtname[FILTNAME_LEN], char *filename)
471 {
472         const int mode = ENUM_ALL;
473         int i;
474         int ret = 0;
475         struct db_enumerator e = init_db_enumerator(mode);
476
477         for(i=0;; i++) {
478                 if(!strncasecmp(e_filters[i].filtname, filtname,
479                                         FILTNAME_LEN))
480                         break;
481                 if(!*e_filters[i].filtname) {
482                         i = -1;
483                         break;
484                 }
485         }
486
487         if(i < 0)
488                 return -1;
489
490         if(!strcmp(filename, "-"))
491                 ret = (e_filters[i].func) (stdout, e);
492         else
493                 ret =  e_write_file(filename, e_filters[i].func, mode);
494
495         return ret;
496 }
497
498 /*
499  * end of common functions
500  */
501
502 /*
503  * ldif import
504  */
505
506 #include "ldif.h"
507
508 /* During LDIF import we need more fields than the
509    ITEM_FIELDS of a *list_item. Eg: "objectclass"
510    to test valid records, ...
511    Here we extends the existing field_types enum
512    to define new fields indexes usable during processing.
513    Newly created LDIF attr names could be associated to
514    them using ldif_conv_table[]. */
515 typedef enum {
516         LDIF_OBJECTCLASS = ITEM_FIELDS + 1
517 } ldif_field_types;
518
519 #define LDIF_ITEM_FIELDS        LDIF_OBJECTCLASS
520
521 typedef char *ldif_item[LDIF_ITEM_FIELDS];
522
523 /* LDIF field's names *must* respect the ordering
524    defined by the field_types enum from database.h
525    This is only used to define (for export only)
526    abook standard field to LDIF attr name mappings */
527 static ldif_item ldif_field_names = {
528         "cn",                   // NAME
529         "mail",                 // EMAIL
530         "streetaddress",        // ADDRESS
531         "streetaddress2",       // ADDRESS2
532         "locality",             // CITY
533         "st",                   // STATE
534         "postalcode",           // ZIP
535         "countryname",          // COUNTRY
536         "homephone",            // PHONE
537         "telephonenumber",      // WORKPHONE
538         "facsimiletelephonenumber",     // FAX
539         "cellphone",            // MOBILEPHONE
540         "xmozillanickname",     // NICK
541         "homeurl",              // URL
542         "description",          // NOTES
543         "anniversary",          // ANNIVERSARY
544         "ou",                   // GROUPS
545 };
546
547 /* Used to map LDIF attr names from input to
548    the abook restricted set of standard fields. */
549 typedef struct {
550         char *key;
551         int  index;
552 } ldif_available_items;
553
554 static ldif_available_items ldif_conv_table[] = {
555         /* initial field names respect the field_types enum
556            from database.h but this is only for readability.
557            This ldif_item struct allow use to define multiple
558            LDIF field names ("attribute names") for one abook field */
559
560         {"cn",                  NAME},          // 0
561         {"mail",                EMAIL},
562         {"streetaddress",       ADDRESS},
563         {"streetaddress2",      ADDRESS2},
564         {"locality",            CITY},
565         {"st",                  STATE},
566         {"postalcode",          ZIP},
567         {"countryname",         COUNTRY},
568         {"homephone",           PHONE},
569         {"telephonenumber",     WORKPHONE},     // workphone, according to Mozilla
570         {"facsimiletelephonenumber",    FAX},
571         {"cellphone",           MOBILEPHONE},
572         {"mozillanickname",     NICK},
573         {"homeurl",             URL},
574         {"description",         NOTES},
575         {"anniversary",         ANNIVERSARY},
576         {"ou",                  GROUPS},        // 16
577
578         // here comes a couple of aliases
579         {"mozillasecondemail",  EMAIL},
580         {"homecity",            CITY},
581         {"zip",                 ZIP},
582         {"tel",                 PHONE},
583         {"xmozillaanyphone",    WORKPHONE},     // ever used ?
584         {"workphone",           WORKPHONE},
585         {"fax",                 FAX},
586         {"telexnumber",         FAX},
587         {"mobilephone",         MOBILEPHONE},
588         {"mobile",              MOBILEPHONE},
589         {"xmozillanickname",    NICK},
590         {"labeledURI",          URL},
591         {"notes",               NOTES},
592         {"birthday",            ANNIVERSARY},
593         {"category",            GROUPS},
594
595         /* TODO:
596            "sn": append to lastname ?
597            "surname": append to lastname ?
598            "givenname": prepend to firstname ? */
599
600         /* here starts dummy fields:
601
602            As long as additional indexes are created
603            (using the above ldif_field_types),
604            any other LDIF attr name can be added and
605            used during ldif entry processing.
606            But obviously fields > ITEM_FIELDS (database.h) won't be
607            copied into the final *list_item. */
608
609         /* - to avoid mistake, don't use the special ITEM_FIELDS value.
610            - see also: http://mxr.mozilla.org/comm-central/source/mailnews/addrbook/src/nsAbLDIFService.cpp */
611
612         // used to check valid LDIF records:
613         {"objectclass",         LDIF_OBJECTCLASS}
614 };
615 const int LDIF_IMPORTABLE_ITEM_FIELDS = (int)sizeof(ldif_conv_table)/sizeof(*ldif_conv_table);
616
617 /*
618   Handles multi-line strings.
619   If a string starts with a space, it's the continuation
620   of the previous line. Thus we need to always read ahead.
621   But for this to work with stdin, we need to stores the next
622   line for later use in case it's not a continuation of the
623   first line.
624  */
625 static char *
626 ldif_read_line(FILE *in, char **next_line)
627 {
628         char *buf = NULL;
629         char *ptr, *tmp;
630         char *line;
631
632         // buf filled with the first line
633         if(!*next_line)
634                 buf = getaline(in);
635         else {
636                 buf = xstrdup(*next_line);
637                 xfree(*next_line);
638         }
639
640         while(!feof(in)) {
641                 // if no line already read-ahead.
642                 line = getaline(in);
643                 if(!line) break;
644
645                 // this is not a continuation of what is already in buf
646                 // store it for the next round
647                 if(*line != ' ') {
648                         *next_line = line;
649                         break;
650                 }
651
652                 // starts with ' ': this is the continuation of buf
653                 ptr = line;
654                 while( *ptr == ' ')
655                         ptr++;
656
657                 tmp = buf;
658                 buf = strconcat(buf, ptr, NULL);
659                 free(tmp);
660                 free(line);
661         }
662
663         if(buf && *buf == '#' ) {
664                 free(buf);
665                 return NULL;
666         }
667
668         return buf;
669 }
670
671 static void
672 ldif_add_item(ldif_item li)
673 {
674         list_item item;
675         int i;
676
677         /* if there's no value for "objectclass"
678            it's probably a buggy record */
679         if(!li[LDIF_OBJECTCLASS])
680                 goto bail_out;
681
682         /* just copy from our extended ldif_item to a regular
683            list_item,
684            TODO: API could be changed so db_fput_byid() is usable */
685         item = item_create();
686         for(i=0; i < ITEM_FIELDS; i++) {
687                 if(li[i] && *li[i])
688                         item[i] = xstrdup(li[i]);
689         }
690
691         add_item2database(item);
692         item_free(&item);
693
694 bail_out:
695         for(i=0; i < LDIF_ITEM_FIELDS; i++)
696                 xfree(li[i]);
697 }
698
699 static void
700 ldif_convert(ldif_item item, char *type, char *value)
701 {
702         /* this is the first (mandatory) attribute to expected
703            from a new valid LDIF record.
704            The previous record must be added to the database before
705            we can go further with the new one */
706         if(!strcmp(type, "dn")) {
707                 ldif_add_item(item);
708                 return;
709         }
710
711         int i, index;
712
713         for( i=0; i < LDIF_IMPORTABLE_ITEM_FIELDS; i++ ) {
714
715                 if( *value &&                                           // there's a value for the attr provided
716                     ldif_conv_table[i].key &&                           // there exists an ldif attr name...
717                     !strcasecmp(ldif_conv_table[i].key, type)) {        // ...matching that provided at input
718
719                         assert((i >= 0) && (i < LDIF_ITEM_FIELDS));
720                         // which abook field this attribute's name maps to ?
721                         index = ldif_conv_table[i].index;
722                         assert((index >= 0) && (index < LDIF_ITEM_FIELDS));
723
724                         /* TODO: here must be handled multi-valued cases
725                            (first or latest win, append or prepend values, ...)
726                            Currently: emails are appended, for other fields the
727                            first attribute found wins.
728                            Eg: the value of "mobile" will be taken into
729                            account if such a line comes before "cellphone". */
730
731                         /* Remember: LDIF_ITEM_FIELDS > ITEM_FIELDS,
732                            lower (common) indexes of *ldif_item map well to *list_item.
733                            We can use item_fput()... */
734                         if(index < ITEM_FIELDS) {
735                                 // multiple email support, but two only will stay
736                                 // in the final *list_item
737                                 if(index == EMAIL && item_fget(item, EMAIL)) {
738                                         item_fput(item,
739                                                   EMAIL,
740                                                   strconcat(item_fget(item, EMAIL), ",", value, 0));
741                                 }
742                                 else {
743                                         /* Don't override already initialized fields:
744                                            This is the rule of the "first win" */
745                                         if(! item_fget(item, index))
746                                                 item_fput(item, index, xstrdup(value));
747                                 }
748                         }
749
750                         /* ... but if the ldif field's name index is higher
751                            than what standards abook fields struct can hold,
752                            these extra indexes must be managed manually.
753                            This is the case of LDIF_OBJECTCLASS ("objectclass" attr) */
754                         else {
755                                 item[index] = xstrdup(value);
756                         }
757
758                         // matching attr found and field filled:
759                         // no further attr search is needed for `type`
760                         break;
761                 }
762         }
763 }
764
765 static int
766 ldif_parse_file(FILE *handle)
767 {
768         char *line = NULL;
769         char *next_line = NULL;
770         char *type, *value;
771         int vlen;
772
773         /* This is our extended fields holder to put the values from
774            successfully parsed LDIF attributes.
775            ldif_item item is temporary. When the end of an entry is reached,
776            values are copied into a regular *list_item struct, see ldif_add_item() */
777         ldif_item item;
778
779         memset(item, 0, sizeof(item));
780
781         do {
782                 line = ldif_read_line(handle, &next_line);
783
784                 // EOF or empty lines: continue;
785                 if(!line || *line == '\0') continue;
786
787                 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
788                         xfree(line);
789                         continue; /* just skip the errors */
790                 }
791
792                 ldif_convert(item, type, value);
793
794                 xfree(line);
795         } while ( !feof(handle) );
796
797         // force registration (= ldif_add_item()) of the last LDIF entry
798         ldif_convert(item, "dn", "");
799
800         return 0;
801 }
802
803 /*
804  * end of ldif import
805  */
806
807 /*
808  * mutt alias import filter
809  */
810
811 #include "getname.h"
812
813 static int
814 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
815 {
816         char *line, *ptr;
817         char *start, *end;
818         abook_list *glist = NULL;
819
820         if( !(line = ptr = getaline(in)) )
821                 return 1; /* error / EOF */
822
823         SKIPWS(ptr);
824
825         if(strncmp("alias", ptr, 5)) {
826                 free(line);
827                 return 1;
828         }
829
830         ptr += 5;
831         SKIPWS(ptr);
832
833         /* If the group option is used, save the groups */
834         *groups = NULL;
835         start = ptr;
836         int n_groups;
837         for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
838                 ptr += 6;
839                 SKIPWS(ptr);
840                 start = ptr;
841                 SKIPNONWS(ptr);
842                 end = ptr;
843                 abook_list_append(&glist,xstrndup(start, end - start));
844                 SKIPWS(ptr);
845         }
846
847         if(n_groups && groups)
848                 *groups = abook_list_to_csv(glist);
849
850         abook_list_free(&glist);        
851
852         /* alias */
853         start = ptr;
854         SKIPNONWS(ptr);
855         end = ptr;
856         SKIPWS(ptr);
857         if(alias)
858                 *alias = xstrndup(start, end - start);
859
860         /* rest (email) */
861         *rest = xstrdup(ptr);
862
863         xfree(line);
864         return 0;
865 }
866
867 static void
868 mutt_fix_quoting(char *p)
869 {
870         char *escape = 0;
871
872         for(; *p; p++) {
873                 switch(*p) {
874                         case '\"':
875                                 if(escape)
876                                         *escape = ' ';
877                                 break;
878                         case '\\':
879                                 escape = p;
880                                 break;
881                         default:
882                                 escape = 0;
883                 }
884         }
885 }
886
887 static void
888 mutt_parse_email(list_item item)
889 {
890         char *line = item_fget(item, NAME);
891         char *tmp;
892         char *name, *email;
893 #if 0
894         char *start = line;
895         int i = 0;
896 #endif
897
898         mutt_fix_quoting(line);
899         tmp = strconcat("From: ", line, NULL);
900         getname(tmp, &name, &email);
901         free(tmp);
902
903         if(name)
904                 item_fput(item, NAME, name);
905         else
906                 return;
907
908         if(email)
909                 item_fput(item, EMAIL, email);
910         else
911                 return;
912
913         /*
914          * this is completely broken
915          */
916 #if 0
917         while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
918                 tmp = strconcat("From: ", ++start, NULL);
919                 getname(tmp, &name, &email);
920                 free(tmp);
921                 free(name);
922                 if(email) {
923                         if(*email) {
924                                 tmp = strconcat(item[EMAIL], ",", email, NULL);
925                                 free(item[EMAIL]);
926                                 item[EMAIL] = tmp;
927                         } else {
928                                 xfree(email);
929                         }
930                 }
931         }
932 #endif
933 }
934
935 static int
936 mutt_parse_file(FILE *in)
937 {
938         list_item item = item_create();
939
940         for(;;) {
941                 memset(item, 0, fields_count * sizeof(char *));
942
943                 if(!mutt_read_line(in,
944                         (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
945                         (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
946                         &item[field_id(NAME)]) )
947                         mutt_parse_email(item);
948
949                 if(feof(in)) {
950                         item_empty(item);
951                         break;
952                 }
953
954                 add_item2database(item);
955         }
956         item_free(&item);
957
958         return 0;
959 }
960
961 /*
962  * end of mutt alias import filter
963  */
964
965
966 /*
967  * ldif export filter
968  */
969
970 static void
971 ldif_fput_type_and_value(FILE *out,char *type, char *value )
972 {
973         char *tmp;
974
975         tmp = ldif_type_and_value(type, value, strlen(value));
976
977         fputs(tmp, out);
978
979         free(tmp);
980 }
981
982 static int
983 ldif_export_database(FILE *out, struct db_enumerator e)
984 {
985         char email[MAX_EMAILSTR_LEN];
986         abook_list *emails, *em;
987
988         fprintf(out, "version: 1\n");
989
990         db_enumerate_items(e) {
991                 char *tmp;
992                 int j;
993                 get_first_email(email, e.item);
994
995                 if(*email)
996                         tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
997                 /* TODO: this may not be enough for a trully "Distinguished" name
998                    needed by LDAP. Appending a random uuid could do the trick */
999                 else
1000                         tmp = strdup_printf("cn=%s",db_name_get(e.item));
1001
1002                 ldif_fput_type_and_value(out, "dn", tmp);
1003                 free(tmp);
1004
1005                 for(j = 0; j < ITEM_FIELDS; j++) {
1006                         if(j == EMAIL) {
1007                                 if(*email) {
1008                                         tmp = db_email_get(e.item);
1009                                         emails = csv_to_abook_list(tmp);
1010                                         free(tmp);
1011                                         for(em = emails; em; em = em->next)
1012                                                 ldif_fput_type_and_value(out,
1013                                                                          ldif_field_names[EMAIL],
1014                                                                          em->data);
1015                                 }
1016                         }
1017                         else if(db_fget(e.item,j)) {
1018                                 ldif_fput_type_and_value(out,
1019                                                          ldif_field_names[j],
1020                                                          db_fget(e.item, j));
1021                         }
1022                 }
1023
1024                 fprintf(out, "objectclass: top\n"
1025                                 "objectclass: person\n\n");
1026         }
1027
1028         return 0;
1029 }
1030
1031 /*
1032  * end of ldif export filter
1033  */
1034
1035 /*
1036  * html export filter
1037  */
1038
1039 static void            html_export_write_head(FILE *out);
1040 static void            html_export_write_tail(FILE *out);
1041
1042 extern struct index_elem *index_elements;
1043
1044 static void
1045 html_print_emails(FILE *out, struct list_field *f)
1046 {
1047         abook_list *l = csv_to_abook_list(f->data);
1048
1049         for(; l; l = l->next) {
1050                 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
1051                 if(l->next)
1052                         fprintf(out, ", ");
1053         }
1054
1055         abook_list_free(&l);
1056 }
1057
1058 static int
1059 html_export_database(FILE *out, struct db_enumerator e)
1060 {
1061         struct list_field f;
1062         struct index_elem *cur;
1063
1064         if(list_is_empty())
1065                 return 2;
1066
1067         init_index();
1068
1069         html_export_write_head(out);
1070
1071         db_enumerate_items(e) {
1072                 fprintf(out, " <tr>\n");
1073                 for(cur = index_elements; cur; cur = cur->next) {
1074                         if(cur->type != INDEX_FIELD)
1075                                 continue;
1076                         
1077                         get_list_field(e.item, cur, &f);
1078
1079                         fprintf(out, "  <td>");
1080
1081                         if(f.type == FIELD_EMAILS) {
1082                                 html_print_emails(out, &f);
1083                         } else {
1084                                 if (strcmp(safe_str(f.data), "") == 0)
1085                                         fprintf(out, "&nbsp;");
1086                                 else
1087                                         fprintf(out, "%s", safe_str(f.data));
1088                         }
1089                         fprintf(out, "</td>\n");
1090                 }
1091                 fprintf(out, " </tr>\n");
1092         }
1093
1094         html_export_write_tail(out);
1095
1096         return 0;
1097 }
1098
1099 static void
1100 html_export_write_head(FILE *out)
1101 {
1102         char *realname = get_real_name(), *str;
1103         struct index_elem *cur;
1104
1105         fprintf(out, "<!DOCTYPE html>\n");
1106         fprintf(out, "<html>\n");
1107         fprintf(out, "<head>\n");
1108         fprintf(out, " <meta charset=\"utf-8\" />\n");
1109         fprintf(out, " <title>");
1110         fprintf(out, _("%s's addressbook"), realname );
1111         fprintf(out, "</title>\n");
1112         fprintf(out, " <style type=\"text/css\">\n");
1113         fprintf(out, "  table {border-collapse: collapse ; border: 1px solid #000;}\n");
1114         fprintf(out, "  table th, table td {text-align: left; border: 1px solid #000; padding: 0.33em;}\n");
1115         fprintf(out, "  table th {border-bottom: 3px double #000; background-color: #ccc;}\n");
1116         fprintf(out, " </style>\n");
1117         fprintf(out, "</head>\n");
1118         fprintf(out, "<body>\n");
1119         fprintf(out, "<h1>");
1120         fprintf(out, _("%s's addressbook"), realname);
1121         fprintf(out, "</h1>\n");
1122
1123         fprintf(out, "<table>\n");
1124         fprintf(out, "<thead>\n");
1125         fprintf(out, " <tr>\n");
1126         for(cur = index_elements; cur; cur = cur->next) {
1127                 if(cur->type != INDEX_FIELD)
1128                         continue;
1129
1130                 get_field_info(cur->d.field.id, NULL, &str, NULL);
1131
1132                 fprintf(out, "  <th>");
1133
1134                 if (strcmp(str, "") == 0)
1135                         fprintf(out, "&nbsp;");
1136                 else
1137                         fprintf(out, "%s", str);
1138
1139                 fprintf(out, "</th>\n");
1140         }
1141         fprintf(out, " </tr>\n");
1142         fprintf(out, "<thead>\n");
1143         fprintf(out, "<tbody>\n");
1144
1145         free(realname);
1146 }
1147
1148 static void
1149 html_export_write_tail(FILE *out)
1150 {
1151         fprintf(out, "</table>\n");
1152         fprintf(out, "</body>\n");
1153         fprintf(out, "</html>");
1154 }
1155
1156 /*
1157  * end of html export filter
1158  */
1159
1160
1161 /*
1162  * pine addressbook import filter
1163  */
1164
1165 #define PINE_BUF_SIZE 2048
1166
1167 static void
1168 pine_fixbuf(char *buf)
1169 {
1170         int i,j;
1171
1172         for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1173                 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1174 }
1175
1176 static void
1177 pine_convert_emails(char *s)
1178 {
1179         int i;
1180         char *tmp;
1181
1182         if(s == NULL || *s != '(')
1183                 return;
1184
1185         for(i = 0; s[i]; i++)
1186                 s[i] = s[i + 1];
1187
1188         if( ( tmp = strchr(s,')')) )
1189                 *tmp = '\0';
1190
1191         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1192                 if(i > MAX_LIST_ITEMS - 1) {
1193                         *tmp = '\0';
1194                         break;
1195                 }
1196
1197 }
1198
1199 static void
1200 pine_parse_buf(char *buf)
1201 {
1202         list_item item;
1203         char *start = buf;
1204         char *end;
1205         char tmp[PINE_BUF_SIZE];
1206         int i, len, last;
1207         int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1208
1209         item = item_create();
1210
1211         for(i=0, last=0; !last ; i++) {
1212                 if( !(end = strchr(start, '\t')) )
1213                         last=1;
1214
1215                 len = last ? strlen(start) : (int) (end-start);
1216                 len = min(len, PINE_BUF_SIZE - 1);
1217
1218                 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1219                                 && pine_conv_table[i] >= 0) {
1220                         strncpy(tmp, start, len);
1221                         tmp[len] = 0;
1222                         if(*tmp)
1223                                 item_fput(item, pine_conv_table[i],
1224                                                 xstrdup(tmp));
1225                 }
1226                 start = end + 1;
1227         }
1228
1229         pine_convert_emails(item_fget(item, EMAIL));
1230         add_item2database(item);
1231         item_free(&item);
1232 }
1233
1234
1235 #define LINESIZE        1024
1236
1237 static int
1238 pine_parse_file(FILE *in)
1239 {
1240         char line[LINESIZE];
1241         char *buf = NULL;
1242         char *ptr;
1243         int i;
1244
1245         fgets(line, LINESIZE, in);
1246
1247         while(!feof(in)) {
1248                 for(i = 2;;i++) {
1249                         buf = xrealloc(buf, i*LINESIZE);
1250                         if(i == 2)
1251                                 strcpy(buf, line);
1252                         fgets(line, LINESIZE, in);
1253                         ptr=(char *)&line;
1254                         if(*ptr != ' ' || feof(in))
1255                                 break;
1256                         else
1257                                 while(*ptr == ' ')
1258                                         ptr++;
1259
1260                         strcat(buf, ptr);
1261                 }
1262                 if(*buf == '#') {
1263                         xfree(buf);
1264                         continue;
1265                 }
1266                 pine_fixbuf(buf);
1267
1268                 pine_parse_buf(buf);
1269
1270                 xfree(buf);
1271         }
1272
1273         return 0;
1274 }
1275
1276 /*
1277  * end of pine addressbook import filter
1278  */
1279
1280
1281 /*
1282  * pine addressbook export filter
1283  *
1284  *  filter doesn't wrap the lines as it should but Pine seems to handle
1285  *  created files without problems - JH
1286  */
1287
1288 static int
1289 pine_export_database(FILE *out, struct db_enumerator e)
1290 {
1291         char *emails;
1292
1293         db_enumerate_items(e) {
1294                 emails = db_email_get(e.item);
1295                 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1296                                 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1297                                 safe_str(db_fget(e.item, NICK)),
1298                                 safe_str(db_name_get(e.item)),
1299                                 emails,
1300                                 safe_str(db_fget(e.item, NOTES))
1301                                 );
1302                 free(emails);
1303         }
1304
1305         return 0;
1306 }
1307
1308 /*
1309  * end of pine addressbook export filter
1310  */
1311
1312
1313 /*
1314  * csv import filter
1315  */
1316
1317 /* This is used by both allcsv_export_database() and csv_export_common()
1318    to distinguish between standard and defined fields.
1319    To avoid confusions this should stay > ITEM_FIELDS  */
1320 #define CUSTOM_FIELD_START_INDEX       (ITEM_FIELDS + 10)
1321
1322 /* FIXME
1323  * these files should be parsed according to a certain
1324  * lay out, or the default if layout is not given, at
1325  * the moment only default is done...
1326  */
1327
1328 #define CSV_COMMENT_CHAR        '#'
1329 #define CSV_DUPLICATE_SEPARATOR " "
1330 #define CSV_TABLE_SIZE(t)       (sizeof (t) / sizeof *(t))
1331
1332 static int csv_conv_table[] = {
1333         NAME,
1334         EMAIL,
1335         PHONE,
1336         NOTES,
1337         NICK
1338 };
1339
1340 static int allcsv_conv_table[] = {
1341         NAME,
1342         EMAIL,
1343         ADDRESS,
1344         ADDRESS2,
1345         CITY,
1346         STATE,
1347         ZIP,
1348         COUNTRY,
1349         PHONE,
1350         WORKPHONE,
1351         FAX,
1352         MOBILEPHONE,
1353         NICK,
1354         URL,
1355         NOTES,
1356         ANNIVERSARY
1357 };
1358
1359 static int palmcsv_conv_table[] = {
1360         NAME,           /* Last name */
1361         NAME,           /* First name */
1362         NOTES,          /* Title */
1363         NICK,           /* Company */
1364         WORKPHONE,
1365         PHONE,
1366         FAX,
1367         MOBILEPHONE,
1368         EMAIL,
1369         ADDRESS,
1370         CITY,
1371         STATE,
1372         ZIP,
1373         COUNTRY,
1374         ANNIVERSARY,
1375 };
1376
1377 static void
1378 csv_convert_emails(char *s)
1379 {
1380         int i;
1381         char *tmp;
1382
1383         if(s == NULL)
1384                 return;
1385
1386         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1387                 if(i > MAX_LIST_ITEMS - 1) {
1388                         *tmp = 0;
1389                         break;
1390                 }
1391
1392 }
1393
1394 static char *
1395 csv_remove_quotes(char *s)
1396 {
1397         char *copy, *trimmed;
1398         int len;
1399
1400         copy = trimmed = xstrdup(s);
1401         strtrim(trimmed);
1402
1403         len = strlen(trimmed);
1404         if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1405                 if(len < 3) {
1406                         xfree(copy);
1407                         return NULL;
1408                 }
1409                 trimmed[len - 1] = 0;
1410                 trimmed++;
1411                 trimmed = xstrdup(trimmed);
1412                 free(copy);
1413                 return trimmed;
1414         }
1415
1416         xfree(copy);
1417         return xstrdup(s);
1418 }
1419
1420 static int
1421 csv_field_to_item(int *table_base, size_t table_size, int field)
1422 {
1423         if(field < table_size)
1424                 return field_id(table_base[field]);
1425
1426         return -1;
1427 }
1428
1429 static void
1430 csv_store_item(list_item item, int i, char *s)
1431 {
1432         char *newstr = NULL;
1433
1434         if(!s || !*s)
1435                 return;
1436
1437         if( !(newstr = csv_remove_quotes(s)) )
1438                 return;
1439
1440         if(i >= 0) {
1441                 if (item[i] != NULL) {
1442                         char *oldstr = item[i];
1443
1444                         item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1445                                 oldstr, NULL);
1446                         xfree(newstr);
1447                         xfree(oldstr);
1448                 } else {
1449                         item[i] = newstr;
1450                 }
1451         } else {
1452                 xfree(newstr);
1453         }
1454 }
1455
1456 static int
1457 csv_is_valid_quote_end(char *p)
1458 {
1459         if(*p != '\"')
1460                 return FALSE;
1461
1462         for(p++; *p; p++) {
1463                 if(*p == ',')
1464                         return TRUE;
1465                 else if(!ISSPACE(*p))
1466                         return FALSE;
1467         }
1468
1469         return TRUE;
1470 }
1471
1472 static int
1473 csv_is_valid_quote_start(char *p)
1474 {
1475         for(; *p; p++) {
1476                 if(*p == '\"')
1477                         return TRUE;
1478                 else if(!ISSPACE(*p))
1479                         return FALSE;
1480         }
1481
1482         return FALSE;
1483 }
1484
1485 static void
1486 csv_parse_line(char *line, int *table_base, size_t table_size)
1487 {
1488         char *p, *start;
1489         int field;
1490         bool in_quote = FALSE;
1491         list_item item;
1492
1493         item = item_create();
1494
1495         for(p = start = line, field = 0; *p; p++) {
1496                 if(in_quote) {
1497                         if(csv_is_valid_quote_end(p))
1498                                 in_quote = FALSE;
1499                 } else {
1500                         if ( (((p - start) / sizeof (char)) < 2 ) &&
1501                                 csv_is_valid_quote_start(p) )
1502                                 in_quote = TRUE;
1503                 }
1504
1505                 if(*p == ',' && !in_quote) {
1506                         *p = 0;
1507                         csv_store_item(item,
1508                                 csv_field_to_item(table_base,table_size,field),
1509                                 start);
1510                         field++;
1511                         start = p + 1;
1512                 }
1513         }
1514         /*
1515          * store last field
1516          */
1517         csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1518                 start);
1519
1520         csv_convert_emails(item_fget(item, EMAIL));
1521         add_item2database(item);
1522         item_free(&item);
1523 }
1524
1525 static int
1526 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1527 {
1528         char *line = NULL;
1529
1530         while(!feof(in)) {
1531                 line = getaline(in);
1532
1533                 if(line && *line && *line != CSV_COMMENT_CHAR)
1534                         csv_parse_line(line, conv_table, table_size);
1535
1536                 xfree(line);
1537         }
1538
1539         return 0;
1540 }
1541
1542 static int
1543 csv_parse_file(FILE *in)
1544 {
1545         return csv_parse_file_common(in, csv_conv_table,
1546                 CSV_TABLE_SIZE(csv_conv_table));
1547 }
1548
1549 static int
1550 allcsv_parse_file(FILE *in)
1551 {
1552         return csv_parse_file_common(in, allcsv_conv_table,
1553                 CSV_TABLE_SIZE(allcsv_conv_table));
1554 }
1555
1556 static int
1557 palmcsv_parse_file(FILE *in)
1558 {
1559         return csv_parse_file_common(in, palmcsv_conv_table,
1560                 CSV_TABLE_SIZE(palmcsv_conv_table));
1561 }
1562
1563 /*
1564  * end of csv import filter
1565  */
1566
1567 /*
1568  * vCard import filter
1569  */
1570
1571 static char *vcard_fields[] = {
1572         "FN",                   /* FORMATTED NAME */
1573         "EMAIL",                /* EMAIL */
1574         "ADR",                  /* ADDRESS */
1575         "ADR",                  /* ADDRESS2 */
1576         "ADR",                  /* CITY */
1577         "ADR",                  /* STATE */
1578         "ADR",                  /* ZIP */
1579         "ADR",                  /* COUNTRY */
1580         "TEL",                  /* PHONE */
1581         "TEL",                  /* WORKPHONE */
1582         "TEL",                  /* FAX */
1583         "TEL",                  /* MOBILEPHONE */
1584         "NICKNAME",             /* NICK */
1585         "URL",                  /* URL */
1586         "NOTE",                 /* NOTES */
1587         "BDAY",                 /* ANNIVERSARY */
1588         "N",                    /* NAME: special case/mapping in vcard_parse_line() */
1589         NULL                    /* ITEM_FIELDS */
1590 };
1591
1592 enum {
1593         VCARD_KEY = 0,
1594         VCARD_KEY_ATTRIBUTE,
1595         VCARD_VALUE,
1596 };
1597
1598 static char *
1599 vcard_get_line_element(char *line, int element)
1600 {
1601         int i;
1602         char *line_copy = 0;
1603         char *result = 0;
1604         char *key = 0;
1605         char *key_attr = 0;
1606         char *value = 0;
1607
1608         line_copy = xstrdup(line);
1609
1610         /* change newline characters, if present, to end of string */
1611         for(i=0; line_copy[i]; i++) {
1612                 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1613                         line_copy[i] = '\0';
1614                         break;
1615                 }
1616         }
1617
1618         /* separate key from value */
1619         for(i=0; line_copy[i]; i++) {
1620                 if(line_copy[i] == ':') {
1621                         line_copy[i] = '\0';
1622                         key = line_copy;
1623                         value = &line_copy[i+1];
1624                         break;
1625                 }
1626         }
1627
1628         /* separate key from key attributes */
1629         /* works for vCard 2 as well (automagically) */
1630         if (key) {
1631                 for(i=0; key[i]; i++) {
1632                         if(key[i] == ';') {
1633                                 key[i] = '\0';
1634                                 key_attr = &key[i+1];
1635                                 break;
1636                         }
1637                 }
1638         }
1639
1640         switch(element) {
1641         case VCARD_KEY:
1642                 if(key)
1643                         result = xstrdup(key);
1644                 break;
1645         case VCARD_KEY_ATTRIBUTE:
1646                 if(key_attr)
1647                         result = xstrdup(key_attr);
1648                 break;
1649         case VCARD_VALUE:
1650                 if(value)
1651                         result = xstrdup(value);
1652                 break;
1653         }
1654
1655         xfree(line_copy);
1656         return result;
1657 }
1658
1659 static void
1660 vcard_parse_email(list_item item, char *line)
1661 {
1662         char *email;
1663
1664         email = vcard_get_line_element(line, VCARD_VALUE);
1665
1666         if(item[1]) {
1667                 item[1] = strconcat(item[1], ",", email, 0);
1668                 xfree(email);
1669         }
1670         else {
1671                 item[1] = email;
1672         }
1673 }
1674
1675
1676 /*
1677  * mappings between vCard ADR field and abook's ADDRESS
1678  * see rfc2426 section 3.2.1
1679  */
1680 static void
1681 vcard_parse_address(list_item item, char *line)
1682 {
1683         char *value;
1684
1685         value = vcard_get_line_element(line, VCARD_VALUE);
1686         if(!value)
1687                 return;
1688
1689         // vCard(post office box) - not used
1690         strsep(&value, ";");
1691         if(!value) return;
1692
1693         // vCard(the extended address)
1694         item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
1695         if(!value) return;
1696
1697         // vCard(the street address)
1698         item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
1699         if(!value) return;
1700
1701         // vCard(the locality)
1702         item_fput(item, CITY, xstrdup(strsep(&value, ";")));
1703         if(!value) return;
1704
1705         // vCard(the region)
1706         item_fput(item, STATE, xstrdup(strsep(&value, ";")));
1707         if(!value) return;
1708
1709         // vCard(the postal code)
1710         item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
1711         if(!value) return;
1712
1713         // vCard(the country name)
1714         item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
1715
1716         // support of optional trailing ";" to the ADR field
1717         if(value && *value) xfree(value);
1718 }
1719
1720 static void
1721 vcard_parse_name(list_item item, char *line)
1722 {
1723         // store the "N" field into "NAME" *if* no "FN:"
1724         // value has already been stored here
1725         if(item[0]) return;
1726
1727         int i = -1;
1728         item[0] = vcard_get_line_element(line, VCARD_VALUE);
1729         // "N:" can be multivalued => replace ';' separators by ' '
1730         while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1731
1732         // http://www.daniweb.com/software-development/c/code/216919
1733         char *original = item[0], *p = original;
1734         int trimmed = 0;
1735         do {
1736           if (*original != ' ' || trimmed) {
1737             trimmed = 1; *p++ = *original;
1738           }
1739         } while(*original++);
1740 }
1741
1742 static void
1743 vcard_parse_phone(list_item item, char *line)
1744 {
1745         char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1746         char *value = vcard_get_line_element(line, VCARD_VALUE);
1747
1748         /* set the standard number */
1749         if (!type) item_fput(item, PHONE, value);
1750
1751         /*
1752          * see rfc2426 section 3.3.1
1753          * Note: we probably support both vCard 2 and 3
1754          */
1755         else {
1756                 if (strcasestr(type, "home") != NULL)
1757                         item_fput(item, PHONE, xstrdup(value));
1758                 else if (strcasestr(type, "work") != NULL)
1759                         item_fput(item, WORKPHONE, xstrdup(value));
1760                 else if (strcasestr(type, "fax") != NULL)
1761                         item_fput(item, FAX, xstrdup(value));
1762                 else if (strcasestr(type, "cell") != NULL)
1763                         item_fput(item, MOBILEPHONE, xstrdup(value));
1764
1765                 xfree(type);
1766                 xfree(value);
1767         }
1768 }
1769
1770 static void
1771 vcard_parse_line(list_item item, char *line)
1772 {
1773         int i;
1774         char *key;
1775
1776         for(i=0; vcard_fields[i]; i++) {
1777                 key = vcard_fields[i];
1778
1779                 if(0 == strncmp(key, line, strlen(key))) {
1780                         if(0 == strcmp(key, "EMAIL"))
1781                                 vcard_parse_email(item, line);
1782                         else if(i == 2)
1783                                 vcard_parse_address(item, line);
1784                         else if(0 == strcmp(key, "TEL"))
1785                                 vcard_parse_phone(item, line);
1786                         else if(0 == strcmp(key, "N"))
1787                                 vcard_parse_name(item, line);
1788                         else
1789                                 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1790                         return;
1791                 }
1792         }
1793 }
1794
1795 static void
1796 vcard_parse_item(FILE *in)
1797 {
1798         char *line = NULL;
1799         list_item item = item_create();
1800
1801         while(!feof(in)) {
1802                 line = getaline(in);
1803
1804                 if(line && !strncmp("END:VCARD", line, 9)) {
1805                         xfree(line);
1806                         break;
1807                 }
1808                 else if(line) {
1809                         vcard_parse_line(item, line);
1810                         xfree(line);
1811                 }
1812         }
1813
1814         add_item2database(item);
1815         item_free(&item);
1816 }
1817
1818 static int
1819 vcard_parse_file(FILE *in)
1820 {
1821         char *line = NULL;
1822
1823         while(!feof(in)) {
1824                 line = getaline(in);
1825
1826                 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1827                         xfree(line);
1828                         vcard_parse_item(in);
1829                 }
1830                 else if(line) {
1831                         xfree(line);
1832                 }
1833         }
1834
1835         return 0;
1836 }
1837
1838 /*
1839  * end of vCard import filter
1840  */
1841
1842 /*
1843  * csv addressbook export filters
1844  */
1845
1846 #define CSV_LAST                (-1)
1847 #define CSV_UNDEFINED           (-2)
1848 #define CSV_SPECIAL(X)          (-3 - (X))
1849 #define CSV_IS_SPECIAL(X)       ((X) <= -3)
1850
1851 static int
1852 csv_export_common(FILE *out, struct db_enumerator e,
1853                 int fields[], void (*special_func)(FILE *, int, int))
1854 {
1855         int i;
1856
1857         db_enumerate_items(e) {
1858                 for(i = 0; fields[i] != CSV_LAST; i++) {
1859                         if(fields[i] == CSV_UNDEFINED)
1860                                 fprintf(out, "\"\"");
1861                         else if(CSV_IS_SPECIAL(fields[i])) {
1862                                 if(special_func)
1863                                         (*special_func)(out, e.item, fields[i]);
1864                         }
1865                         else if(fields[i] >= CUSTOM_FIELD_START_INDEX) {
1866                                 fprintf(out, "\"%s\"",
1867                                         safe_str(db_fget_byid(e.item, fields[i] - CUSTOM_FIELD_START_INDEX)));
1868                         }
1869                         else
1870                                 /*fprintf(out,(
1871                         strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1872                         strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1873                                 "\"%s\"" : "%s",
1874                                 safe_str(database[e.item][field_idx(fields[i])])
1875                                 );*/
1876                                 fprintf(out, "\"%s\"",
1877                                         safe_str(db_fget(e.item,fields[i])));
1878
1879                         if(fields[i + 1] != CSV_LAST)
1880                                 fputc(',', out);
1881                 }
1882                 fputc('\n', out);
1883         }
1884
1885         return 0;
1886 }
1887
1888 static int
1889 csv_export_database(FILE *out, struct db_enumerator e)
1890 {
1891         int csv_export_fields[] = {
1892                 NAME,
1893                 EMAIL,
1894                 PHONE,
1895                 NOTES,
1896                 NICK,
1897                 CSV_LAST
1898         };
1899
1900         csv_export_common(out, e, csv_export_fields, NULL);
1901
1902         return 0;
1903 }
1904
1905 static int
1906 allcsv_export_database(FILE *out, struct db_enumerator e)
1907 {
1908         /*
1909          * TODO: Should get these atomatically from abook_fileds
1910          *  - JH
1911          */
1912         int allcsv_export_fields[ITEM_FIELDS + 6] = { // only the 5 custom fields are allowed so far
1913                 NAME,
1914                 EMAIL,
1915                 ADDRESS,
1916                 ADDRESS2,
1917                 CITY,
1918                 STATE,
1919                 ZIP,
1920                 COUNTRY,
1921                 PHONE,
1922                 WORKPHONE,
1923                 FAX,
1924                 MOBILEPHONE, // spelt "mobile" in standard_fields
1925                 NICK,
1926                 URL,
1927                 NOTES,
1928                 ANNIVERSARY,
1929                 GROUPS,
1930                 CSV_LAST
1931         };
1932
1933         fprintf(out, "#");
1934         int i = 0;
1935         while(allcsv_export_fields[i+1] != CSV_LAST) {
1936                 fprintf(out, "\"%s\",", standard_fields[i++].key);
1937         }
1938         fprintf(out, "\"%s\"", standard_fields[i].key);
1939
1940         /*
1941           Custom fields handling:
1942           This loop appends custom fields' id at the end of allcsv_export_fields and shift
1943           the CSV_LAST sentinel value each time one is found.
1944           CUSTOM_FIELD_START_INDEX is added to these index values so csv_export_common()
1945           can later recognize them and call db_fget_byid() instead of the traditional db_fget()
1946
1947           It only search for defined the [legacy?] "custom" fields.
1948         */
1949
1950         // pointer to the end of the field list
1951         int append_field = ITEM_FIELDS;
1952         // custom field's trailing number (between 1 and 5)
1953         int j;
1954         // full custom field name, eg "custom4"
1955         char custom_field_key[8];
1956         // index used by custom_field_key
1957         int field_no;
1958         // name of the defined field <field_no> as chosen by the user
1959         char *custom_field_name;
1960
1961         for (j = 1; j <= 5; j++) {
1962                 snprintf(custom_field_key, 8, "custom%d", j++);
1963                 if(find_declared_field(custom_field_key)) {
1964                         find_field_number(custom_field_key, &field_no);
1965                         get_field_info(field_no, NULL, &custom_field_name, NULL);
1966                         // append the field to the list
1967                         allcsv_export_fields[append_field] = field_no + CUSTOM_FIELD_START_INDEX;
1968                         allcsv_export_fields[++append_field] = CSV_LAST;
1969                         // print column name
1970                         fprintf(out, ",\"%s\"", custom_field_name);
1971                 }
1972         }
1973         free(custom_field_name);
1974         fprintf(out, "\n");
1975
1976         csv_export_common(out, e, allcsv_export_fields, NULL);
1977
1978         return 0;
1979 }
1980
1981 /*
1982  * palm csv
1983  */
1984
1985 #define PALM_CSV_NAME   CSV_SPECIAL(0)
1986 #define PALM_CSV_END    CSV_SPECIAL(1)
1987 #define PALM_CSV_CAT    CSV_SPECIAL(2)
1988
1989 static void
1990 palm_split_and_write_name(FILE *out, char *name)
1991 {
1992         char *p;
1993
1994         assert(name);
1995
1996         if ( (p = strchr(name, ' ')) ) {
1997                 /*
1998                  * last name first
1999                  */
2000                 fprintf(out, "\"%s\",\"" , p + 1);
2001                 fwrite((void *)name, p - name, sizeof(char), out);
2002                 fputc('\"', out);
2003         } else {
2004                 fprintf(out, "\"%s\"", safe_str(name));
2005         }
2006 }
2007
2008 static void
2009 palm_csv_handle_specials(FILE *out, int item, int field)
2010 {
2011         switch(field) {
2012                 case PALM_CSV_NAME:
2013                         palm_split_and_write_name(out, db_name_get(item));
2014                         break;
2015                 case PALM_CSV_CAT:
2016                         fprintf(out, "\"abook\"");
2017                         break;
2018                 case PALM_CSV_END:
2019                         fprintf(out, "\"0\"");
2020                         break;
2021                 default:
2022                         assert(0);
2023         }
2024 }
2025
2026 static int
2027 palm_export_database(FILE *out, struct db_enumerator e)
2028 {
2029         int palm_export_fields[] = {
2030                 PALM_CSV_NAME,          /* LASTNAME, FIRSTNAME  */
2031                 CSV_UNDEFINED,          /* TITLE                */
2032                 CSV_UNDEFINED,          /* COMPANY              */
2033                 WORKPHONE,              /* WORK PHONE           */
2034                 PHONE,                  /* HOME PHONE           */
2035                 FAX,                    /* FAX                  */
2036                 MOBILEPHONE,            /* OTHER                */
2037                 EMAIL,                  /* EMAIL                */
2038                 ADDRESS,                /* ADDRESS              */
2039                 CITY,                   /* CITY                 */
2040                 STATE,                  /* STATE                */
2041                 ZIP,                    /* ZIP                  */
2042                 COUNTRY,                /* COUNTRY              */
2043                 NICK,                   /* DEFINED 1            */
2044                 URL,                    /* DEFINED 2            */
2045                 CSV_UNDEFINED,          /* DEFINED 3            */
2046                 CSV_UNDEFINED,          /* DEFINED 4            */
2047                 NOTES,                  /* NOTE                 */
2048                 PALM_CSV_END,           /* "0"                  */
2049                 PALM_CSV_CAT,           /* CATEGORY             */
2050                 CSV_LAST
2051         };
2052
2053         csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
2054
2055         return 0;
2056 }
2057
2058 /*
2059  * end of csv export filters
2060  */
2061
2062 /*
2063  * vCard 2 addressbook export filter
2064  */
2065
2066 static int
2067 vcard_export_database(FILE *out, struct db_enumerator e)
2068 {
2069   db_enumerate_items(e)
2070     vcard_export_item(out, e.item);
2071   return 0;
2072 }
2073
2074 void
2075 vcard_export_item(FILE *out, int item)
2076 {
2077         int j, email_no;
2078         char *name, *tmp;
2079         abook_list *emails, *em;
2080         fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2081                 safe_str(db_name_get(item)));
2082
2083         name = get_surname(db_name_get(item));
2084         for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2085           if((db_name_get(item))[j] == ' ')
2086             break;
2087         }
2088         fprintf(out, "N:%s;%.*s\r\n",
2089                 safe_str(name),
2090                 j,
2091                 safe_str(db_name_get(item))
2092                 );
2093
2094         free(name);
2095
2096         if(db_fget(item, NICK))
2097           fprintf(out, "NICKNAME:%s\r\n",
2098                   safe_str(db_fget(item, NICK)));
2099         if(db_fget(item, ANNIVERSARY))
2100           fprintf(out, "BDAY:%s\r\n",
2101                   safe_str(db_fget(item, ANNIVERSARY)));
2102
2103         // see rfc6350 section 6.3.1
2104         if(db_fget(item, ADDRESS)) {
2105                 fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
2106                         // pobox (unsupported)
2107                         safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
2108                         safe_str(db_fget(item, ADDRESS)), // street
2109                         safe_str(db_fget(item, CITY)), // locality
2110                         safe_str(db_fget(item, STATE)), // region
2111                         safe_str(db_fget(item, ZIP)), // code (postal)
2112                         safe_str(db_fget(item, COUNTRY)) // country
2113                         );
2114         }
2115
2116         if(db_fget(item, PHONE))
2117           fprintf(out, "TEL;HOME:%s\r\n",
2118                   db_fget(item, PHONE));
2119         if(db_fget(item, WORKPHONE))
2120           fprintf(out, "TEL;WORK:%s\r\n",
2121                   db_fget(item, WORKPHONE));
2122         if(db_fget(item, FAX))
2123           fprintf(out, "TEL;FAX:%s\r\n",
2124                   db_fget(item, FAX));
2125         if(db_fget(item, MOBILEPHONE))
2126           fprintf(out, "TEL;CELL:%s\r\n",
2127                   db_fget(item, MOBILEPHONE));
2128
2129         tmp = db_email_get(item);
2130         if(*tmp) {
2131           emails = csv_to_abook_list(tmp);
2132           fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
2133           email_no = 1;
2134           for(em = emails->next; em; em = em->next, email_no++ )
2135                   fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
2136
2137           abook_list_free(&emails);
2138         }
2139         free(tmp);
2140
2141         if(db_fget(item, NOTES))
2142           fprintf(out, "NOTE:%s\r\n",
2143                   db_fget(item, NOTES));
2144         if(db_fget(item, URL))
2145           fprintf(out, "URL:%s\r\n",
2146                   db_fget(item, URL));
2147
2148         fprintf(out, "END:VCARD\r\n\r\n");
2149
2150 }
2151
2152 /*
2153  * end of vCard export filter
2154  */
2155
2156
2157 /*
2158  * mutt alias export filter
2159  */
2160
2161 static char *
2162 mutt_alias_genalias(int i)
2163 {
2164         char *tmp, *pos;
2165
2166         if(db_fget(i, NICK))
2167                 return xstrdup(db_fget(i, NICK));
2168
2169         tmp = xstrdup(db_name_get(i));
2170
2171         if( ( pos = strchr(tmp, ' ') ) )
2172                 *pos = 0;
2173
2174         strlower(tmp);
2175
2176         return tmp;
2177 }
2178
2179 /*
2180  * This function is a variant of abook_list_to_csv
2181  * */
2182 static char *
2183 mutt_alias_gengroups(int i)
2184 {
2185         char *groups, *res = NULL;
2186         char groupstr[7] = "-group ";
2187         abook_list *list, *tmp;
2188
2189         groups = db_fget(i, GROUPS);
2190
2191         if(!groups)
2192                 return NULL;
2193
2194         list = csv_to_abook_list(groups);
2195         for(tmp = list; tmp; tmp = tmp->next) {
2196                 if(tmp == list) {
2197                         res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2198                         res = strcpy(res, groupstr);
2199                 } else {
2200                         res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2201                         strcat(res, " ");
2202                         strcat(res, groupstr);
2203                 }
2204                 strcat(res, tmp->data);
2205         }
2206         abook_list_free(&list);
2207         xfree(groups);
2208
2209         return res;
2210 }
2211
2212 static int
2213 mutt_alias_export(FILE *out, struct db_enumerator e)
2214 {
2215         char email[MAX_EMAIL_LEN];
2216         char *alias = NULL;
2217         char *groups = NULL;
2218         int email_addresses;
2219         char *ptr;
2220
2221         db_enumerate_items(e) {
2222                 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2223                 groups = (field_id(GROUPS) != -1) ?  mutt_alias_gengroups(e.item) : NULL;
2224                 get_first_email(email, e.item);
2225
2226                 /* do not output contacts without email address */
2227                 /* cause this does not make sense in mutt aliases */
2228                 if (*email) {
2229
2230                         /* output first email address */
2231                         fprintf(out,"alias ");
2232                         if(groups)
2233                                 fprintf(out, "%s ", groups);
2234                         if(alias)
2235                                 fprintf(out, "%s ", alias);
2236                         fprintf(out, "%s <%s>\n",
2237                                         db_name_get(e.item),
2238                                         email);
2239
2240                         /* number of email addresses */
2241                         email_addresses = 1;
2242                         ptr = db_email_get(e.item);
2243                         while (*ptr != '\0') {
2244                                 if (*ptr == ',') {
2245                                         email_addresses++;
2246                                 }
2247                                 ptr++;
2248                         }
2249
2250                         /* output other email addresses */
2251                         while (email_addresses-- > 1) {
2252                                 roll_emails(e.item, ROTATE_RIGHT);
2253                                 get_first_email(email, e.item);
2254                                 fprintf(out,"alias ");
2255                                 if( groups )
2256                                         fprintf(out, "%s ", groups);
2257                                 if(alias)
2258                                         fprintf(out, "%s__%s ", alias, email);
2259                                 else
2260                                         fprintf(out, "%s__%s ", db_name_get(e.item), email);
2261                                 fprintf(out, "%s <%s>\n",
2262                                                 db_name_get(e.item),
2263                                                 email);
2264                         }
2265                         roll_emails(e.item, ROTATE_RIGHT);
2266                         xfree(alias);
2267                         xfree(groups);
2268                 }
2269         }
2270
2271         return 0;
2272 }
2273
2274 void muttq_print_item(FILE *file, int item)
2275 {
2276         abook_list *emails, *e;
2277         char *tmp = db_email_get(item);
2278
2279         emails = csv_to_abook_list(tmp);
2280         free(tmp);
2281
2282         for(e = emails; e; e = e->next) {
2283                 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2284                                 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2285                                 );
2286                 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2287                         break;
2288         }
2289         abook_list_free(&emails);
2290 }
2291
2292 static int
2293 mutt_query_export_database(FILE *out, struct db_enumerator e)
2294 {
2295   fprintf(out, "All items\n");
2296   db_enumerate_items(e)
2297     muttq_print_item(out, e.item);
2298   return 0;
2299 }
2300
2301 /*
2302  * end of mutt alias export filter
2303  */
2304
2305
2306 /*
2307  * printable export filter
2308  */
2309
2310
2311 static void
2312 text_write_address_us(FILE *out, int i) {
2313         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2314
2315         if(db_fget(i, ADDRESS2))
2316                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2317
2318         if(db_fget(i, CITY))
2319                 fprintf(out, "\n%s", db_fget(i, CITY));
2320
2321         if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2322                 fputc('\n', out);
2323
2324                 if(db_fget(i, STATE)) {
2325                         fprintf(out, "%s", db_fget(i, STATE));
2326                         if(db_fget(i, ZIP))
2327                                 fputc(' ', out);
2328                 }
2329
2330                 if(db_fget(i, ZIP))
2331                         fprintf(out, "%s", db_fget(i, ZIP));
2332         }
2333
2334         if(db_fget(i, COUNTRY))
2335                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2336 }
2337
2338
2339 static void
2340 text_write_address_uk(FILE *out, int i) {
2341         int j;
2342
2343         for(j = ADDRESS; j <= COUNTRY; j++)
2344                 if(db_fget(i, j))
2345                         fprintf(out, "\n%s", db_fget(i, j));
2346 }
2347
2348 static void
2349 text_write_address_eu(FILE *out, int i) {
2350         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2351
2352         if(db_fget(i, ADDRESS2))
2353                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2354
2355         if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2356                 fputc('\n', out);
2357
2358                 if(db_fget(i, ZIP)) {
2359                         fprintf(out, "%s", db_fget(i, ZIP));
2360                         if(db_fget(i, CITY))
2361                                 fputc(' ', out);
2362                 }
2363
2364                 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2365         }
2366
2367         if(db_fget(i, STATE))
2368                 fprintf(out, "\n%s", db_fget(i, STATE));
2369
2370         if(db_fget(i, COUNTRY))
2371                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2372 }
2373
2374 static int
2375 text_export_database(FILE * out, struct db_enumerator e)
2376 {
2377         abook_list *emails, *em;
2378         int j;
2379         char *realname = get_real_name(), *str = NULL, *tmp;
2380         char *style = opt_get_str(STR_ADDRESS_STYLE);
2381
2382         fprintf(out,
2383                 "-----------------------------------------\n%s's address book\n"
2384                 "-----------------------------------------\n\n\n",
2385                 realname);
2386         free(realname);
2387
2388         db_enumerate_items(e) {
2389                 fprintf(out,
2390                         "-----------------------------------------\n\n");
2391                 fprintf(out, "%s", db_name_get(e.item));
2392                 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2393                         fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2394                 fprintf(out, "\n");
2395
2396                 tmp = db_email_get(e.item);
2397                 if(*tmp) {
2398                         emails = csv_to_abook_list(tmp);
2399
2400                         fprintf(out, "\n");
2401                         for(em = emails; em; em = em->next)
2402                                 fprintf(out, "%s\n", em->data);
2403
2404                         abook_list_free(&emails);
2405                 }
2406                 free(tmp);
2407                 /* Print address */
2408                 if(db_fget(e.item, ADDRESS)) {
2409                         if(!safe_strcmp(style, "us"))   /* US like */
2410                                 text_write_address_us(out, e.item);
2411                         else if(!safe_strcmp(style, "uk"))      /* UK like */
2412                                 text_write_address_uk(out, e.item);
2413                         else    /* EU like */
2414                                 text_write_address_eu(out, e.item);
2415
2416                         fprintf(out, "\n");
2417                 }
2418
2419                 if((db_fget(e.item, PHONE)) ||
2420                         (db_fget(e.item, WORKPHONE)) ||
2421                         (db_fget(e.item, FAX)) ||
2422                         (db_fget(e.item, MOBILEPHONE))) {
2423                         fprintf(out, "\n");
2424                         for(j = PHONE; j <= MOBILEPHONE; j++)
2425                                 if(db_fget(e.item, j)) {
2426                                         get_field_info(field_id(j),
2427                                                         NULL, &str, NULL);
2428                                         fprintf(out, "%s: %s\n", str,
2429                                                 db_fget(e.item, j));
2430                                 }
2431                 }
2432
2433                 if(db_fget(e.item, URL))
2434                         fprintf(out, "\n%s\n", db_fget(e.item, URL));
2435                 if(db_fget(e.item, NOTES))
2436                         fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2437
2438                 fprintf(out, "\n");
2439         }
2440
2441         fprintf(out, "-----------------------------------------\n");
2442
2443         return 0;
2444 }
2445
2446 /*
2447  * end of printable export filter
2448  */
2449
2450 /*
2451  * elm alias export filter
2452  */
2453
2454 static int
2455 elm_alias_export(FILE *out, struct db_enumerator e)
2456 {
2457         char email[MAX_EMAIL_LEN];
2458         char *alias = NULL;
2459
2460         db_enumerate_items(e) {
2461                 alias = mutt_alias_genalias(e.item);
2462                 get_first_email(email, e.item);
2463                 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2464                 xfree(alias);
2465         }
2466
2467         return 0;
2468 }
2469
2470 /*
2471  * end of elm alias export filter
2472  */
2473
2474
2475 /*
2476  * Spruce export filter
2477  */
2478
2479 static int
2480 spruce_export_database (FILE *out, struct db_enumerator e)
2481 {
2482         char email[MAX_EMAIL_LEN];
2483
2484         fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2485
2486         db_enumerate_items(e) {
2487                 get_first_email(email, e.item);
2488                 if(strcmp(email, "")) {
2489                         fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2490                                         e.item,
2491                                         db_name_get(e.item),
2492                                         email,
2493                                         safe_str(db_fget(e.item, NOTES))
2494                                         );
2495                 }
2496         }
2497
2498         fprintf (out, "# End of address book file.\n");
2499
2500         return 0;
2501 }
2502
2503 /*
2504  * end of Spruce export filter
2505  */
2506
2507 /*
2508  * wanderlust addressbook export filter
2509  */
2510
2511 static int
2512 wl_export_database(FILE *out, struct db_enumerator e)
2513 {
2514         char email[MAX_EMAIL_LEN];
2515
2516         fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2517         db_enumerate_items(e) {
2518                 get_first_email(email, e.item);
2519                 if(*email) {
2520                         fprintf(out,
2521                                 "%s\t\"%s\"\t\"%s\"\n",
2522                                 email,
2523                                 safe_str(db_fget(e.item, NICK)),
2524                                 safe_str(db_name_get(e.item))
2525                         );
2526                 }
2527         }
2528
2529         fprintf (out, "\n# End of address book file.\n");
2530
2531         return 0;
2532 }
2533
2534 /*
2535  * end of wanderlust addressbook export filter
2536  */
2537
2538 /*
2539  * BSD calendar export filter
2540  */
2541
2542 static int
2543 bsdcal_export_database(FILE *out, struct db_enumerator e)
2544 {
2545         db_enumerate_items(e) {
2546                 int year, month = 0, day = 0;
2547                 char *anniversary = db_fget(e.item, ANNIVERSARY);
2548
2549                 if(anniversary) {
2550                         if(!parse_date_string(anniversary, &day, &month, &year))
2551                                 continue;
2552
2553                         fprintf(out,
2554                                 _("%02d/%02d\tAnniversary of %s\n"),
2555                                 month,
2556                                 day,
2557                                 safe_str(db_name_get(e.item))
2558                         );
2559                 }
2560         }
2561
2562         return 0;
2563 }
2564
2565 /*
2566  * end of BSD calendar export filter
2567  */
2568
2569 /*
2570  * custom export filter
2571  */
2572
2573 static int
2574 find_field_enum(char *s) {
2575         int i = -1;
2576         while(standard_fields[++i].key)
2577                 if(!strcmp(standard_fields[i].key, s))
2578                         return i;
2579         return -1;
2580 }
2581
2582 /* Convert a string with named placeholders to
2583    a *printf() compatible string.
2584    Stores the abook field values into ft. */
2585 void
2586 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2587 {
2588         if(! fmt_string || ! ft) {
2589           fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2590           exit(EXIT_FAILURE);
2591         }
2592
2593         char tmp[1] = { 0 };
2594         char *p, *start, *field_name = NULL;
2595         p = start = s;
2596
2597         while(*p) {
2598                 if(*p == '{') {
2599                   start = ++p;
2600
2601                   if(! *start) goto cannotparse;
2602                   p = strchr(start, '}');
2603                   if(! p) goto cannotparse;
2604                   strcat(fmt_string, "%s");
2605                   field_name = strndup(start, (size_t)(p-start));
2606                   *ft = find_field_enum(field_name);
2607                   if(*ft == -1) {
2608                     fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2609                     exit(EXIT_FAILURE);
2610                   }
2611
2612                   ft++;
2613                   start = ++p;
2614                 }
2615
2616                 else if(*p == '\\') {
2617                         ++p;
2618                         if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2619                         else if(*p == 'n') *tmp = '\n';
2620                         else if(*p == 't') *tmp = '\t';
2621                         else if(*p == 'r') *tmp = '\r';
2622                         else if(*p == 'v') *tmp = '\v';
2623                         else if(*p == 'b') *tmp = '\b';
2624                         else if(*p == 'a') *tmp = '\a';
2625                         else *tmp = *p;
2626                         strncat(fmt_string, tmp, 1);
2627                         start = ++p;
2628                 }
2629
2630                 // if no '\' following: quick mode using strchr/strncat
2631                 else if(! strchr(start, '\\')) {
2632                   p = strchr(start, '{');
2633                   if(p) { // copy until the next placeholder
2634                     strncat(fmt_string, start, (size_t)(p-start));
2635                     start = p;
2636                   }
2637                   else { // copy till the end
2638                     strncat( fmt_string,
2639                              start,
2640                              FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2641                     break;
2642                   }
2643                 }
2644
2645                 // otherwise character by character
2646                 else {
2647                         strncat(fmt_string, p, 1);
2648                         start = ++p;
2649                 }
2650         }
2651
2652         *ft = ITEM_FIELDS;
2653         return;
2654
2655  cannotparse:
2656         fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2657         free(ft);
2658         exit(EXIT_FAILURE);
2659 }
2660
2661 static int
2662 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2663
2664
2665 // stores the format string generated from --outformatstr {custom_format}
2666 // (when "custom" output format is requested)
2667 // overrides default value of custom_format set by from abook.c
2668 extern char custom_format[FORMAT_STRING_LEN];
2669 char parsed_custom_format[FORMAT_STRING_LEN];
2670 enum field_types *custom_format_fields = 0;
2671
2672 /* wrapper for custom_export_item:
2673    1) avoid messing with extern pointer
2674    2) adds \n
2675    3) follow the prototype needed for an abook_output_item_filter entry */
2676 void
2677 custom_print_item(FILE *out, int item)
2678 {
2679
2680   if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2681     fprintf(out, "\n");
2682 }
2683
2684 static int
2685 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2686 {
2687   char *p, *q = 0;
2688
2689   // if the first character is '!':
2690   // we first check that all fields exist before continuing
2691   if(*fmt == '!') {
2692     enum field_types *ftp = ft;
2693     while(*ft != ITEM_FIELDS) {
2694       if(! db_fget(item, *ft) )
2695         return 1;
2696       ft++;
2697     }
2698     ft = ftp;
2699     fmt++;
2700   }
2701
2702   while (*fmt) {
2703     if(!strncmp(fmt, "%s", 2)) {
2704       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2705       ft++;
2706       fmt+=2;
2707     } else if (*ft == ITEM_FIELDS) {
2708       fprintf(out, "%s", fmt);
2709       return 0;
2710     } else {
2711       p = strchr(fmt, '%');
2712       if(*p) {
2713         q = strndup(fmt, (size_t)(p-fmt));
2714         fprintf(out, "%s", q);
2715         free(q);
2716         fmt = p;
2717       }
2718       else {
2719         fprintf(out, "%s", fmt);
2720         return 0;
2721       }
2722     }
2723   }
2724
2725   return 0;
2726 }
2727
2728 static int
2729 custom_export_database(FILE *out, struct db_enumerator e)
2730 {
2731         enum field_types *ft =
2732           (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types));
2733
2734         parse_custom_format(custom_format, (char*)&parsed_custom_format, ft);
2735         db_enumerate_items(e) {
2736           if(custom_export_item(out, e.item, (char*)&parsed_custom_format, ft) == 0)
2737             fprintf(out, "\n");
2738         }
2739         return 0;
2740 }
2741
2742 /*
2743  * end of custom export filter
2744  */