"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/attachment.c" (16 Feb 2018, 16760 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

    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    file_user = savestr(file); /* TODO recreate after fexpand()!?! */
  218    incs = oucs = NULL;
  219 
  220    if((msgno = a_attachment_is_msg(file)) < 0){
  221       int e;
  222       char const *cp, *ncp;
  223 
  224 jrefexp:
  225       if((file = fexpand(file, FEXP_LOCAL | FEXP_NVAR)) == NULL){
  226          aerr = n_ATTACH_ERR_OTHER;
  227          goto jleave;
  228       }
  229 
  230 #ifndef HAVE_ICONV
  231       if(oucs != NULL && oucs != (char*)-1){
  232          n_err(_("No iconv support, cannot do %s\n"),
  233             n_shexp_quote_cp(file_user, FAL0));
  234          aerr = n_ATTACH_ERR_ICONV_NAVAIL;
  235          goto jleave;
  236       }
  237 #endif
  238 
  239       if((
  240 #ifdef HAVE_ICONV
  241             (oucs != NULL && oucs != (char*)-1)
  242                ? (cnvfp = Fopen(file, "r")) == NULL :
  243 #endif
  244                access(file, R_OK) != 0)){
  245          e = n_err_no;
  246 
  247          /* It may not have worked because of a character-set specification,
  248           * so try to extract that and retry once */
  249          if(incs == NULL && (cp = strrchr(file, '=')) != NULL){
  250             size_t i;
  251             char *nfp, c;
  252 
  253             nfp = savestrbuf(file, PTR2SIZE(cp - file));
  254             file = nfp;
  255 
  256             for(ncp = ++cp; (c = *cp) != '\0'; ++cp)
  257                if(!alnumchar(c) && !punctchar(c))
  258                   break;
  259                else if(c == '#'){
  260                   if(incs == NULL){
  261                      i = PTR2SIZE(cp - ncp);
  262                      incs = (i == 0 || (i == 1 && ncp[0] == '-'))
  263                            ? (char*)-1 : savestrbuf(ncp, i);
  264                      ncp = &cp[1];
  265                   }else
  266                      break;
  267                }
  268             if(c == '\0'){
  269                char *xp;
  270 
  271                i = PTR2SIZE(cp - ncp);
  272                if(i == 0 || (i == 1 && ncp[0] == '-'))
  273                   xp = (char*)-1;
  274                else
  275                   xp = savestrbuf(ncp, i);
  276                if(incs == NULL)
  277                   incs = xp;
  278                else
  279                   oucs = xp;
  280                file = nfp;
  281                goto jrefexp;
  282             }
  283          }
  284 
  285          n_err(_("Failed to access attachment %s: %s\n"),
  286             n_shexp_quote_cp(file, FAL0), n_err_to_doc(e));
  287          aerr = n_ATTACH_ERR_FILE_OPEN;
  288          goto jleave;
  289       }
  290    }
  291 
  292    nap = a_attachment_setup_base(csalloc(1, sizeof *nap), file);
  293    nap->a_path_user = file_user;
  294    if(msgno >= 0)
  295       nap = a_attachment_setup_msg(nap, file, msgno);
  296    else{
  297       nap->a_input_charset = (incs == NULL || incs == (char*)-1)
  298             ? savestr(ok_vlook(ttycharset)) : incs;
  299 #ifdef HAVE_ICONV
  300       if(cnvfp != NULL){
  301          nap->a_charset = oucs;
  302          if(!a_attachment_iconv(nap, cnvfp)){
  303             nap = NULL;
  304             aerr = n_ATTACH_ERR_ICONV_FAILED;
  305             goto jleave;
  306          }
  307          nap->a_conv = AC_TMPFILE;
  308       }else
  309 #endif
  310             if(incs != NULL && oucs == NULL)
  311          nap->a_conv = AC_FIX_INCS;
  312       else
  313          nap->a_conv = AC_DEFAULT;
  314    }
  315 
  316    if(aplist != NULL){
  317       for(ap = aplist; ap->a_flink != NULL; ap = ap->a_flink)
  318          ;
  319       ap->a_flink = nap;
  320       nap->a_blink = ap;
  321    }else
  322       aplist = nap;
  323 
  324 jleave:
  325    if(aerr_or_null != NULL)
  326       *aerr_or_null = aerr;
  327    if(newap_or_null != NULL)
  328       *newap_or_null = nap;
  329    NYD_LEAVE;
  330    return aplist;
  331 }
  332 
  333 FL struct attachment *
  334 n_attachment_append_list(struct attachment *aplist, char const *names){
  335    struct str shin;
  336    struct n_string shou, *shoup;
  337    NYD_ENTER;
  338 
  339    shoup = n_string_creat_auto(&shou);
  340 
  341    for(shin.s = n_UNCONST(names), shin.l = UIZ_MAX;;){
  342       struct attachment *nap;
  343       enum n_shexp_state shs;
  344 
  345       shs = n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
  346             n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG |
  347             n_SHEXP_PARSE_IFS_ADD_COMMA | n_SHEXP_PARSE_IGNORE_EMPTY),
  348             shoup, &shin, NULL);
  349       if(shs & n_SHEXP_STATE_ERR_MASK)
  350          break;
  351 
  352       if(shs & n_SHEXP_STATE_OUTPUT){
  353          aplist = n_attachment_append(aplist, n_string_cp(shoup), NULL, &nap);
  354          if(nap != NULL){
  355             if(n_psonce & n_PSO_INTERACTIVE)
  356                a_attachment_yay(nap);
  357          }
  358       }
  359 
  360       if(shs & n_SHEXP_STATE_STOP)
  361          break;
  362    }
  363    n_string_gut(shoup);
  364    NYD_LEAVE;
  365    return aplist;
  366 }
  367 
  368 FL struct attachment *
  369 n_attachment_remove(struct attachment *aplist, struct attachment *ap){
  370    struct attachment *bap, *fap;
  371    NYD_ENTER;
  372 
  373 #ifdef HAVE_DEVEL
  374    for(bap = aplist; aplist != NULL && aplist != ap; aplist = aplist->a_flink)
  375       ;
  376    assert(aplist != NULL);
  377    aplist = bap;
  378 #endif
  379 
  380    if(ap == aplist){
  381       if((aplist = ap->a_flink) != NULL)
  382          aplist->a_blink = NULL;
  383    }else{
  384       bap = ap->a_blink;
  385       fap = ap->a_flink;
  386       if(bap != NULL)
  387          bap->a_flink = fap;
  388       if(fap != NULL)
  389          fap->a_blink = bap;
  390    }
  391 
  392    if(ap->a_conv == AC_TMPFILE)
  393       Fclose(ap->a_tmpf);
  394    NYD_LEAVE;
  395    return aplist;
  396 }
  397 
  398 FL struct attachment *
  399 n_attachment_find(struct attachment *aplist, char const *name,
  400       bool_t *stat_or_null){
  401    int msgno;
  402    char const *bname;
  403    bool_t status, sym;
  404    struct attachment *saved;
  405    NYD_ENTER;
  406 
  407    saved = NULL;
  408    status = FAL0;
  409 
  410    if((bname = strrchr(name, '/')) != NULL){
  411       for(++bname; aplist != NULL; aplist = aplist->a_flink)
  412          if(!strcmp(name, aplist->a_path)){
  413             status = TRU1;
  414             /* Exact match with path components: done */
  415             goto jleave;
  416          }else if(!strcmp(bname, aplist->a_path_bname)){
  417             if(!status){
  418                saved = aplist;
  419                status = TRU1;
  420             }else
  421                status = TRUM1;
  422          }
  423    }else if((msgno = a_attachment_is_msg(name)) < 0){
  424       for(sym = FAL0; aplist != NULL; aplist = aplist->a_flink){
  425          if(!strcmp(name, aplist->a_name)){
  426             if(!status || !sym){
  427                saved = aplist;
  428                sym = TRU1;
  429             }
  430          }else if(!strcmp(name, aplist->a_path_bname)){
  431             if(!status)
  432                saved = aplist;
  433          }else
  434             continue;
  435          status = status ? TRUM1 : TRU1;
  436       }
  437    }else{
  438       for(; aplist != NULL; aplist = aplist->a_flink){
  439          if(aplist->a_msgno > 0 && aplist->a_msgno == msgno){
  440             status = TRU1;
  441             goto jleave;
  442          }
  443       }
  444    }
  445    if(saved != NULL)
  446       aplist = saved;
  447 
  448 jleave:
  449    if(stat_or_null != NULL)
  450       *stat_or_null = status;
  451    NYD_LEAVE;
  452    return aplist;
  453 }
  454 
  455 FL struct attachment *
  456 n_attachment_list_edit(struct attachment *aplist, enum n_go_input_flags gif){
  457    char prefix[32];
  458    struct str shin;
  459    struct n_string shou, *shoup;
  460    struct attachment *naplist, *ap;
  461    ui32_t attno;
  462    NYD_ENTER;
  463 
  464    if((n_psonce & (n_PSO_INTERACTIVE | n_PSO_ATTACH_QUOTE_NOTED)
  465          ) == n_PSO_INTERACTIVE){
  466       n_psonce |= n_PSO_ATTACH_QUOTE_NOTED;
  467       fprintf(n_stdout,
  468          _("# Only supports sh(1)ell-style quoting for file names\n"));
  469    }
  470 
  471    shoup = n_string_creat_auto(&shou);
  472 
  473    /* Modify already present ones?  Append some more? */
  474    attno = 1;
  475 
  476    for(naplist = NULL;;){
  477       snprintf(prefix, sizeof prefix, _("#%" PRIu32 " filename: "), attno);
  478 
  479       if(aplist != NULL){
  480          /* TODO If we would create .a_path_user in append() after any
  481           * TODO expansion then we could avoid closing+rebuilding the temporary
  482           * TODO file if the new user input matches the original value! */
  483          if(aplist->a_conv == AC_TMPFILE)
  484             Fclose(aplist->a_tmpf);
  485          shin.s = n_shexp_quote_cp(aplist->a_path_user, FAL0);
  486       }else
  487          shin.s = n_UNCONST(n_empty);
  488 
  489       ap = NULL;
  490       if((shin.s = n_go_input_cp(gif, prefix, shin.s)) != NULL){
  491          enum n_shexp_state shs;
  492          char const *s_save;
  493 
  494          s_save = shin.s;
  495          shin.l = UIZ_MAX;
  496          shs = n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
  497                n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_LOG |
  498                n_SHEXP_PARSE_IGNORE_EMPTY),
  499                shoup, &shin, NULL);
  500          if(!(shs & n_SHEXP_STATE_STOP))
  501             n_err(_("# May be given one argument a time only: %s\n"),
  502                n_shexp_quote_cp(s_save, FAL0));
  503          if((shs & (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_STOP |
  504                   n_SHEXP_STATE_ERR_MASK)
  505                ) != (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_STOP))
  506             break;
  507 
  508          naplist = n_attachment_append(naplist, n_string_cp(shoup), NULL, &ap);
  509          if(ap != NULL){
  510             if(n_psonce & n_PSO_INTERACTIVE)
  511                a_attachment_yay(ap);
  512             ++attno;
  513          }
  514       }
  515 
  516       if(aplist != NULL)
  517          aplist = aplist->a_flink;
  518       else if(ap == NULL)
  519          break;
  520    }
  521    NYD_LEAVE;
  522    return naplist;
  523 }
  524 
  525 FL ssize_t
  526 n_attachment_list_print(struct attachment const *aplist, FILE *fp){
  527    struct attachment const *ap;
  528    ui32_t attno;
  529    ssize_t rv;
  530    NYD_ENTER;
  531 
  532    rv = 0;
  533 
  534    for(attno = 1, ap = aplist; ap != NULL; ++rv, ++attno, ap = ap->a_flink){
  535       if(ap->a_msgno > 0)
  536          fprintf(fp, "#%" PRIu32 ". message/rfc822: %u\n", attno, ap->a_msgno);
  537       else{
  538          fprintf(fp, "#%" PRIu32 ": %s [%s -- %s",
  539             attno, n_shexp_quote_cp(ap->a_name, FAL0),
  540             n_shexp_quote_cp(ap->a_path, FAL0),
  541             (ap->a_content_type != NULL
  542              ? ap->a_content_type : _("unclassified content")));
  543 
  544          if(ap->a_conv == AC_TMPFILE)
  545             /* I18N: input and output character set as given */
  546             fprintf(fp, _(", incs=%s -> oucs=%s (readily converted)"),
  547                ap->a_input_charset, ap->a_charset);
  548          else if(ap->a_conv == AC_FIX_INCS)
  549             /* I18N: input character set as given, no conversion to apply */
  550             fprintf(fp, _(", incs=%s (no conversion)"), ap->a_input_charset);
  551          else if(ap->a_conv == AC_DEFAULT){
  552             if(ap->a_input_charset != NULL)
  553                /* I18N: input character set as given, output iterates */
  554                fprintf(fp, _(", incs=%s -> oucs=*sendcharsets*"),
  555                   ap->a_input_charset);
  556             else if(ap->a_content_type == NULL ||
  557                   !ascncasecmp(ap->a_content_type, "text/", 5))
  558                fprintf(fp, _(", default character set handling"));
  559          }
  560          fprintf(fp, "]\n");
  561       }
  562    }
  563    NYD_LEAVE;
  564    return rv;
  565 }
  566 
  567 /* s-it-mode */