1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2005-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #include <mimeview.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 
28 #include "mailutils/cli.h"
29 #include "mailutils/argcv.h"
30 
31 #include "mailcap.h"
32 
33 static int dry_run;    /* Dry run mode */
34 static int lint;       /* Syntax check mode */
35 static int identify;   /* Print only the file's type */
36 static char *metamail; /* Name of metamail program, if requested */
37 static char *mimetypes_config = DEFAULT_CUPS_CONFDIR;
38 static char *no_ask_types;  /* List of MIME types for which no questions
39 			       should be asked */
40 static int interactive = -1;
41 char const *mimeview_file;      /* Name of the file to view */
42 mu_stream_t mimeview_stream;    /* The corresponding stream */
43 
44 
45 static void
cli_no_ask(struct mu_parseopt * po,struct mu_option * opt,char const * arg)46 cli_no_ask (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
47 {
48   no_ask_types = mu_strdup (arg ? arg : "*");
49   setenv ("MM_NOASK", arg, 1); /* In case we are given --metamail option */
50 }
51 
52 static void
cli_no_interactive(struct mu_parseopt * po,struct mu_option * opt,char const * arg)53 cli_no_interactive (struct mu_parseopt *po, struct mu_option *opt,
54 		    char const *arg)
55 {
56   interactive = 0;
57 }
58 
59 static void
cli_debug(struct mu_parseopt * po,struct mu_option * opt,char const * arg)60 cli_debug (struct mu_parseopt *po, struct mu_option *opt,
61 	   char const *arg)
62 {
63   mu_debug_level_t lev;
64   if (!arg)
65     lev = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE2);
66   else
67     {
68       mu_debug_get_category_level (MU_DEBCAT_APP, &lev);
69       for (; *arg; arg++)
70 	{
71 	  switch (*arg)
72 	    {
73 	    case 'l':
74 	      lev |= MU_DEBUG_LEVEL_MASK (MU_DEBUG_TRACE4);
75 	      break;
76 
77 	    case 'g':
78 	      lev |= MU_DEBUG_LEVEL_MASK (MU_DEBUG_TRACE3);
79 	      break;
80 
81 	    default:
82 	      if (mu_isdigit (*arg))
83 		lev |= MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE0 + *arg - '0');
84 	      else
85 		mu_parseopt_error (po, _("ignoring invalid debug flag: %c"),
86 				   *arg);
87 	  }
88 	}
89     }
90   mu_debug_set_category_level (MU_DEBCAT_APP, lev);
91 }
92 
93 static void
cli_metamail(struct mu_parseopt * po,struct mu_option * opt,char const * arg)94 cli_metamail (struct mu_parseopt *po, struct mu_option *opt,
95 	      char const *arg)
96 {
97   if (!arg)
98     arg = "metamail";
99   metamail = mu_strdup (arg);
100 }
101 
102 static struct mu_option mimeview_options[] = {
103   { "no-ask", 'a', N_("TYPE-LIST"), MU_OPTION_ARG_OPTIONAL,
104     N_("do not ask for confirmation before displaying files, or, if TYPE-LIST is given, do not ask for confirmation before displaying such files whose MIME type matches one of the patterns from TYPE-LIST"),
105     mu_c_string, NULL, cli_no_ask },
106   { "no-interactive", 'h', NULL, MU_OPTION_DEFAULT,
107    N_("disable interactive mode"),
108     mu_c_string, NULL, cli_no_interactive },
109   { "print",  0, NULL, MU_OPTION_ALIAS },
110   { "debug", 'd', N_("FLAGS"),  MU_OPTION_ARG_OPTIONAL,
111     N_("enable debugging output"),
112     mu_c_string, NULL, cli_debug },
113   { "mimetypes", 'f', N_("FILE"), MU_OPTION_DEFAULT,
114     N_("use this mime.types file"),
115     mu_c_string, &mimetypes_config },
116 
117   { "dry-run",   'n', NULL, MU_OPTION_DEFAULT,
118     N_("do nothing, just print what would have been done"),
119     mu_c_bool, &dry_run },
120 
121   { "lint",      't', NULL, MU_OPTION_DEFAULT,
122     N_("test mime.types syntax and exit"),
123     mu_c_bool, &lint },
124 
125   { "identify",  'i', NULL, MU_OPTION_DEFAULT,
126     N_("identify MIME type of each file"),
127     mu_c_bool, &identify },
128 
129   { "metamail",    0, N_("FILE"), MU_OPTION_ARG_OPTIONAL,
130     N_("use metamail to display files"),
131     mu_c_string, NULL, cli_metamail },
132 
133   MU_OPTION_END
134 }, *options[] = { mimeview_options, NULL };
135 
136 struct mu_cfg_param mimeview_cfg_param[] = {
137   { "mimetypes", mu_c_string, &mimetypes_config, 0, NULL,
138     N_("Use this mime.types file."),
139     N_("file") },
140   { "metamail", mu_c_string, &metamail, 0, NULL,
141     N_("Use this program to display files."),
142     N_("prog") },
143   { NULL }
144 };
145 
146 struct mu_cli_setup cli = {
147   options,
148   mimeview_cfg_param,
149   N_("GNU mimeview -- display files, using mailcap mechanism."),
150   N_("FILE [FILE ...]"),
151   NULL,
152   N_("Debug flags are:\n\
153   g - Mime.types parser traces\n\
154   l - Mime.types lexical analyzer traces\n\
155   0-9 - Set debugging level\n")
156 };
157 
158 static char *capa[] = {
159   "debug",
160   NULL
161 };
162 
163 static int
open_file(char const * name)164 open_file (char const *name)
165 {
166   int rc;
167   struct stat st;
168 
169   if (stat (name, &st))
170     {
171       mu_error (_("cannot stat `%s': %s"), name, mu_strerror (errno));
172       return -1;
173     }
174   if (!S_ISREG (st.st_mode) && !S_ISLNK (st.st_mode))
175     {
176       mu_error (_("not a regular file or symbolic link: `%s'"), name);
177       return -1;
178     }
179 
180   mimeview_file = name;
181   rc = mu_file_stream_create (&mimeview_stream, mimeview_file, MU_STREAM_READ);
182   if (rc)
183     {
184       mu_error (_("Cannot open `%s': %s"), name, mu_strerror (rc));
185       return -1;
186     }
187   return 0;
188 }
189 
190 void
close_file()191 close_file ()
192 {
193   mu_stream_close (mimeview_stream);
194 }
195 
196 void
display_file(const char * file,const char * type)197 display_file (const char *file, const char *type)
198 {
199   int status;
200 
201   if (identify)
202     {
203       printf ("%s: %s\n", file, type ? type : "unknown");
204       return;
205     }
206 
207   if (!type)
208     return;
209 
210   if (metamail)
211     {
212       char *argv[7];
213 
214       argv[0] = "metamail";
215       argv[1] = "-b";
216 
217       argv[2] = interactive ? "-p" : "-h";
218 
219       argv[3] = "-c";
220       argv[4] = (char*) type;
221       argv[5] = (char*) mimeview_file;
222       argv[6] = NULL;
223 
224       if (mu_debug_level_p (MU_DEBCAT_APP, MU_DEBUG_TRACE0))
225 	{
226 	  char *string;
227 	  mu_argcv_string (6, argv, &string);
228 	  mu_debug (MU_DEBCAT_APP, MU_DEBUG_TRACE0,
229 		    (_("executing %s...\n"), string));
230 	  free (string);
231 	}
232 
233       if (!dry_run)
234 	mu_spawnvp (metamail, argv, &status);
235     }
236   else
237     {
238       mu_header_t hdr;
239       char *text;
240 
241       mu_asprintf (&text, "Content-Type: %s\n", type);
242       status = mu_header_create (&hdr, text, strlen (text));
243       if (status)
244 	mu_error (_("cannot create header: %s"), mu_strerror (status));
245       else
246 	{
247 	  display_stream_mailcap (mimeview_file, mimeview_stream, hdr,
248 				  no_ask_types, interactive, dry_run,
249 				  MU_DEBCAT_APP);
250 	  mu_header_destroy (&hdr);
251 	}
252     }
253 }
254 
255 int
main(int argc,char ** argv)256 main (int argc, char **argv)
257 {
258   MU_APP_INIT_NLS ();
259 
260   interactive = isatty (fileno (stdin));
261 
262   mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
263   if (dry_run)
264     {
265       mu_debug_level_t lev;
266       mu_debug_get_category_level (MU_DEBCAT_APP, &lev);
267       lev |= MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE2);
268       mu_debug_set_category_level (MU_DEBCAT_APP, lev);
269     }
270 
271   if (argc == 0 && !lint)
272     {
273       mu_error (_("no files given"));
274       return 1;
275     }
276 
277   if (mimetypes_parse (mimetypes_config))
278     return 1;
279   if (lint)
280     return 0;
281 
282   while (argc--)
283     {
284       const char *type;
285       char const *file = *argv++;
286       if (open_file (file))
287 	continue;
288       type = get_file_type ();
289       display_file (file, type);
290       close_file ();
291     }
292 
293   return 0;
294 }
295