xref: /openbsd/usr.bin/cvs/root.c (revision 264ca280)
1 /*	$OpenBSD: root.c,v 1.48 2015/01/16 06:40:07 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "cvs.h"
32 
33 extern char *cvs_rootstr;
34 
35 /* keep these ordered with the defines */
36 const char *cvs_methods[] = {
37 	"",
38 	"local",
39 	"ssh",
40 	"pserver",
41 	"kserver",
42 	"gserver",
43 	"ext",
44 	"fork",
45 };
46 
47 #define CVS_NBMETHODS	(sizeof(cvs_methods)/sizeof(cvs_methods[0]))
48 
49 /*
50  * cvsroot_parse()
51  *
52  * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
53  * environment variable) and store the fields in a dynamically
54  * allocated cvs_root structure.  The format of the string is as follows:
55  *	[:method:][[user[:pass]@]host[:port]:]path
56  * Returns a pointer to the allocated information on success, or NULL
57  * on failure.
58  */
59 static struct cvsroot *
60 cvsroot_parse(const char *str)
61 {
62 	u_int i;
63 	char *cp, *sp, *pp;
64 	const char *errstr;
65 	static struct cvsroot *root = NULL;
66 
67 	if (root != NULL)
68 		return (root);
69 
70 	root = xcalloc(1, sizeof(*root));
71 	root->cr_method = CVS_METHOD_NONE;
72 	root->cr_str = xstrdup(str);
73 	root->cr_buf = xstrdup(str);
74 
75 	sp = root->cr_buf;
76 	cp = root->cr_buf;
77 	if (*sp == ':') {
78 		sp++;
79 		if ((cp = strchr(sp, ':')) == NULL)
80 			fatal("failed to parse CVSROOT: unterminated method");
81 
82 		*(cp++) = '\0';
83 
84 		for (i = 0; i < CVS_NBMETHODS; i++) {
85 			if (strcmp(sp, cvs_methods[i]) == 0) {
86 				root->cr_method = i;
87 				break;
88 			}
89 		}
90 		if (i == CVS_NBMETHODS)
91 			fatal("cvsroot_parse: unknown method `%s'", sp);
92 	}
93 
94 	/* find the start of the actual path */
95 	if ((sp = strchr(cp, '/')) == NULL)
96 		fatal("no path specification in CVSROOT");
97 
98 	root->cr_dir = sp;
99 	STRIP_SLASH(root->cr_dir);
100 	if (sp == cp) {
101 		if (root->cr_method == CVS_METHOD_NONE)
102 			root->cr_method = CVS_METHOD_LOCAL;
103 		/* stop here, it's just a path */
104 		return (root);
105 	}
106 
107 	if (*(sp - 1) != ':')
108 		fatal("missing host/path delimiter in CVSROOT");
109 
110 	*(sp - 1) = '\0';
111 
112 	/*
113 	 * looks like we have more than just a directory path, so
114 	 * attempt to split it into user and host parts
115 	 */
116 	sp = strchr(cp, '@');
117 	if (sp != NULL) {
118 		*(sp++) = '\0';
119 
120 		/* password ? */
121 		pp = strchr(cp, ':');
122 		if (pp != NULL) {
123 			*(pp++) = '\0';
124 			root->cr_pass = pp;
125 		}
126 
127 		root->cr_user = cp;
128 	} else
129 		sp = cp;
130 
131 	pp = strchr(sp, ':');
132 	if (pp != NULL) {
133 		*(pp++) = '\0';
134 		root->cr_port = strtonum(pp, 1, 65535, &errstr);
135 		if (errstr != NULL)
136 			fatal("port specification in CVSROOT is %s", errstr);
137 
138 	}
139 
140 	root->cr_host = sp;
141 
142 	if (root->cr_method == CVS_METHOD_NONE) {
143 		/* no method found from start of CVSROOT, guess */
144 		if (root->cr_host != NULL)
145 			root->cr_method = CVS_METHOD_SERVER;
146 		else
147 			root->cr_method = CVS_METHOD_LOCAL;
148 	}
149 
150 	return (root);
151 }
152 
153 /*
154  * cvsroot_get()
155  *
156  * Get the CVSROOT information for a specific directory <dir>.  The
157  * value is taken from one of 3 possible sources (in order of precedence):
158  *
159  * 1) the `-d' command-line option
160  * 2) the CVS/Root file found in checked-out trees
161  * 3) the CVSROOT environment variable
162  */
163 struct cvsroot *
164 cvsroot_get(const char *dir)
165 {
166 	char rootpath[PATH_MAX], *rootstr, line[128];
167 	FILE *fp;
168 
169 	if (cvs_rootstr != NULL)
170 		return cvsroot_parse(cvs_rootstr);
171 
172 	if (cvs_server_active == 1)
173 		return cvsroot_parse(dir);
174 
175 	if (cvs_cmdop == CVS_OP_IMPORT) {
176 		if ((rootstr = getenv("CVSROOT")) != NULL)
177 			return (cvsroot_parse(rootstr));
178 		return (NULL);
179 	}
180 
181 	(void)xsnprintf(rootpath, PATH_MAX, "%s/%s", dir, CVS_PATH_ROOTSPEC);
182 
183 	if ((fp = fopen(rootpath, "r")) == NULL) {
184 		if (errno == ENOENT) {
185 			/* try env as a last resort */
186 			if ((rootstr = getenv("CVSROOT")) != NULL)
187 				return cvsroot_parse(rootstr);
188 			else
189 				return (NULL);
190 		} else {
191 			fatal("cvsroot_get: fopen: `%s': %s",
192 			    CVS_PATH_ROOTSPEC, strerror(errno));
193 		}
194 	}
195 
196 	if (fgets(line, (int)sizeof(line), fp) == NULL)
197 		fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC);
198 
199 	(void)fclose(fp);
200 
201 	line[strcspn(line, "\n")] = '\0';
202 	if (line[0] == '\0')
203 		cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC);
204 
205 	return cvsroot_parse(line);
206 }
207