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