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