]> git.deb.at Git - pkg/abook.git/blob - filter.c
* custom output format: fixes and enhancements
[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         if(db_fget(item, ADDRESS))
2031           fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
2032                   safe_str(db_fget(item, ADDRESS)),
2033                   safe_str(db_fget(item, ADDRESS2)),
2034                   safe_str(db_fget(item, CITY)),
2035                   safe_str(db_fget(item, STATE)),
2036                   safe_str(db_fget(item, ZIP)),
2037                   safe_str(db_fget(item, COUNTRY))
2038                   );
2039
2040         if(db_fget(item, PHONE))
2041           fprintf(out, "TEL;HOME:%s\r\n",
2042                   db_fget(item, PHONE));
2043         if(db_fget(item, WORKPHONE))
2044           fprintf(out, "TEL;WORK:%s\r\n",
2045                   db_fget(item, WORKPHONE));
2046         if(db_fget(item, FAX))
2047           fprintf(out, "TEL;FAX:%s\r\n",
2048                   db_fget(item, FAX));
2049         if(db_fget(item, MOBILEPHONE))
2050           fprintf(out, "TEL;CELL:%s\r\n",
2051                   db_fget(item, MOBILEPHONE));
2052
2053         tmp = db_email_get(item);
2054         if(*tmp) {
2055           emails = csv_to_abook_list(tmp);
2056
2057           for(em = emails; em; em = em->next)
2058             fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
2059
2060           abook_list_free(&emails);
2061         }
2062         free(tmp);
2063
2064         if(db_fget(item, NOTES))
2065           fprintf(out, "NOTE:%s\r\n",
2066                   db_fget(item, NOTES));
2067         if(db_fget(item, URL))
2068           fprintf(out, "URL:%s\r\n",
2069                   db_fget(item, URL));
2070
2071         fprintf(out, "END:VCARD\r\n\r\n");
2072
2073 }
2074
2075 /*
2076  * end of vCard export filter
2077  */
2078
2079
2080 /*
2081  * mutt alias export filter
2082  */
2083
2084 static char *
2085 mutt_alias_genalias(int i)
2086 {
2087         char *tmp, *pos;
2088
2089         if(db_fget(i, NICK))
2090                 return xstrdup(db_fget(i, NICK));
2091
2092         tmp = xstrdup(db_name_get(i));
2093
2094         if( ( pos = strchr(tmp, ' ') ) )
2095                 *pos = 0;
2096
2097         strlower(tmp);
2098
2099         return tmp;
2100 }
2101
2102 /*
2103  * This function is a variant of abook_list_to_csv
2104  * */
2105 static char *
2106 mutt_alias_gengroups(int i)
2107 {
2108         char *groups, *res = NULL;
2109         char groupstr[7] = "-group ";
2110         abook_list *list, *tmp;
2111
2112         groups = db_fget(i, GROUPS);
2113
2114         if(!groups)
2115                 return NULL;
2116
2117         list = csv_to_abook_list(groups);
2118         for(tmp = list; tmp; tmp = tmp->next) {
2119                 if(tmp == list) {
2120                         res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2121                         res = strcpy(res, groupstr);
2122                 } else {
2123                         res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2124                         strcat(res, " ");
2125                         strcat(res, groupstr);
2126                 }
2127                 strcat(res, tmp->data);
2128         }
2129         abook_list_free(&list);
2130         xfree(groups);
2131
2132         return res;
2133 }
2134
2135 static int
2136 mutt_alias_export(FILE *out, struct db_enumerator e)
2137 {
2138         char email[MAX_EMAIL_LEN];
2139         char *alias = NULL;
2140         char *groups = NULL;
2141         int email_addresses;
2142         char *ptr;
2143
2144         db_enumerate_items(e) {
2145                 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2146                 groups = (field_id(GROUPS) != -1) ?  mutt_alias_gengroups(e.item) : NULL;
2147                 get_first_email(email, e.item);
2148
2149                 /* do not output contacts without email address */
2150                 /* cause this does not make sense in mutt aliases */
2151                 if (*email) {
2152
2153                         /* output first email address */
2154                         fprintf(out,"alias ");
2155                         if(groups)
2156                                 fprintf(out, "%s ", groups);
2157                         if(alias)
2158                                 fprintf(out, "%s ", alias);
2159                         fprintf(out, "%s <%s>\n",
2160                                         db_name_get(e.item),
2161                                         email);
2162
2163                         /* number of email addresses */
2164                         email_addresses = 1;
2165                         ptr = db_email_get(e.item);
2166                         while (*ptr != '\0') {
2167                                 if (*ptr == ',') {
2168                                         email_addresses++;
2169                                 }
2170                                 ptr++;
2171                         }
2172
2173                         /* output other email addresses */
2174                         while (email_addresses-- > 1) {
2175                                 roll_emails(e.item, ROTATE_RIGHT);
2176                                 get_first_email(email, e.item);
2177                                 fprintf(out,"alias ");
2178                                 if( groups )
2179                                         fprintf(out, "%s ", groups);
2180                                 if(alias)
2181                                         fprintf(out, "%s__%s ", alias, email);
2182                                 else
2183                                         fprintf(out, "%s__%s ", db_name_get(e.item), email);
2184                                 fprintf(out, "%s <%s>\n",
2185                                                 db_name_get(e.item),
2186                                                 email);
2187                         }
2188                         roll_emails(e.item, ROTATE_RIGHT);
2189                         xfree(alias);
2190                         xfree(groups);
2191                 }
2192         }
2193
2194         return 0;
2195 }
2196
2197 void muttq_print_item(FILE *file, int item)
2198 {
2199         abook_list *emails, *e;
2200         char *tmp = db_email_get(item);
2201
2202         emails = csv_to_abook_list(tmp);
2203         free(tmp);
2204
2205         for(e = emails; e; e = e->next) {
2206                 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2207                                 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2208                                 );
2209                 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2210                         break;
2211         }
2212         abook_list_free(&emails);
2213 }
2214
2215 static int
2216 mutt_query_export_database(FILE *out, struct db_enumerator e)
2217 {
2218   fprintf(out, "All items\n");
2219   db_enumerate_items(e)
2220     muttq_print_item(out, e.item);
2221   return 0;
2222 }
2223
2224 /*
2225  * end of mutt alias export filter
2226  */
2227
2228
2229 /*
2230  * printable export filter
2231  */
2232
2233
2234 static void
2235 text_write_address_us(FILE *out, int i) {
2236         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2237
2238         if(db_fget(i, ADDRESS2))
2239                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2240
2241         if(db_fget(i, CITY))
2242                 fprintf(out, "\n%s", db_fget(i, CITY));
2243
2244         if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2245                 fputc('\n', out);
2246
2247                 if(db_fget(i, STATE)) {
2248                         fprintf(out, "%s", db_fget(i, STATE));
2249                         if(db_fget(i, ZIP))
2250                                 fputc(' ', out);
2251                 }
2252
2253                 if(db_fget(i, ZIP))
2254                         fprintf(out, "%s", db_fget(i, ZIP));
2255         }
2256
2257         if(db_fget(i, COUNTRY))
2258                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2259 }
2260
2261
2262 static void
2263 text_write_address_uk(FILE *out, int i) {
2264         int j;
2265
2266         for(j = ADDRESS; j <= COUNTRY; j++)
2267                 if(db_fget(i, j))
2268                         fprintf(out, "\n%s", db_fget(i, j));
2269 }
2270
2271 static void
2272 text_write_address_eu(FILE *out, int i) {
2273         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2274
2275         if(db_fget(i, ADDRESS2))
2276                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2277
2278         if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2279                 fputc('\n', out);
2280
2281                 if(db_fget(i, ZIP)) {
2282                         fprintf(out, "%s", db_fget(i, ZIP));
2283                         if(db_fget(i, CITY))
2284                                 fputc(' ', out);
2285                 }
2286
2287                 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2288         }
2289
2290         if(db_fget(i, STATE))
2291                 fprintf(out, "\n%s", db_fget(i, STATE));
2292
2293         if(db_fget(i, COUNTRY))
2294                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2295 }
2296
2297 static int
2298 text_export_database(FILE * out, struct db_enumerator e)
2299 {
2300         abook_list *emails, *em;
2301         int j;
2302         char *realname = get_real_name(), *str = NULL, *tmp;
2303         char *style = opt_get_str(STR_ADDRESS_STYLE);
2304
2305         fprintf(out,
2306                 "-----------------------------------------\n%s's address book\n"
2307                 "-----------------------------------------\n\n\n",
2308                 realname);
2309         free(realname);
2310
2311         db_enumerate_items(e) {
2312                 fprintf(out,
2313                         "-----------------------------------------\n\n");
2314                 fprintf(out, "%s", db_name_get(e.item));
2315                 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2316                         fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2317                 fprintf(out, "\n");
2318
2319                 tmp = db_email_get(e.item);
2320                 if(*tmp) {
2321                         emails = csv_to_abook_list(tmp);
2322
2323                         fprintf(out, "\n");
2324                         for(em = emails; em; em = em->next)
2325                                 fprintf(out, "%s\n", em->data);
2326
2327                         abook_list_free(&emails);
2328                 }
2329                 free(tmp);
2330                 /* Print address */
2331                 if(db_fget(e.item, ADDRESS)) {
2332                         if(!safe_strcmp(style, "us"))   /* US like */
2333                                 text_write_address_us(out, e.item);
2334                         else if(!safe_strcmp(style, "uk"))      /* UK like */
2335                                 text_write_address_uk(out, e.item);
2336                         else    /* EU like */
2337                                 text_write_address_eu(out, e.item);
2338
2339                         fprintf(out, "\n");
2340                 }
2341
2342                 if((db_fget(e.item, PHONE)) ||
2343                         (db_fget(e.item, WORKPHONE)) ||
2344                         (db_fget(e.item, FAX)) ||
2345                         (db_fget(e.item, MOBILEPHONE))) {
2346                         fprintf(out, "\n");
2347                         for(j = PHONE; j <= MOBILEPHONE; j++)
2348                                 if(db_fget(e.item, j)) {
2349                                         get_field_info(field_id(j),
2350                                                         NULL, &str, NULL);
2351                                         fprintf(out, "%s: %s\n", str,
2352                                                 db_fget(e.item, j));
2353                                 }
2354                 }
2355
2356                 if(db_fget(e.item, URL))
2357                         fprintf(out, "\n%s\n", db_fget(e.item, URL));
2358                 if(db_fget(e.item, NOTES))
2359                         fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2360
2361                 fprintf(out, "\n");
2362         }
2363
2364         fprintf(out, "-----------------------------------------\n");
2365
2366         return 0;
2367 }
2368
2369 /*
2370  * end of printable export filter
2371  */
2372
2373 /*
2374  * elm alias export filter
2375  */
2376
2377 static int
2378 elm_alias_export(FILE *out, struct db_enumerator e)
2379 {
2380         char email[MAX_EMAIL_LEN];
2381         char *alias = NULL;
2382
2383         db_enumerate_items(e) {
2384                 alias = mutt_alias_genalias(e.item);
2385                 get_first_email(email, e.item);
2386                 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2387                 xfree(alias);
2388         }
2389
2390         return 0;
2391 }
2392
2393 /*
2394  * end of elm alias export filter
2395  */
2396
2397
2398 /*
2399  * Spruce export filter
2400  */
2401
2402 static int
2403 spruce_export_database (FILE *out, struct db_enumerator e)
2404 {
2405         char email[MAX_EMAIL_LEN];
2406
2407         fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2408
2409         db_enumerate_items(e) {
2410                 get_first_email(email, e.item);
2411                 if(strcmp(email, "")) {
2412                         fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2413                                         e.item,
2414                                         db_name_get(e.item),
2415                                         email,
2416                                         safe_str(db_fget(e.item, NOTES))
2417                                         );
2418                 }
2419         }
2420
2421         fprintf (out, "# End of address book file.\n");
2422
2423         return 0;
2424 }
2425
2426 /*
2427  * end of Spruce export filter
2428  */
2429
2430 /*
2431  * wanderlust addressbook export filter
2432  */
2433
2434 static int
2435 wl_export_database(FILE *out, struct db_enumerator e)
2436 {
2437         char email[MAX_EMAIL_LEN];
2438
2439         fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2440         db_enumerate_items(e) {
2441                 get_first_email(email, e.item);
2442                 if(*email) {
2443                         fprintf(out,
2444                                 "%s\t\"%s\"\t\"%s\"\n",
2445                                 email,
2446                                 safe_str(db_fget(e.item, NICK)),
2447                                 safe_str(db_name_get(e.item))
2448                         );
2449                 }
2450         }
2451
2452         fprintf (out, "\n# End of address book file.\n");
2453
2454         return 0;
2455 }
2456
2457 /*
2458  * end of wanderlust addressbook export filter
2459  */
2460
2461 /*
2462  * BSD calendar export filter
2463  */
2464
2465 static int
2466 bsdcal_export_database(FILE *out, struct db_enumerator e)
2467 {
2468         db_enumerate_items(e) {
2469                 int year, month = 0, day = 0;
2470                 char *anniversary = db_fget(e.item, ANNIVERSARY);
2471
2472                 if(anniversary) {
2473                         if(!parse_date_string(anniversary, &day, &month, &year))
2474                                 continue;
2475
2476                         fprintf(out,
2477                                 _("%02d/%02d\tAnniversary of %s\n"),
2478                                 month,
2479                                 day,
2480                                 safe_str(db_name_get(e.item))
2481                         );
2482                 }
2483         }
2484
2485         return 0;
2486 }
2487
2488 // see also enum field_types @database.h
2489 extern abook_field standard_fields[];
2490 static int
2491 find_field_enum(char *s) {
2492         int i = -1;
2493         while(standard_fields[++i].key)
2494                 if(!strcmp(standard_fields[i].key, s))
2495                         return i;
2496         return -1;
2497 }
2498
2499 /* Convert a string with named placeholders to
2500    a *printf() compatible string.
2501    Stores the abook field values into ft. */
2502 void
2503 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2504 {
2505         if(! fmt_string || ! ft) {
2506           fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2507           exit(EXIT_FAILURE);
2508         }
2509
2510         char tmp[1] = { 0 };
2511         char *p, *start, *field_name = NULL;
2512         p = start = s;
2513
2514         while(*p) {
2515                 if(*p == '{') {
2516                   start = ++p;
2517
2518                   if(! *start) goto cannotparse;
2519                   p = strchr(start, '}');
2520                   if(! p) goto cannotparse;
2521                   strcat(fmt_string, "%s");
2522                   field_name = strndup(start, (size_t)(p-start));
2523                   *ft = find_field_enum(field_name);
2524                   if(*ft == -1) {
2525                     fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2526                     exit(EXIT_FAILURE);
2527                   }
2528
2529                   ft++;
2530                   start = ++p;
2531                 }
2532
2533                 else if(*p == '\\') {
2534                         ++p;
2535                         if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2536                         else if(*p == 'n') *tmp = '\n';
2537                         else if(*p == 't') *tmp = '\t';
2538                         else if(*p == 'r') *tmp = '\r';
2539                         else if(*p == 'v') *tmp = '\v';
2540                         else if(*p == 'b') *tmp = '\b';
2541                         else if(*p == 'a') *tmp = '\a';
2542                         else *tmp = *p;
2543                         strncat(fmt_string, tmp, 1);
2544                         start = ++p;
2545                 }
2546
2547                 // if no '\' following: quick mode using strchr/strncat
2548                 else if(! strchr(start, '\\')) {
2549                   p = strchr(start, '{');
2550                   if(p) { // copy until the next placeholder
2551                     strncat(fmt_string, start, (size_t)(p-start));
2552                     start = p;
2553                   }
2554                   else { // copy till the end
2555                     strncat( fmt_string,
2556                              start,
2557                              FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2558                     break;
2559                   }
2560                 }
2561
2562                 // otherwise character by character
2563                 else {
2564                         strncat(fmt_string, p, 1);
2565                         start = ++p;
2566                 }
2567         }
2568
2569         *ft = ITEM_FIELDS;
2570         return;
2571
2572  cannotparse:
2573         fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2574         free(fmt_string);
2575         while(*ft) free(ft--);
2576         exit(EXIT_FAILURE);
2577 }
2578
2579 static int
2580 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2581
2582
2583 // used to store the format string from --outformatstr when "custom" format is used
2584 // default value overriden in export_file()
2585 extern char *parsed_custom_format;
2586 extern enum field_types *custom_format_fields;
2587
2588 /* wrapper for custom_export_item:
2589    1) avoid messing with extern pointer
2590    2) adds \n
2591    3) follow the prototype needed for an abook_output_item_filter entry */
2592 void
2593 custom_print_item(FILE *out, int item)
2594 {
2595
2596   if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2597     fprintf(out, "\n");
2598 }
2599
2600 static int
2601 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2602 {
2603   char *p, *q = 0;
2604
2605   // if the first character is '!':
2606   // we first check that all fields exist before continuing
2607   if(*fmt == '!') {
2608     enum field_types *ftp = ft;
2609     while(*ft != ITEM_FIELDS) {
2610       if(! db_fget(item, *ft) )
2611         return 1;
2612       ft++;
2613     }
2614     ft = ftp;
2615     fmt++;
2616   }
2617
2618   while (*fmt) {
2619     if(!strncmp(fmt, "%s", 2)) {
2620       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2621       ft++;
2622       fmt+=2;
2623     } else if (*ft == ITEM_FIELDS) {
2624       fprintf(out, "%s", fmt);
2625       return 0;
2626     } else {
2627       p = strchr(fmt, '%');
2628       if(*p) {
2629         q = strndup(fmt, (size_t)(p-fmt));
2630         fprintf(out, "%s", q);
2631         free(q);
2632         fmt = p;
2633       }
2634       else {
2635         fprintf(out, "%s", fmt);
2636         return 0;
2637       }
2638     }
2639   }
2640
2641   return 0;
2642 }
2643
2644 // used to store the format string from --outformatstr when "custom" format is used
2645 // default value overriden from abook.c
2646 extern char custom_format[FORMAT_STRING_LEN];
2647
2648 static int
2649 custom_export_database(FILE *out, struct db_enumerator e)
2650 {
2651         char *format_string =
2652           (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2653
2654         enum field_types *ft =
2655           (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2656
2657         parse_custom_format(custom_format, format_string, ft);
2658
2659         db_enumerate_items(e) {
2660           if(custom_export_item(out, e.item, format_string, ft) == 0)
2661             fprintf(out, "\n");
2662         }
2663         return 0;
2664 }
2665
2666 /*
2667  * end of BSD calendar export filter
2668  */
2669