"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/urlcrecry.c" (8 Aug 2018, 49257 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 "urlcrecry.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  *@ URL parsing, credential handling and crypto hooks.
    3  *@ .netrc parser quite loosely based upon NetBSD usr.bin/ftp/
    4  *@   $NetBSD: ruserpass.c,v 1.33 2007/04/17 05:52:04 lukem Exp $
    5  *
    6  * Copyright (c) 2014 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    7  * SPDX-License-Identifier: ISC
    8  *
    9  * Permission to use, copy, modify, and/or distribute this software for any
   10  * purpose with or without fee is hereby granted, provided that the above
   11  * copyright notice and this permission notice appear in all copies.
   12  *
   13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   20  */
   21 #undef n_FILE
   22 #define n_FILE urlcrecry
   23 
   24 #ifndef HAVE_AMALGAMATION
   25 # include "nail.h"
   26 #endif
   27 
   28 #ifdef HAVE_NETRC
   29   /* NetBSD usr.bin/ftp/ruserpass.c uses 100 bytes for that, we need four
   30    * concurrently (dummy, host, user, pass), so make it a KB */
   31 # define NRC_TOKEN_MAXLEN   (1024 / 4)
   32 
   33 enum nrc_token {
   34    NRC_ERROR      = -1,
   35    NRC_NONE       = 0,
   36    NRC_DEFAULT,
   37    NRC_LOGIN,
   38    NRC_PASSWORD,
   39    NRC_ACCOUNT,
   40    NRC_MACDEF,
   41    NRC_MACHINE,
   42    NRC_INPUT
   43 };
   44 
   45 struct nrc_node {
   46    struct nrc_node   *nrc_next;
   47    struct nrc_node   *nrc_result;   /* In match phase, former possible one */
   48    ui32_t            nrc_mlen;      /* Length of machine name */
   49    ui32_t            nrc_ulen;      /* Length of user name */
   50    ui32_t            nrc_plen;      /* Length of password */
   51    char              nrc_dat[n_VFIELD_SIZE(sizeof(ui32_t))];
   52 };
   53 # define NRC_NODE_ERR   ((struct nrc_node*)-1)
   54 
   55 static struct nrc_node  *_nrc_list;
   56 #endif /* HAVE_NETRC */
   57 
   58 /* Find the last @ before a slash
   59  * TODO Casts off the const but this is ok here; obsolete function! */
   60 #ifdef HAVE_SOCKETS /* temporary (we'll have file://..) */
   61 static char *           _url_last_at_before_slash(char const *sp);
   62 #endif
   63 
   64 #ifdef HAVE_NETRC
   65 /* Initialize .netrc cache */
   66 static void             _nrc_init(void);
   67 static enum nrc_token   __nrc_token(FILE *fi, char buffer[NRC_TOKEN_MAXLEN],
   68                            bool_t *nl_last);
   69 
   70 /* We shall lookup a machine in .netrc says ok_blook(netrc_lookup).
   71  * only_pass is true then the lookup is for the password only, otherwise we
   72  * look for a user (and add password only if we have an exact machine match) */
   73 static bool_t           _nrc_lookup(struct url *urlp, bool_t only_pass);
   74 
   75 /* 0=no match; 1=exact match; -1=wildcard match */
   76 static int              __nrc_host_match(struct nrc_node const *nrc,
   77                            struct url const *urlp);
   78 static bool_t           __nrc_find_user(struct url *urlp,
   79                            struct nrc_node const *nrc);
   80 static bool_t           __nrc_find_pass(struct url *urlp, bool_t user_match,
   81                            struct nrc_node const *nrc);
   82 #endif /* HAVE_NETRC */
   83 
   84 /* The password can also be gained through external agents TODO v15-compat */
   85 #ifdef HAVE_AGENT
   86 static bool_t           _agent_shell_lookup(struct url *urlp, char const *comm);
   87 #endif
   88 
   89 #ifdef HAVE_SOCKETS
   90 static char *
   91 _url_last_at_before_slash(char const *sp)
   92 {
   93    char const *cp;
   94    char c;
   95    NYD2_ENTER;
   96 
   97    for (cp = sp; (c = *cp) != '\0'; ++cp)
   98       if (c == '/')
   99          break;
  100    while (cp > sp && *--cp != '@')
  101       ;
  102    if (*cp != '@')
  103       cp = NULL;
  104    NYD2_LEAVE;
  105    return n_UNCONST(cp);
  106 }
  107 #endif
  108 
  109 #ifdef HAVE_NETRC
  110 static void
  111 _nrc_init(void)
  112 {
  113    char buffer[NRC_TOKEN_MAXLEN], host[NRC_TOKEN_MAXLEN],
  114       user[NRC_TOKEN_MAXLEN], pass[NRC_TOKEN_MAXLEN], *netrc_load;
  115    struct stat sb;
  116    FILE * volatile fi;
  117    enum nrc_token t;
  118    bool_t volatile ispipe;
  119    bool_t seen_default, nl_last;
  120    struct nrc_node * volatile ntail, * volatile nhead, * volatile nrc;
  121    NYD_ENTER;
  122 
  123    n_UNINIT(ntail, NULL);
  124    nhead = NULL;
  125    nrc = NRC_NODE_ERR;
  126    ispipe = FAL0;
  127    fi = NULL;
  128 
  129    hold_all_sigs(); /* todo */
  130 
  131    if ((netrc_load = ok_vlook(netrc_pipe)) != NULL) {
  132       ispipe = TRU1;
  133       if ((fi = Popen(netrc_load, "r", ok_vlook(SHELL), NULL, n_CHILD_FD_NULL)
  134             ) == NULL) {
  135          n_perr(netrc_load, 0);
  136          goto j_leave;
  137       }
  138    } else {
  139       if ((netrc_load = fexpand(ok_vlook(NETRC), FEXP_LOCAL | FEXP_NOPROTO)
  140             ) == NULL)
  141          goto j_leave;
  142 
  143       if ((fi = Fopen(netrc_load, "r")) == NULL) {
  144          n_err(_("Cannot open %s\n"), n_shexp_quote_cp(netrc_load, FAL0));
  145          goto j_leave;
  146       }
  147 
  148       /* Be simple and apply rigid (permission) check(s) */
  149       if (fstat(fileno(fi), &sb) == -1 || !S_ISREG(sb.st_mode) ||
  150             (sb.st_mode & (S_IRWXG | S_IRWXO))) {
  151          n_err(_("Not a regular file, or accessible by non-user: %s\n"),
  152             n_shexp_quote_cp(netrc_load, FAL0));
  153          goto jleave;
  154       }
  155    }
  156 
  157    seen_default = FAL0;
  158    nl_last = TRU1;
  159 jnext:
  160    switch((t = __nrc_token(fi, buffer, &nl_last))) {
  161    case NRC_NONE:
  162       break;
  163    default: /* Doesn't happen (but on error?), keep CC happy */
  164    case NRC_DEFAULT:
  165 jdef:
  166       /* We ignore the default entry (require an exact host match), and we also
  167        * ignore anything after such an entry (faulty syntax) */
  168       seen_default = TRU1;
  169       /* FALLTHRU */
  170    case NRC_MACHINE:
  171 jm_h:
  172       /* Normalize HOST to lowercase */
  173       *host = '\0';
  174       if (!seen_default && (t = __nrc_token(fi, host, &nl_last)) != NRC_INPUT)
  175          goto jerr;
  176       else {
  177          char *cp;
  178          for (cp = host; *cp != '\0'; ++cp)
  179             *cp = lowerconv(*cp);
  180       }
  181 
  182       *user = *pass = '\0';
  183       while ((t = __nrc_token(fi, buffer, &nl_last)) != NRC_NONE &&
  184             t != NRC_MACHINE && t != NRC_DEFAULT) {
  185          switch(t) {
  186          case NRC_LOGIN:
  187             if ((t = __nrc_token(fi, user, &nl_last)) != NRC_INPUT)
  188                goto jerr;
  189             break;
  190          case NRC_PASSWORD:
  191             if ((t = __nrc_token(fi, pass, &nl_last)) != NRC_INPUT)
  192                goto jerr;
  193             break;
  194          case NRC_ACCOUNT:
  195             if ((t = __nrc_token(fi, buffer, &nl_last)) != NRC_INPUT)
  196                goto jerr;
  197             break;
  198          case NRC_MACDEF:
  199             if ((t = __nrc_token(fi, buffer, &nl_last)) != NRC_INPUT)
  200                goto jerr;
  201             else {
  202                int i = 0, c;
  203                while ((c = getc(fi)) != EOF)
  204                   if (c == '\n') { /* xxx */
  205                      /* Don't care about comments here, since we parse until
  206                       * we've seen two successive newline characters */
  207                      if (i)
  208                         break;
  209                      i = 1;
  210                   } else
  211                      i = 0;
  212             }
  213             break;
  214          default:
  215          case NRC_ERROR:
  216             goto jerr;
  217          }
  218       }
  219 
  220       if (!seen_default && (*user != '\0' || *pass != '\0')) {
  221          size_t hl = strlen(host), ul = strlen(user), pl = strlen(pass);
  222          struct nrc_node *nx = n_alloc(n_VSTRUCT_SIZEOF(struct nrc_node,
  223                nrc_dat) + hl +1 + ul +1 + pl +1);
  224 
  225          if (nhead != NULL)
  226             ntail->nrc_next = nx;
  227          else
  228             nhead = nx;
  229          ntail = nx;
  230          nx->nrc_next = NULL;
  231          nx->nrc_mlen = hl;
  232          nx->nrc_ulen = ul;
  233          nx->nrc_plen = pl;
  234          memcpy(nx->nrc_dat, host, ++hl);
  235          memcpy(nx->nrc_dat + hl, user, ++ul);
  236          memcpy(nx->nrc_dat + hl + ul, pass, ++pl);
  237       }
  238       if (t == NRC_MACHINE)
  239          goto jm_h;
  240       if (t == NRC_DEFAULT)
  241          goto jdef;
  242       if (t != NRC_NONE)
  243          goto jnext;
  244       break;
  245    case NRC_ERROR:
  246 jerr:
  247       if(n_poption & n_PO_D_V)
  248          n_err(_("Errors occurred while parsing %s\n"),
  249             n_shexp_quote_cp(netrc_load, FAL0));
  250       assert(nrc == NRC_NODE_ERR);
  251       goto jleave;
  252    }
  253 
  254    if (nhead != NULL)
  255       nrc = nhead;
  256 jleave:
  257    if (fi != NULL) {
  258       if (ispipe)
  259             Pclose(fi, TRU1);
  260       else
  261          Fclose(fi);
  262    }
  263    if (nrc == NRC_NODE_ERR)
  264       while (nhead != NULL) {
  265          ntail = nhead;
  266          nhead = nhead->nrc_next;
  267          n_free(ntail);
  268       }
  269 j_leave:
  270    _nrc_list = nrc;
  271    rele_all_sigs();
  272    NYD_LEAVE;
  273 }
  274 
  275 static enum nrc_token
  276 __nrc_token(FILE *fi, char buffer[NRC_TOKEN_MAXLEN], bool_t *nl_last)
  277 {
  278    int c;
  279    char *cp;
  280    enum nrc_token rv;
  281    NYD2_ENTER;
  282 
  283    rv = NRC_NONE;
  284    for (;;) {
  285       bool_t seen_nl;
  286 
  287       c = EOF;
  288       if (feof(fi) || ferror(fi))
  289          goto jleave;
  290 
  291       for (seen_nl = *nl_last; (c = getc(fi)) != EOF && whitechar(c);)
  292          seen_nl |= (c == '\n');
  293 
  294       if (c == EOF)
  295          goto jleave;
  296       /* fetchmail and derived parsers support comments */
  297       if ((*nl_last = seen_nl) && c == '#') {
  298          while ((c = getc(fi)) != EOF && c != '\n')
  299             ;
  300          continue;
  301       }
  302       break;
  303    }
  304 
  305    cp = buffer;
  306    /* Is it a quoted token?  At least IBM syntax also supports ' quotes */
  307    if (c == '"' || c == '\'') {
  308       int quotec = c;
  309 
  310       /* Not requiring the closing QM is (Net)BSD syntax */
  311       while ((c = getc(fi)) != EOF && c != quotec) {
  312          /* Reverse solidus escaping the next character is (Net)BSD syntax */
  313          if (c == '\\')
  314             if ((c = getc(fi)) == EOF)
  315                break;
  316          *cp++ = c;
  317          if (PTRCMP(cp, ==, buffer + NRC_TOKEN_MAXLEN)) {
  318             rv = NRC_ERROR;
  319             goto jleave;
  320          }
  321       }
  322    } else {
  323       *cp++ = c;
  324       while ((c = getc(fi)) != EOF && !whitechar(c)) {
  325          /* Rverse solidus  escaping the next character is (Net)BSD syntax */
  326          if (c == '\\' && (c = getc(fi)) == EOF)
  327                break;
  328          *cp++ = c;
  329          if (PTRCMP(cp, ==, buffer + NRC_TOKEN_MAXLEN)) {
  330             rv = NRC_ERROR;
  331             goto jleave;
  332          }
  333       }
  334       *nl_last = (c == '\n');
  335    }
  336    *cp = '\0';
  337 
  338    if (*buffer == '\0')
  339       do {/*rv = NRC_NONE*/} while (0);
  340    else if (!strcmp(buffer, "default"))
  341       rv = NRC_DEFAULT;
  342    else if (!strcmp(buffer, "login"))
  343       rv = NRC_LOGIN;
  344    else if (!strcmp(buffer, "password") || !strcmp(buffer, "passwd"))
  345       rv = NRC_PASSWORD;
  346    else if (!strcmp(buffer, "account"))
  347       rv = NRC_ACCOUNT;
  348    else if (!strcmp(buffer, "macdef"))
  349       rv = NRC_MACDEF;
  350    else if (!strcmp(buffer, "machine"))
  351       rv = NRC_MACHINE;
  352    else
  353       rv = NRC_INPUT;
  354 jleave:
  355    if (c == EOF && !feof(fi))
  356       rv = NRC_ERROR;
  357    NYD2_LEAVE;
  358    return rv;
  359 }
  360 
  361 static bool_t
  362 _nrc_lookup(struct url *urlp, bool_t only_pass)
  363 {
  364    struct nrc_node *nrc, *nrc_wild, *nrc_exact;
  365    bool_t rv = FAL0;
  366    NYD_ENTER;
  367 
  368    assert(!only_pass || urlp->url_user.s != NULL);
  369    assert(only_pass || urlp->url_user.s == NULL);
  370 
  371    if (_nrc_list == NULL)
  372       _nrc_init();
  373    if (_nrc_list == NRC_NODE_ERR)
  374       goto jleave;
  375 
  376    nrc_wild = nrc_exact = NULL;
  377    for (nrc = _nrc_list; nrc != NULL; nrc = nrc->nrc_next)
  378       switch (__nrc_host_match(nrc, urlp)) {
  379       case 1:
  380          nrc->nrc_result = nrc_exact;
  381          nrc_exact = nrc;
  382          continue;
  383       case -1:
  384          nrc->nrc_result = nrc_wild;
  385          nrc_wild = nrc;
  386          /* FALLTHRU */
  387       case 0:
  388          continue;
  389       }
  390 
  391    if (!only_pass && urlp->url_user.s == NULL) {
  392       /* Must be an unambiguous entry of its kind */
  393       if (nrc_exact != NULL && nrc_exact->nrc_result != NULL)
  394          goto jleave;
  395       if (__nrc_find_user(urlp, nrc_exact))
  396          goto j_user;
  397 
  398       if (nrc_wild != NULL && nrc_wild->nrc_result != NULL)
  399          goto jleave;
  400       if (!__nrc_find_user(urlp, nrc_wild))
  401          goto jleave;
  402 j_user:
  403       ;
  404    }
  405 
  406    if (__nrc_find_pass(urlp, TRU1, nrc_exact) ||
  407          __nrc_find_pass(urlp, TRU1, nrc_wild) ||
  408          /* Do not try to find a password without exact user match unless we've
  409           * been called during credential lookup, a.k.a. the second time */
  410          !only_pass ||
  411          __nrc_find_pass(urlp, FAL0, nrc_exact) ||
  412          __nrc_find_pass(urlp, FAL0, nrc_wild))
  413       rv = TRU1;
  414 jleave:
  415    NYD_LEAVE;
  416    return rv;
  417 }
  418 
  419 static int
  420 __nrc_host_match(struct nrc_node const *nrc, struct url const *urlp)
  421 {
  422    char const *d2, *d1;
  423    size_t l2, l1;
  424    int rv = 0;
  425    NYD2_ENTER;
  426 
  427    /* Find a matching machine -- entries are all lowercase normalized */
  428    if (nrc->nrc_mlen == urlp->url_host.l) {
  429       if (n_LIKELY(!memcmp(nrc->nrc_dat, urlp->url_host.s, urlp->url_host.l)))
  430          rv = 1;
  431       goto jleave;
  432    }
  433 
  434    /* Cannot be an exact match, but maybe the .netrc machine starts with
  435     * a "*." glob, which we recognize as an extension, meaning "skip
  436     * a single subdomain, then match the rest" */
  437    d1 = nrc->nrc_dat + 2;
  438    l1 = nrc->nrc_mlen;
  439    if (l1 <= 2 || d1[-1] != '.' || d1[-2] != '*')
  440       goto jleave;
  441    l1 -= 2;
  442 
  443    /* Brute skipping over one subdomain, no RFC 1035 or RFC 1122 checks;
  444     * in fact this even succeeds for ".host.com", but - why care, here? */
  445    d2 = urlp->url_host.s;
  446    l2 = urlp->url_host.l;
  447    while (l2 > 0) {
  448       --l2;
  449       if (*d2++ == '.')
  450          break;
  451    }
  452 
  453    if (l2 == l1 && !memcmp(d1, d2, l1))
  454       /* This matches, but we won't use it directly but watch out for an
  455        * exact match first! */
  456       rv = -1;
  457 jleave:
  458    NYD2_LEAVE;
  459    return rv;
  460 }
  461 
  462 static bool_t
  463 __nrc_find_user(struct url *urlp, struct nrc_node const *nrc)
  464 {
  465    NYD2_ENTER;
  466 
  467    for (; nrc != NULL; nrc = nrc->nrc_result)
  468       if (nrc->nrc_ulen > 0) {
  469          /* Fake it was part of URL otherwise XXX */
  470          urlp->url_flags |= n_URL_HAD_USER;
  471          /* That buffer will be duplicated by url_parse() in this case! */
  472          urlp->url_user.s = n_UNCONST(nrc->nrc_dat + nrc->nrc_mlen +1);
  473          urlp->url_user.l = nrc->nrc_ulen;
  474          break;
  475       }
  476 
  477    NYD2_LEAVE;
  478    return (nrc != NULL);
  479 }
  480 
  481 static bool_t
  482 __nrc_find_pass(struct url *urlp, bool_t user_match, struct nrc_node const *nrc)
  483 {
  484    NYD2_ENTER;
  485 
  486    for (; nrc != NULL; nrc = nrc->nrc_result) {
  487       bool_t um = (nrc->nrc_ulen == urlp->url_user.l &&
  488             !memcmp(nrc->nrc_dat + nrc->nrc_mlen +1, urlp->url_user.s,
  489                urlp->url_user.l));
  490 
  491       if (user_match) {
  492          if (!um)
  493             continue;
  494       } else if (!um && nrc->nrc_ulen > 0)
  495          continue;
  496       if (nrc->nrc_plen == 0)
  497          continue;
  498 
  499       /* We are responsible for duplicating this buffer! */
  500       urlp->url_pass.s = savestrbuf(nrc->nrc_dat + nrc->nrc_mlen +1 +
  501             nrc->nrc_ulen + 1, (urlp->url_pass.l = nrc->nrc_plen));
  502       break;
  503    }
  504 
  505    NYD2_LEAVE;
  506    return (nrc != NULL);
  507 }
  508 #endif /* HAVE_NETRC */
  509 
  510 #ifdef HAVE_AGENT
  511 static bool_t
  512 _agent_shell_lookup(struct url *urlp, char const *comm) /* TODO v15-compat */
  513 {
  514    char buf[128];
  515    char const *env_addon[8];
  516    struct str s;
  517    FILE *pbuf;
  518    union {int c; sighandler_type sht;} u;
  519    size_t cl, l;
  520    bool_t rv = FAL0;
  521    NYD2_ENTER;
  522 
  523    env_addon[0] = str_concat_csvl(&s, "NAIL_USER", "=", urlp->url_user.s,
  524          NULL)->s;
  525    env_addon[1] = str_concat_csvl(&s,
  526          "NAIL_USER_ENC", "=", urlp->url_user_enc.s, NULL)->s;
  527    env_addon[2] = str_concat_csvl(&s, "NAIL_HOST", "=", urlp->url_host.s,
  528          NULL)->s;
  529    env_addon[3] = str_concat_csvl(&s, "NAIL_HOST_PORT", "=", urlp->url_h_p.s,
  530          NULL)->s;
  531    env_addon[4] = NULL;
  532 
  533    if ((pbuf = Popen(comm, "r", ok_vlook(SHELL), env_addon, -1)) == NULL) {
  534       n_err(_("*agent-shell-lookup* startup failed (%s)\n"), comm);
  535       goto jleave;
  536    }
  537 
  538    for (s.s = NULL, s.l = cl = l = 0; (u.c = getc(pbuf)) != EOF; ++cl) {
  539       if (u.c == '\n') /* xxx */
  540          continue;
  541       buf[l++] = u.c;
  542       if (l == sizeof(buf) - 1) {
  543          n_str_add_buf(&s, buf, l);
  544          l = 0;
  545       }
  546    }
  547    if (l > 0)
  548       n_str_add_buf(&s, buf, l);
  549 
  550    if (!Pclose(pbuf, TRU1)) {
  551       n_err(_("*agent-shell-lookup* execution failure (%s)\n"), comm);
  552       goto jleave;
  553    }
  554 
  555    /* We are responsible for duplicating this buffer! */
  556    if (s.s != NULL)
  557       urlp->url_pass.s = savestrbuf(s.s, urlp->url_pass.l = s.l);
  558    else if (cl > 0)
  559       urlp->url_pass.s = n_UNCONST(n_empty), urlp->url_pass.l = 0;
  560    rv = TRU1;
  561 jleave:
  562    if (s.s != NULL)
  563       n_free(s.s);
  564    NYD2_LEAVE;
  565    return rv;
  566 }
  567 #endif /* HAVE_AGENT */
  568 
  569 FL char *
  570 (urlxenc)(char const *cp, bool_t ispath n_MEMORY_DEBUG_ARGS)
  571 {
  572    char *n, *np, c1;
  573    NYD2_ENTER;
  574 
  575    /* C99 */{
  576       size_t i;
  577 
  578       i = strlen(cp);
  579       if(i >= UIZ_MAX / 3){
  580          n = NULL;
  581          goto jleave;
  582       }
  583       i *= 3;
  584       ++i;
  585       np = n = (n_autorec_alloc_from_pool)(NULL, i n_MEMORY_DEBUG_ARGSCALL);
  586    }
  587 
  588    for (; (c1 = *cp) != '\0'; ++cp) {
  589       /* (RFC 1738) RFC 3986, 2.3 Unreserved Characters:
  590        *    ALPHA / DIGIT / "-" / "." / "_" / "~"
  591        * However add a special is[file]path mode for file-system friendliness */
  592       if (alnumchar(c1) || c1 == '_')
  593          *np++ = c1;
  594       else if (!ispath) {
  595          if (c1 != '-' && c1 != '.' && c1 != '~')
  596             goto jesc;
  597          *np++ = c1;
  598       } else if (PTRCMP(np, >, n) && (*cp == '-' || *cp == '.')) /* XXX imap */
  599          *np++ = c1;
  600       else {
  601 jesc:
  602          np[0] = '%';
  603          n_c_to_hex_base16(np + 1, c1);
  604          np += 3;
  605       }
  606    }
  607    *np = '\0';
  608 jleave:
  609    NYD2_LEAVE;
  610    return n;
  611 }
  612 
  613 FL char *
  614 (urlxdec)(char const *cp n_MEMORY_DEBUG_ARGS)
  615 {
  616    char *n, *np;
  617    si32_t c;
  618    NYD2_ENTER;
  619 
  620    np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
  621          n_MEMORY_DEBUG_ARGSCALL);
  622 
  623    while ((c = (uc_i)*cp++) != '\0') {
  624       if (c == '%' && cp[0] != '\0' && cp[1] != '\0') {
  625          si32_t o = c;
  626          if (n_LIKELY((c = n_c_from_hex_base16(cp)) >= '\0'))
  627             cp += 2;
  628          else
  629             c = o;
  630       }
  631       *np++ = (char)c;
  632    }
  633    *np = '\0';
  634    NYD2_LEAVE;
  635    return n;
  636 }
  637 
  638 FL int
  639 c_urlcodec(void *vp){
  640    bool_t ispath;
  641    size_t alen;
  642    char const **argv, *varname, *varres, *act, *cp;
  643    NYD_ENTER;
  644 
  645    argv = vp;
  646    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
  647 
  648    act = *argv;
  649    for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
  650       ;
  651    if((ispath = (*act == 'p'))){
  652       if(!ascncasecmp(++act, "ath", 3))
  653          act += 3;
  654    }
  655    if(act >= cp)
  656       goto jesynopsis;
  657    alen = PTR2SIZE(cp - act);
  658    if(*cp != '\0')
  659       ++cp;
  660 
  661    n_pstate_err_no = n_ERR_NONE;
  662 
  663    if(is_ascncaseprefix(act, "encode", alen))
  664       varres = urlxenc(cp, ispath);
  665    else if(is_ascncaseprefix(act, "decode", alen))
  666       varres = urlxdec(cp);
  667    else
  668       goto jesynopsis;
  669 
  670    if(varres == NULL){
  671       n_pstate_err_no = n_ERR_CANCELED;
  672       varres = cp;
  673       vp = NULL;
  674    }
  675 
  676    if(varname != NULL){
  677       if(!n_var_vset(varname, (uintptr_t)varres)){
  678          n_pstate_err_no = n_ERR_NOTSUP;
  679          cp = NULL;
  680       }
  681    }else{
  682       struct str in, out;
  683 
  684       in.l = strlen(in.s = n_UNCONST(varres));
  685       makeprint(&in, &out);
  686       if(fprintf(n_stdout, "%s\n", out.s) < 0){
  687          n_pstate_err_no = n_err_no;
  688          vp = NULL;
  689       }
  690       n_free(out.s);
  691    }
  692 
  693 jleave:
  694    NYD_LEAVE;
  695    return (vp != NULL ? 0 : 1);
  696 jesynopsis:
  697    n_err(_("Synopsis: urlcodec: "
  698       "<[path]e[ncode]|[path]d[ecode]> <rest-of-line>\n"));
  699    n_pstate_err_no = n_ERR_INVAL;
  700    vp = NULL;
  701    goto jleave;
  702 }
  703 
  704 FL int
  705 c_urlencode(void *v) /* XXX IDNA?? */
  706 {
  707    char **ap;
  708    NYD_ENTER;
  709 
  710    n_OBSOLETE("`urlencode': please use `urlcodec enc[ode]' instead");
  711 
  712    for (ap = v; *ap != NULL; ++ap) {
  713       char *in = *ap, *out = urlxenc(in, FAL0);
  714 
  715       if(out == NULL)
  716          out = n_UNCONST(V_(n_error));
  717       fprintf(n_stdout,
  718          " in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
  719          in, strlen(in), out, strlen(out));
  720    }
  721    NYD_LEAVE;
  722    return 0;
  723 }
  724 
  725 FL int
  726 c_urldecode(void *v) /* XXX IDNA?? */
  727 {
  728    char **ap;
  729    NYD_ENTER;
  730 
  731    n_OBSOLETE("`urldecode': please use `urlcodec dec[ode]' instead");
  732 
  733    for (ap = v; *ap != NULL; ++ap) {
  734       char *in = *ap, *out = urlxdec(in);
  735 
  736       if(out == NULL)
  737          out = n_UNCONST(V_(n_error));
  738       fprintf(n_stdout,
  739          " in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
  740          in, strlen(in), out, strlen(out));
  741    }
  742    NYD_LEAVE;
  743    return 0;
  744 }
  745 
  746 FL char *
  747 url_mailto_to_address(char const *mailtop){ /* TODO hack! RFC 6068; factory? */
  748    size_t i;
  749    char *rv;
  750    char const *mailtop_orig;
  751    NYD_ENTER;
  752 
  753    if(!is_prefix("mailto:", mailtop_orig = mailtop)){
  754       rv = NULL;
  755       goto jleave;
  756    }
  757    mailtop += sizeof("mailto:") -1;
  758 
  759    /* TODO This is all intermediate, and for now just enough to understand
  760     * TODO a little bit of a little more advanced List-Post: headers. */
  761    /* Strip any hfield additions, keep only to addr-spec's */
  762    if((rv = strchr(mailtop, '?')) != NULL)
  763       rv = savestrbuf(mailtop, i = PTR2SIZE(rv - mailtop));
  764    else
  765       rv = savestrbuf(mailtop, i = strlen(mailtop));
  766 
  767    i = strlen(rv);
  768 
  769    /* Simply perform percent-decoding if there is a percent % */
  770    if(memchr(rv, '%', i) != NULL){
  771       char *rv_base;
  772       bool_t err;
  773 
  774       for(err = FAL0, mailtop = rv_base = rv; i > 0;){
  775          char c;
  776 
  777          if((c = *mailtop++) == '%'){
  778             si32_t cc;
  779 
  780             if(i < 3 || (cc = n_c_from_hex_base16(mailtop)) < 0){
  781                if(!err && (err = TRU1, n_poption & n_PO_D_V))
  782                   n_err(_("Invalid RFC 6068 'mailto' URL: %s\n"),
  783                      n_shexp_quote_cp(mailtop_orig, FAL0));
  784                goto jhex_putc;
  785             }
  786             *rv++ = (char)cc;
  787             mailtop += 2;
  788             i -= 3;
  789          }else{
  790 jhex_putc:
  791             *rv++ = c;
  792             --i;
  793          }
  794       }
  795       *rv = '\0';
  796       rv = rv_base;
  797    }
  798 jleave:
  799    NYD_LEAVE;
  800    return rv;
  801 }
  802 
  803 FL char const *
  804 n_servbyname(char const *proto, ui16_t *irv_or_null){
  805    static struct{
  806       char const name[14];
  807       char const port[8];
  808       ui16_t portno;
  809    } const tbl[] = {
  810       { "smtp", "25", 25},
  811       { "smtps", "465", 465},
  812       { "submission", "587", 587},
  813       { "submissions", "465", 465},
  814       { "pop3", "110", 110},
  815       { "pop3s", "995", 995},
  816       { "imap", "143", 143},
  817       { "imaps", "993", 993},
  818       { "file", "", 0}
  819    };
  820    char const *rv;
  821    size_t l, i;
  822    NYD2_ENTER;
  823 
  824    for(rv = proto; *rv != '\0'; ++rv)
  825       if(*rv == ':')
  826          break;
  827    l = PTR2SIZE(rv - proto);
  828 
  829    for(rv = NULL, i = 0; i < n_NELEM(tbl); ++i)
  830       if(!ascncasecmp(tbl[i].name, proto, l)){
  831          rv = tbl[i].port;
  832          if(irv_or_null != NULL)
  833             *irv_or_null = tbl[i].portno;
  834          break;
  835       }
  836    NYD2_LEAVE;
  837    return rv;
  838 }
  839 
  840 #ifdef HAVE_SOCKETS /* Note: not indented for that -- later: file:// etc.! */
  841 FL bool_t
  842 url_parse(struct url *urlp, enum cproto cproto, char const *data)
  843 {
  844 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
  845 # define a_ALLPROTO
  846 #endif
  847 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP || \
  848       defined HAVE_TLS
  849 # define a_ANYPROTO
  850    char *cp, *x;
  851 #endif
  852    bool_t rv = FAL0;
  853    NYD_ENTER;
  854    n_UNUSED(data);
  855 
  856    memset(urlp, 0, sizeof *urlp);
  857    urlp->url_input = data;
  858    urlp->url_cproto = cproto;
  859 
  860    /* Network protocol */
  861 #define a_PROTOX(X,Y,Z) \
  862    urlp->url_portno = Y;\
  863    memcpy(urlp->url_proto, X "://\0", sizeof(X "://\0"));\
  864    urlp->url_proto[sizeof(X) -1] = '\0';\
  865    urlp->url_proto_len = sizeof(X) -1;\
  866    do{ Z; }while(0)
  867 #define a_PRIVPROTOX(X,Y,Z) \
  868    do{ a_PROTOX(X, Y, Z); }while(0)
  869 #define a__IF(X,Y,Z)  \
  870    if(!ascncasecmp(data, X "://", sizeof(X "://") -1)){\
  871       a_PROTOX(X, Y, Z);\
  872       data += sizeof(X "://") -1;\
  873       goto juser;\
  874    }
  875 #define a_IF(X,Y) a__IF(X, Y, (void)0)
  876 #ifdef HAVE_TLS
  877 # define a_IFS(X,Y) a__IF(X, Y, urlp->url_flags |= n_URL_TLS_REQUIRED)
  878 # define a_IFs(X,Y) a__IF(X, Y, urlp->url_flags |= n_URL_TLS_OPTIONAL)
  879 #else
  880 # define a_IFS(X,Y) goto jeproto;
  881 # define a_IFs(X,Y) a_IF(X, Y)
  882 #endif
  883 
  884    switch(cproto){
  885    case CPROTO_CERTINFO:
  886       /* The special `tls' certificate info protocol
  887        * We do allow all protos here, for later getaddrinfo() usage! */
  888 #ifdef HAVE_TLS
  889       if((cp = strstr(data, "://")) == NULL)
  890          a_PRIVPROTOX("https", 443, urlp->url_flags |= n_URL_TLS_REQUIRED);
  891       else{
  892          size_t i;
  893 
  894          if((i = PTR2SIZE(&cp[sizeof("://") -1] - data)) + 2 >=
  895                sizeof(urlp->url_proto))
  896             goto jeproto;
  897          memcpy(urlp->url_proto, data, i);
  898          data += i;
  899          i -= sizeof("://") -1;
  900          urlp->url_proto[i] = '\0';\
  901          urlp->url_proto_len = i;
  902          urlp->url_flags |= n_URL_TLS_REQUIRED;
  903       }
  904       break;
  905 #else
  906       goto jeproto;
  907 #endif
  908    case CPROTO_CCRED:
  909       /* The special S/MIME etc. credential lookup TODO TLS client cert! */
  910 #ifdef HAVE_TLS
  911       a_PRIVPROTOX("ccred", 0, (void)0);
  912       break;
  913 #else
  914       goto jeproto;
  915 #endif
  916    case CPROTO_SOCKS:
  917       a_IF("socks5", 1080);
  918       a_IF("socks", 1080);
  919       a_PROTOX("socks", 1080, (void)0);
  920       break;
  921    case CPROTO_SMTP:
  922 #ifdef HAVE_SMTP
  923       a_IFS("smtps", 465)
  924       a_IFs("smtp", 25)
  925       a_IFs("submission", 587)
  926       a_IFS("submissions", 465)
  927       a_PROTOX("smtp", 25, urlp->url_flags |= n_URL_TLS_OPTIONAL);
  928       break;
  929 #else
  930       goto jeproto;
  931 #endif
  932    case CPROTO_POP3:
  933 #ifdef HAVE_POP3
  934       a_IFS("pop3s", 995)
  935       a_IFs("pop3", 110)
  936       a_PROTOX("pop3", 110, urlp->url_flags |= n_URL_TLS_OPTIONAL);
  937       break;
  938 #else
  939       goto jeproto;
  940 #endif
  941 #ifdef HAVE_IMAP
  942    case CPROTO_IMAP:
  943       a_IFS("imaps", 993)
  944       a_IFs("imap", 143)
  945       a_PROTOX("imap", 143, urlp->url_flags |= n_URL_TLS_OPTIONAL);
  946       break;
  947 #else
  948       goto jeproto;
  949 #endif
  950    }
  951 
  952 #undef a_PRIVPROTOX
  953 #undef a_PROTOX
  954 #undef a__IF
  955 #undef a_IF
  956 #undef a_IFS
  957 #undef a_IFs
  958 
  959    if (strstr(data, "://") != NULL) {
  960 #if !defined a_ALLPROTO || defined HAVE_TLS
  961 jeproto:
  962 #endif
  963       n_err(_("URL proto:// prefix invalid: %s\n"), urlp->url_input);
  964       goto jleave;
  965    }
  966 #ifdef a_ANYPROTO
  967 
  968    /* User and password, I */
  969 juser:
  970    if ((cp = _url_last_at_before_slash(data)) != NULL) {
  971       size_t l;
  972       char const *urlpe, *d;
  973       char *ub;
  974 
  975       l = PTR2SIZE(cp - data);
  976       ub = n_lofi_alloc(l +1);
  977       d = data;
  978       urlp->url_flags |= n_URL_HAD_USER;
  979       data = &cp[1];
  980 
  981       /* And also have a password? */
  982       if((cp = memchr(d, ':', l)) != NULL){
  983          size_t i = PTR2SIZE(cp - d);
  984 
  985          l -= i + 1;
  986          memcpy(ub, cp + 1, l);
  987          ub[l] = '\0';
  988 
  989          if((urlp->url_pass.s = urlxdec(ub)) == NULL)
  990             goto jurlp_err;
  991          urlp->url_pass.l = strlen(urlp->url_pass.s);
  992          if((urlpe = urlxenc(urlp->url_pass.s, FAL0)) == NULL)
  993             goto jurlp_err;
  994          if(strcmp(ub, urlpe))
  995             goto jurlp_err;
  996          l = i;
  997       }
  998 
  999       memcpy(ub, d, l);
 1000       ub[l] = '\0';
 1001       if((urlp->url_user.s = urlxdec(ub)) == NULL)
 1002          goto jurlp_err;
 1003       urlp->url_user.l = strlen(urlp->url_user.s);
 1004       if((urlp->url_user_enc.s = urlxenc(urlp->url_user.s, FAL0)) == NULL)
 1005          goto jurlp_err;
 1006       urlp->url_user_enc.l = strlen(urlp->url_user_enc.s);
 1007 
 1008       if(urlp->url_user_enc.l != l || memcmp(urlp->url_user_enc.s, ub, l)){
 1009 jurlp_err:
 1010          n_err(_("String is not properly URL percent encoded: %s\n"), ub);
 1011          d = NULL;
 1012       }
 1013 
 1014       n_lofi_free(ub);
 1015       if(d == NULL)
 1016          goto jleave;
 1017    }
 1018 
 1019    /* Servername and port -- and possible path suffix */
 1020    if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, use IPAddress! */
 1021       urlp->url_port = x = savestr(x = &cp[1]);
 1022       if ((x = strchr(x, '/')) != NULL) {
 1023          *x = '\0';
 1024          while(*++x == '/')
 1025             ;
 1026       }
 1027 
 1028       if((n_idec_ui16_cp(&urlp->url_portno, urlp->url_port, 10, NULL
 1029                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1030             ) != n_IDEC_STATE_CONSUMED || urlp->url_portno == 0){
 1031          n_err(_("URL with invalid port number: %s\n"), urlp->url_input);
 1032          goto jleave;
 1033       }
 1034    } else {
 1035       if ((x = strchr(data, '/')) != NULL) {
 1036          data = savestrbuf(data, PTR2SIZE(x - data));
 1037          while(*++x == '/')
 1038             ;
 1039       }
 1040       cp = n_UNCONST(data + strlen(data));
 1041    }
 1042 
 1043    /* A (non-empty) path may only occur with IMAP */
 1044    if (x != NULL && *x != '\0') {
 1045       /* Take care not to count adjacent solidus for real, on either end */
 1046       char *x2;
 1047       size_t i;
 1048       bool_t trailsol;
 1049 
 1050       for(trailsol = FAL0, x2 = savestrbuf(x, i = strlen(x)); i > 0;
 1051             trailsol = TRU1, --i)
 1052          if(x2[i - 1] != '/')
 1053             break;
 1054       x2[i] = '\0';
 1055 
 1056       if (i > 0) {
 1057          if (cproto != CPROTO_IMAP) {
 1058             n_err(_("URL protocol doesn't support paths: \"%s\"\n"),
 1059                urlp->url_input);
 1060             goto jleave;
 1061          }
 1062 # ifdef HAVE_IMAP
 1063          if(trailsol){
 1064             urlp->url_path.s = n_autorec_alloc(i + sizeof("/INBOX"));
 1065             memcpy(urlp->url_path.s, x, i);
 1066             memcpy(&urlp->url_path.s[i], "/INBOX", sizeof("/INBOX"));
 1067             urlp->url_path.l = (i += sizeof("/INBOX") -1);
 1068          }else
 1069 # endif
 1070             urlp->url_path.l = i, urlp->url_path.s = x2;
 1071       }
 1072    }
 1073 # ifdef HAVE_IMAP
 1074    if(cproto == CPROTO_IMAP && urlp->url_path.s == NULL)
 1075       urlp->url_path.s = savestrbuf("INBOX",
 1076             urlp->url_path.l = sizeof("INBOX") -1);
 1077 # endif
 1078 
 1079    urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
 1080    {  size_t i;
 1081       for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
 1082          *cp = lowerconv(*cp);
 1083    }
 1084 # ifdef HAVE_IDNA
 1085    if(!ok_blook(idna_disable)){
 1086       struct n_string idna;
 1087 
 1088       if(!n_idna_to_ascii(n_string_creat_auto(&idna), urlp->url_host.s,
 1089                urlp->url_host.l)){
 1090          n_err(_("URL host fails IDNA conversion: %s\n"), urlp->url_input);
 1091          goto jleave;
 1092       }
 1093       urlp->url_host.s = n_string_cp(&idna);
 1094       urlp->url_host.l = idna.s_len;
 1095    }
 1096 # endif /* HAVE_IDNA */
 1097 
 1098    /* .url_h_p: HOST:PORT */
 1099    {  size_t upl, i;
 1100       struct str *s = &urlp->url_h_p;
 1101 
 1102       upl = (urlp->url_port == NULL) ? 0 : 1u + strlen(urlp->url_port);
 1103       s->s = n_autorec_alloc(urlp->url_host.l + upl +1);
 1104       memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
 1105       if(upl > 0){
 1106          s->s[i++] = ':';
 1107          memcpy(&s->s[i], urlp->url_port, --upl);
 1108          i += upl;
 1109       }
 1110       s->s[s->l = i] = '\0';
 1111    }
 1112 
 1113    /* User, II
 1114     * If there was no user in the URL, do we have *user-HOST* or *user*? */
 1115    if (!(urlp->url_flags & n_URL_HAD_USER)) {
 1116       if ((urlp->url_user.s = xok_vlook(user, urlp, OXM_PLAIN | OXM_H_P))
 1117             == NULL) {
 1118          /* No, check whether .netrc lookup is desired */
 1119 # ifdef HAVE_NETRC
 1120          if (!ok_blook(v15_compat) ||
 1121                !xok_blook(netrc_lookup, urlp, OXM_PLAIN | OXM_H_P) ||
 1122                !_nrc_lookup(urlp, FAL0))
 1123 # endif
 1124             urlp->url_user.s = n_UNCONST(ok_vlook(LOGNAME));
 1125       }
 1126 
 1127       urlp->url_user.l = strlen(urlp->url_user.s);
 1128       urlp->url_user.s = savestrbuf(urlp->url_user.s, urlp->url_user.l);
 1129       if((urlp->url_user_enc.s = urlxenc(urlp->url_user.s, FAL0)) == NULL){
 1130          n_err(_("Cannot URL encode %s\n"), urlp->url_user.s);
 1131          goto jleave;
 1132       }
 1133       urlp->url_user_enc.l = strlen(urlp->url_user_enc.s);
 1134    }
 1135 
 1136    /* And then there are a lot of prebuild string combinations TODO do lazy */
 1137 
 1138    /* .url_u_h: .url_user@.url_host
 1139     * For SMTP we apply ridiculously complicated *v15-compat* plus
 1140     * *smtp-hostname* / *hostname* dependent rules */
 1141    {  struct str h, *s;
 1142       size_t i;
 1143 
 1144       if (cproto == CPROTO_SMTP && ok_blook(v15_compat) &&
 1145             (cp = ok_vlook(smtp_hostname)) != NULL) {
 1146          if (*cp == '\0')
 1147             cp = n_nodename(TRU1);
 1148          h.s = savestrbuf(cp, h.l = strlen(cp));
 1149       } else
 1150          h = urlp->url_host;
 1151 
 1152       s = &urlp->url_u_h;
 1153       i = urlp->url_user.l;
 1154 
 1155       s->s = n_autorec_alloc(i + 1 + h.l +1);
 1156       if (i > 0) {
 1157          memcpy(s->s, urlp->url_user.s, i);
 1158          s->s[i++] = '@';
 1159       }
 1160       memcpy(s->s + i, h.s, h.l +1);
 1161       i += h.l;
 1162       s->l = i;
 1163    }
 1164 
 1165    /* .url_u_h_p: .url_user@.url_host[:.url_port] */
 1166    {  struct str *s = &urlp->url_u_h_p;
 1167       size_t i = urlp->url_user.l;
 1168 
 1169       s->s = n_autorec_alloc(i + 1 + urlp->url_h_p.l +1);
 1170       if (i > 0) {
 1171          memcpy(s->s, urlp->url_user.s, i);
 1172          s->s[i++] = '@';
 1173       }
 1174       memcpy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
 1175       i += urlp->url_h_p.l;
 1176       s->l = i;
 1177    }
 1178 
 1179    /* .url_eu_h_p: .url_user_enc@.url_host[:.url_port] */
 1180    {  struct str *s = &urlp->url_eu_h_p;
 1181       size_t i = urlp->url_user_enc.l;
 1182 
 1183       s->s = n_autorec_alloc(i + 1 + urlp->url_h_p.l +1);
 1184       if (i > 0) {
 1185          memcpy(s->s, urlp->url_user_enc.s, i);
 1186          s->s[i++] = '@';
 1187       }
 1188       memcpy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
 1189       i += urlp->url_h_p.l;
 1190       s->l = i;
 1191    }
 1192 
 1193    /* .url_p_u_h_p: .url_proto://.url_u_h_p */
 1194    {  size_t i;
 1195       char *ud;
 1196 
 1197       ud = n_autorec_alloc((i = urlp->url_proto_len + sizeof("://") -1 +
 1198             urlp->url_u_h_p.l) +1);
 1199       urlp->url_proto[urlp->url_proto_len] = ':';
 1200       memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_u_h_p.s,
 1201          urlp->url_u_h_p.l +1);
 1202       urlp->url_proto[urlp->url_proto_len] = '\0';
 1203 
 1204       urlp->url_p_u_h_p = ud;
 1205    }
 1206 
 1207    /* .url_p_eu_h_p, .url_p_eu_h_p_p: .url_proto://.url_eu_h_p[/.url_path] */
 1208    {  size_t i;
 1209       char *ud;
 1210 
 1211       ud = n_autorec_alloc((i = urlp->url_proto_len + sizeof("://") -1 +
 1212             urlp->url_eu_h_p.l) + 1 + urlp->url_path.l +1);
 1213       urlp->url_proto[urlp->url_proto_len] = ':';
 1214       memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_eu_h_p.s,
 1215          urlp->url_eu_h_p.l +1);
 1216       urlp->url_proto[urlp->url_proto_len] = '\0';
 1217 
 1218       if (urlp->url_path.l == 0)
 1219          urlp->url_p_eu_h_p = urlp->url_p_eu_h_p_p = ud;
 1220       else {
 1221          urlp->url_p_eu_h_p = savestrbuf(ud, i);
 1222          urlp->url_p_eu_h_p_p = ud;
 1223          ud += i;
 1224          *ud++ = '/';
 1225          memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
 1226       }
 1227    }
 1228 
 1229    rv = TRU1;
 1230 #endif /* a_ANYPROTO */
 1231 jleave:
 1232    NYD_LEAVE;
 1233    return rv;
 1234 #undef a_ANYPROTO
 1235 #undef a_ALLPROTO
 1236 }
 1237 
 1238 FL bool_t
 1239 ccred_lookup_old(struct ccred *ccp, enum cproto cproto, char const *addr)
 1240 {
 1241    char const *pname, *pxstr, *authdef, *s;
 1242    size_t pxlen, addrlen, i;
 1243    char *vbuf;
 1244    ui8_t authmask;
 1245    enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
 1246       ware = NONE;
 1247    bool_t addr_is_nuser = FAL0; /* XXX v15.0 legacy! v15_compat */
 1248    NYD_ENTER;
 1249 
 1250    n_OBSOLETE(_("Use of old-style credentials, which will vanish in v15!\n"
 1251       "  Please read the manual section "
 1252          "\"On URL syntax and credential lookup\""));
 1253 
 1254    memset(ccp, 0, sizeof *ccp);
 1255 
 1256    switch (cproto) {
 1257    default:
 1258    case CPROTO_SMTP:
 1259       pname = "SMTP";
 1260       pxstr = "smtp-auth";
 1261       pxlen = sizeof("smtp-auth") -1;
 1262       authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
 1263             AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
 1264       authdef = "none";
 1265       addr_is_nuser = TRU1;
 1266       break;
 1267    case CPROTO_POP3:
 1268       pname = "POP3";
 1269       pxstr = "pop3-auth";
 1270       pxlen = sizeof("pop3-auth") -1;
 1271       authmask = AUTHTYPE_PLAIN;
 1272       authdef = "plain";
 1273       break;
 1274 #ifdef HAVE_IMAP
 1275    case CPROTO_IMAP:
 1276       pname = "IMAP";
 1277       pxstr = "imap-auth";
 1278       pxlen = sizeof("imap-auth") -1;
 1279       authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
 1280       authdef = "login";
 1281       break;
 1282 #endif
 1283    }
 1284 
 1285    ccp->cc_cproto = cproto;
 1286    addrlen = strlen(addr);
 1287    vbuf = n_lofi_alloc(pxlen + addrlen + sizeof("-password-")-1 +1);
 1288    memcpy(vbuf, pxstr, pxlen);
 1289 
 1290    /* Authentication type */
 1291    vbuf[pxlen] = '-';
 1292    memcpy(vbuf + pxlen + 1, addr, addrlen +1);
 1293    if ((s = n_var_vlook(vbuf, FAL0)) == NULL) {
 1294       vbuf[pxlen] = '\0';
 1295       if ((s = n_var_vlook(vbuf, FAL0)) == NULL)
 1296          s = n_UNCONST(authdef);
 1297    }
 1298 
 1299    if (!asccasecmp(s, "none")) {
 1300       ccp->cc_auth = "NONE";
 1301       ccp->cc_authtype = AUTHTYPE_NONE;
 1302       /*ware = NONE;*/
 1303    } else if (!asccasecmp(s, "plain")) {
 1304       ccp->cc_auth = "PLAIN";
 1305       ccp->cc_authtype = AUTHTYPE_PLAIN;
 1306       ware = REQ_PASS | REQ_USER;
 1307    } else if (!asccasecmp(s, "login")) {
 1308       ccp->cc_auth = "LOGIN";
 1309       ccp->cc_authtype = AUTHTYPE_LOGIN;
 1310       ware = REQ_PASS | REQ_USER;
 1311    } else if (!asccasecmp(s, "cram-md5")) {
 1312       ccp->cc_auth = "CRAM-MD5";
 1313       ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
 1314       ware = REQ_PASS | REQ_USER;
 1315    } else if (!asccasecmp(s, "gssapi")) {
 1316       ccp->cc_auth = "GSS-API";
 1317       ccp->cc_authtype = AUTHTYPE_GSSAPI;
 1318       ware = REQ_USER;
 1319    } /* no else */
 1320 
 1321    /* Verify method */
 1322    if (!(ccp->cc_authtype & authmask)) {
 1323       n_err(_("Unsupported %s authentication method: %s\n"), pname, s);
 1324       ccp = NULL;
 1325       goto jleave;
 1326    }
 1327 # ifndef HAVE_MD5
 1328    if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
 1329       n_err(_("No CRAM-MD5 support compiled in\n"));
 1330       ccp = NULL;
 1331       goto jleave;
 1332    }
 1333 # endif
 1334 # ifndef HAVE_GSSAPI
 1335    if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
 1336       n_err(_("No GSS-API support compiled in\n"));
 1337       ccp = NULL;
 1338       goto jleave;
 1339    }
 1340 # endif
 1341 
 1342    /* User name */
 1343    if (!(ware & (WANT_USER | REQ_USER)))
 1344       goto jpass;
 1345 
 1346    if (!addr_is_nuser) {
 1347       if ((s = _url_last_at_before_slash(addr)) != NULL) {
 1348          char *cp;
 1349 
 1350          cp = savestrbuf(addr, PTR2SIZE(s - addr));
 1351 
 1352          if((ccp->cc_user.s = urlxdec(cp)) == NULL){
 1353             n_err(_("String is not properly URL percent encoded: %s\n"), cp);
 1354             ccp = NULL;
 1355             goto jleave;
 1356          }
 1357          ccp->cc_user.l = strlen(ccp->cc_user.s);
 1358       } else if (ware & REQ_USER)
 1359          goto jgetuser;
 1360       goto jpass;
 1361    }
 1362 
 1363    memcpy(vbuf + pxlen, "-user-", i = sizeof("-user-") -1);
 1364    i += pxlen;
 1365    memcpy(vbuf + i, addr, addrlen +1);
 1366    if ((s = n_var_vlook(vbuf, FAL0)) == NULL) {
 1367       vbuf[--i] = '\0';
 1368       if ((s = n_var_vlook(vbuf, FAL0)) == NULL && (ware & REQ_USER)) {
 1369          if ((s = getuser(NULL)) == NULL) {
 1370 jgetuser:   /* TODO v15.0: today we simply bail, but we should call getuser().
 1371              * TODO even better: introduce "PROTO-user" and "PROTO-pass" and
 1372              * TODO check that first, then! change control flow, grow vbuf */
 1373             n_err(_("A user is necessary for %s authentication\n"), pname);
 1374             ccp = NULL;
 1375             goto jleave;
 1376          }
 1377       }
 1378    }
 1379    ccp->cc_user.l = strlen(ccp->cc_user.s = savestr(s));
 1380 
 1381    /* Password */
 1382 jpass:
 1383    if (!(ware & (WANT_PASS | REQ_PASS)))
 1384       goto jleave;
 1385 
 1386    if (!addr_is_nuser) {
 1387       memcpy(vbuf, "password-", i = sizeof("password-") -1);
 1388    } else {
 1389       memcpy(vbuf + pxlen, "-password-", i = sizeof("-password-") -1);
 1390       i += pxlen;
 1391    }
 1392    memcpy(vbuf + i, addr, addrlen +1);
 1393    if ((s = n_var_vlook(vbuf, FAL0)) == NULL) {
 1394       vbuf[--i] = '\0';
 1395       if ((!addr_is_nuser || (s = n_var_vlook(vbuf, FAL0)) == NULL) &&
 1396             (ware & REQ_PASS)) {
 1397          if ((s = getpassword(savecat(_("Password for "), pname))) == NULL) {
 1398             n_err(_("A password is necessary for %s authentication\n"),
 1399                pname);
 1400             ccp = NULL;
 1401             goto jleave;
 1402          }
 1403       }
 1404    }
 1405    if (s != NULL)
 1406       ccp->cc_pass.l = strlen(ccp->cc_pass.s = savestr(s));
 1407 
 1408 jleave:
 1409    n_lofi_free(vbuf);
 1410    if (ccp != NULL && (n_poption & n_PO_D_VV))
 1411       n_err(_("Credentials: host %s, user %s, pass %s\n"),
 1412          addr, (ccp->cc_user.s != NULL ? ccp->cc_user.s : n_empty),
 1413          (ccp->cc_pass.s != NULL ? ccp->cc_pass.s : n_empty));
 1414    NYD_LEAVE;
 1415    return (ccp != NULL);
 1416 }
 1417 
 1418 FL bool_t
 1419 ccred_lookup(struct ccred *ccp, struct url *urlp)
 1420 {
 1421    char *s;
 1422    char const *pstr, *authdef;
 1423    ui8_t authmask;
 1424    enum okeys authokey;
 1425    enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
 1426       ware;
 1427    NYD_ENTER;
 1428 
 1429    memset(ccp, 0, sizeof *ccp);
 1430    ccp->cc_user = urlp->url_user;
 1431 
 1432    ware = NONE;
 1433 
 1434    switch ((ccp->cc_cproto = urlp->url_cproto)) {
 1435    case CPROTO_CCRED:
 1436       authokey = (enum okeys)-1;
 1437       authmask = AUTHTYPE_PLAIN;
 1438       authdef = "plain";
 1439       pstr = "ccred";
 1440       break;
 1441    default:
 1442    case CPROTO_SMTP:
 1443       authokey = ok_v_smtp_auth;
 1444       authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
 1445             AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
 1446       authdef = "plain";
 1447       pstr = "smtp";
 1448       break;
 1449    case CPROTO_POP3:
 1450       authokey = ok_v_pop3_auth;
 1451       authmask = AUTHTYPE_PLAIN;
 1452       authdef = "plain";
 1453       pstr = "pop3";
 1454       break;
 1455 #ifdef HAVE_IMAP
 1456    case CPROTO_IMAP:
 1457       pstr = "imap";
 1458       authokey = ok_v_imap_auth;
 1459       authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
 1460       authdef = "login";
 1461       break;
 1462 #endif
 1463    }
 1464 
 1465    /* Authentication type */
 1466    if (authokey == (enum okeys)-1 ||
 1467          (s = xok_VLOOK(authokey, urlp, OXM_ALL)) == NULL)
 1468       s = n_UNCONST(authdef);
 1469 
 1470    if (!asccasecmp(s, "none")) {
 1471       ccp->cc_auth = "NONE";
 1472       ccp->cc_authtype = AUTHTYPE_NONE;
 1473       /*ware = NONE;*/
 1474    } else if (!asccasecmp(s, "plain")) {
 1475       ccp->cc_auth = "PLAIN";
 1476       ccp->cc_authtype = AUTHTYPE_PLAIN;
 1477       ware = REQ_PASS | REQ_USER;
 1478    } else if (!asccasecmp(s, "login")) {
 1479       ccp->cc_auth = "LOGIN";
 1480       ccp->cc_authtype = AUTHTYPE_LOGIN;
 1481       ware = REQ_PASS | REQ_USER;
 1482    } else if (!asccasecmp(s, "cram-md5")) {
 1483       ccp->cc_auth = "CRAM-MD5";
 1484       ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
 1485       ware = REQ_PASS | REQ_USER;
 1486    } else if (!asccasecmp(s, "gssapi")) {
 1487       ccp->cc_auth = "GSS-API";
 1488       ccp->cc_authtype = AUTHTYPE_GSSAPI;
 1489       ware = REQ_USER;
 1490    } /* no else */
 1491 
 1492    /* Verify method */
 1493    if (!(ccp->cc_authtype & authmask)) {
 1494       n_err(_("Unsupported %s authentication method: %s\n"), pstr, s);
 1495       ccp = NULL;
 1496       goto jleave;
 1497    }
 1498 # ifndef HAVE_MD5
 1499    if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
 1500       n_err(_("No CRAM-MD5 support compiled in\n"));
 1501       ccp = NULL;
 1502       goto jleave;
 1503    }
 1504 # endif
 1505 # ifndef HAVE_GSSAPI
 1506    if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
 1507       n_err(_("No GSS-API support compiled in\n"));
 1508       ccp = NULL;
 1509       goto jleave;
 1510    }
 1511 # endif
 1512 
 1513    /* Password */
 1514    ccp->cc_pass = urlp->url_pass;
 1515    if (ccp->cc_pass.s != NULL)
 1516       goto jleave;
 1517 
 1518    if ((s = xok_vlook(password, urlp, OXM_ALL)) != NULL)
 1519       goto js2pass;
 1520 # ifdef HAVE_AGENT /* TODO v15-compat obsolete */
 1521    if ((s = xok_vlook(agent_shell_lookup, urlp, OXM_ALL)) != NULL) {
 1522       n_OBSOLETE(_("*agent-shell-lookup* will vanish.  Please use encrypted "
 1523          "~/.netrc (via *netrc-pipe*), or simply `source' an encrypted file"));
 1524       if (!_agent_shell_lookup(urlp, s)) {
 1525          ccp = NULL;
 1526          goto jleave;
 1527       } else if (urlp->url_pass.s != NULL) {
 1528          ccp->cc_pass = urlp->url_pass;
 1529          goto jleave;
 1530       }
 1531    }
 1532 # endif
 1533 # ifdef HAVE_NETRC
 1534    if (xok_blook(netrc_lookup, urlp, OXM_ALL) && _nrc_lookup(urlp, TRU1)) {
 1535       ccp->cc_pass = urlp->url_pass;
 1536       goto jleave;
 1537    }
 1538 # endif
 1539    if (ware & REQ_PASS) {
 1540       if((s = getpassword(savecat(urlp->url_u_h.s, _(" requires a password: ")))
 1541             ) != NULL)
 1542 js2pass:
 1543          ccp->cc_pass.l = strlen(ccp->cc_pass.s = savestr(s));
 1544       else {
 1545          n_err(_("A password is necessary for %s authentication\n"), pstr);
 1546          ccp = NULL;
 1547       }
 1548    }
 1549 
 1550 jleave:
 1551    if(ccp != NULL && (n_poption & n_PO_D_VV))
 1552       n_err(_("Credentials: host %s, user %s, pass %s\n"),
 1553          urlp->url_h_p.s, (ccp->cc_user.s != NULL ? ccp->cc_user.s : n_empty),
 1554          (ccp->cc_pass.s != NULL ? ccp->cc_pass.s : n_empty));
 1555    NYD_LEAVE;
 1556    return (ccp != NULL);
 1557 }
 1558 #endif /* HAVE_SOCKETS */
 1559 
 1560 #ifdef HAVE_NETRC
 1561 FL int
 1562 c_netrc(void *v)
 1563 {
 1564    char **argv = v;
 1565    struct nrc_node *nrc;
 1566    bool_t load_only;
 1567    NYD_ENTER;
 1568 
 1569    load_only = FAL0;
 1570    if (*argv == NULL)
 1571       goto jlist;
 1572    if (argv[1] != NULL)
 1573       goto jerr;
 1574    if (!asccasecmp(*argv, "show"))
 1575       goto jlist;
 1576    load_only = TRU1;
 1577    if (!asccasecmp(*argv, "load"))
 1578       goto jlist;
 1579    if (!asccasecmp(*argv, "clear"))
 1580       goto jclear;
 1581 jerr:
 1582    n_err(_("Synopsis: netrc: (<show> or) <clear> the .netrc cache\n"));
 1583    v = NULL;
 1584 jleave:
 1585    NYD_LEAVE;
 1586    return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
 1587 
 1588 jlist:   {
 1589    FILE *fp;
 1590    size_t l;
 1591 
 1592    if (_nrc_list == NULL)
 1593       _nrc_init();
 1594    if (_nrc_list == NRC_NODE_ERR) {
 1595       n_err(_("Interpolate what file?\n"));
 1596       v = NULL;
 1597       goto jleave;
 1598    }
 1599    if (load_only)
 1600       goto jleave;
 1601 
 1602    if ((fp = Ftmp(NULL, "netrc", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
 1603       n_perr(_("tmpfile"), 0);
 1604       v = NULL;
 1605       goto jleave;
 1606    }
 1607 
 1608    for (l = 0, nrc = _nrc_list; nrc != NULL; ++l, nrc = nrc->nrc_next) {
 1609       fprintf(fp, _("machine %s "), nrc->nrc_dat); /* XXX quote? */
 1610       if (nrc->nrc_ulen > 0)
 1611          fprintf(fp, _("login \"%s\" "),
 1612             string_quote(nrc->nrc_dat + nrc->nrc_mlen +1));
 1613       if (nrc->nrc_plen > 0)
 1614          fprintf(fp, _("password \"%s\"\n"),
 1615             string_quote(nrc->nrc_dat + nrc->nrc_mlen +1 + nrc->nrc_ulen +1));
 1616       else
 1617          putc('\n', fp);
 1618    }
 1619 
 1620    page_or_print(fp, l);
 1621    Fclose(fp);
 1622    }
 1623    goto jleave;
 1624 
 1625 jclear:
 1626    if (_nrc_list == NRC_NODE_ERR)
 1627       _nrc_list = NULL;
 1628    while ((nrc = _nrc_list) != NULL) {
 1629       _nrc_list = nrc->nrc_next;
 1630       n_free(nrc);
 1631    }
 1632    goto jleave;
 1633 }
 1634 #endif /* HAVE_NETRC */
 1635 
 1636 #ifdef HAVE_MD5
 1637 FL char *
 1638 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
 1639 {
 1640    char const *cp = vp;
 1641    size_t i, j;
 1642    NYD_ENTER;
 1643 
 1644    for (i = 0; i < MD5TOHEX_SIZE / 2; ++i) {
 1645       j = i << 1;
 1646 # define __hex(n) ((n) > 9 ? (n) - 10 + 'a' : (n) + '0')
 1647       hex[j] = __hex((cp[i] & 0xF0) >> 4);
 1648       hex[++j] = __hex(cp[i] & 0x0F);
 1649 # undef __hex
 1650    }
 1651    NYD_LEAVE;
 1652    return hex;
 1653 }
 1654 
 1655 FL char *
 1656 cram_md5_string(struct str const *user, struct str const *pass,
 1657    char const *b64)
 1658 {
 1659    struct str in, out;
 1660    char digest[16], *cp;
 1661    NYD_ENTER;
 1662 
 1663    out.s = NULL;
 1664    if(user->l >= UIZ_MAX - 1 - MD5TOHEX_SIZE - 1)
 1665       goto jleave;
 1666    if(pass->l >= INT_MAX)
 1667       goto jleave;
 1668 
 1669    in.s = n_UNCONST(b64);
 1670    in.l = strlen(in.s);
 1671    if(!b64_decode(&out, &in))
 1672       goto jleave;
 1673    if(out.l >= INT_MAX){
 1674       n_free(out.s);
 1675       out.s = NULL;
 1676       goto jleave;
 1677    }
 1678 
 1679    hmac_md5((uc_i*)out.s, out.l, (uc_i*)pass->s, pass->l, digest);
 1680    n_free(out.s);
 1681    cp = md5tohex(n_autorec_alloc(MD5TOHEX_SIZE +1), digest);
 1682 
 1683    in.l = user->l + MD5TOHEX_SIZE +1;
 1684    in.s = n_lofi_alloc(user->l + 1 + MD5TOHEX_SIZE +1);
 1685    memcpy(in.s, user->s, user->l);
 1686    in.s[user->l] = ' ';
 1687    memcpy(&in.s[user->l + 1], cp, MD5TOHEX_SIZE);
 1688    if(b64_encode(&out, &in, B64_SALLOC | B64_CRLF) == NULL)
 1689       out.s = NULL;
 1690    n_lofi_free(in.s);
 1691 jleave:
 1692    NYD_LEAVE;
 1693    return out.s;
 1694 }
 1695 
 1696 FL void
 1697 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
 1698    void *digest)
 1699 {
 1700    /*
 1701     * This code is taken from
 1702     *
 1703     * Network Working Group                                       H. Krawczyk
 1704     * Request for Comments: 2104                                          IBM
 1705     * Category: Informational                                      M. Bellare
 1706     *                                                                    UCSD
 1707     *                                                              R. Canetti
 1708     *                                                                     IBM
 1709     *                                                           February 1997
 1710     *
 1711     *
 1712     *             HMAC: Keyed-Hashing for Message Authentication
 1713     */
 1714    md5_ctx context;
 1715    unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
 1716    unsigned char k_opad[65]; /* outer padding - key XORd with opad */
 1717    unsigned char tk[16];
 1718    int i;
 1719    NYD_ENTER;
 1720 
 1721    /* if key is longer than 64 bytes reset it to key=MD5(key) */
 1722    if (key_len > 64) {
 1723       md5_ctx tctx;
 1724 
 1725       md5_init(&tctx);
 1726       md5_update(&tctx, key, key_len);
 1727       md5_final(tk, &tctx);
 1728 
 1729       key = tk;
 1730       key_len = 16;
 1731    }
 1732 
 1733    /* the HMAC_MD5 transform looks like:
 1734     *
 1735     * MD5(K XOR opad, MD5(K XOR ipad, text))
 1736     *
 1737     * where K is an n byte key
 1738     * ipad is the byte 0x36 repeated 64 times
 1739     * opad is the byte 0x5c repeated 64 times
 1740     * and text is the data being protected */
 1741 
 1742    /* start out by storing key in pads */
 1743    memset(k_ipad, 0, sizeof k_ipad);
 1744    memset(k_opad, 0, sizeof k_opad);
 1745    memcpy(k_ipad, key, key_len);
 1746    memcpy(k_opad, key, key_len);
 1747 
 1748    /* XOR key with ipad and opad values */
 1749    for (i=0; i<64; i++) {
 1750       k_ipad[i] ^= 0x36;
 1751       k_opad[i] ^= 0x5c;
 1752    }
 1753 
 1754    /* perform inner MD5 */
 1755    md5_init(&context);                    /* init context for 1st pass */
 1756    md5_update(&context, k_ipad, 64);      /* start with inner pad */
 1757    md5_update(&context, text, text_len);  /* then text of datagram */
 1758    md5_final(digest, &context);           /* finish up 1st pass */
 1759 
 1760    /* perform outer MD5 */
 1761    md5_init(&context);                 /* init context for 2nd pass */
 1762    md5_update(&context, k_opad, 64);   /* start with outer pad */
 1763    md5_update(&context, digest, 16);   /* then results of 1st hash */
 1764    md5_final(digest, &context);        /* finish up 2nd pass */
 1765    NYD_LEAVE;
 1766 }
 1767 #endif /* HAVE_MD5 */
 1768 
 1769 /* s-it-mode */