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 GNU Wget.
6 GNU Wget 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 GNU Wget 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 Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables. You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL". If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so. If you do not wish to do
28 so, delete this exception statement from your version. */
30 /* This file used to be kept in synch with the code in Fetchmail, but
31 the latter has diverged since. */
47 #define NETRC_FILE_NAME ".netrc"
51 static acc_t *parse_netrc (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 /* Some data not given -- try finding the host. */
95 for (l = netrc_list; l; l = l->next)
99 else if (!strcasecmp (l->host, host))
106 /* Looking for password in .netrc. */
107 if (!strcmp (l->acc, *acc))
108 *passwd = l->passwd; /* usernames match; password OK */
110 *passwd = NULL; /* usernames don't match */
114 /* If password was given, use it. The account is l->acc. */
127 /* Try looking for the default account. */
128 for (l = netrc_list; l; l = l->next)
145 /* Normally, these functions would be defined by your package. */
146 # define xmalloc malloc
148 # define xstrdup strdup
150 # define xrealloc realloc
152 /* Read a line from FP. The function reallocs the storage as needed
153 to accomodate for any length of the line. Reallocs are done
154 storage exponentially, doubling the storage after each overflow to
155 minimize the number of calls to realloc() and fgets(). The newline
156 character at the end of line is retained.
158 After end-of-file is encountered without anything being read, NULL
159 is returned. NULL is also returned on error. To distinguish
160 between these two cases, use the stdio function ferror(). */
163 read_whole_line (FILE *fp)
167 char *line = xmalloc (bufsize);
169 while (fgets (line + length, bufsize - length, fp))
171 length += strlen (line + length);
173 if (line[length - 1] == '\n')
175 /* fgets() guarantees to read the whole line, or to use up the
176 space we've given it. We can double the buffer
179 line = xrealloc (line, bufsize);
181 if (length == 0 || ferror (fp))
186 if (length + 1 < bufsize)
187 /* Relieve the memory from our exponential greediness. We say
188 `length + 1' because the terminating \0 is not included in
189 LENGTH. We don't need to zero-terminate the string ourselves,
190 though, because fgets() does that. */
191 line = xrealloc (line, length + 1);
194 #endif /* STANDALONE */
196 /* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is
197 set to a ready-to-use acc_t, in any event. */
199 maybe_add_to_list (acc_t **newentry, acc_t **list)
205 /* We need an account name in order to add the entry to the list. */
208 /* Free any allocated space. */
209 xfree_null (a->host);
211 xfree_null (a->passwd);
217 /* Add the current machine into our list. */
222 /* Allocate a new acc_t structure. */
223 a = xmalloc (sizeof (acc_t));
226 /* Zero the structure, so that it is ready to use. */
227 memset (a, 0, sizeof(*a));
229 /* Return the new pointers. */
235 /* Helper function for the parser, shifts contents of
236 null-terminated string once character to the left.
237 Used in processing \ and " constructs in the netrc file */
239 shift_left(char *string)
243 for (p=string; *p; ++p)
247 /* Parse a .netrc file (as described in the ftp(1) manual page). */
249 parse_netrc (const char *path)
252 char *line, *p, *tok;
253 const char *premature_token;
254 acc_t *current, *retval;
257 /* The latest token we've seen in the file. */
260 tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
261 } last_token = tok_nothing;
263 current = retval = NULL;
265 fp = fopen (path, "r");
268 fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
269 path, strerror (errno));
273 /* Initialize the file data. */
275 premature_token = NULL;
277 /* While there are lines in the file... */
278 while ((line = read_whole_line (fp)) != NULL)
282 /* Parse the line. */
286 /* Skip leading whitespace. */
287 while (*p && ISSPACE (*p))
290 /* If the line is empty, then end any macro definition. */
291 if (last_token == tok_macdef && !*p)
292 /* End of macro if the line is empty. */
293 last_token = tok_nothing;
295 /* If we are defining macros, then skip parsing the line. */
296 while (*p && last_token != tok_macdef)
298 /* Skip any whitespace. */
299 while (*p && ISSPACE (*p))
302 /* Discard end-of-line comments; also, stop processing if
303 the above `while' merely skipped trailing whitespace. */
304 if (*p == '#' || !*p)
307 /* If the token starts with quotation mark, note this fact,
308 and squash the quotation character */
316 /* Find the end of the token, handling quotes and escapes. */
317 while (*p && (quote ? *p != '"' : !ISSPACE (*p))){
323 /* If field was quoted, squash the trailing quotation mark
324 and reset quote flag. */
331 /* Null-terminate the token, if it isn't already. */
339 current->acc = xstrdup (tok);
341 premature_token = "login";
345 /* Start a new machine entry. */
346 maybe_add_to_list (¤t, &retval);
347 current->host = xstrdup (tok);
352 current->passwd = xstrdup (tok);
354 premature_token = "password";
357 /* We handle most of tok_macdef above. */
360 premature_token = "macdef";
363 /* We don't handle the account keyword at all. */
366 premature_token = "account";
369 /* We handle tok_nothing below this switch. */
376 fprintf (stderr, _("\
377 %s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
378 exec_name, path, ln, premature_token);
379 premature_token = NULL;
382 if (last_token != tok_nothing)
383 /* We got a value, so reset the token state. */
384 last_token = tok_nothing;
387 /* Fetch the next token. */
388 if (!strcmp (tok, "account"))
389 last_token = tok_account;
390 else if (!strcmp (tok, "default"))
392 maybe_add_to_list (¤t, &retval);
394 else if (!strcmp (tok, "login"))
395 last_token = tok_login;
397 else if (!strcmp (tok, "macdef"))
398 last_token = tok_macdef;
400 else if (!strcmp (tok, "machine"))
401 last_token = tok_machine;
403 else if (!strcmp (tok, "password"))
404 last_token = tok_password;
407 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
408 exec_name, path, ln, tok);
417 /* Finalize the last machine entry we found. */
418 maybe_add_to_list (¤t, &retval);
421 /* Reverse the order of the list so that it appears in file order. */
426 acc_t *saved_reference;
428 /* Change the direction of the pointers. */
429 saved_reference = current->next;
430 current->next = retval;
432 /* Advance to the next node. */
434 current = saved_reference;
441 /* Free a netrc list. */
451 xfree_null (l->passwd);
452 xfree_null (l->host);
459 #include <sys/types.h>
460 #include <sys/stat.h>
463 main (int argc, char **argv)
466 char *program_name, *file, *target;
469 if (argc < 2 || argc > 3)
471 fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
475 program_name = argv[0];
479 if (stat (file, &sb))
481 fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
486 head = parse_netrc (file);
490 /* Skip if we have a target and this isn't it. */
491 if (target && a->host && strcmp (target, a->host))
499 /* Print the host name if we have no target. */
501 fputs (a->host, stdout);
503 fputs ("DEFAULT", stdout);
508 /* Print the account name. */
509 fputs (a->acc, stdout);
513 /* Print the password, if there is any. */
515 fputs (a->passwd, stdout);
518 fputc ('\n', stdout);
520 /* Exit if we found the target. */
526 /* Exit with failure if we had a target, success otherwise. */
532 #endif /* STANDALONE */