"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 }