]> Pileus Git - ~andy/fetchmail/blob - netrc.c
e275c9d3b7182bf36f37cf44c615ede1e5e2a966
[~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 && !strcmp(list->host, host))
302             if (!list->account || !strcmp(list->account, account))
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
314 #ifdef STANDALONE
315 #include <sys/types.h>
316 #include <sys/stat.h>
317
318 extern int errno;
319
320 int
321 main (argc, argv)
322      int argc;
323      char **argv;
324 {
325     struct stat sb;
326     char *program_name, *file, *host, *account;
327     netrc_entry *head, *a;
328
329     program_name = argv[0];
330     file = argv[1];
331     host = argv[2];
332     account = argv[3];
333
334     if (stat (file, &sb))
335     {
336         fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
337                  strerror (errno));
338         exit (1);
339     }
340
341     head = parse_netrc (file);
342     if (!head)
343     {
344         fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
345         exit (1);
346     }
347
348     if (host && account)
349     {
350         int i, status;
351         status = 0;
352
353         printf("Host: %s, Account: %s\n", host, account);
354             
355         a = search_netrc (head, host, account);
356         if (a)
357         {
358             /* Print out the password (if any). */
359             if (a->password)
360             {
361                 fputc (' ', stdout);
362                 fputs (a->password, stdout);
363             }
364         }
365         fputc ('\n', stdout);
366
367         exit (status);
368     }
369
370     /* Print out the entire contents of the netrc. */
371     a = head;
372     while (a)
373     {
374         /* Print the host name. */
375         if (a->host)
376             fputs (a->host, stdout);
377         else
378             fputs ("DEFAULT", stdout);
379
380         fputc (' ', stdout);
381
382         /* Print the account name. */
383         fputs (a->account, stdout);
384
385         if (a->password)
386         {
387             /* Print the password, if there is any. */
388             fputc (' ', stdout);
389             fputs (a->password, stdout);
390         }
391
392         fputc ('\n', stdout);
393         a = a->next;
394     }
395
396     exit (0);
397 }
398 #endif /* STANDALONE */