Coverage Report

Created: 2026-10-15 06:19

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
812
#define MAX_NETRC_LINE  16384
77
1.62k
#define MAX_NETRC_FILE  (128 * 1024)
78
808
#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
10.8k
  ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \
85
34
   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
808
{
140
808
  curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN);
141
808
  lexer->data = data;
142
808
  lexer->content = lexer->pos = content;
143
808
}
144
145
static void netrc_lexer_cleanup(struct netrc_lexer *lexer)
146
808
{
147
808
  lexer->content = lexer->pos = NULL;
148
808
  lexer->data = NULL;
149
808
  curlx_dyn_free(&lexer->literal);
150
808
}
151
152
static void netrc_skip_blanks(struct netrc_lexer *lexer)
153
41.3k
{
154
41.3k
  const char *s = lexer->pos;
155
61.8k
  while(*s) {
156
61.2k
    curlx_str_passblanks(&s);
157
66.1k
    while(*s == '\r')
158
4.85k
      ++s;
159
61.2k
    if(*s == '\n') {
160
20.5k
      ++s;
161
20.5k
    }
162
40.7k
    else
163
40.7k
      break;
164
61.2k
  }
165
41.3k
  lexer->pos = s;
166
41.3k
}
167
168
static void netrc_skip_to_empty_line(struct netrc_lexer *lexer)
169
675
{
170
675
  const char *s = lexer->pos;
171
1.53k
  while(*s) {
172
1.48k
    if(*s == '\r')
173
197
      ++s;
174
1.28k
    else if(*s == '\n') {
175
872
      ++s;
176
1.06k
      while(*s == '\r')
177
195
        ++s;
178
872
      if(*s == '\n')
179
625
        goto out;
180
872
    }
181
417
    else
182
417
      ++s;
183
1.48k
  }
184
675
out:
185
675
  lexer->pos = s;
186
675
}
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
866
{
196
866
  NETRCcode rc = NETRC_SYNTAX_ERROR;
197
866
  const char *s = lexer->pos;
198
866
  bool escape = FALSE;
199
866
  CURLcode result;
200
201
866
  DEBUGASSERT(*s == '\"');
202
866
  ++s; /* pass the leading quote */
203
169k
  while(*s) {
204
169k
    char c = *s;
205
169k
    if(escape) {
206
1.26k
      escape = FALSE;
207
1.26k
      switch(c) {
208
216
      case 'n':
209
216
        c = '\n';
210
216
        break;
211
251
      case 'r':
212
251
        c = '\r';
213
251
        break;
214
470
      case 't':
215
470
        c = '\t';
216
470
        break;
217
1.26k
      }
218
1.26k
    }
219
168k
    else if(c == '\\') {
220
1.26k
      escape = TRUE;
221
1.26k
      ++s;
222
1.26k
      continue;
223
1.26k
    }
224
166k
    else if(c == '\"') {
225
734
      ++s; /* pass the ending quote */
226
      /* Ensure even an empty literal has a null-terminator. */
227
734
      result = curlx_dyn_addn(&lexer->literal, "", 0);
228
734
      rc = curl2netrc(result);
229
734
      goto out;
230
734
    }
231
167k
    result = curlx_dyn_addn(&lexer->literal, &c, 1);
232
167k
    if(result) {
233
11
      rc = curl2netrc(result);
234
11
      goto out;
235
11
    }
236
167k
    ++s;
237
167k
  }
238
866
out:
239
866
  lexer->pos = s;
240
866
  return rc;
241
866
}
242
243
static void netrc_lexer_push(struct netrc_lexer *lexer)
244
27
{
245
27
  lexer->pushed = TRUE;
246
27
}
247
248
static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer,
249
                                  bool want_literal)
250
41.3k
{
251
41.3k
  const char *s = lexer->pos, *start;
252
41.3k
  NETRCcode rc = NETRC_OK;
253
41.3k
  size_t slen;
254
41.3k
  CURLcode result;
255
256
41.3k
  if(lexer->pushed) {
257
27
    lexer->pushed = FALSE;
258
27
    goto out;
259
27
  }
260
261
41.3k
  curlx_dyn_reset(&lexer->literal);
262
41.3k
  netrc_skip_blanks(lexer);
263
41.3k
  s = lexer->pos;
264
265
41.3k
  switch(*s) {
266
638
  case 0:
267
638
    lexer->token = NETRC_TOK_EOF;
268
638
    break;
269
866
  case '\"':
270
866
    rc = netrc_lexer_quoted(lexer);
271
866
    lexer->token = NETRC_TOK_LITERAL;
272
866
    s = lexer->pos;
273
866
    break;
274
39.8k
  default:
275
    /* unquoted token */
276
39.8k
    start = s;
277
1.17M
    while(*s && !ISBLANK(*s) && !ISNEWLINE(*s))
278
1.13M
      ++s;
279
39.8k
    slen = s - start;
280
39.8k
    if(!slen) {
281
3.05k
      rc = NETRC_SYNTAX_ERROR;
282
3.05k
    }
283
39.8k
    if(want_literal) {
284
10.1k
      lexer->token = NETRC_TOK_LITERAL;
285
10.1k
      result = curlx_dyn_addn(&lexer->literal, start, slen);
286
10.1k
      rc = curl2netrc(result);
287
10.1k
    }
288
29.7k
    else if((slen == 7) && curl_strnequal(start, "machine", slen)) {
289
523
      lexer->token = NETRC_TOK_MACHINE;
290
523
    }
291
29.1k
    else if((slen == 7) && curl_strnequal(start, "default", slen)) {
292
6.38k
      lexer->token = NETRC_TOK_DEFAULT;
293
6.38k
    }
294
22.8k
    else if((slen == 7) && curl_strnequal(start, "account", slen)) {
295
295
      lexer->token = NETRC_TOK_ACCOUNT;
296
295
    }
297
22.5k
    else if((slen == 5) && curl_strnequal(start, "login", slen)) {
298
6.07k
      lexer->token = NETRC_TOK_LOGIN;
299
6.07k
    }
300
16.4k
    else if((slen == 8) && curl_strnequal(start, "password", slen)) {
301
3.27k
      lexer->token = NETRC_TOK_PASSWD;
302
3.27k
    }
303
13.1k
    else if((slen == 6) && curl_strnequal(start, "macdef", slen)) {
304
675
      lexer->token = NETRC_TOK_MACDEF;
305
675
    }
306
12.4k
    else {
307
12.4k
      lexer->token = NETRC_TOK_JUNK;
308
12.4k
    }
309
39.8k
    break;
310
41.3k
  }
311
312
41.3k
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
41.3k
  lexer->pos = s;
319
41.3k
  return rc;
320
41.3k
}
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
9.83k
{
335
9.83k
  curlx_safefree(sc->login);
336
9.83k
  curlx_safefree(sc->passwd);
337
9.83k
  sc->matches_host = FALSE;
338
9.83k
}
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
808
{
346
808
  memset(sc, 0, sizeof(*sc));
347
808
  netrc_lexer_init(&sc->lexer, data, content);
348
808
  sc->hostname = hostname;
349
808
  sc->user = (user && user[0]) ? user : NULL;
350
808
  netrc_scan_reset(sc);
351
808
}
352
353
static void netrc_scan_cleanup(struct netrc_scanner *sc)
354
808
{
355
808
  netrc_scan_reset(sc);
356
808
  sc->hostname = NULL;
357
808
  sc->user = NULL;
358
808
  Curl_creds_unlink(&sc->creds);
359
808
  netrc_lexer_cleanup(&sc->lexer);
360
808
}
361
362
static NETRCcode netrc_scan_literal(struct netrc_scanner *sc,
363
                                    char **pdest)
364
9.64k
{
365
9.64k
  NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE);
366
9.64k
  if(!rc) {
367
9.61k
    if(sc->lexer.token == NETRC_TOK_LITERAL) {
368
9.59k
      if(pdest && sc->matches_host) {
369
8.56k
        curlx_free(*pdest);
370
8.56k
        *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal));
371
8.56k
        if(!*pdest)
372
0
          rc = NETRC_OUT_OF_MEMORY;
373
8.56k
      }
374
9.59k
    }
375
22
    else
376
22
      netrc_lexer_push(&sc->lexer);
377
9.61k
  }
378
9.64k
  return rc;
379
9.64k
}
380
381
static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc)
382
8.22k
{
383
8.22k
  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
8.22k
  if(sc->matches_host) {
390
6.56k
    if(sc->login) {
391
3.82k
      if(sc->user) {
392
3.77k
        if(Curl_timestrcmp(sc->user, sc->login))
393
3.27k
          goto out;
394
        /* We look for a specific user,
395
         * entry is only interesting with password */
396
502
        sc->found = !!sc->passwd;
397
502
      }
398
49
      else {
399
49
        sc->found = TRUE;
400
49
      }
401
3.82k
    }
402
2.73k
    else if(sc->passwd) {
403
      /* found a passwd that applies to any user */
404
33
      sc->found = TRUE;
405
33
    }
406
2.70k
    else {
407
      /* entry has nothing interesting */
408
2.70k
    }
409
3.28k
    if(sc->found) {
410
#if NETRC_DEBUG
411
      CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found");
412
#endif
413
82
      if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd,
414
82
                           NULL, NULL, NULL, CREDS_NETRC, &sc->creds))
415
0
        rc = NETRC_OUT_OF_MEMORY;
416
82
    }
417
3.28k
  }
418
8.22k
out:
419
8.22k
  netrc_scan_reset(sc);
420
8.22k
  return rc;
421
8.22k
}
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
808
{
429
808
  struct netrc_scanner sc;
430
808
  NETRCcode rc = NETRC_OK;
431
432
808
  Curl_creds_unlink(pcreds);
433
808
  netrc_scan_init(&sc, data, content, hostname, user);
434
435
31.3k
  while(!rc && !sc.found) {
436
31.2k
    rc = netrc_lexer_next(&sc.lexer, FALSE);
437
31.2k
    if(!rc) {
438
      /* Does this token end any previous entry? */
439
31.0k
      switch(sc.lexer.token) {
440
638
      case NETRC_TOK_EOF:
441
1.16k
      case NETRC_TOK_MACHINE:
442
7.54k
      case NETRC_TOK_DEFAULT:
443
8.22k
      case NETRC_TOK_MACDEF:
444
8.22k
        rc = netrc_scan_end_entry(&sc);
445
8.22k
        if(rc || sc.found)
446
82
          goto out;
447
8.14k
        break;
448
22.8k
      default:
449
22.8k
        break;
450
31.0k
      }
451
452
30.9k
      switch(sc.lexer.token) {
453
563
      case NETRC_TOK_EOF:
454
563
        goto out;
455
523
      case NETRC_TOK_MACHINE:
456
523
        rc = netrc_lexer_next(&sc.lexer, TRUE);
457
523
        if(!rc) {
458
522
          if(sc.lexer.token == NETRC_TOK_LITERAL) {
459
517
            sc.matches_host = curl_strequal(
460
517
              sc.hostname, curlx_dyn_ptr(&sc.lexer.literal));
461
517
          }
462
5
          else {
463
5
            sc.matches_host = FALSE;
464
5
            netrc_lexer_push(&sc.lexer);
465
5
          }
466
522
        }
467
523
        break;
468
6.37k
      case NETRC_TOK_DEFAULT:
469
6.37k
        sc.matches_host = TRUE;
470
6.37k
        break;
471
295
      case NETRC_TOK_ACCOUNT:
472
295
        rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */
473
295
        break;
474
6.07k
      case NETRC_TOK_LOGIN:
475
6.07k
        rc = netrc_scan_literal(&sc, &sc.login);
476
6.07k
        break;
477
3.27k
      case NETRC_TOK_PASSWD:
478
3.27k
        rc = netrc_scan_literal(&sc, &sc.passwd);
479
3.27k
        break;
480
675
      case NETRC_TOK_MACDEF:
481
675
        netrc_skip_to_empty_line(&sc.lexer);
482
675
        break;
483
733
      case NETRC_TOK_LITERAL:
484
13.2k
      case NETRC_TOK_JUNK:
485
13.2k
      default:
486
        /* skip this */
487
13.2k
        break;
488
30.9k
      }
489
30.9k
    }
490
31.2k
  }
491
492
808
out:
493
808
  if(!rc) {
494
645
    if(sc.creds)
495
82
      Curl_creds_link(pcreds, sc.creds);
496
563
    else
497
563
      rc = NETRC_NO_MATCH;
498
645
  }
499
808
  netrc_scan_cleanup(&sc);
500
808
  return rc;
501
808
}
502
503
static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
504
812
{
505
812
  NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
506
812
  FILE *file = curlx_fopen(filename, FOPEN_READTEXT);
507
508
812
  curlx_dyn_reset(filebuf);
509
812
  if(file) {
510
812
    curlx_struct_stat stat;
511
812
    if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
512
812
      CURLcode result = CURLE_OK;
513
812
      bool eof;
514
812
      struct dynbuf linebuf;
515
812
      curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
516
812
      ret = NETRC_OK;
517
71.8k
      do {
518
71.8k
        const char *line;
519
        /* Curl_get_line always returns lines ending with a newline */
520
71.8k
        result = Curl_get_line(&linebuf, file, &eof);
521
71.8k
        if(!result) {
522
71.8k
          line = curlx_dyn_ptr(&linebuf);
523
          /* skip comments on load */
524
71.8k
          curlx_str_passblanks(&line);
525
71.8k
          if(*line == '#')
526
194
            continue;
527
71.6k
          result = curlx_dyn_add(filebuf, line);
528
71.6k
        }
529
71.6k
        if(result) {
530
4
          curlx_dyn_free(filebuf);
531
4
          ret = curl2netrc(result);
532
4
          break;
533
4
        }
534
71.8k
      } while(!eof);
535
812
      curlx_dyn_free(&linebuf);
536
812
    }
537
812
    curlx_fclose(file);
538
812
  }
539
812
  return ret;
540
812
}
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
812
{
549
812
  struct dynbuf *filebuf = &store->filebuf;
550
551
812
  if(!store->loaded || strcmp(netrcfile, store->filename)) {
552
812
    NETRCcode ret;
553
812
    store->loaded = FALSE;
554
812
    ret = file2memory(netrcfile, filebuf);
555
812
    if(ret) {
556
4
      CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile);
557
4
      return ret;
558
4
    }
559
808
    curlx_free(store->filename);
560
808
    store->filename = curlx_strdup(netrcfile);
561
808
    if(!store->filename) {
562
0
      curlx_dyn_reset(&store->filebuf);
563
0
      return NETRC_OUT_OF_MEMORY;
564
0
    }
565
808
    store->loaded = TRUE;
566
808
  }
567
808
  if(!curlx_dyn_len(filebuf))
568
0
    return NETRC_NO_MATCH;
569
570
808
  return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds);
571
808
}
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
812
{
586
812
  NETRCcode retcode = NETRC_OK;
587
588
812
  CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'",
589
812
             netrcfile, hostname, user);
590
812
  Curl_creds_unlink(pcreds);
591
812
  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
812
  else
659
812
    retcode = netrc_scan_file(
660
812
      data, store, hostname, user, netrcfile, pcreds);
661
662
812
out:
663
812
  if(retcode)
664
730
    Curl_creds_unlink(pcreds);
665
812
  return retcode;
666
812
}
667
668
void Curl_netrc_init(struct store_netrc *store)
669
1.62k
{
670
1.62k
  curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE);
671
1.62k
  store->loaded = FALSE;
672
1.62k
  store->filename = NULL;
673
1.62k
}
674
void Curl_netrc_cleanup(struct store_netrc *store)
675
1.62k
{
676
1.62k
  curlx_dyn_free(&store->filebuf);
677
1.62k
  curlx_safefree(store->filename);
678
1.62k
  store->loaded = FALSE;
679
1.62k
}
680
681
const char *Curl_netrc_strerror(NETRCcode ret)
682
817
{
683
817
  switch(ret) {
684
83
  default:
685
83
    return ""; /* not a legit error */
686
1
  case NETRC_FILE_MISSING:
687
1
    return "no such file";
688
564
  case NETRC_NO_MATCH:
689
564
    return "no matching entry";
690
1
  case NETRC_OUT_OF_MEMORY:
691
1
    return "out of memory";
692
168
  case NETRC_SYNTAX_ERROR:
693
168
    return "syntax error";
694
817
  }
695
  /* never reached */
696
817
}
697
698
#endif /* !CURL_DISABLE_NETRC */