tin  2.4.5
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.5.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 : 2018-11-25
7  * Notes : mailcap parsing as defined in RFC 1524
8  *
9  * Copyright (c) 2000-2021 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 *
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, 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  */
158 static 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  */
292 static 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  */
350 static 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, 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
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:1453
static char buf[16]
Definition: langinfo.c:50
static char * end
Definition: plp_snprintf.c:205
char * my_strdup(const char *str)
Definition: string.c:139
int atoi(const char *s)
char * str_trim(char *string)
Definition: string.c:538
int strfpath(const char *format, char *str, size_t maxsize, struct t_group *group, t_bool expand_all)
Definition: misc.c:1724
int strncasecmp(const char *p, const char *q, size_t n)
Definition: string.c:490
const char * get_param(t_param *list, const char *name)
Definition: rfc2046.c:568
char * escape_shell_meta(const char *source, int quote_area)
Definition: misc.c:1750
#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
static char * get_mailcap_field(char *mailcap)
Definition: rfc1524.c:293
void free_mailcap(t_mailcap *tmailcap)
Definition: rfc1524.c:475
t_mailcap * get_mailcap_entry(t_part *part, const char *path)
Definition: rfc1524.c:68
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:2061
char * command
Definition: tin.h:2058
int textualnewlines
Definition: tin.h:2067
char * nametemplate
Definition: tin.h:2063
char * compose
Definition: tin.h:2059
t_bool copiousoutput
Definition: tin.h:2069
char * edit
Definition: tin.h:2062
char * x11bitmap
Definition: tin.h:2066
char * type
Definition: tin.h:2057
t_bool needsterminal
Definition: tin.h:2068
char * test
Definition: tin.h:2065
char * composetyped
Definition: tin.h:2060
char * print
Definition: tin.h:2064
#define LEN
Definition: tin.h:857
#define STRCPY(dst, src)
Definition: tin.h:817
#define my_malloc(size)
Definition: tin.h:2207
#define FreeIfNeeded(p)
Definition: tin.h:2214
#define CURR_GROUP
Definition: tin.h:1046
@ sgl_quote
Definition: tin.h:1250
@ dbl_quote
Definition: tin.h:1249
@ no_quote
Definition: tin.h:1248
#define FreeAndNull(p)
Definition: tin.h:2215
#define my_realloc(ptr, size)
Definition: tin.h:2209
#define my_calloc(nmemb, size)
Definition: tin.h:2208