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