root.c revision 102840
117721Speter/* 217721Speter * Copyright (c) 1992, Mark D. Baushke 317721Speter * 417721Speter * You may distribute under the terms of the GNU General Public License as 532785Speter * specified in the README file that comes with the CVS source distribution. 617721Speter * 717721Speter * Name of Root 817721Speter * 917721Speter * Determine the path to the CVSROOT and set "Root" accordingly. 1017721Speter */ 1117721Speter 1217721Speter#include "cvs.h" 1325839Speter#include "getline.h" 1417721Speter 1581404Speter/* Printable names for things in the current_parsed_root->method enum variable. 1625839Speter Watch out if the enum is changed in cvs.h! */ 1725839Speter 1825839Speterchar *method_names[] = { 1981404Speter "undefined", "local", "server (rsh)", "pserver", "kserver", "gserver", "ext", "fork" 2025839Speter}; 2125839Speter 2225839Speter#ifndef DEBUG 2325839Speter 2417721Speterchar * 2532785SpeterName_Root (dir, update_dir) 2632785Speter char *dir; 2732785Speter char *update_dir; 2817721Speter{ 2917721Speter FILE *fpin; 3017721Speter char *ret, *xupdate_dir; 3125839Speter char *root = NULL; 3225839Speter size_t root_allocated = 0; 3325839Speter char *tmp; 3425839Speter char *cvsadm; 3517721Speter char *cp; 3617721Speter 3717721Speter if (update_dir && *update_dir) 3817721Speter xupdate_dir = update_dir; 3917721Speter else 4017721Speter xupdate_dir = "."; 4117721Speter 4217721Speter if (dir != NULL) 4317721Speter { 4425839Speter cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 4517721Speter (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 4625839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 4717721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 4817721Speter } 4917721Speter else 5017721Speter { 5125839Speter cvsadm = xstrdup (CVSADM); 5225839Speter tmp = xstrdup (CVSADM_ROOT); 5317721Speter } 5417721Speter 5517721Speter /* 5617721Speter * Do not bother looking for a readable file if there is no cvsadm 5717721Speter * directory present. 5817721Speter * 5917721Speter * It is possible that not all repositories will have a CVS/Root 6017721Speter * file. This is ok, but the user will need to specify -d 6117721Speter * /path/name or have the environment variable CVSROOT set in 6225839Speter * order to continue. */ 6317721Speter if ((!isdir (cvsadm)) || (!isreadable (tmp))) 6417721Speter { 6525839Speter ret = NULL; 6625839Speter goto out; 6717721Speter } 6817721Speter 6917721Speter /* 7017721Speter * The assumption here is that the CVS Root is always contained in the 7117721Speter * first line of the "Root" file. 7217721Speter */ 7317721Speter fpin = open_file (tmp, "r"); 7417721Speter 7525839Speter if (getline (&root, &root_allocated, fpin) < 0) 7617721Speter { 7725839Speter /* FIXME: should be checking for end of file separately; errno 7825839Speter is not set in that case. */ 7917721Speter error (0, 0, "in directory %s:", xupdate_dir); 8017721Speter error (0, errno, "cannot read %s", CVSADM_ROOT); 8117721Speter error (0, 0, "please correct this problem"); 8225839Speter ret = NULL; 8325839Speter goto out; 8417721Speter } 8517721Speter (void) fclose (fpin); 8617721Speter if ((cp = strrchr (root, '\n')) != NULL) 8717721Speter *cp = '\0'; /* strip the newline */ 8817721Speter 8917721Speter /* 9017721Speter * root now contains a candidate for CVSroot. It must be an 9125839Speter * absolute pathname or specify a remote server. 9217721Speter */ 9317721Speter 9425839Speter if ( 9517721Speter#ifdef CLIENT_SUPPORT 9625839Speter (strchr (root, ':') == NULL) && 9725839Speter#endif 9825839Speter ! isabsolute (root)) 9917721Speter { 10017721Speter error (0, 0, "in directory %s:", xupdate_dir); 10117721Speter error (0, 0, 10217721Speter "ignoring %s because it does not contain an absolute pathname.", 10317721Speter CVSADM_ROOT); 10425839Speter ret = NULL; 10525839Speter goto out; 10617721Speter } 10717721Speter 10817721Speter#ifdef CLIENT_SUPPORT 10917721Speter if ((strchr (root, ':') == NULL) && !isdir (root)) 11017721Speter#else /* ! CLIENT_SUPPORT */ 11117721Speter if (!isdir (root)) 11217721Speter#endif /* CLIENT_SUPPORT */ 11317721Speter { 11417721Speter error (0, 0, "in directory %s:", xupdate_dir); 11517721Speter error (0, 0, 11617721Speter "ignoring %s because it specifies a non-existent repository %s", 11717721Speter CVSADM_ROOT, root); 11825839Speter ret = NULL; 11925839Speter goto out; 12017721Speter } 12117721Speter 12217721Speter /* allocate space to return and fill it in */ 12325839Speter strip_trailing_slashes (root); 12417721Speter ret = xstrdup (root); 12525839Speter out: 12625839Speter free (cvsadm); 12725839Speter free (tmp); 12825839Speter if (root != NULL) 12925839Speter free (root); 13017721Speter return (ret); 13117721Speter} 13217721Speter 13317721Speter/* 13417721Speter * Write the CVS/Root file so that the environment variable CVSROOT 13517721Speter * and/or the -d option to cvs will be validated or not necessary for 13617721Speter * future work. 13717721Speter */ 13817721Spetervoid 13917721SpeterCreate_Root (dir, rootdir) 14032785Speter char *dir; 14132785Speter char *rootdir; 14217721Speter{ 14317721Speter FILE *fout; 14425839Speter char *tmp; 14517721Speter 14617721Speter if (noexec) 14717721Speter return; 14817721Speter 14917721Speter /* record the current cvs root */ 15017721Speter 15117721Speter if (rootdir != NULL) 15217721Speter { 15317721Speter if (dir != NULL) 15425839Speter { 15525839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 15617721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 15725839Speter } 15817721Speter else 15925839Speter tmp = xstrdup (CVSADM_ROOT); 16025839Speter 16117721Speter fout = open_file (tmp, "w+"); 16217721Speter if (fprintf (fout, "%s\n", rootdir) < 0) 16317721Speter error (1, errno, "write to %s failed", tmp); 16417721Speter if (fclose (fout) == EOF) 16517721Speter error (1, errno, "cannot close %s", tmp); 16625839Speter free (tmp); 16717721Speter } 16817721Speter} 16925839Speter 17025839Speter#endif /* ! DEBUG */ 17125839Speter 17225839Speter 17326801Speter/* The root_allow_* stuff maintains a list of legal CVSROOT 17426801Speter directories. Then we can check against them when a remote user 17526801Speter hands us a CVSROOT directory. */ 17626801Speter 17766525Speterstatic int root_allow_count; 17826801Speterstatic char **root_allow_vector; 17966525Speterstatic int root_allow_size; 18026801Speter 18126801Spetervoid 18226801Speterroot_allow_add (arg) 18326801Speter char *arg; 18426801Speter{ 18526801Speter char *p; 18626801Speter 18726801Speter if (root_allow_size <= root_allow_count) 18826801Speter { 18926801Speter if (root_allow_size == 0) 19026801Speter { 19126801Speter root_allow_size = 1; 19226801Speter root_allow_vector = 19326801Speter (char **) malloc (root_allow_size * sizeof (char *)); 19426801Speter } 19526801Speter else 19626801Speter { 19726801Speter root_allow_size *= 2; 19826801Speter root_allow_vector = 19926801Speter (char **) realloc (root_allow_vector, 20026801Speter root_allow_size * sizeof (char *)); 20126801Speter } 20226801Speter 20326801Speter if (root_allow_vector == NULL) 20426801Speter { 20526801Speter no_memory: 20626801Speter /* Strictly speaking, we're not supposed to output anything 20726801Speter now. But we're about to exit(), give it a try. */ 20826801Speter printf ("E Fatal server error, aborting.\n\ 20926801Spetererror ENOMEM Virtual memory exhausted.\n"); 21026801Speter 21126801Speter /* I'm doing this manually rather than via error_exit () 21226801Speter because I'm not sure whether we want to call server_cleanup. 21326801Speter Needs more investigation.... */ 21426801Speter 21526801Speter#ifdef SYSTEM_CLEANUP 21626801Speter /* Hook for OS-specific behavior, for example socket 21726801Speter subsystems on NT and OS2 or dealing with windows 21826801Speter and arguments on Mac. */ 21926801Speter SYSTEM_CLEANUP (); 22026801Speter#endif 22126801Speter 22226801Speter exit (EXIT_FAILURE); 22326801Speter } 22426801Speter } 22526801Speter p = malloc (strlen (arg) + 1); 22626801Speter if (p == NULL) 22726801Speter goto no_memory; 22826801Speter strcpy (p, arg); 22926801Speter root_allow_vector[root_allow_count++] = p; 23026801Speter} 23126801Speter 23226801Spetervoid 23326801Speterroot_allow_free () 23426801Speter{ 23526801Speter if (root_allow_vector != NULL) 23666525Speter free_names (&root_allow_count, root_allow_vector); 23726801Speter root_allow_size = 0; 23826801Speter} 23926801Speter 24026801Speterint 24126801Speterroot_allow_ok (arg) 24226801Speter char *arg; 24326801Speter{ 24466525Speter int i; 24532785Speter 24632785Speter if (root_allow_count == 0) 24732785Speter { 24832785Speter /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 24932785Speter or later without reading the documentation about 25032785Speter --allow-root. Printing an error here doesn't disclose any 25132785Speter particularly useful information to an attacker because a 25232785Speter CVS server configured in this way won't let *anyone* in. */ 25332785Speter 25432785Speter /* Note that we are called from a context where we can spit 25532785Speter back "error" rather than waiting for the next request which 25632785Speter expects responses. */ 25732785Speter printf ("\ 25832785Spetererror 0 Server configuration missing --allow-root in inetd.conf\n"); 25932785Speter error_exit (); 26032785Speter } 26132785Speter 26226801Speter for (i = 0; i < root_allow_count; ++i) 26326801Speter if (strcmp (root_allow_vector[i], arg) == 0) 26426801Speter return 1; 26526801Speter return 0; 26626801Speter} 26726801Speter 26881404Speter 26981404Speter 27054427Speter/* This global variable holds the global -d option. It is NULL if -d 27154427Speter was not used, which means that we must get the CVSroot information 27254427Speter from the CVSROOT environment variable or from a CVS/Root file. */ 27326801Speter 27454427Speterchar *CVSroot_cmdline; 27554427Speter 27681404Speter 27781404Speter 27825839Speter/* Parse a CVSROOT variable into its constituent parts -- method, 27925839Speter * username, hostname, directory. The prototypical CVSROOT variable 28025839Speter * looks like: 28125839Speter * 28225839Speter * :method:user@host:path 28325839Speter * 28425839Speter * Some methods may omit fields; local, for example, doesn't need user 28525839Speter * and host. 28625839Speter * 28781404Speter * Returns pointer to new cvsroot_t on success, NULL on failure. */ 28825839Speter 28981404Spetercvsroot_t *current_parsed_root = NULL; 29025839Speter 29181404Speter 29281404Speter 29381404Speter/* allocate and initialize a cvsroot_t 29481404Speter * 29581404Speter * We must initialize the strings to NULL so we know later what we should 29681404Speter * free 29781404Speter * 29881404Speter * Some of the other zeroes remain meaningful as, "never set, use default", 29981404Speter * or the like 30081404Speter */ 30181404Speterstatic cvsroot_t * 30281404Speternew_cvsroot_t () 30325839Speter{ 30481404Speter cvsroot_t *newroot; 30525839Speter 30681404Speter /* gotta store it somewhere */ 30781404Speter newroot = xmalloc(sizeof(cvsroot_t)); 30881404Speter 30981404Speter newroot->original = NULL; 31081404Speter newroot->method = null_method; 31181404Speter newroot->username = NULL; 31281404Speter newroot->password = NULL; 31381404Speter newroot->hostname = NULL; 31481404Speter newroot->port = 0; 31581404Speter newroot->directory = NULL; 31681404Speter#ifdef CLIENT_SUPPORT 31781404Speter newroot->isremote = 0; 31881404Speter#endif /* CLIENT_SUPPORT */ 31981404Speter 32081404Speter return newroot; 32181404Speter} 32281404Speter 32381404Speter 32481404Speter 32581404Speter/* Dispose of a cvsroot_t and its component parts */ 32681404Spetervoid 32781404Speterfree_cvsroot_t (root) 32881404Speter cvsroot_t *root; 32981404Speter{ 33081404Speter if (root->original != NULL) 33181404Speter free (root->original); 33281404Speter if (root->username != NULL) 33381404Speter free (root->username); 33481404Speter if (root->password != NULL) 33525839Speter { 33681404Speter /* I like to be paranoid */ 33781404Speter memset (root->password, 0, strlen (root->password)); 33881404Speter free (root->password); 33925839Speter } 34081404Speter if (root->hostname != NULL) 34181404Speter free (root->hostname); 34281404Speter if (root->directory != NULL) 34381404Speter free (root->directory); 34481404Speter free (root); 34581404Speter} 34625839Speter 34766525Speter 34825839Speter 34981404Speter/* 35081404Speter * parse a CVSROOT string to allocate and return a new cvsroot_t structure 35181404Speter */ 35281404Spetercvsroot_t * 35381404Speterparse_cvsroot (root_in) 35481404Speter char *root_in; 35581404Speter{ 35681404Speter cvsroot_t *newroot; /* the new root to be returned */ 35781404Speter char *cvsroot_save; /* what we allocated so we can dispose 35881404Speter * it when finished */ 35981404Speter char *firstslash; /* save where the path spec starts 36081404Speter * while we parse 36181404Speter * [[user][:password]@]host[:[port]] 36281404Speter */ 36381404Speter char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ 36481404Speter int check_hostname, no_port, no_password; 36581404Speter 36681404Speter /* allocate some space */ 36781404Speter newroot = new_cvsroot_t(); 36881404Speter 36981404Speter /* save the original string */ 37081404Speter newroot->original = xstrdup (root_in); 37181404Speter 37281404Speter /* and another copy we can munge while parsing */ 37381404Speter cvsroot_save = cvsroot_copy = xstrdup (root_in); 37481404Speter 37566525Speter if (*cvsroot_copy == ':') 37625839Speter { 37725839Speter char *method = ++cvsroot_copy; 37825839Speter 37925839Speter /* Access method specified, as in 38081404Speter * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", 38181404Speter * "cvs -d [:(ext|server):][[user]@]host[:]/path", 38254427Speter * "cvs -d :local:e:\path", 38354427Speter * "cvs -d :fork:/path". 38425839Speter * We need to get past that part of CVSroot before parsing the 38525839Speter * rest of it. 38625839Speter */ 38725839Speter 38825839Speter if (! (p = strchr (method, ':'))) 38925839Speter { 390102840Speter error (0, 0, "No closing `:' on method in CVSROOT."); 39166525Speter free (cvsroot_save); 39281404Speter goto error_exit; 39325839Speter } 39425839Speter *p = '\0'; 39525839Speter cvsroot_copy = ++p; 39625839Speter 39725839Speter /* Now we have an access method -- see if it's valid. */ 39825839Speter 39925839Speter if (strcmp (method, "local") == 0) 40081404Speter newroot->method = local_method; 40125839Speter else if (strcmp (method, "pserver") == 0) 40281404Speter newroot->method = pserver_method; 40325839Speter else if (strcmp (method, "kserver") == 0) 40481404Speter newroot->method = kserver_method; 40532785Speter else if (strcmp (method, "gserver") == 0) 40681404Speter newroot->method = gserver_method; 40725839Speter else if (strcmp (method, "server") == 0) 40881404Speter newroot->method = server_method; 40925839Speter else if (strcmp (method, "ext") == 0) 41081404Speter newroot->method = ext_method; 41154427Speter else if (strcmp (method, "fork") == 0) 41281404Speter newroot->method = fork_method; 41325839Speter else 41425839Speter { 415102840Speter error (0, 0, "Unknown method (`%s') in CVSROOT.", method); 41666525Speter free (cvsroot_save); 41781404Speter goto error_exit; 41825839Speter } 41925839Speter } 42025839Speter else 42125839Speter { 42225839Speter /* If the method isn't specified, assume 423102840Speter SERVER_METHOD/EXT_METHOD if the string looks like a relative path or 42425839Speter LOCAL_METHOD otherwise. */ 42525839Speter 42681404Speter newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) 42781404Speter/*#ifdef RSH_NOT_TRANSPARENT 42825839Speter ? server_method 42981404Speter#else*/ 43025839Speter ? ext_method 43181404Speter/*#endif*/ 43225839Speter : local_method); 43325839Speter } 43425839Speter 43581404Speter#ifdef CLIENT_SUPPORT 43681404Speter newroot->isremote = (newroot->method != local_method); 43781404Speter#endif /* CLIENT_SUPPORT */ 43825839Speter 43925839Speter 44081404Speter if ((newroot->method != local_method) 44181404Speter && (newroot->method != fork_method)) 44225839Speter { 44381404Speter /* split the string into [[user][:password]@]host[:[port]] & /path 44481404Speter * 44581404Speter * this will allow some characters such as '@' & ':' to remain unquoted 44681404Speter * in the path portion of the spec 44781404Speter */ 44881404Speter if ((p = strchr (cvsroot_copy, '/')) == NULL) 44981404Speter { 450102840Speter error (0, 0, "CVSROOT requires a path spec:"); 45181404Speter error (0, 0, ":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); 45281404Speter error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); 45381404Speter free (cvsroot_save); 45481404Speter goto error_exit; 45581404Speter } 45681404Speter firstslash = p; /* == NULL if '/' not in string */ 45781404Speter *p = '\0'; 45825839Speter 45981404Speter /* Check to see if there is a username[:password] in the string. */ 46066525Speter if ((p = strchr (cvsroot_copy, '@')) != NULL) 46125839Speter { 46225839Speter *p = '\0'; 46381404Speter /* check for a password */ 46481404Speter if ((q = strchr (cvsroot_copy, ':')) != NULL) 46581404Speter { 46681404Speter *q = '\0'; 46781404Speter newroot->password = xstrdup (++q); 46881404Speter /* Don't check for *newroot->password == '\0' since 46981404Speter * a user could conceivably wish to specify a blank password 47081404Speter * (newroot->password == NULL means to use the 47181404Speter * password from .cvspass) 47281404Speter */ 47381404Speter } 47481404Speter 47581404Speter /* copy the username */ 47681404Speter if (*cvsroot_copy != '\0') 47781404Speter /* a blank username is impossible, so leave it NULL in that 47881404Speter * case so we know to use the default username 47981404Speter */ 48081404Speter newroot->username = xstrdup (cvsroot_copy); 48181404Speter 48225839Speter cvsroot_copy = ++p; 48325839Speter } 48425839Speter 48581404Speter /* now deal with host[:[port]] */ 48681404Speter 48781404Speter /* the port */ 48866525Speter if ((p = strchr (cvsroot_copy, ':')) != NULL) 48925839Speter { 49081404Speter *p++ = '\0'; 49181404Speter if (strlen(p)) 49281404Speter { 49381404Speter q = p; 49481404Speter if (*q == '-') q++; 49581404Speter while (*q) 49681404Speter { 49781404Speter if (!isdigit(*q++)) 49881404Speter { 499102840Speter error (0, 0, "CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 50081404Speter p); 501102840Speter error (0, 0, "Perhaps you entered a relative pathname?"); 50281404Speter free (cvsroot_save); 50381404Speter goto error_exit; 50481404Speter } 50581404Speter } 50681404Speter if ((newroot->port = atoi (p)) <= 0) 50781404Speter { 508102840Speter error (0, 0, "CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 50981404Speter p); 510102840Speter error (0, 0, "Perhaps you entered a relative pathname?"); 51181404Speter free (cvsroot_save); 51281404Speter goto error_exit; 51381404Speter } 51481404Speter } 51525839Speter } 51681404Speter 51781404Speter /* copy host */ 51881404Speter if (*cvsroot_copy != '\0') 51981404Speter /* blank hostnames are invalid, but for now leave the field NULL 52081404Speter * and catch the error during the sanity checks later 52181404Speter */ 52281404Speter newroot->hostname = xstrdup (cvsroot_copy); 52381404Speter 52481404Speter /* restore the '/' */ 52581404Speter cvsroot_copy = firstslash; 52681404Speter *cvsroot_copy = '/'; 52725839Speter } 52825839Speter 52981404Speter /* parse the path for all methods */ 53081404Speter newroot->directory = xstrdup(cvsroot_copy); 53166525Speter free (cvsroot_save); 53225839Speter 53381404Speter /* 53481404Speter * Do various sanity checks. 53581404Speter */ 53681404Speter 53725839Speter#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 53881404Speter if (newroot->method != local_method) 53925839Speter { 540102840Speter error (0, 0, "CVSROOT is set for a remote access method but your"); 541102840Speter error (0, 0, "CVS executable doesn't support it."); 54281404Speter goto error_exit; 54325839Speter } 54425839Speter#endif 54525839Speter 54681404Speter#if ! defined (SERVER_SUPPORT) && ! defined (DEBUG) 54781404Speter if (newroot->method == fork_method) 54825839Speter { 549102840Speter error (0, 0, "CVSROOT is set to use the :fork: access method but your"); 550102840Speter error (0, 0, "CVS executable doesn't support it."); 55181404Speter goto error_exit; 55281404Speter } 55381404Speter#endif 55481404Speter 55581404Speter if (newroot->username && ! newroot->hostname) 55681404Speter { 557102840Speter error (0, 0, "Missing hostname in CVSROOT."); 55881404Speter goto error_exit; 55925839Speter } 56025839Speter 56132785Speter check_hostname = 0; 56281404Speter no_password = 0; 56381404Speter no_port = 0; 56481404Speter switch (newroot->method) 56525839Speter { 56625839Speter case local_method: 56781404Speter if (newroot->username || newroot->hostname) 56825839Speter { 569102840Speter error (0, 0, "Can't specify hostname and username in CVSROOT"); 570102840Speter error (0, 0, "when using local access method."); 57181404Speter goto error_exit; 57225839Speter } 57325839Speter /* cvs.texinfo has always told people that CVSROOT must be an 57425839Speter absolute pathname. Furthermore, attempts to use a relative 57525839Speter pathname produced various errors (I couldn't get it to work), 57625839Speter so there would seem to be little risk in making this a fatal 57725839Speter error. */ 57881404Speter if (!isabsolute (newroot->directory)) 57981404Speter { 580102840Speter error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 58181404Speter newroot->directory); 582102840Speter error (0, 0, "when using local access method."); 58381404Speter goto error_exit; 58481404Speter } 58581404Speter no_port = 1; 58681404Speter no_password = 1; 58725839Speter break; 58866525Speter case fork_method: 58966525Speter /* We want :fork: to behave the same as other remote access 59066525Speter methods. Therefore, don't check to see that the repository 59166525Speter name is absolute -- let the server do it. */ 59281404Speter if (newroot->username || newroot->hostname) 59366525Speter { 594102840Speter error (0, 0, "Can't specify hostname and username in CVSROOT"); 595102840Speter error (0, 0, "when using fork access method."); 59681404Speter goto error_exit; 59766525Speter } 59881404Speter if (!isabsolute (newroot->directory)) 59981404Speter { 600102840Speter error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 60181404Speter newroot->directory); 602102840Speter error (0, 0, "when using fork access method."); 60381404Speter goto error_exit; 60481404Speter } 60581404Speter no_port = 1; 60681404Speter no_password = 1; 60766525Speter break; 60825839Speter case kserver_method: 60925839Speter#ifndef HAVE_KERBEROS 610102840Speter error (0, 0, "CVSROOT is set for a kerberos access method but your"); 611102840Speter error (0, 0, "CVS executable doesn't support it."); 61281404Speter goto error_exit; 61354427Speter#else 61432785Speter check_hostname = 1; 61532785Speter break; 61654427Speter#endif 61732785Speter case gserver_method: 61832785Speter#ifndef HAVE_GSSAPI 619102840Speter error (0, 0, "CVSROOT is set for a GSSAPI access method but your"); 620102840Speter error (0, 0, "CVS executable doesn't support it."); 62181404Speter goto error_exit; 62254427Speter#else 62332785Speter check_hostname = 1; 62432785Speter break; 62554427Speter#endif 62625839Speter case server_method: 62725839Speter case ext_method: 62881404Speter no_port = 1; 62981404Speter no_password = 1; 63081404Speter check_hostname = 1; 63181404Speter break; 63225839Speter case pserver_method: 63332785Speter check_hostname = 1; 63432785Speter break; 635102840Speter default: 636102840Speter error (1, 0, "Invalid method found in parse_cvsroot"); 63732785Speter } 63832785Speter 63981404Speter if (no_password && newroot->password) 64032785Speter { 64181404Speter error (0, 0, "CVSROOT password specification is only valid for"); 64281404Speter error (0, 0, "pserver connection method."); 64381404Speter goto error_exit; 64481404Speter } 64581404Speter 64681404Speter if (check_hostname && !newroot->hostname) 64781404Speter { 648102840Speter error (0, 0, "Didn't specify hostname in CVSROOT."); 64981404Speter goto error_exit; 65081404Speter } 65181404Speter 65281404Speter if (no_port && newroot->port) 65325839Speter { 65481404Speter error (0, 0, "CVSROOT port specification is only valid for gserver, kserver,"); 65581404Speter error (0, 0, "and pserver connection methods."); 65681404Speter goto error_exit; 65725839Speter } 65825839Speter 65981404Speter if (*newroot->directory == '\0') 66025839Speter { 661102840Speter error (0, 0, "Missing directory in CVSROOT."); 66281404Speter goto error_exit; 66325839Speter } 66425839Speter 66525839Speter /* Hooray! We finally parsed it! */ 66681404Speter return newroot; 66781404Speter 66881404Spetererror_exit: 66981404Speter free_cvsroot_t (newroot); 67081404Speter return NULL; 67125839Speter} 67225839Speter 67325839Speter 67425839Speter 67581404Speter#ifdef AUTH_CLIENT_SUPPORT 67681404Speter/* Use root->username, root->hostname, root->port, and root->directory 67781404Speter * to create a normalized CVSROOT fit for the .cvspass file 67881404Speter * 67981404Speter * username defaults to the result of getcaller() 68081404Speter * port defaults to the result of get_cvs_port_number() 68181404Speter * 68281404Speter * FIXME - we could cache the canonicalized version of a root inside the 68381404Speter * cvsroot_t, but we'd have to un'const the input here and stop expecting the 68481404Speter * caller to be responsible for our return value 68581404Speter */ 68681404Speterchar * 68781404Speternormalize_cvsroot (root) 68881404Speter const cvsroot_t *root; 68981404Speter{ 69081404Speter char *cvsroot_canonical; 69181404Speter char *p, *hostname, *username; 69281404Speter char port_s[64]; 69381404Speter 69481404Speter /* get the appropriate port string */ 69581404Speter sprintf (port_s, "%d", get_cvs_port_number (root)); 69681404Speter 69781404Speter /* use a lower case hostname since we know hostnames are case insensitive */ 69881404Speter /* Some logic says we should be tacking our domain name on too if it isn't 69981404Speter * there already, but for now this works. Reverse->Forward lookups are 70081404Speter * almost certainly too much since that would make CVS immune to some of 70181404Speter * the DNS trickery that makes life easier for sysadmins when they want to 70281404Speter * move a repository or the like 70381404Speter */ 70481404Speter p = hostname = xstrdup(root->hostname); 70581404Speter while (*p) 70681404Speter { 70781404Speter *p = tolower(*p); 70881404Speter p++; 70981404Speter } 71081404Speter 71181404Speter /* get the username string */ 71281404Speter username = root->username ? root->username : getcaller(); 71381404Speter cvsroot_canonical = xmalloc ( strlen(username) 71481404Speter + strlen(hostname) + strlen(port_s) 71581404Speter + strlen(root->directory) + 12); 71681404Speter sprintf (cvsroot_canonical, ":pserver:%s@%s:%s%s", 71781404Speter username, hostname, port_s, root->directory); 71881404Speter 71981404Speter free (hostname); 72081404Speter return cvsroot_canonical; 72181404Speter} 72281404Speter#endif /* AUTH_CLIENT_SUPPORT */ 72381404Speter 72481404Speter 72581404Speter 72681404Speter/* allocate and return a cvsroot_t structure set up as if we're using the local 72781404Speter * repository DIR. */ 72881404Spetercvsroot_t * 72981404Speterlocal_cvsroot (dir) 73025839Speter char *dir; 73125839Speter{ 73281404Speter cvsroot_t *newroot = new_cvsroot_t(); 73381404Speter 73481404Speter newroot->original = xstrdup(dir); 73581404Speter newroot->method = local_method; 73681404Speter newroot->directory = xstrdup(dir); 73781404Speter 73881404Speter return newroot; 73925839Speter} 74025839Speter 74125839Speter 74281404Speter 74325839Speter#ifdef DEBUG 74454427Speter/* This is for testing the parsing function. Use 74525839Speter 74654427Speter gcc -I. -I.. -I../lib -DDEBUG root.c -o root 74754427Speter 74854427Speter to compile. */ 74954427Speter 75025839Speter#include <stdio.h> 75125839Speter 75225839Speterchar *program_name = "testing"; 75325839Speterchar *command_name = "parse_cvsroot"; /* XXX is this used??? */ 75425839Speter 75554427Speter/* Toy versions of various functions when debugging under unix. Yes, 75654427Speter these make various bad assumptions, but they're pretty easy to 75754427Speter debug when something goes wrong. */ 75854427Speter 75925839Spetervoid 76054427Spetererror_exit PROTO ((void)) 76154427Speter{ 76254427Speter exit (1); 76354427Speter} 76454427Speter 76554427Speterint 76654427Speterisabsolute (dir) 76754427Speter const char *dir; 76854427Speter{ 76954427Speter return (dir && (*dir == '/')); 77054427Speter} 77154427Speter 77254427Spetervoid 77325839Spetermain (argc, argv) 77425839Speter int argc; 77525839Speter char *argv[]; 77625839Speter{ 77725839Speter program_name = argv[0]; 77825839Speter 77925839Speter if (argc != 2) 78025839Speter { 78125839Speter fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 78225839Speter exit (2); 78325839Speter } 78425839Speter 78581404Speter if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) 78625839Speter { 78754427Speter fprintf (stderr, "%s: Parsing failed.\n", program_name); 78825839Speter exit (1); 78925839Speter } 79025839Speter printf ("CVSroot: %s\n", argv[1]); 79181404Speter printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); 79281404Speter printf ("current_parsed_root->username: %s\n", 79381404Speter current_parsed_root->username ? current_parsed_root->username : "NULL"); 79481404Speter printf ("current_parsed_root->hostname: %s\n", 79581404Speter current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); 79681404Speter printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); 79725839Speter 79825839Speter exit (0); 79925839Speter /* NOTREACHED */ 80025839Speter} 80125839Speter#endif 802