1 /* Read and parse the .netrc file to get hosts, accounts, and passwords.
2 Copyright (C) 1996, Free Software Foundation, Inc.
4 This file is part of Wget.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
20 /* This file used to be kept in synch with the code in Fetchmail, but
21 the latter has diverged since. */
35 #include <sys/types.h>
47 #define NETRC_FILE_NAME ".netrc"
51 static acc_t *parse_netrc PARAMS ((const char *));
53 /* Return the correct user and password, given the host, user (as
54 given in the URL), and password (as given in the URL). May return
57 If SLACK_DEFAULT is set, allow looking for a "default" account.
58 You will typically turn it off for HTTP. */
60 search_netrc (const char *host, const char **acc, const char **passwd,
64 static int processed_netrc;
71 char *home = home_dir();
79 char *path = (char *)alloca (strlen (home) + 1
80 + strlen (NETRC_FILE_NAME) + 1);
81 sprintf (path, "%s/%s", home, NETRC_FILE_NAME);
83 err = stat (path, &buf);
85 netrc_list = parse_netrc (path);
88 /* If nothing to do... */
91 /* Acc and password found; all OK. */
94 if (!*acc && !slack_default)
96 /* Some data not given -- try finding the host. */
97 for (l = netrc_list; l; l = l->next)
101 else if (!strcasecmp (l->host, host))
108 /* Looking for password in .netrc. */
109 if (!strcmp (l->acc, *acc))
110 *passwd = l->passwd; /* usernames match; password OK */
112 *passwd = NULL; /* usernames don't match */
116 /* If password was given, use it. The account is l->acc. */
129 /* Try looking for the default account. */
130 for (l = netrc_list; l; l = l->next)
144 /* Normally, these functions would be defined by your package. */
145 # define xmalloc malloc
147 # define xstrdup strdup
149 /* The function reads a whole line. It reads the line realloc-ing the
150 storage exponentially, doubling the storage after each overflow to
151 minimize the number of calls to realloc().
153 It is not an exemplary of correctness, since it kills off the
154 newline (and no, there is no way to know if there was a newline at
156 # define xrealloc realloc
157 # define DYNAMIC_LINE_BUFFER 40
160 read_whole_line (FILE *fp)
166 bufsize = DYNAMIC_LINE_BUFFER;
167 line = xmalloc(bufsize);
168 /* Construct the line. */
169 while ((c = getc(fp)) != EOF && c != '\n')
172 line = (char *)xrealloc(line, (bufsize <<= 1));
181 /* Check for overflow at zero-termination (no need to double the
182 buffer in this case. */
184 line = (char *)xrealloc(line, i + 1);
189 #endif /* STANDALONE */
191 /* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is
192 set to a ready-to-use acc_t, in any event. */
194 maybe_add_to_list (acc_t **newentry, acc_t **list)
200 /* We need an account name in order to add the entry to the list. */
203 /* Free any allocated space. */
212 /* Add the current machine into our list. */
217 /* Allocate a new acc_t structure. */
218 a = (acc_t *)xmalloc (sizeof (acc_t));
221 /* Zero the structure, so that it is ready to use. */
222 memset (a, 0, sizeof(*a));
224 /* Return the new pointers. */
230 /* Helper function for the parser, shifts contents of
231 null-terminated string once character to the left.
232 Used in processing \ and " constructs in the netrc file */
234 shift_left(char *string){
237 for (p=string; *p; ++p)
241 /* Parse a .netrc file (as described in the ftp(1) manual page). */
243 parse_netrc (const char *path)
246 char *line, *p, *tok, *premature_token;
247 acc_t *current, *retval;
250 /* The latest token we've seen in the file. */
253 tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
254 } last_token = tok_nothing;
256 current = retval = NULL;
258 fp = fopen (path, "r");
261 fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
262 path, strerror (errno));
266 /* Initialize the file data. */
268 premature_token = NULL;
270 /* While there are lines in the file... */
271 while ((line = read_whole_line (fp)))
273 /* Do away with line separators. */
274 int len = strlen (line);
275 if (len && line[len - 1] == '\n')
277 if (len && line[len - 1] == '\r')
282 /* Parse the line. */
286 /* If the line is empty, then end any macro definition. */
287 if (last_token == tok_macdef && !*p)
288 /* End of macro if the line is empty. */
289 last_token = tok_nothing;
291 /* If we are defining macros, then skip parsing the line. */
292 while (*p && last_token != tok_macdef)
294 /* Skip any whitespace. */
295 while (*p && ISSPACE (*p))
298 /* Discard end-of-line comments. */
302 /* If the token starts with quotation mark, note this fact,
303 and squash the quotation character */
311 /* Find the end of the token, handling quotes and escapes. */
312 while (*p && (quote ? *p != '"' : !ISSPACE (*p))){
318 /* if field was quoted, squash the trailing quotation mark */
322 /* Null-terminate the token, if it isn't already. */
330 current->acc = xstrdup (tok);
332 premature_token = "login";
336 /* Start a new machine entry. */
337 maybe_add_to_list (¤t, &retval);
338 current->host = xstrdup (tok);
343 current->passwd = xstrdup (tok);
345 premature_token = "password";
348 /* We handle most of tok_macdef above. */
351 premature_token = "macdef";
354 /* We don't handle the account keyword at all. */
357 premature_token = "account";
360 /* We handle tok_nothing below this switch. */
367 fprintf (stderr, _("\
368 %s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
369 exec_name, path, ln, premature_token);
370 premature_token = NULL;
373 if (last_token != tok_nothing)
374 /* We got a value, so reset the token state. */
375 last_token = tok_nothing;
378 /* Fetch the next token. */
379 if (!strcmp (tok, "account"))
380 last_token = tok_account;
381 else if (!strcmp (tok, "default"))
383 maybe_add_to_list (¤t, &retval);
385 else if (!strcmp (tok, "login"))
386 last_token = tok_login;
388 else if (!strcmp (tok, "macdef"))
389 last_token = tok_macdef;
391 else if (!strcmp (tok, "machine"))
392 last_token = tok_machine;
394 else if (!strcmp (tok, "password"))
395 last_token = tok_password;
398 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
399 exec_name, path, ln, tok);
408 /* Finalize the last machine entry we found. */
409 maybe_add_to_list (¤t, &retval);
412 /* Reverse the order of the list so that it appears in file order. */
417 acc_t *saved_reference;
419 /* Change the direction of the pointers. */
420 saved_reference = current->next;
421 current->next = retval;
423 /* Advance to the next node. */
425 current = saved_reference;
432 /* Free a netrc list. */
442 FREE_MAYBE (l->passwd);
443 FREE_MAYBE (l->host);
450 #include <sys/types.h>
451 #include <sys/stat.h>
454 main (int argc, char **argv)
457 char *program_name, *file, *target;
460 if (argc < 2 || argc > 3)
462 fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
466 program_name = argv[0];
470 if (stat (file, &sb))
472 fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
477 head = parse_netrc (file);
481 /* Skip if we have a target and this isn't it. */
482 if (target && a->host && strcmp (target, a->host))
490 /* Print the host name if we have no target. */
492 fputs (a->host, stdout);
494 fputs ("DEFAULT", stdout);
499 /* Print the account name. */
500 fputs (a->acc, stdout);
504 /* Print the password, if there is any. */
506 fputs (a->passwd, stdout);
509 fputc ('\n', stdout);
511 /* Exit if we found the target. */
517 /* Exit with failure if we had a target, success otherwise. */
523 #endif /* STANDALONE */