]> sjero.net Git - wget/blob - src/netrc.c
[svn] Reset the QUOTE flag after the closing quote.
[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 (a->host);
219       xfree (a->acc);
220       xfree (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, *premature_token;
262   acc_t *current, *retval;
263   int ln, quote;
264
265   /* The latest token we've seen in the file.  */
266   enum
267   {
268     tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
269   } last_token = tok_nothing;
270
271   current = retval = NULL;
272
273   fp = fopen (path, "r");
274   if (!fp)
275     {
276       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
277                path, strerror (errno));
278       return retval;
279     }
280
281   /* Initialize the file data.  */
282   ln = 0;
283   premature_token = NULL;
284
285   /* While there are lines in the file...  */
286   while ((line = read_whole_line (fp)))
287     {
288       ln ++;
289
290       /* Parse the line.  */
291       p = line;
292       quote = 0;
293
294       /* Skip leading whitespace.  */
295       while (*p && ISSPACE (*p))
296         p ++;
297
298       /* If the line is empty, then end any macro definition.  */
299       if (last_token == tok_macdef && !*p)
300         /* End of macro if the line is empty.  */
301         last_token = tok_nothing;
302
303       /* If we are defining macros, then skip parsing the line.  */
304       while (*p && last_token != tok_macdef)
305         {
306           /* Skip any whitespace.  */
307           while (*p && ISSPACE (*p))
308             p ++;
309
310           /* Discard end-of-line comments; also, stop processing if
311              the above `while' merely skipped trailing whitespace.  */
312           if (*p == '#' || !*p)
313             break;
314
315           /* If the token starts with quotation mark, note this fact,
316              and squash the quotation character */
317           if (*p == '"'){
318             quote = 1;
319             shift_left (p);
320           }
321
322           tok = p;
323
324           /* Find the end of the token, handling quotes and escapes.  */
325           while (*p && (quote ? *p != '"' : !ISSPACE (*p))){
326             if (*p == '\\')
327               shift_left (p);
328             p ++;
329           }
330
331           /* If field was quoted, squash the trailing quotation mark
332              and reset quote flag.  */
333           if (quote)
334             {
335               shift_left (p);
336               quote = 0;
337             }
338
339           /* Null-terminate the token, if it isn't already.  */
340           if (*p)
341             *p ++ = '\0';
342
343           switch (last_token)
344             {
345             case tok_login:
346               if (current)
347                 current->acc = xstrdup (tok);
348               else
349                 premature_token = "login";
350               break;
351
352             case tok_machine:
353               /* Start a new machine entry.  */
354               maybe_add_to_list (&current, &retval);
355               current->host = xstrdup (tok);
356               break;
357
358             case tok_password:
359               if (current)
360                 current->passwd = xstrdup (tok);
361               else
362                 premature_token = "password";
363               break;
364
365               /* We handle most of tok_macdef above.  */
366             case tok_macdef:
367               if (!current)
368                 premature_token = "macdef";
369               break;
370
371               /* We don't handle the account keyword at all.  */
372             case tok_account:
373               if (!current)
374                 premature_token = "account";
375               break;
376
377               /* We handle tok_nothing below this switch.  */
378             case tok_nothing:
379               break;
380             }
381
382           if (premature_token)
383             {
384               fprintf (stderr, _("\
385 %s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
386                        exec_name, path, ln, premature_token);
387               premature_token = NULL;
388             }
389
390           if (last_token != tok_nothing)
391             /* We got a value, so reset the token state.  */
392             last_token = tok_nothing;
393           else
394             {
395               /* Fetch the next token.  */
396               if (!strcmp (tok, "account"))
397                 last_token = tok_account;
398               else if (!strcmp (tok, "default"))
399                 {
400                   maybe_add_to_list (&current, &retval);
401                 }
402               else if (!strcmp (tok, "login"))
403                 last_token = tok_login;
404
405               else if (!strcmp (tok, "macdef"))
406                 last_token = tok_macdef;
407
408               else if (!strcmp (tok, "machine"))
409                 last_token = tok_machine;
410
411               else if (!strcmp (tok, "password"))
412                 last_token = tok_password;
413
414               else
415                 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
416                          exec_name, path, ln, tok);
417             }
418         }
419
420       xfree (line);
421     }
422
423   fclose (fp);
424
425   /* Finalize the last machine entry we found.  */
426   maybe_add_to_list (&current, &retval);
427   xfree (current);
428
429   /* Reverse the order of the list so that it appears in file order.  */
430   current = retval;
431   retval = NULL;
432   while (current)
433     {
434       acc_t *saved_reference;
435
436       /* Change the direction of the pointers.  */
437       saved_reference = current->next;
438       current->next = retval;
439
440       /* Advance to the next node.  */
441       retval = current;
442       current = saved_reference;
443     }
444
445   return retval;
446 }
447
448
449 /* Free a netrc list.  */
450 void
451 free_netrc(acc_t *l)
452 {
453   acc_t *t;
454
455   while (l)
456     {
457       t = l->next;
458       FREE_MAYBE (l->acc);
459       FREE_MAYBE (l->passwd);
460       FREE_MAYBE (l->host);
461       xfree (l);
462       l = t;
463     }
464 }
465
466 #ifdef STANDALONE
467 #include <sys/types.h>
468 #include <sys/stat.h>
469
470 int
471 main (int argc, char **argv)
472 {
473   struct stat sb;
474   char *program_name, *file, *target;
475   acc_t *head, *a;
476
477   if (argc < 2 || argc > 3)
478     {
479       fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
480       exit (1);
481     }
482
483   program_name = argv[0];
484   file = argv[1];
485   target = argv[2];
486
487   if (stat (file, &sb))
488     {
489       fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
490                strerror (errno));
491       exit (1);
492     }
493
494   head = parse_netrc (file);
495   a = head;
496   while (a)
497     {
498       /* Skip if we have a target and this isn't it.  */
499       if (target && a->host && strcmp (target, a->host))
500         {
501           a = a->next;
502           continue;
503         }
504
505       if (!target)
506         {
507           /* Print the host name if we have no target.  */
508           if (a->host)
509             fputs (a->host, stdout);
510           else
511             fputs ("DEFAULT", stdout);
512
513           fputc (' ', stdout);
514         }
515
516       /* Print the account name.  */
517       fputs (a->acc, stdout);
518
519       if (a->passwd)
520         {
521           /* Print the password, if there is any.  */
522           fputc (' ', stdout);
523           fputs (a->passwd, stdout);
524         }
525
526       fputc ('\n', stdout);
527
528       /* Exit if we found the target.  */
529       if (target)
530         exit (0);
531       a = a->next;
532     }
533
534   /* Exit with failure if we had a target, success otherwise.  */
535   if (target)
536     exit (1);
537
538   exit (0);
539 }
540 #endif /* STANDALONE */