/src/CMake/Utilities/cmcurl/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 "strcase.h" |
40 | | #include "curl_get_line.h" |
41 | | #include "curlx/fopen.h" |
42 | | #include "curlx/strparse.h" |
43 | | |
44 | | /* Get user and password from .netrc when given a machine name */ |
45 | | |
46 | | enum host_lookup_state { |
47 | | NOTHING, |
48 | | HOSTFOUND, /* the 'machine' keyword was found */ |
49 | | HOSTVALID, /* this is "our" machine! */ |
50 | | MACDEF |
51 | | }; |
52 | | |
53 | | enum found_state { |
54 | | NONE, |
55 | | LOGIN, |
56 | | PASSWORD |
57 | | }; |
58 | | |
59 | 0 | #define FOUND_LOGIN 1 |
60 | 0 | #define FOUND_PASSWORD 2 |
61 | | |
62 | 0 | #define MAX_NETRC_LINE 16384 |
63 | 0 | #define MAX_NETRC_FILE (128 * 1024) |
64 | 0 | #define MAX_NETRC_TOKEN 4096 |
65 | | |
66 | | /* convert a dynbuf call CURLcode error to a NETRCcode error */ |
67 | | #define curl2netrc(result) \ |
68 | 0 | (((result) == CURLE_OUT_OF_MEMORY) ? \ |
69 | 0 | NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR) |
70 | | |
71 | | static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) |
72 | 0 | { |
73 | 0 | NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ |
74 | 0 | FILE *file = curlx_fopen(filename, FOPEN_READTEXT); |
75 | |
|
76 | 0 | if(file) { |
77 | 0 | curlx_struct_stat stat; |
78 | 0 | if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { |
79 | 0 | CURLcode result = CURLE_OK; |
80 | 0 | bool eof; |
81 | 0 | struct dynbuf linebuf; |
82 | 0 | curlx_dyn_init(&linebuf, MAX_NETRC_LINE); |
83 | 0 | ret = NETRC_OK; |
84 | 0 | do { |
85 | 0 | const char *line; |
86 | | /* Curl_get_line always returns lines ending with a newline */ |
87 | 0 | result = Curl_get_line(&linebuf, file, &eof); |
88 | 0 | if(!result) { |
89 | 0 | line = curlx_dyn_ptr(&linebuf); |
90 | | /* skip comments on load */ |
91 | 0 | curlx_str_passblanks(&line); |
92 | 0 | if(*line == '#') |
93 | 0 | continue; |
94 | 0 | result = curlx_dyn_add(filebuf, line); |
95 | 0 | } |
96 | 0 | if(result) { |
97 | 0 | curlx_dyn_free(filebuf); |
98 | 0 | ret = curl2netrc(result); |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | } while(!eof); |
102 | 0 | curlx_dyn_free(&linebuf); |
103 | 0 | } |
104 | 0 | curlx_fclose(file); |
105 | 0 | } |
106 | 0 | return ret; |
107 | 0 | } |
108 | | |
109 | | /* bundled parser state to keep function signatures compact */ |
110 | | struct netrc_state { |
111 | | char *login; |
112 | | char *password; |
113 | | enum host_lookup_state state; |
114 | | enum found_state keyword; |
115 | | NETRCcode retcode; |
116 | | unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ |
117 | | bool our_login; |
118 | | bool done; |
119 | | bool specific_login; |
120 | | }; |
121 | | |
122 | | /* |
123 | | * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t |
124 | | * escape sequences. Advances *tok_endp past the closing '"'. |
125 | | * |
126 | | * Returns NETRC_OK or error. |
127 | | */ |
128 | | static NETRCcode netrc_quoted_token(const char **tok_endp, |
129 | | struct dynbuf *token) |
130 | 0 | { |
131 | 0 | bool escape = FALSE; |
132 | 0 | NETRCcode rc = NETRC_SYNTAX_ERROR; |
133 | 0 | const char *tok_end = *tok_endp; |
134 | 0 | tok_end++; /* pass the leading quote */ |
135 | 0 | while(*tok_end) { |
136 | 0 | CURLcode result; |
137 | 0 | char s = *tok_end; |
138 | 0 | if(escape) { |
139 | 0 | escape = FALSE; |
140 | 0 | switch(s) { |
141 | 0 | case 'n': |
142 | 0 | s = '\n'; |
143 | 0 | break; |
144 | 0 | case 'r': |
145 | 0 | s = '\r'; |
146 | 0 | break; |
147 | 0 | case 't': |
148 | 0 | s = '\t'; |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | else if(s == '\\') { |
153 | 0 | escape = TRUE; |
154 | 0 | tok_end++; |
155 | 0 | continue; |
156 | 0 | } |
157 | 0 | else if(s == '\"') { |
158 | 0 | tok_end++; /* pass the ending quote */ |
159 | 0 | rc = NETRC_OK; |
160 | 0 | break; |
161 | 0 | } |
162 | 0 | result = curlx_dyn_addn(token, &s, 1); |
163 | 0 | if(result) { |
164 | 0 | *tok_endp = tok_end; |
165 | 0 | return curl2netrc(result); |
166 | 0 | } |
167 | 0 | tok_end++; |
168 | 0 | } |
169 | 0 | *tok_endp = tok_end; |
170 | 0 | return rc; |
171 | 0 | } |
172 | | |
173 | | /* |
174 | | * Gets the next token from the netrc buffer at *tokp. Writes the token into |
175 | | * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input |
176 | | * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE |
177 | | * when the line is exhausted. |
178 | | * |
179 | | * Returns NETRC_OK or an error code. |
180 | | */ |
181 | | static NETRCcode netrc_get_token(const char **tokp, |
182 | | const char **tok_endp, |
183 | | struct dynbuf *token, |
184 | | enum host_lookup_state *statep, |
185 | | bool *lineend) |
186 | 0 | { |
187 | 0 | const char *tok = *tokp; |
188 | 0 | const char *tok_end; |
189 | |
|
190 | 0 | *lineend = FALSE; |
191 | 0 | curlx_dyn_reset(token); |
192 | 0 | curlx_str_passblanks(&tok); |
193 | | |
194 | | /* tok is first non-space letter */ |
195 | 0 | if(*statep == MACDEF) { |
196 | 0 | if((*tok == '\n') || (*tok == '\r')) |
197 | 0 | *statep = NOTHING; /* end of macro definition */ |
198 | 0 | *lineend = TRUE; |
199 | 0 | *tokp = tok; |
200 | 0 | return NETRC_OK; |
201 | 0 | } |
202 | | |
203 | 0 | if(!*tok || (*tok == '\n')) { |
204 | | /* end of line */ |
205 | 0 | *lineend = TRUE; |
206 | 0 | *tokp = tok; |
207 | 0 | return NETRC_OK; |
208 | 0 | } |
209 | | |
210 | 0 | tok_end = tok; |
211 | 0 | if(*tok == '\"') { |
212 | | /* quoted string */ |
213 | 0 | NETRCcode ret = netrc_quoted_token(&tok_end, token); |
214 | 0 | if(ret) |
215 | 0 | return ret; |
216 | 0 | } |
217 | 0 | else { |
218 | | /* unquoted token */ |
219 | 0 | size_t len = 0; |
220 | 0 | CURLcode result; |
221 | 0 | while(*tok_end > ' ') { |
222 | 0 | tok_end++; |
223 | 0 | len++; |
224 | 0 | } |
225 | 0 | if(!len) |
226 | 0 | return NETRC_SYNTAX_ERROR; |
227 | 0 | result = curlx_dyn_addn(token, tok, len); |
228 | 0 | if(result) |
229 | 0 | return curl2netrc(result); |
230 | 0 | } |
231 | | |
232 | 0 | *tok_endp = tok_end; |
233 | |
|
234 | 0 | if(curlx_dyn_len(token)) |
235 | 0 | *tokp = curlx_dyn_ptr(token); |
236 | 0 | else |
237 | | /* set it to blank to avoid NULL */ |
238 | 0 | *tokp = ""; |
239 | |
|
240 | 0 | return NETRC_OK; |
241 | 0 | } |
242 | | |
243 | | /* |
244 | | * Reset parser for a new machine entry. Frees password and optionally login |
245 | | * if it was not user-specified. |
246 | | */ |
247 | | static void netrc_new_machine(struct netrc_state *ns) |
248 | 0 | { |
249 | 0 | ns->keyword = NONE; |
250 | 0 | ns->found = 0; |
251 | 0 | ns->our_login = FALSE; |
252 | 0 | curlx_safefree(ns->password); |
253 | 0 | if(!ns->specific_login) |
254 | 0 | curlx_safefree(ns->login); |
255 | 0 | } |
256 | | |
257 | | /* |
258 | | * Process a parsed token through the HOSTVALID state machine branch. This |
259 | | * handles login/password values and keyword transitions for the matched host. |
260 | | * |
261 | | * Returns NETRC_OK or an error code. |
262 | | */ |
263 | | static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) |
264 | 0 | { |
265 | 0 | if(ns->keyword == LOGIN) { |
266 | 0 | if(ns->specific_login) |
267 | 0 | ns->our_login = !Curl_timestrcmp(ns->login, tok); |
268 | 0 | else { |
269 | 0 | ns->our_login = TRUE; |
270 | 0 | curlx_free(ns->login); |
271 | 0 | ns->login = curlx_strdup(tok); |
272 | 0 | if(!ns->login) |
273 | 0 | return NETRC_OUT_OF_MEMORY; |
274 | 0 | } |
275 | 0 | ns->found |= FOUND_LOGIN; |
276 | 0 | ns->keyword = NONE; |
277 | 0 | } |
278 | 0 | else if(ns->keyword == PASSWORD) { |
279 | 0 | curlx_free(ns->password); |
280 | 0 | ns->password = curlx_strdup(tok); |
281 | 0 | if(!ns->password) |
282 | 0 | return NETRC_OUT_OF_MEMORY; |
283 | 0 | ns->found |= FOUND_PASSWORD; |
284 | 0 | ns->keyword = NONE; |
285 | 0 | } |
286 | 0 | else if(curl_strequal("login", tok)) |
287 | 0 | ns->keyword = LOGIN; |
288 | 0 | else if(curl_strequal("password", tok)) |
289 | 0 | ns->keyword = PASSWORD; |
290 | 0 | else if(curl_strequal("machine", tok)) { |
291 | | /* a new machine here */ |
292 | |
|
293 | 0 | if(ns->found & FOUND_PASSWORD && |
294 | | /* a password was provided for this host */ |
295 | | |
296 | 0 | ((!ns->specific_login || ns->our_login) || |
297 | | /* either there was no specific login to search for, or this |
298 | | is the specific one we wanted */ |
299 | 0 | (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { |
300 | | /* or we look for a specific login, but that was not specified */ |
301 | |
|
302 | 0 | ns->done = TRUE; |
303 | 0 | return NETRC_OK; |
304 | 0 | } |
305 | | |
306 | 0 | ns->state = HOSTFOUND; |
307 | 0 | netrc_new_machine(ns); |
308 | 0 | } |
309 | 0 | else if(curl_strequal("default", tok)) { |
310 | 0 | ns->state = HOSTVALID; |
311 | 0 | ns->retcode = NETRC_OK; |
312 | 0 | netrc_new_machine(ns); |
313 | 0 | } |
314 | 0 | if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) |
315 | 0 | ns->done = TRUE; |
316 | 0 | return NETRC_OK; |
317 | 0 | } |
318 | | |
319 | | /* |
320 | | * Process one parsed token through the netrc state |
321 | | * machine. Updates the parser state in *ns. |
322 | | * Returns NETRC_OK or an error code. |
323 | | */ |
324 | | static NETRCcode netrc_handle_token(struct netrc_state *ns, |
325 | | const char *tok, |
326 | | const char *host) |
327 | 0 | { |
328 | 0 | switch(ns->state) { |
329 | 0 | case NOTHING: |
330 | 0 | if(curl_strequal("macdef", tok)) |
331 | 0 | ns->state = MACDEF; |
332 | 0 | else if(curl_strequal("machine", tok)) { |
333 | 0 | ns->state = HOSTFOUND; |
334 | 0 | netrc_new_machine(ns); |
335 | 0 | } |
336 | 0 | else if(curl_strequal("default", tok)) { |
337 | 0 | ns->state = HOSTVALID; |
338 | 0 | ns->retcode = NETRC_OK; |
339 | 0 | } |
340 | 0 | break; |
341 | 0 | case MACDEF: |
342 | 0 | if(!*tok) |
343 | 0 | ns->state = NOTHING; |
344 | 0 | break; |
345 | 0 | case HOSTFOUND: |
346 | 0 | if(curl_strequal(host, tok)) { |
347 | 0 | ns->state = HOSTVALID; |
348 | 0 | ns->retcode = NETRC_OK; |
349 | 0 | } |
350 | 0 | else |
351 | 0 | ns->state = NOTHING; |
352 | 0 | break; |
353 | 0 | case HOSTVALID: |
354 | 0 | return netrc_hostvalid(ns, tok); |
355 | 0 | } |
356 | 0 | return NETRC_OK; |
357 | 0 | } |
358 | | |
359 | | /* |
360 | | * Finalize the parse result: fill in defaults and free |
361 | | * resources on error. |
362 | | */ |
363 | | static NETRCcode netrc_finalize(struct netrc_state *ns, |
364 | | char **loginp, |
365 | | char **passwordp, |
366 | | struct store_netrc *store) |
367 | 0 | { |
368 | 0 | NETRCcode retcode = ns->retcode; |
369 | 0 | if(!retcode) { |
370 | 0 | if(!ns->password && ns->our_login) { |
371 | | /* success without a password, set a blank one */ |
372 | 0 | ns->password = curlx_strdup(""); |
373 | 0 | if(!ns->password) |
374 | 0 | retcode = NETRC_OUT_OF_MEMORY; |
375 | 0 | } |
376 | 0 | else if(!ns->login && !ns->password) |
377 | | /* a default with no credentials */ |
378 | 0 | retcode = NETRC_NO_MATCH; |
379 | 0 | } |
380 | 0 | if(!retcode) { |
381 | | /* success */ |
382 | 0 | if(!ns->specific_login) |
383 | 0 | *loginp = ns->login; |
384 | | |
385 | | /* netrc_finalize() can return a password even when specific_login is set |
386 | | but our_login is false (e.g., host matched but the requested login |
387 | | never matched). See test 685. */ |
388 | 0 | *passwordp = ns->password; |
389 | 0 | } |
390 | 0 | else { |
391 | 0 | curlx_dyn_free(&store->filebuf); |
392 | 0 | store->loaded = FALSE; |
393 | 0 | if(!ns->specific_login) |
394 | 0 | curlx_free(ns->login); |
395 | 0 | curlx_free(ns->password); |
396 | 0 | } |
397 | 0 | return retcode; |
398 | 0 | } |
399 | | |
400 | | /* |
401 | | * Returns zero on success. |
402 | | */ |
403 | | static NETRCcode parsenetrc(struct store_netrc *store, |
404 | | const char *host, |
405 | | char **loginp, |
406 | | char **passwordp, |
407 | | const char *netrcfile) |
408 | 0 | { |
409 | 0 | const char *netrcbuffer; |
410 | 0 | struct dynbuf token; |
411 | 0 | struct dynbuf *filebuf = &store->filebuf; |
412 | 0 | struct netrc_state ns; |
413 | |
|
414 | 0 | memset(&ns, 0, sizeof(ns)); |
415 | 0 | ns.retcode = NETRC_NO_MATCH; |
416 | 0 | ns.login = *loginp; |
417 | 0 | ns.specific_login = !!ns.login; |
418 | |
|
419 | 0 | DEBUGASSERT(!*passwordp); |
420 | 0 | curlx_dyn_init(&token, MAX_NETRC_TOKEN); |
421 | |
|
422 | 0 | if(!store->loaded) { |
423 | 0 | NETRCcode ret = file2memory(netrcfile, filebuf); |
424 | 0 | if(ret) |
425 | 0 | return ret; |
426 | 0 | store->loaded = TRUE; |
427 | 0 | } |
428 | | |
429 | 0 | netrcbuffer = curlx_dyn_ptr(filebuf); |
430 | |
|
431 | 0 | while(!ns.done) { |
432 | 0 | const char *tok = netrcbuffer; |
433 | 0 | while(tok && !ns.done) { |
434 | 0 | const char *tok_end; |
435 | 0 | bool lineend; |
436 | 0 | NETRCcode ret; |
437 | |
|
438 | 0 | ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); |
439 | 0 | if(ret) { |
440 | 0 | ns.retcode = ret; |
441 | 0 | goto out; |
442 | 0 | } |
443 | 0 | if(lineend) |
444 | 0 | break; |
445 | | |
446 | 0 | ret = netrc_handle_token(&ns, tok, host); |
447 | 0 | if(ret) { |
448 | 0 | ns.retcode = ret; |
449 | 0 | goto out; |
450 | 0 | } |
451 | | /* tok_end cannot point to a null byte here since lines are always |
452 | | newline terminated */ |
453 | 0 | DEBUGASSERT(*tok_end); |
454 | 0 | tok = ++tok_end; |
455 | 0 | } |
456 | 0 | if(!ns.done) { |
457 | 0 | const char *nl = NULL; |
458 | 0 | if(tok) |
459 | 0 | nl = strchr(tok, '\n'); |
460 | 0 | if(!nl) |
461 | 0 | break; |
462 | | /* point to next line */ |
463 | 0 | netrcbuffer = &nl[1]; |
464 | 0 | } |
465 | 0 | } /* while !done */ |
466 | | |
467 | 0 | out: |
468 | 0 | curlx_dyn_free(&token); |
469 | 0 | return netrc_finalize(&ns, loginp, passwordp, store); |
470 | 0 | } |
471 | | |
472 | | const char *Curl_netrc_strerror(NETRCcode ret) |
473 | 0 | { |
474 | 0 | switch(ret) { |
475 | 0 | default: |
476 | 0 | return ""; /* not a legit error */ |
477 | 0 | case NETRC_FILE_MISSING: |
478 | 0 | return "no such file"; |
479 | 0 | case NETRC_NO_MATCH: |
480 | 0 | return "no matching entry"; |
481 | 0 | case NETRC_OUT_OF_MEMORY: |
482 | 0 | return "out of memory"; |
483 | 0 | case NETRC_SYNTAX_ERROR: |
484 | 0 | return "syntax error"; |
485 | 0 | } |
486 | | /* never reached */ |
487 | 0 | } |
488 | | |
489 | | /* |
490 | | * @unittest: 1304 |
491 | | * |
492 | | * *loginp and *passwordp MUST be allocated if they are not NULL when passed |
493 | | * in. |
494 | | */ |
495 | | NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, |
496 | | char **loginp, char **passwordp, |
497 | | const char *netrcfile) |
498 | 0 | { |
499 | 0 | NETRCcode retcode = NETRC_OK; |
500 | 0 | char *filealloc = NULL; |
501 | |
|
502 | 0 | if(!netrcfile) { |
503 | 0 | char *home = NULL; |
504 | 0 | char *homea = NULL; |
505 | 0 | #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) |
506 | 0 | char pwbuf[1024]; |
507 | 0 | #endif |
508 | 0 | filealloc = curl_getenv("NETRC"); |
509 | 0 | if(!filealloc) { |
510 | 0 | homea = curl_getenv("HOME"); /* portable environment reader */ |
511 | 0 | if(homea) { |
512 | 0 | home = homea; |
513 | 0 | #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) |
514 | 0 | } |
515 | 0 | else { |
516 | 0 | struct passwd pw, *pw_res; |
517 | 0 | if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) && |
518 | 0 | pw_res) { |
519 | 0 | home = pw.pw_dir; |
520 | 0 | } |
521 | | #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) |
522 | | } |
523 | | else { |
524 | | struct passwd *pw; |
525 | | pw = getpwuid(geteuid()); |
526 | | if(pw) { |
527 | | home = pw->pw_dir; |
528 | | } |
529 | | #elif defined(_WIN32) |
530 | | } |
531 | | else { |
532 | | homea = curl_getenv("USERPROFILE"); |
533 | | if(homea) { |
534 | | home = homea; |
535 | | } |
536 | | #endif |
537 | 0 | } |
538 | |
|
539 | 0 | if(!home) |
540 | 0 | return NETRC_FILE_MISSING; /* no home directory found (or possibly out |
541 | | of memory) */ |
542 | | |
543 | 0 | filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); |
544 | 0 | if(!filealloc) { |
545 | 0 | curlx_free(homea); |
546 | 0 | return NETRC_OUT_OF_MEMORY; |
547 | 0 | } |
548 | 0 | } |
549 | 0 | retcode = parsenetrc(store, host, loginp, passwordp, filealloc); |
550 | 0 | curlx_free(filealloc); |
551 | | #ifdef _WIN32 |
552 | | if(retcode == NETRC_FILE_MISSING) { |
553 | | /* fallback to the old-style "_netrc" file */ |
554 | | filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); |
555 | | if(!filealloc) { |
556 | | curlx_free(homea); |
557 | | return NETRC_OUT_OF_MEMORY; |
558 | | } |
559 | | retcode = parsenetrc(store, host, loginp, passwordp, filealloc); |
560 | | curlx_free(filealloc); |
561 | | } |
562 | | #endif |
563 | 0 | curlx_free(homea); |
564 | 0 | } |
565 | 0 | else |
566 | 0 | retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); |
567 | 0 | return retcode; |
568 | 0 | } |
569 | | |
570 | | void Curl_netrc_init(struct store_netrc *store) |
571 | 0 | { |
572 | 0 | curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); |
573 | 0 | store->loaded = FALSE; |
574 | 0 | } |
575 | | void Curl_netrc_cleanup(struct store_netrc *store) |
576 | 0 | { |
577 | 0 | curlx_dyn_free(&store->filebuf); |
578 | | store->loaded = FALSE; |
579 | 0 | } |
580 | | #endif |