]> Pileus Git - ~andy/fetchmail/blob - netrc.c
27c5427f208e298b933b91210d7b15ea5bb7e1ea
[~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 #ifdef HAVE_STRING_H
25 #  include <string.h>
26 #else
27 #  include <strings.h>
28 #endif
29
30 #include "config.h"
31 #include "fetchmail.h"
32 #include "netrc.h"
33
34 #ifdef STANDALONE
35 /* Normally defined in xstrdup.c. */
36 # define xstrdup strdup
37
38 /* Normally defined in xmalloc.c */
39 # define xmalloc malloc
40 # define xrealloc realloc
41 #endif
42
43 /* fetchmail is not consistent with the xmalloc functions. */
44 # define xrealloc realloc
45 # define xstrdup strdup
46
47 /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
48    set to a ready-to-use netrc_entry, in any event. */
49 static void
50 maybe_add_to_list (newentry, list)
51      netrc_entry **newentry;
52      netrc_entry **list;
53 {
54   netrc_entry *a, *l;
55   a = *newentry;
56   l = *list;
57
58   /* We need an account name in order to add the entry to the list. */
59   if (a && ! a->account)
60     {
61       /* Free any allocated space. */
62       free (a->host);
63       free (a->account);
64       free (a->password);
65     }
66   else
67     {
68       if (a)
69         {
70           /* Add the current machine into our list. */
71           a->next = l;
72           l = a;
73         }
74
75       /* Allocate a new netrc_entry structure. */
76       a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
77     }
78
79   /* Zero the structure, so that it is ready to use. */
80   memset (a, 0, sizeof(*a));
81
82   /* Return the new pointers. */
83   *newentry = a;
84   *list = l;
85   return;
86 }
87
88
89 /* Parse FILE as a .netrc file (as described in ftp(1)), and return a
90    list of entries.  NULL is returned if the file could not be
91    parsed. */
92 netrc_entry *
93 parse_netrc (file)
94      char *file;
95 {
96   FILE *fp;
97   char buf[POPBUFSIZE+1], *p, *tok, *premature_token;
98   netrc_entry *current, *retval;
99   int ln;
100
101   /* The latest token we've seen in the file. */
102   enum
103   {
104     tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
105   } last_token = tok_nothing;
106
107   current = retval = NULL;
108
109   fp = fopen (file, "r");
110   if (!fp)
111     {
112       /* Just return NULL if we can't open the file. */
113       return NULL;
114     }
115
116   /* Initialize the file data. */
117   ln = 0;
118   premature_token = NULL;
119
120   /* While there are lines in the file... */
121   while (fgets(buf, POPBUFSIZE, fp))
122     {
123       ln ++;
124
125       /* Parse the line. */
126       p = buf;
127
128       /* If the line is empty, then end any macro definition. */
129       if (last_token == tok_macdef && !*p)
130         /* End of macro if the line is empty. */
131         last_token = tok_nothing;
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 */