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 (¤t, &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 (¤t, &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 (¤t, &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