Coverage Report

Created: 2026-09-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/netrc.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifndef CURL_DISABLE_NETRC
27
28
#ifdef HAVE_PWD_H
29
#ifdef __AMIGA__
30
#undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */
31
#endif
32
#include <pwd.h>
33
#ifdef __AMIGA__
34
#define __NO_NET_API
35
#endif
36
#endif
37
38
#include "netrc.h"
39
#include "urldata.h"
40
#include "creds.h"
41
#include "curl_trc.h"
42
#include "strcase.h"
43
#include "curl_get_line.h"
44
#include "curlx/win32-fopen.h"
45
#include "curlx/strparse.h"
46
47
48
/* .netrc is not really a standard. The GNU definition can be found here:
49
 * https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
50
 * This gives grammar like:
51
 *
52
 * LITERAL := \S+ | QUOTED
53
 * QUOTED  := "(\\[rnt\]|[^"])*"
54
 * ANYTHING := .
55
 * EMPTY_LINE := \r*\n\r*\n
56
 * MACHINE := machine         # case-insensitive
57
 * LOGIN   := login           # case-insensitive
58
 * PASSWD  := password        # case-insensitive
59
 * ACCOUNT := account         # case-insensitive
60
 * MACDEF  := macdef          # case-insensitive
61
 * DEFAULT := default         # case-insensitive
62
 *
63
 * MACRO   := MACDEF ANYTHING* EMPTY_LINE
64
 * JUNK    := LITERAL
65
 * LKEY     := ( LOGIN | PASSWD | ACCOUNT ) LITERAL
66
 * MENTRY   := MACHINE LITERAL LKEY*
67
 * DENTRY   := DEFAULT LKEY*
68
 * NETRC   := (MENTRY | DENTRY | MACRO | JUNK )* EOF
69
 *
70
 * Tokens are separated by whitespace or newlines. which have otherwise
71
 * no special meaning, apart from the empty line ending a MACRO.
72
 *
73
 * Parsing is not strict, unmatched LITERALs are ignored
74
 */
75
76
12
#define MAX_NETRC_LINE  16384
77
26.6k
#define MAX_NETRC_FILE  (128 * 1024)
78
12
#define MAX_NETRC_TOKEN 4096
79
80
#define NETRC_DEBUG   0
81
82
/* convert a dynbuf call CURLcode error to a NETRCcode error */
83
#define curl2netrc(r)                                  \
84
0
  ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \
85
0
   NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR))
86
87
typedef enum {
88
  NETRC_TOK_EOF,
89
  NETRC_TOK_LITERAL,
90
  NETRC_TOK_MACHINE,
91
  NETRC_TOK_DEFAULT,
92
  NETRC_TOK_ACCOUNT,
93
  NETRC_TOK_LOGIN,
94
  NETRC_TOK_PASSWD,
95
  NETRC_TOK_MACDEF,
96
  NETRC_TOK_JUNK
97
} curl_netrc_token;
98
99
struct netrc_lexer {
100
  struct Curl_easy *data;
101
  const char *content;
102
  const char *pos;
103
  struct dynbuf literal;
104
  curl_netrc_token token;
105
  bool pushed;
106
};
107
108
#if NETRC_DEBUG
109
static const char *netrc_tokenstr(curl_netrc_token token)
110
{
111
  switch(token) {
112
  case NETRC_TOK_EOF:
113
    return "[EOF]";
114
  case NETRC_TOK_LITERAL:
115
    return "[LITERAL]";
116
  case NETRC_TOK_MACHINE:
117
    return "[MACHINE]";
118
  case NETRC_TOK_DEFAULT:
119
    return "[DEFAULT]";
120
  case NETRC_TOK_ACCOUNT:
121
    return "[ACCOUNT]";
122
  case NETRC_TOK_LOGIN:
123
    return "[LOGIN]";
124
  case NETRC_TOK_PASSWD:
125
    return "[PASSWORD]";
126
  case NETRC_TOK_MACDEF:
127
    return "[MACDEF]";
128
  case NETRC_TOK_JUNK:
129
    return "[JUNK]";
130
  default:
131
    return "[???]";
132
  }
133
}
134
#endif
135
136
static void netrc_lexer_init(struct netrc_lexer *lexer,
137
                             struct Curl_easy *data,
138
                             const char *content)
139
12
{
140
12
  curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN);
141
12
  lexer->data = data;
142
12
  lexer->content = lexer->pos = content;
143
12
}
144
145
static void netrc_lexer_cleanup(struct netrc_lexer *lexer)
146
12
{
147
12
  lexer->content = lexer->pos = NULL;
148
12
  lexer->data = NULL;
149
12
  curlx_dyn_free(&lexer->literal);
150
12
}
151
152
static void netrc_skip_blanks(struct netrc_lexer *lexer)
153
12
{
154
12
  const char *s = lexer->pos;
155
24
  while(*s) {
156
12
    curlx_str_passblanks(&s);
157
12
    while(*s == '\r')
158
0
      ++s;
159
12
    if(*s == '\n') {
160
12
      ++s;
161
12
    }
162
0
    else
163
0
      break;
164
12
  }
165
12
  lexer->pos = s;
166
12
}
167
168
static void netrc_skip_to_empty_line(struct netrc_lexer *lexer)
169
0
{
170
0
  const char *s = lexer->pos;
171
0
  while(*s) {
172
0
    if(*s == '\r')
173
0
      ++s;
174
0
    else if(*s == '\n') {
175
0
      ++s;
176
0
      while(*s == '\r')
177
0
        ++s;
178
0
      if(*s == '\n')
179
0
        goto out;
180
0
    }
181
0
    else
182
0
      ++s;
183
0
  }
184
0
out:
185
0
  lexer->pos = s;
186
0
}
187
188
/*
189
 * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t
190
 * escape sequences. Advances *tok_endp past the closing '"'.
191
 *
192
 * Returns NETRC_OK or error.
193
 */
194
static NETRCcode netrc_lexer_quoted(struct netrc_lexer *lexer)
195
0
{
196
0
  NETRCcode rc = NETRC_SYNTAX_ERROR;
197
0
  const char *s = lexer->pos;
198
0
  bool escape = FALSE;
199
0
  CURLcode result;
200
201
0
  DEBUGASSERT(*s == '\"');
202
0
  ++s; /* pass the leading quote */
203
0
  while(*s) {
204
0
    char c = *s;
205
0
    if(escape) {
206
0
      escape = FALSE;
207
0
      switch(c) {
208
0
      case 'n':
209
0
        c = '\n';
210
0
        break;
211
0
      case 'r':
212
0
        c = '\r';
213
0
        break;
214
0
      case 't':
215
0
        c = '\t';
216
0
        break;
217
0
      }
218
0
    }
219
0
    else if(c == '\\') {
220
0
      escape = TRUE;
221
0
      ++s;
222
0
      continue;
223
0
    }
224
0
    else if(c == '\"') {
225
0
      ++s; /* pass the ending quote */
226
      /* Ensure even an empty literal has a terminating NUL. */
227
0
      result = curlx_dyn_addn(&lexer->literal, "", 0);
228
0
      rc = curl2netrc(result);
229
0
      goto out;
230
0
    }
231
0
    result = curlx_dyn_addn(&lexer->literal, &c, 1);
232
0
    if(result) {
233
0
      rc = curl2netrc(result);
234
0
      goto out;
235
0
    }
236
0
    ++s;
237
0
  }
238
0
out:
239
0
  lexer->pos = s;
240
0
  return rc;
241
0
}
242
243
static void netrc_lexer_push(struct netrc_lexer *lexer)
244
0
{
245
0
  lexer->pushed = TRUE;
246
0
}
247
248
static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer,
249
                                  bool want_literal)
250
12
{
251
12
  const char *s = lexer->pos, *start;
252
12
  NETRCcode rc = NETRC_OK;
253
12
  size_t slen;
254
12
  CURLcode result;
255
256
12
  if(lexer->pushed) {
257
0
    lexer->pushed = FALSE;
258
0
    goto out;
259
0
  }
260
261
12
  curlx_dyn_reset(&lexer->literal);
262
12
  netrc_skip_blanks(lexer);
263
12
  s = lexer->pos;
264
265
12
  switch(*s) {
266
12
  case 0:
267
12
    lexer->token = NETRC_TOK_EOF;
268
12
    break;
269
0
  case '\"':
270
0
    rc = netrc_lexer_quoted(lexer);
271
0
    lexer->token = NETRC_TOK_LITERAL;
272
0
    s = lexer->pos;
273
0
    break;
274
0
  default:
275
    /* unquoted token */
276
0
    start = s;
277
0
    while(*s && !ISBLANK(*s) && !ISNEWLINE(*s))
278
0
      ++s;
279
0
    slen = s - start;
280
0
    if(!slen) {
281
0
      rc = NETRC_SYNTAX_ERROR;
282
0
    }
283
0
    if(want_literal) {
284
0
      lexer->token = NETRC_TOK_LITERAL;
285
0
      result = curlx_dyn_addn(&lexer->literal, start, slen);
286
0
      rc = curl2netrc(result);
287
0
    }
288
0
    else if((slen == 7) && curl_strnequal(start, "machine", slen)) {
289
0
      lexer->token = NETRC_TOK_MACHINE;
290
0
    }
291
0
    else if((slen == 7) && curl_strnequal(start, "default", slen)) {
292
0
      lexer->token = NETRC_TOK_DEFAULT;
293
0
    }
294
0
    else if((slen == 7) && curl_strnequal(start, "account", slen)) {
295
0
      lexer->token = NETRC_TOK_ACCOUNT;
296
0
    }
297
0
    else if((slen == 5) && curl_strnequal(start, "login", slen)) {
298
0
      lexer->token = NETRC_TOK_LOGIN;
299
0
    }
300
0
    else if((slen == 8) && curl_strnequal(start, "password", slen)) {
301
0
      lexer->token = NETRC_TOK_PASSWD;
302
0
    }
303
0
    else if((slen == 6) && curl_strnequal(start, "macdef", slen)) {
304
0
      lexer->token = NETRC_TOK_MACDEF;
305
0
    }
306
0
    else {
307
0
      lexer->token = NETRC_TOK_JUNK;
308
0
    }
309
0
    break;
310
12
  }
311
312
12
out:
313
#if NETRC_DEBUG
314
  CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d",
315
             netrc_tokenstr(lexer->token),
316
             curlx_dyn_ptr(&lexer->literal), rc);
317
#endif
318
12
  lexer->pos = s;
319
12
  return rc;
320
12
}
321
322
struct netrc_scanner {
323
  struct netrc_lexer lexer;
324
  const char *hostname; /* non-NULL, machine to scan for */
325
  const char *user; /* maybe NULL, login to scan for */
326
  char *login;
327
  char *passwd;
328
  struct Curl_creds *creds;
329
  bool matches_host;
330
  bool found;
331
};
332
333
static void netrc_scan_reset(struct netrc_scanner *sc)
334
36
{
335
36
  curlx_safefree(sc->login);
336
36
  curlx_safefree(sc->passwd);
337
36
  sc->matches_host = FALSE;
338
36
}
339
340
static void netrc_scan_init(struct netrc_scanner *sc,
341
                            struct Curl_easy *data,
342
                            const char *content,
343
                            const char *hostname,
344
                            const char *user)
345
12
{
346
12
  memset(sc, 0, sizeof(*sc));
347
12
  netrc_lexer_init(&sc->lexer, data, content);
348
12
  sc->hostname = hostname;
349
12
  sc->user = (user && user[0]) ? user : NULL;
350
12
  netrc_scan_reset(sc);
351
12
}
352
353
static void netrc_scan_cleanup(struct netrc_scanner *sc)
354
12
{
355
12
  netrc_scan_reset(sc);
356
12
  sc->hostname = NULL;
357
12
  sc->user = NULL;
358
12
  Curl_creds_unlink(&sc->creds);
359
12
  netrc_lexer_cleanup(&sc->lexer);
360
12
}
361
362
static NETRCcode netrc_scan_literal(struct netrc_scanner *sc,
363
                                    char **pdest)
364
0
{
365
0
  NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE);
366
0
  if(!rc) {
367
0
    if(sc->lexer.token == NETRC_TOK_LITERAL) {
368
0
      if(pdest && sc->matches_host) {
369
0
        curlx_free(*pdest);
370
0
        *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal));
371
0
        if(!*pdest)
372
0
          rc = NETRC_OUT_OF_MEMORY;
373
0
      }
374
0
    }
375
0
    else
376
0
      netrc_lexer_push(&sc->lexer);
377
0
  }
378
0
  return rc;
379
0
}
380
381
static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc)
382
12
{
383
12
  NETRCcode rc = NETRC_OK;
384
#if NETRC_DEBUG
385
  CURL_TRC_M(sc->lexer.data,
386
             "[NETRC] entry matches_host=%d, login='%s', passwd='%s'",
387
             sc->matches_host, sc->login, sc->passwd);
388
#endif
389
12
  if(sc->matches_host) {
390
0
    if(sc->login) {
391
0
      if(sc->user) {
392
0
        if(Curl_timestrcmp(sc->user, sc->login))
393
0
          goto out;
394
        /* We look for a specific user,
395
         * entry is only interesting with password */
396
0
        sc->found = !!sc->passwd;
397
0
      }
398
0
      else {
399
0
        sc->found = TRUE;
400
0
      }
401
0
    }
402
0
    else if(sc->passwd) {
403
      /* found a passwd that applies to any user */
404
0
      sc->found = TRUE;
405
0
    }
406
0
    else {
407
      /* entry has nothing interesting */
408
0
    }
409
0
    if(sc->found) {
410
#if NETRC_DEBUG
411
      CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found");
412
#endif
413
0
      if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd,
414
0
                           NULL, NULL, NULL, CREDS_NETRC, &sc->creds))
415
0
        rc = NETRC_OUT_OF_MEMORY;
416
0
    }
417
0
  }
418
12
out:
419
12
  netrc_scan_reset(sc);
420
12
  return rc;
421
12
}
422
423
static NETRCcode netrc_scan(struct Curl_easy *data,
424
                            const char *content,
425
                            const char *hostname,
426
                            const char *user,
427
                            struct Curl_creds **pcreds)
428
12
{
429
12
  struct netrc_scanner sc;
430
12
  NETRCcode rc = NETRC_OK;
431
432
12
  Curl_creds_unlink(pcreds);
433
12
  netrc_scan_init(&sc, data, content, hostname, user);
434
435
12
  while(!rc && !sc.found) {
436
12
    rc = netrc_lexer_next(&sc.lexer, FALSE);
437
12
    if(!rc) {
438
      /* Does this token end any previous entry? */
439
12
      switch(sc.lexer.token) {
440
12
      case NETRC_TOK_EOF:
441
12
      case NETRC_TOK_MACHINE:
442
12
      case NETRC_TOK_DEFAULT:
443
12
      case NETRC_TOK_MACDEF:
444
12
        rc = netrc_scan_end_entry(&sc);
445
12
        if(rc || sc.found)
446
0
          goto out;
447
12
        break;
448
12
      default:
449
0
        break;
450
12
      }
451
452
12
      switch(sc.lexer.token) {
453
12
      case NETRC_TOK_EOF:
454
12
        goto out;
455
0
      case NETRC_TOK_MACHINE:
456
0
        rc = netrc_lexer_next(&sc.lexer, TRUE);
457
0
        if(!rc) {
458
0
          if(sc.lexer.token == NETRC_TOK_LITERAL) {
459
0
            sc.matches_host = curl_strequal(
460
0
              sc.hostname, curlx_dyn_ptr(&sc.lexer.literal));
461
0
          }
462
0
          else {
463
0
            sc.matches_host = FALSE;
464
0
            netrc_lexer_push(&sc.lexer);
465
0
          }
466
0
        }
467
0
        break;
468
0
      case NETRC_TOK_DEFAULT:
469
0
        sc.matches_host = TRUE;
470
0
        break;
471
0
      case NETRC_TOK_ACCOUNT:
472
0
        rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */
473
0
        break;
474
0
      case NETRC_TOK_LOGIN:
475
0
        rc = netrc_scan_literal(&sc, &sc.login);
476
0
        break;
477
0
      case NETRC_TOK_PASSWD:
478
0
        rc = netrc_scan_literal(&sc, &sc.passwd);
479
0
        break;
480
0
      case NETRC_TOK_MACDEF:
481
0
        netrc_skip_to_empty_line(&sc.lexer);
482
0
        break;
483
0
      case NETRC_TOK_LITERAL:
484
0
      case NETRC_TOK_JUNK:
485
0
      default:
486
        /* skip this */
487
0
        break;
488
12
      }
489
12
    }
490
12
  }
491
492
12
out:
493
12
  if(!rc) {
494
12
    if(sc.creds)
495
0
      Curl_creds_link(pcreds, sc.creds);
496
12
    else
497
12
      rc = NETRC_NO_MATCH;
498
12
  }
499
12
  netrc_scan_cleanup(&sc);
500
12
  return rc;
501
12
}
502
503
static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
504
12
{
505
12
  NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
506
12
  FILE *file = curlx_fopen(filename, FOPEN_READTEXT);
507
508
12
  curlx_dyn_reset(filebuf);
509
12
  if(file) {
510
12
    curlx_struct_stat stat;
511
12
    if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
512
12
      CURLcode result = CURLE_OK;
513
12
      bool eof;
514
12
      struct dynbuf linebuf;
515
12
      curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
516
12
      ret = NETRC_OK;
517
12
      do {
518
12
        const char *line;
519
        /* Curl_get_line always returns lines ending with a newline */
520
12
        result = Curl_get_line(&linebuf, file, &eof);
521
12
        if(!result) {
522
12
          line = curlx_dyn_ptr(&linebuf);
523
          /* skip comments on load */
524
12
          curlx_str_passblanks(&line);
525
12
          if(*line == '#')
526
0
            continue;
527
12
          result = curlx_dyn_add(filebuf, line);
528
12
        }
529
12
        if(result) {
530
0
          curlx_dyn_free(filebuf);
531
0
          ret = curl2netrc(result);
532
0
          break;
533
0
        }
534
12
      } while(!eof);
535
12
      curlx_dyn_free(&linebuf);
536
12
    }
537
12
    curlx_fclose(file);
538
12
  }
539
12
  return ret;
540
12
}
541
542
static NETRCcode netrc_scan_file(struct Curl_easy *data,
543
                                 struct store_netrc *store,
544
                                 const char *hostname,
545
                                 const char *user,
546
                                 const char *netrcfile,
547
                                 struct Curl_creds **pcreds)
548
12
{
549
12
  struct dynbuf *filebuf = &store->filebuf;
550
551
12
  if(!store->loaded || strcmp(netrcfile, store->filename)) {
552
12
    NETRCcode ret;
553
12
    store->loaded = FALSE;
554
12
    ret = file2memory(netrcfile, filebuf);
555
12
    if(ret) {
556
0
      CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile);
557
0
      return ret;
558
0
    }
559
12
    curlx_free(store->filename);
560
12
    store->filename = curlx_strdup(netrcfile);
561
12
    if(!store->filename) {
562
0
      curlx_dyn_reset(&store->filebuf);
563
0
      return NETRC_OUT_OF_MEMORY;
564
0
    }
565
12
    store->loaded = TRUE;
566
12
  }
567
12
  if(!curlx_dyn_len(filebuf))
568
0
    return NETRC_NO_MATCH;
569
570
12
  return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds);
571
12
}
572
573
/*
574
 * @unittest: 1304
575
 *
576
 * *loginp and *passwordp MUST be allocated if they are not NULL when passed
577
 * in.
578
 */
579
NETRCcode Curl_netrc_scan(struct Curl_easy *data,
580
                          struct store_netrc *store,
581
                          const char *hostname,
582
                          const char *user,
583
                          const char *netrcfile,
584
                          struct Curl_creds **pcreds)
585
12
{
586
12
  NETRCcode retcode = NETRC_OK;
587
588
12
  CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'",
589
12
             netrcfile, hostname, user);
590
12
  Curl_creds_unlink(pcreds);
591
12
  if(!netrcfile) {
592
0
    char *home = NULL;
593
0
    char *homea = NULL;
594
0
    char *filealloc = NULL;
595
0
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
596
0
    char pwbuf[1024];
597
0
#endif
598
0
    filealloc = curl_getenv("NETRC");
599
0
    if(!filealloc) {
600
0
      homea = curl_getenv("HOME"); /* portable environment reader */
601
0
      if(homea) {
602
0
        home = homea;
603
0
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
604
0
      }
605
0
      else {
606
0
        struct passwd pw, *pw_res;
607
0
        if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
608
0
           pw_res) {
609
0
          home = pw.pw_dir;
610
0
        }
611
#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
612
      }
613
      else {
614
        struct passwd *pw;
615
        pw = getpwuid(geteuid());
616
        if(pw) {
617
          home = pw->pw_dir;
618
        }
619
#elif defined(_WIN32)
620
      }
621
      else {
622
        homea = curl_getenv("USERPROFILE");
623
        if(homea) {
624
          home = homea;
625
        }
626
#endif
627
0
      }
628
629
0
      if(!home)
630
0
        return NETRC_FILE_MISSING; /* no home directory found (or possibly out
631
                                      of memory) */
632
633
0
      filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
634
0
      if(!filealloc) {
635
0
        curlx_free(homea);
636
0
        retcode = NETRC_OUT_OF_MEMORY;
637
0
        goto out;
638
0
      }
639
0
    }
640
0
    retcode = netrc_scan_file(
641
0
      data, store, hostname, user, filealloc, pcreds);
642
0
    curlx_free(filealloc);
643
#ifdef _WIN32
644
    if(retcode == NETRC_FILE_MISSING) {
645
      /* fallback to the old-style "_netrc" file */
646
      filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
647
      if(!filealloc) {
648
        curlx_free(homea);
649
        return NETRC_OUT_OF_MEMORY;
650
      }
651
      retcode = netrc_scan_file(
652
        data, store, hostname, user, filealloc, pcreds);
653
      curlx_free(filealloc);
654
    }
655
#endif
656
0
    curlx_free(homea);
657
0
  }
658
12
  else
659
12
    retcode = netrc_scan_file(
660
12
      data, store, hostname, user, netrcfile, pcreds);
661
662
12
out:
663
12
  if(retcode)
664
12
    Curl_creds_unlink(pcreds);
665
12
  return retcode;
666
12
}
667
668
void Curl_netrc_init(struct store_netrc *store)
669
26.6k
{
670
26.6k
  curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE);
671
26.6k
  store->loaded = FALSE;
672
26.6k
  store->filename = NULL;
673
26.6k
}
674
void Curl_netrc_cleanup(struct store_netrc *store)
675
33.2k
{
676
33.2k
  curlx_dyn_free(&store->filebuf);
677
33.2k
  curlx_safefree(store->filename);
678
33.2k
  store->loaded = FALSE;
679
33.2k
}
680
681
const char *Curl_netrc_strerror(NETRCcode ret)
682
0
{
683
0
  switch(ret) {
684
0
  default:
685
0
    return ""; /* not a legit error */
686
0
  case NETRC_FILE_MISSING:
687
0
    return "no such file";
688
0
  case NETRC_NO_MATCH:
689
0
    return "no matching entry";
690
0
  case NETRC_OUT_OF_MEMORY:
691
0
    return "out of memory";
692
0
  case NETRC_SYNTAX_ERROR:
693
0
    return "syntax error";
694
0
  }
695
  /* never reached */
696
0
}
697
698
#endif /* !CURL_DISABLE_NETRC */