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