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