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