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