]> sjero.net Git - wget/blob - src/netrc.c
[svn] Large file support added. Published in <87psyr6jn7.fsf@xemacs.org>.
[wget] / src / netrc.c
1 /* Read and parse the .netrc file to get hosts, accounts, and passwords.
2    Copyright (C) 1996, Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
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.
10
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.
15
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.
19
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.  */
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
53 extern int errno;
54 #endif
55
56 #define NETRC_FILE_NAME ".netrc"
57
58 acc_t *netrc_list;
59
60 static 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.  */
68 void
69 search_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
171 char *
172 read_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.  */
207 static void
208 maybe_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 */
247 static void
248 shift_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).  */
257 static acc_t *
258 parse_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)))
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.  */
451 void
452 free_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
471 int
472 main (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 */