source: vendor/wget/current/src/netrc.c

Last change on this file was 3440, checked in by bird, 18 years ago

wget 1.10.2

File size: 12.4 KB
Line 
1/* Read and parse the .netrc file to get hosts, accounts, and passwords.
2 Copyright (C) 1996, Free Software Foundation, Inc.
3
4This file is part of GNU Wget.
5
6GNU Wget is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9(at your option) any later version.
10
11GNU Wget is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Wget; if not, write to the Free Software
18Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20In addition, as a special exception, the Free Software Foundation
21gives permission to link the code of its release of Wget with the
22OpenSSL project's "OpenSSL" library (or with modified versions of it
23that use the same license as the "OpenSSL" library), and distribute
24the linked executables. You must obey the GNU General Public License
25in all respects for all of the code used other than "OpenSSL". If you
26modify this file, you may extend this exception to your version of the
27file, but you are not obligated to do so. If you do not wish to do
28so, delete this exception statement from your version. */
29
30/* This file used to be kept in synch with the code in Fetchmail, but
31 the latter has diverged since. */
32
33#ifdef HAVE_CONFIG_H
34# include <config.h>
35#endif
36
37#include <stdio.h>
38#include <stdlib.h>
39#ifdef HAVE_STRING_H
40# include <string.h>
41#else
42# include <strings.h>
43#endif
44#include <sys/types.h>
45#include <errno.h>
46
47#include "wget.h"
48#include "utils.h"
49#include "netrc.h"
50#include "init.h"
51
52#ifndef errno
53extern int errno;
54#endif
55
56#define NETRC_FILE_NAME ".netrc"
57
58acc_t *netrc_list;
59
60static acc_t *parse_netrc PARAMS ((const char *));
61
62/* Return the correct user and password, given the host, user (as
63 given in the URL), and password (as given in the URL). May return
64 NULL.
65
66 If SLACK_DEFAULT is set, allow looking for a "default" account.
67 You will typically turn it off for HTTP. */
68void
69search_netrc (const char *host, const char **acc, const char **passwd,
70 int slack_default)
71{
72 acc_t *l;
73 static int processed_netrc;
74
75 if (!opt.netrc)
76 return;
77 /* Find ~/.netrc. */
78 if (!processed_netrc)
79 {
80 char *home = home_dir ();
81
82 netrc_list = NULL;
83 processed_netrc = 1;
84 if (home)
85 {
86 int err;
87 struct_stat buf;
88 char *path = (char *)alloca (strlen (home) + 1
89 + strlen (NETRC_FILE_NAME) + 1);
90 sprintf (path, "%s/%s", home, NETRC_FILE_NAME);
91 xfree (home);
92 err = stat (path, &buf);
93 if (err == 0)
94 netrc_list = parse_netrc (path);
95 }
96 }
97 /* If nothing to do... */
98 if (!netrc_list)
99 return;
100 /* Acc and password found; all OK. */
101 if (*acc && *passwd)
102 return;
103 /* Some data not given -- try finding the host. */
104 for (l = netrc_list; l; l = l->next)
105 {
106 if (!l->host)
107 continue;
108 else if (!strcasecmp (l->host, host))
109 break;
110 }
111 if (l)
112 {
113 if (*acc)
114 {
115 /* Looking for password in .netrc. */
116 if (!strcmp (l->acc, *acc))
117 *passwd = l->passwd; /* usernames match; password OK */
118 else
119 *passwd = NULL; /* usernames don't match */
120 }
121 else /* NOT *acc */
122 {
123 /* If password was given, use it. The account is l->acc. */
124 *acc = l->acc;
125 if (l->passwd)
126 *passwd = l->passwd;
127 }
128 return;
129 }
130 else
131 {
132 if (!slack_default)
133 return;
134 if (*acc)
135 return;
136 /* Try looking for the default account. */
137 for (l = netrc_list; l; l = l->next)
138 if (!l->host)
139 break;
140 if (!l)
141 return;
142 *acc = l->acc;
143 if (!*passwd)
144 *passwd = l->passwd;
145 return;
146 }
147}
148
149
150#ifdef STANDALONE
151
152#include <assert.h>
153
154/* Normally, these functions would be defined by your package. */
155# define xmalloc malloc
156# define xfree free
157# define xstrdup strdup
158
159# define xrealloc realloc
160
161/* Read a line from FP. The function reallocs the storage as needed
162 to accomodate for any length of the line. Reallocs are done
163 storage exponentially, doubling the storage after each overflow to
164 minimize the number of calls to realloc() and fgets(). The newline
165 character at the end of line is retained.
166
167 After end-of-file is encountered without anything being read, NULL
168 is returned. NULL is also returned on error. To distinguish
169 between these two cases, use the stdio function ferror(). */
170
171char *
172read_whole_line (FILE *fp)
173{
174 int length = 0;
175 int bufsize = 81;
176 char *line = (char *)xmalloc (bufsize);
177
178 while (fgets (line + length, bufsize - length, fp))
179 {
180 length += strlen (line + length);
181 assert (length > 0);
182 if (line[length - 1] == '\n')
183 break;
184 /* fgets() guarantees to read the whole line, or to use up the
185 space we've given it. We can double the buffer
186 unconditionally. */
187 bufsize <<= 1;
188 line = xrealloc (line, bufsize);
189 }
190 if (length == 0 || ferror (fp))
191 {
192 xfree (line);
193 return NULL;
194 }
195 if (length + 1 < bufsize)
196 /* Relieve the memory from our exponential greediness. We say
197 `length + 1' because the terminating \0 is not included in
198 LENGTH. We don't need to zero-terminate the string ourselves,
199 though, because fgets() does that. */
200 line = xrealloc (line, length + 1);
201 return line;
202}
203#endif /* STANDALONE */
204
205/* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is
206 set to a ready-to-use acc_t, in any event. */
207static void
208maybe_add_to_list (acc_t **newentry, acc_t **list)
209{
210 acc_t *a, *l;
211 a = *newentry;
212 l = *list;
213
214 /* We need an account name in order to add the entry to the list. */
215 if (a && ! a->acc)
216 {
217 /* Free any allocated space. */
218 xfree_null (a->host);
219 xfree_null (a->acc);
220 xfree_null (a->passwd);
221 }
222 else
223 {
224 if (a)
225 {
226 /* Add the current machine into our list. */
227 a->next = l;
228 l = a;
229 }
230
231 /* Allocate a new acc_t structure. */
232 a = (acc_t *)xmalloc (sizeof (acc_t));
233 }
234
235 /* Zero the structure, so that it is ready to use. */
236 memset (a, 0, sizeof(*a));
237
238 /* Return the new pointers. */
239 *newentry = a;
240 *list = l;
241 return;
242}
243
244/* Helper function for the parser, shifts contents of
245 null-terminated string once character to the left.
246 Used in processing \ and " constructs in the netrc file */
247static void
248shift_left(char *string)
249{
250 char *p;
251
252 for (p=string; *p; ++p)
253 *p = *(p+1);
254}
255
256/* Parse a .netrc file (as described in the ftp(1) manual page). */
257static acc_t *
258parse_netrc (const char *path)
259{
260 FILE *fp;
261 char *line, *p, *tok;
262 const char *premature_token;
263 acc_t *current, *retval;
264 int ln, quote;
265
266 /* The latest token we've seen in the file. */
267 enum
268 {
269 tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
270 } last_token = tok_nothing;
271
272 current = retval = NULL;
273
274 fp = fopen (path, "r");
275 if (!fp)
276 {
277 fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
278 path, strerror (errno));
279 return retval;
280 }
281
282 /* Initialize the file data. */
283 ln = 0;
284 premature_token = NULL;
285
286 /* While there are lines in the file... */
287 while ((line = read_whole_line (fp)) != NULL)
288 {
289 ln ++;
290
291 /* Parse the line. */
292 p = line;
293 quote = 0;
294
295 /* Skip leading whitespace. */
296 while (*p && ISSPACE (*p))
297 p ++;
298
299 /* If the line is empty, then end any macro definition. */
300 if (last_token == tok_macdef && !*p)
301 /* End of macro if the line is empty. */
302 last_token = tok_nothing;
303
304 /* If we are defining macros, then skip parsing the line. */
305 while (*p && last_token != tok_macdef)
306 {
307 /* Skip any whitespace. */
308 while (*p && ISSPACE (*p))
309 p ++;
310
311 /* Discard end-of-line comments; also, stop processing if
312 the above `while' merely skipped trailing whitespace. */
313 if (*p == '#' || !*p)
314 break;
315
316 /* If the token starts with quotation mark, note this fact,
317 and squash the quotation character */
318 if (*p == '"'){
319 quote = 1;
320 shift_left (p);
321 }
322
323 tok = p;
324
325 /* Find the end of the token, handling quotes and escapes. */
326 while (*p && (quote ? *p != '"' : !ISSPACE (*p))){
327 if (*p == '\\')
328 shift_left (p);
329 p ++;
330 }
331
332 /* If field was quoted, squash the trailing quotation mark
333 and reset quote flag. */
334 if (quote)
335 {
336 shift_left (p);
337 quote = 0;
338 }
339
340 /* Null-terminate the token, if it isn't already. */
341 if (*p)
342 *p ++ = '\0';
343
344 switch (last_token)
345 {
346 case tok_login:
347 if (current)
348 current->acc = xstrdup (tok);
349 else
350 premature_token = "login";
351 break;
352
353 case tok_machine:
354 /* Start a new machine entry. */
355 maybe_add_to_list (&current, &retval);
356 current->host = xstrdup (tok);
357 break;
358
359 case tok_password:
360 if (current)
361 current->passwd = xstrdup (tok);
362 else
363 premature_token = "password";
364 break;
365
366 /* We handle most of tok_macdef above. */
367 case tok_macdef:
368 if (!current)
369 premature_token = "macdef";
370 break;
371
372 /* We don't handle the account keyword at all. */
373 case tok_account:
374 if (!current)
375 premature_token = "account";
376 break;
377
378 /* We handle tok_nothing below this switch. */
379 case tok_nothing:
380 break;
381 }
382
383 if (premature_token)
384 {
385 fprintf (stderr, _("\
386%s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
387 exec_name, path, ln, premature_token);
388 premature_token = NULL;
389 }
390
391 if (last_token != tok_nothing)
392 /* We got a value, so reset the token state. */
393 last_token = tok_nothing;
394 else
395 {
396 /* Fetch the next token. */
397 if (!strcmp (tok, "account"))
398 last_token = tok_account;
399 else if (!strcmp (tok, "default"))
400 {
401 maybe_add_to_list (&current, &retval);
402 }
403 else if (!strcmp (tok, "login"))
404 last_token = tok_login;
405
406 else if (!strcmp (tok, "macdef"))
407 last_token = tok_macdef;
408
409 else if (!strcmp (tok, "machine"))
410 last_token = tok_machine;
411
412 else if (!strcmp (tok, "password"))
413 last_token = tok_password;
414
415 else
416 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
417 exec_name, path, ln, tok);
418 }
419 }
420
421 xfree (line);
422 }
423
424 fclose (fp);
425
426 /* Finalize the last machine entry we found. */
427 maybe_add_to_list (&current, &retval);
428 xfree (current);
429
430 /* Reverse the order of the list so that it appears in file order. */
431 current = retval;
432 retval = NULL;
433 while (current)
434 {
435 acc_t *saved_reference;
436
437 /* Change the direction of the pointers. */
438 saved_reference = current->next;
439 current->next = retval;
440
441 /* Advance to the next node. */
442 retval = current;
443 current = saved_reference;
444 }
445
446 return retval;
447}
448
449
450/* Free a netrc list. */
451void
452free_netrc(acc_t *l)
453{
454 acc_t *t;
455
456 while (l)
457 {
458 t = l->next;
459 xfree_null (l->acc);
460 xfree_null (l->passwd);
461 xfree_null (l->host);
462 xfree (l);
463 l = t;
464 }
465}
466
467#ifdef STANDALONE
468#include <sys/types.h>
469#include <sys/stat.h>
470
471int
472main (int argc, char **argv)
473{
474 struct_stat sb;
475 char *program_name, *file, *target;
476 acc_t *head, *a;
477
478 if (argc < 2 || argc > 3)
479 {
480 fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
481 exit (1);
482 }
483
484 program_name = argv[0];
485 file = argv[1];
486 target = argv[2];
487
488 if (stat (file, &sb))
489 {
490 fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
491 strerror (errno));
492 exit (1);
493 }
494
495 head = parse_netrc (file);
496 a = head;
497 while (a)
498 {
499 /* Skip if we have a target and this isn't it. */
500 if (target && a->host && strcmp (target, a->host))
501 {
502 a = a->next;
503 continue;
504 }
505
506 if (!target)
507 {
508 /* Print the host name if we have no target. */
509 if (a->host)
510 fputs (a->host, stdout);
511 else
512 fputs ("DEFAULT", stdout);
513
514 fputc (' ', stdout);
515 }
516
517 /* Print the account name. */
518 fputs (a->acc, stdout);
519
520 if (a->passwd)
521 {
522 /* Print the password, if there is any. */
523 fputc (' ', stdout);
524 fputs (a->passwd, stdout);
525 }
526
527 fputc ('\n', stdout);
528
529 /* Exit if we found the target. */
530 if (target)
531 exit (0);
532 a = a->next;
533 }
534
535 /* Exit with failure if we had a target, success otherwise. */
536 if (target)
537 exit (1);
538
539 exit (0);
540}
541#endif /* STANDALONE */
Note: See TracBrowser for help on using the repository browser.