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