]> Pileus Git - ~andy/fetchmail/blob - netrc.c
a56dfd7b5a1385d5bc0bb7b1d020ac2594686a0f
[~andy/fetchmail] / netrc.c
1 /* netrc.c -- parse the .netrc file to get hosts, accounts, and passwords
2    Copyright (C) 1996, Free Software Foundation, Inc.
3    Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19 /* Compile with -DSTANDALONE to test this module. */
20
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "config.h"
27 #include "fetchmail.h"
28 #include "netrc.h"
29
30 #ifdef STANDALONE
31 /* Normally defined in xstrdup.c. */
32 # define xstrdup strdup
33
34 /* Normally defined in xmalloc.c */
35 # define xmalloc malloc
36 # define xrealloc realloc
37 #endif
38
39 /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
40    set to a ready-to-use netrc_entry, in any event. */
41 static void
42 maybe_add_to_list (newentry, list)
43      netrc_entry **newentry;
44      netrc_entry **list;
45 {
46     netrc_entry *a, *l;
47     a = *newentry;
48     l = *list;
49
50     /* We need an account name in order to add the entry to the list. */
51     if (a && ! a->account)
52     {
53         /* Free any allocated space. */
54         free (a->host);
55         free (a->account);
56         free (a->password);
57     }
58     else
59     {
60         if (a)
61         {
62             /* Add the current machine into our list. */
63             a->next = l;
64             l = a;
65         }
66
67         /* Allocate a new netrc_entry structure. */
68         a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
69     }
70
71     /* Zero the structure, so that it is ready to use. */
72     memset (a, 0, sizeof(*a));
73
74     /* Return the new pointers. */
75     *newentry = a;
76     *list = l;
77     return;
78 }
79
80
81 /* Parse FILE as a .netrc file (as described in ftp(1)), and return a
82    list of entries.  NULL is returned if the file could not be
83    parsed. */
84 netrc_entry *
85 parse_netrc (file)
86      char *file;
87 {
88     FILE *fp;
89     char buf[POPBUFSIZE+1], *p, *tok, *premature_token;
90     netrc_entry *current, *retval;
91     int ln;
92
93     /* The latest token we've seen in the file. */
94     enum
95     {
96         tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
97     } last_token = tok_nothing;
98
99     current = retval = NULL;
100
101     fp = fopen (file, "r");
102     if (!fp)
103     {
104         /* Just return NULL if we can't open the file. */
105         return NULL;
106     }
107
108     /* Initialize the file data. */
109     ln = 0;
110     premature_token = NULL;
111
112     /* While there are lines in the file... */
113     while (fgets(buf, POPBUFSIZE, fp))
114     {
115         ln ++;
116
117         /* Strip trailing CRLF */
118         p = buf + strlen(buf) - 1;
119         while (*p && isspace(*p))
120             *p-- = '\0';
121
122         /* Parse the line. */
123         p = buf;
124
125         /* If the line is empty... */
126         if (!*p)
127             if (last_token == tok_macdef)       /* end of macro */
128                 last_token = tok_nothing;
129             else
130                 continue;                       /* otherwise ignore it */
131
132         /* If we are defining macros, then skip parsing the line. */
133         while (*p && last_token != tok_macdef)
134         {
135             /* Skip any whitespace. */
136             while (*p && isspace (*p))
137                 p++;
138
139             /* Discard end-of-line comments. */
140             if (*p == '#')
141                 break;
142
143             tok = p;
144
145             /* Find the end of the token. */
146             while (*p && !isspace (*p))
147                 p ++;
148
149             /* Null-terminate the token, if it isn't already. */
150             if (*p)
151                 *p ++ = '\0';
152
153             switch (last_token)
154             {
155             case tok_login:
156                 if (current)
157                     current->account = (char *) xstrdup (tok);
158                 else
159                     premature_token = "login";
160                 break;
161
162             case tok_machine:
163                 /* Start a new machine entry. */
164                 maybe_add_to_list (&current, &retval);
165                 current->host = (char *) xstrdup (tok);
166                 break;
167
168             case tok_password:
169                 if (current)
170                     current->password = (char *) xstrdup (tok);
171                 else
172                     premature_token = "password";
173                 break;
174
175                 /* We handle most of tok_macdef above. */
176             case tok_macdef:
177                 if (!current)
178                     premature_token = "macdef";
179                 break;
180
181                 /* We don't handle the account keyword at all. */
182             case tok_account:
183                 if (!current)
184                     premature_token = "account";
185                 break;
186
187                 /* We handle tok_nothing below this switch. */
188             case tok_nothing:
189                 break;
190             }
191
192             if (premature_token)
193             {
194 #ifdef HAVE_ERROR
195                 error_at_line (0, 0, file, ln,
196                                _("warning: found \"%s\" before any host names"),
197                                premature_token);
198 #else
199                 fprintf (stderr,
200                          "%s:%d: warning: found \"%s\" before any host names\n",
201                          file, ln, premature_token);
202 #endif
203                 premature_token = NULL;
204             }
205
206             if (last_token != tok_nothing)
207                 /* We got a value, so reset the token state. */
208                 last_token = tok_nothing;
209             else
210             {
211                 /* Fetch the next token. */
212                 if (!strcmp (tok, "account"))
213                     last_token = tok_account;
214
215                 if (!strcmp (tok, "default"))
216                 {
217                     maybe_add_to_list (&current, &retval);
218                 }
219                 else if (!strcmp (tok, "login"))
220                     last_token = tok_login;
221
222                 else if (!strcmp (tok, "macdef"))
223                     last_token = tok_macdef;
224
225                 else if (!strcmp (tok, "machine"))
226                     last_token = tok_machine;
227
228                 else if (!strcmp (tok, "password"))
229                     last_token = tok_password;
230
231                 else
232                 {
233                     fprintf (stderr, "%s:%d: warning: unknown token \"%s\"\n",
234                              file, ln, tok);
235                 }
236             }
237         }
238     }
239
240     fclose (fp);
241
242     /* Finalize the last machine entry we found. */
243     maybe_add_to_list (&current, &retval);
244     free (current);
245
246     /* Reverse the order of the list so that it appears in file order. */
247     current = retval;
248     retval = NULL;
249     while (current)
250     {
251         netrc_entry *saved_reference;
252
253         /* Change the direction of the pointers. */
254         saved_reference = current->next;
255         current->next = retval;
256
257         /* Advance to the next node. */
258         retval = current;
259         current = saved_reference;
260     }
261
262     return retval;
263 }
264
265
266 /* Return the netrc entry from LIST corresponding to HOST.  NULL is
267    returned if no such entry exists. */
268 netrc_entry *
269 search_netrc (list, host)
270      netrc_entry *list;
271      char *host;
272 {
273     /* Look for the HOST in LIST. */
274     while (list)
275     {
276         if (!list->host)
277             /* We hit the default entry. */
278             break;
279
280         else if (!strcmp (list->host, host))
281             /* We found a matching entry. */
282             break;
283
284         list = list->next;
285     }
286
287     /* Return the matching entry, or NULL. */
288     return list;
289 }
290
291
292 #ifdef STANDALONE
293 #include <sys/types.h>
294 #include <sys/stat.h>
295
296 extern int errno;
297
298 int
299 main (argc, argv)
300      int argc;
301      char **argv;
302 {
303     struct stat sb;
304     char *program_name, *file, *target;
305     netrc_entry *head, *a;
306
307     if (argc < 2)
308     {
309         fprintf (stderr, "Usage: %s NETRC [HOSTNAME]...\n", argv[0]);
310         exit (1);
311     }
312
313     program_name = argv[0];
314     file = argv[1];
315     target = argv[2];
316
317     if (stat (file, &sb))
318     {
319         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
320                  strerror (errno));
321         exit (1);
322     }
323
324     head = parse_netrc (file);
325     if (!head)
326     {
327         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
328         exit (1);
329     }
330
331     if (argc > 2)
332     {
333         int i, status;
334         status = 0;
335         for (i = 2; i < argc; i++)
336         {
337             /* Print out the host that we are checking for. */
338             fputs (argv[i], stdout);
339
340             a = search_netrc (head, argv[i]);
341             if (a)
342             {
343                 /* Print out the account and password (if any). */
344                 fputc (' ', stdout);
345                 fputs (a->account, stdout);
346                 if (a->password)
347                 {
348                     fputc (' ', stdout);
349                     fputs (a->password, stdout);
350                 }
351             }
352             else
353                 status = 1;
354
355             fputc ('\n', stdout);
356         }
357         exit (status);
358     }
359
360     /* Print out the entire contents of the netrc. */
361     a = head;
362     while (a)
363     {
364         /* Print the host name. */
365         if (a->host)
366             fputs (a->host, stdout);
367         else
368             fputs ("DEFAULT", stdout);
369
370         fputc (' ', stdout);
371
372         /* Print the account name. */
373         fputs (a->account, stdout);
374
375         if (a->password)
376         {
377             /* Print the password, if there is any. */
378             fputc (' ', stdout);
379             fputs (a->password, stdout);
380         }
381
382         fputc ('\n', stdout);
383         a = a->next;
384     }
385
386     exit (0);
387 }
388 #endif /* STANDALONE */