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