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