]> Pileus Git - ~andy/fetchmail/blob - netrc.c
update.
[~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 (char *file)
80 {
81     FILE *fp;
82     char buf[POPBUFSIZE+1], *p, *tok;
83     const char *premature_token;
84     netrc_entry *current, *retval;
85     int ln;
86
87     /* The latest token we've seen in the file. */
88     enum
89     {
90         tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
91     } last_token = tok_nothing;
92
93     current = retval = NULL;
94
95     fp = fopen (file, "r");
96     if (!fp)
97     {
98         /* Just return NULL if we can't open the file. */
99         return NULL;
100     }
101
102     /* Initialize the file data. */
103     ln = 0;
104     premature_token = NULL;
105
106     /* While there are lines in the file... */
107     while (fgets(buf, sizeof(buf) - 1, fp))
108     {
109         ln++;
110
111         /* Strip trailing CRLF */
112         for (p = buf + strlen(buf) - 1; (p >= buf) && isspace((unsigned char)*p); p--)
113             *p = '\0';
114
115         /* Parse the line. */
116         p = buf;
117
118         /* If the line is empty... */
119         if (!*p)
120         {
121             if (last_token == tok_macdef)       /* end of macro */
122                 last_token = tok_nothing;
123             else
124                 continue;                       /* otherwise ignore it */
125         }
126
127         /* If we are defining macros, then skip parsing the line. */
128         while (*p && last_token != tok_macdef)
129         {
130             char quote_char = 0;
131             char *pp;
132
133             /* Skip any whitespace. */
134             while (*p && isspace ((unsigned char)*p))
135                 p++;
136
137             /* Discard end-of-line comments. */
138             if (*p == '#')
139                 break;
140
141             tok = pp = p;
142
143             /* Find the end of the token. */
144             while (*p && (quote_char || !isspace ((unsigned char)*p)))
145             {
146                 if (quote_char)
147                 {
148                     if (quote_char == *p)
149                     {
150                         quote_char = 0;
151                         p ++;
152                     }
153                     else
154                     {
155                         *pp = *p;
156                         p ++;
157                         pp ++;
158                     }
159                 }
160                 else
161                 {
162                     if (*p == '"' || *p == '\'')
163                         quote_char = *p;
164                     else
165                     {
166                         *pp = *p;
167                         pp ++;
168                     }
169                     p ++;
170                 }
171             }
172             /* Null-terminate the token, if it isn't already. */
173             if (*p)
174                 *p ++ = '\0';
175             *pp = 0;
176
177             switch (last_token)
178             {
179             case tok_login:
180                 if (current)
181                     current->login = (char *) xstrdup (tok);
182                 else
183                     premature_token = "login";
184                 break;
185
186             case tok_machine:
187                 /* Start a new machine entry. */
188                 maybe_add_to_list (&current, &retval);
189                 current->host = (char *) xstrdup (tok);
190                 break;
191
192             case tok_password:
193                 if (current)
194                     current->password = (char *) xstrdup (tok);
195                 else
196                     premature_token = "password";
197                 break;
198
199                 /* We handle most of tok_macdef above. */
200             case tok_macdef:
201                 if (!current)
202                     premature_token = "macdef";
203                 break;
204
205                 /* We don't handle the account keyword at all. */
206             case tok_account:
207                 if (!current)
208                     premature_token = "account";
209                 break;
210
211                 /* We handle tok_nothing below this switch. */
212             case tok_nothing:
213                 break;
214             }
215
216             if (premature_token)
217             {
218                 fprintf (stderr,
219                          GT_("%s:%d: warning: found \"%s\" before any host names\n"),
220                          file, ln, premature_token);
221                 premature_token = NULL;
222             }
223
224             if (last_token != tok_nothing)
225                 /* We got a value, so reset the token state. */
226                 last_token = tok_nothing;
227             else
228             {
229                 /* Fetch the next token. */
230                 if (!strcmp (tok, "default"))
231                 {
232                     maybe_add_to_list (&current, &retval);
233                 }
234                 else if (!strcmp (tok, "login"))
235                     last_token = tok_login;
236
237                 else if (!strcmp (tok, "user"))
238                     last_token = tok_login;
239
240                 else if (!strcmp (tok, "macdef"))
241                     last_token = tok_macdef;
242
243                 else if (!strcmp (tok, "machine"))
244                     last_token = tok_machine;
245
246                 else if (!strcmp (tok, "password"))
247                     last_token = tok_password;
248
249                 else if (!strcmp (tok, "passwd"))
250                     last_token = tok_password;
251
252                 else if (!strcmp (tok, "account"))
253                     last_token = tok_account;
254
255                 else
256                 {
257                     fprintf (stderr, GT_("%s:%d: warning: unknown token \"%s\"\n"),
258                              file, ln, tok);
259                 }
260             }
261         }
262     }
263
264     fclose (fp);
265
266     /* Finalize the last machine entry we found. */
267     maybe_add_to_list (&current, &retval);
268     free (current);
269
270     /* Reverse the order of the list so that it appears in file order. */
271     current = retval;
272     retval = NULL;
273     while (current)
274     {
275         netrc_entry *saved_reference;
276
277         /* Change the direction of the pointers. */
278         saved_reference = current->next;
279         current->next = retval;
280
281         /* Advance to the next node. */
282         retval = current;
283         current = saved_reference;
284     }
285
286     return retval;
287 }
288
289
290 /* Return the netrc entry from LIST corresponding to HOST.  NULL is
291    returned if no such entry exists. */
292 netrc_entry *
293 search_netrc (netrc_entry *list, char *host, char *login)
294 {
295     /* Look for the HOST in LIST. */
296     while (list)
297     {
298         if (list->host && !strcmp(list->host, host))
299             if (!list->login || !strcmp(list->login, login))
300                 /* We found a matching entry. */
301                 break;
302
303         list = list->next;
304     }
305
306     /* Return the matching entry, or NULL. */
307     return list;
308 }
309
310 void
311 free_netrc(netrc_entry *a) {
312     while(a) {
313         netrc_entry *n = a->next;
314         if (a->password != NULL) {
315                 memset(a->password, 0x55, strlen(a->password));
316                 free(a->password);
317         }
318         xfree(a->login);
319         xfree(a->host);
320         xfree(a);
321         a = n;
322     }
323 }
324
325 #ifdef STANDALONE
326 #include <sys/types.h>
327 #include <sys/stat.h>
328
329 #include <errno.h>
330
331 int main (int argc, char **argv)
332 {
333     struct stat sb;
334     char *program_name, *file, *host, *login;
335     netrc_entry *head, *a;
336
337     program_name = argv[0];
338     file = argv[1];
339     host = argv[2];
340     login = argv[3];
341
342     switch (argc) {
343         case 2:
344         case 4:
345             break;
346         default:
347             fprintf (stderr, "Usage: %s <file> [<host> <login>]\n", argv[0]);
348             exit(EXIT_FAILURE);
349     }
350
351     if (stat (file, &sb))
352     {
353         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
354                  strerror (errno));
355         exit (1);
356     }
357
358     head = parse_netrc (file);
359     if (!head)
360     {
361         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
362         exit (1);
363     }
364
365     if (host && login)
366     {
367         int status;
368         status = EXIT_SUCCESS;
369
370         printf("Host: %s, Login: %s\n", host, login);
371             
372         a = search_netrc (head, host, login);
373         if (a)
374         {
375             /* Print out the password (if any). */
376             if (a->password)
377             {
378                 printf("Password: %s\n", a->password);
379             }
380         } else
381             status = EXIT_FAILURE;
382         fputc ('\n', stdout);
383
384         exit (status);
385     }
386
387     /* Print out the entire contents of the netrc. */
388     a = head;
389     while (a)
390     {
391         /* Print the host name. */
392         if (a->host)
393             fputs (a->host, stdout);
394         else
395             fputs ("DEFAULT", stdout);
396
397         fputc (' ', stdout);
398
399         /* Print the login name. */
400         fputs (a->login, stdout);
401
402         if (a->password)
403         {
404             /* Print the password, if there is any. */
405             fputc (' ', stdout);
406             fputs (a->password, stdout);
407         }
408
409         fputc ('\n', stdout);
410         a = a->next;
411     }
412
413     free_netrc(head);
414
415     exit (0);
416 }
417 #endif /* STANDALONE */