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