tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

rfc1524.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : rfc1524.c
4 * Author : Urs Janssen <urs@tin.org>, Jason Faultless <jason@altarstone.com>
5 * Created : 2000-05-15
6 * Updated : 2021-02-23
7 * Notes : mailcap parsing as defined in RFC 1524
8 *
9 * Copyright (c) 2000-2022 Urs Janssen <urs@tin.org>, Jason Faultless <jason@altarstone.com>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef TIN_H
41# include "tin.h"
42#endif /* !TIN_H */
43
44
45/*
46 * As defined in RFC 1524, Appendix A
47 * TODO: what about !unix systems?
48 */
49#define DEFAULT_MAILCAPS "~/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap:/etc/mail/mailcap"
50
51/* maximum number of mailcap fields */
52#define MAILCAPFIELDS 13
53
54/* local prototypes */
55static char *expand_mailcap_meta(const char *mailcap, t_part *part, t_bool escape_shell_meta_chars, const char *path);
56static char *get_mailcap_field(char *mailcap);
57static t_mailcap *parse_mailcap_line(const char *mailcap, t_part *part, const char *path);
58
59
60/*
61 * mainloop:
62 * scan mailcap file(s), look for a matching entry, extract fields,
63 * expand metas
64 *
65 * TODO: don't used fixed length buffers
66 */
69 t_part *part,
70 const char *path)
71{
72 FILE *fp;
73 char *ptr, *ptr2, *nptr;
74 char buf[LEN];
75 char filename[LEN]; /* name of current mailcap file */
76 char mailcap[LEN]; /* full match */
77 char *mailcaps; /* possible mailcap files */
78 char wildcap[LEN]; /* basetype match */
79 t_mailcap *foo = (t_mailcap *) 0;
80
81 /* build list of mailcap files */
82 if ((ptr = getenv("MAILCAPS")) != NULL && strlen(ptr)) {
83 mailcaps = my_malloc(strlen(ptr) + strlen(DEFAULT_MAILCAPS) + 2);
84 sprintf(mailcaps, "%s:%s", ptr, DEFAULT_MAILCAPS);
85 } else
86 mailcaps = my_strdup(DEFAULT_MAILCAPS);
87
88 mailcap[0] = '\0';
89 wildcap[0] = '\0';
90 buf[0] = '\0';
91
92 ptr = buf;
93
94 nptr = strtok(mailcaps, ":");
95 while (nptr != NULL) {
96 /* expand ~ and/or $HOME etc. */
97 if (strfpath(nptr, filename, sizeof(filename) - 1, &CURR_GROUP, FALSE)) {
98 if ((fp = fopen(filename, "r")) != NULL) {
99 while ((fgets(ptr, (int) (sizeof(buf) - strlen(buf)), fp)) != NULL) {
100 if (*ptr == '#' || *ptr == '\n') /* skip comments & blank lines */
101 continue;
102
103 ptr = buf + strlen(buf) - 1;
104
105 if (*ptr == '\n') /* remove linebreaks */
106 *ptr-- = '\0';
107
108 if (*ptr == '\\') /* continuation line */
109 continue; /* append */
110 else
111 ptr = buf;
112
113 if ((ptr2 = strchr(buf, '/')) != NULL) {
114 if (!strncasecmp(ptr, content_types[part->type], strlen(ptr) - strlen(ptr2))) {
115 if (!strncasecmp(ptr + strlen(content_types[part->type]) + 1, part->subtype, strlen(part->subtype))) {
116 /* full match, so parse line and evaluate test if given. */
117 STRCPY(mailcap, ptr);
118 FreeIfNeeded(foo);
119 foo = parse_mailcap_line(mailcap, part, path);
120 if (foo != NULL) {
121 fclose(fp); /* perfect match with test succeeded (if given) */
122 free(mailcaps);
123 return foo;
124 }
125 } else {
126 if ((*(ptr2 + 1) == '*') || (*(ptr2 + 1) == ';')) { /* wildmat match */
127 if (!strlen(wildcap)) { /* we don't already have a wildmat match */
128 STRCPY(wildcap, buf);
129 FreeIfNeeded(foo);
130 foo = parse_mailcap_line(wildcap, part, path);
131 if (foo == NULL) /* test failed */
132 wildcap[0] = '\0'; /* ignore match */
133 }
134 } /* else subtype mismatch, no action required */
135 }
136 } /* else no match, no action required */
137 } /* else invalid mailcap line (no /), no action required */
138 if (strlen(wildcap)) { /* we just had a wildmat match */
139 fclose(fp);
140 free(mailcaps);
141 return foo;
142 }
143 } /* while ((fgets(ptr, ... */
144 fclose(fp);
145 }
146 } /* else strfpath() failed, no action required */
147 nptr = strtok(NULL, ":"); /* get next filename */
148 }
149 free(mailcaps);
150 FreeAndNull(foo);
151 return foo;
152}
153
154
155/*
156 * extract fields, expand metas - called from get_mailcap_entry()
157 */
158static t_mailcap*
160 const char *mailcap,
161 t_part *part,
162 const char *path)
163{
164 char *ptr, *optr, *buf;
165 int i = MAILCAPFIELDS - 2; /* max MAILCAPFIELDS - required fields */
166 t_mailcap *tmailcap;
167
168 /* malloc and init */
169 tmailcap = my_malloc(sizeof(t_mailcap));
170 tmailcap->type = NULL;
171 tmailcap->command = NULL;
172 tmailcap->needsterminal = FALSE;
173 tmailcap->copiousoutput = FALSE;
174 tmailcap->textualnewlines = 0;
175 tmailcap->description = NULL;
176 tmailcap->test = NULL;
177 tmailcap->nametemplate = NULL;
178 tmailcap->compose = NULL;
179 tmailcap->composetyped = NULL;
180 tmailcap->edit = NULL;
181 tmailcap->print = NULL;
182 tmailcap->x11bitmap = NULL;
183
184 optr = ptr = my_strdup(mailcap);
185
186 /* get required entries */
187 ptr = get_mailcap_field(ptr);
188 buf = my_calloc(1, strlen(content_types[part->type]) + strlen(part->subtype) + 2);
189 sprintf(buf, "%s/%s", content_types[part->type], part->subtype);
190 tmailcap->type = buf;
191 ptr += strlen(ptr) + 1;
192 if ((ptr = get_mailcap_field(ptr)) != NULL) {
193 tmailcap->command = ptr;
194 ptr += strlen(ptr) + 1;
195 } else { /* required filed missing */
196 free(optr);
197 free_mailcap(tmailcap);
198 return ((t_mailcap *) 0);
199 }
200
201 while ((ptr = get_mailcap_field(ptr)) != NULL) {
202 if (i-- <= 0) /* number of possible fields exhausted */
203 break;
204 if (!strncasecmp(ptr, "needsterminal", 13)) {
205 tmailcap->needsterminal = TRUE;
206 ptr += strlen(ptr) + 1;
207 }
208 if (!strncasecmp(ptr, "copiousoutput", 13)) {
209 tmailcap->copiousoutput = TRUE;
210 ptr += strlen(ptr) + 1;
211 }
212 if (!strncasecmp(ptr, "description=", 12)) {
213 tmailcap->description = ptr + 12;
214 ptr += strlen(ptr) + 1;
215 }
216 if (!strncasecmp(ptr, "nametemplate=", 13)) {
217 tmailcap->nametemplate = expand_mailcap_meta(ptr + 13, part, FALSE, path);
218 ptr += strlen(ptr) + 1;
219 }
220 if (!strncasecmp(ptr, "test=", 5)) {
221 tmailcap->test = ptr + 5;
222 ptr += strlen(ptr) + 1;
223 }
224 if (!strncasecmp(ptr, "textualnewlines=", 16)) {
225 tmailcap->textualnewlines = atoi(ptr + 16);
226 ptr += strlen(ptr) + 1;
227 }
228 if (!strncasecmp(ptr, "compose=", 8)) {
229 tmailcap->compose = ptr + 8;
230 ptr += strlen(ptr) + 1;
231 }
232 if (!strncasecmp(ptr, "composetyped=", 13)) {
233 tmailcap->composetyped = ptr + 13;
234 ptr += strlen(ptr) + 1;
235 }
236 if (!strncasecmp(ptr, "edit=", 5)) {
237 tmailcap->edit = ptr + 5;
238 ptr += strlen(ptr) + 1;
239 }
240 if (!strncasecmp(ptr, "print=", 6)) {
241 tmailcap->print = ptr + 6;
242 ptr += strlen(ptr) + 1;
243 }
244 if (!strncasecmp(ptr, "x11-bitmap=", 11)) {
245 tmailcap->x11bitmap = ptr + 11;
246 ptr += strlen(ptr) + 1;
247 }
248 }
249
250 /*
251 * expand metas - we do it in a 2nd pass to be able to honor
252 * nametemplate
253 */
254 if (tmailcap->command != NULL) /* TODO: pass body part to command on stdin if no % found */
255 tmailcap->command = expand_mailcap_meta(tmailcap->command, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
256 if (tmailcap->description != NULL)
257 tmailcap->description = expand_mailcap_meta(tmailcap->description, part, FALSE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
258 if (tmailcap->test != NULL)
259 tmailcap->test = expand_mailcap_meta(tmailcap->test, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
260 if (tmailcap->compose != NULL)
261 tmailcap->compose = expand_mailcap_meta(tmailcap->compose, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
262 if (tmailcap->composetyped != NULL)
263 tmailcap->composetyped = expand_mailcap_meta(tmailcap->composetyped, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
264 if (tmailcap->edit != NULL)
265 tmailcap->edit = expand_mailcap_meta(tmailcap->edit, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
266 if (tmailcap->print != NULL)
267 tmailcap->print = expand_mailcap_meta(tmailcap->print, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
268 if (tmailcap->x11bitmap != NULL)
269 tmailcap->x11bitmap = expand_mailcap_meta(tmailcap->x11bitmap, part, TRUE, tmailcap->nametemplate ? tmailcap->nametemplate : path);
270
271 free(optr);
272
273 if (tmailcap->test != NULL) { /* test field given */
274 /*
275 * TODO: EndWin()/InitWin() around system needed?
276 * use invoke_cmd()?
277 */
278 if (system(tmailcap->test)) { /* test failed? */
279 free_mailcap(tmailcap);
280 return ((t_mailcap *) 0);
281 }
282 }
283 return tmailcap;
284}
285
286
287/*
288 * extract fields - called from parse_mailcap_line()
289 *
290 * TODO: add handling for singlequotes
291 */
292static char *
294 char *mailcap)
295{
296 char *ptr;
297 t_bool backquote = FALSE;
298 t_bool doublequote = FALSE;
299
300 ptr = str_trim(mailcap);
301
302 while (*ptr != '\0') {
303 switch (*ptr) {
304 case '\\':
305 backquote = bool_not(backquote);
306 break;
307
308 case '"':
309 if (!backquote)
310 doublequote = bool_not(doublequote);
311 backquote = FALSE;
312 break;
313
314 case ';':
315 if (!backquote && !doublequote) { /* field separator (plain ;) */
316 *ptr = '\0';
317 return mailcap;
318 }
319 if (backquote && !doublequote) /* remove \ in \; if not inside "" or '' */
320 *(ptr - 1) = ' ';
321 backquote = FALSE;
322 break;
323
324 default:
325 backquote = FALSE;
326 break;
327 }
328 ptr++;
329 }
330 return mailcap;
331}
332
333
334#define CHECK_SPACE(minlen) { \
335 while (space <= (minlen)) { /* need more space? */ \
336 olen = strlen(line); \
337 space += linelen; \
338 linelen <<= 1; \
339 line = my_realloc(line, linelen); \
340 memset(line + olen, 0, linelen - olen); \
341 } \
342}
343
344
345/*
346 * expand metas - called from parse_mailcap_line()
347 *
348 * TODO: expand %F, %n
349 */
350static char *
352 const char *mailcap,
353 t_part *part,
354 t_bool escape_shell_meta_chars,
355 const char *path)
356{
357 const char *ptr;
358 char *line, *lptr;
359 int quote = no_quote;
360 size_t linelen, space, olen;
361
362 if (!(strchr(mailcap, '%'))) /* nothing to expand */
363 return my_strdup(mailcap); /* waste of mem, but simplyfies the frees */
364
365 linelen = LEN * 2; /* initial maxlen */
366 space = linelen - 1; /* available space in string */
367 line = my_calloc(1, linelen);
368 lptr = line;
369 ptr = mailcap;
370
371 while (*ptr != '\0') {
372 /*
373 * to avoid reallocs() for the all the single char cases
374 * we do a check here
375 */
376 if (space < 10) { /* 'worst'case are two chars ... */
377 olen = strlen(line); /* get current length of string */
378 space += linelen; /* recalc available space */
379 linelen <<= 1; /* double maxlen */
380 line = my_realloc(line, linelen);
381 memset(line + olen, 0, linelen - olen); /* weed out junk */
382 lptr = line + olen; /* adjust pointer to current position */
383 }
384
385 if (*ptr == '\\') {
386 ptr++;
387 if ((*ptr == '\\') || (*ptr == '%')) {
388 *lptr++ = *ptr++;
389 space--;
390 }
391 continue;
392 }
393 if (*ptr == '%') {
394 ptr++;
395 if (*ptr == '{') { /* Content-Type parameter */
396 char *end;
397
398 if ((end = strchr(ptr, '}')) != NULL) {
399 if (part->params != NULL) {
400 char *parameter;
401 const char *value;
402
403 parameter = my_calloc(1, end - ptr + 1);
404 strncpy(parameter, ptr + 1, (size_t) (end - ptr - 1)); /* extract parameter name */
405 if ((value = get_param(part->params, parameter)) != NULL) { /* match? */
406 const char *nptr = escape_shell_meta_chars ? escape_shell_meta(value, quote) : value;
407
408 CHECK_SPACE(strlen(nptr));
409 strcat(line, nptr);
410 lptr = line + strlen(line);
411 space -= strlen(line);
412 }
413 free(parameter);
414 }
415 ptr = end; /* skip past closing } */
416 ptr++;
417 } else {
418 /* sequence broken, output literally */
419 *lptr++ = '%';
420 *lptr++ = *ptr++;
421 space -= 2;
422 }
423 continue;
424#if 0 /* TODO */
425 } else if (*ptr == 'F') { /* Content-Types and Filenames of sub parts */
426 } else if (*ptr == 'n') { /* Number of sub parts */
427 }
428#endif /* 0 */
429 } else if (*ptr == 's') { /* Filename */
430 const char *nptr = escape_shell_meta_chars ? escape_shell_meta(path, quote) : path;
431
432 CHECK_SPACE(strlen(nptr) + 2);
433 strcat(line, nptr);
434 lptr = line + strlen(line);
435 space -= strlen(line);
436 ptr++;
437 continue;
438 } else if (*ptr == 't') { /* Content-Type */
439 const char *nptr = escape_shell_meta_chars ? escape_shell_meta(part->subtype, quote) : part->subtype;
440
441 CHECK_SPACE((strlen(content_types[part->type]) + 1 + strlen(nptr)));
442 strcat(line, content_types[part->type]);
443 strcat(line, "/");
444 strcat(line, nptr);
445 lptr = line + strlen(line);
446 space -= strlen(line);
447 ptr++;
448 continue;
449 } else { /* unknown % sequence */
450 *lptr++ = '%';
451 space--;
452 continue;
453 }
454 }
455
456 if (escape_shell_meta_chars) {
457 if ((*ptr == '\'') && (quote != dbl_quote))
458 quote = (quote == no_quote ? sgl_quote : no_quote);
459 else if ((*ptr == '"') && (quote != sgl_quote))
460 quote = (quote == no_quote ? dbl_quote : no_quote);
461 }
462
463 /* any other char */
464 *lptr++ = *ptr++;
465 space--;
466 }
467 return line;
468}
469
470
471/*
472 * frees the malloced space
473 */
474void
476 t_mailcap *tmailcap)
477{
478 FreeIfNeeded(tmailcap->type);
479 FreeIfNeeded(tmailcap->command);
480 FreeIfNeeded(tmailcap->compose);
481 FreeIfNeeded(tmailcap->composetyped);
482 FreeIfNeeded(tmailcap->description);
483 FreeIfNeeded(tmailcap->edit);
484 FreeIfNeeded(tmailcap->nametemplate);
485 FreeIfNeeded(tmailcap->print);
486 FreeIfNeeded(tmailcap->test);
487 FreeIfNeeded(tmailcap->x11bitmap);
488 free(tmailcap);
489}
unsigned t_bool
Definition: bool.h:77
#define bool_not(b)
Definition: bool.h:81
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
constext * content_types[]
Definition: lang.c:1466
static char buf[16]
Definition: langinfo.c:50
static char * end
Definition: plp_snprintf.c:205
char * str_trim(char *string)
Definition: string.c:539
int atoi(const char *s)
const char * get_param(t_param *list, const char *name)
Definition: rfc2046.c:568
char * my_strdup(const char *str)
Definition: string.c:139
int strfpath(const char *format, char *str, size_t maxsize, struct t_group *group, t_bool expand_all)
Definition: misc.c:1699
char * escape_shell_meta(const char *source, int quote_area)
Definition: misc.c:1725
int strncasecmp(const char *p, const char *q, size_t n)
Definition: string.c:491
#define DEFAULT_MAILCAPS
Definition: rfc1524.c:49
static char * expand_mailcap_meta(const char *mailcap, t_part *part, t_bool escape_shell_meta_chars, const char *path)
Definition: rfc1524.c:351
#define CHECK_SPACE(minlen)
Definition: rfc1524.c:334
#define MAILCAPFIELDS
Definition: rfc1524.c:52
t_mailcap * get_mailcap_entry(t_part *part, const char *path)
Definition: rfc1524.c:68
static char * get_mailcap_field(char *mailcap)
Definition: rfc1524.c:293
void free_mailcap(t_mailcap *tmailcap)
Definition: rfc1524.c:475
static t_mailcap * parse_mailcap_line(const char *mailcap, t_part *part, const char *path)
Definition: rfc1524.c:159
Definition: rfc2046.h:93
unsigned type
Definition: rfc2046.h:94
char * subtype
Definition: rfc2046.h:100
t_param * params
Definition: rfc2046.h:102
char * description
Definition: tin.h:2100
char * command
Definition: tin.h:2097
int textualnewlines
Definition: tin.h:2106
char * nametemplate
Definition: tin.h:2102
char * compose
Definition: tin.h:2098
t_bool copiousoutput
Definition: tin.h:2108
char * edit
Definition: tin.h:2101
char * x11bitmap
Definition: tin.h:2105
char * type
Definition: tin.h:2096
t_bool needsterminal
Definition: tin.h:2107
char * test
Definition: tin.h:2104
char * composetyped
Definition: tin.h:2099
char * print
Definition: tin.h:2103
#define LEN
Definition: tin.h:860
#define STRCPY(dst, src)
Definition: tin.h:820
#define my_malloc(size)
Definition: tin.h:2245
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define CURR_GROUP
Definition: tin.h:1054
@ sgl_quote
Definition: tin.h:1259
@ dbl_quote
Definition: tin.h:1258
@ no_quote
Definition: tin.h:1257
#define FreeAndNull(p)
Definition: tin.h:2253
#define my_realloc(ptr, size)
Definition: tin.h:2247
#define my_calloc(nmemb, size)
Definition: tin.h:2246