"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/attachment.c" (25 Mar 2018, 17476 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

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