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