]> git.deb.at Git - pkg/abook.git/blob - filter.c
autotools update: 2/2: autofiles after `autoreconf -f`
[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 */
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 enum {
1558         VCARD_KEY = 0,
1559         VCARD_KEY_ATTRIBUTE,
1560         VCARD_VALUE,
1561 };
1562
1563 static char *
1564 vcard_get_line_element(char *line, int element)
1565 {
1566         int i;
1567         char *line_copy = 0;
1568         char *result = 0;
1569         char *key = 0;
1570         char *key_attr = 0;
1571         char *value = 0;
1572
1573         line_copy = xstrdup(line);
1574
1575         /* change newline characters, if present, to end of string */
1576         for(i=0; line_copy[i]; i++) {
1577                 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1578                         line_copy[i] = '\0';
1579                         break;
1580                 }
1581         }
1582
1583         /* separate key from value */
1584         for(i=0; line_copy[i]; i++) {
1585                 if(line_copy[i] == ':') {
1586                         line_copy[i] = '\0';
1587                         key = line_copy;
1588                         value = &line_copy[i+1];
1589                         break;
1590                 }
1591         }
1592
1593         /* separate key from key attributes */
1594         /* works for vCard 2 as well (automagically) */
1595         if (key) {
1596                 for(i=0; key[i]; i++) {
1597                         if(key[i] == ';') {
1598                                 key[i] = '\0';
1599                                 key_attr = &key[i+1];
1600                                 break;
1601                         }
1602                 }
1603         }
1604
1605         switch(element) {
1606         case VCARD_KEY:
1607                 if(key)
1608                         result = xstrdup(key);
1609                 break;
1610         case VCARD_KEY_ATTRIBUTE:
1611                 if(key_attr)
1612                         result = xstrdup(key_attr);
1613                 break;
1614         case VCARD_VALUE:
1615                 if(value)
1616                         result = xstrdup(value);
1617                 break;
1618         }
1619
1620         xfree(line_copy);
1621         return result;
1622 }
1623
1624 static void
1625 vcard_parse_email(list_item item, char *line)
1626 {
1627         char *email;
1628
1629         email = vcard_get_line_element(line, VCARD_VALUE);
1630
1631         if(item[1]) {
1632                 item[1] = strconcat(item[1], ",", email, 0);
1633                 xfree(email);
1634         }
1635         else {
1636                 item[1] = email;
1637         }
1638 }
1639
1640
1641 /*
1642  * mappings between vCard ADR field and abook's ADDRESS
1643  * see rfc2426 section 3.2.1
1644  */
1645 static void
1646 vcard_parse_address(list_item item, char *line)
1647 {
1648         char *value;
1649
1650         value = vcard_get_line_element(line, VCARD_VALUE);
1651         if(!value)
1652                 return;
1653
1654         // vCard(post office box) - not used
1655         strsep(&value, ";");
1656         // vCard(the extended address)
1657         item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
1658         // vCard(the street address)
1659         item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
1660         // vCard(the locality)
1661         item_fput(item, CITY, xstrdup(strsep(&value, ";")));
1662         // vCard(the region)
1663         item_fput(item, STATE, xstrdup(strsep(&value, ";")));
1664         // vCard(the postal code)
1665         item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
1666         // vCard(the country name)
1667         item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
1668
1669         if(*value) xfree(value);
1670 }
1671
1672 static void
1673 vcard_parse_name(list_item item, char *line)
1674 {
1675         // store the "N" field into "NAME" *if* no "FN:"
1676         // value has already been stored here
1677         if(item[0]) return;
1678
1679         int i = -1;
1680         item[0] = vcard_get_line_element(line, VCARD_VALUE);
1681         // "N:" can be multivalued => replace ';' separators by ' '
1682         while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1683
1684         // http://www.daniweb.com/software-development/c/code/216919
1685         char *original = item[0], *p = original;
1686         int trimmed = 0;
1687         do {
1688           if (*original != ' ' || trimmed) {
1689             trimmed = 1; *p++ = *original;
1690           }
1691         } while(*original++);
1692 }
1693
1694 static void
1695 vcard_parse_phone(list_item item, char *line)
1696 {
1697         char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1698         char *value = vcard_get_line_element(line, VCARD_VALUE);
1699
1700         /* set the standard number */
1701         if (!type) item_fput(item, PHONE, value);
1702
1703         /*
1704          * see rfc2426 section 3.3.1
1705          * Note: we probably support both vCard 2 and 3
1706          */
1707         else {
1708                 if (strcasestr(type, "home") != NULL)
1709                         item_fput(item, PHONE, xstrdup(value));
1710                 else if (strcasestr(type, "work") != NULL)
1711                         item_fput(item, WORKPHONE, xstrdup(value));
1712                 else if (strcasestr(type, "fax") != NULL)
1713                         item_fput(item, FAX, xstrdup(value));
1714                 else if (strcasestr(type, "cell") != NULL)
1715                         item_fput(item, MOBILEPHONE, xstrdup(value));
1716
1717                 xfree(type);
1718                 xfree(value);
1719         }
1720 }
1721
1722 static void
1723 vcard_parse_line(list_item item, char *line)
1724 {
1725         int i;
1726         char *key;
1727
1728         for(i=0; vcard_fields[i]; i++) {
1729                 key = vcard_fields[i];
1730
1731                 if(0 == strncmp(key, line, strlen(key))) {
1732                         if(0 == strcmp(key, "EMAIL"))
1733                                 vcard_parse_email(item, line);
1734                         else if(i == 2)
1735                                 vcard_parse_address(item, line);
1736                         else if(0 == strcmp(key, "TEL"))
1737                                 vcard_parse_phone(item, line);
1738                         else if(0 == strcmp(key, "N"))
1739                                 vcard_parse_name(item, line);
1740                         else
1741                                 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1742                         return;
1743                 }
1744         }
1745 }
1746
1747 static void
1748 vcard_parse_item(FILE *in)
1749 {
1750         char *line = NULL;
1751         list_item item = item_create();
1752
1753         while(!feof(in)) {
1754                 line = getaline(in);
1755
1756                 if(line && !strncmp("END:VCARD", line, 9)) {
1757                         xfree(line);
1758                         break;
1759                 }
1760                 else if(line) {
1761                         vcard_parse_line(item, line);
1762                         xfree(line);
1763                 }
1764         }
1765
1766         add_item2database(item);
1767         item_free(&item);
1768 }
1769
1770 static int
1771 vcard_parse_file(FILE *in)
1772 {
1773         char *line = NULL;
1774
1775         while(!feof(in)) {
1776                 line = getaline(in);
1777
1778                 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1779                         xfree(line);
1780                         vcard_parse_item(in);
1781                 }
1782                 else if(line) {
1783                         xfree(line);
1784                 }
1785         }
1786
1787         return 0;
1788 }
1789
1790 /*
1791  * end of vCard import filter
1792  */
1793
1794 /*
1795  * csv addressbook export filters
1796  */
1797
1798 #define CSV_LAST                (-1)
1799 #define CSV_UNDEFINED           (-2)
1800 #define CSV_SPECIAL(X)          (-3 - (X))
1801 #define CSV_IS_SPECIAL(X)       ((X) <= -3)
1802
1803 static int
1804 csv_export_common(FILE *out, struct db_enumerator e,
1805                 int fields[], void (*special_func)(FILE *, int, int))
1806 {
1807         int i;
1808
1809         db_enumerate_items(e) {
1810                 for(i = 0; fields[i] != CSV_LAST; i++) {
1811                         if(fields[i] == CSV_UNDEFINED)
1812                                 fprintf(out, "\"\"");
1813                         else if(CSV_IS_SPECIAL(fields[i])) {
1814                                 if(special_func)
1815                                         (*special_func)(out, e.item, fields[i]);
1816                         } else
1817                                 /*fprintf(out,(
1818                         strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1819                         strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1820                                 "\"%s\"" : "%s",
1821                                 safe_str(database[e.item][field_idx(fields[i])])
1822                                 );*/
1823                                 fprintf(out, "\"%s\"",
1824                                         safe_str(db_fget(e.item,fields[i])));
1825
1826                         if(fields[i + 1] != CSV_LAST)
1827                                 fputc(',', out);
1828                 }
1829                 fputc('\n', out);
1830         }
1831
1832         return 0;
1833 }
1834
1835 static int
1836 csv_export_database(FILE *out, struct db_enumerator e)
1837 {
1838         int csv_export_fields[] = {
1839                 NAME,
1840                 EMAIL,
1841                 PHONE,
1842                 NOTES,
1843                 NICK,
1844                 CSV_LAST
1845         };
1846
1847         csv_export_common(out, e, csv_export_fields, NULL);
1848
1849         return 0;
1850 }
1851
1852 static int
1853 allcsv_export_database(FILE *out, struct db_enumerator e)
1854 {
1855         /*
1856          * TODO: Should get these atomatically from abook_fileds
1857          *  - JH
1858          */
1859         int allcsv_export_fields[] = {
1860                 NAME,
1861                 EMAIL,
1862                 ADDRESS,
1863                 ADDRESS2,
1864                 CITY,
1865                 STATE,
1866                 ZIP,
1867                 COUNTRY,
1868                 PHONE,
1869                 WORKPHONE,
1870                 FAX,
1871                 MOBILEPHONE,
1872                 NICK,
1873                 URL,
1874                 NOTES,
1875                 ANNIVERSARY,
1876                 GROUPS,
1877                 CSV_LAST
1878         };
1879
1880         fprintf(out, "#");
1881         fprintf(out, "\"NAME\",");
1882         fprintf(out, "\"EMAIL\",");
1883         fprintf(out, "\"ADDRESS\",");
1884         fprintf(out, "\"ADDRESS2\",");
1885         fprintf(out, "\"CITY\",");
1886         fprintf(out, "\"STATE\",");
1887         fprintf(out, "\"ZIP\",");
1888         fprintf(out, "\"COUNTRY\",");
1889         fprintf(out, "\"PHONE\",");
1890         fprintf(out, "\"WORKPHONE\",");
1891         fprintf(out, "\"FAX\",");
1892         fprintf(out, "\"MOBILEPHONE\",");
1893         fprintf(out, "\"NICK\",");
1894         fprintf(out, "\"URL\",");
1895         fprintf(out, "\"NOTES\",");
1896         fprintf(out, "\"ANNIVERSARY\",");
1897         fprintf(out, "\"GROUPS\"\n");
1898
1899         csv_export_common(out, e, allcsv_export_fields, NULL);
1900
1901         return 0;
1902 }
1903
1904 /*
1905  * palm csv
1906  */
1907
1908 #define PALM_CSV_NAME   CSV_SPECIAL(0)
1909 #define PALM_CSV_END    CSV_SPECIAL(1)
1910 #define PALM_CSV_CAT    CSV_SPECIAL(2)
1911
1912 static void
1913 palm_split_and_write_name(FILE *out, char *name)
1914 {
1915         char *p;
1916
1917         assert(name);
1918
1919         if ( (p = strchr(name, ' ')) ) {
1920                 /*
1921                  * last name first
1922                  */
1923                 fprintf(out, "\"%s\",\"" , p + 1);
1924                 fwrite((void *)name, p - name, sizeof(char), out);
1925                 fputc('\"', out);
1926         } else {
1927                 fprintf(out, "\"%s\"", safe_str(name));
1928         }
1929 }
1930
1931 static void
1932 palm_csv_handle_specials(FILE *out, int item, int field)
1933 {
1934         switch(field) {
1935                 case PALM_CSV_NAME:
1936                         palm_split_and_write_name(out, db_name_get(item));
1937                         break;
1938                 case PALM_CSV_CAT:
1939                         fprintf(out, "\"abook\"");
1940                         break;
1941                 case PALM_CSV_END:
1942                         fprintf(out, "\"0\"");
1943                         break;
1944                 default:
1945                         assert(0);
1946         }
1947 }
1948
1949 static int
1950 palm_export_database(FILE *out, struct db_enumerator e)
1951 {
1952         int palm_export_fields[] = {
1953                 PALM_CSV_NAME,          /* LASTNAME, FIRSTNAME  */
1954                 CSV_UNDEFINED,          /* TITLE                */
1955                 CSV_UNDEFINED,          /* COMPANY              */
1956                 WORKPHONE,              /* WORK PHONE           */
1957                 PHONE,                  /* HOME PHONE           */
1958                 FAX,                    /* FAX                  */
1959                 MOBILEPHONE,            /* OTHER                */
1960                 EMAIL,                  /* EMAIL                */
1961                 ADDRESS,                /* ADDRESS              */
1962                 CITY,                   /* CITY                 */
1963                 STATE,                  /* STATE                */
1964                 ZIP,                    /* ZIP                  */
1965                 COUNTRY,                /* COUNTRY              */
1966                 NICK,                   /* DEFINED 1            */
1967                 URL,                    /* DEFINED 2            */
1968                 CSV_UNDEFINED,          /* DEFINED 3            */
1969                 CSV_UNDEFINED,          /* DEFINED 4            */
1970                 NOTES,                  /* NOTE                 */
1971                 PALM_CSV_END,           /* "0"                  */
1972                 PALM_CSV_CAT,           /* CATEGORY             */
1973                 CSV_LAST
1974         };
1975
1976         csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1977
1978         return 0;
1979 }
1980
1981 /*
1982  * end of csv export filters
1983  */
1984
1985 /*
1986  * vCard 2 addressbook export filter
1987  */
1988
1989 static int
1990 vcard_export_database(FILE *out, struct db_enumerator e)
1991 {
1992   db_enumerate_items(e)
1993     vcard_export_item(out, e.item);
1994   return 0;
1995 }
1996
1997 void
1998 vcard_export_item(FILE *out, int item)
1999 {
2000         int j, email_no;
2001         char *name, *tmp;
2002         abook_list *emails, *em;
2003         fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2004                 safe_str(db_name_get(item)));
2005
2006         name = get_surname(db_name_get(item));
2007         for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2008           if((db_name_get(item))[j] == ' ')
2009             break;
2010         }
2011         fprintf(out, "N:%s;%.*s\r\n",
2012                 safe_str(name),
2013                 j,
2014                 safe_str(db_name_get(item))
2015                 );
2016
2017         free(name);
2018
2019         // see rfc6350 section 6.3.1
2020         if(db_fget(item, ADDRESS)) {
2021                 fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
2022                         // pobox (unsupported)
2023                         safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
2024                         safe_str(db_fget(item, ADDRESS)), // street
2025                         safe_str(db_fget(item, CITY)), // locality
2026                         safe_str(db_fget(item, STATE)), // region
2027                         safe_str(db_fget(item, ZIP)), // code (postal)
2028                         safe_str(db_fget(item, COUNTRY)) // country
2029                         );
2030         }
2031
2032         if(db_fget(item, PHONE))
2033           fprintf(out, "TEL;HOME:%s\r\n",
2034                   db_fget(item, PHONE));
2035         if(db_fget(item, WORKPHONE))
2036           fprintf(out, "TEL;WORK:%s\r\n",
2037                   db_fget(item, WORKPHONE));
2038         if(db_fget(item, FAX))
2039           fprintf(out, "TEL;FAX:%s\r\n",
2040                   db_fget(item, FAX));
2041         if(db_fget(item, MOBILEPHONE))
2042           fprintf(out, "TEL;CELL:%s\r\n",
2043                   db_fget(item, MOBILEPHONE));
2044
2045         tmp = db_email_get(item);
2046         if(*tmp) {
2047           emails = csv_to_abook_list(tmp);
2048           fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
2049           email_no = 1;
2050           for(em = emails->next; em; em = em->next, email_no++ )
2051                   fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
2052
2053           abook_list_free(&emails);
2054         }
2055         free(tmp);
2056
2057         if(db_fget(item, NOTES))
2058           fprintf(out, "NOTE:%s\r\n",
2059                   db_fget(item, NOTES));
2060         if(db_fget(item, URL))
2061           fprintf(out, "URL:%s\r\n",
2062                   db_fget(item, URL));
2063
2064         fprintf(out, "END:VCARD\r\n\r\n");
2065
2066 }
2067
2068 /*
2069  * end of vCard export filter
2070  */
2071
2072
2073 /*
2074  * mutt alias export filter
2075  */
2076
2077 static char *
2078 mutt_alias_genalias(int i)
2079 {
2080         char *tmp, *pos;
2081
2082         if(db_fget(i, NICK))
2083                 return xstrdup(db_fget(i, NICK));
2084
2085         tmp = xstrdup(db_name_get(i));
2086
2087         if( ( pos = strchr(tmp, ' ') ) )
2088                 *pos = 0;
2089
2090         strlower(tmp);
2091
2092         return tmp;
2093 }
2094
2095 /*
2096  * This function is a variant of abook_list_to_csv
2097  * */
2098 static char *
2099 mutt_alias_gengroups(int i)
2100 {
2101         char *groups, *res = NULL;
2102         char groupstr[7] = "-group ";
2103         abook_list *list, *tmp;
2104
2105         groups = db_fget(i, GROUPS);
2106
2107         if(!groups)
2108                 return NULL;
2109
2110         list = csv_to_abook_list(groups);
2111         for(tmp = list; tmp; tmp = tmp->next) {
2112                 if(tmp == list) {
2113                         res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2114                         res = strcpy(res, groupstr);
2115                 } else {
2116                         res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2117                         strcat(res, " ");
2118                         strcat(res, groupstr);
2119                 }
2120                 strcat(res, tmp->data);
2121         }
2122         abook_list_free(&list);
2123         xfree(groups);
2124
2125         return res;
2126 }
2127
2128 static int
2129 mutt_alias_export(FILE *out, struct db_enumerator e)
2130 {
2131         char email[MAX_EMAIL_LEN];
2132         char *alias = NULL;
2133         char *groups = NULL;
2134         int email_addresses;
2135         char *ptr;
2136
2137         db_enumerate_items(e) {
2138                 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2139                 groups = (field_id(GROUPS) != -1) ?  mutt_alias_gengroups(e.item) : NULL;
2140                 get_first_email(email, e.item);
2141
2142                 /* do not output contacts without email address */
2143                 /* cause this does not make sense in mutt aliases */
2144                 if (*email) {
2145
2146                         /* output first email address */
2147                         fprintf(out,"alias ");
2148                         if(groups)
2149                                 fprintf(out, "%s ", groups);
2150                         if(alias)
2151                                 fprintf(out, "%s ", alias);
2152                         fprintf(out, "%s <%s>\n",
2153                                         db_name_get(e.item),
2154                                         email);
2155
2156                         /* number of email addresses */
2157                         email_addresses = 1;
2158                         ptr = db_email_get(e.item);
2159                         while (*ptr != '\0') {
2160                                 if (*ptr == ',') {
2161                                         email_addresses++;
2162                                 }
2163                                 ptr++;
2164                         }
2165
2166                         /* output other email addresses */
2167                         while (email_addresses-- > 1) {
2168                                 roll_emails(e.item, ROTATE_RIGHT);
2169                                 get_first_email(email, e.item);
2170                                 fprintf(out,"alias ");
2171                                 if( groups )
2172                                         fprintf(out, "%s ", groups);
2173                                 if(alias)
2174                                         fprintf(out, "%s__%s ", alias, email);
2175                                 else
2176                                         fprintf(out, "%s__%s ", db_name_get(e.item), email);
2177                                 fprintf(out, "%s <%s>\n",
2178                                                 db_name_get(e.item),
2179                                                 email);
2180                         }
2181                         roll_emails(e.item, ROTATE_RIGHT);
2182                         xfree(alias);
2183                         xfree(groups);
2184                 }
2185         }
2186
2187         return 0;
2188 }
2189
2190 void muttq_print_item(FILE *file, int item)
2191 {
2192         abook_list *emails, *e;
2193         char *tmp = db_email_get(item);
2194
2195         emails = csv_to_abook_list(tmp);
2196         free(tmp);
2197
2198         for(e = emails; e; e = e->next) {
2199                 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2200                                 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2201                                 );
2202                 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2203                         break;
2204         }
2205         abook_list_free(&emails);
2206 }
2207
2208 static int
2209 mutt_query_export_database(FILE *out, struct db_enumerator e)
2210 {
2211   fprintf(out, "All items\n");
2212   db_enumerate_items(e)
2213     muttq_print_item(out, e.item);
2214   return 0;
2215 }
2216
2217 /*
2218  * end of mutt alias export filter
2219  */
2220
2221
2222 /*
2223  * printable export filter
2224  */
2225
2226
2227 static void
2228 text_write_address_us(FILE *out, int i) {
2229         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2230
2231         if(db_fget(i, ADDRESS2))
2232                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2233
2234         if(db_fget(i, CITY))
2235                 fprintf(out, "\n%s", db_fget(i, CITY));
2236
2237         if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2238                 fputc('\n', out);
2239
2240                 if(db_fget(i, STATE)) {
2241                         fprintf(out, "%s", db_fget(i, STATE));
2242                         if(db_fget(i, ZIP))
2243                                 fputc(' ', out);
2244                 }
2245
2246                 if(db_fget(i, ZIP))
2247                         fprintf(out, "%s", db_fget(i, ZIP));
2248         }
2249
2250         if(db_fget(i, COUNTRY))
2251                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2252 }
2253
2254
2255 static void
2256 text_write_address_uk(FILE *out, int i) {
2257         int j;
2258
2259         for(j = ADDRESS; j <= COUNTRY; j++)
2260                 if(db_fget(i, j))
2261                         fprintf(out, "\n%s", db_fget(i, j));
2262 }
2263
2264 static void
2265 text_write_address_eu(FILE *out, int i) {
2266         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2267
2268         if(db_fget(i, ADDRESS2))
2269                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2270
2271         if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2272                 fputc('\n', out);
2273
2274                 if(db_fget(i, ZIP)) {
2275                         fprintf(out, "%s", db_fget(i, ZIP));
2276                         if(db_fget(i, CITY))
2277                                 fputc(' ', out);
2278                 }
2279
2280                 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2281         }
2282
2283         if(db_fget(i, STATE))
2284                 fprintf(out, "\n%s", db_fget(i, STATE));
2285
2286         if(db_fget(i, COUNTRY))
2287                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2288 }
2289
2290 static int
2291 text_export_database(FILE * out, struct db_enumerator e)
2292 {
2293         abook_list *emails, *em;
2294         int j;
2295         char *realname = get_real_name(), *str = NULL, *tmp;
2296         char *style = opt_get_str(STR_ADDRESS_STYLE);
2297
2298         fprintf(out,
2299                 "-----------------------------------------\n%s's address book\n"
2300                 "-----------------------------------------\n\n\n",
2301                 realname);
2302         free(realname);
2303
2304         db_enumerate_items(e) {
2305                 fprintf(out,
2306                         "-----------------------------------------\n\n");
2307                 fprintf(out, "%s", db_name_get(e.item));
2308                 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2309                         fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2310                 fprintf(out, "\n");
2311
2312                 tmp = db_email_get(e.item);
2313                 if(*tmp) {
2314                         emails = csv_to_abook_list(tmp);
2315
2316                         fprintf(out, "\n");
2317                         for(em = emails; em; em = em->next)
2318                                 fprintf(out, "%s\n", em->data);
2319
2320                         abook_list_free(&emails);
2321                 }
2322                 free(tmp);
2323                 /* Print address */
2324                 if(db_fget(e.item, ADDRESS)) {
2325                         if(!safe_strcmp(style, "us"))   /* US like */
2326                                 text_write_address_us(out, e.item);
2327                         else if(!safe_strcmp(style, "uk"))      /* UK like */
2328                                 text_write_address_uk(out, e.item);
2329                         else    /* EU like */
2330                                 text_write_address_eu(out, e.item);
2331
2332                         fprintf(out, "\n");
2333                 }
2334
2335                 if((db_fget(e.item, PHONE)) ||
2336                         (db_fget(e.item, WORKPHONE)) ||
2337                         (db_fget(e.item, FAX)) ||
2338                         (db_fget(e.item, MOBILEPHONE))) {
2339                         fprintf(out, "\n");
2340                         for(j = PHONE; j <= MOBILEPHONE; j++)
2341                                 if(db_fget(e.item, j)) {
2342                                         get_field_info(field_id(j),
2343                                                         NULL, &str, NULL);
2344                                         fprintf(out, "%s: %s\n", str,
2345                                                 db_fget(e.item, j));
2346                                 }
2347                 }
2348
2349                 if(db_fget(e.item, URL))
2350                         fprintf(out, "\n%s\n", db_fget(e.item, URL));
2351                 if(db_fget(e.item, NOTES))
2352                         fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2353
2354                 fprintf(out, "\n");
2355         }
2356
2357         fprintf(out, "-----------------------------------------\n");
2358
2359         return 0;
2360 }
2361
2362 /*
2363  * end of printable export filter
2364  */
2365
2366 /*
2367  * elm alias export filter
2368  */
2369
2370 static int
2371 elm_alias_export(FILE *out, struct db_enumerator e)
2372 {
2373         char email[MAX_EMAIL_LEN];
2374         char *alias = NULL;
2375
2376         db_enumerate_items(e) {
2377                 alias = mutt_alias_genalias(e.item);
2378                 get_first_email(email, e.item);
2379                 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2380                 xfree(alias);
2381         }
2382
2383         return 0;
2384 }
2385
2386 /*
2387  * end of elm alias export filter
2388  */
2389
2390
2391 /*
2392  * Spruce export filter
2393  */
2394
2395 static int
2396 spruce_export_database (FILE *out, struct db_enumerator e)
2397 {
2398         char email[MAX_EMAIL_LEN];
2399
2400         fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2401
2402         db_enumerate_items(e) {
2403                 get_first_email(email, e.item);
2404                 if(strcmp(email, "")) {
2405                         fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2406                                         e.item,
2407                                         db_name_get(e.item),
2408                                         email,
2409                                         safe_str(db_fget(e.item, NOTES))
2410                                         );
2411                 }
2412         }
2413
2414         fprintf (out, "# End of address book file.\n");
2415
2416         return 0;
2417 }
2418
2419 /*
2420  * end of Spruce export filter
2421  */
2422
2423 /*
2424  * wanderlust addressbook export filter
2425  */
2426
2427 static int
2428 wl_export_database(FILE *out, struct db_enumerator e)
2429 {
2430         char email[MAX_EMAIL_LEN];
2431
2432         fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2433         db_enumerate_items(e) {
2434                 get_first_email(email, e.item);
2435                 if(*email) {
2436                         fprintf(out,
2437                                 "%s\t\"%s\"\t\"%s\"\n",
2438                                 email,
2439                                 safe_str(db_fget(e.item, NICK)),
2440                                 safe_str(db_name_get(e.item))
2441                         );
2442                 }
2443         }
2444
2445         fprintf (out, "\n# End of address book file.\n");
2446
2447         return 0;
2448 }
2449
2450 /*
2451  * end of wanderlust addressbook export filter
2452  */
2453
2454 /*
2455  * BSD calendar export filter
2456  */
2457
2458 static int
2459 bsdcal_export_database(FILE *out, struct db_enumerator e)
2460 {
2461         db_enumerate_items(e) {
2462                 int year, month = 0, day = 0;
2463                 char *anniversary = db_fget(e.item, ANNIVERSARY);
2464
2465                 if(anniversary) {
2466                         if(!parse_date_string(anniversary, &day, &month, &year))
2467                                 continue;
2468
2469                         fprintf(out,
2470                                 _("%02d/%02d\tAnniversary of %s\n"),
2471                                 month,
2472                                 day,
2473                                 safe_str(db_name_get(e.item))
2474                         );
2475                 }
2476         }
2477
2478         return 0;
2479 }
2480
2481 // see also enum field_types @database.h
2482 extern abook_field standard_fields[];
2483 static int
2484 find_field_enum(char *s) {
2485         int i = -1;
2486         while(standard_fields[++i].key)
2487                 if(!strcmp(standard_fields[i].key, s))
2488                         return i;
2489         return -1;
2490 }
2491
2492 /* Convert a string with named placeholders to
2493    a *printf() compatible string.
2494    Stores the abook field values into ft. */
2495 void
2496 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2497 {
2498         if(! fmt_string || ! ft) {
2499           fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2500           exit(EXIT_FAILURE);
2501         }
2502
2503         char tmp[1] = { 0 };
2504         char *p, *start, *field_name = NULL;
2505         p = start = s;
2506
2507         while(*p) {
2508                 if(*p == '{') {
2509                   start = ++p;
2510
2511                   if(! *start) goto cannotparse;
2512                   p = strchr(start, '}');
2513                   if(! p) goto cannotparse;
2514                   strcat(fmt_string, "%s");
2515                   field_name = strndup(start, (size_t)(p-start));
2516                   *ft = find_field_enum(field_name);
2517                   if(*ft == -1) {
2518                     fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2519                     exit(EXIT_FAILURE);
2520                   }
2521
2522                   ft++;
2523                   start = ++p;
2524                 }
2525
2526                 else if(*p == '\\') {
2527                         ++p;
2528                         if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2529                         else if(*p == 'n') *tmp = '\n';
2530                         else if(*p == 't') *tmp = '\t';
2531                         else if(*p == 'r') *tmp = '\r';
2532                         else if(*p == 'v') *tmp = '\v';
2533                         else if(*p == 'b') *tmp = '\b';
2534                         else if(*p == 'a') *tmp = '\a';
2535                         else *tmp = *p;
2536                         strncat(fmt_string, tmp, 1);
2537                         start = ++p;
2538                 }
2539
2540                 // if no '\' following: quick mode using strchr/strncat
2541                 else if(! strchr(start, '\\')) {
2542                   p = strchr(start, '{');
2543                   if(p) { // copy until the next placeholder
2544                     strncat(fmt_string, start, (size_t)(p-start));
2545                     start = p;
2546                   }
2547                   else { // copy till the end
2548                     strncat( fmt_string,
2549                              start,
2550                              FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2551                     break;
2552                   }
2553                 }
2554
2555                 // otherwise character by character
2556                 else {
2557                         strncat(fmt_string, p, 1);
2558                         start = ++p;
2559                 }
2560         }
2561
2562         *ft = ITEM_FIELDS;
2563         return;
2564
2565  cannotparse:
2566         fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2567         free(fmt_string);
2568         while(*ft) free(ft--);
2569         exit(EXIT_FAILURE);
2570 }
2571
2572 static int
2573 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2574
2575
2576 // used to store the format string from --outformatstr when "custom" format is used
2577 // default value overriden in export_file()
2578 extern char *parsed_custom_format;
2579 extern enum field_types *custom_format_fields;
2580
2581 /* wrapper for custom_export_item:
2582    1) avoid messing with extern pointer
2583    2) adds \n
2584    3) follow the prototype needed for an abook_output_item_filter entry */
2585 void
2586 custom_print_item(FILE *out, int item)
2587 {
2588
2589   if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2590     fprintf(out, "\n");
2591 }
2592
2593 static int
2594 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2595 {
2596   char *p, *q = 0;
2597
2598   // if the first character is '!':
2599   // we first check that all fields exist before continuing
2600   if(*fmt == '!') {
2601     enum field_types *ftp = ft;
2602     while(*ft != ITEM_FIELDS) {
2603       if(! db_fget(item, *ft) )
2604         return 1;
2605       ft++;
2606     }
2607     ft = ftp;
2608     fmt++;
2609   }
2610
2611   while (*fmt) {
2612     if(!strncmp(fmt, "%s", 2)) {
2613       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2614       ft++;
2615       fmt+=2;
2616     } else if (*ft == ITEM_FIELDS) {
2617       fprintf(out, "%s", fmt);
2618       return 0;
2619     } else {
2620       p = strchr(fmt, '%');
2621       if(*p) {
2622         q = strndup(fmt, (size_t)(p-fmt));
2623         fprintf(out, "%s", q);
2624         free(q);
2625         fmt = p;
2626       }
2627       else {
2628         fprintf(out, "%s", fmt);
2629         return 0;
2630       }
2631     }
2632   }
2633
2634   return 0;
2635 }
2636
2637 // used to store the format string from --outformatstr when "custom" format is used
2638 // default value overriden from abook.c
2639 extern char custom_format[FORMAT_STRING_LEN];
2640
2641 static int
2642 custom_export_database(FILE *out, struct db_enumerator e)
2643 {
2644         char *format_string =
2645           (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2646
2647         enum field_types *ft =
2648           (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2649
2650         parse_custom_format(custom_format, format_string, ft);
2651
2652         db_enumerate_items(e) {
2653           if(custom_export_item(out, e.item, format_string, ft) == 0)
2654             fprintf(out, "\n");
2655         }
2656         return 0;
2657 }
2658
2659 /*
2660  * end of BSD calendar export filter
2661  */
2662