]> Pileus Git - ~andy/fetchmail/blob - netrc.c
b95d134d9e6d417307583c65acbfa64577447254
[~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         if (a->host)
55             free (a->host);
56         if (a->password)
57             free (a->password);
58     }
59     else
60     {
61         if (a)
62         {
63             /* Add the current machine into our list. */
64             a->next = l;
65             l = a;
66         }
67
68         /* Allocate a new netrc_entry structure. */
69         a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
70     }
71
72     /* Zero the structure, so that it is ready to use. */
73     memset (a, 0, sizeof(*a));
74
75     /* Return the new pointers. */
76     *newentry = a;
77     *list = l;
78     return;
79 }
80
81
82 /* Parse FILE as a .netrc file (as described in ftp(1)), and return a
83    list of entries.  NULL is returned if the file could not be
84    parsed. */
85 netrc_entry *
86 parse_netrc (file)
87      char *file;
88 {
89     FILE *fp;
90     char buf[POPBUFSIZE+1], *p, *tok, *premature_token;
91     netrc_entry *current, *retval;
92     int ln;
93
94     /* The latest token we've seen in the file. */
95     enum
96     {
97         tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
98     } last_token = tok_nothing;
99
100     current = retval = NULL;
101
102     fp = fopen (file, "r");
103     if (!fp)
104     {
105         /* Just return NULL if we can't open the file. */
106         return NULL;
107     }
108
109     /* Initialize the file data. */
110     ln = 0;
111     premature_token = NULL;
112
113     /* While there are lines in the file... */
114     while (fgets(buf, POPBUFSIZE, fp))
115     {
116         ln ++;
117
118         /* Strip trailing CRLF */
119         p = buf + strlen(buf) - 1;
120         while (*p && isspace(*p))
121             *p-- = '\0';
122
123         /* Parse the line. */
124         p = buf;
125
126         /* If the line is empty... */
127         if (!*p)
128             if (last_token == tok_macdef)       /* end of macro */
129                 last_token = tok_nothing;
130             else
131                 continue;                       /* otherwise ignore it */
132
133         /* If we are defining macros, then skip parsing the line. */
134         while (*p && last_token != tok_macdef)
135         {
136             /* Skip any whitespace. */
137             while (*p && isspace (*p))
138                 p++;
139
140             /* Discard end-of-line comments. */
141             if (*p == '#')
142                 break;
143
144             tok = p;
145
146             /* Find the end of the token. */
147             while (*p && !isspace (*p))
148                 p ++;
149
150             /* Null-terminate the token, if it isn't already. */
151             if (*p)
152                 *p ++ = '\0';
153
154             switch (last_token)
155             {
156             case tok_login:
157                 if (current)
158                     current->account = (char *) xstrdup (tok);
159                 else
160                     premature_token = "login";
161                 break;
162
163             case tok_machine:
164                 /* Start a new machine entry. */
165                 maybe_add_to_list (&current, &retval);
166                 current->host = (char *) xstrdup (tok);
167                 break;
168
169             case tok_password:
170                 if (current)
171                     current->password = (char *) xstrdup (tok);
172                 else
173                     premature_token = "password";
174                 break;
175
176                 /* We handle most of tok_macdef above. */
177             case tok_macdef:
178                 if (!current)
179                     premature_token = "macdef";
180                 break;
181
182                 /* We don't handle the account keyword at all. */
183             case tok_account:
184                 if (!current)
185                     premature_token = "account";
186                 break;
187
188                 /* We handle tok_nothing below this switch. */
189             case tok_nothing:
190                 break;
191             }
192
193             if (premature_token)
194             {
195 #ifdef HAVE_ERROR
196                 error_at_line (0, 0, file, ln,
197                                _("warning: found \"%s\" before any host names"),
198                                premature_token);
199 #else
200                 fprintf (stderr,
201                          "%s:%d: warning: found \"%s\" before any host names\n",
202                          file, ln, premature_token);
203 #endif
204                 premature_token = NULL;
205             }
206
207             if (last_token != tok_nothing)
208                 /* We got a value, so reset the token state. */
209                 last_token = tok_nothing;
210             else
211             {
212                 /* Fetch the next token. */
213                 if (!strcmp (tok, "account"))
214                     last_token = tok_account;
215
216                 if (!strcmp (tok, "default"))
217                 {
218                     maybe_add_to_list (&current, &retval);
219                 }
220                 else if (!strcmp (tok, "login"))
221                     last_token = tok_login;
222
223                 else if (!strcmp (tok, "macdef"))
224                     last_token = tok_macdef;
225
226                 else if (!strcmp (tok, "machine"))
227                     last_token = tok_machine;
228
229                 else if (!strcmp (tok, "password"))
230                     last_token = tok_password;
231
232                 else
233                 {
234                     fprintf (stderr, "%s:%d: warning: unknown token \"%s\"\n",
235                              file, ln, tok);
236                 }
237             }
238         }
239     }
240
241     fclose (fp);
242
243     /* Finalize the last machine entry we found. */
244     maybe_add_to_list (&current, &retval);
245     free (current);
246
247     /* Reverse the order of the list so that it appears in file order. */
248     current = retval;
249     retval = NULL;
250     while (current)
251     {
252         netrc_entry *saved_reference;
253
254         /* Change the direction of the pointers. */
255         saved_reference = current->next;
256         current->next = retval;
257
258         /* Advance to the next node. */
259         retval = current;
260         current = saved_reference;
261     }
262
263     return retval;
264 }
265
266
267 /* Return the netrc entry from LIST corresponding to HOST.  NULL is
268    returned if no such entry exists. */
269 netrc_entry *
270 search_netrc (list, host)
271      netrc_entry *list;
272      char *host;
273 {
274     /* Look for the HOST in LIST. */
275     while (list)
276     {
277         if (!list->host)
278             /* We hit the default entry. */
279             break;
280
281         else if (!strcmp (list->host, host))
282             /* We found a matching entry. */
283             break;
284
285         list = list->next;
286     }
287
288     /* Return the matching entry, or NULL. */
289     return list;
290 }
291
292
293 #ifdef STANDALONE
294 #include <sys/types.h>
295 #include <sys/stat.h>
296
297 extern int errno;
298
299 int
300 main (argc, argv)
301      int argc;
302      char **argv;
303 {
304     struct stat sb;
305     char *program_name, *file, *target;
306     netrc_entry *head, *a;
307
308     if (argc < 2)
309     {
310         fprintf (stderr, "Usage: %s NETRC [HOSTNAME]...\n", argv[0]);
311         exit (1);
312     }
313
314     program_name = argv[0];
315     file = argv[1];
316     target = argv[2];
317
318     if (stat (file, &sb))
319     {
320         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
321                  strerror (errno));
322         exit (1);
323     }
324
325     head = parse_netrc (file);
326     if (!head)
327     {
328         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
329         exit (1);
330     }
331
332     if (argc > 2)
333     {
334         int i, status;
335         status = 0;
336         for (i = 2; i < argc; i++)
337         {
338             /* Print out the host that we are checking for. */
339             fputs (argv[i], stdout);
340
341             a = search_netrc (head, argv[i]);
342             if (a)
343             {
344                 /* Print out the account and password (if any). */
345                 fputc (' ', stdout);
346                 fputs (a->account, stdout);
347                 if (a->password)
348                 {
349                     fputc (' ', stdout);
350                     fputs (a->password, stdout);
351                 }
352             }
353             else
354                 status = 1;
355
356             fputc ('\n', stdout);
357         }
358         exit (status);
359     }
360
361     /* Print out the entire contents of the netrc. */
362     a = head;
363     while (a)
364     {
365         /* Print the host name. */
366         if (a->host)
367             fputs (a->host, stdout);
368         else
369             fputs ("DEFAULT", stdout);
370
371         fputc (' ', stdout);
372
373         /* Print the account name. */
374         fputs (a->account, stdout);
375
376         if (a->password)
377         {
378             /* Print the password, if there is any. */
379             fputc (' ', stdout);
380             fputs (a->password, stdout);
381         }
382
383         fputc ('\n', stdout);
384         a = a->next;
385     }
386
387     exit (0);
388 }
389 #endif /* STANDALONE */