"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.3/src/rfc1524.c" (25 Nov 2018, 14458 Bytes) of package /linux/misc/tin-2.4.3.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.4.2_vs_2.4.3.

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