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