"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.2/src/rfc1524.c" (9 Dec 2022, 14634 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "rfc1524.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.6.1_vs_2.6.2.
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-2023 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 */
55 static char *expand_mailcap_meta(const char *mailcap, t_part *part, t_bool escape_shell_meta_chars, const char *path);
56 static char *get_mailcap_field(char *mailcap);
57 static 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 */
67 t_mailcap *
68 get_mailcap_entry(
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 (*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 */
158 static t_mailcap*
159 parse_mailcap_line(
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 */
292 static char *
293 get_mailcap_field(
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 */
350 static char *
351 expand_mailcap_meta(
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 */
474 void
475 free_mailcap(
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 }