1 /* $OpenBSD: root.c,v 1.45 2008/06/20 23:00:13 tobias 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 CVS_RSTVR(root); 73 74 root->cr_str = xstrdup(str); 75 root->cr_buf = xstrdup(str); 76 77 sp = root->cr_buf; 78 cp = root->cr_buf; 79 if (*sp == ':') { 80 sp++; 81 if ((cp = strchr(sp, ':')) == NULL) 82 fatal("failed to parse CVSROOT: unterminated method"); 83 84 *(cp++) = '\0'; 85 86 for (i = 0; i < CVS_NBMETHODS; i++) { 87 if (strcmp(sp, cvs_methods[i]) == 0) { 88 root->cr_method = i; 89 break; 90 } 91 } 92 if (i == CVS_NBMETHODS) 93 fatal("cvsroot_parse: unknown method `%s'", sp); 94 } 95 96 /* find the start of the actual path */ 97 if ((sp = strchr(cp, '/')) == NULL) 98 fatal("no path specification in CVSROOT"); 99 100 root->cr_dir = sp; 101 STRIP_SLASH(root->cr_dir); 102 if (sp == cp) { 103 if (root->cr_method == CVS_METHOD_NONE) 104 root->cr_method = CVS_METHOD_LOCAL; 105 /* stop here, it's just a path */ 106 return (root); 107 } 108 109 if (*(sp - 1) != ':') 110 fatal("missing host/path delimiter in CVSROOT"); 111 112 *(sp - 1) = '\0'; 113 114 /* 115 * looks like we have more than just a directory path, so 116 * attempt to split it into user and host parts 117 */ 118 sp = strchr(cp, '@'); 119 if (sp != NULL) { 120 *(sp++) = '\0'; 121 122 /* password ? */ 123 pp = strchr(cp, ':'); 124 if (pp != NULL) { 125 *(pp++) = '\0'; 126 root->cr_pass = pp; 127 } 128 129 root->cr_user = cp; 130 } else 131 sp = cp; 132 133 pp = strchr(sp, ':'); 134 if (pp != NULL) { 135 *(pp++) = '\0'; 136 root->cr_port = strtonum(pp, 1, 65535, &errstr); 137 if (errstr != NULL) 138 fatal("port specification in CVSROOT is %s", errstr); 139 140 } 141 142 root->cr_host = sp; 143 144 if (root->cr_method == CVS_METHOD_NONE) { 145 /* no method found from start of CVSROOT, guess */ 146 if (root->cr_host != NULL) 147 root->cr_method = CVS_METHOD_SERVER; 148 else 149 root->cr_method = CVS_METHOD_LOCAL; 150 } 151 152 return (root); 153 } 154 155 /* 156 * cvsroot_get() 157 * 158 * Get the CVSROOT information for a specific directory <dir>. The 159 * value is taken from one of 3 possible sources (in order of precedence): 160 * 161 * 1) the `-d' command-line option 162 * 2) the CVS/Root file found in checked-out trees 163 * 3) the CVSROOT environment variable 164 */ 165 struct cvsroot * 166 cvsroot_get(const char *dir) 167 { 168 char rootpath[MAXPATHLEN], *rootstr, line[128]; 169 FILE *fp; 170 171 if (cvs_rootstr != NULL) 172 return cvsroot_parse(cvs_rootstr); 173 174 if (cvs_server_active == 1) 175 return cvsroot_parse(dir); 176 177 if (cvs_cmdop == CVS_OP_IMPORT) 178 return NULL; 179 180 (void)xsnprintf(rootpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_ROOTSPEC); 181 182 if ((fp = fopen(rootpath, "r")) == NULL) { 183 if (errno == ENOENT) { 184 /* try env as a last resort */ 185 if ((rootstr = getenv("CVSROOT")) != NULL) 186 return cvsroot_parse(rootstr); 187 else 188 return (NULL); 189 } else { 190 fatal("cvsroot_get: fopen: `%s': %s", 191 CVS_PATH_ROOTSPEC, strerror(errno)); 192 } 193 } 194 195 if (fgets(line, (int)sizeof(line), fp) == NULL) 196 fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC); 197 198 (void)fclose(fp); 199 200 line[strcspn(line, "\n")] = '\0'; 201 if (line[0] == '\0') 202 cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC); 203 204 return cvsroot_parse(line); 205 } 206