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