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