]> sjero.net Git - wget/blob - src/netrc.c
[svn] Make sure xfree is #define'd in standalone mode in files that
[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           xfree (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 xfree free
147 # define xstrdup strdup
148
149 /* The function reads a whole line.  It reads the line realloc-ing the
150    storage exponentially, doubling the storage after each overflow to
151    minimize the number of calls to realloc().
152
153    It is not an exemplary of correctness, since it kills off the
154    newline (and no, there is no way to know if there was a newline at
155    EOF).  */
156 # define xrealloc realloc
157 # define DYNAMIC_LINE_BUFFER 40
158
159 char *
160 read_whole_line (FILE *fp)
161 {
162   char *line;
163   int i, bufsize, c;
164
165   i = 0;
166   bufsize = DYNAMIC_LINE_BUFFER;
167   line = xmalloc(bufsize);
168   /* Construct the line.  */
169   while ((c = getc(fp)) != EOF && c != '\n')
170     {
171       if (i > bufsize - 1)
172         line = (char *)xrealloc(line, (bufsize <<= 1));
173       line[i++] = c;
174     }
175   if (c == EOF && !i)
176     {
177       xfree(line);
178       return NULL;
179     }
180
181   /* Check for overflow at zero-termination (no need to double the
182      buffer in this case.  */
183   if (i == bufsize)
184     line = (char *)xrealloc(line, i + 1);
185   line[i] = '\0';
186   return line;
187 }
188
189 #endif /* STANDALONE */
190
191 /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
192    set to a ready-to-use acc_t, in any event.  */
193 static void
194 maybe_add_to_list (acc_t **newentry, acc_t **list)
195 {
196   acc_t *a, *l;
197   a = *newentry;
198   l = *list;
199
200   /* We need an account name in order to add the entry to the list.  */
201   if (a && ! a->acc)
202     {
203       /* Free any allocated space.  */
204       xfree (a->host);
205       xfree (a->acc);
206       xfree (a->passwd);
207     }
208   else
209     {
210       if (a)
211         {
212           /* Add the current machine into our list.  */
213           a->next = l;
214           l = a;
215         }
216
217       /* Allocate a new acc_t structure.  */
218       a = (acc_t *)xmalloc (sizeof (acc_t));
219     }
220
221   /* Zero the structure, so that it is ready to use.  */
222   memset (a, 0, sizeof(*a));
223
224   /* Return the new pointers.  */
225   *newentry = a;
226   *list = l;
227   return;
228 }
229
230 /* Helper function for the parser, shifts contents of
231    null-terminated string once character to the left.
232    Used in processing \ and " constructs in the netrc file */
233 static void
234 shift_left(char *string){
235   char *p;
236   
237   for (p=string; *p; ++p)
238     *p = *(p+1);
239 }
240
241 /* Parse a .netrc file (as described in the ftp(1) manual page).  */
242 static acc_t *
243 parse_netrc (const char *path)
244 {
245   FILE *fp;
246   char *line, *p, *tok, *premature_token;
247   acc_t *current, *retval;
248   int ln, quote;
249
250   /* The latest token we've seen in the file.  */
251   enum
252   {
253     tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
254   } last_token = tok_nothing;
255
256   current = retval = NULL;
257
258   fp = fopen (path, "r");
259   if (!fp)
260     {
261       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
262                path, strerror (errno));
263       return retval;
264     }
265
266   /* Initialize the file data.  */
267   ln = 0;
268   premature_token = NULL;
269
270   /* While there are lines in the file...  */
271   while ((line = read_whole_line (fp)))
272     {
273       ln ++;
274
275       /* Parse the line.  */
276       p = line;
277       quote = 0;
278
279       /* If the line is empty, then end any macro definition.  */
280       if (last_token == tok_macdef && !*p)
281         /* End of macro if the line is empty.  */
282         last_token = tok_nothing;
283
284       /* If we are defining macros, then skip parsing the line.  */
285       while (*p && last_token != tok_macdef)
286         {
287           /* Skip any whitespace.  */
288           while (*p && ISSPACE (*p))
289             p ++;
290
291           /* Discard end-of-line comments.  */
292           if (*p == '#')
293             break;
294
295           /* If the token starts with quotation mark, note this fact,
296              and squash the quotation character */
297           if (*p == '"'){
298             quote = 1;
299             shift_left (p);
300           }
301
302           tok = p;
303
304           /* Find the end of the token, handling quotes and escapes.  */
305           while (*p && (quote ? *p != '"' : !ISSPACE (*p))){
306             if (*p == '\\')
307               shift_left (p);
308             p ++;
309           }
310
311           /* if field was quoted, squash the trailing quotation mark */
312           if (quote)
313             shift_left(p);
314
315           /* Null-terminate the token, if it isn't already.  */
316           if (*p)
317             *p ++ = '\0';
318
319           switch (last_token)
320             {
321             case tok_login:
322               if (current)
323                 current->acc = xstrdup (tok);
324               else
325                 premature_token = "login";
326               break;
327
328             case tok_machine:
329               /* Start a new machine entry.  */
330               maybe_add_to_list (&current, &retval);
331               current->host = xstrdup (tok);
332               break;
333
334             case tok_password:
335               if (current)
336                 current->passwd = xstrdup (tok);
337               else
338                 premature_token = "password";
339               break;
340
341               /* We handle most of tok_macdef above.  */
342             case tok_macdef:
343               if (!current)
344                 premature_token = "macdef";
345               break;
346
347               /* We don't handle the account keyword at all.  */
348             case tok_account:
349               if (!current)
350                 premature_token = "account";
351               break;
352
353               /* We handle tok_nothing below this switch.  */
354             case tok_nothing:
355               break;
356             }
357
358           if (premature_token)
359             {
360               fprintf (stderr, _("\
361 %s: %s:%d: warning: \"%s\" token appears before any machine name\n"),
362                        exec_name, path, ln, premature_token);
363               premature_token = NULL;
364             }
365
366           if (last_token != tok_nothing)
367             /* We got a value, so reset the token state.  */
368             last_token = tok_nothing;
369           else
370             {
371               /* Fetch the next token.  */
372               if (!strcmp (tok, "account"))
373                 last_token = tok_account;
374               else if (!strcmp (tok, "default"))
375                 {
376                   maybe_add_to_list (&current, &retval);
377                 }
378               else if (!strcmp (tok, "login"))
379                 last_token = tok_login;
380
381               else if (!strcmp (tok, "macdef"))
382                 last_token = tok_macdef;
383
384               else if (!strcmp (tok, "machine"))
385                 last_token = tok_machine;
386
387               else if (!strcmp (tok, "password"))
388                 last_token = tok_password;
389
390               else
391                 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"),
392                          exec_name, path, ln, tok);
393             }
394         }
395
396       xfree (line);
397     }
398
399   fclose (fp);
400
401   /* Finalize the last machine entry we found.  */
402   maybe_add_to_list (&current, &retval);
403   xfree (current);
404
405   /* Reverse the order of the list so that it appears in file order.  */
406   current = retval;
407   retval = NULL;
408   while (current)
409     {
410       acc_t *saved_reference;
411
412       /* Change the direction of the pointers.  */
413       saved_reference = current->next;
414       current->next = retval;
415
416       /* Advance to the next node.  */
417       retval = current;
418       current = saved_reference;
419     }
420
421   return retval;
422 }
423
424
425 /* Free a netrc list.  */
426 void
427 free_netrc(acc_t *l)
428 {
429   acc_t *t;
430
431   while (l)
432     {
433       t = l->next;
434       FREE_MAYBE (l->acc);
435       FREE_MAYBE (l->passwd);
436       FREE_MAYBE (l->host);
437       xfree (l);
438       l = t;
439     }
440 }
441
442 #ifdef STANDALONE
443 #include <sys/types.h>
444 #include <sys/stat.h>
445
446 int
447 main (int argc, char **argv)
448 {
449   struct stat sb;
450   char *program_name, *file, *target;
451   acc_t *head, *a;
452
453   if (argc < 2 || argc > 3)
454     {
455       fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]);
456       exit (1);
457     }
458
459   program_name = argv[0];
460   file = argv[1];
461   target = argv[2];
462
463   if (stat (file, &sb))
464     {
465       fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file,
466                strerror (errno));
467       exit (1);
468     }
469
470   head = parse_netrc (file);
471   a = head;
472   while (a)
473     {
474       /* Skip if we have a target and this isn't it.  */
475       if (target && a->host && strcmp (target, a->host))
476         {
477           a = a->next;
478           continue;
479         }
480
481       if (!target)
482         {
483           /* Print the host name if we have no target.  */
484           if (a->host)
485             fputs (a->host, stdout);
486           else
487             fputs ("DEFAULT", stdout);
488
489           fputc (' ', stdout);
490         }
491
492       /* Print the account name.  */
493       fputs (a->acc, stdout);
494
495       if (a->passwd)
496         {
497           /* Print the password, if there is any.  */
498           fputc (' ', stdout);
499           fputs (a->passwd, stdout);
500         }
501
502       fputc ('\n', stdout);
503
504       /* Exit if we found the target.  */
505       if (target)
506         exit (0);
507       a = a->next;
508     }
509
510   /* Exit with failure if we had a target, success otherwise.  */
511   if (target)
512     exit (1);
513
514   exit (0);
515 }
516 #endif /* STANDALONE */