"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.19/src/mx/cred-netrc.c" (26 Apr 2020, 17573 Bytes) of package /linux/misc/s-nail-14.9.19.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 "cred-netrc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.18_vs_14.9.19.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Implementation of cred-netrc.h.
    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  *@ TODO With an on_loop_tick_event, trigger cache update once per loop max.
    6  *@ TODO I.e., unless *netrc-pipe* was set, auto check for updates.
    7  *
    8  * Copyright (c) 2014 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    9  * SPDX-License-Identifier: ISC
   10  *
   11  * Permission to use, copy, modify, and/or distribute this software for any
   12  * purpose with or without fee is hereby granted, provided that the above
   13  * copyright notice and this permission notice appear in all copies.
   14  *
   15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   22  */
   23 #undef su_FILE
   24 #define su_FILE cred_netrc
   25 #define mx_SOURCE
   26 #define mx_SOURCE_CRED_NETRC
   27 
   28 #ifndef mx_HAVE_AMALGAMATION
   29 # include "mx/nail.h"
   30 #endif
   31 
   32 su_EMPTY_FILE()
   33 #ifdef mx_HAVE_NETRC
   34 #include <su/cs.h>
   35 #include <su/cs-dict.h>
   36 #include <su/mem.h>
   37 
   38 #include "mx/cmd.h"
   39 #include "mx/child.h"
   40 #include "mx/file-streams.h"
   41 #include "mx/url.h"
   42 
   43 #include "mx/cred-netrc.h"
   44 #include "su/code-in.h"
   45 
   46 /* NetBSD usr.bin/ftp/ruserpass.c uses 100 bytes for that, we need four
   47 * concurrently (dummy, host, user, pass), so make it a KB */
   48 #define a_NETRC_TOKEN_MAXLEN (1024u / 4)
   49 
   50 /* Dictionary stores a_netrc_entry, not owned */
   51 #define a_NETRC_FLAGS (su_CS_DICT_CASE | su_CS_DICT_HEAD_RESORT |\
   52       su_CS_DICT_ERR_PASS)
   53 #define a_NETRC_TRESHOLD_SHIFT 3
   54 
   55 enum a_netrc_token{
   56    a_NETRC_ERROR = -1,
   57    a_NETRC_NONE = 0,
   58    a_NETRC_DEFAULT,
   59    a_NETRC_LOGIN,
   60    a_NETRC_PASSWORD,
   61    a_NETRC_ACCOUNT,
   62    a_NETRC_MACDEF,
   63    a_NETRC_MACHINE,
   64    a_NETRC_INPUT
   65 };
   66 
   67 struct a_netrc_entry{
   68    struct a_netrc_entry *nrce_next;
   69    u32 nrce_password_idx; /* Offset in .nrce_dat, U32_MAX if not set */
   70    boole nrce_has_login; /* Have login at .nrce_dat[0] */
   71    char nrce_dat[VFIELD_SIZE(3)];
   72 };
   73 
   74 static struct su_cs_dict *a_netrc_dp, a_netrc__d; /* XXX atexit _gut (DVL()) */
   75 
   76 /* We stop parsing and _gut(FAL0) on hard errors like NOMEM, OVERFLOW and IO */
   77 static void a_netrc_create(void);
   78 static enum a_netrc_token a_netrc__token(FILE *fi,
   79       char buffer[a_NETRC_TOKEN_MAXLEN], boole *nl_last);
   80 static void a_netrc_gut(boole gut_dp);
   81 
   82 /* */
   83 static struct n_strlist *a_netrc_dump(char const *cmdname, char const *key,
   84       void const *dat);
   85 
   86 /* */
   87 static char *a_netrc_bsd_quote(char const *v);
   88 
   89 static void
   90 a_netrc_create(void){
   91    enum{
   92       a_NONE,
   93       a_IS_PIPE = 1u<<0,
   94       a_LOGIN = 1u<<1,
   95       a_PASSWORD = 1u<<2,
   96       a_SEEN_DEFAULT = 1u<<3,
   97       a_ERROR = 1u<<4
   98    };
   99 
  100    char buffer[a_NETRC_TOKEN_MAXLEN], machine[a_NETRC_TOKEN_MAXLEN],
  101       login[a_NETRC_TOKEN_MAXLEN], password[a_NETRC_TOKEN_MAXLEN], *netrc_load;
  102    struct stat sb;
  103    boole nl_last;
  104    enum a_netrc_token t;
  105    FILE *fi;
  106    struct a_netrc_entry *nrcep;
  107    char const *emsg;
  108    u32 f;
  109    NYD_IN;
  110 
  111    a_netrc_dp = su_cs_dict_set_treshold_shift(
  112          su_cs_dict_create(&a_netrc__d, a_NETRC_FLAGS, NIL),
  113             a_NETRC_TRESHOLD_SHIFT);
  114 
  115    f = a_NONE;
  116    UNINIT(emsg, NIL);
  117    nrcep = NIL;
  118    fi = NIL;
  119 
  120    if((netrc_load = ok_vlook(netrc_pipe)) != NIL){
  121       f |= a_IS_PIPE;
  122       if((fi = mx_fs_pipe_open(netrc_load, "r", ok_vlook(SHELL), NIL,
  123             mx_CHILD_FD_NULL)) == NIL)
  124          goto jerrdoc;
  125    }else{
  126       if((netrc_load = fexpand(ok_vlook(NETRC), (FEXP_NOPROTO |
  127             FEXP_LOCAL_FILE | FEXP_NSHELL))) == NIL)
  128          goto jleave;
  129 
  130       if((fi = mx_fs_open(netrc_load, "r")) == NIL)
  131          goto jerrdoc;
  132 
  133       /* Be simple and apply rigid (permission) check(s) */
  134       if(fstat(fileno(fi), &sb) == -1 || !S_ISREG(sb.st_mode) ||
  135             (sb.st_mode & (S_IRWXG | S_IRWXO))){
  136          emsg = N_("Not a regular file, or accessible by non-user\n");
  137          goto jerr;
  138       }
  139    }
  140 
  141    nl_last = TRU1;
  142    switch((t = a_netrc__token(fi, buffer, &nl_last))){
  143    case a_NETRC_NONE:
  144       break;
  145    default: /* Does not happen (but on error?), keep CC happy */
  146    case a_NETRC_DEFAULT:
  147 jdef:
  148       /* We ignore the default entry (require an exact host match), and we
  149        * also ignore anything after such an entry (faulty syntax) */
  150       f |= a_SEEN_DEFAULT;
  151       /* FALLTHRU */
  152    case a_NETRC_MACHINE:
  153 jm_h:
  154       /* Normalize HOST to lowercase */
  155       *machine = '\0';
  156       if(!(f & a_SEEN_DEFAULT) &&
  157             (t = a_netrc__token(fi, machine, &nl_last)) != a_NETRC_INPUT)
  158          goto jenotinput;
  159 
  160       *login = *password = '\0';
  161       f &= ~(a_LOGIN | a_PASSWORD);
  162 
  163       while((t = a_netrc__token(fi, buffer, &nl_last)) != a_NETRC_NONE &&
  164             t != a_NETRC_MACHINE && t != a_NETRC_DEFAULT){
  165          switch(t){
  166          case a_NETRC_LOGIN:
  167             if((t = a_netrc__token(fi, login, &nl_last)) != a_NETRC_INPUT)
  168                goto jenotinput;
  169             f |= a_LOGIN;
  170             break;
  171          case a_NETRC_PASSWORD:
  172             if((t = a_netrc__token(fi, password, &nl_last)) != a_NETRC_INPUT)
  173                goto jenotinput;
  174             f |= a_PASSWORD;
  175             break;
  176          case a_NETRC_ACCOUNT:
  177             if((t = a_netrc__token(fi, buffer, &nl_last)) != a_NETRC_INPUT)
  178                goto jenotinput;
  179             break;
  180          case a_NETRC_MACDEF:
  181             if((t = a_netrc__token(fi, buffer, &nl_last)) != a_NETRC_INPUT){
  182 jenotinput:
  183                emsg = N_("parse error");
  184                goto jerr;
  185             }else{
  186                int i, c;
  187 
  188                for(i = 0; (c = getc(fi)) != EOF;)
  189                   if(c == '\n'){ /* xxx */
  190                      /* Do not care about comments here, since we parse until
  191                       * we have seen two successive newline characters */
  192                      if(i)
  193                         break;
  194                      i = 1;
  195                   }else
  196                      i = 0;
  197             }
  198             break;
  199          default:
  200          case a_NETRC_ERROR:
  201             emsg = N_("parse error (unknown token)");
  202             goto jerr;
  203          }
  204       }
  205 
  206       if(!(f & a_SEEN_DEFAULT) && (f & (a_LOGIN | a_PASSWORD))){
  207          union {void *v; struct a_netrc_entry *nrce;} p;
  208          u32 llen, plen;
  209 
  210          llen = (f & a_LOGIN) ? su_cs_len(login) : 0;
  211          plen = (f & a_PASSWORD) ? su_cs_len(password) : 0;
  212          nrcep = su_ALLOC(VSTRUCT_SIZEOF(struct a_netrc_entry, nrce_dat) +
  213                llen +1 + plen +1);
  214          if(nrcep == NIL)
  215             goto jerrdoc;
  216          nrcep->nrce_next = NIL;
  217 
  218          if((nrcep->nrce_has_login = ((f & a_LOGIN) != 0)))
  219             su_mem_copy(&nrcep->nrce_dat[0], login, ++llen);
  220 
  221          if(f & a_PASSWORD)
  222             su_mem_copy(&nrcep->nrce_dat[nrcep->nrce_password_idx = llen],
  223                password, ++plen);
  224          else
  225             nrcep->nrce_password_idx = U32_MAX;
  226 
  227          if((p.v = su_cs_dict_lookup(a_netrc_dp, machine)) != NIL){
  228             while(p.nrce->nrce_next != NIL)
  229                p.nrce = p.nrce->nrce_next;
  230             p.nrce->nrce_next = nrcep;
  231          }else{
  232             s32 err;
  233 
  234             if((err = su_cs_dict_insert(a_netrc_dp, machine, nrcep)
  235                   ) != su_ERR_NONE){
  236                emsg = su_err_doc(err);
  237                goto jerr;
  238             }
  239          }
  240 
  241          nrcep = NIL;
  242       }
  243 
  244       if(t != a_NETRC_NONE && (f & a_SEEN_DEFAULT) && (n_poption & n_PO_D_V))
  245          n_err(_(".netrc: \"default\" must be last entry, ignoring: %s\n"),
  246             n_shexp_quote_cp(netrc_load, FAL0));
  247       if(t == a_NETRC_MACHINE)
  248          goto jm_h;
  249       if(t == a_NETRC_DEFAULT)
  250          goto jdef;
  251       ASSERT(t == a_NETRC_NONE);
  252       break;
  253    case a_NETRC_ERROR:
  254       emsg = N_("parse error (unknown top level token)");
  255       goto jerr;
  256    }
  257 
  258    nrcep = NIL;
  259 jleave:
  260    if(nrcep != NIL)
  261       su_FREE(nrcep);
  262 
  263    if(fi != NIL){
  264       if(f & a_IS_PIPE)
  265          mx_fs_pipe_close(fi, TRU1);
  266       else
  267          mx_fs_close(fi);
  268    }
  269 
  270    if(f & a_ERROR)
  271       a_netrc_gut(FAL0);
  272 
  273    NYD_OU;
  274    return;
  275 
  276 jerrdoc:
  277    emsg = su_err_doc(su_err_no());
  278 jerr:
  279    n_err(_(".netrc: %s: %s\n"), n_shexp_quote_cp(netrc_load, FAL0), V_(emsg));
  280    f |= a_ERROR;
  281    goto jleave;
  282 }
  283 
  284 static enum a_netrc_token
  285 a_netrc__token(FILE *fi, char buffer[a_NETRC_TOKEN_MAXLEN], boole *nl_last){
  286    static struct token_type{
  287       s8 tt_token;
  288       char tt_name[15];
  289    } const *ttap, tta[] = {
  290       {a_NETRC_NONE, ""},
  291       {a_NETRC_DEFAULT, "default"},
  292       {a_NETRC_LOGIN, "login"},
  293       {a_NETRC_PASSWORD, "password\0"},
  294       {a_NETRC_PASSWORD, "passwd"},
  295       {a_NETRC_ACCOUNT, "account"},
  296       {a_NETRC_MACDEF, "macdef"},
  297       {a_NETRC_MACHINE, "machine"}
  298    };
  299    int c;
  300    char *cp;
  301    enum a_netrc_token rv;
  302    NYD2_IN;
  303 
  304    rv = a_NETRC_NONE;
  305 
  306    for(;;){
  307       boole seen_nl;
  308 
  309       c = EOF;
  310       if(feof(fi) || ferror(fi))
  311          goto jleave;
  312 
  313       for(seen_nl = *nl_last; (c = getc(fi)) != EOF && su_cs_is_white(c);)
  314          seen_nl |= (c == '\n');
  315 
  316       if(c == EOF)
  317          goto jleave;
  318       /* fetchmail and derived parsers support comments */
  319       if((*nl_last = seen_nl) && c == '#'){
  320          while((c = getc(fi)) != EOF && c != '\n')
  321             ;
  322          continue;
  323       }
  324       break;
  325    }
  326 
  327    /* Is it a quoted token?  At least IBM syntax also supports ' quotes */
  328    cp = buffer;
  329    if(c == '"' || c == '\''){
  330       int quotec;
  331 
  332       quotec = c;
  333       /* Not requiring the closing QM is (Net)BSD syntax */
  334       while((c = getc(fi)) != EOF && c != quotec){
  335          /* Reverse solidus escaping the next character is (Net)BSD syntax */
  336          if(c == '\\')
  337             if((c = getc(fi)) == EOF)
  338                break;
  339          *cp++ = c;
  340          if(PCMP(cp, ==, &buffer[a_NETRC_TOKEN_MAXLEN -1])){
  341             rv = a_NETRC_ERROR;
  342             goto jleave;
  343          }
  344       }
  345    }else{
  346       *cp++ = c;
  347       while((c = getc(fi)) != EOF && !su_cs_is_white(c)){
  348          /* Reverse solidus escaping the next character is (Net)BSD syntax */
  349          if(c == '\\' && (c = getc(fi)) == EOF)
  350                break;
  351          *cp++ = c;
  352          if(PCMP(cp, ==, &buffer[a_NETRC_TOKEN_MAXLEN -1])){
  353             rv = a_NETRC_ERROR;
  354             goto jleave;
  355          }
  356       }
  357       *nl_last = (c == '\n');
  358    }
  359    *cp = '\0';
  360 
  361    /* */
  362    rv = a_NETRC_INPUT;
  363    for(ttap = &tta[0]; ttap < &tta[NELEM(tta)]; ++ttap)
  364       if(!su_cs_cmp(buffer, ttap->tt_name)){
  365          rv = ttap->tt_token;
  366          break;
  367       }
  368 
  369 jleave:
  370    if(c == EOF && !feof(fi))
  371       rv = a_NETRC_ERROR;
  372 
  373    NYD2_OU;
  374    return rv;
  375 }
  376 
  377 static void
  378 a_netrc_gut(boole gut_dp){
  379    NYD2_IN;
  380 
  381    if(a_netrc_dp != NIL){
  382       struct su_cs_dict_view csdv;
  383 
  384       su_CS_DICT_FOREACH(a_netrc_dp, &csdv){
  385          struct a_netrc_entry *nrcep, *tmp;
  386 
  387          for(nrcep = S(struct a_netrc_entry*,su_cs_dict_view_data(&csdv));
  388                nrcep != NIL;){
  389             tmp = nrcep;
  390             nrcep = nrcep->nrce_next;
  391             su_FREE(tmp);
  392          }
  393       }
  394 
  395       if(gut_dp){
  396          su_cs_dict_gut(a_netrc_dp);
  397          a_netrc_dp = NIL;
  398       }else
  399          su_cs_dict_clear(a_netrc_dp);
  400    }
  401 
  402    NYD2_OU;
  403 }
  404 
  405 static struct n_strlist *
  406 a_netrc_dump(char const *cmdname, char const *key, void const *dat){
  407    struct n_string s_b, *s;
  408    struct n_strlist *slp;
  409    struct a_netrc_entry const *nrcep;
  410    NYD2_IN;
  411    UNUSED(cmdname);
  412 
  413    s = n_string_book(n_string_creat_auto(&s_b), 127);
  414    s = n_string_resize(s, n_STRLIST_PLAIN_SIZE());
  415 
  416    for(nrcep = S(struct a_netrc_entry const*,dat); nrcep != NIL;
  417          nrcep = nrcep->nrce_next){
  418       if(S(void const*,nrcep) != dat)
  419          s = n_string_push_c(s, '\n');
  420 
  421       s = n_string_push_buf(s, "machine ", sizeof("machine ") -1);
  422       s = n_string_push_cp(s, a_netrc_bsd_quote(key));
  423 
  424       if(nrcep->nrce_has_login){
  425          s = n_string_push_buf(s, " login ", sizeof(" login ") -1);
  426          s = n_string_push_cp(s, a_netrc_bsd_quote(&nrcep->nrce_dat[0]));
  427       }
  428 
  429       if(nrcep->nrce_password_idx != U32_MAX){
  430          s = n_string_push_buf(s, " password ", sizeof(" password ") -1);
  431          s = n_string_push_cp(s,
  432                a_netrc_bsd_quote(&nrcep->nrce_dat[nrcep->nrce_password_idx]));
  433       }
  434    }
  435 
  436    s = n_string_push_c(s, '\n');
  437 
  438    n_string_cp(s);
  439 
  440    slp = R(struct n_strlist*,S(void*,s->s_dat));
  441    /* xxx Should we assert alignment constraint of slp is satisfied?
  442     * xxx Should be, heap memory with alignment < sizeof(void*) bitter? */
  443    slp->sl_next = NIL;
  444    slp->sl_len = s->s_len;
  445    n_string_drop_ownership(s);
  446 
  447    NYD2_OU;
  448    return slp;
  449 }
  450 
  451 static char *
  452 a_netrc_bsd_quote(char const *v){
  453    char const *cp;
  454    uz i;
  455    char c, *rv;
  456    boole quotes;
  457    NYD2_IN;
  458 
  459    quotes = FAL0;
  460 
  461    for(i = 0, cp = v; (c = *cp) != '\0'; ++i, ++cp)
  462       /* \n etc. weird, granted */
  463       if(su_cs_find_c("\"\\ \n\r\t\v", c) != NIL){
  464          ++i;
  465          if(!quotes && c != '"' && c != '\\')
  466             quotes = TRU1;
  467       }
  468    if(quotes)
  469       i += 2;
  470 
  471    rv = n_autorec_alloc(i +1);
  472 
  473    i = 0;
  474    if(quotes)
  475       rv[i++] = '"';
  476    for(cp = v; (c = *cp) != '\0'; rv[i++] = c, ++cp)
  477       if(c == '"' || c == '\\')
  478          rv[i++] = '\\';
  479    if(quotes)
  480       rv[i++] = '"';
  481    rv[i] = '\0';
  482 
  483    NYD2_OU;
  484    return rv;
  485 }
  486 
  487 int
  488 c_netrc(void *vp){
  489    boole load_only;
  490    char **argv;
  491    NYD_IN;
  492 
  493    argv = vp;
  494 
  495    load_only = FAL0;
  496    if(*argv == NIL)
  497       goto jlist;
  498    if(su_cs_starts_with_case("lookup", *argv)){
  499       if(argv[1] == NIL)
  500          goto jerr;
  501       goto jlookup;
  502    }
  503    if(argv[1] != NIL)
  504       goto jerr;
  505    if(su_cs_starts_with_case("show", *argv))
  506       goto jlist;
  507    if(su_cs_starts_with_case("clear", *argv))
  508       goto jclear;
  509 
  510    load_only = TRU1;
  511    if(su_cs_starts_with_case("load", *argv))
  512       goto jclear;
  513 jerr:
  514    mx_cmd_print_synopsis(mx_cmd_firstfit("netrc"), NIL);
  515    vp = NIL;
  516 jleave:
  517    NYD_OU;
  518    return (vp == NIL ? n_EXIT_ERR : n_EXIT_OK);
  519 
  520 jlookup:{
  521    struct mx_netrc_entry nrce;
  522    struct mx_url url;
  523 
  524    if(!mx_url_parse(&url, CPROTO_NONE, argv[1])){
  525       n_err(_("netrc: lookup: invalid URL: %s\n"),
  526          n_shexp_quote_cp(argv[1], FAL0));
  527       vp = NIL;
  528    }else if(mx_netrc_lookup(&nrce, &url)){
  529       fprintf(n_stdout, "netrc: lookup: %s: machine %s",
  530          url.url_u_h.s, a_netrc_bsd_quote(nrce.nrce_machine));
  531       if(nrce.nrce_login != NIL)
  532          fprintf(n_stdout, " login %s", a_netrc_bsd_quote(nrce.nrce_login));
  533       if(nrce.nrce_password != NIL)
  534          fprintf(n_stdout, " password %s",
  535             a_netrc_bsd_quote(nrce.nrce_password));
  536       putc('\n', n_stdout);
  537    }else{
  538       fprintf(n_stdout, _("netrc: lookup: no entry for: %s\n"), url.url_u_h.s);
  539       vp = NIL;
  540    }
  541    goto jleave;
  542    }
  543 
  544 jclear:
  545    a_netrc_gut(TRU1);
  546    if(load_only)
  547       goto jlist;
  548    goto jleave;
  549 
  550 jlist:
  551    if(a_netrc_dp == NIL)
  552       a_netrc_create();
  553 
  554    if(!load_only){
  555       struct n_strlist *slp;
  556 
  557       slp = NIL;
  558       if(!(mx_xy_dump_dict("netrc", a_netrc_dp, &slp, NIL,
  559                &a_netrc_dump) &&
  560             mx_page_or_print_strlist("netrc", slp, TRU1)))
  561          vp = NIL;
  562    }
  563    goto jleave;
  564 }
  565 
  566 boole
  567 mx_netrc_lookup(struct mx_netrc_entry *result, struct mx_url const *urlp){
  568    struct n_string s_b, *s;
  569    union {void *v; uz i; struct a_netrc_entry *nrce;} p;
  570    char const *host;
  571    boole rv;
  572    NYD_IN;
  573 
  574    s = n_string_creat_auto(&s_b);
  575 
  576    rv = FAL0;
  577 
  578    if(a_netrc_dp == NIL)
  579       a_netrc_create();
  580    if(su_cs_dict_count(a_netrc_dp) == 0)
  581       goto jleave;
  582 
  583    s = n_string_book(s, urlp->url_host.l + 2);
  584 
  585    if((p.v = su_cs_dict_lookup(a_netrc_dp, host = urlp->url_host.s)) == NIL){
  586       /* Cannot be an exact match, but maybe .netrc provides a matching
  587        * "*." wildcard entry, which we recognize as an extension, meaning
  588        * "skip a single subdomain, then match the rest" */
  589       for(host = urlp->url_host.s, p.i = urlp->url_host.l;;){
  590          if(--p.i <= 1)
  591             goto jleave;
  592          if(*host++ == '.')
  593             break;
  594       }
  595 
  596       s = n_string_push_buf(s, "*.", sizeof("*.") -1);
  597       s = n_string_push_buf(s, host, p.i);
  598       if((p.v = su_cs_dict_lookup(a_netrc_dp, host = n_string_cp(s))) == NIL)
  599          goto jleave;
  600    }
  601 
  602    /* Without user we will only return a result if unambiguous */
  603    rv = TRUM1;
  604    if(urlp->url_user.s != NIL){
  605       /* (No do{}while() because of gcc 3.4.3 union bug (Solaris 5.10)) */
  606       for(; p.nrce != NIL; p.nrce = p.nrce->nrce_next){
  607          if(p.nrce->nrce_has_login){
  608             if(!su_cs_cmp(&p.nrce->nrce_dat[0], urlp->url_user.s)){
  609                rv = TRUM1;
  610                break;
  611             }
  612          }else if(rv == TRUM1 && p.nrce->nrce_next == NIL)
  613             break;
  614          rv = TRU1;
  615       }
  616    }else if(p.nrce->nrce_next != NIL)
  617       p.nrce = NIL;
  618 
  619    if(p.nrce != NIL){
  620       su_mem_set(result, 0, sizeof(*result));
  621       result->nrce_machine = host;
  622       if(p.nrce->nrce_has_login)
  623          result->nrce_login = &p.nrce->nrce_dat[0];
  624       if(p.nrce->nrce_password_idx != U32_MAX)
  625          result->nrce_password = &p.nrce->nrce_dat[p.nrce->nrce_password_idx];
  626    }else
  627       rv = FAL0;
  628 
  629 jleave:
  630    /* su_string_gut(s); */
  631    NYD_OU;
  632    return rv;
  633 }
  634 
  635 #include "su/code-ou.h"
  636 #endif /* mx_HAVE_NETRC */
  637 /* s-it-mode */