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