mirror of
https://github.com/curl/curl.git
synced 2026-05-20 14:56:19 +03:00
Refactor the netrc scanner. Add test case for checking that the last matched machine with unmatched login does not return the password as success (unit1304). Closes #21624
685 lines
17 KiB
C
685 lines
17 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
*
|
|
* This software is licensed as described in the file COPYING, which
|
|
* you should have received as part of this distribution. The terms
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
***************************************************************************/
|
|
#include "curl_setup.h"
|
|
|
|
#ifndef CURL_DISABLE_NETRC
|
|
|
|
#ifdef HAVE_PWD_H
|
|
#ifdef __AMIGA__
|
|
#undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */
|
|
#endif
|
|
#include <pwd.h>
|
|
#ifdef __AMIGA__
|
|
#define __NO_NET_API
|
|
#endif
|
|
#endif
|
|
|
|
#include "netrc.h"
|
|
#include "urldata.h"
|
|
#include "creds.h"
|
|
#include "curl_trc.h"
|
|
#include "strcase.h"
|
|
#include "curl_get_line.h"
|
|
#include "curlx/fopen.h"
|
|
#include "curlx/strparse.h"
|
|
|
|
|
|
/* .netrc is not really a standard. The GNU definition can be found here:
|
|
* https://www.gnu.org/software/inetutils/manual/\
|
|
* html_node/The-_002enetrc-file.html
|
|
* This gives grammar like:
|
|
*
|
|
* LITERAL := \S+ | QUOTED
|
|
* QUOTED := "(\\[rnt\]|[^"])*"
|
|
* ANYTHING := .
|
|
* EMPTY_LINE := \r*\n\r*\n
|
|
* MACHINE := machine # case-insensitive
|
|
* LOGIN := login # case-insensitive
|
|
* PASSWD := password # case-insensitive
|
|
* ACCOUNT := account # case-insensitive
|
|
* MACDEF := macdef # case-insensitive
|
|
* DEFAULT := default # case-insensitive
|
|
*
|
|
* MACRO := MACDEF ANYTHING* EMPTY_LINE
|
|
* JUNK := LITERAL
|
|
* LKEY := ( LOGIN | PASSWD | ACCOUNT ) LITERAL
|
|
* MENTRY := MACHINE LITERAL LKEY*
|
|
* DENTRY := DEFAULT LKEY*
|
|
* NETRC := (MENTRY | DENTRY | MACRO | JUNK )* EOF
|
|
*
|
|
* Tokens are separated by whitespace or newlines. which have otherwise
|
|
* no special meaning, apart from the empty line ending a MACRO.
|
|
*
|
|
* Parsing is not strict, unmatched LITERALs are ignored
|
|
*/
|
|
|
|
#define MAX_NETRC_LINE 16384
|
|
#define MAX_NETRC_FILE (128 * 1024)
|
|
#define MAX_NETRC_TOKEN 4096
|
|
|
|
#define NETRC_DEBUG 0
|
|
|
|
/* convert a dynbuf call CURLcode error to a NETRCcode error */
|
|
#define curl2netrc(r) \
|
|
((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \
|
|
NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR))
|
|
|
|
typedef enum {
|
|
NETRC_TOK_EOF,
|
|
NETRC_TOK_LITERAL,
|
|
NETRC_TOK_MACHINE,
|
|
NETRC_TOK_DEFAULT,
|
|
NETRC_TOK_ACCOUNT,
|
|
NETRC_TOK_LOGIN,
|
|
NETRC_TOK_PASSWD,
|
|
NETRC_TOK_MACDEF,
|
|
NETRC_TOK_JUNK
|
|
} curl_netrc_token;
|
|
|
|
struct netrc_lexer {
|
|
struct Curl_easy *data;
|
|
const char *content;
|
|
const char *pos;
|
|
struct dynbuf literal;
|
|
curl_netrc_token token;
|
|
bool pushed;
|
|
};
|
|
|
|
#if NETRC_DEBUG
|
|
static const char *netrc_tokenstr(curl_netrc_token token)
|
|
{
|
|
switch(token) {
|
|
case NETRC_TOK_EOF:
|
|
return "[EOF]";
|
|
case NETRC_TOK_LITERAL:
|
|
return "[LITERAL]";
|
|
case NETRC_TOK_MACHINE:
|
|
return "[MACHINE]";
|
|
case NETRC_TOK_DEFAULT:
|
|
return "[DEFAULT]";
|
|
case NETRC_TOK_ACCOUNT:
|
|
return "[ACCOUNT]";
|
|
case NETRC_TOK_LOGIN:
|
|
return "[LOGIN]";
|
|
case NETRC_TOK_PASSWD:
|
|
return "[PASSWORD]";
|
|
case NETRC_TOK_MACDEF:
|
|
return "[MACDEF]";
|
|
case NETRC_TOK_JUNK:
|
|
return "[JUNK]";
|
|
default:
|
|
return "[???]";
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void netrc_lexer_init(struct netrc_lexer *lexer,
|
|
struct Curl_easy *data,
|
|
const char *content)
|
|
{
|
|
curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN);
|
|
lexer->data = data;
|
|
lexer->content = lexer->pos = content;
|
|
}
|
|
|
|
static void netrc_lexer_cleanup(struct netrc_lexer *lexer)
|
|
{
|
|
lexer->content = lexer->pos = NULL;
|
|
lexer->data = NULL;
|
|
curlx_dyn_free(&lexer->literal);
|
|
}
|
|
|
|
static void netrc_skip_blanks(struct netrc_lexer *lexer)
|
|
{
|
|
const char *s = lexer->pos;
|
|
while(*s) {
|
|
curlx_str_passblanks(&s);
|
|
while(*s == '\r')
|
|
++s;
|
|
if(*s == '\n') {
|
|
++s;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
lexer->pos = s;
|
|
}
|
|
|
|
static void netrc_skip_to_empty_line(struct netrc_lexer *lexer)
|
|
{
|
|
const char *s = lexer->pos;
|
|
while(*s) {
|
|
if(*s == '\r')
|
|
++s;
|
|
else if(*s == '\n') {
|
|
++s;
|
|
while(*s == '\r')
|
|
++s;
|
|
if(*s == '\n')
|
|
goto out;
|
|
}
|
|
else
|
|
++s;
|
|
}
|
|
out:
|
|
lexer->pos = s;
|
|
}
|
|
|
|
/*
|
|
* Parse a quoted token starting after the opening '"'. Handles \n, \r, \t
|
|
* escape sequences. Advances *tok_endp past the closing '"'.
|
|
*
|
|
* Returns NETRC_OK or error.
|
|
*/
|
|
static NETRCcode netrc_lexer_quoted(struct netrc_lexer *lexer)
|
|
{
|
|
NETRCcode rc = NETRC_SYNTAX_ERROR;
|
|
const char *s = lexer->pos;
|
|
bool escape = FALSE;
|
|
CURLcode result;
|
|
|
|
DEBUGASSERT(*s == '\"');
|
|
++s; /* pass the leading quote */
|
|
while(*s) {
|
|
char c = *s;
|
|
if(escape) {
|
|
escape = FALSE;
|
|
switch(c) {
|
|
case 'n':
|
|
c = '\n';
|
|
break;
|
|
case 'r':
|
|
c = '\r';
|
|
break;
|
|
case 't':
|
|
c = '\t';
|
|
break;
|
|
}
|
|
}
|
|
else if(c == '\\') {
|
|
escape = TRUE;
|
|
++s;
|
|
continue;
|
|
}
|
|
else if(c == '\"') {
|
|
++s; /* pass the ending quote */
|
|
rc = NETRC_OK;
|
|
goto out;
|
|
}
|
|
result = curlx_dyn_addn(&lexer->literal, &c, 1);
|
|
if(result) {
|
|
rc = curl2netrc(result);
|
|
goto out;
|
|
}
|
|
++s;
|
|
}
|
|
out:
|
|
lexer->pos = s;
|
|
return rc;
|
|
}
|
|
|
|
static void netrc_lexer_push(struct netrc_lexer *lexer)
|
|
{
|
|
lexer->pushed = TRUE;
|
|
}
|
|
|
|
static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer,
|
|
bool want_literal)
|
|
{
|
|
const char *s = lexer->pos, *start;
|
|
NETRCcode rc = NETRC_OK;
|
|
size_t slen;
|
|
CURLcode result;
|
|
|
|
if(lexer->pushed) {
|
|
lexer->pushed = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
curlx_dyn_reset(&lexer->literal);
|
|
netrc_skip_blanks(lexer);
|
|
s = lexer->pos;
|
|
|
|
switch(*s) {
|
|
case 0:
|
|
lexer->token = NETRC_TOK_EOF;
|
|
break;
|
|
case '\"':
|
|
rc = netrc_lexer_quoted(lexer);
|
|
lexer->token = NETRC_TOK_LITERAL;
|
|
s = lexer->pos;
|
|
break;
|
|
default:
|
|
/* unquoted token */
|
|
start = s;
|
|
while(*s && !ISBLANK(*s) && !ISNEWLINE(*s))
|
|
++s;
|
|
slen = s - start;
|
|
if(!slen) {
|
|
rc = NETRC_SYNTAX_ERROR;
|
|
}
|
|
if(want_literal) {
|
|
lexer->token = NETRC_TOK_LITERAL;
|
|
result = curlx_dyn_addn(&lexer->literal, start, slen);
|
|
rc = curl2netrc(result);
|
|
}
|
|
else if((slen == 7) && curl_strnequal(start, "machine", slen)) {
|
|
lexer->token = NETRC_TOK_MACHINE;
|
|
}
|
|
else if((slen == 7) && curl_strnequal(start, "default", slen)) {
|
|
lexer->token = NETRC_TOK_DEFAULT;
|
|
}
|
|
else if((slen == 7) && curl_strnequal(start, "account", slen)) {
|
|
lexer->token = NETRC_TOK_ACCOUNT;
|
|
}
|
|
else if((slen == 5) && curl_strnequal(start, "login", slen)) {
|
|
lexer->token = NETRC_TOK_LOGIN;
|
|
}
|
|
else if((slen == 8) && curl_strnequal(start, "password", slen)) {
|
|
lexer->token = NETRC_TOK_PASSWD;
|
|
}
|
|
else if((slen == 6) && curl_strnequal(start, "macdef", slen)) {
|
|
lexer->token = NETRC_TOK_MACDEF;
|
|
}
|
|
else {
|
|
lexer->token = NETRC_TOK_JUNK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
out:
|
|
#if NETRC_DEBUG
|
|
CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d",
|
|
netrc_tokenstr(lexer->token),
|
|
curlx_dyn_ptr(&lexer->literal), rc);
|
|
#endif
|
|
lexer->pos = s;
|
|
return rc;
|
|
}
|
|
|
|
struct netrc_scanner {
|
|
struct netrc_lexer lexer;
|
|
const char *hostname; /* non-NULL, machine to scan for */
|
|
const char *user; /* maybe NULL, login to scan for */
|
|
char *login;
|
|
char *passwd;
|
|
struct Curl_creds *creds;
|
|
bool matches_host;
|
|
bool found;
|
|
};
|
|
|
|
static void netrc_scan_reset(struct netrc_scanner *sc)
|
|
{
|
|
curlx_safefree(sc->login);
|
|
curlx_safefree(sc->passwd);
|
|
sc->matches_host = FALSE;
|
|
}
|
|
|
|
static void netrc_scan_init(struct netrc_scanner *sc,
|
|
struct Curl_easy *data,
|
|
const char *content,
|
|
const char *hostname,
|
|
const char *user)
|
|
{
|
|
memset(sc, 0, sizeof(*sc));
|
|
netrc_lexer_init(&sc->lexer, data, content);
|
|
sc->hostname = hostname;
|
|
sc->user = (user && user[0]) ? user : NULL;
|
|
netrc_scan_reset(sc);
|
|
}
|
|
|
|
static void netrc_scan_cleanup(struct netrc_scanner *sc)
|
|
{
|
|
netrc_scan_reset(sc);
|
|
sc->hostname = NULL;
|
|
sc->user = NULL;
|
|
Curl_creds_unlink(&sc->creds);
|
|
netrc_lexer_cleanup(&sc->lexer);
|
|
}
|
|
|
|
static NETRCcode netrc_scan_literal(struct netrc_scanner *sc,
|
|
char **pdest)
|
|
{
|
|
NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE);
|
|
if(!rc) {
|
|
if(sc->lexer.token == NETRC_TOK_LITERAL) {
|
|
if(pdest && sc->matches_host) {
|
|
curlx_free(*pdest);
|
|
*pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal));
|
|
if(!*pdest)
|
|
rc = NETRC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
netrc_lexer_push(&sc->lexer);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc)
|
|
{
|
|
NETRCcode rc = NETRC_OK;
|
|
#if NETRC_DEBUG
|
|
CURL_TRC_M(sc->lexer.data,
|
|
"[NETRC] entry matches_host=%d, login='%s', passwd='%s'",
|
|
sc->matches_host, sc->login, sc->passwd);
|
|
#endif
|
|
if(sc->matches_host) {
|
|
if(sc->login) {
|
|
if(sc->user) {
|
|
if(Curl_timestrcmp(sc->user, sc->login))
|
|
goto out;
|
|
/* We look for a specific user,
|
|
* entry is only interesting with password */
|
|
sc->found = !!sc->passwd;
|
|
}
|
|
else {
|
|
sc->found = TRUE;
|
|
}
|
|
}
|
|
else if(sc->passwd) {
|
|
/* found a passwd that applies to any user */
|
|
sc->found = TRUE;
|
|
}
|
|
else {
|
|
/* entry has nothing interesting */
|
|
}
|
|
if(sc->found) {
|
|
#if NETRC_DEBUG
|
|
CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found");
|
|
#endif
|
|
if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd,
|
|
NULL, NULL, NULL, CREDS_NETRC, &sc->creds))
|
|
rc = NETRC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
out:
|
|
netrc_scan_reset(sc);
|
|
return rc;
|
|
}
|
|
|
|
static NETRCcode netrc_scan(struct Curl_easy *data,
|
|
const char *content,
|
|
const char *hostname,
|
|
const char *user,
|
|
struct Curl_creds **pcreds)
|
|
{
|
|
struct netrc_scanner sc;
|
|
NETRCcode rc = NETRC_OK;
|
|
|
|
Curl_creds_unlink(pcreds);
|
|
netrc_scan_init(&sc, data, content, hostname, user);
|
|
|
|
while(!rc && !sc.found) {
|
|
rc = netrc_lexer_next(&sc.lexer, FALSE);
|
|
if(!rc) {
|
|
/* Does this token end any previous entry? */
|
|
switch(sc.lexer.token) {
|
|
case NETRC_TOK_EOF:
|
|
case NETRC_TOK_MACHINE:
|
|
case NETRC_TOK_DEFAULT:
|
|
case NETRC_TOK_MACDEF:
|
|
rc = netrc_scan_end_entry(&sc);
|
|
if(rc || sc.found)
|
|
goto out;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(sc.lexer.token) {
|
|
case NETRC_TOK_EOF:
|
|
goto out;
|
|
case NETRC_TOK_MACHINE:
|
|
rc = netrc_lexer_next(&sc.lexer, TRUE);
|
|
if(!rc) {
|
|
if(sc.lexer.token == NETRC_TOK_LITERAL) {
|
|
sc.matches_host = curl_strequal(
|
|
sc.hostname, curlx_dyn_ptr(&sc.lexer.literal));
|
|
}
|
|
else {
|
|
sc.matches_host = FALSE;
|
|
netrc_lexer_push(&sc.lexer);
|
|
}
|
|
}
|
|
break;
|
|
case NETRC_TOK_DEFAULT:
|
|
sc.matches_host = TRUE;
|
|
break;
|
|
case NETRC_TOK_ACCOUNT:
|
|
rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */
|
|
break;
|
|
case NETRC_TOK_LOGIN:
|
|
rc = netrc_scan_literal(&sc, &sc.login);
|
|
break;
|
|
case NETRC_TOK_PASSWD:
|
|
rc = netrc_scan_literal(&sc, &sc.passwd);
|
|
break;
|
|
case NETRC_TOK_MACDEF:
|
|
netrc_skip_to_empty_line(&sc.lexer);
|
|
break;
|
|
case NETRC_TOK_LITERAL:
|
|
case NETRC_TOK_JUNK:
|
|
default:
|
|
/* skip this */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
if(!rc) {
|
|
if(sc.creds)
|
|
Curl_creds_link(pcreds, sc.creds);
|
|
else
|
|
rc = NETRC_NO_MATCH;
|
|
}
|
|
netrc_scan_cleanup(&sc);
|
|
return rc;
|
|
}
|
|
|
|
static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
|
|
{
|
|
NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
|
|
FILE *file = curlx_fopen(filename, FOPEN_READTEXT);
|
|
|
|
if(file) {
|
|
curlx_struct_stat stat;
|
|
if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
|
|
CURLcode result = CURLE_OK;
|
|
bool eof;
|
|
struct dynbuf linebuf;
|
|
curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
|
|
ret = NETRC_OK;
|
|
do {
|
|
const char *line;
|
|
/* Curl_get_line always returns lines ending with a newline */
|
|
result = Curl_get_line(&linebuf, file, &eof);
|
|
if(!result) {
|
|
line = curlx_dyn_ptr(&linebuf);
|
|
/* skip comments on load */
|
|
curlx_str_passblanks(&line);
|
|
if(*line == '#')
|
|
continue;
|
|
result = curlx_dyn_add(filebuf, line);
|
|
}
|
|
if(result) {
|
|
curlx_dyn_free(filebuf);
|
|
ret = curl2netrc(result);
|
|
break;
|
|
}
|
|
} while(!eof);
|
|
curlx_dyn_free(&linebuf);
|
|
}
|
|
curlx_fclose(file);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static NETRCcode netrc_scan_file(struct Curl_easy *data,
|
|
struct store_netrc *store,
|
|
const char *hostname,
|
|
const char *user,
|
|
const char *netrcfile,
|
|
struct Curl_creds **pcreds)
|
|
{
|
|
struct dynbuf *filebuf = &store->filebuf;
|
|
|
|
if(!store->loaded) {
|
|
NETRCcode ret = file2memory(netrcfile, filebuf);
|
|
if(ret) {
|
|
CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile);
|
|
return ret;
|
|
}
|
|
store->loaded = TRUE;
|
|
}
|
|
|
|
return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds);
|
|
}
|
|
|
|
/*
|
|
* @unittest: 1304
|
|
*
|
|
* *loginp and *passwordp MUST be allocated if they are not NULL when passed
|
|
* in.
|
|
*/
|
|
NETRCcode Curl_netrc_scan(struct Curl_easy *data,
|
|
struct store_netrc *store,
|
|
const char *hostname,
|
|
const char *user,
|
|
const char *netrcfile,
|
|
struct Curl_creds **pcreds)
|
|
{
|
|
NETRCcode retcode = NETRC_OK;
|
|
char *filealloc = NULL;
|
|
|
|
CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'",
|
|
netrcfile, hostname, user);
|
|
Curl_creds_unlink(pcreds);
|
|
if(!netrcfile) {
|
|
char *home = NULL;
|
|
char *homea = NULL;
|
|
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
|
|
char pwbuf[1024];
|
|
#endif
|
|
filealloc = curl_getenv("NETRC");
|
|
if(!filealloc) {
|
|
homea = curl_getenv("HOME"); /* portable environment reader */
|
|
if(homea) {
|
|
home = homea;
|
|
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
|
|
}
|
|
else {
|
|
struct passwd pw, *pw_res;
|
|
if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
|
|
pw_res) {
|
|
home = pw.pw_dir;
|
|
}
|
|
#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
|
|
}
|
|
else {
|
|
struct passwd *pw;
|
|
pw = getpwuid(geteuid());
|
|
if(pw) {
|
|
home = pw->pw_dir;
|
|
}
|
|
#elif defined(_WIN32)
|
|
}
|
|
else {
|
|
homea = curl_getenv("USERPROFILE");
|
|
if(homea) {
|
|
home = homea;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(!home)
|
|
return NETRC_FILE_MISSING; /* no home directory found (or possibly out
|
|
of memory) */
|
|
|
|
filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
|
|
if(!filealloc) {
|
|
curlx_free(homea);
|
|
retcode = NETRC_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
}
|
|
retcode = netrc_scan_file(
|
|
data, store, hostname, user, filealloc, pcreds);
|
|
curlx_free(filealloc);
|
|
#ifdef _WIN32
|
|
if(retcode == NETRC_FILE_MISSING) {
|
|
/* fallback to the old-style "_netrc" file */
|
|
filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
|
|
if(!filealloc) {
|
|
curlx_free(homea);
|
|
return NETRC_OUT_OF_MEMORY;
|
|
}
|
|
retcode = netrc_scan_file(
|
|
data, store, hostname, user, filealloc, pcreds);
|
|
curlx_free(filealloc);
|
|
}
|
|
#endif
|
|
curlx_free(homea);
|
|
}
|
|
else
|
|
retcode = netrc_scan_file(
|
|
data, store, hostname, user, netrcfile, pcreds);
|
|
|
|
out:
|
|
if(retcode)
|
|
Curl_creds_unlink(pcreds);
|
|
return retcode;
|
|
}
|
|
|
|
void Curl_netrc_init(struct store_netrc *store)
|
|
{
|
|
curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE);
|
|
store->loaded = FALSE;
|
|
}
|
|
void Curl_netrc_cleanup(struct store_netrc *store)
|
|
{
|
|
curlx_dyn_free(&store->filebuf);
|
|
store->loaded = FALSE;
|
|
}
|
|
|
|
const char *Curl_netrc_strerror(NETRCcode ret)
|
|
{
|
|
switch(ret) {
|
|
default:
|
|
return ""; /* not a legit error */
|
|
case NETRC_FILE_MISSING:
|
|
return "no such file";
|
|
case NETRC_NO_MATCH:
|
|
return "no matching entry";
|
|
case NETRC_OUT_OF_MEMORY:
|
|
return "out of memory";
|
|
case NETRC_SYNTAX_ERROR:
|
|
return "syntax error";
|
|
}
|
|
/* never reached */
|
|
}
|
|
|
|
#endif /* !CURL_DISABLE_NETRC */
|