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