]> git.deb.at Git - pkg/abook.git/blob - abook.c
mutt-query (3/3): register the --mutt-query-format per-item filter and use it
[pkg/abook.git] / abook.c
1 /*
2  * $Id$
3  *
4  * by JH <jheinonen@users.sourceforge.net>
5  *
6  * Copyright (C) Jaakko Heinonen
7  */
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <ctype.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #ifdef HAVE_CONFIG_H
19 #       include "config.h"
20 #endif
21 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
22 #       include <locale.h>
23 #endif
24 #include <assert.h>
25 #include "abook.h"
26 #include "gettext.h"
27 #include "ui.h"
28 #include "database.h"
29 #include "list.h"
30 #include "filter.h"
31 #include "edit.h"
32 #include "misc.h"
33 #include "options.h"
34 #include "getname.h"
35 #include "getopt.h"
36 #include "views.h"
37 #include "xmalloc.h"
38
39 static void             init_abook();
40 static void             quit_abook_sig(int i);
41 static void             set_filenames();
42 static void             parse_command_line(int argc, char **argv);
43 static void             show_usage();
44 static void             mutt_query(char *str);
45 static void             init_mutt_query();
46 static void             convert(char *srcformat, char *srcfile,
47                                 char *dstformat, char *dstfile);
48 static void             add_email(int);
49
50 char *datafile = NULL;
51 static char *rcfile = NULL;
52
53 struct abook_output_item_filter selected_item_filter;
54
55 bool alternative_datafile = FALSE;
56 bool alternative_rcfile = FALSE;
57
58
59 static int
60 datafile_writeable()
61 {
62         FILE *f;
63
64         assert(datafile != NULL);
65
66         if( (f = fopen(datafile, "a")) == NULL)
67                 return FALSE;
68
69         fclose(f);
70
71         return TRUE;
72 }
73
74 static void
75 check_abook_directory()
76 {
77         struct stat s;
78         char *dir;
79
80         assert(!is_ui_initialized());
81
82         if(alternative_datafile)
83                 return;
84
85         dir = strconcat(getenv("HOME"), "/" DIR_IN_HOME, NULL);
86         assert(dir != NULL);
87
88         if(stat(dir, &s) == -1) {
89                 if(errno != ENOENT) {
90                         perror(dir);
91                         free(dir);
92                         exit(EXIT_FAILURE);
93                 }
94                 if(mkdir(dir, 0700) == -1) {
95                         printf(_("Cannot create directory %s\n"), dir);
96                         perror(dir);
97                         free(dir);
98                         exit(EXIT_FAILURE);
99                 }
100         } else if(!S_ISDIR(s.st_mode)) {
101                 printf(_("%s is not a directory\n"), dir);
102                 free(dir);
103                 exit(EXIT_FAILURE);
104         }
105
106         free(dir);
107 }
108
109 static void
110 xmalloc_error_handler(int err)
111 {
112         /*
113          * We don't try to save addressbook here because we don't know
114          * if it's fully loaded to memory.
115          */
116         if(is_ui_initialized())
117                 close_ui();
118
119         fprintf(stderr, _("Memory allocation failure: %s\n"), strerror(err));
120         exit(EXIT_FAILURE);
121 }
122
123 static void
124 init_abook()
125 {
126         set_filenames();
127         check_abook_directory();
128         init_opts();
129         if(load_opts(rcfile) > 0) {
130                 printf(_("Press enter to continue...\n"));
131                 fgetc(stdin);
132         }
133         init_default_views();
134
135         signal(SIGTERM, quit_abook_sig);
136
137         init_index();
138
139         if(init_ui())
140                 exit(EXIT_FAILURE);
141
142         umask(DEFAULT_UMASK);
143
144         if(!datafile_writeable()) {
145                 char *s = strdup_printf(_("File %s is not writeable"), datafile);
146                 refresh_screen();
147                 statusline_msg(s);
148                 free(s);
149                 if(load_database(datafile) || !statusline_ask_boolean(
150                                         _("If you continue all changes will "
151                                 "be lost. Do you want to continue?"), FALSE)) {
152                         free_opts();
153                         /*close_database();*/
154                         close_ui();
155                         exit(EXIT_FAILURE);
156                 }
157         } else
158                 load_database(datafile);
159
160         refresh_screen();
161 }
162
163 void
164 quit_abook(int save_db)
165 {
166         if(save_db)  {
167                 if(opt_get_bool(BOOL_AUTOSAVE))
168                         save_database();
169                 else if(statusline_ask_boolean(_("Save database"), TRUE))
170                         save_database();
171         } else if(!statusline_ask_boolean(_("Quit without saving"), FALSE))
172                 return;
173
174         free_opts();
175         close_database();
176
177         close_ui();
178
179         exit(EXIT_SUCCESS);
180 }
181
182 static void
183 quit_abook_sig(int i)
184 {
185         quit_abook(QUIT_SAVE);
186 }
187
188 int
189 main(int argc, char **argv)
190 {
191 #if defined(HAVE_SETLOCALE) && defined(HAVE_LOCALE_H)
192         setlocale(LC_MESSAGES, "");
193         setlocale(LC_TIME, "");
194         setlocale(LC_CTYPE, "");
195         setlocale(LC_COLLATE, "");
196 #endif
197         bindtextdomain(PACKAGE, LOCALEDIR);
198         textdomain(PACKAGE);
199
200         xmalloc_set_error_handler(xmalloc_error_handler);
201
202         prepare_database_internals();
203
204         parse_command_line(argc, argv);
205
206         init_abook();
207
208         get_commands();
209
210         quit_abook(QUIT_SAVE);
211
212         return 0;
213 }
214
215 static void
216 free_filenames()
217 {
218         xfree(rcfile);
219         xfree(datafile);
220 }
221
222
223 static void
224 set_filenames()
225 {
226         struct stat s;
227
228         if( (stat(getenv("HOME"), &s)) == -1 || ! S_ISDIR(s.st_mode) ) {
229                 fprintf(stderr,_("%s is not a valid HOME directory\n"), getenv("HOME") );
230                 exit(EXIT_FAILURE);
231         }
232
233         if(!datafile)
234                 datafile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
235                                 DATAFILE, NULL);
236
237         if(!rcfile)
238                 rcfile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
239                                 RCFILE, NULL);
240
241         atexit(free_filenames);
242 }
243
244 /*
245  * CLI
246  */
247
248 enum {
249         MODE_CONT,
250         MODE_ADD_EMAIL,
251         MODE_ADD_EMAIL_QUIET,
252         MODE_QUERY,
253         MODE_CONVERT
254 };
255
256 static void
257 change_mode(int *current, int mode)
258 {
259         if(*current != MODE_CONT) {
260                 fprintf(stderr, _("Cannot combine options --mutt-query, "
261                                 "--convert, "
262                                 "--add-email or --add-email-quiet\n"));
263                 exit(EXIT_FAILURE);
264         }
265
266         *current = mode;
267 }
268
269 void
270 set_filename(char **var, char *path)
271 {
272         char *cwd;
273
274         assert(var != NULL);
275         assert(*var == NULL); /* or else we probably leak memory */
276         assert(path != NULL);
277
278         if(*path == '/') {
279                 *var = xstrdup(path);
280                 return;
281         }
282
283         cwd = my_getcwd();
284
285         *var = strconcat(cwd, "/", path, NULL);
286
287         free(cwd);
288 }
289
290 #define set_convert_var(X) do { if(mode != MODE_CONVERT) {\
291         fprintf(stderr, _("please use option --%s after --convert option\n"),\
292                         long_options[option_index].name);\
293                 exit(EXIT_FAILURE);\
294         } else\
295                 X = optarg;\
296         } while(0)
297
298 static void
299 parse_command_line(int argc, char **argv)
300 {
301         int mode = MODE_CONT;
302         char *query_string = NULL;
303         char *informat = "abook",
304                 *outformat = "text",
305                 *infile = "-",
306                 *outfile = "-";
307         int c;
308         selected_item_filter = select_output_item_filter("muttq");
309
310         for(;;) {
311                 int option_index = 0;
312                 enum {
313                         OPT_ADD_EMAIL,
314                         OPT_ADD_EMAIL_QUIET,
315                         OPT_MUTT_QUERY,
316                         OPT_CONVERT,
317                         OPT_INFORMAT,
318                         OPT_OUTFORMAT,
319                         OPT_INFILE,
320                         OPT_OUTFILE,
321                         OPT_FORMATS
322                 };
323                 static struct option long_options[] = {
324                         { "help", 0, 0, 'h' },
325                         { "add-email", 0, 0, OPT_ADD_EMAIL },
326                         { "add-email-quiet", 0, 0, OPT_ADD_EMAIL_QUIET },
327                         { "datafile", 1, 0, 'f' },
328                         { "mutt-query", 1, 0, OPT_MUTT_QUERY },
329                         { "config", 1, 0, 'C' },
330                         { "convert", 0, 0, OPT_CONVERT },
331                         { "informat", 1, 0, OPT_INFORMAT },
332                         { "outformat", 1, 0, OPT_OUTFORMAT },
333                         { "infile", 1, 0, OPT_INFILE },
334                         { "outfile", 1, 0, OPT_OUTFILE },
335                         { "formats", 0, 0, OPT_FORMATS },
336                         { 0, 0, 0, 0 }
337                 };
338
339                 c = getopt_long(argc, argv, "hC:",
340                                 long_options, &option_index);
341
342                 if(c == -1)
343                         break;
344
345                 switch(c) {
346                         case 'h':
347                                 show_usage();
348                                 exit(EXIT_SUCCESS);
349                         case OPT_ADD_EMAIL:
350                                 change_mode(&mode, MODE_ADD_EMAIL);
351                                 break;
352                         case OPT_ADD_EMAIL_QUIET:
353                                 change_mode(&mode, MODE_ADD_EMAIL_QUIET);
354                                 break;
355                         case 'f':
356                                 set_filename(&datafile, optarg);
357                                 alternative_datafile = TRUE;
358                                 break;
359                         case OPT_MUTT_QUERY:
360                                 query_string = optarg;
361                                 change_mode(&mode, MODE_QUERY);
362                                 break;
363                         case 'C':
364                                 set_filename(&rcfile, optarg);
365                                 alternative_rcfile = TRUE;
366                                 break;
367                         case OPT_CONVERT:
368                                 change_mode(&mode, MODE_CONVERT);
369                                 break;
370                         case OPT_INFORMAT:
371                                 set_convert_var(informat);
372                                 break;
373                         case OPT_OUTFORMAT:
374                                 outformat = optarg;
375                                 selected_item_filter = select_output_item_filter(outformat);
376                                 break;
377                         case OPT_INFILE:
378                                 set_convert_var(infile);
379                                 break;
380                         case OPT_OUTFILE:
381                                 set_convert_var(outfile);
382                                 break;
383                         case OPT_FORMATS:
384                                 print_filters();
385                                 exit(EXIT_SUCCESS);
386                         default:
387                                 exit(EXIT_FAILURE);
388                 }
389         }
390
391         if(! selected_item_filter.func)
392                 selected_item_filter = select_output_item_filter("muttq");
393         if(optind < argc) {
394                 fprintf(stderr, _("%s: unrecognized arguments on command line\n"),
395                                 argv[0]);
396                 exit(EXIT_FAILURE);
397         }
398
399         switch(mode) {
400                 case MODE_ADD_EMAIL:
401                         add_email(0);
402                 case MODE_ADD_EMAIL_QUIET:
403                         add_email(1);
404                 case MODE_QUERY:
405                         mutt_query(query_string);
406                 case MODE_CONVERT:
407                         convert(informat, infile, outformat, outfile);
408         }
409 }
410
411
412 static void
413 show_usage()
414 {
415         puts    (PACKAGE " v " VERSION "\n");
416         puts    (_("     -h     --help                          show usage"));
417         puts    (_("     -C     --config        <file>          use an alternative configuration file"));
418         puts    (_("    --datafile      <file>          use an alternative addressbook file"));
419         puts    (_("    --mutt-query    <string>        make a query for mutt"));
420         puts    (_("    --add-email                     "
421                         "read an e-mail message from stdin and\n"
422                 "                                       "
423                 "add the sender to the addressbook"));
424         puts    (_("    --add-email-quiet               "
425                 "same as --add-email but doesn't\n"
426                 "                                       require to confirm adding"));
427         putchar('\n');
428         puts    (_("    --convert                       convert address book files"));
429         puts    (_("    options to use with --convert:"));
430         puts    (_("    --informat      <format>        format for input file"));
431         puts    (_("                                    (default: abook)"));
432         puts    (_("    --infile        <file>          source file"));
433         puts    (_("                                    (default: stdin)"));
434         puts    (_("    --outformat     <format>        format for output file"));
435         puts    (_("                                    (default: text)"));
436         puts    (_("    --outfile       <file>          destination file"));
437         puts    (_("                                    (default: stdout)"));
438         puts    (_("    --formats                       list available formats"));
439 }
440
441 /*
442  * end of CLI
443  */
444
445
446 static void
447 quit_mutt_query(int status)
448 {
449         close_database();
450         free_opts();
451
452         exit(status);
453 }
454
455 static void
456 mutt_query(char *str)
457 {
458         init_mutt_query();
459
460         if( str == NULL || !strcasecmp(str, "all") ) {
461                 export_file("muttq", "-");
462         } else {
463                 int search_fields[] = {NAME, EMAIL, NICK, -1};
464                 int i;
465                 if( (i = find_item(str, 0, search_fields)) < 0 ) {
466                         printf("Not found\n");
467                         quit_mutt_query(EXIT_FAILURE);
468                 }
469                 putchar('\n');
470                 while(i >= 0) {
471                         e_write_item(stdout, i, selected_item_filter.func);
472                         i = find_item(str, i + 1, search_fields);
473                 }
474         }
475
476         quit_mutt_query(EXIT_SUCCESS);
477 }
478
479 static void
480 init_mutt_query()
481 {
482         set_filenames();
483         init_opts();
484         load_opts(rcfile);
485
486         if( load_database(datafile) ) {
487                 printf(_("Cannot open database\n"));
488                 quit_mutt_query(EXIT_FAILURE);
489                 exit(EXIT_FAILURE);
490         }
491 }
492
493
494 static char *
495 make_mailstr(int item)
496 {
497         char email[MAX_EMAIL_LEN];
498         char *ret;
499         char *name = strdup_printf("\"%s\"", db_name_get(item));
500
501         get_first_email(email, item);
502
503         ret = *email ?
504                 strdup_printf("%s <%s>", name, email) :
505                 xstrdup(name);
506
507         free(name);
508
509         return ret;
510 }
511
512 void
513 print_stderr(int item)
514 {
515         fprintf (stderr, "%c", '\n');
516
517         if( is_valid_item(item) )
518                 muttq_print_item(stderr, item);
519         else {
520                 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
521                 db_enumerate_items(e) {
522                         muttq_print_item(stderr, e.item);
523                 }
524         }
525
526 }
527
528 void
529 launch_mutt(int item)
530 {
531         char *cmd = NULL, *mailstr = NULL;
532         char *mutt_command = opt_get_str(STR_MUTT_COMMAND);
533
534         if(mutt_command == NULL || !*mutt_command)
535                 return;
536
537         if( is_valid_item(item) )
538                 mailstr = make_mailstr(item);
539         else {
540                 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
541                 char *tmp = NULL;
542                 db_enumerate_items(e) {
543                         tmp = mailstr;
544                         mailstr = tmp ?
545                                 strconcat(tmp, ",", make_mailstr(e.item), NULL):
546                                 strconcat(make_mailstr(e.item), NULL);
547                         free(tmp);
548                 }
549         }
550
551         cmd = strconcat(mutt_command, " \'", mailstr, "\'", NULL);
552         free(mailstr);
553 #ifdef DEBUG
554         fprintf(stderr, "cmd: %s\n", cmd);
555 #endif
556         system(cmd);
557         free(cmd);
558
559         /*
560          * we need to make sure that curses settings are correct
561          */
562         ui_init_curses();
563 }
564
565 void
566 launch_wwwbrowser(int item)
567 {
568         char *cmd = NULL;
569
570         if( !is_valid_item(item) )
571                 return;
572
573         if(db_fget(item, URL))
574                 cmd = strdup_printf("%s '%s'",
575                                 opt_get_str(STR_WWW_COMMAND),
576                                 safe_str(db_fget(item, URL)));
577         else
578                 return;
579
580         if ( cmd )
581                 system(cmd);
582
583         free(cmd);
584
585         /*
586          * we need to make sure that curses settings are correct
587          */
588         ui_init_curses();
589 }
590
591 FILE *
592 abook_fopen (const char *path, const char *mode)
593 {
594         struct stat s;
595         bool stat_ok;
596
597         stat_ok = (stat(path, &s) != -1);
598
599         if(strchr(mode, 'r'))
600                 return (stat_ok && S_ISREG(s.st_mode)) ?
601                         fopen(path, mode) : NULL;
602         else
603                 return (stat_ok && S_ISDIR(s.st_mode)) ?
604                         NULL : fopen(path, mode);
605 }
606
607 static void
608 convert(char *srcformat, char *srcfile, char *dstformat, char *dstfile)
609 {
610         int ret=0;
611
612         if( !srcformat || !srcfile || !dstformat || !dstfile ) {
613                 fprintf(stderr, _("too few arguments to make conversion\n"));
614                 fprintf(stderr, _("try --help\n"));
615         }
616
617 #ifndef DEBUG
618         if( !strcasecmp(srcformat, dstformat) ) {
619                 printf( _("input and output formats are the same\n"
620                         "exiting...\n"));
621                 exit(EXIT_FAILURE);
622         }
623 #endif
624
625         set_filenames();
626         init_opts();
627         load_opts(rcfile);
628         init_standard_fields();
629
630         switch(import_file(srcformat, srcfile)) {
631                 case -1:
632                         fprintf(stderr,
633                                 _("input format %s not supported\n"), srcformat);
634                         ret = 1;
635                         break;
636                 case 1:
637                         fprintf(stderr, _("cannot read file %s\n"), srcfile);
638                         ret = 1;
639                         break;
640         }
641
642         if(!ret)
643                 switch(export_file(dstformat, dstfile)) {
644                         case -1:
645                                 fprintf(stderr,
646                                         _("output format %s not supported\n"),
647                                         dstformat);
648                                 ret = 1;
649                                 break;
650                         case 1:
651                                 fprintf(stderr,
652                                         _("cannot write file %s\n"), dstfile);
653                                 ret = 1;
654                                 break;
655                 }
656
657         close_database();
658         free_opts();
659         exit(ret);
660 }
661
662 /*
663  * --add-email handling
664  */
665
666 static int add_email_count = 0;
667
668 static void
669 quit_add_email()
670 {
671         if(add_email_count > 0) {
672                 if(save_database() < 0) {
673                         fprintf(stderr, _("cannot open %s\n"), datafile);
674                         exit(EXIT_FAILURE);
675                 }
676                 printf(_("%d item(s) added to %s\n"), add_email_count, datafile);
677         } else {
678                 puts(_("Valid sender address not found"));
679         }
680
681         exit(EXIT_SUCCESS);
682 }
683
684 static void
685 quit_add_email_sig(int signal)
686 {
687         quit_add_email();
688 }
689
690 static void
691 init_add_email()
692 {
693         set_filenames();
694         check_abook_directory();
695         init_opts();
696         load_opts(rcfile);
697         init_standard_fields();
698         atexit(free_opts);
699
700         /*
701          * we don't actually care if loading fails or not
702          */
703         load_database(datafile);
704
705         atexit(close_database);
706
707         signal(SIGINT, quit_add_email_sig);
708 }
709
710 static int
711 add_email_add_item(int quiet, char *name, char *email)
712 {
713         list_item item;
714
715         if(opt_get_bool(BOOL_ADD_EMAIL_PREVENT_DUPLICATES)) {
716                 int search_fields[] = { EMAIL, -1 };
717                 if(find_item(email, 0, search_fields) >= 0) {
718                         if(!quiet)
719                                 printf(_("Address %s already in addressbook\n"),
720                                                 email);
721                         return 0;
722                 }
723         }
724
725         if(!quiet) {
726                 FILE *in = fopen("/dev/tty", "r");
727                 char c;
728                 if(!in) {
729                         fprintf(stderr, _("cannot open /dev/tty\n"
730                                 "you may want to use --add-email-quiet\n"));
731                         exit(EXIT_FAILURE);
732                 }
733
734                 do {
735                         printf(_("Add \"%s <%s>\" to %s? (%c/%c)\n"),
736                                         name,
737                                         email,
738                                         datafile,
739                                         *S_("keybinding for yes|y"),
740                                         *S_("keybinding for no|n"));
741                         c = tolower(getc(in));
742                         if(c == *S_("keybinding for no|n")) {
743                                 fclose(in);
744                                 return 0;
745                         }
746                 } while(c != *S_("keybinding for yes|y"));
747                 fclose(in);
748         }
749
750         item = item_create();
751         item_fput(item, NAME, xstrdup(name));
752         item_fput(item, EMAIL, xstrdup(email));
753         add_item2database(item);
754         item_free(&item);
755
756         return 1;
757 }
758
759 static void
760 add_email(int quiet)
761 {
762         char *line;
763         char *name = NULL, *email = NULL;
764         struct stat s;
765
766         if( (fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode) ) {
767                 fprintf(stderr, _("stdin is a directory or cannot stat stdin\n"));
768                 exit(EXIT_FAILURE);
769         }
770
771         init_add_email();
772
773         do {
774                 line = getaline(stdin);
775                 if(line && !strncasecmp("From:", line, 5) ) {
776                         getname(line, &name, &email);
777                         add_email_count += add_email_add_item(quiet,
778                                         name, email);
779                         xfree(name);
780                         xfree(email);
781                 }
782                 xfree(line);
783         } while( !feof(stdin) );
784
785         quit_add_email();
786 }
787
788 /*
789  * end of --add-email handling
790  */