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