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