]> Pileus Git - ~andy/fetchmail/blob - netrc.c
7c5007026492b78931540f1b84d08a666bc4538e
[~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             if (last_token == tok_macdef)       /* end of macro */
118                 last_token = tok_nothing;
119             else
120                 continue;                       /* otherwise ignore it */
121
122         /* If we are defining macros, then skip parsing the line. */
123         while (*p && last_token != tok_macdef)
124         {
125             char quote_char = 0;
126             char *pp;
127
128             /* Skip any whitespace. */
129             while (*p && isspace (*p))
130                 p++;
131
132             /* Discard end-of-line comments. */
133             if (*p == '#')
134                 break;
135
136             tok = pp = p;
137
138             /* Find the end of the token. */
139             while (*p && (quote_char || !isspace (*p)))
140             {
141                 if (quote_char)
142                 {
143                     if (quote_char == *p)
144                     {
145                         quote_char = 0;
146                         p ++;
147                     }
148                     else
149                     {
150                         *pp = *p;
151                         p ++;
152                         pp ++;
153                     }
154                 }
155                 else
156                 {
157                     if (*p == '"' || *p == '\'')
158                         quote_char = *p;
159                     else
160                     {
161                         *pp = *p;
162                         pp ++;
163                     }
164                     p ++;
165                 }
166             }
167             /* Null-terminate the token, if it isn't already. */
168             if (*p)
169                 *p ++ = '\0';
170             *pp = 0;
171
172             switch (last_token)
173             {
174             case tok_login:
175                 if (current)
176                     current->account = (char *) xstrdup (tok);
177                 else
178                     premature_token = "login";
179                 break;
180
181             case tok_machine:
182                 /* Start a new machine entry. */
183                 maybe_add_to_list (&current, &retval);
184                 current->host = (char *) xstrdup (tok);
185                 break;
186
187             case tok_password:
188                 if (current)
189                     current->password = (char *) xstrdup (tok);
190                 else
191                     premature_token = "password";
192                 break;
193
194                 /* We handle most of tok_macdef above. */
195             case tok_macdef:
196                 if (!current)
197                     premature_token = "macdef";
198                 break;
199
200                 /* We don't handle the account keyword at all. */
201             case tok_account:
202                 if (!current)
203                     premature_token = "account";
204                 break;
205
206                 /* We handle tok_nothing below this switch. */
207             case tok_nothing:
208                 break;
209             }
210
211             if (premature_token)
212             {
213 #ifdef HAVE_ERROR
214                 error_at_line (0, file, ln,
215                                _("warning: found \"%s\" before any host names"),
216                                premature_token);
217 #else
218                 fprintf (stderr,
219                          _("%s:%d: warning: found \"%s\" before any host names\n"),
220                          file, ln, premature_token);
221 #endif
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, _("%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, account)
295      netrc_entry *list;
296      char *host, *account;
297 {
298     /* Look for the HOST in LIST. */
299     while (list)
300     {
301         if (!list->host)
302             /* We hit the default entry. */
303             break;
304
305         else if (!strcmp(list->host, host))
306             if (!list->account || !strcmp(list->account, account))
307                 /* We found a matching entry. */
308                 break;
309
310         list = list->next;
311     }
312
313     /* Return the matching entry, or NULL. */
314     return list;
315 }
316
317
318 #ifdef STANDALONE
319 #include <sys/types.h>
320 #include <sys/stat.h>
321
322 extern int errno;
323
324 int
325 main (argc, argv)
326      int argc;
327      char **argv;
328 {
329     struct stat sb;
330     char *program_name, *file, *host, *account;
331     netrc_entry *head, *a;
332
333     program_name = argv[0];
334     file = argv[1];
335     host = argv[2];
336     account = argv[3];
337
338     if (stat (file, &sb))
339     {
340         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
341                  strerror (errno));
342         exit (1);
343     }
344
345     head = parse_netrc (file);
346     if (!head)
347     {
348         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
349         exit (1);
350     }
351
352     if (host && account)
353     {
354         int i, status;
355         status = 0;
356
357         printf("Host: %s, Account: %s\n", host, account);
358             
359         a = search_netrc (head, host, account);
360         if (a)
361         {
362             /* Print out the password (if any). */
363             if (a->password)
364             {
365                 fputc (' ', stdout);
366                 fputs (a->password, stdout);
367             }
368         }
369         fputc ('\n', stdout);
370
371         exit (status);
372     }
373
374     /* Print out the entire contents of the netrc. */
375     a = head;
376     while (a)
377     {
378         /* Print the host name. */
379         if (a->host)
380             fputs (a->host, stdout);
381         else
382             fputs ("DEFAULT", stdout);
383
384         fputc (' ', stdout);
385
386         /* Print the account name. */
387         fputs (a->account, stdout);
388
389         if (a->password)
390         {
391             /* Print the password, if there is any. */
392             fputc (' ', stdout);
393             fputs (a->password, stdout);
394         }
395
396         fputc ('\n', stdout);
397         a = a->next;
398     }
399
400     exit (0);
401 }
402 #endif /* STANDALONE */