xref: /openbsd-src/usr.bin/cvs/root.c (revision 4dcde51379711c241d4d9b93f9d4e9eae2d51f3d)
1*4dcde513Sjoris /*	$OpenBSD: root.c,v 1.49 2017/06/01 08:08:24 joris Exp $	*/
26c121f58Sjfb /*
36c121f58Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
46c121f58Sjfb  * All rights reserved.
56c121f58Sjfb  *
66c121f58Sjfb  * Redistribution and use in source and binary forms, with or without
76c121f58Sjfb  * modification, are permitted provided that the following conditions
86c121f58Sjfb  * are met:
96c121f58Sjfb  *
106c121f58Sjfb  * 1. Redistributions of source code must retain the above copyright
116c121f58Sjfb  *    notice, this list of conditions and the following disclaimer.
126c121f58Sjfb  * 2. The name of the author may not be used to endorse or promote products
136c121f58Sjfb  *    derived from this software without specific prior written permission.
146c121f58Sjfb  *
156c121f58Sjfb  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
166c121f58Sjfb  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
176c121f58Sjfb  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
186c121f58Sjfb  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
196c121f58Sjfb  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
206c121f58Sjfb  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
216c121f58Sjfb  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
226c121f58Sjfb  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
236c121f58Sjfb  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
246c121f58Sjfb  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256c121f58Sjfb  */
266c121f58Sjfb 
271f8531bdSotto #include <errno.h>
281f8531bdSotto #include <stdlib.h>
291f8531bdSotto #include <string.h>
306c121f58Sjfb 
316c121f58Sjfb #include "cvs.h"
326c121f58Sjfb 
336c121f58Sjfb extern char *cvs_rootstr;
346c121f58Sjfb 
356c121f58Sjfb /* keep these ordered with the defines */
366c121f58Sjfb const char *cvs_methods[] = {
376c121f58Sjfb 	"",
386c121f58Sjfb 	"local",
396c121f58Sjfb 	"ssh",
406c121f58Sjfb 	"pserver",
416c121f58Sjfb 	"kserver",
426c121f58Sjfb 	"gserver",
436c121f58Sjfb 	"ext",
446c121f58Sjfb 	"fork",
456c121f58Sjfb };
466c121f58Sjfb 
476c121f58Sjfb #define CVS_NBMETHODS	(sizeof(cvs_methods)/sizeof(cvs_methods[0]))
486c121f58Sjfb 
497f530f5cSjfb /*
506c121f58Sjfb  * cvsroot_parse()
516c121f58Sjfb  *
526c121f58Sjfb  * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
536c121f58Sjfb  * environment variable) and store the fields in a dynamically
546c121f58Sjfb  * allocated cvs_root structure.  The format of the string is as follows:
55023558c8Sxsa  *	[:method:][[user[:pass]@]host[:port]:]path
566c121f58Sjfb  * Returns a pointer to the allocated information on success, or NULL
576c121f58Sjfb  * on failure.
586c121f58Sjfb  */
59babed5a8Sjoris static struct cvsroot *
cvsroot_parse(const char * str)606c121f58Sjfb cvsroot_parse(const char *str)
616c121f58Sjfb {
626c121f58Sjfb 	u_int i;
636c121f58Sjfb 	char *cp, *sp, *pp;
64ec4a7e96Sxsa 	const char *errstr;
65babed5a8Sjoris 	static struct cvsroot *root = NULL;
666c121f58Sjfb 
67babed5a8Sjoris 	if (root != NULL)
68dd10a000Sjoris 		return (root);
697f530f5cSjfb 
703b4c5c25Sray 	root = xcalloc(1, sizeof(*root));
716c121f58Sjfb 	root->cr_method = CVS_METHOD_NONE;
720450b43bSjoris 	root->cr_str = xstrdup(str);
730450b43bSjoris 	root->cr_buf = xstrdup(str);
746c121f58Sjfb 
756c121f58Sjfb 	sp = root->cr_buf;
766c121f58Sjfb 	cp = root->cr_buf;
776c121f58Sjfb 	if (*sp == ':') {
786c121f58Sjfb 		sp++;
79296ae42bSxsa 		if ((cp = strchr(sp, ':')) == NULL)
80296ae42bSxsa 			fatal("failed to parse CVSROOT: unterminated method");
81296ae42bSxsa 
826c121f58Sjfb 		*(cp++) = '\0';
836c121f58Sjfb 
846c121f58Sjfb 		for (i = 0; i < CVS_NBMETHODS; i++) {
856c121f58Sjfb 			if (strcmp(sp, cvs_methods[i]) == 0) {
866c121f58Sjfb 				root->cr_method = i;
876c121f58Sjfb 				break;
886c121f58Sjfb 			}
896c121f58Sjfb 		}
90296ae42bSxsa 		if (i == CVS_NBMETHODS)
91296ae42bSxsa 			fatal("cvsroot_parse: unknown method `%s'", sp);
926c121f58Sjfb 	}
936c121f58Sjfb 
946c121f58Sjfb 	/* find the start of the actual path */
95296ae42bSxsa 	if ((sp = strchr(cp, '/')) == NULL)
96296ae42bSxsa 		fatal("no path specification in CVSROOT");
976c121f58Sjfb 
986c121f58Sjfb 	root->cr_dir = sp;
993ad3fb45Sjoris 	STRIP_SLASH(root->cr_dir);
1006c121f58Sjfb 	if (sp == cp) {
1016c121f58Sjfb 		if (root->cr_method == CVS_METHOD_NONE)
1026c121f58Sjfb 			root->cr_method = CVS_METHOD_LOCAL;
1036c121f58Sjfb 		/* stop here, it's just a path */
1046c121f58Sjfb 		return (root);
1056c121f58Sjfb 	}
1066c121f58Sjfb 
107296ae42bSxsa 	if (*(sp - 1) != ':')
108296ae42bSxsa 		fatal("missing host/path delimiter in CVSROOT");
109296ae42bSxsa 
1106c121f58Sjfb 	*(sp - 1) = '\0';
1116c121f58Sjfb 
1126c121f58Sjfb 	/*
1136c121f58Sjfb 	 * looks like we have more than just a directory path, so
1146c121f58Sjfb 	 * attempt to split it into user and host parts
1156c121f58Sjfb 	 */
1166c121f58Sjfb 	sp = strchr(cp, '@');
1176c121f58Sjfb 	if (sp != NULL) {
1186c121f58Sjfb 		*(sp++) = '\0';
1196c121f58Sjfb 
1206c121f58Sjfb 		/* password ? */
1216c121f58Sjfb 		pp = strchr(cp, ':');
1226c121f58Sjfb 		if (pp != NULL) {
1236c121f58Sjfb 			*(pp++) = '\0';
1246c121f58Sjfb 			root->cr_pass = pp;
1256c121f58Sjfb 		}
1266c121f58Sjfb 
1276c121f58Sjfb 		root->cr_user = cp;
1283917c9bfSderaadt 	} else
1299c4cd84fSjfb 		sp = cp;
1306c121f58Sjfb 
1316c121f58Sjfb 	pp = strchr(sp, ':');
1326c121f58Sjfb 	if (pp != NULL) {
1336c121f58Sjfb 		*(pp++) = '\0';
13447c33804Sjoris 		root->cr_port = strtonum(pp, 1, 65535, &errstr);
135ec4a7e96Sxsa 		if (errstr != NULL)
136ec4a7e96Sxsa 			fatal("port specification in CVSROOT is %s", errstr);
1376c121f58Sjfb 
1386c121f58Sjfb 	}
1396c121f58Sjfb 
1406c121f58Sjfb 	root->cr_host = sp;
1416c121f58Sjfb 
1426c121f58Sjfb 	if (root->cr_method == CVS_METHOD_NONE) {
1436c121f58Sjfb 		/* no method found from start of CVSROOT, guess */
1446c121f58Sjfb 		if (root->cr_host != NULL)
1456c121f58Sjfb 			root->cr_method = CVS_METHOD_SERVER;
1466c121f58Sjfb 		else
1476c121f58Sjfb 			root->cr_method = CVS_METHOD_LOCAL;
1486c121f58Sjfb 	}
1496c121f58Sjfb 
1506c121f58Sjfb 	return (root);
1516c121f58Sjfb }
1526c121f58Sjfb 
153f367048cSjoris /*
1546c121f58Sjfb  * cvsroot_get()
1556c121f58Sjfb  *
1566c121f58Sjfb  * Get the CVSROOT information for a specific directory <dir>.  The
1576c121f58Sjfb  * value is taken from one of 3 possible sources (in order of precedence):
1586c121f58Sjfb  *
1596c121f58Sjfb  * 1) the `-d' command-line option
1606c121f58Sjfb  * 2) the CVS/Root file found in checked-out trees
1616c121f58Sjfb  * 3) the CVSROOT environment variable
1626c121f58Sjfb  */
1636c121f58Sjfb struct cvsroot *
cvsroot_get(const char * dir)1646c121f58Sjfb cvsroot_get(const char *dir)
1656c121f58Sjfb {
166b9fc9a72Sderaadt 	char rootpath[PATH_MAX], *rootstr, line[128];
1676c121f58Sjfb 	FILE *fp;
1686c121f58Sjfb 
1696c121f58Sjfb 	if (cvs_rootstr != NULL)
1706c121f58Sjfb 		return cvsroot_parse(cvs_rootstr);
1716c121f58Sjfb 
172b0d19690Stobias 	if (cvs_server_active == 1)
173b0d19690Stobias 		return cvsroot_parse(dir);
174b0d19690Stobias 
1758fdeb7f5Snicm 	if (cvs_cmdop == CVS_OP_IMPORT) {
1768fdeb7f5Snicm 		if ((rootstr = getenv("CVSROOT")) != NULL)
1778fdeb7f5Snicm 			return (cvsroot_parse(rootstr));
1788fdeb7f5Snicm 		return (NULL);
1798fdeb7f5Snicm 	}
1802048ee2eStobias 
181b9fc9a72Sderaadt 	(void)xsnprintf(rootpath, PATH_MAX, "%s/%s", dir, CVS_PATH_ROOTSPEC);
18227b85f85Sxsa 
183296ae42bSxsa 	if ((fp = fopen(rootpath, "r")) == NULL) {
1846c121f58Sjfb 		if (errno == ENOENT) {
1856c121f58Sjfb 			/* try env as a last resort */
1866c121f58Sjfb 			if ((rootstr = getenv("CVSROOT")) != NULL)
1876c121f58Sjfb 				return cvsroot_parse(rootstr);
1886c121f58Sjfb 			else
1893ad3fb45Sjoris 				return (NULL);
1903917c9bfSderaadt 		} else {
191296ae42bSxsa 			fatal("cvsroot_get: fopen: `%s': %s",
192296ae42bSxsa 			    CVS_PATH_ROOTSPEC, strerror(errno));
1936c121f58Sjfb 		}
1946c121f58Sjfb 	}
1956c121f58Sjfb 
196296ae42bSxsa 	if (fgets(line, (int)sizeof(line), fp) == NULL)
197296ae42bSxsa 		fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC);
198296ae42bSxsa 
1996c121f58Sjfb 	(void)fclose(fp);
2006c121f58Sjfb 
201b625fa02Sgilles 	line[strcspn(line, "\n")] = '\0';
202b625fa02Sgilles 	if (line[0] == '\0')
2033ad3fb45Sjoris 		cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC);
204a6f992bdSjfb 
205a6f992bdSjfb 	return cvsroot_parse(line);
2066c121f58Sjfb }
207*4dcde513Sjoris 
208*4dcde513Sjoris int
cvsroot_is_local(void)209*4dcde513Sjoris cvsroot_is_local(void)
210*4dcde513Sjoris {
211*4dcde513Sjoris 	if (current_cvsroot == NULL)
212*4dcde513Sjoris 		fatal("cvsroot_is_local: no CVSROOT");
213*4dcde513Sjoris 
214*4dcde513Sjoris 	return (current_cvsroot->cr_method == CVS_METHOD_LOCAL);
215*4dcde513Sjoris }
216*4dcde513Sjoris 
217*4dcde513Sjoris int
cvsroot_is_remote(void)218*4dcde513Sjoris cvsroot_is_remote(void)
219*4dcde513Sjoris {
220*4dcde513Sjoris 	if (current_cvsroot == NULL)
221*4dcde513Sjoris 		fatal("cvsroot_is_remote: no CVSROOT");
222*4dcde513Sjoris 
223*4dcde513Sjoris 	return (current_cvsroot->cr_method != CVS_METHOD_LOCAL);
224*4dcde513Sjoris }
225