1/* Read and parse the .netrc file to get hosts, accounts, and passwords. 2 Copyright (C) 1996, 2007, 2008, 2009 Free Software Foundation, Inc. 3 4This file is part of GNU Wget. 5 6GNU Wget is free software; you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation; either version 3 of the License, or 9(at your option) any later version. 10 11GNU Wget is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with Wget. If not, see <http://www.gnu.org/licenses/>. 18 19Additional permission under GNU GPL version 3 section 7 20 21If you modify this program, or any covered work, by linking or 22combining it with the OpenSSL project's OpenSSL library (or a 23modified version of that library), containing parts covered by the 24terms of the OpenSSL or SSLeay licenses, the Free Software Foundation 25grants you additional permission to convey the resulting work. 26Corresponding Source for a non-source form of such a combination 27shall include the source code for the parts of OpenSSL used as well 28as that of the covered work. */ 29 30/* This file used to be kept in synch with the code in Fetchmail, but 31 the latter has diverged since. */ 32 33#include "wget.h" 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <errno.h> 39 40#include "utils.h" 41#include "netrc.h" 42#include "init.h" 43 44#define NETRC_FILE_NAME ".netrc" 45 46acc_t *netrc_list; 47 48static acc_t *parse_netrc (const char *); 49 50/* Return the correct user and password, given the host, user (as 51 given in the URL), and password (as given in the URL). May return 52 NULL. 53 54 If SLACK_DEFAULT is set, allow looking for a "default" account. 55 You will typically turn it off for HTTP. */ 56void 57search_netrc (const char *host, const char **acc, const char **passwd, 58 int slack_default) 59{ 60 acc_t *l; 61 static int processed_netrc; 62 63 if (!opt.netrc) 64 return; 65 /* Find ~/.netrc. */ 66 if (!processed_netrc) 67 { 68#ifdef __VMS 69 70 int err; 71 struct_stat buf; 72 char *path = "SYS$LOGIN:.netrc"; 73 74 netrc_list = NULL; 75 processed_netrc = 1; 76 77 err = stat (path, &buf); 78 if (err == 0) 79 netrc_list = parse_netrc (path); 80 81#else /* def __VMS */ 82 83 char *home = home_dir (); 84 85 netrc_list = NULL; 86 processed_netrc = 1; 87 if (home) 88 { 89 int err; 90 struct_stat buf; 91 char *path = (char *)alloca (strlen (home) + 1 92 + strlen (NETRC_FILE_NAME) + 1); 93 sprintf (path, "%s/%s", home, NETRC_FILE_NAME); 94 xfree (home); 95 err = stat (path, &buf); 96 if (err == 0) 97 netrc_list = parse_netrc (path); 98 } 99 100#endif /* def __VMS [else] */ 101 } 102 /* If nothing to do... */ 103 if (!netrc_list) 104 return; 105 /* Acc and password found; all OK. */ 106 if (*acc && *passwd) 107 return; 108 /* Some data not given -- try finding the host. */ 109 for (l = netrc_list; l; l = l->next) 110 { 111 if (!l->host) 112 continue; 113 else if (!strcasecmp (l->host, host)) 114 break; 115 } 116 if (l) 117 { 118 if (*acc) 119 { 120 /* Looking for password in .netrc. */ 121 if (!strcmp (l->acc, *acc)) 122 *passwd = l->passwd; /* usernames match; password OK */ 123 else 124 *passwd = NULL; /* usernames don't match */ 125 } 126 else /* NOT *acc */ 127 { 128 /* If password was given, use it. The account is l->acc. */ 129 *acc = l->acc; 130 if (l->passwd) 131 *passwd = l->passwd; 132 } 133 return; 134 } 135 else 136 { 137 if (!slack_default) 138 return; 139 if (*acc) 140 return; 141 /* Try looking for the default account. */ 142 for (l = netrc_list; l; l = l->next) 143 if (!l->host) 144 break; 145 if (!l) 146 return; 147 *acc = l->acc; 148 if (!*passwd) 149 *passwd = l->passwd; 150 return; 151 } 152} 153 154 155#ifdef STANDALONE 156 157#include <assert.h> 158 159/* Normally, these functions would be defined by your package. */ 160# define xmalloc malloc 161# define xfree free 162# define xstrdup strdup 163 164# define xrealloc realloc 165 166/* Read a line from FP. The function reallocs the storage as needed 167 to accomodate for any length of the line. Reallocs are done 168 storage exponentially, doubling the storage after each overflow to 169 minimize the number of calls to realloc() and fgets(). The newline 170 character at the end of line is retained. 171 172 After end-of-file is encountered without anything being read, NULL 173 is returned. NULL is also returned on error. To distinguish 174 between these two cases, use the stdio function ferror(). */ 175 176char * 177read_whole_line (FILE *fp) 178{ 179 int length = 0; 180 int bufsize = 81; 181 char *line = xmalloc (bufsize); 182 183 while (fgets (line + length, bufsize - length, fp)) 184 { 185 length += strlen (line + length); 186 assert (length > 0); 187 if (line[length - 1] == '\n') 188 break; 189 /* fgets() guarantees to read the whole line, or to use up the 190 space we've given it. We can double the buffer 191 unconditionally. */ 192 bufsize <<= 1; 193 line = xrealloc (line, bufsize); 194 } 195 if (length == 0 || ferror (fp)) 196 { 197 xfree (line); 198 return NULL; 199 } 200 if (length + 1 < bufsize) 201 /* Relieve the memory from our exponential greediness. We say 202 `length + 1' because the terminating \0 is not included in 203 LENGTH. We don't need to zero-terminate the string ourselves, 204 though, because fgets() does that. */ 205 line = xrealloc (line, length + 1); 206 return line; 207} 208#endif /* STANDALONE */ 209 210/* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is 211 set to a ready-to-use acc_t, in any event. */ 212static void 213maybe_add_to_list (acc_t **newentry, acc_t **list) 214{ 215 acc_t *a, *l; 216 a = *newentry; 217 l = *list; 218 219 /* We need an account name in order to add the entry to the list. */ 220 if (a && ! a->acc) 221 { 222 /* Free any allocated space. */ 223 xfree_null (a->host); 224 xfree_null (a->acc); 225 xfree_null (a->passwd); 226 } 227 else 228 { 229 if (a) 230 { 231 /* Add the current machine into our list. */ 232 a->next = l; 233 l = a; 234 } 235 236 /* Allocate a new acc_t structure. */ 237 a = xmalloc (sizeof (acc_t)); 238 } 239 240 /* Zero the structure, so that it is ready to use. */ 241 memset (a, 0, sizeof(*a)); 242 243 /* Return the new pointers. */ 244 *newentry = a; 245 *list = l; 246 return; 247} 248 249/* Helper function for the parser, shifts contents of 250 null-terminated string once character to the left. 251 Used in processing \ and " constructs in the netrc file */ 252static void 253shift_left(char *string) 254{ 255 char *p; 256 257 for (p=string; *p; ++p) 258 *p = *(p+1); 259} 260 261/* Parse a .netrc file (as described in the ftp(1) manual page). */ 262static acc_t * 263parse_netrc (const char *path) 264{ 265 FILE *fp; 266 char *line, *p, *tok; 267 const char *premature_token; 268 acc_t *current, *retval; 269 int ln, qmark; 270 271 /* The latest token we've seen in the file. */ 272 enum 273 { 274 tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password 275 } last_token = tok_nothing; 276 277 current = retval = NULL; 278 279 fp = fopen (path, "r"); 280 if (!fp) 281 { 282 fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name, 283 path, strerror (errno)); 284 return retval; 285 } 286 287 /* Initialize the file data. */ 288 ln = 0; 289 premature_token = NULL; 290 291 /* While there are lines in the file... */ 292 while ((line = read_whole_line (fp)) != NULL) 293 { 294 ln ++; 295 296 /* Parse the line. */ 297 p = line; 298 qmark = 0; 299 300 /* Skip leading whitespace. */ 301 while (*p && c_isspace (*p)) 302 p ++; 303 304 /* If the line is empty, then end any macro definition. */ 305 if (last_token == tok_macdef && !*p) 306 /* End of macro if the line is empty. */ 307 last_token = tok_nothing; 308 309 /* If we are defining macros, then skip parsing the line. */ 310 while (*p && last_token != tok_macdef) 311 { 312 /* Skip any whitespace. */ 313 while (*p && c_isspace (*p)) 314 p ++; 315 316 /* Discard end-of-line comments; also, stop processing if 317 the above `while' merely skipped trailing whitespace. */ 318 if (*p == '#' || !*p) 319 break; 320 321 /* If the token starts with quotation mark, note this fact, 322 and squash the quotation character */ 323 if (*p == '"'){ 324 qmark = 1; 325 shift_left (p); 326 } 327 328 tok = p; 329 330 /* Find the end of the token, handling quotes and escapes. */ 331 while (*p && (qmark ? *p != '"' : !c_isspace (*p))){ 332 if (*p == '\\') 333 shift_left (p); 334 p ++; 335 } 336 337 /* If field was quoted, squash the trailing quotation mark 338 and reset qmark flag. */ 339 if (qmark) 340 { 341 shift_left (p); 342 qmark = 0; 343 } 344 345 /* Null-terminate the token, if it isn't already. */ 346 if (*p) 347 *p ++ = '\0'; 348 349 switch (last_token) 350 { 351 case tok_login: 352 if (current) 353 current->acc = xstrdup (tok); 354 else 355 premature_token = "login"; 356 break; 357 358 case tok_machine: 359 /* Start a new machine entry. */ 360 maybe_add_to_list (¤t, &retval); 361 current->host = xstrdup (tok); 362 break; 363 364 case tok_password: 365 if (current) 366 current->passwd = xstrdup (tok); 367 else 368 premature_token = "password"; 369 break; 370 371 /* We handle most of tok_macdef above. */ 372 case tok_macdef: 373 if (!current) 374 premature_token = "macdef"; 375 break; 376 377 /* We don't handle the account keyword at all. */ 378 case tok_account: 379 if (!current) 380 premature_token = "account"; 381 break; 382 383 /* We handle tok_nothing below this switch. */ 384 case tok_nothing: 385 break; 386 } 387 388 if (premature_token) 389 { 390 fprintf (stderr, _("\ 391%s: %s:%d: warning: %s token appears before any machine name\n"), 392 exec_name, path, ln, quote (premature_token)); 393 premature_token = NULL; 394 } 395 396 if (last_token != tok_nothing) 397 /* We got a value, so reset the token state. */ 398 last_token = tok_nothing; 399 else 400 { 401 /* Fetch the next token. */ 402 if (!strcmp (tok, "account")) 403 last_token = tok_account; 404 else if (!strcmp (tok, "default")) 405 { 406 maybe_add_to_list (¤t, &retval); 407 } 408 else if (!strcmp (tok, "login")) 409 last_token = tok_login; 410 411 else if (!strcmp (tok, "macdef")) 412 last_token = tok_macdef; 413 414 else if (!strcmp (tok, "machine")) 415 last_token = tok_machine; 416 417 else if (!strcmp (tok, "password")) 418 last_token = tok_password; 419 420 else 421 fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"), 422 exec_name, path, ln, tok); 423 } 424 } 425 426 xfree (line); 427 } 428 429 fclose (fp); 430 431 /* Finalize the last machine entry we found. */ 432 maybe_add_to_list (¤t, &retval); 433 xfree (current); 434 435 /* Reverse the order of the list so that it appears in file order. */ 436 current = retval; 437 retval = NULL; 438 while (current) 439 { 440 acc_t *saved_reference; 441 442 /* Change the direction of the pointers. */ 443 saved_reference = current->next; 444 current->next = retval; 445 446 /* Advance to the next node. */ 447 retval = current; 448 current = saved_reference; 449 } 450 451 return retval; 452} 453 454 455/* Free a netrc list. */ 456void 457free_netrc(acc_t *l) 458{ 459 acc_t *t; 460 461 while (l) 462 { 463 t = l->next; 464 xfree_null (l->acc); 465 xfree_null (l->passwd); 466 xfree_null (l->host); 467 xfree (l); 468 l = t; 469 } 470} 471 472#ifdef STANDALONE 473#include <sys/types.h> 474#include <sys/stat.h> 475 476int 477main (int argc, char **argv) 478{ 479 struct_stat sb; 480 char *program_name, *file, *target; 481 acc_t *head, *a; 482 483 if (argc < 2 || argc > 3) 484 { 485 fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]); 486 exit (1); 487 } 488 489 program_name = argv[0]; 490 file = argv[1]; 491 target = argv[2]; 492 493 if (stat (file, &sb)) 494 { 495 fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file, 496 strerror (errno)); 497 exit (1); 498 } 499 500 head = parse_netrc (file); 501 a = head; 502 while (a) 503 { 504 /* Skip if we have a target and this isn't it. */ 505 if (target && a->host && strcmp (target, a->host)) 506 { 507 a = a->next; 508 continue; 509 } 510 511 if (!target) 512 { 513 /* Print the host name if we have no target. */ 514 if (a->host) 515 fputs (a->host, stdout); 516 else 517 fputs ("DEFAULT", stdout); 518 519 fputc (' ', stdout); 520 } 521 522 /* Print the account name. */ 523 fputs (a->acc, stdout); 524 525 if (a->passwd) 526 { 527 /* Print the password, if there is any. */ 528 fputc (' ', stdout); 529 fputs (a->passwd, stdout); 530 } 531 532 fputc ('\n', stdout); 533 534 /* Exit if we found the target. */ 535 if (target) 536 exit (0); 537 a = a->next; 538 } 539 540 /* Exit with failure if we had a target, success otherwise. */ 541 if (target) 542 exit (1); 543 544 exit (0); 545} 546#endif /* STANDALONE */ 547