]> Pileus Git - ~andy/fetchmail/blob - netrc.c
44339590b0ee549a7c62e324026e9d518e9020a3
[~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             char quote_char = 0;
137             char *pp;
138
139             /* Skip any whitespace. */
140             while (*p && isspace (*p))
141                 p++;
142
143             /* Discard end-of-line comments. */
144             if (*p == '#')
145                 break;
146
147             tok = pp = p;
148
149             /* Find the end of the token. */
150             while (*p && (quote_char || !isspace (*p)))
151             {
152                 if (quote_char)
153                 {
154                     if (quote_char == *p)
155                     {
156                         quote_char = 0;
157                         p ++;
158                     }
159                     else
160                     {
161                         *pp = *p;
162                         p ++;
163                         pp ++;
164                     }
165                 }
166                 else
167                 {
168                     if (*p == '"' || *p == '\'')
169                         quote_char = *p;
170                     else
171                     {
172                         *pp = *p;
173                         pp ++;
174                     }
175                     p ++;
176                 }
177             }
178             /* Null-terminate the token, if it isn't already. */
179             if (*p)
180                 *p ++ = '\0';
181             *pp = 0;
182
183             switch (last_token)
184             {
185             case tok_login:
186                 if (current)
187                     current->account = (char *) xstrdup (tok);
188                 else
189                     premature_token = "login";
190                 break;
191
192             case tok_machine:
193                 /* Start a new machine entry. */
194                 maybe_add_to_list (&current, &retval);
195                 current->host = (char *) xstrdup (tok);
196                 break;
197
198             case tok_password:
199                 if (current)
200                     current->password = (char *) xstrdup (tok);
201                 else
202                     premature_token = "password";
203                 break;
204
205                 /* We handle most of tok_macdef above. */
206             case tok_macdef:
207                 if (!current)
208                     premature_token = "macdef";
209                 break;
210
211                 /* We don't handle the account keyword at all. */
212             case tok_account:
213                 if (!current)
214                     premature_token = "account";
215                 break;
216
217                 /* We handle tok_nothing below this switch. */
218             case tok_nothing:
219                 break;
220             }
221
222             if (premature_token)
223             {
224 #ifdef HAVE_ERROR
225                 error_at_line (0, 0, file, ln,
226                                _("warning: found \"%s\" before any host names"),
227                                premature_token);
228 #else
229                 fprintf (stderr,
230                          "%s:%d: warning: found \"%s\" before any host names\n",
231                          file, ln, premature_token);
232 #endif
233                 premature_token = NULL;
234             }
235
236             if (last_token != tok_nothing)
237                 /* We got a value, so reset the token state. */
238                 last_token = tok_nothing;
239             else
240             {
241                 /* Fetch the next token. */
242                 if (!strcmp (tok, "account"))
243                     last_token = tok_account;
244
245                 if (!strcmp (tok, "default"))
246                 {
247                     maybe_add_to_list (&current, &retval);
248                 }
249                 else if (!strcmp (tok, "login"))
250                     last_token = tok_login;
251
252                 else if (!strcmp (tok, "user"))
253                     last_token = tok_login;
254
255                 else if (!strcmp (tok, "macdef"))
256                     last_token = tok_macdef;
257
258                 else if (!strcmp (tok, "machine"))
259                     last_token = tok_machine;
260
261                 else if (!strcmp (tok, "password"))
262                     last_token = tok_password;
263
264                 else
265                 {
266                     fprintf (stderr, "%s:%d: warning: unknown token \"%s\"\n",
267                              file, ln, tok);
268                 }
269             }
270         }
271     }
272
273     fclose (fp);
274
275     /* Finalize the last machine entry we found. */
276     maybe_add_to_list (&current, &retval);
277     free (current);
278
279     /* Reverse the order of the list so that it appears in file order. */
280     current = retval;
281     retval = NULL;
282     while (current)
283     {
284         netrc_entry *saved_reference;
285
286         /* Change the direction of the pointers. */
287         saved_reference = current->next;
288         current->next = retval;
289
290         /* Advance to the next node. */
291         retval = current;
292         current = saved_reference;
293     }
294
295     return retval;
296 }
297
298
299 /* Return the netrc entry from LIST corresponding to HOST.  NULL is
300    returned if no such entry exists. */
301 netrc_entry *
302 search_netrc (list, host)
303      netrc_entry *list;
304      char *host;
305 {
306     /* Look for the HOST in LIST. */
307     while (list)
308     {
309         if (!list->host)
310             /* We hit the default entry. */
311             break;
312
313         else if (!strcmp (list->host, host))
314             /* We found a matching entry. */
315             break;
316
317         list = list->next;
318     }
319
320     /* Return the matching entry, or NULL. */
321     return list;
322 }
323
324
325 #ifdef STANDALONE
326 #include <sys/types.h>
327 #include <sys/stat.h>
328
329 extern int errno;
330
331 int
332 main (argc, argv)
333      int argc;
334      char **argv;
335 {
336     struct stat sb;
337     char *program_name, *file, *target;
338     netrc_entry *head, *a;
339
340     if (argc < 2)
341     {
342         fprintf (stderr, "Usage: %s NETRC [HOSTNAME]...\n", argv[0]);
343         exit (1);
344     }
345
346     program_name = argv[0];
347     file = argv[1];
348     target = argv[2];
349
350     if (stat (file, &sb))
351     {
352         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
353                  strerror (errno));
354         exit (1);
355     }
356
357     head = parse_netrc (file);
358     if (!head)
359     {
360         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
361         exit (1);
362     }
363
364     if (argc > 2)
365     {
366         int i, status;
367         status = 0;
368         for (i = 2; i < argc; i++)
369         {
370             /* Print out the host that we are checking for. */
371             fputs (argv[i], stdout);
372
373             a = search_netrc (head, argv[i]);
374             if (a)
375             {
376                 /* Print out the account and password (if any). */
377                 fputc (' ', stdout);
378                 fputs (a->account, stdout);
379                 if (a->password)
380                 {
381                     fputc (' ', stdout);
382                     fputs (a->password, stdout);
383                 }
384             }
385             else
386                 status = 1;
387
388             fputc ('\n', stdout);
389         }
390         exit (status);
391     }
392
393     /* Print out the entire contents of the netrc. */
394     a = head;
395     while (a)
396     {
397         /* Print the host name. */
398         if (a->host)
399             fputs (a->host, stdout);
400         else
401             fputs ("DEFAULT", stdout);
402
403         fputc (' ', stdout);
404
405         /* Print the account name. */
406         fputs (a->account, stdout);
407
408         if (a->password)
409         {
410             /* Print the password, if there is any. */
411             fputc (' ', stdout);
412             fputs (a->password, stdout);
413         }
414
415         fputc ('\n', stdout);
416         a = a->next;
417     }
418
419     exit (0);
420 }
421 #endif /* STANDALONE */