3 * mirabilos <m@mirbsd.org>
5 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
7 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
10 * Poritons Copyright (c) 1992, Mark D. Baushke
12 * You may distribute under the terms of the GNU General Public License as
13 * specified in the README file that comes with the CVS source distribution.
17 * Determine the path to the CVSROOT and set "Root" accordingly.
24 /* Printable names for things in the current_parsed_root->method enum variable.
25 Watch out if the enum is changed in cvs.h! */
27 const char method_names[][16] = {
28 "undefined", "local", "server (rsh)", "pserver",
29 "kserver", "gserver", "ext", "fork"
35 Name_Root (const char *dir, const char *update_dir)
39 const char *xupdate_dir;
41 size_t root_allocated = 0;
47 TRACE (TRACE_FLOW, "Name_Root (%s, %s)",
49 update_dir ? update_dir : "(null)");
51 if (update_dir && *update_dir)
52 xupdate_dir = update_dir;
58 cvsadm = Xasprintf ("%s/%s", dir, CVSADM);
59 tmp = Xasprintf ("%s/%s", dir, CVSADM_ROOT);
63 cvsadm = xstrdup (CVSADM);
64 tmp = xstrdup (CVSADM_ROOT);
68 * Do not bother looking for a readable file if there is no cvsadm
71 * It is possible that not all repositories will have a CVS/Root
72 * file. This is ok, but the user will need to specify -d
73 * /path/name or have the environment variable CVSROOT set in
74 * order to continue. */
75 if ((!isdir (cvsadm)) || (!isreadable (tmp)))
82 * The assumption here is that the CVS Root is always contained in the
83 * first line of the "Root" file.
85 fpin = xfopen (tmp, "r");
87 if ((len = getline (&root, &root_allocated, fpin)) < 0)
89 int saved_errno = errno;
90 /* FIXME: should be checking for end of file separately; errno
91 is not set in that case. */
92 error (0, 0, "in directory %s:", xupdate_dir);
93 error (0, saved_errno, "cannot read %s", CVSADM_ROOT);
94 error (0, 0, "please correct this problem");
101 *cp = '\0'; /* strip the newline */
104 * root now contains a candidate for CVSroot. It must be an
105 * absolute pathname or specify a remote server.
108 ret = parse_cvsroot (root);
111 error (0, 0, "in directory %s:", xupdate_dir);
113 "ignoring %s because it does not contain a valid root.",
118 if (!ret->isremote && !isdir (ret->directory))
120 error (0, 0, "in directory %s:", xupdate_dir);
122 "ignoring %s because it specifies a non-existent repository %s",
140 * Write the CVS/Root file so that the environment variable CVSROOT
141 * and/or the -d option to cvs will be validated or not necessary for
145 Create_Root (const char *dir, const char *rootdir)
153 /* record the current cvs root */
158 tmp = Xasprintf ("%s/%s", dir, CVSADM_ROOT);
160 tmp = xstrdup (CVSADM_ROOT);
162 fout = xfopen (tmp, "w+");
163 if (fprintf (fout, "%s\n", rootdir) < 0)
164 error (1, errno, "write to %s failed", tmp);
165 if (fclose (fout) == EOF)
166 error (1, errno, "cannot close %s", tmp);
175 /* Translate an absolute repository string for a primary server and return it.
178 * root_in The root to be translated.
181 * A translated string this function owns, or a pointer to the original
182 * string passed in if no translation was necessary.
184 * If the returned string is the translated one, it may be overwritten
185 * by the next call to this function.
188 primary_root_translate (const char *root_in)
192 static char *previous = NULL;
195 /* This can happen, for instance, during `cvs init'. */
196 if (!config) return root_in;
198 if (config->PrimaryServer
199 && !strncmp (root_in, config->PrimaryServer->directory,
200 strlen (config->PrimaryServer->directory))
201 && (ISSLASH (root_in[strlen (config->PrimaryServer->directory)])
202 || root_in[strlen (config->PrimaryServer->directory)] == '\0')
206 Xasnprintf (previous, &len,
207 "%s%s", current_parsed_root->directory,
208 root_in + strlen (config->PrimaryServer->directory));
209 if (previous && previous != translated)
211 return previous = translated;
215 /* There is no primary root configured or it didn't match. */
221 /* Translate a primary root in reverse for PATHNAMEs in responses.
224 * root_in The root to be translated.
227 * A translated string this function owns, or a pointer to the original
228 * string passed in if no translation was necessary.
230 * If the returned string is the translated one, it may be overwritten
231 * by the next call to this function.
234 primary_root_inverse_translate (const char *root_in)
238 static char *previous = NULL;
241 /* This can happen, for instance, during `cvs init'. */
242 if (!config) return root_in;
244 if (config->PrimaryServer
245 && !strncmp (root_in, current_parsed_root->directory,
246 strlen (current_parsed_root->directory))
247 && (ISSLASH (root_in[strlen (current_parsed_root->directory)])
248 || root_in[strlen (current_parsed_root->directory)] == '\0')
252 Xasnprintf (previous, &len,
253 "%s%s", config->PrimaryServer->directory,
254 root_in + strlen (current_parsed_root->directory));
255 if (previous && previous != translated)
257 return previous = translated;
261 /* There is no primary root configured or it didn't match. */
267 /* The root_allow_* stuff maintains a list of valid CVSROOT
268 directories. Then we can check against them when a remote user
269 hands us a CVSROOT directory. */
270 static List *root_allow;
271 static List *root_allow_regexp;
276 if (n->data) free_config (n->data);
282 root_allow_add (const char *arg, const char *configPath)
286 if (!root_allow) root_allow = getlist();
288 n->key = xstrdup (arg);
289 n->data = parse_config (arg, configPath);
290 n->delproc = delconfig;
291 addnode (root_allow, n);
295 root_allow_regexp_add (const char *arg, const char *configPath)
299 if (!root_allow_regexp) root_allow_regexp = getlist();
301 n->key = xstrdup (arg);
303 /* This is a regexp, not the final cvsroot path - we cannot attach
304 it a config. So we attach configPath and we'll root_allow_add()
305 the actual, matching root in root_allow_compare_regexp() */
306 n->data = (void*)configPath;
308 addnode (root_allow_regexp, n);
312 root_allow_free (void)
314 dellist (&root_allow);
315 dellist (&root_allow_regexp);
319 root_allow_used (void)
321 return root_allow || root_allow_regexp;
324 /* walklist() callback for determining if 'root_to_check' matches
325 n->key (a regexp). If yes, 'root_to_check' will be added as if
326 directly specified through --allow-root.
329 root_allow_compare_regexp (Node *n, void *root_to_check)
334 if (regcomp(&re, n->key,
335 REG_EXTENDED|REG_NOSUB) != 0)
337 return 0; /* report error? */
339 status = regexec(&re, root_to_check, (size_t) 0, NULL, 0);
343 /* n->data contains gConfigPath */
344 root_allow_add (root_to_check, n->data);
351 root_allow_ok (const char *arg)
353 if (!root_allow_used())
355 /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10
356 or later without reading the documentation about
357 --allow-root. Printing an error here doesn't disclose any
358 particularly useful information to an attacker because a
359 CVS server configured in this way won't let *anyone* in. */
361 /* Note that we are called from a context where we can spit
362 back "error" rather than waiting for the next request which
363 expects responses. */
365 error 0 Server configuration missing --allow-root or --allow-root-regexp in inetd.conf\n");
369 /* Look for 'arg' in the list of full-path allowed roots */
370 if (findnode (root_allow, arg))
373 /* Match 'arg' against the list of allowed roots regexps */
374 if (walklist (root_allow_regexp, root_allow_compare_regexp, (void*)arg))
382 /* Get a config we stored in response to root_allow.
385 * The config associated with ARG.
388 get_root_allow_config (const char *arg, const char *configPath)
392 TRACE (TRACE_FUNCTION, "get_root_allow_config (%s)", arg);
395 n = findnode (root_allow, arg);
399 if (n) return n->data;
400 return parse_config (arg, configPath);
405 /* This global variable holds the global -d option. It is NULL if -d
406 was not used, which means that we must get the CVSroot information
407 from the CVSROOT environment variable or from a CVS/Root file. */
408 char *CVSroot_cmdline;
412 /* FIXME - Deglobalize this. */
413 cvsroot_t *current_parsed_root = NULL;
414 /* Used to save the original root being processed so that we can still find it
415 * in lists and the like after a `Redirect' response. Also set to mirror
416 * current_parsed_root in server mode so that code which runs on both the
417 * client and server but which wants to use original data on the client can
418 * just always reference the original_parsed_root.
420 const cvsroot_t *original_parsed_root;
423 /* allocate and initialize a cvsroot_t
425 * We must initialize the strings to NULL so we know later what we should
428 * Some of the other zeroes remain meaningful as, "never set, use default",
431 /* Functions which allocate memory are not pure. */
432 static cvsroot_t *new_cvsroot_t(void)
433 __attribute__( (__malloc__) );
439 /* gotta store it somewhere */
440 newroot = xmalloc(sizeof(cvsroot_t));
442 newroot->original = NULL;
443 newroot->directory = NULL;
444 newroot->method = null_method;
445 newroot->isremote = false;
446 #ifdef CLIENT_SUPPORT
447 newroot->username = NULL;
448 newroot->password = NULL;
449 newroot->hostname = NULL;
450 newroot->cvs_rsh = NULL;
451 newroot->cvs_server = NULL;
453 newroot->proxy_hostname = NULL;
454 newroot->proxy_port = 0;
455 newroot->redirect = true; /* Advertise Redirect support */
456 #endif /* CLIENT_SUPPORT */
463 /* Dispose of a cvsroot_t and its component parts.
466 * It is dangerous for most code to call this function since parse_cvsroot
467 * maintains a cache of parsed roots.
470 free_cvsroot_t (cvsroot_t *root)
473 if (root->original != NULL)
474 free (root->original);
475 if (root->directory != NULL)
476 free (root->directory);
477 #ifdef CLIENT_SUPPORT
478 if (root->username != NULL)
479 free (root->username);
480 if (root->password != NULL)
482 /* I like to be paranoid */
483 memset (root->password, 0, strlen (root->password));
484 free (root->password);
486 if (root->hostname != NULL)
487 free (root->hostname);
488 if (root->cvs_rsh != NULL)
489 free (root->cvs_rsh);
490 if (root->cvs_server != NULL)
491 free (root->cvs_server);
492 if (root->proxy_hostname != NULL)
493 free (root->proxy_hostname);
494 #endif /* CLIENT_SUPPORT */
499 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
500 static char *validate_hostname(const char *) __attribute__((__malloc__));
501 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
504 * Parse a CVSROOT string to allocate and return a new cvsroot_t structure.
505 * Valid specifications are:
507 * :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path
508 * [:(ext|server):][[user]@]host[:]/path
513 * root_in C String containing the CVSROOT to be parsed.
516 * A pointer to a newly allocated cvsroot_t structure upon success and
517 * NULL upon failure. The caller should never dispose of this structure,
518 * as it is stored in a cache, but the caller may rely on it not to
522 * This would have been a lot easier to write in Perl.
524 * Would it make sense to reimplement the root and config file parsing
531 parse_cvsroot (const char *root_in)
533 cvsroot_t *newroot; /* the new root to be returned */
534 char *cvsroot_save; /* what we allocated so we can dispose
535 * it when finished */
536 char *cvsroot_copy, *p; /* temporary pointers for parsing */
537 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
538 char *q; /* temporary pointer for parsing */
539 char *firstslash; /* save where the path spec starts
541 * [[user][:password]@]host[:[port]]
543 int check_hostname, no_port, no_password, no_proxy;
544 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
545 static List *cache = NULL;
548 assert (root_in != NULL);
550 /* This message is TRACE_FLOW since this function is called repeatedly by
551 * the recursion routines.
553 TRACE (TRACE_FLOW, "parse_cvsroot (%s)", root_in);
555 if ((node = findnode (cache, root_in)))
560 /* allocate some space */
561 newroot = new_cvsroot_t();
563 /* save the original string */
564 newroot->original = xstrdup (root_in);
566 /* and another copy we can munge while parsing */
567 cvsroot_save = cvsroot_copy = xstrdup (root_in);
569 if (*cvsroot_copy == ':')
571 char *method = ++cvsroot_copy;
573 /* Access method specified, as in
574 * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path",
575 * "cvs -d [:(ext|server):][[user]@]host[:]/path",
576 * "cvs -d :local:e:\path",
577 * "cvs -d :fork:/path".
578 * We need to get past that part of CVSroot before parsing the
582 if (! (p = strchr (method, ':')))
584 error (0, 0, "No closing `:' on method in CVSROOT.");
590 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
591 /* Look for method options, for instance, proxy, proxyport.
592 * Calling strtok again is saved until after parsing the method.
594 method = strtok (method, ";");
596 /* Could just exit now, but this keeps the error message in sync.
599 #endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
603 error (0, 0, "Missing method in CVSROOT.");
607 /* Now we have an access method -- see if it's valid. */
609 if (!strcasecmp (method, "local"))
610 newroot->method = local_method;
611 else if (!strcasecmp (method, "pserver"))
612 newroot->method = pserver_method;
613 else if (!strcasecmp (method, "kserver"))
614 newroot->method = kserver_method;
615 else if (!strcasecmp (method, "gserver"))
616 newroot->method = gserver_method;
617 else if (!strcasecmp (method, "server"))
618 newroot->method = server_method;
619 else if (strncmp (method, "ext=", 4) == 0)
621 newroot->cvs_rsh = xstrdup(method + 4);
622 newroot->method = ext_method;
624 else if (!strcasecmp (method, "extssh"))
626 newroot->cvs_rsh = xstrdup("ssh");
627 newroot->method = extssh_method;
629 else if (!strcasecmp (method, "ext"))
630 newroot->method = ext_method;
631 else if (!strcasecmp (method, "fork"))
632 newroot->method = fork_method;
635 error (0, 0, "Unknown method (`%s') in CVSROOT.", method);
639 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
640 /* Parse the method options, for instance, proxy, proxyport */
641 while ((p = strtok (NULL, ";")))
643 char *q = strchr (p, '=');
646 error (0, 0, "Option (`%s') has no argument in CVSROOT.",
652 TRACE (TRACE_DATA, "CVSROOT option=`%s' value=`%s'", p, q);
653 if (!strcasecmp (p, "proxy"))
655 if (!(newroot->proxy_hostname = validate_hostname(q))) {
656 error(0, 0, "Invalid proxy hostname: %s", q);
660 else if (!strcasecmp (p, "proxyport"))
668 "CVSROOT may only specify a positive, non-zero, integer proxy port (not `%s').",
673 if ((newroot->proxy_port = atoi(q)) <= 0 ||
674 newroot->proxy_port > 65535)
675 goto proxy_port_error;
677 else if (!strcasecmp (p, "CVS_RSH"))
679 /* override CVS_RSH environment variable */
680 if (newroot->method == ext_method
681 || newroot->method == extssh_method)
682 newroot->cvs_rsh = xstrdup (q);
684 else if (!strcasecmp (p, "CVS_SERVER"))
686 /* override CVS_SERVER environment variable */
687 if (newroot->method == ext_method
688 || newroot->method == extssh_method
689 || newroot->method == fork_method)
690 newroot->cvs_server = xstrdup (q);
692 else if (!strcasecmp (p, "Redirect"))
693 readBool ("CVSROOT", "Redirect", q, &newroot->redirect);
696 error (0, 0, "Unknown option (`%s') in CVSROOT.", p);
700 #endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
704 /* If the method isn't specified, assume EXT_METHOD if the string looks
705 like a relative path and LOCAL_METHOD otherwise. */
707 newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/'))
713 * There are a few sanity checks we can do now, only knowing the
714 * method of this root.
717 newroot->isremote = (newroot->method != local_method);
719 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
720 if (readonlyfs && newroot->isremote && (newroot->method != ext_method)
721 && (newroot->method != extssh_method) && (newroot->method != fork_method))
723 "Read-only repository feature unavailable with remote roots (cvsroot = %s)",
726 if ((newroot->method != local_method)
727 && (newroot->method != fork_method)
730 /* split the string into [[user][:password]@]host[:[port]] & /path
732 * this will allow some characters such as '@' & ':' to remain unquoted
733 * in the path portion of the spec
735 if ((p = strchr (cvsroot_copy, '/')) == NULL)
737 error (0, 0, "CVSROOT requires a path spec:");
739 ":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path");
740 error (0, 0, "[:(ext|server):][[user]@]host[:]/path");
743 firstslash = p; /* == NULL if '/' not in string */
746 /* Check to see if there is a username[:password] in the string. */
747 if ((p = strchr (cvsroot_copy, '@')) != NULL)
750 /* check for a password */
751 if ((q = strchr (cvsroot_copy, ':')) != NULL)
754 newroot->password = xstrdup (++q);
755 /* Don't check for *newroot->password == '\0' since
756 * a user could conceivably wish to specify a blank password
758 * (newroot->password == NULL means to use the
759 * password from .cvspass)
763 /* copy the username */
764 if (*cvsroot_copy != '\0')
765 /* a blank username is impossible, so leave it NULL in that
766 * case so we know to use the default username
769 /* for want of strcspn */
770 if (/* no at, obviously */ strchr(cvsroot_copy, '@') ||
771 /* no colon, interference with CVSROOT/passwd file */
772 strchr(cvsroot_copy, ':') ||
773 /* no linefeeds, interference with pserver protocol */
774 strchr(cvsroot_copy, '\012')) {
775 error(0, 0, "Bad username \"%s\"", cvsroot_copy);
778 /* other limitations include not beginning with a
779 * hyphen-minus but that’s not even a requirement
780 * in POSIX, let alone other operating environments…
782 newroot->username = xstrdup (cvsroot_copy);
788 /* now deal with host[:[port]] */
791 if ((p = strchr (cvsroot_copy, ':')) != NULL)
802 goto parse_port_error;
804 if ((newroot->port = atoi(p)) <= 0 || newroot->port > 65535) {
807 "CVSROOT may only specify a positive, non-zero, integer port (not `%s').",
809 error (0, 0, "Perhaps you entered a relative pathname?");
815 /* check and copy host */
816 newroot->hostname = validate_hostname(cvsroot_copy);
818 /* restore the '/' */
819 cvsroot_copy = firstslash;
822 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
825 * Parse the path for all methods.
827 /* Here & local_cvsroot() should be the only places this needs to be
828 * called on a CVSROOT now. cvsroot->original is saved for error messages
829 * and, otherwise, we want no trailing slashes.
831 Sanitize_Repository_Name (cvsroot_copy);
832 newroot->directory = xstrdup (cvsroot_copy);
835 * Do various sanity checks.
838 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
839 if (newroot->username && ! newroot->hostname)
841 /* this defangs sanity.sh tests for remote reject, though */
843 error (0, 0, "Missing or bad hostname in CVSROOT.");
847 /* We won't have attempted to parse these without CLIENT_SUPPORT or
854 #endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
855 switch (newroot->method)
858 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
859 if (newroot->username || newroot->hostname)
861 error (0, 0, "Can't specify hostname and username in CVSROOT");
862 error (0, 0, "when using local access method.");
865 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
866 /* cvs.texinfo has always told people that CVSROOT must be an
867 absolute pathname. Furthermore, attempts to use a relative
868 pathname produced various errors (I couldn't get it to work),
869 so there would seem to be little risk in making this a fatal
871 if (!ISABSOLUTE (newroot->directory))
873 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')",
875 error (0, 0, "when using local access method.");
878 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
879 /* We don't need to check for these in :local: mode, really, since
880 * we shouldn't be able to hit the code above which parses them, but
881 * I'm leaving them here in lieu of assertions.
884 /* no_password already set */
885 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
887 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
889 /* We want :fork: to behave the same as other remote access
890 methods. Therefore, don't check to see that the repository
891 name is absolute -- let the server do it. */
892 if (newroot->username || newroot->hostname)
894 error (0, 0, "Can't specify hostname and username in CVSROOT");
895 error (0, 0, "when using fork access method.");
898 newroot->hostname = xstrdup("server"); /* for error messages */
899 if (!ISABSOLUTE (newroot->directory))
901 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')",
903 error (0, 0, "when using fork access method.");
907 /* no_password already set */
911 /* no_password already set */
916 /* no_password already set */
922 /* no_password already set */
930 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
932 error (1, 0, "Invalid method found in parse_cvsroot");
935 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
936 if (no_password && newroot->password)
938 error (0, 0, "CVSROOT password specification is only valid for");
939 error (0, 0, "pserver connection method.");
942 if (no_proxy && (newroot->proxy_hostname || newroot->proxy_port))
945 "CVSROOT proxy specification is only valid for gserver and");
946 error (0, 0, "pserver connection methods.");
950 if (!newroot->proxy_hostname && newroot->proxy_port)
952 error (0, 0, "Proxy port specified in CVSROOT without proxy host.");
956 if (check_hostname && !newroot->hostname)
959 if (no_port && newroot->port)
962 "CVSROOT port specification is only valid for extssh,");
963 error (0, 0, "gserver, kserver and pserver connection methods.");
966 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
968 if (*newroot->directory == '\0')
970 error (0, 0, "Missing directory in CVSROOT.");
974 /* Hooray! We finally parsed it! */
977 if (!cache) cache = getlist();
979 node->key = xstrdup (newroot->original);
980 node->data = newroot;
981 addnode (cache, node);
986 free_cvsroot_t (newroot);
992 #ifdef AUTH_CLIENT_SUPPORT
993 /* Use root->username, root->hostname, root->port, and root->directory
994 * to create a normalized CVSROOT fit for the .cvspass file
996 * username defaults to the result of getcaller()
997 * port defaults to the result of get_cvs_port_number()
999 * FIXME - we could cache the canonicalized version of a root inside the
1000 * cvsroot_t, but we'd have to un'const the input here and stop expecting the
1001 * caller to be responsible for our return value
1004 * ROOT->method == pserver_method
1007 normalize_cvsroot (const cvsroot_t *root)
1009 char *cvsroot_canonical;
1012 assert (root && root->hostname && root->directory);
1014 /* use a lower case hostname since we know hostnames are case insensitive */
1015 /* Some logic says we should be tacking our domain name on too if it isn't
1016 * there already, but for now this works. Reverse->Forward lookups are
1017 * almost certainly too much since that would make CVS immune to some of
1018 * the DNS trickery that makes life easier for sysadmins when they want to
1019 * move a repository or the like
1021 p = hostname = xstrdup (root->hostname);
1028 cvsroot_canonical = Xasprintf (":pserver:%s@%s:%d%s",
1029 root->username ? root->username
1031 hostname, get_cvs_port_number (root),
1035 return cvsroot_canonical;
1037 #endif /* AUTH_CLIENT_SUPPORT */
1041 #ifdef PROXY_SUPPORT
1042 /* A walklist() function to walk the root_allow list looking for a PrimaryServer
1043 * configuration with a directory matching the requested directory.
1045 * If found, replace it.
1047 static bool get_local_root_dir_done;
1049 get_local_root_dir (Node *p, void *root_in)
1051 struct config *c = p->data;
1054 if (get_local_root_dir_done)
1057 if (c->PrimaryServer && !strcmp (*r, c->PrimaryServer->directory))
1060 *r = xstrdup (p->key);
1061 get_local_root_dir_done = true;
1065 #endif /* PROXY_SUPPORT */
1069 /* allocate and return a cvsroot_t structure set up as if we're using the local
1070 * repository DIR. */
1072 local_cvsroot (const char *dir)
1074 cvsroot_t *newroot = new_cvsroot_t();
1076 newroot->original = xstrdup(dir);
1077 newroot->method = local_method;
1078 newroot->directory = xstrdup(dir);
1079 /* Here and parse_cvsroot() should be the only places this needs to be
1080 * called on a CVSROOT now. cvsroot->original is saved for error messages
1081 * and, otherwise, we want no trailing slashes.
1083 Sanitize_Repository_Name (newroot->directory);
1085 #ifdef PROXY_SUPPORT
1086 /* Translate the directory to a local one in the case that we are
1087 * configured as a secondary. If root_allow has not been initialized,
1090 get_local_root_dir_done = false;
1091 walklist (root_allow, get_local_root_dir, &newroot->directory);
1092 #endif /* PROXY_SUPPORT */
1100 /* This is for testing the parsing function. Use
1102 gcc -I. -I.. -I../lib -DDEBUG root.c -o root
1108 char *program_name = "testing";
1109 char *cvs_cmd_name = "parse_cvsroot"; /* XXX is this used??? */
1112 main (int argc, char *argv[])
1114 program_name = argv[0];
1118 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name);
1122 if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL)
1124 fprintf (stderr, "%s: Parsing failed.\n", program_name);
1127 printf ("CVSroot: %s\n", argv[1]);
1128 printf ("current_parsed_root->method: %s\n",
1129 method_names[current_parsed_root->method]);
1130 printf ("current_parsed_root->username: %s\n",
1131 current_parsed_root->username
1132 ? current_parsed_root->username : "NULL");
1133 printf ("current_parsed_root->hostname: %s\n",
1134 current_parsed_root->hostname
1135 ? current_parsed_root->hostname : "NULL");
1136 printf ("current_parsed_root->directory: %s\n",
1137 current_parsed_root->directory);
1144 #if defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
1145 #define CLS_INVALID 0
1146 #define CLS_INSIDE 1
1147 #define CLS_OUTSIDE 2
1148 #define CLS_SEPARATOR 4
1150 #define CLASSIFY(x) classify[(unsigned char)(x)]
1152 validate_hostname(const char *s)
1156 static char classify_initialised = 0, *classify;
1158 /* initialise classification table */
1159 if (!classify_initialised) {
1162 classify = xmalloc(256);
1163 for (sz = 0; sz < 256; ++sz)
1164 CLASSIFY(sz) = CLS_INVALID;
1165 for (ccp = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1167 CLASSIFY(*ccp) = CLS_INSIDE | CLS_OUTSIDE;
1168 CLASSIFY('-') = CLS_INSIDE;
1169 CLASSIFY('.') = CLS_SEPARATOR;
1170 classify_initialised = 1;
1173 /* total size limit tolerating a trailing dot */
1174 if ((sz = strlen(s)) > 256)
1178 /* drop trailing dot */
1179 if ((unsigned char)buf[sz - 1] == (unsigned char)'.')
1188 /* check each label */
1191 /* must begin with [0-9A-Za-z] */
1192 if (!(CLASSIFY(*cp++) & CLS_OUTSIDE))
1195 /* arbitrary many [0-9A-Za-z-] */
1196 while (CLASSIFY(*cp) & CLS_INSIDE) {
1200 /* except the last must have been [0-9A-Za-z] again */
1201 if (!(CLASSIFY(cp[-1]) & CLS_OUTSIDE))
1203 /* maximum label size */
1207 if (CLASSIFY(*cp) & CLS_SEPARATOR) {
1211 /* must be end of string now */
1214 /* it is, everything okay */
1217 #endif /* defined(CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */