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