"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/attachment.c" (8 Aug 2018, 17678 Bytes) of package /linux/misc/s-nail-14.9.11.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 "attachment.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Handling of attachments.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  * SPDX-License-Identifier: BSD-3-Clause
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE attachment
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 /* We use calloc() for struct attachment */
   44 n_CTAV(AC_DEFAULT == 0);
   45 
   46 /* Return >=0 if file denotes a valid message number */
   47 static int a_attachment_is_msg(char const *file);
   48 
   49 /* Fill in some basic attachment fields */
   50 static struct attachment *a_attachment_setup_base(struct attachment *ap,
   51                            char const *file);
   52 
   53 /* Setup ap to point to a message */
   54 static struct attachment *a_attachment_setup_msg(struct attachment *ap,
   55                            char const *msgcp, int msgno);
   56 
   57 /* Try to create temporary charset converted version */
   58 #ifdef HAVE_ICONV
   59 static bool_t a_attachment_iconv(struct attachment *ap, FILE *ifp);
   60 #endif
   61 
   62 /* */
   63 static void a_attachment_yay(struct attachment const *ap);
   64 
   65 static int
   66 a_attachment_is_msg(char const *file){
   67    int rv;
   68    NYD2_ENTER;
   69 
   70    rv = -1;
   71 
   72    if(file[0] == '#'){
   73       uiz_t ib;
   74 
   75       /* TODO Message numbers should be size_t, and 0 may be a valid one */
   76       if(file[2] == '\0' && file[1] == '.'){
   77          if(dot != NULL)
   78             rv = (int)PTR2SIZE(dot - message + 1);
   79       }else if((n_idec_uiz_cp(&ib, &file[1], 10, NULL
   80                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
   81             ) != n_IDEC_STATE_CONSUMED || ib == 0 || UICMP(z, ib, >, msgCount))
   82          rv = -1;
   83       else
   84          rv = (int)ib;
   85    }
   86    NYD2_LEAVE;
   87    return rv;
   88 }
   89 
   90 static struct attachment *
   91 a_attachment_setup_base(struct attachment *ap, char const *file){
   92    NYD2_ENTER;
   93    ap->a_input_charset = ap->a_charset = NULL;
   94    ap->a_path_user = ap->a_path = ap->a_path_bname = ap->a_name = file;
   95    if((file = strrchr(file, '/')) != NULL)
   96       ap->a_path_bname = ap->a_name = ++file;
   97    else
   98       file = ap->a_name;
   99    ap->a_content_type = n_mimetype_classify_filename(file);
  100    ap->a_content_disposition = "attachment";
  101    ap->a_content_description = NULL;
  102    ap->a_content_id = NULL;
  103    NYD2_LEAVE;
  104    return ap;
  105 }
  106 
  107 static struct attachment *
  108 a_attachment_setup_msg(struct attachment *ap, char const *msgcp, int msgno){
  109    NYD2_ENTER;
  110    ap->a_path_user = ap->a_path = ap->a_path_bname = ap->a_name = msgcp;
  111    ap->a_msgno = msgno;
  112    ap->a_content_type =
  113    ap->a_content_description =
  114    ap->a_content_disposition = NULL;
  115    ap->a_content_id = NULL;
  116    NYD2_LEAVE;
  117    return ap;
  118 }
  119 
  120 #ifdef HAVE_ICONV
  121 static bool_t
  122 a_attachment_iconv(struct attachment *ap, FILE *ifp){
  123    struct str oul = {NULL, 0}, inl = {NULL, 0};
  124    size_t cnt, lbsize;
  125    iconv_t icp;
  126    FILE *ofp;
  127    NYD_ENTER;
  128 
  129    hold_sigs(); /* TODO until we have signal manager (see TODO) */
  130 
  131    ofp = NULL;
  132 
  133    icp = n_iconv_open(ap->a_charset, ap->a_input_charset);
  134    if(icp == (iconv_t)-1){
  135       if(n_err_no == n_ERR_INVAL)
  136          goto jeconv;
  137       else
  138          n_perr(_("iconv_open"), 0);
  139       goto jerr;
  140    }
  141 
  142    cnt = (size_t)fsize(ifp);
  143 
  144    if((ofp = Ftmp(NULL, "atticonv", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL){
  145       n_perr(_("Temporary attachment data file"), 0);
  146       goto jerr;
  147    }
  148 
  149    for(lbsize = 0;;){
  150       if(fgetline(&inl.s, &lbsize, &cnt, &inl.l, ifp, 0) == NULL){
  151          if(!cnt)
  152             break;
  153          n_perr(_("I/O read error occurred"), 0);
  154          goto jerr;
  155       }
  156 
  157       if(n_iconv_str(icp, n_ICONV_IGN_NOREVERSE, &oul, &inl, NULL) != 0)
  158          goto jeconv;
  159       if((inl.l = fwrite(oul.s, sizeof *oul.s, oul.l, ofp)) != oul.l){
  160          n_perr(_("I/O write error occurred"), 0);
  161          goto jerr;
  162       }
  163    }
  164    fflush_rewind(ofp);
  165 
  166    ap->a_tmpf = ofp;
  167 jleave:
  168    if(inl.s != NULL)
  169       n_free(inl.s);
  170    if(oul.s != NULL)
  171       n_free(oul.s);
  172    if(icp != (iconv_t)-1)
  173       n_iconv_close(icp);
  174    Fclose(ifp);
  175 
  176    rele_sigs(); /* TODO until we have signal manager (see TODO) */
  177    NYD_LEAVE;
  178    return (ofp != NULL);
  179 
  180 jeconv:
  181    n_err(_("Cannot convert from %s to %s\n"),
  182       ap->a_input_charset, ap->a_charset);
  183 jerr:
  184    if(ofp != NULL)
  185       Fclose(ofp);
  186    ofp = NULL;
  187    goto jleave;
  188 }
  189 #endif /* HAVE_ICONV */
  190 
  191 static void
  192 a_attachment_yay(struct attachment const *ap){
  193    NYD2_ENTER;
  194    if(ap->a_msgno > 0)
  195       fprintf(n_stdout, _("Added message/rfc822 attachment for message #%u\n"),
  196          ap->a_msgno);
  197    else
  198       fprintf(n_stdout, _("Added attachment %s (%s)\n"),
  199          n_shexp_quote_cp(ap->a_name, FAL0),
  200          n_shexp_quote_cp(ap->a_path_user, FAL0));
  201    NYD2_LEAVE;
  202 }
  203 
  204 FL struct attachment *
  205 n_attachment_append(struct attachment *aplist, char const *file,
  206       enum n_attach_error *aerr_or_null, struct attachment **newap_or_null){
  207 #ifdef HAVE_ICONV
  208    FILE *cnvfp;
  209 #endif
  210    int msgno;
  211    char const *file_user, *incs, *oucs;
  212    struct attachment *nap, *ap;
  213    enum n_attach_error aerr;
  214    NYD_ENTER;
  215 
  216 #ifdef HAVE_ICONV
  217    cnvfp = NULL;
  218 #endif
  219    aerr = n_ATTACH_ERR_NONE;
  220    nap = NULL;
  221    incs = oucs = NULL;
  222 
  223    if(*file == '\0'){
  224       aerr = n_ATTACH_ERR_OTHER;
  225       goto jleave;
  226    }
  227    file_user = savestr(file); /* TODO recreate after fexpand()!?! */
  228 
  229    if((msgno = a_attachment_is_msg(file)) < 0){
  230       int e;
  231       char const *cp, *ncp;
  232 
  233 jrefexp:
  234       if((file = fexpand(file, FEXP_LOCAL | FEXP_NVAR)) == NULL){
  235          aerr = n_ATTACH_ERR_OTHER;
  236          goto jleave;
  237       }
  238 
  239 #ifndef HAVE_ICONV
  240       if(oucs != NULL && oucs != (char*)-1){
  241          n_err(_("No iconv support, cannot do %s\n"),
  242             n_shexp_quote_cp(file_user, FAL0));
  243          aerr = n_ATTACH_ERR_ICONV_NAVAIL;
  244          goto jleave;
  245       }
  246 #endif
  247 
  248       if((
  249 #ifdef HAVE_ICONV
  250             (oucs != NULL && oucs != (char*)-1)
  251                ? (cnvfp = Fopen(file, "r")) == NULL :
  252 #endif
  253                access(file, R_OK) != 0)){
  254          e = n_err_no;
  255 
  256          /* It may not have worked because of a character-set specification,
  257           * so try to extract that and retry once */
  258          if(incs == NULL && (cp = strrchr(file, '=')) != NULL){
  259             size_t i;
  260             char *nfp, c;
  261 
  262             nfp = savestrbuf(file, PTR2SIZE(cp - file));
  263 
  264             for(ncp = ++cp; (c = *cp) != '\0'; ++cp)
  265                if(!alnumchar(c) && !punctchar(c))
  266                   break;
  267                else if(c == '#'){
  268                   if(incs == NULL){
  269                      i = PTR2SIZE(cp - ncp);
  270                      if(i == 0 || (i == 1 && ncp[0] == '-'))
  271                         incs = (char*)-1;
  272                      else if((incs = n_iconv_normalize_name(savestrbuf(ncp, i))
  273                            ) == NULL){
  274                         e = n_ERR_INVAL;
  275                         goto jerr_fopen;
  276                      }
  277                      ncp = &cp[1];
  278                   }else
  279                      break;
  280                }
  281             if(c == '\0'){
  282                char *xp;
  283 
  284                i = PTR2SIZE(cp - ncp);
  285                if(i == 0 || (i == 1 && ncp[0] == '-'))
  286                   xp = (char*)-1;
  287                else if((xp = n_iconv_normalize_name(savestrbuf(ncp, i))
  288                      ) == NULL){
  289                   e = n_ERR_INVAL;
  290                   goto jerr_fopen;
  291                }
  292                if(incs == NULL)
  293                   incs = xp;
  294                else
  295                   oucs = xp;
  296                file = nfp;
  297                goto jrefexp;
  298             }
  299          }
  300 
  301 jerr_fopen:
  302          n_err(_("Failed to access attachment %s: %s\n"),
  303             n_shexp_quote_cp(file, FAL0), n_err_to_doc(e));
  304          aerr = n_ATTACH_ERR_FILE_OPEN;
  305          goto jleave;
  306       }
  307    }
  308 
  309    nap = a_attachment_setup_base(n_autorec_calloc(1, sizeof *nap), file);
  310    nap->a_path_user = file_user;
  311    if(msgno >= 0)
  312       nap = a_attachment_setup_msg(nap, file, msgno);
  313    else{
  314       nap->a_input_charset = (incs == NULL || incs == (char*)-1)
  315             ? savestr(ok_vlook(ttycharset)) : incs;
  316 #ifdef HAVE_ICONV
  317       if(cnvfp != NULL){
  318          nap->a_charset = oucs;
  319          if(!a_attachment_iconv(nap, cnvfp)){
  320             nap = NULL;
  321             aerr = n_ATTACH_ERR_ICONV_FAILED;
  322             goto jleave;
  323          }
  324          nap->a_conv = AC_TMPFILE;
  325       }else
  326 #endif
  327             if(incs != NULL && oucs == NULL)
  328          nap->a_conv = AC_FIX_INCS;
  329       else
  330          nap->a_conv = AC_DEFAULT;
  331    }
  332 
  333    if(aplist != NULL){
  334       for(ap = aplist; ap->a_flink != NULL; ap = ap->a_flink)
  335          ;
  336       ap->a_flink = nap;
  337       nap->a_blink = ap;
  338    }else
  339       aplist = nap;
  340 
  341 jleave:
  342    if(aerr_or_null != NULL)
  343       *aerr_or_null = aerr;
  344    if(newap_or_null != NULL)
  345       *newap_or_null = nap;
  346    NYD_LEAVE;
  347    return aplist;
  348 }
  349 
  350 FL struct attachment *
  351 n_attachment_append_list(struct attachment *aplist, char const *names){
  352    struct str shin;
  353    struct n_string shou, *shoup;
  354    NYD_ENTER;
  355 
  356    shoup = n_string_creat_auto(&shou);
  357 
  358    for(shin.s = n_UNCONST(names), shin.l = UIZ_MAX;;){
  359       struct attachment *nap;
  360       enum n_shexp_state shs;
  361 
  362       shs = n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
  363             n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG |
  364             n_SHEXP_PARSE_IFS_ADD_COMMA | n_SHEXP_PARSE_IGNORE_EMPTY),
  365             shoup, &shin, NULL);
  366       if(shs & n_SHEXP_STATE_ERR_MASK)
  367          break;
  368 
  369       if(shs & n_SHEXP_STATE_OUTPUT){
  370          aplist = n_attachment_append(aplist, n_string_cp(shoup), NULL, &nap);
  371          if(nap != NULL){
  372             if(n_psonce & n_PSO_INTERACTIVE)
  373                a_attachment_yay(nap);
  374          }
  375       }
  376 
  377       if(shs & n_SHEXP_STATE_STOP)
  378          break;
  379    }
  380    n_string_gut(shoup);
  381    NYD_LEAVE;
  382    return aplist;
  383 }
  384 
  385 FL struct attachment *
  386 n_attachment_remove(struct attachment *aplist, struct attachment *ap){
  387    struct attachment *bap, *fap;
  388    NYD_ENTER;
  389 
  390 #ifdef HAVE_DEVEL
  391    for(bap = aplist; aplist != NULL && aplist != ap; aplist = aplist->a_flink)
  392       ;
  393    assert(aplist != NULL);
  394    aplist = bap;
  395 #endif
  396 
  397    if(ap == aplist){
  398       if((aplist = ap->a_flink) != NULL)
  399          aplist->a_blink = NULL;
  400    }else{
  401       bap = ap->a_blink;
  402       fap = ap->a_flink;
  403       if(bap != NULL)
  404          bap->a_flink = fap;
  405       if(fap != NULL)
  406          fap->a_blink = bap;
  407    }
  408 
  409    if(ap->a_conv == AC_TMPFILE)
  410       Fclose(ap->a_tmpf);
  411    NYD_LEAVE;
  412    return aplist;
  413 }
  414 
  415 FL struct attachment *
  416 n_attachment_find(struct attachment *aplist, char const *name,
  417       bool_t *stat_or_null){
  418    int msgno;
  419    char const *bname;
  420    bool_t status, sym;
  421    struct attachment *saved;
  422    NYD_ENTER;
  423 
  424    saved = NULL;
  425    status = FAL0;
  426 
  427    if((bname = strrchr(name, '/')) != NULL){
  428       for(++bname; aplist != NULL; aplist = aplist->a_flink)
  429          if(!strcmp(name, aplist->a_path)){
  430             status = TRU1;
  431             /* Exact match with path components: done */
  432             goto jleave;
  433          }else if(!strcmp(bname, aplist->a_path_bname)){
  434             if(!status){
  435                saved = aplist;
  436                status = TRU1;
  437             }else
  438                status = TRUM1;
  439          }
  440    }else if((msgno = a_attachment_is_msg(name)) < 0){
  441       for(sym = FAL0; aplist != NULL; aplist = aplist->a_flink){
  442          if(!strcmp(name, aplist->a_name)){
  443             if(!status || !sym){
  444                saved = aplist;
  445                sym = TRU1;
  446             }
  447          }else if(!strcmp(name, aplist->a_path_bname)){
  448             if(!status)
  449                saved = aplist;
  450          }else
  451             continue;
  452          status = status ? TRUM1 : TRU1;
  453       }
  454    }else{
  455       for(; aplist != NULL; aplist = aplist->a_flink){
  456          if(aplist->a_msgno > 0 && aplist->a_msgno == msgno){
  457             status = TRU1;
  458             goto jleave;
  459          }
  460       }
  461    }
  462    if(saved != NULL)
  463       aplist = saved;
  464 
  465 jleave:
  466    if(stat_or_null != NULL)
  467       *stat_or_null = status;
  468    NYD_LEAVE;
  469    return aplist;
  470 }
  471 
  472 FL struct attachment *
  473 n_attachment_list_edit(struct attachment *aplist, enum n_go_input_flags gif){
  474    char prefix[32];
  475    struct str shin;
  476    struct n_string shou, *shoup;
  477    struct attachment *naplist, *ap;
  478    ui32_t attno;
  479    NYD_ENTER;
  480 
  481    if((n_psonce & (n_PSO_INTERACTIVE | n_PSO_ATTACH_QUOTE_NOTED)
  482          ) == n_PSO_INTERACTIVE){
  483       n_psonce |= n_PSO_ATTACH_QUOTE_NOTED;
  484       fprintf(n_stdout,
  485          _("# Only supports sh(1)ell-style quoting for file names\n"));
  486    }
  487 
  488    shoup = n_string_creat_auto(&shou);
  489 
  490    /* Modify already present ones?  Append some more? */
  491    attno = 1;
  492 
  493    for(naplist = NULL;;){
  494       snprintf(prefix, sizeof prefix, A_("#%" PRIu32 " filename: "), attno);
  495 
  496       if(aplist != NULL){
  497          /* TODO If we would create .a_path_user in append() after any
  498           * TODO expansion then we could avoid closing+rebuilding the temporary
  499           * TODO file if the new user input matches the original value! */
  500          if(aplist->a_conv == AC_TMPFILE)
  501             Fclose(aplist->a_tmpf);
  502          shin.s = n_shexp_quote_cp(aplist->a_path_user, FAL0);
  503       }else
  504          shin.s = n_UNCONST(n_empty);
  505 
  506       ap = NULL;
  507       if((shin.s = n_go_input_cp(gif, prefix, shin.s)) != NULL){
  508          enum n_shexp_state shs;
  509          char const *s_save;
  510 
  511          s_save = shin.s;
  512          shin.l = UIZ_MAX;
  513          shs = n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
  514                n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG |
  515                n_SHEXP_PARSE_IGNORE_EMPTY),
  516                shoup, &shin, NULL);
  517          UIS(
  518          if(!(shs & n_SHEXP_STATE_STOP))
  519             n_err(_("# May be given one argument a time only: %s\n"),
  520                n_shexp_quote_cp(s_save, FAL0));
  521          )
  522          if((shs & (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_STOP |
  523                   n_SHEXP_STATE_ERR_MASK)
  524                ) != (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_STOP))
  525             break;
  526 
  527          naplist = n_attachment_append(naplist, n_string_cp(shoup), NULL, &ap);
  528          if(ap != NULL){
  529             if(n_psonce & n_PSO_INTERACTIVE)
  530                a_attachment_yay(ap);
  531             ++attno;
  532          }
  533       }
  534 
  535       if(aplist != NULL){
  536          aplist = aplist->a_flink;
  537          /* In non-interactive or batch mode an empty line ends processing */
  538          if((n_psonce & n_PSO_INTERACTIVE) && !(n_poption & n_PO_BATCH_FLAG))
  539             continue;
  540       }
  541       if(ap == NULL)
  542          break;
  543    }
  544    NYD_LEAVE;
  545    return naplist;
  546 }
  547 
  548 FL ssize_t
  549 n_attachment_list_print(struct attachment const *aplist, FILE *fp){
  550    struct attachment const *ap;
  551    ui32_t attno;
  552    ssize_t rv;
  553    NYD_ENTER;
  554 
  555    rv = 0;
  556 
  557    for(attno = 1, ap = aplist; ap != NULL; ++rv, ++attno, ap = ap->a_flink){
  558       if(ap->a_msgno > 0)
  559          fprintf(fp, "#%" PRIu32 ". message/rfc822: %u\n", attno, ap->a_msgno);
  560       else{
  561          char const *incs, *oucs;
  562 
  563          if(!(n_psonce & n_PSO_REPRODUCIBLE)){
  564             incs = ap->a_input_charset;
  565             oucs = ap->a_charset;
  566          }else
  567             incs = oucs = n_reproducible_name;
  568 
  569          fprintf(fp, "#%" PRIu32 ": %s [%s -- %s",
  570             attno, n_shexp_quote_cp(ap->a_name, FAL0),
  571             n_shexp_quote_cp(ap->a_path, FAL0),
  572             (ap->a_content_type != NULL
  573              ? ap->a_content_type : _("unclassified content")));
  574 
  575          if(ap->a_conv == AC_TMPFILE)
  576             /* I18N: input and output character set as given */
  577             fprintf(fp, _(", incs=%s -> oucs=%s (readily converted)"),
  578                incs, oucs);
  579          else if(ap->a_conv == AC_FIX_INCS)
  580             /* I18N: input character set as given, no conversion to apply */
  581             fprintf(fp, _(", incs=%s (no conversion)"), incs);
  582          else if(ap->a_conv == AC_DEFAULT){
  583             if(incs != NULL)
  584                /* I18N: input character set as given, output iterates */
  585                fprintf(fp, _(", incs=%s -> oucs=*sendcharsets*"), incs);
  586             else if(ap->a_content_type == NULL ||
  587                   !ascncasecmp(ap->a_content_type, "text/", 5))
  588                fprintf(fp, _(", default character set handling"));
  589          }
  590          fprintf(fp, "]\n");
  591       }
  592    }
  593    NYD_LEAVE;
  594    return rv;
  595 }
  596 
  597 /* s-it-mode */