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