]> sjero.net Git - wget/blob - src/netrc.c
[svn] Initial revision
[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 Wget.
5
6 This program 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 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 /* This file used to be kept in synch with the code in Fetchmail, but
21    the latter has diverged since.  */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #ifdef HAVE_STRING_H
31 # include <string.h>
32 #else
33 # include <strings.h>
34 #endif
35 #include <sys/types.h>
36 #include <errno.h>
37
38 #include "wget.h"
39 #include "utils.h"
40 #include "netrc.h"
41 #include "init.h"
42
43 #ifndef errno
44 extern int errno;
45 #endif
46
47 #define NETRC_FILE_NAME ".netrc"
48
49 acc_t *netrc_list;
50
51 static acc_t *parse_netrc PARAMS ((const char *));
52
53 /* Return the correct user and password, given the host, user (as
54    given in the URL), and password (as given in the URL).  May return
55    NULL.
56
57    If SLACK_DEFAULT is set, allow looking for a "default" account.
58    You will typically turn it off for HTTP.  */
59 void
60 search_netrc (const char *host, const char **acc, const char **passwd,
61               int slack_default)
62 {
63   acc_t *l;
64   static int processed_netrc;
65
66   if (!opt.netrc)
67     return;
68   /* Find ~/.netrc.  */
69   if (!processed_netrc)
70     {
71       char *home = home_dir();
72
73       netrc_list = NULL;
74       processed_netrc = 1;
75       if (home)
76         {
77           int err;
78           struct stat buf;
79           char *path = (char *)alloca (strlen (home) + 1
80                                        + strlen (NETRC_FILE_NAME) + 1);
81           sprintf (path, "%s/%s", home, NETRC_FILE_NAME);
82           free (home);
83           err = stat (path, &buf);
84           if (err == 0)
85             netrc_list = parse_netrc (path);
86         }
87     }
88   /* If nothing to do...  */
89   if (!netrc_list)
90     return;
91   /* Acc and password found; all OK.  */
92   if (*acc && *passwd)
93     return;
94   if (!*acc && !slack_default)
95     return;
96   /* Some data not given -- try finding the host.  */
97   for (l = netrc_list; l; l = l->next)
98     {
99       if (!l->host)
100         continue;
101       else if (!strcasecmp (l->host, host))
102         break;
103     }
104   if (l)
105     {
106       if (*acc)
107         {
108           /* Looking for password in .netrc.  */
109           if (!strcmp (l->acc, *acc))
110             *passwd = l->passwd; /* usernames match; password OK */
111           else
112             *passwd = NULL;     /* usernames don't match */
113         }
114       else                      /* NOT *acc */
115         {
116           /* If password was given, use it.  The account is l->acc.  */
117           *acc = l->acc;
118           if (l->passwd)
119             *passwd = l->passwd;
120         }
121       return;
122     }
123   else
124     {
125       if (!slack_default)
126         return;
127       if (*acc)
128         return;
129       /* Try looking for the default account.  */
130       for (l = netrc_list; l; l = l->next)
131         if (!l->host)
132           break;
133       if (!l)
134         return;
135       *acc = l->acc;
136       if (!*passwd)
137         *passwd = l->passwd;
138       return;
139     }
140 }
141
142
143 #ifdef STANDALONE
144 /* Normally, these functions would be defined by your package.  */
145 # define xmalloc malloc
146 # define xstrdup strdup
147
148 /* The function reads a whole line.  It reads the line realloc-ing the
149    storage exponentially, doubling the storage after each overflow to
150    minimize the number of calls to realloc().
151
152    It is not an exemplary of correctness, since it kills off the
153    newline (and no, there is no way to know if there was a newline at
154    EOF).  */
155 # define xrealloc realloc
156 # define DYNAMIC_LINE_BUFFER 40
157
158 char *
159 read_whole_line (FILE *fp)
160 {
161   char *line;
162   int i, bufsize, c;
163
164   i = 0;
165   bufsize = DYNAMIC_LINE_BUFFER;
166   line = xmalloc(bufsize);
167   /* Construct the line.  */
168   while ((c = getc(fp)) != EOF && c != '\n')
169     {
170       if (i > bufsize - 1)
171         line = (char *)xrealloc(line, (bufsize <<= 1));
172       line[i++] = c;
173     }
174   if (c == EOF && !i)
175     {
176       free(line);
177       return NULL;
178     }
179
180   /* Check for overflow at zero-termination (no need to double the
181      buffer in this case.  */
182   if (i == bufsize)
183     line = (char *)xrealloc(line, i + 1);
184   line[i] = '\0';
185   return line;
186 }
187
188 #endif /* STANDALONE */
189
190 /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
191    set to a ready-to-use acc_t, in any event.  */
192 static void
193 maybe_add_to_list (acc_t **newentry, acc_t **list)
194 {
195   acc_t *a, *l;
196   a = *newentry;
197   l = *list;
198
199   /* We need an account name in order to add the entry to the list.  */
200   if (a && ! a->acc)
201     {
202       /* Free any allocated space.  */
203       free (a->host);
204       free (a->acc);
205       free (a->passwd);
206     }
207   else
208     {
209       if (a)
210         {
211           /* Add the current machine into our list.  */
212           a->next = l;
213           l = a;
214         }
215
216       /* Allocate a new acc_t structure.  */
217       a = (acc_t *)xmalloc (sizeof (acc_t));
218     }
219
220   /* Zero the structure, so that it is ready to use.  */
221   memset (a, 0, sizeof(*a));
222
223   /* Return the new pointers.  */
224   *newentry = a;
225   *list = l;
226   return;
227 }
228
229
230 /* Parse a .netrc file (as described in the ftp(1) manual page).  */
231 static acc_t *
232 parse_netrc (const char *path)
233 {
234   FILE *fp;
235   char *line, *p, *tok, *premature_token;
236   acc_t *current, *retval;
237   int ln;
238
239   /* The latest token we've seen in the file.  */
240   enum
241   {
242     tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
243   } last_token = tok_nothing;
244
245   current = retval = NULL;
246
247   fp = fopen (path, "r");
248   if (!fp)
249     {
250       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
251                path, strerror (errno));
252       return retval;
253     }
254
255   /* Initialize the file data.  */
256   ln = 0;
257   premature_token = NULL;
258
259   /* While there are lines in the file...  */
260   while ((line = read_whole_line (fp)))
261     {
262       ln ++;
263
264       /* Parse the line.  */
265       p = line;
266
267       /* If the line is empty, then end any macro definition.  */
268       if (last_token == tok_macdef && !*p)
269         /* End of macro if the line is empty.  */
270         last_token = tok_nothing;
271
272       /* If we are defining macros, then skip parsing the line.  */
273       while (*p && last_token != tok_macdef)
274         {
275           /* Skip any whitespace.  */
276           while (*p && ISSPACE (*p))
277             p ++;
278
279           /* Discard end-of-line comments.  */
280           if (*p == '#')
281             break;
282
283           tok = p;
284
285           /* Find the end of the token.  */
286           while (*p && !ISSPACE (*p))
287             p ++;
288
289           /* Null-terminate the token, if it isn't already.  */
290           if (*p)
291             *p ++ = '\0';
292
293           switch (last_token)
294             {
295             case tok_login:
296               if (current)
297                 current->acc = xstrdup (tok);
298               else
299                 premature_token = "login";
300               break;
301
302             case tok_machine:
303               /* Start a new machine entry.  */
304               maybe_add_to_list (&current, &retval);
305               current->host = xstrdup (tok);
306               break;
307
308             case tok_password:
309               if (current)
310                 current->passwd = xstrdup (tok);
311               else
312                 premature_token = "password";
313               break;
314
315               /* We handle most of tok_macdef above.  */
316             case tok_macdef:
317               if (!current)
318                 premature_token = "macdef";
319               break;
320
321               /* We don't handle the account keyword at all.  */
322             case tok_account:
323               if (!current)
324                 premature_token = "account";
325               break;
326
327               /* We handle tok_nothing below this switch.  */
328             case tok_nothing:
329               break;
330             }
331
332           if (premature_token)
333             {
334               fprintf (stderr, _("\
335 %s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
336                        exec_name, path, ln, premature_token);
337               premature_token = NULL;
338             }
339
340           if (last_token != tok_nothing)
341             /* We got a value, so reset the token state.  */
342             last_token = tok_nothing;
343           else
344             {
345               /* Fetch the next token.  */
346               if (!strcmp (tok, "account"))
347                 last_token = tok_account;
348               else if (!strcmp (tok, "default"))
349                 {
350                   maybe_add_to_list (&current, &retval);
351                 }
352               else if (!strcmp (tok, "login"))
353                 last_token = tok_login;
354
355               else if (!strcmp (tok, "macdef"))
356                 last_token = tok_macdef;
357
358               else if (!strcmp (tok, "machine"))
359                 last_token = tok_machine;
360
361               else if (!strcmp (tok, "password"))
362                 last_token = tok_password;
363
364               else
365                 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
366                          exec_name, path, ln, tok);
367             }
368         }
369
370       free (line);
371     }
372
373   fclose (fp);
374
375   /* Finalize the last machine entry we found.  */
376   maybe_add_to_list (&current, &retval);
377   free (current);
378
379   /* Reverse the order of the list so that it appears in file order.  */
380   current = retval;
381   retval = NULL;
382   while (current)
383     {
384       acc_t *saved_reference;
385
386       /* Change the direction of the pointers.  */
387       saved_reference = current->next;
388       current->next = retval;
389
390       /* Advance to the next node.  */
391       retval = current;
392       current = saved_reference;
393     }
394
395   return retval;
396 }
397
398
399 /* Free a netrc list.  */
400 void
401 free_netrc(acc_t *l)
402 {
403   acc_t *t;
404
405   while (l)
406     {
407       t = l->next;
408       FREE_MAYBE (l->acc);
409       FREE_MAYBE (l->passwd);
410       FREE_MAYBE (l->host);
411       free(l);
412       l = t;
413     }
414 }
415
416 #ifdef STANDALONE
417 #include <sys/types.h>
418 #include <sys/stat.h>
419
420 int
421 main (int argc, char **argv)
422 {
423   struct stat sb;
424   char *program_name, *file, *target;
425   acc_t *head, *a;
426
427   if (argc < 2 || argc > 3)
428     {
429       fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
430       exit (1);
431     }
432
433   program_name = argv[0];
434   file = argv[1];
435   target = argv[2];
436
437   if (stat (file, &sb))
438     {
439       fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
440                strerror (errno));
441       exit (1);
442     }
443
444   head = parse_netrc (file);
445   a = head;
446   while (a)
447     {
448       /* Skip if we have a target and this isn't it.  */
449       if (target && a->host && strcmp (target, a->host))
450         {
451           a = a->next;
452           continue;
453         }
454
455       if (!target)
456         {
457           /* Print the host name if we have no target.  */
458           if (a->host)
459             fputs (a->host, stdout);
460           else
461             fputs ("DEFAULT", stdout);
462
463           fputc (' ', stdout);
464         }
465
466       /* Print the account name.  */
467       fputs (a->acc, stdout);
468
469       if (a->passwd)
470         {
471           /* Print the password, if there is any.  */
472           fputc (' ', stdout);
473           fputs (a->passwd, stdout);
474         }
475
476       fputc ('\n', stdout);
477
478       /* Exit if we found the target.  */
479       if (target)
480         exit (0);
481       a = a->next;
482     }
483
484   /* Exit with failure if we had a target, success otherwise.  */
485   if (target)
486     exit (1);
487
488   exit (0);
489 }
490 #endif /* STANDALONE */