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