117721Speter/* 2177391Sobrien * Copyright (C) 1986-2008 The Free Software Foundation, Inc. 317721Speter * 4175261Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175261Sobrien * and others. 6175261Sobrien * 7175261Sobrien * Poritons Copyright (c) 1992, Mark D. Baushke 8175261Sobrien * 917721Speter * You may distribute under the terms of the GNU General Public License as 1032785Speter * specified in the README file that comes with the CVS source distribution. 1117721Speter * 1217721Speter * Name of Root 1317721Speter * 1417721Speter * Determine the path to the CVSROOT and set "Root" accordingly. 1517721Speter */ 1617721Speter 1717721Speter#include "cvs.h" 18175261Sobrien#include <assert.h> 1925839Speter#include "getline.h" 2017721Speter 2181404Speter/* Printable names for things in the current_parsed_root->method enum variable. 2225839Speter Watch out if the enum is changed in cvs.h! */ 2325839Speter 24128266Speterconst char method_names[][16] = { 25128266Speter "undefined", "local", "server (rsh)", "pserver", 26175261Sobrien "kserver", "gserver", "ext", "extssh", "fork" 2725839Speter}; 2825839Speter 2925839Speter#ifndef DEBUG 3025839Speter 31175261Sobriencvsroot_t * 3232785SpeterName_Root (dir, update_dir) 33175261Sobrien const char *dir; 34175261Sobrien const char *update_dir; 3517721Speter{ 3617721Speter FILE *fpin; 37175261Sobrien cvsroot_t *ret; 38175261Sobrien const char *xupdate_dir; 3925839Speter char *root = NULL; 4025839Speter size_t root_allocated = 0; 4125839Speter char *tmp; 4225839Speter char *cvsadm; 4317721Speter char *cp; 44128266Speter int len; 4517721Speter 4617721Speter if (update_dir && *update_dir) 4717721Speter xupdate_dir = update_dir; 4817721Speter else 4917721Speter xupdate_dir = "."; 5017721Speter 5117721Speter if (dir != NULL) 5217721Speter { 5325839Speter cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 5417721Speter (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 5525839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 5617721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 5717721Speter } 5817721Speter else 5917721Speter { 6025839Speter cvsadm = xstrdup (CVSADM); 6125839Speter tmp = xstrdup (CVSADM_ROOT); 6217721Speter } 6317721Speter 6417721Speter /* 6517721Speter * Do not bother looking for a readable file if there is no cvsadm 6617721Speter * directory present. 6717721Speter * 6817721Speter * It is possible that not all repositories will have a CVS/Root 6917721Speter * file. This is ok, but the user will need to specify -d 7017721Speter * /path/name or have the environment variable CVSROOT set in 7125839Speter * order to continue. */ 7217721Speter if ((!isdir (cvsadm)) || (!isreadable (tmp))) 7317721Speter { 7425839Speter ret = NULL; 7525839Speter goto out; 7617721Speter } 7717721Speter 7817721Speter /* 7917721Speter * The assumption here is that the CVS Root is always contained in the 8017721Speter * first line of the "Root" file. 8117721Speter */ 8217721Speter fpin = open_file (tmp, "r"); 8317721Speter 84128266Speter if ((len = getline (&root, &root_allocated, fpin)) < 0) 8517721Speter { 86128266Speter int saved_errno = errno; 8725839Speter /* FIXME: should be checking for end of file separately; errno 8825839Speter is not set in that case. */ 8917721Speter error (0, 0, "in directory %s:", xupdate_dir); 90128266Speter error (0, saved_errno, "cannot read %s", CVSADM_ROOT); 9117721Speter error (0, 0, "please correct this problem"); 9225839Speter ret = NULL; 9325839Speter goto out; 9417721Speter } 95128266Speter fclose (fpin); 96175261Sobrien cp = root + len - 1; 97128266Speter if (*cp == '\n') 9817721Speter *cp = '\0'; /* strip the newline */ 9917721Speter 10017721Speter /* 10117721Speter * root now contains a candidate for CVSroot. It must be an 10225839Speter * absolute pathname or specify a remote server. 10317721Speter */ 10417721Speter 105175261Sobrien ret = parse_cvsroot (root); 106175261Sobrien if (ret == NULL) 10717721Speter { 10817721Speter error (0, 0, "in directory %s:", xupdate_dir); 10917721Speter error (0, 0, 110175261Sobrien "ignoring %s because it does not contain a valid root.", 11117721Speter CVSADM_ROOT); 11225839Speter goto out; 11317721Speter } 11417721Speter 115175261Sobrien if (!ret->isremote && !isdir (ret->directory)) 11617721Speter { 11717721Speter error (0, 0, "in directory %s:", xupdate_dir); 11817721Speter error (0, 0, 11917721Speter "ignoring %s because it specifies a non-existent repository %s", 12017721Speter CVSADM_ROOT, root); 121175261Sobrien free_cvsroot_t (ret); 12225839Speter ret = NULL; 12325839Speter goto out; 12417721Speter } 12517721Speter 126175261Sobrien 12725839Speter out: 12825839Speter free (cvsadm); 12925839Speter free (tmp); 13025839Speter if (root != NULL) 13125839Speter free (root); 132175261Sobrien return ret; 13317721Speter} 13417721Speter 135128266Speter 136128266Speter 13717721Speter/* 13817721Speter * Write the CVS/Root file so that the environment variable CVSROOT 13917721Speter * and/or the -d option to cvs will be validated or not necessary for 14017721Speter * future work. 14117721Speter */ 14217721Spetervoid 14317721SpeterCreate_Root (dir, rootdir) 144128266Speter const char *dir; 145128266Speter const char *rootdir; 14617721Speter{ 14717721Speter FILE *fout; 14825839Speter char *tmp; 14917721Speter 15017721Speter if (noexec) 15117721Speter return; 15217721Speter 15317721Speter /* record the current cvs root */ 15417721Speter 15517721Speter if (rootdir != NULL) 15617721Speter { 15717721Speter if (dir != NULL) 15825839Speter { 15925839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 16017721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 16125839Speter } 16217721Speter else 16325839Speter tmp = xstrdup (CVSADM_ROOT); 16425839Speter 16517721Speter fout = open_file (tmp, "w+"); 16617721Speter if (fprintf (fout, "%s\n", rootdir) < 0) 16717721Speter error (1, errno, "write to %s failed", tmp); 16817721Speter if (fclose (fout) == EOF) 16917721Speter error (1, errno, "cannot close %s", tmp); 17025839Speter free (tmp); 17117721Speter } 17217721Speter} 17325839Speter 17425839Speter#endif /* ! DEBUG */ 17525839Speter 17625839Speter 17726801Speter/* The root_allow_* stuff maintains a list of legal CVSROOT 17826801Speter directories. Then we can check against them when a remote user 17926801Speter hands us a CVSROOT directory. */ 18026801Speter 18166525Speterstatic int root_allow_count; 18226801Speterstatic char **root_allow_vector; 18366525Speterstatic int root_allow_size; 18426801Speter 185177391Sobrienint 186177391Sobrienroot_allow_used () 187177391Sobrien{ 188177391Sobrien return root_allow_count; 189177391Sobrien} 190177391Sobrien 19126801Spetervoid 19226801Speterroot_allow_add (arg) 19326801Speter char *arg; 19426801Speter{ 19526801Speter char *p; 19626801Speter 19726801Speter if (root_allow_size <= root_allow_count) 19826801Speter { 19926801Speter if (root_allow_size == 0) 20026801Speter { 20126801Speter root_allow_size = 1; 20226801Speter root_allow_vector = 203109655Speter (char **) xmalloc (root_allow_size * sizeof (char *)); 20426801Speter } 20526801Speter else 20626801Speter { 20726801Speter root_allow_size *= 2; 20826801Speter root_allow_vector = 209109655Speter (char **) xrealloc (root_allow_vector, 21026801Speter root_allow_size * sizeof (char *)); 21126801Speter } 21226801Speter 21326801Speter if (root_allow_vector == NULL) 21426801Speter { 21526801Speter no_memory: 21626801Speter /* Strictly speaking, we're not supposed to output anything 21726801Speter now. But we're about to exit(), give it a try. */ 21826801Speter printf ("E Fatal server error, aborting.\n\ 21926801Spetererror ENOMEM Virtual memory exhausted.\n"); 22026801Speter 221107484Speter error_exit (); 22226801Speter } 22326801Speter } 224109655Speter p = xmalloc (strlen (arg) + 1); 22526801Speter if (p == NULL) 22626801Speter goto no_memory; 22726801Speter strcpy (p, arg); 22826801Speter root_allow_vector[root_allow_count++] = p; 22926801Speter} 23026801Speter 23126801Spetervoid 23226801Speterroot_allow_free () 23326801Speter{ 23426801Speter if (root_allow_vector != NULL) 23566525Speter free_names (&root_allow_count, root_allow_vector); 23626801Speter root_allow_size = 0; 23726801Speter} 23826801Speter 23926801Speterint 24026801Speterroot_allow_ok (arg) 24126801Speter char *arg; 24226801Speter{ 24366525Speter int i; 24432785Speter 24532785Speter if (root_allow_count == 0) 24632785Speter { 24732785Speter /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 24832785Speter or later without reading the documentation about 24932785Speter --allow-root. Printing an error here doesn't disclose any 25032785Speter particularly useful information to an attacker because a 25132785Speter CVS server configured in this way won't let *anyone* in. */ 25232785Speter 25332785Speter /* Note that we are called from a context where we can spit 25432785Speter back "error" rather than waiting for the next request which 25532785Speter expects responses. */ 25632785Speter printf ("\ 25732785Spetererror 0 Server configuration missing --allow-root in inetd.conf\n"); 25832785Speter error_exit (); 25932785Speter } 26032785Speter 26126801Speter for (i = 0; i < root_allow_count; ++i) 26226801Speter if (strcmp (root_allow_vector[i], arg) == 0) 26326801Speter return 1; 26426801Speter return 0; 26526801Speter} 26626801Speter 26781404Speter 26881404Speter 26954427Speter/* This global variable holds the global -d option. It is NULL if -d 27054427Speter was not used, which means that we must get the CVSroot information 27154427Speter from the CVSROOT environment variable or from a CVS/Root file. */ 27254427Speterchar *CVSroot_cmdline; 27354427Speter 27481404Speter 27581404Speter 276107484Speter/* FIXME - Deglobalize this. */ 27781404Spetercvsroot_t *current_parsed_root = NULL; 27825839Speter 27981404Speter 28081404Speter 28181404Speter/* allocate and initialize a cvsroot_t 28281404Speter * 28381404Speter * We must initialize the strings to NULL so we know later what we should 28481404Speter * free 28581404Speter * 28681404Speter * Some of the other zeroes remain meaningful as, "never set, use default", 28781404Speter * or the like 28881404Speter */ 28981404Speterstatic cvsroot_t * 29081404Speternew_cvsroot_t () 29125839Speter{ 29281404Speter cvsroot_t *newroot; 29325839Speter 29481404Speter /* gotta store it somewhere */ 29581404Speter newroot = xmalloc(sizeof(cvsroot_t)); 29681404Speter 29781404Speter newroot->original = NULL; 29881404Speter newroot->method = null_method; 299175261Sobrien newroot->isremote = 0; 300128266Speter#ifdef CLIENT_SUPPORT 30181404Speter newroot->username = NULL; 30281404Speter newroot->password = NULL; 30381404Speter newroot->hostname = NULL; 30481404Speter newroot->port = 0; 30581404Speter newroot->directory = NULL; 306128266Speter newroot->proxy_hostname = NULL; 307128266Speter newroot->proxy_port = 0; 30881404Speter#endif /* CLIENT_SUPPORT */ 30981404Speter 31081404Speter return newroot; 31181404Speter} 31281404Speter 31381404Speter 31481404Speter 31581404Speter/* Dispose of a cvsroot_t and its component parts */ 31681404Spetervoid 31781404Speterfree_cvsroot_t (root) 31881404Speter cvsroot_t *root; 31981404Speter{ 32081404Speter if (root->original != NULL) 32181404Speter free (root->original); 322128266Speter if (root->directory != NULL) 323128266Speter free (root->directory); 324128266Speter#ifdef CLIENT_SUPPORT 32581404Speter if (root->username != NULL) 32681404Speter free (root->username); 32781404Speter if (root->password != NULL) 32825839Speter { 32981404Speter /* I like to be paranoid */ 33081404Speter memset (root->password, 0, strlen (root->password)); 33181404Speter free (root->password); 33225839Speter } 33381404Speter if (root->hostname != NULL) 33481404Speter free (root->hostname); 335128266Speter if (root->proxy_hostname != NULL) 336128266Speter free (root->proxy_hostname); 337128266Speter#endif /* CLIENT_SUPPORT */ 33881404Speter free (root); 33981404Speter} 34025839Speter 34166525Speter 34225839Speter 34381404Speter/* 344107484Speter * Parse a CVSROOT string to allocate and return a new cvsroot_t structure. 345107484Speter * Valid specifications are: 346107484Speter * 347107484Speter * :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path 348107484Speter * [:(ext|server):][[user]@]host[:]/path 349107484Speter * [:local:[e:]]/path 350107484Speter * :fork:/path 351107484Speter * 352107484Speter * INPUTS 353107484Speter * root_in C String containing the CVSROOT to be parsed. 354107484Speter * 355107484Speter * RETURNS 356107484Speter * A pointer to a newly allocated cvsroot_t structure upon success and 357107484Speter * NULL upon failure. The caller is responsible for disposing of 358107484Speter * new structures with a call to free_cvsroot_t(). 359107484Speter * 360107484Speter * NOTES 361107484Speter * This would have been a lot easier to write in Perl. 362107484Speter * 363107484Speter * SEE ALSO 364107484Speter * free_cvsroot_t() 36581404Speter */ 36681404Spetercvsroot_t * 36781404Speterparse_cvsroot (root_in) 368128266Speter const char *root_in; 36981404Speter{ 37081404Speter cvsroot_t *newroot; /* the new root to be returned */ 37181404Speter char *cvsroot_save; /* what we allocated so we can dispose 37281404Speter * it when finished */ 37381404Speter char *firstslash; /* save where the path spec starts 37481404Speter * while we parse 37581404Speter * [[user][:password]@]host[:[port]] 37681404Speter */ 37781404Speter char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ 378128266Speter#ifdef CLIENT_SUPPORT 37981404Speter int check_hostname, no_port, no_password; 380128266Speter#endif /* CLIENT_SUPPORT */ 38181404Speter 382175261Sobrien assert (root_in); 383175261Sobrien 38481404Speter /* allocate some space */ 38581404Speter newroot = new_cvsroot_t(); 38681404Speter 38781404Speter /* save the original string */ 38881404Speter newroot->original = xstrdup (root_in); 38981404Speter 39081404Speter /* and another copy we can munge while parsing */ 39181404Speter cvsroot_save = cvsroot_copy = xstrdup (root_in); 39281404Speter 39366525Speter if (*cvsroot_copy == ':') 39425839Speter { 39525839Speter char *method = ++cvsroot_copy; 39625839Speter 39725839Speter /* Access method specified, as in 39881404Speter * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", 39981404Speter * "cvs -d [:(ext|server):][[user]@]host[:]/path", 40054427Speter * "cvs -d :local:e:\path", 40154427Speter * "cvs -d :fork:/path". 40225839Speter * We need to get past that part of CVSroot before parsing the 40325839Speter * rest of it. 40425839Speter */ 40525839Speter 40625839Speter if (! (p = strchr (method, ':'))) 40725839Speter { 408102840Speter error (0, 0, "No closing `:' on method in CVSROOT."); 40981404Speter goto error_exit; 41025839Speter } 41125839Speter *p = '\0'; 41225839Speter cvsroot_copy = ++p; 41325839Speter 414128266Speter#ifdef CLIENT_SUPPORT 415128266Speter /* Look for method options, for instance, proxy, proxyport. 416128266Speter * We don't handle these, but we like to try and warn the user that 417128266Speter * they are being ignored. 418128266Speter */ 419175261Sobrien if ((p = strchr (method, ';')) != NULL) 420128266Speter { 421128266Speter *p++ = '\0'; 422128266Speter if (!really_quiet) 423128266Speter { 424128266Speter error (0, 0, 425128266Speter"WARNING: Ignoring method options found in CVSROOT: `%s'.", 426128266Speter p); 427128266Speter error (0, 0, 428128266Speter"Use CVS version 1.12.7 or later to handle method options."); 429128266Speter } 430128266Speter } 431128266Speter#endif /* CLIENT_SUPPORT */ 432128266Speter 43325839Speter /* Now we have an access method -- see if it's valid. */ 43425839Speter 43525839Speter if (strcmp (method, "local") == 0) 43681404Speter newroot->method = local_method; 43725839Speter else if (strcmp (method, "pserver") == 0) 43881404Speter newroot->method = pserver_method; 43925839Speter else if (strcmp (method, "kserver") == 0) 44081404Speter newroot->method = kserver_method; 44132785Speter else if (strcmp (method, "gserver") == 0) 44281404Speter newroot->method = gserver_method; 44325839Speter else if (strcmp (method, "server") == 0) 44481404Speter newroot->method = server_method; 44525839Speter else if (strcmp (method, "ext") == 0) 44681404Speter newroot->method = ext_method; 447177391Sobrien else if (strcmp (method, "extssh") == 0) 448177391Sobrien newroot->method = extssh_method; 44954427Speter else if (strcmp (method, "fork") == 0) 45081404Speter newroot->method = fork_method; 45125839Speter else 45225839Speter { 453102840Speter error (0, 0, "Unknown method (`%s') in CVSROOT.", method); 45481404Speter goto error_exit; 45525839Speter } 45625839Speter } 45725839Speter else 45825839Speter { 459107484Speter /* If the method isn't specified, assume EXT_METHOD if the string looks 460107484Speter like a relative path and LOCAL_METHOD otherwise. */ 46125839Speter 46281404Speter newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) 46325839Speter ? ext_method 46425839Speter : local_method); 46525839Speter } 46625839Speter 46781404Speter newroot->isremote = (newroot->method != local_method); 46825839Speter 46981404Speter if ((newroot->method != local_method) 47081404Speter && (newroot->method != fork_method)) 47125839Speter { 47281404Speter /* split the string into [[user][:password]@]host[:[port]] & /path 47381404Speter * 47481404Speter * this will allow some characters such as '@' & ':' to remain unquoted 47581404Speter * in the path portion of the spec 47681404Speter */ 47781404Speter if ((p = strchr (cvsroot_copy, '/')) == NULL) 47881404Speter { 479102840Speter error (0, 0, "CVSROOT requires a path spec:"); 480128266Speter error (0, 0, 481128266Speter":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); 48281404Speter error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); 48381404Speter goto error_exit; 48481404Speter } 48581404Speter firstslash = p; /* == NULL if '/' not in string */ 48681404Speter *p = '\0'; 48725839Speter 488128266Speter /* Don't parse username, password, hostname, or port without client 489128266Speter * support. 490128266Speter */ 491128266Speter#ifdef CLIENT_SUPPORT 49281404Speter /* Check to see if there is a username[:password] in the string. */ 49366525Speter if ((p = strchr (cvsroot_copy, '@')) != NULL) 49425839Speter { 49525839Speter *p = '\0'; 49681404Speter /* check for a password */ 49781404Speter if ((q = strchr (cvsroot_copy, ':')) != NULL) 49881404Speter { 49981404Speter *q = '\0'; 50081404Speter newroot->password = xstrdup (++q); 50181404Speter /* Don't check for *newroot->password == '\0' since 50281404Speter * a user could conceivably wish to specify a blank password 503107484Speter * 50481404Speter * (newroot->password == NULL means to use the 50581404Speter * password from .cvspass) 50681404Speter */ 50781404Speter } 50881404Speter 50981404Speter /* copy the username */ 51081404Speter if (*cvsroot_copy != '\0') 51181404Speter /* a blank username is impossible, so leave it NULL in that 51281404Speter * case so we know to use the default username 51381404Speter */ 51481404Speter newroot->username = xstrdup (cvsroot_copy); 51581404Speter 51625839Speter cvsroot_copy = ++p; 51725839Speter } 51825839Speter 51981404Speter /* now deal with host[:[port]] */ 52081404Speter 52181404Speter /* the port */ 52266525Speter if ((p = strchr (cvsroot_copy, ':')) != NULL) 52325839Speter { 52481404Speter *p++ = '\0'; 52581404Speter if (strlen(p)) 52681404Speter { 52781404Speter q = p; 52881404Speter if (*q == '-') q++; 52981404Speter while (*q) 53081404Speter { 53181404Speter if (!isdigit(*q++)) 53281404Speter { 533128266Speter error (0, 0, 534128266Speter"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 53581404Speter p); 536128266Speter error (0, 0, 537128266Speter "Perhaps you entered a relative pathname?"); 53881404Speter goto error_exit; 53981404Speter } 54081404Speter } 54181404Speter if ((newroot->port = atoi (p)) <= 0) 54281404Speter { 543128266Speter error (0, 0, 544128266Speter"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 54581404Speter p); 546102840Speter error (0, 0, "Perhaps you entered a relative pathname?"); 54781404Speter goto error_exit; 54881404Speter } 54981404Speter } 55025839Speter } 55181404Speter 55281404Speter /* copy host */ 55381404Speter if (*cvsroot_copy != '\0') 55481404Speter /* blank hostnames are invalid, but for now leave the field NULL 55581404Speter * and catch the error during the sanity checks later 55681404Speter */ 55781404Speter newroot->hostname = xstrdup (cvsroot_copy); 55881404Speter 55981404Speter /* restore the '/' */ 56081404Speter cvsroot_copy = firstslash; 56181404Speter *cvsroot_copy = '/'; 562128266Speter#endif /* CLIENT_SUPPORT */ 56325839Speter } 56425839Speter 565128266Speter /* 566128266Speter * Parse the path for all methods. 567128266Speter */ 568128266Speter /* Here & local_cvsroot() should be the only places this needs to be 569128266Speter * called on a CVSROOT now. cvsroot->original is saved for error messages 570128266Speter * and, otherwise, we want no trailing slashes. 571128266Speter */ 572128266Speter Sanitize_Repository_Name( cvsroot_copy ); 57381404Speter newroot->directory = xstrdup(cvsroot_copy); 57425839Speter 57581404Speter /* 57681404Speter * Do various sanity checks. 57781404Speter */ 57881404Speter 57925839Speter#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 58081404Speter if (newroot->method != local_method) 58125839Speter { 582102840Speter error (0, 0, "CVSROOT is set for a remote access method but your"); 583102840Speter error (0, 0, "CVS executable doesn't support it."); 58481404Speter goto error_exit; 58525839Speter } 58625839Speter#endif 58725839Speter 58881404Speter#if ! defined (SERVER_SUPPORT) && ! defined (DEBUG) 58981404Speter if (newroot->method == fork_method) 59025839Speter { 591102840Speter error (0, 0, "CVSROOT is set to use the :fork: access method but your"); 592102840Speter error (0, 0, "CVS executable doesn't support it."); 59381404Speter goto error_exit; 59481404Speter } 59581404Speter#endif 59681404Speter 597128266Speter#ifdef CLIENT_SUPPORT 59881404Speter if (newroot->username && ! newroot->hostname) 59981404Speter { 600102840Speter error (0, 0, "Missing hostname in CVSROOT."); 60181404Speter goto error_exit; 60225839Speter } 60325839Speter 60432785Speter check_hostname = 0; 605128266Speter no_password = 1; 60681404Speter no_port = 0; 607128266Speter#endif /* CLIENT_SUPPORT */ 60881404Speter switch (newroot->method) 60925839Speter { 61025839Speter case local_method: 611128266Speter#ifdef CLIENT_SUPPORT 61281404Speter if (newroot->username || newroot->hostname) 61325839Speter { 614102840Speter error (0, 0, "Can't specify hostname and username in CVSROOT"); 615102840Speter error (0, 0, "when using local access method."); 61681404Speter goto error_exit; 61725839Speter } 618128266Speter no_port = 1; 619128266Speter /* no_password already set */ 620128266Speter#endif /* CLIENT_SUPPORT */ 62125839Speter /* cvs.texinfo has always told people that CVSROOT must be an 62225839Speter absolute pathname. Furthermore, attempts to use a relative 62325839Speter pathname produced various errors (I couldn't get it to work), 62425839Speter so there would seem to be little risk in making this a fatal 62525839Speter error. */ 62681404Speter if (!isabsolute (newroot->directory)) 62781404Speter { 628102840Speter error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 62981404Speter newroot->directory); 630102840Speter error (0, 0, "when using local access method."); 63181404Speter goto error_exit; 63281404Speter } 63325839Speter break; 634128266Speter#ifdef CLIENT_SUPPORT 63566525Speter case fork_method: 63666525Speter /* We want :fork: to behave the same as other remote access 63766525Speter methods. Therefore, don't check to see that the repository 63866525Speter name is absolute -- let the server do it. */ 63981404Speter if (newroot->username || newroot->hostname) 64066525Speter { 641102840Speter error (0, 0, "Can't specify hostname and username in CVSROOT"); 642102840Speter error (0, 0, "when using fork access method."); 64381404Speter goto error_exit; 64466525Speter } 645128266Speter newroot->hostname = xstrdup("server"); /* for error messages */ 64681404Speter if (!isabsolute (newroot->directory)) 64781404Speter { 648102840Speter error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 64981404Speter newroot->directory); 650102840Speter error (0, 0, "when using fork access method."); 65181404Speter goto error_exit; 65281404Speter } 65381404Speter no_port = 1; 654128266Speter /* no_password already set */ 65566525Speter break; 65625839Speter case kserver_method: 657128266Speter# ifndef HAVE_KERBEROS 658102840Speter error (0, 0, "CVSROOT is set for a kerberos access method but your"); 659102840Speter error (0, 0, "CVS executable doesn't support it."); 66081404Speter goto error_exit; 661128266Speter# else 66232785Speter check_hostname = 1; 663128266Speter /* no_password already set */ 66432785Speter break; 665128266Speter# endif 66632785Speter case gserver_method: 667128266Speter# ifndef HAVE_GSSAPI 668102840Speter error (0, 0, "CVSROOT is set for a GSSAPI access method but your"); 669102840Speter error (0, 0, "CVS executable doesn't support it."); 67081404Speter goto error_exit; 671128266Speter# else 67232785Speter check_hostname = 1; 673128266Speter /* no_password already set */ 67432785Speter break; 675128266Speter# endif 67625839Speter case server_method: 67725839Speter case ext_method: 678177391Sobrien case extssh_method: 67981404Speter no_port = 1; 680128266Speter /* no_password already set */ 68181404Speter check_hostname = 1; 68281404Speter break; 68325839Speter case pserver_method: 684128266Speter no_password = 0; 68532785Speter check_hostname = 1; 68632785Speter break; 687128266Speter#endif /* CLIENT_SUPPORT */ 688102840Speter default: 689102840Speter error (1, 0, "Invalid method found in parse_cvsroot"); 69032785Speter } 69132785Speter 692128266Speter#ifdef CLIENT_SUPPORT 69381404Speter if (no_password && newroot->password) 69432785Speter { 69581404Speter error (0, 0, "CVSROOT password specification is only valid for"); 69681404Speter error (0, 0, "pserver connection method."); 69781404Speter goto error_exit; 69881404Speter } 69981404Speter 70081404Speter if (check_hostname && !newroot->hostname) 70181404Speter { 702102840Speter error (0, 0, "Didn't specify hostname in CVSROOT."); 70381404Speter goto error_exit; 70481404Speter } 70581404Speter 70681404Speter if (no_port && newroot->port) 70725839Speter { 70881404Speter error (0, 0, "CVSROOT port specification is only valid for gserver, kserver,"); 70981404Speter error (0, 0, "and pserver connection methods."); 71081404Speter goto error_exit; 71125839Speter } 712128266Speter#endif /* CLIENT_SUPPORT */ 71325839Speter 71481404Speter if (*newroot->directory == '\0') 71525839Speter { 716102840Speter error (0, 0, "Missing directory in CVSROOT."); 71781404Speter goto error_exit; 71825839Speter } 71925839Speter 72025839Speter /* Hooray! We finally parsed it! */ 721107484Speter free (cvsroot_save); 72281404Speter return newroot; 72381404Speter 72481404Spetererror_exit: 725107484Speter free (cvsroot_save); 72681404Speter free_cvsroot_t (newroot); 72781404Speter return NULL; 72825839Speter} 72925839Speter 73025839Speter 73125839Speter 73281404Speter#ifdef AUTH_CLIENT_SUPPORT 73381404Speter/* Use root->username, root->hostname, root->port, and root->directory 73481404Speter * to create a normalized CVSROOT fit for the .cvspass file 73581404Speter * 73681404Speter * username defaults to the result of getcaller() 73781404Speter * port defaults to the result of get_cvs_port_number() 73881404Speter * 73981404Speter * FIXME - we could cache the canonicalized version of a root inside the 74081404Speter * cvsroot_t, but we'd have to un'const the input here and stop expecting the 74181404Speter * caller to be responsible for our return value 74281404Speter */ 74381404Speterchar * 74481404Speternormalize_cvsroot (root) 74581404Speter const cvsroot_t *root; 74681404Speter{ 74781404Speter char *cvsroot_canonical; 74881404Speter char *p, *hostname, *username; 74981404Speter char port_s[64]; 75081404Speter 751175261Sobrien assert (root && root->hostname && root->directory); 752175261Sobrien 75381404Speter /* get the appropriate port string */ 75481404Speter sprintf (port_s, "%d", get_cvs_port_number (root)); 75581404Speter 75681404Speter /* use a lower case hostname since we know hostnames are case insensitive */ 75781404Speter /* Some logic says we should be tacking our domain name on too if it isn't 75881404Speter * there already, but for now this works. Reverse->Forward lookups are 75981404Speter * almost certainly too much since that would make CVS immune to some of 76081404Speter * the DNS trickery that makes life easier for sysadmins when they want to 76181404Speter * move a repository or the like 76281404Speter */ 76381404Speter p = hostname = xstrdup(root->hostname); 76481404Speter while (*p) 76581404Speter { 76681404Speter *p = tolower(*p); 76781404Speter p++; 76881404Speter } 76981404Speter 77081404Speter /* get the username string */ 77181404Speter username = root->username ? root->username : getcaller(); 77281404Speter cvsroot_canonical = xmalloc ( strlen(username) 77381404Speter + strlen(hostname) + strlen(port_s) 77481404Speter + strlen(root->directory) + 12); 77581404Speter sprintf (cvsroot_canonical, ":pserver:%s@%s:%s%s", 77681404Speter username, hostname, port_s, root->directory); 77781404Speter 77881404Speter free (hostname); 77981404Speter return cvsroot_canonical; 78081404Speter} 78181404Speter#endif /* AUTH_CLIENT_SUPPORT */ 78281404Speter 78381404Speter 78481404Speter 78581404Speter/* allocate and return a cvsroot_t structure set up as if we're using the local 78681404Speter * repository DIR. */ 78781404Spetercvsroot_t * 78881404Speterlocal_cvsroot (dir) 789128266Speter const char *dir; 79025839Speter{ 79181404Speter cvsroot_t *newroot = new_cvsroot_t(); 79281404Speter 79381404Speter newroot->original = xstrdup(dir); 79481404Speter newroot->method = local_method; 79581404Speter newroot->directory = xstrdup(dir); 796128266Speter /* Here and parse_cvsroot() should be the only places this needs to be 797128266Speter * called on a CVSROOT now. cvsroot->original is saved for error messages 798128266Speter * and, otherwise, we want no trailing slashes. 799128266Speter */ 800128266Speter Sanitize_Repository_Name( newroot->directory ); 80181404Speter return newroot; 80225839Speter} 80325839Speter 80425839Speter 80581404Speter 80625839Speter#ifdef DEBUG 80754427Speter/* This is for testing the parsing function. Use 80825839Speter 80954427Speter gcc -I. -I.. -I../lib -DDEBUG root.c -o root 81054427Speter 81154427Speter to compile. */ 81254427Speter 81325839Speter#include <stdio.h> 81425839Speter 81525839Speterchar *program_name = "testing"; 816128266Speterchar *cvs_cmd_name = "parse_cvsroot"; /* XXX is this used??? */ 81725839Speter 81854427Speter/* Toy versions of various functions when debugging under unix. Yes, 81954427Speter these make various bad assumptions, but they're pretty easy to 82054427Speter debug when something goes wrong. */ 82154427Speter 82225839Spetervoid 82354427Spetererror_exit PROTO ((void)) 82454427Speter{ 82554427Speter exit (1); 82654427Speter} 82754427Speter 82854427Speterint 82954427Speterisabsolute (dir) 83054427Speter const char *dir; 83154427Speter{ 83254427Speter return (dir && (*dir == '/')); 83354427Speter} 83454427Speter 83554427Spetervoid 83625839Spetermain (argc, argv) 83725839Speter int argc; 83825839Speter char *argv[]; 83925839Speter{ 84025839Speter program_name = argv[0]; 84125839Speter 84225839Speter if (argc != 2) 84325839Speter { 84425839Speter fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 84525839Speter exit (2); 84625839Speter } 84725839Speter 84881404Speter if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) 84925839Speter { 85054427Speter fprintf (stderr, "%s: Parsing failed.\n", program_name); 85125839Speter exit (1); 85225839Speter } 85325839Speter printf ("CVSroot: %s\n", argv[1]); 85481404Speter printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); 85581404Speter printf ("current_parsed_root->username: %s\n", 85681404Speter current_parsed_root->username ? current_parsed_root->username : "NULL"); 85781404Speter printf ("current_parsed_root->hostname: %s\n", 85881404Speter current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); 85981404Speter printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); 86025839Speter 86125839Speter exit (0); 86225839Speter /* NOTREACHED */ 86325839Speter} 86425839Speter#endif 865