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