Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/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/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
0
#define MAX_NETRC_LINE  16384
77
489k
#define MAX_NETRC_FILE  (128 * 1024)
78
0
#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
0
{
140
0
  curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN);
141
0
  lexer->data = data;
142
0
  lexer->content = lexer->pos = content;
143
0
}
144
145
static void netrc_lexer_cleanup(struct netrc_lexer *lexer)
146
0
{
147
0
  lexer->content = lexer->pos = NULL;
148
0
  lexer->data = NULL;
149
0
  curlx_dyn_free(&lexer->literal);
150
0
}
151
152
static void netrc_skip_blanks(struct netrc_lexer *lexer)
153
0
{
154
0
  const char *s = lexer->pos;
155
0
  while(*s) {
156
0
    curlx_str_passblanks(&s);
157
0
    while(*s == '\r')
158
0
      ++s;
159
0
    if(*s == '\n') {
160
0
      ++s;
161
0
    }
162
0
    else
163
0
      break;
164
0
  }
165
0
  lexer->pos = s;
166
0
}
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
0
      rc = NETRC_OK;
227
0
      goto out;
228
0
    }
229
0
    result = curlx_dyn_addn(&lexer->literal, &c, 1);
230
0
    if(result) {
231
0
      rc = curl2netrc(result);
232
0
      goto out;
233
0
    }
234
0
    ++s;
235
0
  }
236
0
out:
237
0
  lexer->pos = s;
238
0
  return rc;
239
0
}
240
241
static void netrc_lexer_push(struct netrc_lexer *lexer)
242
0
{
243
0
  lexer->pushed = TRUE;
244
0
}
245
246
static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer,
247
                                  bool want_literal)
248
0
{
249
0
  const char *s = lexer->pos, *start;
250
0
  NETRCcode rc = NETRC_OK;
251
0
  size_t slen;
252
0
  CURLcode result;
253
254
0
  if(lexer->pushed) {
255
0
    lexer->pushed = FALSE;
256
0
    goto out;
257
0
  }
258
259
0
  curlx_dyn_reset(&lexer->literal);
260
0
  netrc_skip_blanks(lexer);
261
0
  s = lexer->pos;
262
263
0
  switch(*s) {
264
0
  case 0:
265
0
    lexer->token = NETRC_TOK_EOF;
266
0
    break;
267
0
  case '\"':
268
0
    rc = netrc_lexer_quoted(lexer);
269
0
    lexer->token = NETRC_TOK_LITERAL;
270
0
    s = lexer->pos;
271
0
    break;
272
0
  default:
273
    /* unquoted token */
274
0
    start = s;
275
0
    while(*s && !ISBLANK(*s) && !ISNEWLINE(*s))
276
0
      ++s;
277
0
    slen = s - start;
278
0
    if(!slen) {
279
0
      rc = NETRC_SYNTAX_ERROR;
280
0
    }
281
0
    if(want_literal) {
282
0
      lexer->token = NETRC_TOK_LITERAL;
283
0
      result = curlx_dyn_addn(&lexer->literal, start, slen);
284
0
      rc = curl2netrc(result);
285
0
    }
286
0
    else if((slen == 7) && curl_strnequal(start, "machine", slen)) {
287
0
      lexer->token = NETRC_TOK_MACHINE;
288
0
    }
289
0
    else if((slen == 7) && curl_strnequal(start, "default", slen)) {
290
0
      lexer->token = NETRC_TOK_DEFAULT;
291
0
    }
292
0
    else if((slen == 7) && curl_strnequal(start, "account", slen)) {
293
0
      lexer->token = NETRC_TOK_ACCOUNT;
294
0
    }
295
0
    else if((slen == 5) && curl_strnequal(start, "login", slen)) {
296
0
      lexer->token = NETRC_TOK_LOGIN;
297
0
    }
298
0
    else if((slen == 8) && curl_strnequal(start, "password", slen)) {
299
0
      lexer->token = NETRC_TOK_PASSWD;
300
0
    }
301
0
    else if((slen == 6) && curl_strnequal(start, "macdef", slen)) {
302
0
      lexer->token = NETRC_TOK_MACDEF;
303
0
    }
304
0
    else {
305
0
      lexer->token = NETRC_TOK_JUNK;
306
0
    }
307
0
    break;
308
0
  }
309
310
0
out:
311
#if NETRC_DEBUG
312
  CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d",
313
             netrc_tokenstr(lexer->token),
314
             curlx_dyn_ptr(&lexer->literal), rc);
315
#endif
316
0
  lexer->pos = s;
317
0
  return rc;
318
0
}
319
320
struct netrc_scanner {
321
  struct netrc_lexer lexer;
322
  const char *hostname; /* non-NULL, machine to scan for */
323
  const char *user; /* maybe NULL, login to scan for */
324
  char *login;
325
  char *passwd;
326
  struct Curl_creds *creds;
327
  bool matches_host;
328
  bool found;
329
};
330
331
static void netrc_scan_reset(struct netrc_scanner *sc)
332
0
{
333
0
  curlx_safefree(sc->login);
334
0
  curlx_safefree(sc->passwd);
335
0
  sc->matches_host = FALSE;
336
0
}
337
338
static void netrc_scan_init(struct netrc_scanner *sc,
339
                            struct Curl_easy *data,
340
                            const char *content,
341
                            const char *hostname,
342
                            const char *user)
343
0
{
344
0
  memset(sc, 0, sizeof(*sc));
345
0
  netrc_lexer_init(&sc->lexer, data, content);
346
0
  sc->hostname = hostname;
347
0
  sc->user = (user && user[0]) ? user : NULL;
348
0
  netrc_scan_reset(sc);
349
0
}
350
351
static void netrc_scan_cleanup(struct netrc_scanner *sc)
352
0
{
353
0
  netrc_scan_reset(sc);
354
0
  sc->hostname = NULL;
355
0
  sc->user = NULL;
356
0
  Curl_creds_unlink(&sc->creds);
357
0
  netrc_lexer_cleanup(&sc->lexer);
358
0
}
359
360
static NETRCcode netrc_scan_literal(struct netrc_scanner *sc,
361
                                    char **pdest)
362
0
{
363
0
  NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE);
364
0
  if(!rc) {
365
0
    if(sc->lexer.token == NETRC_TOK_LITERAL) {
366
0
      if(pdest && sc->matches_host) {
367
0
        curlx_free(*pdest);
368
0
        *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal));
369
0
        if(!*pdest)
370
0
          rc = NETRC_OUT_OF_MEMORY;
371
0
      }
372
0
    }
373
0
    else
374
0
      netrc_lexer_push(&sc->lexer);
375
0
  }
376
0
  return rc;
377
0
}
378
379
static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc)
380
0
{
381
0
  NETRCcode rc = NETRC_OK;
382
#if NETRC_DEBUG
383
  CURL_TRC_M(sc->lexer.data,
384
             "[NETRC] entry matches_host=%d, login='%s', passwd='%s'",
385
             sc->matches_host, sc->login, sc->passwd);
386
#endif
387
0
  if(sc->matches_host) {
388
0
    if(sc->login) {
389
0
      if(sc->user) {
390
0
        if(Curl_timestrcmp(sc->user, sc->login))
391
0
          goto out;
392
        /* We look for a specific user,
393
         * entry is only interesting with password */
394
0
        sc->found = !!sc->passwd;
395
0
      }
396
0
      else {
397
0
        sc->found = TRUE;
398
0
      }
399
0
    }
400
0
    else if(sc->passwd) {
401
      /* found a passwd that applies to any user */
402
0
      sc->found = TRUE;
403
0
    }
404
0
    else {
405
      /* entry has nothing interesting */
406
0
    }
407
0
    if(sc->found) {
408
#if NETRC_DEBUG
409
      CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found");
410
#endif
411
0
      if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd,
412
0
                           NULL, NULL, NULL, CREDS_NETRC, &sc->creds))
413
0
        rc = NETRC_OUT_OF_MEMORY;
414
0
    }
415
0
  }
416
0
out:
417
0
  netrc_scan_reset(sc);
418
0
  return rc;
419
0
}
420
421
static NETRCcode netrc_scan(struct Curl_easy *data,
422
                            const char *content,
423
                            const char *hostname,
424
                            const char *user,
425
                            struct Curl_creds **pcreds)
426
0
{
427
0
  struct netrc_scanner sc;
428
0
  NETRCcode rc = NETRC_OK;
429
430
0
  Curl_creds_unlink(pcreds);
431
0
  netrc_scan_init(&sc, data, content, hostname, user);
432
433
0
  while(!rc && !sc.found) {
434
0
    rc = netrc_lexer_next(&sc.lexer, FALSE);
435
0
    if(!rc) {
436
      /* Does this token end any previous entry? */
437
0
      switch(sc.lexer.token) {
438
0
      case NETRC_TOK_EOF:
439
0
      case NETRC_TOK_MACHINE:
440
0
      case NETRC_TOK_DEFAULT:
441
0
      case NETRC_TOK_MACDEF:
442
0
        rc = netrc_scan_end_entry(&sc);
443
0
        if(rc || sc.found)
444
0
          goto out;
445
0
        break;
446
0
      default:
447
0
        break;
448
0
      }
449
450
0
      switch(sc.lexer.token) {
451
0
      case NETRC_TOK_EOF:
452
0
        goto out;
453
0
      case NETRC_TOK_MACHINE:
454
0
        rc = netrc_lexer_next(&sc.lexer, TRUE);
455
0
        if(!rc) {
456
0
          if(sc.lexer.token == NETRC_TOK_LITERAL) {
457
0
            sc.matches_host = curl_strequal(
458
0
              sc.hostname, curlx_dyn_ptr(&sc.lexer.literal));
459
0
          }
460
0
          else {
461
0
            sc.matches_host = FALSE;
462
0
            netrc_lexer_push(&sc.lexer);
463
0
          }
464
0
        }
465
0
        break;
466
0
      case NETRC_TOK_DEFAULT:
467
0
        sc.matches_host = TRUE;
468
0
        break;
469
0
      case NETRC_TOK_ACCOUNT:
470
0
        rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */
471
0
        break;
472
0
      case NETRC_TOK_LOGIN:
473
0
        rc = netrc_scan_literal(&sc, &sc.login);
474
0
        break;
475
0
      case NETRC_TOK_PASSWD:
476
0
        rc = netrc_scan_literal(&sc, &sc.passwd);
477
0
        break;
478
0
      case NETRC_TOK_MACDEF:
479
0
        netrc_skip_to_empty_line(&sc.lexer);
480
0
        break;
481
0
      case NETRC_TOK_LITERAL:
482
0
      case NETRC_TOK_JUNK:
483
0
      default:
484
        /* skip this */
485
0
        break;
486
0
      }
487
0
    }
488
0
  }
489
490
0
out:
491
0
  if(!rc) {
492
0
    if(sc.creds)
493
0
      Curl_creds_link(pcreds, sc.creds);
494
0
    else
495
0
      rc = NETRC_NO_MATCH;
496
0
  }
497
0
  netrc_scan_cleanup(&sc);
498
0
  return rc;
499
0
}
500
501
static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
502
146k
{
503
146k
  NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
504
146k
  FILE *file = curlx_fopen(filename, FOPEN_READTEXT);
505
506
146k
  curlx_dyn_reset(filebuf);
507
146k
  if(file) {
508
0
    curlx_struct_stat stat;
509
0
    if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
510
0
      CURLcode result = CURLE_OK;
511
0
      bool eof;
512
0
      struct dynbuf linebuf;
513
0
      curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
514
0
      ret = NETRC_OK;
515
0
      do {
516
0
        const char *line;
517
        /* Curl_get_line always returns lines ending with a newline */
518
0
        result = Curl_get_line(&linebuf, file, &eof);
519
0
        if(!result) {
520
0
          line = curlx_dyn_ptr(&linebuf);
521
          /* skip comments on load */
522
0
          curlx_str_passblanks(&line);
523
0
          if(*line == '#')
524
0
            continue;
525
0
          result = curlx_dyn_add(filebuf, line);
526
0
        }
527
0
        if(result) {
528
0
          curlx_dyn_free(filebuf);
529
0
          ret = curl2netrc(result);
530
0
          break;
531
0
        }
532
0
      } while(!eof);
533
0
      curlx_dyn_free(&linebuf);
534
0
    }
535
0
    curlx_fclose(file);
536
0
  }
537
146k
  return ret;
538
146k
}
539
540
static NETRCcode netrc_scan_file(struct Curl_easy *data,
541
                                 struct store_netrc *store,
542
                                 const char *hostname,
543
                                 const char *user,
544
                                 const char *netrcfile,
545
                                 struct Curl_creds **pcreds)
546
146k
{
547
146k
  struct dynbuf *filebuf = &store->filebuf;
548
549
146k
  if(!store->loaded || strcmp(netrcfile, store->filename)) {
550
146k
    NETRCcode ret;
551
146k
    store->loaded = FALSE;
552
146k
    ret = file2memory(netrcfile, filebuf);
553
146k
    if(ret) {
554
146k
      CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile);
555
146k
      return ret;
556
146k
    }
557
0
    curlx_free(store->filename);
558
0
    store->filename = curlx_strdup(netrcfile);
559
0
    if(!store->filename) {
560
0
      curlx_dyn_reset(&store->filebuf);
561
0
      return NETRC_OUT_OF_MEMORY;
562
0
    }
563
0
    store->loaded = TRUE;
564
0
  }
565
566
0
  return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds);
567
146k
}
568
569
/*
570
 * @unittest: 1304
571
 *
572
 * *loginp and *passwordp MUST be allocated if they are not NULL when passed
573
 * in.
574
 */
575
NETRCcode Curl_netrc_scan(struct Curl_easy *data,
576
                          struct store_netrc *store,
577
                          const char *hostname,
578
                          const char *user,
579
                          const char *netrcfile,
580
                          struct Curl_creds **pcreds)
581
146k
{
582
146k
  NETRCcode retcode = NETRC_OK;
583
584
146k
  CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'",
585
146k
             netrcfile, hostname, user);
586
146k
  Curl_creds_unlink(pcreds);
587
146k
  if(!netrcfile) {
588
146k
    char *home = NULL;
589
146k
    char *homea = NULL;
590
146k
    char *filealloc = NULL;
591
146k
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
592
146k
    char pwbuf[1024];
593
146k
#endif
594
146k
    filealloc = curl_getenv("NETRC");
595
146k
    if(!filealloc) {
596
146k
      homea = curl_getenv("HOME"); /* portable environment reader */
597
146k
      if(homea) {
598
146k
        home = homea;
599
146k
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
600
146k
      }
601
0
      else {
602
0
        struct passwd pw, *pw_res;
603
0
        if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
604
0
           pw_res) {
605
0
          home = pw.pw_dir;
606
0
        }
607
#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
608
      }
609
      else {
610
        struct passwd *pw;
611
        pw = getpwuid(geteuid());
612
        if(pw) {
613
          home = pw->pw_dir;
614
        }
615
#elif defined(_WIN32)
616
      }
617
      else {
618
        homea = curl_getenv("USERPROFILE");
619
        if(homea) {
620
          home = homea;
621
        }
622
#endif
623
0
      }
624
625
146k
      if(!home)
626
0
        return NETRC_FILE_MISSING; /* no home directory found (or possibly out
627
                                      of memory) */
628
629
146k
      filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
630
146k
      if(!filealloc) {
631
0
        curlx_free(homea);
632
0
        retcode = NETRC_OUT_OF_MEMORY;
633
0
        goto out;
634
0
      }
635
146k
    }
636
146k
    retcode = netrc_scan_file(
637
146k
      data, store, hostname, user, filealloc, pcreds);
638
146k
    curlx_free(filealloc);
639
#ifdef _WIN32
640
    if(retcode == NETRC_FILE_MISSING) {
641
      /* fallback to the old-style "_netrc" file */
642
      filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
643
      if(!filealloc) {
644
        curlx_free(homea);
645
        return NETRC_OUT_OF_MEMORY;
646
      }
647
      retcode = netrc_scan_file(
648
        data, store, hostname, user, filealloc, pcreds);
649
      curlx_free(filealloc);
650
    }
651
#endif
652
146k
    curlx_free(homea);
653
146k
  }
654
0
  else
655
0
    retcode = netrc_scan_file(
656
0
      data, store, hostname, user, netrcfile, pcreds);
657
658
146k
out:
659
146k
  if(retcode)
660
146k
    Curl_creds_unlink(pcreds);
661
146k
  return retcode;
662
146k
}
663
664
void Curl_netrc_init(struct store_netrc *store)
665
489k
{
666
489k
  curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE);
667
489k
  store->loaded = FALSE;
668
489k
  store->filename = NULL;
669
489k
}
670
void Curl_netrc_cleanup(struct store_netrc *store)
671
629k
{
672
629k
  curlx_dyn_free(&store->filebuf);
673
629k
  curlx_safefree(store->filename);
674
629k
  store->loaded = FALSE;
675
629k
}
676
677
const char *Curl_netrc_strerror(NETRCcode ret)
678
0
{
679
0
  switch(ret) {
680
0
  default:
681
0
    return ""; /* not a legit error */
682
0
  case NETRC_FILE_MISSING:
683
0
    return "no such file";
684
0
  case NETRC_NO_MATCH:
685
0
    return "no matching entry";
686
0
  case NETRC_OUT_OF_MEMORY:
687
0
    return "out of memory";
688
0
  case NETRC_SYNTAX_ERROR:
689
0
    return "syntax error";
690
0
  }
691
  /* never reached */
692
0
}
693
694
#endif /* !CURL_DISABLE_NETRC */