]> Pileus Git - ~andy/fetchmail/blob - netrc.c
Minor bug fixes for socket.c
[~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 "config.h"
14
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include "fetchmail.h"
21 #include "netrc.h"
22 #include "i18n.h"
23
24 #ifdef STANDALONE
25 /* Normally defined in xstrdup.c. */
26 # define xstrdup strdup
27
28 /* Normally defined in xmalloc.c */
29 # define xmalloc malloc
30 # define xrealloc realloc
31
32 const char *program_name = "netrc";
33 #endif
34
35 /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
36    set to a ready-to-use netrc_entry, in any event. */
37 static void
38 maybe_add_to_list (netrc_entry **newentry, netrc_entry **list)
39 {
40     netrc_entry *a, *l;
41     a = *newentry;
42     l = *list;
43
44     /* We need a login name in order to add the entry to the list. */
45     if (a && ! a->login)
46     {
47         /* Free any allocated space. */
48         if (a->host)
49             free (a->host);
50         if (a->password)
51             free (a->password);
52     }
53     else
54     {
55         if (a)
56         {
57             /* Add the current machine into our list. */
58             a->next = l;
59             l = a;
60         }
61
62         /* Allocate a new netrc_entry structure. */
63         a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
64     }
65
66     /* Zero the structure, so that it is ready to use. */
67     memset (a, 0, sizeof(*a));
68
69     /* Return the new pointers. */
70     *newentry = a;
71     *list = l;
72     return;
73 }
74
75
76 /* Parse FILE as a .netrc file (as described in ftp(1)), and return a
77    list of entries.  NULL is returned if the file could not be
78    parsed. */
79 netrc_entry *
80 parse_netrc (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 (netrc_entry *list, char *host, char *login)
295 {
296     /* Look for the HOST in LIST. */
297     while (list)
298     {
299         if (list->host && !strcmp(list->host, host))
300             if (!list->login || !strcmp(list->login, login))
301                 /* We found a matching entry. */
302                 break;
303
304         list = list->next;
305     }
306
307     /* Return the matching entry, or NULL. */
308     return list;
309 }
310
311 void
312 free_netrc(netrc_entry *a) {
313     while(a) {
314         netrc_entry *n = a->next;
315         if (a->password != NULL) {
316                 memset(a->password, 0x55, strlen(a->password));
317                 free(a->password);
318         }
319         xfree(a->login);
320         xfree(a->host);
321         xfree(a);
322         a = n;
323     }
324 }
325
326 #ifdef STANDALONE
327 #include <sys/types.h>
328 #include <sys/stat.h>
329
330 #include <errno.h>
331
332 int main (int argc, char **argv)
333 {
334     struct stat sb;
335     char *file, *host, *login;
336     netrc_entry *head, *a;
337
338     program_name = argv[0];
339     file = argv[1];
340     host = argv[2];
341     login = argv[3];
342
343     switch (argc) {
344         case 2:
345         case 4:
346             break;
347         default:
348             fprintf (stderr, "Usage: %s <file> [<host> <login>]\n", argv[0]);
349             exit(EXIT_FAILURE);
350     }
351
352     if (stat (file, &sb))
353     {
354         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
355                  strerror (errno));
356         exit (1);
357     }
358
359     head = parse_netrc (file);
360     if (!head)
361     {
362         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
363         exit (1);
364     }
365
366     if (host && login)
367     {
368         int status;
369         status = EXIT_SUCCESS;
370
371         printf("Host: %s, Login: %s\n", host, login);
372             
373         a = search_netrc (head, host, login);
374         if (a)
375         {
376             /* Print out the password (if any). */
377             if (a->password)
378             {
379                 printf("Password: %s\n", a->password);
380             }
381         } else
382             status = EXIT_FAILURE;
383         fputc ('\n', stdout);
384
385         exit (status);
386     }
387
388     /* Print out the entire contents of the netrc. */
389     a = head;
390     while (a)
391     {
392         /* Print the host name. */
393         if (a->host)
394             fputs (a->host, stdout);
395         else
396             fputs ("DEFAULT", stdout);
397
398         fputc (' ', stdout);
399
400         /* Print the login name. */
401         fputs (a->login, stdout);
402
403         if (a->password)
404         {
405             /* Print the password, if there is any. */
406             fputc (' ', stdout);
407             fputs (a->password, stdout);
408         }
409
410         fputc ('\n', stdout);
411         a = a->next;
412     }
413
414     free_netrc(head);
415
416     exit (0);
417 }
418 #endif /* STANDALONE */