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