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