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