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