root.c revision 107484
134192Sjdp/* 255687Sjdp * Copyright (c) 1992, Mark D. Baushke 334192Sjdp * Copyright (c) 2002, Derek R. Price 434192Sjdp * 534192Sjdp * You may distribute under the terms of the GNU General Public License as 634192Sjdp * specified in the README file that comes with the CVS source distribution. 734192Sjdp * 834192Sjdp * Name of Root 934192Sjdp * 1034192Sjdp * Determine the path to the CVSROOT and set "Root" accordingly. 1134192Sjdp */ 1234192Sjdp 1334192Sjdp#include "cvs.h" 1434192Sjdp#include "getline.h" 1534192Sjdp 1634192Sjdp/* Printable names for things in the current_parsed_root->method enum variable. 1734192Sjdp Watch out if the enum is changed in cvs.h! */ 1834192Sjdp 1934192Sjdpchar *method_names[] = { 2034192Sjdp "undefined", "local", "server (rsh)", "pserver", "kserver", "gserver", "ext", "fork" 2134192Sjdp}; 2234192Sjdp 2334192Sjdp#ifndef DEBUG 2434192Sjdp 2550476Speterchar * 2634192SjdpName_Root (dir, update_dir) 2734192Sjdp char *dir; 2834192Sjdp char *update_dir; 2934192Sjdp{ 3034192Sjdp FILE *fpin; 3176224Sobrien char *ret, *xupdate_dir; 3234192Sjdp char *root = NULL; 3350608Sjdp size_t root_allocated = 0; 3434192Sjdp char *tmp; 3576224Sobrien char *cvsadm; 3635529Sdfr char *cp; 37225152Skib 38216695Skib if (update_dir && *update_dir) 3934192Sjdp xupdate_dir = update_dir; 4034192Sjdp else 41115396Skan xupdate_dir = "."; 4245501Sjdp 4345501Sjdp if (dir != NULL) 44127250Speter { 45127250Speter cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 46127250Speter (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 47127250Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 48127250Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 49127250Speter } 50127250Speter else 51127250Speter { 52127250Speter cvsadm = xstrdup (CVSADM); 5334192Sjdp tmp = xstrdup (CVSADM_ROOT); 54119013Sgordon } 5534192Sjdp 56127250Speter /* 57127250Speter * Do not bother looking for a readable file if there is no cvsadm 58127250Speter * directory present. 5934192Sjdp * 6034192Sjdp * It is possible that not all repositories will have a CVS/Root 61233307Skib * file. This is ok, but the user will need to specify -d 6234192Sjdp * /path/name or have the environment variable CVSROOT set in 6334192Sjdp * order to continue. */ 6434192Sjdp if ((!isdir (cvsadm)) || (!isreadable (tmp))) 6534192Sjdp { 6634192Sjdp ret = NULL; 6734192Sjdp goto out; 68133063Sdfr } 69133063Sdfr 70133063Sdfr /* 71133063Sdfr * The assumption here is that the CVS Root is always contained in the 72133063Sdfr * first line of the "Root" file. 73133063Sdfr */ 74232831Skib fpin = open_file (tmp, "r"); 75232831Skib 76232831Skib if (getline (&root, &root_allocated, fpin) < 0) 77232831Skib { 7850609Sjdp /* FIXME: should be checking for end of file separately; errno 7934192Sjdp is not set in that case. */ 8034192Sjdp error (0, 0, "in directory %s:", xupdate_dir); 8155687Sjdp error (0, errno, "cannot read %s", CVSADM_ROOT); 8250608Sjdp error (0, 0, "please correct this problem"); 8360938Sjake ret = NULL; 8450608Sjdp goto out; 8550608Sjdp } 8650608Sjdp (void) fclose (fpin); 8760938Sjake if ((cp = strrchr (root, '\n')) != NULL) 8850608Sjdp *cp = '\0'; /* strip the newline */ 8963870Sjdp 9055687Sjdp /* 91232831Skib * root now contains a candidate for CVSroot. It must be an 9255687Sjdp * absolute pathname or specify a remote server. 9355687Sjdp */ 9434192Sjdp 9534192Sjdp if ( 9634192Sjdp#ifdef CLIENT_SUPPORT 9734192Sjdp (strchr (root, ':') == NULL) && 9834192Sjdp#endif 9934192Sjdp ! isabsolute (root)) 100153515Skan { 101153515Skan error (0, 0, "in directory %s:", xupdate_dir); 102153515Skan error (0, 0, 103153515Skan "ignoring %s because it does not contain an absolute pathname.", 104153515Skan CVSADM_ROOT); 10562801Sjdp ret = NULL; 10662801Sjdp goto out; 10762801Sjdp } 10862801Sjdp 10962801Sjdp#ifdef CLIENT_SUPPORT 11062801Sjdp if ((strchr (root, ':') == NULL) && !isdir (root)) 11162801Sjdp#else /* ! CLIENT_SUPPORT */ 11262801Sjdp if (!isdir (root)) 11362801Sjdp#endif /* CLIENT_SUPPORT */ 11462801Sjdp { 11562801Sjdp error (0, 0, "in directory %s:", xupdate_dir); 11662801Sjdp error (0, 0, 11762801Sjdp "ignoring %s because it specifies a non-existent repository %s", 11862801Sjdp CVSADM_ROOT, root); 11962801Sjdp ret = NULL; 12062801Sjdp goto out; 12162801Sjdp } 122153515Skan 123153515Skan /* allocate space to return and fill it in */ 124153515Skan strip_trailing_slashes (root); 125153515Skan ret = xstrdup (root); 126153515Skan out: 127153515Skan free (cvsadm); 128153515Skan free (tmp); 129234840Skib if (root != NULL) 130234840Skib free (root); 131234840Skib return (ret); 132234840Skib} 133234840Skib 134234840Skib/* 135153515Skan * Write the CVS/Root file so that the environment variable CVSROOT 136153515Skan * and/or the -d option to cvs will be validated or not necessary for 13734192Sjdp * future work. 13834192Sjdp */ 13934192Sjdpvoid 14034192SjdpCreate_Root (dir, rootdir) 14134192Sjdp char *dir; 14250977Sjdp char *rootdir; 14350977Sjdp{ 14450977Sjdp FILE *fout; 14550977Sjdp char *tmp; 14634192Sjdp 14734192Sjdp if (noexec) 14834192Sjdp return; 14934192Sjdp 15034192Sjdp /* record the current cvs root */ 15134192Sjdp 152153504Smarcel if (rootdir != NULL) 153153504Smarcel { 15434192Sjdp if (dir != NULL) 15534192Sjdp { 15634192Sjdp tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 157116511Smdodd (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 15834192Sjdp } 15934192Sjdp else 16034192Sjdp tmp = xstrdup (CVSADM_ROOT); 16134192Sjdp 16234192Sjdp fout = open_file (tmp, "w+"); 16334192Sjdp if (fprintf (fout, "%s\n", rootdir) < 0) 16434192Sjdp error (1, errno, "write to %s failed", tmp); 16538467Sjb if (fclose (fout) == EOF) 16634192Sjdp error (1, errno, "cannot close %s", tmp); 16738467Sjb free (tmp); 16834192Sjdp } 16938467Sjb} 17034192Sjdp 17150610Sjdp#endif /* ! DEBUG */ 172217153Skib 17334192Sjdp 174133063Sdfr/* The root_allow_* stuff maintains a list of legal CVSROOT 175133063Sdfr directories. Then we can check against them when a remote user 176133063Sdfr hands us a CVSROOT directory. */ 177133063Sdfr 178133063Sdfrstatic int root_allow_count; 179133063Sdfrstatic char **root_allow_vector; 180133063Sdfrstatic int root_allow_size; 181133063Sdfr 182230784Skibvoid 183230784Skibroot_allow_add (arg) 184230784Skib char *arg; 18534192Sjdp{ 18645501Sjdp char *p; 18738467Sjb 18834192Sjdp if (root_allow_size <= root_allow_count) 18938816Sdfr { 19038816Sdfr if (root_allow_size == 0) 19138467Sjb { 19234192Sjdp root_allow_size = 1; 19338816Sdfr root_allow_vector = 19438816Sdfr (char **) malloc (root_allow_size * sizeof (char *)); 19538467Sjb } 19634192Sjdp else 19734192Sjdp { 198177924Simp root_allow_size *= 2; 199177924Simp root_allow_vector = 200177924Simp (char **) realloc (root_allow_vector, 201177924Simp root_allow_size * sizeof (char *)); 202177924Simp } 20334192Sjdp 204153515Skan if (root_allow_vector == NULL) 205153515Skan { 206153515Skan no_memory: 207153515Skan /* Strictly speaking, we're not supposed to output anything 208153515Skan now. But we're about to exit(), give it a try. */ 209153515Skan printf ("E Fatal server error, aborting.\n\ 21085004Sdfrerror ENOMEM Virtual memory exhausted.\n"); 21134192Sjdp 21285004Sdfr error_exit (); 213234841Skib } 21434192Sjdp } 215234841Skib p = malloc (strlen (arg) + 1); 216234841Skib if (p == NULL) 217234841Skib goto no_memory; 218234841Skib strcpy (p, arg); 219234841Skib root_allow_vector[root_allow_count++] = p; 220234841Skib} 221234841Skib 222234841Skibvoid 223234841Skibroot_allow_free () 224189959Skib{ 225238471Skib if (root_allow_vector != NULL) 22634192Sjdp free_names (&root_allow_count, root_allow_vector); 227216695Skib root_allow_size = 0; 228216695Skib} 22934192Sjdp 230153515Skanint 231153515Skanroot_allow_ok (arg) 232153515Skan char *arg; 233153515Skan{ 234153515Skan int i; 23585677Speter 23685677Speter if (root_allow_count == 0) 237232831Skib { 238232831Skib /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 239232831Skib or later without reading the documentation about 240232831Skib --allow-root. Printing an error here doesn't disclose any 241232831Skib particularly useful information to an attacker because a 242232831Skib CVS server configured in this way won't let *anyone* in. */ 24334192Sjdp 244232831Skib /* Note that we are called from a context where we can spit 245232831Skib back "error" rather than waiting for the next request which 246168312Skan expects responses. */ 247168312Skan printf ("\ 248233231Skiberror 0 Server configuration missing --allow-root in inetd.conf\n"); 249233546Skib error_exit (); 250168312Skan } 251168312Skan 252168312Skan for (i = 0; i < root_allow_count; ++i) 253168312Skan if (strcmp (root_allow_vector[i], arg) == 0) 254168312Skan return 1; 255168312Skan return 0; 256168312Skan} 257168312Skan 258189959Skib 259190543Skib 260199829Skib/* This global variable holds the global -d option. It is NULL if -d 261216695Skib was not used, which means that we must get the CVSroot information 262256101Skib from the CVSROOT environment variable or from a CVS/Root file. */ 263238471Skibchar *CVSroot_cmdline; 264194531Skan 265194531Skan 266194531Skan 267214728Skib/* FIXME - Deglobalize this. */ 268216695Skibcvsroot_t *current_parsed_root = NULL; 269228435Skib 270228435Skib 271232831Skib 272234841Skib/* allocate and initialize a cvsroot_t 273234841Skib * 27435529Sdfr * We must initialize the strings to NULL so we know later what we should 275194531Skan * free 27650977Sjdp * 27750977Sjdp * Some of the other zeroes remain meaningful as, "never set, use default", 27850977Sjdp * or the like 27950977Sjdp */ 280229780Suqsstatic cvsroot_t * 28134192Sjdpnew_cvsroot_t () 28234192Sjdp{ 28334192Sjdp cvsroot_t *newroot; 28434192Sjdp 28534192Sjdp /* gotta store it somewhere */ 286192922Sdfr newroot = xmalloc(sizeof(cvsroot_t)); 287133063Sdfr 288153515Skan newroot->original = NULL; 289153515Skan newroot->method = null_method; 290228375Skib newroot->username = NULL; 291153515Skan newroot->password = NULL; 292233231Skib newroot->hostname = NULL; 293153515Skan newroot->port = 0; 294199829Skib newroot->directory = NULL; 295199877Skib#ifdef CLIENT_SUPPORT 296199877Skib newroot->isremote = 0; 297199877Skib#endif /* CLIENT_SUPPORT */ 298216695Skib 299216695Skib return newroot; 300233231Skib} 301233231Skib 302199829Skib 30376296Sjdp 30476296Sjdp/* Dispose of a cvsroot_t and its component parts */ 30576296Sjdpvoid 30676296Sjdpfree_cvsroot_t (root) 30776296Sjdp cvsroot_t *root; 30876296Sjdp{ 30976296Sjdp if (root->original != NULL) 31076296Sjdp free (root->original); 31176296Sjdp if (root->username != NULL) 312216695Skib free (root->username); 313216695Skib if (root->password != NULL) 314216695Skib { 315216695Skib /* I like to be paranoid */ 316216695Skib memset (root->password, 0, strlen (root->password)); 317216695Skib free (root->password); 318216695Skib } 319216695Skib if (root->hostname != NULL) 320216695Skib free (root->hostname); 321216695Skib if (root->directory != NULL) 322216695Skib free (root->directory); 323216695Skib free (root); 324218476Skib} 325216695Skib 326216695Skib 327238471Skib 328238471Skib/* 329238471Skib * Parse a CVSROOT string to allocate and return a new cvsroot_t structure. 330238471Skib * Valid specifications are: 331238471Skib * 332238471Skib * :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path 333238471Skib * [:(ext|server):][[user]@]host[:]/path 334238471Skib * [:local:[e:]]/path 335216695Skib * :fork:/path 336216695Skib * 337216695Skib * INPUTS 338216695Skib * root_in C String containing the CVSROOT to be parsed. 339216695Skib * 340216695Skib * RETURNS 341234841Skib * A pointer to a newly allocated cvsroot_t structure upon success and 342216695Skib * NULL upon failure. The caller is responsible for disposing of 343216695Skib * new structures with a call to free_cvsroot_t(). 344216695Skib * 345216695Skib * NOTES 346216695Skib * This would have been a lot easier to write in Perl. 347216695Skib * 348216695Skib * SEE ALSO 349233361Skib * free_cvsroot_t() 350233361Skib */ 351233361Skibcvsroot_t * 352233361Skibparse_cvsroot (root_in) 353233361Skib char *root_in; 354233361Skib{ 35538816Sdfr cvsroot_t *newroot; /* the new root to be returned */ 356212497Snwhitehorn char *cvsroot_save; /* what we allocated so we can dispose 35734192Sjdp * it when finished */ 358233361Skib char *firstslash; /* save where the path spec starts 359233361Skib * while we parse 360233361Skib * [[user][:password]@]host[:[port]] 361233361Skib */ 362116563Smdodd char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ 36338816Sdfr int check_hostname, no_port, no_password; 36438816Sdfr 36538816Sdfr /* allocate some space */ 36638816Sdfr newroot = new_cvsroot_t(); 36766056Sjdp 368216695Skib /* save the original string */ 36945501Sjdp newroot->original = xstrdup (root_in); 370116557Smdodd 371232831Skib /* and another copy we can munge while parsing */ 37250608Sjdp cvsroot_save = cvsroot_copy = xstrdup (root_in); 37350608Sjdp 37445501Sjdp if (*cvsroot_copy == ':') 375228435Skib { 376216695Skib char *method = ++cvsroot_copy; 377216695Skib 378133063Sdfr /* Access method specified, as in 379133063Sdfr * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", 380133063Sdfr * "cvs -d [:(ext|server):][[user]@]host[:]/path", 381133063Sdfr * "cvs -d :local:e:\path", 382133063Sdfr * "cvs -d :fork:/path". 383142645Sdfr * We need to get past that part of CVSroot before parsing the 384153515Skan * rest of it. 38538816Sdfr */ 386116558Smdodd 387116558Smdodd if (! (p = strchr (method, ':'))) 388116558Smdodd { 389116558Smdodd error (0, 0, "No closing `:' on method in CVSROOT."); 390233231Skib goto error_exit; 391233231Skib } 392116558Smdodd *p = '\0'; 393233231Skib cvsroot_copy = ++p; 394228435Skib 395233231Skib /* Now we have an access method -- see if it's valid. */ 396133063Sdfr 397116558Smdodd if (strcmp (method, "local") == 0) 39834192Sjdp newroot->method = local_method; 399 else if (strcmp (method, "pserver") == 0) 400 newroot->method = pserver_method; 401 else if (strcmp (method, "kserver") == 0) 402 newroot->method = kserver_method; 403 else if (strcmp (method, "gserver") == 0) 404 newroot->method = gserver_method; 405 else if (strcmp (method, "server") == 0) 406 newroot->method = server_method; 407 else if (strcmp (method, "ext") == 0) 408 newroot->method = ext_method; 409 else if (strcmp (method, "fork") == 0) 410 newroot->method = fork_method; 411 else 412 { 413 error (0, 0, "Unknown method (`%s') in CVSROOT.", method); 414 goto error_exit; 415 } 416 } 417 else 418 { 419 /* If the method isn't specified, assume EXT_METHOD if the string looks 420 like a relative path and LOCAL_METHOD otherwise. */ 421 422 newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) 423 ? ext_method 424 : local_method); 425 } 426 427#ifdef CLIENT_SUPPORT 428 newroot->isremote = (newroot->method != local_method); 429#endif /* CLIENT_SUPPORT */ 430 431 432 if ((newroot->method != local_method) 433 && (newroot->method != fork_method)) 434 { 435 /* split the string into [[user][:password]@]host[:[port]] & /path 436 * 437 * this will allow some characters such as '@' & ':' to remain unquoted 438 * in the path portion of the spec 439 */ 440 if ((p = strchr (cvsroot_copy, '/')) == NULL) 441 { 442 error (0, 0, "CVSROOT requires a path spec:"); 443 error (0, 0, ":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); 444 error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); 445 goto error_exit; 446 } 447 firstslash = p; /* == NULL if '/' not in string */ 448 *p = '\0'; 449 450 /* Check to see if there is a username[:password] in the string. */ 451 if ((p = strchr (cvsroot_copy, '@')) != NULL) 452 { 453 *p = '\0'; 454 /* check for a password */ 455 if ((q = strchr (cvsroot_copy, ':')) != NULL) 456 { 457 *q = '\0'; 458 newroot->password = xstrdup (++q); 459 /* Don't check for *newroot->password == '\0' since 460 * a user could conceivably wish to specify a blank password 461 * 462 * (newroot->password == NULL means to use the 463 * password from .cvspass) 464 */ 465 } 466 467 /* copy the username */ 468 if (*cvsroot_copy != '\0') 469 /* a blank username is impossible, so leave it NULL in that 470 * case so we know to use the default username 471 */ 472 newroot->username = xstrdup (cvsroot_copy); 473 474 cvsroot_copy = ++p; 475 } 476 477 /* now deal with host[:[port]] */ 478 479 /* the port */ 480 if ((p = strchr (cvsroot_copy, ':')) != NULL) 481 { 482 *p++ = '\0'; 483 if (strlen(p)) 484 { 485 q = p; 486 if (*q == '-') q++; 487 while (*q) 488 { 489 if (!isdigit(*q++)) 490 { 491 error (0, 0, "CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 492 p); 493 error (0, 0, "Perhaps you entered a relative pathname?"); 494 goto error_exit; 495 } 496 } 497 if ((newroot->port = atoi (p)) <= 0) 498 { 499 error (0, 0, "CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 500 p); 501 error (0, 0, "Perhaps you entered a relative pathname?"); 502 goto error_exit; 503 } 504 } 505 } 506 507 /* copy host */ 508 if (*cvsroot_copy != '\0') 509 /* blank hostnames are invalid, but for now leave the field NULL 510 * and catch the error during the sanity checks later 511 */ 512 newroot->hostname = xstrdup (cvsroot_copy); 513 514 /* restore the '/' */ 515 cvsroot_copy = firstslash; 516 *cvsroot_copy = '/'; 517 } 518 519 /* parse the path for all methods */ 520 newroot->directory = xstrdup(cvsroot_copy); 521 522 /* 523 * Do various sanity checks. 524 */ 525 526#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 527 if (newroot->method != local_method) 528 { 529 error (0, 0, "CVSROOT is set for a remote access method but your"); 530 error (0, 0, "CVS executable doesn't support it."); 531 goto error_exit; 532 } 533#endif 534 535#if ! defined (SERVER_SUPPORT) && ! defined (DEBUG) 536 if (newroot->method == fork_method) 537 { 538 error (0, 0, "CVSROOT is set to use the :fork: access method but your"); 539 error (0, 0, "CVS executable doesn't support it."); 540 goto error_exit; 541 } 542#endif 543 544 if (newroot->username && ! newroot->hostname) 545 { 546 error (0, 0, "Missing hostname in CVSROOT."); 547 goto error_exit; 548 } 549 550 check_hostname = 0; 551 no_password = 0; 552 no_port = 0; 553 switch (newroot->method) 554 { 555 case local_method: 556 if (newroot->username || newroot->hostname) 557 { 558 error (0, 0, "Can't specify hostname and username in CVSROOT"); 559 error (0, 0, "when using local access method."); 560 goto error_exit; 561 } 562 /* cvs.texinfo has always told people that CVSROOT must be an 563 absolute pathname. Furthermore, attempts to use a relative 564 pathname produced various errors (I couldn't get it to work), 565 so there would seem to be little risk in making this a fatal 566 error. */ 567 if (!isabsolute (newroot->directory)) 568 { 569 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 570 newroot->directory); 571 error (0, 0, "when using local access method."); 572 goto error_exit; 573 } 574 no_port = 1; 575 no_password = 1; 576 break; 577 case fork_method: 578 /* We want :fork: to behave the same as other remote access 579 methods. Therefore, don't check to see that the repository 580 name is absolute -- let the server do it. */ 581 if (newroot->username || newroot->hostname) 582 { 583 error (0, 0, "Can't specify hostname and username in CVSROOT"); 584 error (0, 0, "when using fork access method."); 585 goto error_exit; 586 } 587 if (!isabsolute (newroot->directory)) 588 { 589 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 590 newroot->directory); 591 error (0, 0, "when using fork access method."); 592 goto error_exit; 593 } 594 no_port = 1; 595 no_password = 1; 596 break; 597 case kserver_method: 598#ifndef HAVE_KERBEROS 599 error (0, 0, "CVSROOT is set for a kerberos access method but your"); 600 error (0, 0, "CVS executable doesn't support it."); 601 goto error_exit; 602#else 603 check_hostname = 1; 604 break; 605#endif 606 case gserver_method: 607#ifndef HAVE_GSSAPI 608 error (0, 0, "CVSROOT is set for a GSSAPI access method but your"); 609 error (0, 0, "CVS executable doesn't support it."); 610 goto error_exit; 611#else 612 check_hostname = 1; 613 break; 614#endif 615 case server_method: 616 case ext_method: 617 no_port = 1; 618 no_password = 1; 619 check_hostname = 1; 620 break; 621 case pserver_method: 622 check_hostname = 1; 623 break; 624 default: 625 error (1, 0, "Invalid method found in parse_cvsroot"); 626 } 627 628 if (no_password && newroot->password) 629 { 630 error (0, 0, "CVSROOT password specification is only valid for"); 631 error (0, 0, "pserver connection method."); 632 goto error_exit; 633 } 634 635 if (check_hostname && !newroot->hostname) 636 { 637 error (0, 0, "Didn't specify hostname in CVSROOT."); 638 goto error_exit; 639 } 640 641 if (no_port && newroot->port) 642 { 643 error (0, 0, "CVSROOT port specification is only valid for gserver, kserver,"); 644 error (0, 0, "and pserver connection methods."); 645 goto error_exit; 646 } 647 648 if (*newroot->directory == '\0') 649 { 650 error (0, 0, "Missing directory in CVSROOT."); 651 goto error_exit; 652 } 653 654 /* Hooray! We finally parsed it! */ 655 free (cvsroot_save); 656 return newroot; 657 658error_exit: 659 free (cvsroot_save); 660 free_cvsroot_t (newroot); 661 return NULL; 662} 663 664 665 666#ifdef AUTH_CLIENT_SUPPORT 667/* Use root->username, root->hostname, root->port, and root->directory 668 * to create a normalized CVSROOT fit for the .cvspass file 669 * 670 * username defaults to the result of getcaller() 671 * port defaults to the result of get_cvs_port_number() 672 * 673 * FIXME - we could cache the canonicalized version of a root inside the 674 * cvsroot_t, but we'd have to un'const the input here and stop expecting the 675 * caller to be responsible for our return value 676 */ 677char * 678normalize_cvsroot (root) 679 const cvsroot_t *root; 680{ 681 char *cvsroot_canonical; 682 char *p, *hostname, *username; 683 char port_s[64]; 684 685 /* get the appropriate port string */ 686 sprintf (port_s, "%d", get_cvs_port_number (root)); 687 688 /* use a lower case hostname since we know hostnames are case insensitive */ 689 /* Some logic says we should be tacking our domain name on too if it isn't 690 * there already, but for now this works. Reverse->Forward lookups are 691 * almost certainly too much since that would make CVS immune to some of 692 * the DNS trickery that makes life easier for sysadmins when they want to 693 * move a repository or the like 694 */ 695 p = hostname = xstrdup(root->hostname); 696 while (*p) 697 { 698 *p = tolower(*p); 699 p++; 700 } 701 702 /* get the username string */ 703 username = root->username ? root->username : getcaller(); 704 cvsroot_canonical = xmalloc ( strlen(username) 705 + strlen(hostname) + strlen(port_s) 706 + strlen(root->directory) + 12); 707 sprintf (cvsroot_canonical, ":pserver:%s@%s:%s%s", 708 username, hostname, port_s, root->directory); 709 710 free (hostname); 711 return cvsroot_canonical; 712} 713#endif /* AUTH_CLIENT_SUPPORT */ 714 715 716 717/* allocate and return a cvsroot_t structure set up as if we're using the local 718 * repository DIR. */ 719cvsroot_t * 720local_cvsroot (dir) 721 char *dir; 722{ 723 cvsroot_t *newroot = new_cvsroot_t(); 724 725 newroot->original = xstrdup(dir); 726 newroot->method = local_method; 727 newroot->directory = xstrdup(dir); 728 729 return newroot; 730} 731 732 733 734#ifdef DEBUG 735/* This is for testing the parsing function. Use 736 737 gcc -I. -I.. -I../lib -DDEBUG root.c -o root 738 739 to compile. */ 740 741#include <stdio.h> 742 743char *program_name = "testing"; 744char *command_name = "parse_cvsroot"; /* XXX is this used??? */ 745 746/* Toy versions of various functions when debugging under unix. Yes, 747 these make various bad assumptions, but they're pretty easy to 748 debug when something goes wrong. */ 749 750void 751error_exit PROTO ((void)) 752{ 753 exit (1); 754} 755 756int 757isabsolute (dir) 758 const char *dir; 759{ 760 return (dir && (*dir == '/')); 761} 762 763void 764main (argc, argv) 765 int argc; 766 char *argv[]; 767{ 768 program_name = argv[0]; 769 770 if (argc != 2) 771 { 772 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 773 exit (2); 774 } 775 776 if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) 777 { 778 fprintf (stderr, "%s: Parsing failed.\n", program_name); 779 exit (1); 780 } 781 printf ("CVSroot: %s\n", argv[1]); 782 printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); 783 printf ("current_parsed_root->username: %s\n", 784 current_parsed_root->username ? current_parsed_root->username : "NULL"); 785 printf ("current_parsed_root->hostname: %s\n", 786 current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); 787 printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); 788 789 exit (0); 790 /* NOTREACHED */ 791} 792#endif 793/* vim:tabstop=8:shiftwidth=4 794 */ 795