--- /dev/null
+diff -urN cvs-1.11.15/aclconfig.default cvs-1.11.15-cvsacl/aclconfig.default
+--- cvs-1.11.15/aclconfig.default 1970-01-01 01:00:00.000000000 +0100
++++ cvs-1.11.15-cvsacl/aclconfig.default 2004-04-25 20:04:51.637863856 +0200
+@@ -0,0 +1,33 @@
++# Set `UseCVSACL' to yes to use CVS ACL feature.
++UseCVSACL=yes
++
++# Default CVS ACL Permission are to use.
++#CVSACLDefaultPermissions=a
++
++# Default file location for CVS ACL file (access) is CVSROOT/access.
++# If you want to use a different location, define it below.
++#CVSACLFileLocation=/path/to/cvs/access
++
++# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).
++UseSystemGroups=yes
++
++# Set `UseCVSGroups' to yes to use another group file.
++#UseCVSGroups=yes
++
++# Default file location for CVS groups file is CVSROOT/group.
++# If you want to use a different location, define it below.
++#CVSGroupsFileLocation=/path/to/cvs/group
++
++# Set UseSeperateACLFileForEachDir to yes in order to use a
++# seperate 'access' file for each directory.
++# This increased the performance if you have really big repository.
++#UseSeperateACLFileForEachDir=no
++
++# If StopAtFirstPermissionDenied is set to yes
++# operation will stop at first permission denied message.
++# Default is no.
++#StopAtFirstPermissionDenied=no
++
++# Set CVSServerRunAsUser to a system user, in order CVS server
++# to run as.
++#CVSServerRunAsUser=runascvsuser
+diff -urN cvs-1.11.15/CHANGELOG.cvsacl cvs-1.11.15-cvsacl/CHANGELOG.cvsacl
+--- cvs-1.11.15/CHANGELOG.cvsacl 1970-01-01 01:00:00.000000000 +0100
++++ cvs-1.11.15-cvsacl/CHANGELOG.cvsacl 2004-04-25 20:04:51.660860360 +0200
+@@ -0,0 +1,12 @@
++Release 1.2.0
++ - implemented permission inheritance from parents.
++ - new config keyword: StopAtFirstPermissionDenied.
++ - fixed some errors.
++
++Release 1.1.3
++ - fixed a bug related with default permissions.
++ - fixed a bug related with setting permissions using + - signs.
++
++Release 1.1.2
++ - fixed bugs reported on sourceforge.
++
+diff -urN cvs-1.11.15/INSTALL.cvsacl cvs-1.11.15-cvsacl/INSTALL.cvsacl
+--- cvs-1.11.15/INSTALL.cvsacl 1970-01-01 01:00:00.000000000 +0100
++++ cvs-1.11.15-cvsacl/INSTALL.cvsacl 2004-04-25 20:04:51.661860208 +0200
+@@ -0,0 +1,23 @@
++
++Installation
++
++- copy the file acl.c under src directory of CVS source distribution.
++ "cp acl.c /path/to/cvs-1.11.14/src/"
++- copy the patch file cvsacl-patch-1.2.0.beta2 under CVS source distribution
++ directory.
++ "cp cvsacl-patch-1.2.0.beta2 /path/to/cvs-1.11.14/"
++- cd to CVS source directory.
++ "cd /path/to/cvs-1.11.14/"
++- apply the patch.
++ "patch -p0 < cvsacl-patch-1.2.0.beta2"
++- if you are initializing the repository after applying patch, related
++ config files will be created with init command.
++ "cvs -d /path/to/repository init"
++- if you already have a repository, you have to add the aclconfig file
++ to your $CVSROOT/CVSROOT/. aclconfig.default is the default configuration
++ file, you can rename it to aclconfig, and use it .
++- modify aclconfig file, if you need to change some options.
++- as the last step, you have to define yourself as acl administrator.
++ "cvs -d /path/to/repository racl yourname:p -r ALL -d ALL"
++ this command gives p (acl admin) rights to user (yourname),
++ on all repository and tags/branches.
+diff -urN cvs-1.11.15/README.cvsacl cvs-1.11.15-cvsacl/README.cvsacl
+--- cvs-1.11.15/README.cvsacl 1970-01-01 01:00:00.000000000 +0100
++++ cvs-1.11.15-cvsacl/README.cvsacl 2004-04-25 20:04:51.666859448 +0200
+@@ -0,0 +1,282 @@
++
++CVS Access Control List Extension Patch
++
++http://cvsacl.sourceforge.net/
++sbaris@users.sourceforge.net
++
++CVSACL is a patch for CVS. It adds two new subcommands
++(acl & racl) to cvs for access control list management. It
++provides advanced ACL definitions per modules, directories,
++and files on branch/tag for remote cvs repository connections.
++Execution of all CVS subcommands can be controlled with eight
++different permissions.
++ACL definitions works for only remote connections, local users
++can access and modify repository, if unix file system permissions
++allow. If you want all users to make remote connections to
++repository, and not allow local users to access repository, you
++have to set CVSServerRunAsUser keyword in aclconfig file
++(explained below).
++Still local users can use acl and racl subcommands to set
++permissions on directories or files if they have acl admin rights (p)
++on related directories or files.
++So, in order to control all access to repository with this ACL
++extension, you should use CVSServerRunAsUser keyword and force all
++users to make remote connections. CVS repository administrator or
++project managers have to use acl and racl subcommands to manage
++permissions. But there is no gui client supporting these subcommands,
++so you have to use cvs client itself either locally or remotely.
++
++
++
++
++Permission Types
++
++- no access
++ Command line character: n
++ If a user given n permission, it is not allowed for any action on repository.
++- read
++ Command line character: r
++ r permission gives only read access on repository.
++ With r permission you are allowed to run cvs subcommands: annotate,
++ checkout, diff, export, log, rannotate, rdiff, rlog, status.
++- write
++ Command line character: w
++ w permission allows only cvs commit/checkin action.
++ With w permission, you are not allowed to add/remove any file to/from
++ repository, other permissions should be defines for that.
++- tag
++ Command line character: t
++ t permission allows cvs tag and rtag subcommands to run, so you may
++ control tagging and untagging operations. t permission includes r
++ permission, since without reading you can not tag/untag a file.
++ However t permission does not include write permission, you can not
++ commit a file with only t permission.
++- create
++ Command line character: c
++ c permission allows cvs add and import subcommands to run. To add or
++ import a file/directory to repository, you have to given a c
++ permission. Again, c permission does not include write permission,
++ thus you may only add or import files, but you can not modify any
++ existing file. After issuing add subcommand, you have to commit the file
++ to complete adding. This commit subcommand is allowed because you are
++ adding file and not modifying existing one.
++- delete
++ Command line character: d
++ d permission allows cvs remove command to run. To remove a file/directory
++ from repository, d permission have to set. It does not include write
++ permission, so you can not modify contents of an existing file on repository.
++- full access except admin rights
++ Command line character: a
++ a permission gives all access (above permissions) to repository, but it
++ can not modify permissions. Only acl admins may modify the acl definitions.
++- acl admin
++ Command line character: p
++ p permission means that user is an acl admin, so it is allowed to make anything on repository.
++
++
++ACL Config Keywords
++The administrative file aclconfig contains miscellaneous settings which
++affect the behaviour of ACL extension. Currently defined keywords are:
++
++UseCVSACL=value
++Use ACL definitions if set to yes. If you do not want to use ACLs for
++some repositories in a patched CVS server, set this keyword to no. The default is no.
++
++UseCVSACLDefaultPermissions=value
++Value can be any combination of valid permission types (w,r,t,c,d,t,a,p).
++if there is no defined ACL and default permission in access file, or no
++access file at all, this permissions are used. The default is p (admin rights),
++if aclconfig file is created with cvs init.
++
++UseCVSGroups=value
++CVS does not have a CVSROOT/passwd file. However it can be created manually
++(format should be same as /etc/group). If value set to yes, CVS checks for
++groups in file $CVSROOT/CVSROOT/group The default value is no.
++
++UseSystemGroups=value
++Group memberships for users are checked in file /etc/group, if value is set
++to yes. The default value is no.
++
++CVSACLFileLocation=value
++Originally access file is put under CVSROOT/CVSROOT, if you want a different
++location, set value to a valid path. The default value is $CVSROOT/CVSROOT/access.
++
++CVSGroupsFileLocation=value
++IF UseCVSGroups is set to yes, CVS looks for a group file under $CVSROOT/CVSROOT.
++To use a different location for group file set value to a valid path to group.
++The default value is $CVSROOT/CVSROOT/group.
++
++UseSeperateACLFileForEachDir=value
++If value is set to yes, a seperate ACL file (access) is created for each
++directory in repository. If you have a really big repository
++(directories>10,000 and files>100,000), performance may drop due to a big
++acl file, access. Setting the value to yes, may increase performance. Normally,
++you will not need this. The default value is no.
++
++StopAtFirstPermissionDenied=value
++If StopAtFirstPermissionDenied is set to yes
++operation will stop at first permission denied message.
++e.g. when you send commit command for a directory, if you dont
++have write permission for just one file under the directory,
++by default you will have a warning and commit will continue
++on the other files. If you set this keyword to 'no', then
++commit operation will be stopped when inaccassable file found.
++Default is no.
++
++CVSServerRunAsUser=value
++Set CVSServerRunAsUser keyword to a valid system user.
++When a user make a remote connection to CVS, after successfull authentication
++cvs process switch to run as that user, or defined system user in
++$CVSROOT/CVSROOT/passwd. So, you also have to set unix file permissions accordingly.
++A better solution:
++Add a user and group such as both cvsadm.
++Set CVSServerRunAsUser keyword to cvsadm.
++Change unix file system permissions for your repository,
++make cvsadm user and group owner, and read,write,execute permissions and setgid.
++(chown cvsadm -R /path/to/your/repository)
++(chgrp cvsadm -R /path/to/your/repository)
++(chmod 2770 -R /path/to/your/repository)
++Add yourself to cvsadm group (since you are ACL administrator).
++Therefore, only users making remote connections will have access to repository
++if you give rights. Local users can not access to repository via a cvs client or directly.
++
++
++Command Line Usage Information
++acl command is used on checked out files or directories. racl command is
++used on repository without a working copy. Usage information can be obtained
++with standard cvs --help command.
++Output of cvs --help acl and cvs --help racl:
++
++Usage: cvs racl [user||group:permissions] [-Rl] [-r tag]
++ -d [directory] -f [file]
++ -R Process directories recursively.
++ -r rev Existing revision/tag.
++ -l List defined ACLs.
++ -d dir Process on given directory.
++ -f file Process on given file.
++
++Usage: cvs acl [user||group:permissions] [-Rl] [-r tag]
++ -d [directory] -f [file]
++ -R Process directories recursively.
++ -r rev Existing revision/tag.
++ -l List defined ACLs.
++ -d dir Process on given directory.
++ -f file Process on given file.
++
++You may directly set permissions for a user or group or add/remove
++permissions with + and - signs to/from existing permissions.
++If you do not give the branch/tag information, default value of HEAD
++(main branch) will be used. You have to give branch/tag name with -r option.
++You may type ALL for branch/tag field.
++
++While checking for permissions, it goes thorough the list below. So the highest
++significant permission is the first item in list.
++
++- permissions assigned to username for specific directory or file.
++- permissions assigned to group name for specific directory or file.
++- permissions as defaults for specific directory or file.
++- permissions assigned to parent folders (inherits from the first parent
++ which permissions are assigned).
++- permissions as repository defaults.
++- permissions in aclconfig file.
++
++
++
++
++Examples
++ /cvs/
++ |
++ |
++ +--projectA/
++ | |
++ | +---CVSROOT/
++ | |
++ | +---lib/
++ | | |
++ | | +---gnulib/
++ | | |
++ | | +---zlib/
++ | |
++ | +---src/
++ | | |
++ | | +---main.c
++ | | |
++ | | +---server.c
++ | | |
++ | | +---client.c
++ | |
++ | +---gui/
++ |
++ +--projectB/
++We have above directory structure for a cvs repository, and no defined permissions.
++
++Setting main default permissions:
++
++$ cvs -d /cvs/projectA racl cvsadmin:p -r ALL -d ALL
++$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL
++User cvsadmin will be an acl admin, and all other users will have only read
++rights on all branches/tags in projectA repository. This is the default acl
++definition and it overwrites default permissions in $CVSROOT/CVSROOT/aclconfig file.
++
++$ cvs -d /cvs/projectA racl ALL:r -r ALL -d ALL
++$ cvs -d /cvs/projectA racl ALL:n -r ALL -d gui
++After executing these two commands, all users will have read access on all
++directories and files except gui directory. Everyone will be denied to access to gui
++directory becase no access, n, permissions is set.
++
++Setting permissions directly on a file or directory:
++
++$ cvs -d /cvs/projectA racl userX:wcd -d lib
++$ cvs -d /cvs/projectA racl group1:w -d lib
++First command will set write, create, and delete permissions for userX on directory
++lib with branch HEAD (since no branch/tag information given, branch defaults to HEAD).
++Second command will set only write permission for group1 on directory lib with branch HEAD.
++Members of group1 will have only commit rights on lib directory, branch HEAD, they can
++not add or remove any file, just modify existing files.
++If userX is also a member of group1, userX will have write, create, and delete permissions
++because it is specifically given these permissions.
++
++$ cvs -d /cvs/projectA racl userY:wcd -r develStream -d lib
++$ cvs -d /cvs/projectA racl userY:r -r integStream -d lib
++These commands will give wcd permissions to userY on lib directory with tag develstream,
++and r permissions on lib directory with tag integStream.
++
++$ cvs -d /cvs/projectA racl userZ:wcd -d src
++$ cvs -d /cvs/projectA racl userZ:r -f src/main.c
++First command will give wcd permissions to userZ on src directory, but only read
++permission on file main.c in src directory.
++
++Using + and - signs to set permissions on a file or directory:
++
++$ cvs -d /cvs/projectA racl userZ:+t -d src
++$ cvs -d /cvs/projectA racl userZ:-cd -d src
++$ cvs -d /cvs/projectA racl userZ:-wt -d src
++Before the first command, userZ has wcd permissions on src directory, after issuing
++command it will have wcdt permissions. Tag permission will be added. UserZ has wcdt
++permissions, and we execute the second command to remove create and delete permissions.
++So userZ has wt permissions. In the last command we also remove wt permissions, finally
++userZ has no defined permissions left, and it will use the default permissions if set.
++
++Listing permissions on a file or directory:
++
++$ cvs -d /cvs/projectA racl -l -d src
++$ cvs -d /cvs/projectA racl -l -f src
++$ cvs -d /cvs/projectA racl -l -f src/main.c
++
++First command will list the permissions for src directory.
++Example output:
++d src HEAD | userX:wcd group1:r | defaults:r
++userX and group1 has assigned permissions, all other users will have default
++permissions, which is only read.
++
++Second command will list the permissions for files in src directory.
++Example output:
++f src/main.c HEAD | userX:wcd group1:r | defaults:r
++f src/server.c HEAD | userX:wcd group1:r | defaults:r
++f src/client.c HEAD | userX:wcd group1:r | defaults:r
++
++Third command will list the permissions for main.c file in src directory.
++Example output:
++f src/main.c HEAD | userX:wcd group1:r | defaults:r
++
++
+diff -urN cvs-1.11.15/src/acl.c cvs-1.11.15-cvsacl/src/acl.c
+--- cvs-1.11.15/src/acl.c 1970-01-01 01:00:00.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/acl.c 2004-04-25 20:19:28.506559552 +0200
+@@ -0,0 +1,2028 @@
++/*
++ *
++ * CVS ACCESS CONTROL LIST EXTENSION
++ *
++ * sbaris@users.sourceforge.net
++ *
++ * http://cvsacl.sourceforge.net/
++ *
++ * CVSACL is a patch for CVS
++ * It adds two new subcommands (acl & racl) to cvs for access control
++ * list management.
++ * It provides advanced ACL definitions per modules, directories,
++ * and files on branch/tag for remote cvs repository connections.
++ * Execution of all CVS subcommands can be controlled with eight
++ * different permissions.
++ * ACL definitions works for only remote connections, local users can
++ * access and modify repository, if unix file system permissions allow.
++ * If you want all users to make remote connections to repository,
++ * and not allow local users to access repository, you have to set
++ * CVSServerRunAsUser keyword in aclconfig file (explained below).
++ * Still local users can use acl and racl subcommands to set permissions
++ * on directories or files if they have acl admin rights (p) on related
++ * directories or files.
++ * So, in order to control all access to repository with this ACL extension,
++ * you should use CVSServerRunAsUser keyword and force all users to make
++ * remote connections.
++ * CVS repository administrator or project managers have to use acl and racl
++ * subcommands to manage permissions. But there is no gui client supporting
++ * these subcommands, so you have to use cvs client itself either
++ * locally or remotely.
++ *
++ *
++ * Permission Types:
++ * - no permission (n) (1)
++ * - all permissions (a) (2)
++ * - write permission (w) (3)
++ * - tag permission (t) (4)
++ * - read permission (r) (5)
++ * - add permission (c) (6)
++ * - remove permission (d) (7)
++ * - permission change (p) (8)
++ *
++ *
++ *
++ *
++ * ********************************************************************
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 1, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ * *******************************************************************
++ *
++ */
++
++#include "cvs.h"
++#include "getline.h"
++#include <grp.h>
++
++static int acl_fileproc PROTO ((void *callerdat, struct file_info *finfo));
++static Dtype acl_dirproc PROTO ((void *callerdat, const char *dir, const char *repos,
++ const char *update_dir, List *entries));
++
++static int acllist_fileproc PROTO ((void *callerdat, struct file_info *finfo));
++static Dtype acllist_dirproc PROTO ((void *callerdat, const char *dir, const char *repos,
++ const char *update_dir, List *entries));
++
++static void acllist_print PROTO ((char *line, const char *obj));
++
++static int racl_proc PROTO((int argc, char **argv, char *xwhere,
++ char *mwhere, char *mfile, int shorten,
++ int local_specified, char *mname, char *msg));
++
++FILE *open_accessfile (char *xmode, char *repos, char **fname);
++FILE *open_groupfile (char *xmode);
++
++char *get_perms (char *xperms);
++char *make_perms (char *xperms, char *xfounduserpart, char **xerrmsg);
++
++static char *cache_repository;
++static int cache_retval;
++static int founddeniedfile;
++static int cache_perm;
++
++static int is_racl;
++
++int use_cvs_acl = 0;
++char *cvs_acl_default_permissions;
++int use_cvs_groups = 0;
++int use_system_groups = 0;
++int use_seperate_acl_file_for_each_dir = 0;
++char *cvs_acl_file_location = NULL;
++char *cvs_groups_file_location = NULL;
++char *cvs_server_run_as = NULL;
++int stop_at_first_permission_denied = 0;
++
++char *tag = NULL;
++
++char *muser;
++char *mperms;
++static int defaultperms;
++
++static char *default_perms_object;
++char *default_part_perms_accessfile;
++
++int acldir = 0;
++int aclfile = 0;
++int listacl = 0;
++
++int userfound;
++int groupfound;
++
++char *dirs[25];
++
++int aclconfig_default_used;
++
++static const char *const acl_usage[] =
++ {
++ "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n",
++ "\t-R\tProcess directories recursively.\n",
++ "\t-r rev\tExisting revision/tag.\n",
++ "\t-l\tList defined ACLs.\n",
++ "\t-d dir\tProcess on given directory.\n",
++ "\t-f file\tProcess on given file.\n",
++ "(Specify the --help global option for a list of other help options)\n",
++ NULL
++ };
++
++static const char *const racl_usage[] =
++ {
++ "Usage: %s %s [user||group:permissions] [-Rl] [-r tag]\n -d [directory] -f [file]\n",
++ "\t-R\tProcess directories recursively.\n",
++ "\t-r rev\tExisting revision/tag.\n",
++ "\t-l\tList defined ACLs.\n",
++ "\t-d dir\tProcess on given directory.\n",
++ "\t-f file\tProcess on given file.\n",
++ "(Specify the --help global option for a list of other help options)\n",
++ NULL
++ };
++
++
++int
++access_allowed (file, repos, tag, perm, mline, mpos, usecache)
++char *file;
++char *repos;
++char *tag;
++int perm;
++char **mline;
++int *mpos;
++int usecache;
++{
++ int retval = 0;
++ int foundline = 0;
++ FILE *accessfp;
++
++ char *line = NULL;
++ size_t line_allocated = 0;
++
++ char *part_type = NULL;
++ char *part_object = NULL;
++ char *part_tag = NULL;
++ char *part_perms = NULL;
++ char *xline;
++
++ char *iline;
++
++ char *tempv;
++ char *tempc;
++ size_t tempsize;
++ int intcount;
++
++ int oneaccessfile = 0;
++ int accessfilecount;
++
++ int signlevel = 0;
++
++ int dadmin = 0;
++
++ const char *repository;
++ char *filefullname = NULL;
++
++
++ userfound = 0;
++ groupfound = 0;
++
++
++ if (defaultperms)
++ {
++ repository = xstrdup ("ALL");
++ }
++ else
++ repository = Short_Repository (repos);
++
++ /* cache */
++ if (usecache
++ && cache_repository != NULL
++ && strcmp (cache_repository, repository) == 0
++ && !founddeniedfile
++ && perm == cache_perm)
++ return (cache_retval);
++ else
++ {
++ free(cache_repository);
++ cache_repository = xstrdup(repository);
++ cache_perm = perm;
++ }
++
++ if (file != NULL)
++ {
++ filefullname = xmalloc (strlen (repository)
++ + strlen (file)
++ + 2);
++
++ strcpy (filefullname, repository);
++ strcat (filefullname, "/");
++ strcat (filefullname, file);
++ }
++
++
++ iline = xstrdup(repository);
++
++ tempv = strtok(iline, "/\t");
++ tempc = xstrdup(tempv);
++ tempsize = strlen(tempc);
++
++ intcount = 0;
++
++ dirs[intcount] = xstrdup(tempc);
++
++ while ((tempv = strtok(NULL, "/\t")) != NULL)
++ {
++ intcount++;
++
++ xrealloc_and_strcat(&tempc, &tempsize, "/");
++ xrealloc_and_strcat(&tempc, &tempsize, tempv);
++
++ dirs[intcount] = xstrdup(tempc);
++ }
++
++ if (file != NULL)
++ {
++ intcount++;
++ dirs[intcount] = xstrdup(filefullname);
++ }
++
++ for (accessfilecount = intcount; accessfilecount >= 0 && !oneaccessfile; accessfilecount--)
++ {
++ if (!use_seperate_acl_file_for_each_dir)
++ oneaccessfile = 1;
++ else if (use_seperate_acl_file_for_each_dir)
++ oneaccessfile = 0;
++
++ if (oneaccessfile)
++ accessfp = open_accessfile ("r", repos, NULL);
++ else
++ accessfp = open_accessfile ("r", dirs[accessfilecount], NULL);
++
++ if (accessfp != NULL)
++ {
++ while (getline (&line, &line_allocated, accessfp) >= 0)
++ {
++ int x;
++
++ if (line[0] == '#' || line[0] == '\0' || line[0] == '\n')
++ continue;
++
++ xline = xstrdup (line);
++ part_type = strtok (line, ":\t");
++ part_object = strtok (NULL, ":\t");
++ part_tag = strtok (NULL, ":\t");
++ part_perms = strtok (NULL, ":\t");
++
++ for (x = intcount; x >= signlevel; x--)
++ {
++ if (strcmp (dirs[x], part_object) == 0)
++ {
++ if (valid_tag (part_tag, tag))
++ {
++ foundline = 1;
++
++ if (listacl)
++ {
++ *mline = xstrdup (xline);
++ *mpos = ftell (accessfp);
++ }
++
++ if (valid_perm (part_perms, perm))
++ {
++ if (signlevel == x)
++ {
++ if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used)
++ retval = 1;
++ }
++ else if (!aclconfig_default_used)
++ {
++ signlevel = x;
++ retval = 1;
++ }
++ }
++ else
++ {
++ if (signlevel == x)
++ {
++ if (strncmp(part_tag, "ALL", 3) != 0 && !aclconfig_default_used)
++ retval = 0;
++ }
++ else if (!aclconfig_default_used)
++ {
++ signlevel = x;
++ retval = 0;
++
++ if (strncmp (part_type, "f", 1) == 0)
++ founddeniedfile = 1;
++
++ }
++ }
++ }
++ }
++ }
++ if (strncmp (xline, "d:ALL", 5) == 0 && (!groupfound && !userfound || listacl))
++ {
++ /* a default found */
++ if (valid_tag (part_tag, tag))
++ {
++ foundline = 1;
++
++ default_part_perms_accessfile = xstrdup (part_perms);
++
++ if (valid_perm (part_perms, perm))
++ {
++ retval = 1;
++
++ if (perm == 8)
++ dadmin = 1;
++ }
++ else
++ retval = 0;
++ }
++ }
++
++ }
++ if (fclose (accessfp) == EOF)
++ error (1, errno, "cannot close 'access' file");
++
++ }
++ }
++
++ if (!foundline)
++ {
++ /* DEFAULT */
++ if (valid_perm (NULL, perm))
++ retval = 1;
++ else
++ retval = 0;
++ }
++
++ if (dadmin)
++ {
++ retval = dadmin;
++ }
++
++ cache_retval = retval;
++
++ free (filefullname);
++
++ return (retval);
++}
++
++/* Returns 1 if successful, 0 if not. */
++int
++valid_tag (part_tag, tag)
++char *part_tag;
++char *tag;
++{
++ if (tag == NULL)
++ tag = xstrdup ("HEAD");
++
++ if (strcmp (part_tag, "ALL") == 0)
++ return (1);
++
++ if (strcmp (tag, part_tag) == 0)
++ return (1);
++ else
++ return (0);
++}
++
++/* Returns 1 if successful, 0 if not. */
++int
++valid_perm (part_perms, perm)
++char *part_perms;
++int perm;
++{
++ char *perms;
++ int retval = 0;
++
++ perms = get_perms (part_perms);
++
++ /* Allow, if nothing found. */
++ if (perms[0] == '\0')
++ return (1);
++
++
++ if (strstr (perms, "n"))
++ retval = 0; /* no access allowed, exit */
++ if (strstr (perms, "p"))
++ retval = 1; /* admin rights */
++ else if (strstr (perms, "a") && perm != 8)
++ retval = 1; /* all access allowed, exit */
++ else
++ switch (perm)
++ {
++ case 3: /* write permission */
++ if (strstr (perms, "w"))
++ retval = 1;
++ break;
++ case 4: /* tag permission */
++ if (strstr (perms, "t"))
++ retval = 1;
++ break;
++ case 5: /* read permission */
++ if (strstr (perms, "w") || strstr (perms, "t") || strstr (perms, "c") ||
++ strstr (perms, "d") || strstr (perms, "r"))
++ retval = 1;
++ break;
++ case 6: /* create permission */
++ if (strstr (perms, "c"))
++ retval = 1;
++ break;
++ case 7: /* delete permission */
++ if (strstr (perms, "d"))
++ retval = 1;
++ break;
++ case 8: /* permission change */
++ if (strstr (perms, "p"))
++ retval = 1;
++ break;
++ default:
++ retval = 0; /* never reached */
++ break;
++ }
++
++ return (retval);
++}
++
++char *
++get_perms (part_perms)
++char *part_perms;
++
++{
++ char *username;
++ char *xperms;
++ size_t xperms_len = 1;
++
++ FILE *groupfp;
++
++ char *usr;
++ char *per;
++ char *founduser = NULL;
++ char *foundall = NULL;
++ int default_checked = 0;
++
++ aclconfig_default_used = 0;
++
++ xperms = xmalloc (xperms_len);
++ xperms[0] = '\0';
++
++ username = getcaller ();
++
++ /* no defined acl, no default acl in access file,
++ or no access file at all */
++ if (part_perms == NULL)
++ if (cvs_acl_default_permissions)
++ {
++ aclconfig_default_used = 1;
++ return (cvs_acl_default_permissions);
++ }
++ else
++ return (xperms);
++
++check_default:
++ founduser = strstr (part_perms, username);
++ foundall = strstr (part_perms, "ALL!");
++
++ if (founduser)
++ {
++ usr = strtok (founduser, "!\t");
++ per = strtok (NULL, ",\t");
++
++ if (strcmp (usr, username) == 0)
++ {
++ xperms = xstrdup (per);
++ xperms_len = strlen (xperms);
++
++ userfound = 1;
++ }
++ }
++ else
++ {
++ if (use_system_groups) {
++ struct group *griter;
++ setgrent ();
++ while (griter = getgrent ()) {
++ char **users=griter->gr_mem;
++ int index = 0;
++ char *userchk = users [index++];
++ while(userchk != NULL) {
++ if(strcmp (userchk, username) == 0)
++ break;
++ userchk = users[index++];
++ }
++ if (userchk != NULL) {
++ char *grp;
++ if ((grp = strstr (part_perms, griter->gr_name)) && grp[strlen (griter->gr_name)] == '!') {
++ char *gperm = strtok (grp, "!\t");
++ gperm = strtok (NULL, ",\t");
++ xrealloc_and_strcat (&xperms, &xperms_len, gperm);
++
++ groupfound = 1;
++ }
++ }
++ }
++ endgrent ();
++ }
++ else if (use_cvs_groups) {
++ groupfp = open_groupfile ("r");
++ if (groupfp != NULL)
++ {
++ char *line = NULL;
++ size_t line_allocated = 0;
++
++ while (getline (&line, &line_allocated, groupfp) >= 0)
++ {
++ if (strstr (line, username))
++ {
++ char *temp;
++ temp = strstr (line, username);
++
++ if (temp[strlen (username)] == ','
++ || temp[strlen (username)] == ' '
++ || temp[strlen (username)] == '\n')
++ {
++ char *tmp;
++ tmp = strtok (line, ":\t");
++ if (strcmp (tmp, username) != 0)
++ {
++ char *grp;
++ if ((grp = strstr (part_perms, tmp)))
++ if (grp[strlen (tmp)] == '!')
++ {
++ char *gperm;
++ gperm = strtok (grp, "!\t");
++ gperm = strtok (NULL, ",\t");
++
++ xrealloc_and_strcat (&xperms, &xperms_len, gperm);
++
++ groupfound = 1;
++ }
++ }
++ }
++ }
++ }
++ if (fclose (groupfp) == EOF)
++ error (1, errno, "cannot close 'group' file");
++ }
++ }
++ }
++
++ if (foundall && (!groupfound && !userfound))
++ {
++ usr = strtok (strstr (part_perms, "ALL!"), "!\t");
++ per = strtok (NULL, ",\t");
++
++ if (!default_checked)
++ default_perms_object = xstrdup (per);
++
++ if (xperms[0] == '\0')
++ {
++ xperms = xstrdup (per);
++ xperms_len = strlen (xperms);
++ }
++ }
++ else if (xperms[0] == '\0' && !default_checked && default_part_perms_accessfile)
++ {
++ part_perms = xstrdup (default_part_perms_accessfile);
++ default_checked = 1;
++
++ goto check_default;
++ }
++
++ if (xperms[0] != '\0' && strcmp (xperms, "x") == 0)
++ {
++ if (default_perms_object)
++ xperms = xstrdup (default_perms_object);
++ else if (default_part_perms_accessfile)
++ {
++ part_perms = default_part_perms_accessfile;
++ default_checked = 1;
++ goto check_default;
++ }
++ else if (cvs_acl_default_permissions)
++ {
++ aclconfig_default_used = 1;
++ xperms = xstrdup (cvs_acl_default_permissions);
++ }
++ }
++
++ if (xperms[0] == '\0' && cvs_acl_default_permissions)
++ {
++ aclconfig_default_used = 1;
++ xperms = xstrdup (cvs_acl_default_permissions);
++ }
++
++ return (xperms);
++}
++
++
++int
++cvsacl (argc, argv)
++int argc;
++char **argv;
++{
++ char *chdirrepository;
++ int c;
++ int err = 0;
++ int usetag = 0;
++ int recursive = 0;
++
++ int k;
++
++ int which;
++ char *where;
++
++ is_racl = (strcmp (cvs_cmd_name, "racl") == 0);
++
++ if (argc == -1)
++ usage (is_racl ? racl_usage : acl_usage);
++
++ /* parse the args */
++ optind = 0;
++
++ while ((c = getopt (argc, argv, "Rr:dfl")) != -1)
++ {
++ switch (c)
++ {
++ case 'R':
++ recursive = 1;
++ break;
++ case 'r':
++ tag = xstrdup (optarg);
++ break;
++ case 'd':
++ acldir = 1;
++ break;
++ case 'f':
++ aclfile = 1;
++ break;
++ case 'l':
++ listacl = 1;
++ break;
++ case '?':
++ default:
++ usage (is_racl ? racl_usage : acl_usage);
++ break;
++ }
++ }
++
++ argc -= optind;
++ argv += optind;
++
++ if (!acldir && !aclfile)
++ usage (is_racl ? racl_usage : acl_usage);
++ if (acldir && aclfile)
++ usage (is_racl ? racl_usage : acl_usage);
++
++ if (listacl)
++ if (strstr (argv[0], ":"))
++ usage (is_racl ? racl_usage : acl_usage);
++ if (!listacl)
++ if (!strstr (argv[0], ":"))
++ usage (is_racl ? racl_usage : acl_usage);
++
++ if (argc < (is_racl ? 1 : 1))
++ usage (is_racl ? racl_usage : acl_usage);
++
++#ifdef CLIENT_SUPPORT
++
++ if (current_parsed_root->isremote)
++ {
++ start_server ();
++ ign_setup ();
++
++ if(recursive)
++ send_arg ("-R");
++
++ if (acldir)
++ send_arg ("-d");
++
++ if (aclfile)
++ send_arg ("-f");
++
++ if (listacl)
++ send_arg ("-l");
++
++ if(tag)
++ {
++ send_arg ("-r");
++ send_arg (tag);
++ }
++
++ send_arg ("--");
++
++ if (!listacl)
++ {
++ send_arg (argv[0]);
++
++ argc--;
++ argv++;
++ }
++
++ if (is_racl)
++ {
++ int i;
++ for (i = 0; i < argc; ++i)
++ send_arg (argv[i]);
++
++ send_to_server ("racl\012",0);
++ }
++ else
++ {
++ send_files (argc, argv, recursive, 0, SEND_NO_CONTENTS);
++ send_file_names (argc, argv, SEND_EXPAND_WILD);
++ send_to_server ("acl\012", 0);
++ }
++
++ return get_responses_and_close ();
++ }
++#endif
++
++#ifdef SERVER_SUPPORT
++
++ if (!listacl)
++ {
++ muser = strtok (argv[0], ":\t");
++ mperms = strtok (NULL, ":\t");
++
++ /* if set to 'default' */
++ if ((strlen (mperms) == 7) && (strncmp (mperms, "default", 7) == 0))
++ mperms = xstrdup ("x");
++
++ /* Check that the given permissions are valid. */
++ if (!given_perms_valid (mperms))
++ error (1,0,"Invalid permissions: '%s'", mperms);
++
++ argc--;
++ argv++;
++ }
++
++
++ if (!tag)
++ tag = xstrdup ("HEAD");
++
++ if (!cvs_casecmp (argv[0], "ALL"))
++ {
++ argv[0] = xstrdup (".");
++ defaultperms = 1;
++ if (!use_seperate_acl_file_for_each_dir)
++ {
++ recursive = 0;
++ }
++
++ }
++
++ if (is_racl)
++ {
++ DBM *db;
++ int i;
++ db = open_module ();
++ for (i = 0; i < argc; i++)
++ {
++ err += do_module (db, argv[i], MISC, "ACL ing: ",
++ racl_proc, (char *) NULL, 0, !recursive, 0,
++ 0, NULL);
++ }
++ close_module (db);
++ }
++ else
++ {
++ err = racl_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, !recursive, NULL,
++ NULL);
++ }
++ if (err)
++ error (1, 0, "an error accured");
++
++ return (err);
++
++#endif
++}
++
++static int
++racl_proc (argc, argv, xwhere, mwhere, mfile, shorten, local, mname, msg)
++int argc;
++char **argv;
++char *xwhere;
++char *mwhere;
++char *mfile;
++int shorten;
++int local;
++char *mname;
++char *msg;
++{
++ char *myargv[2];
++ int err = 0;
++ int which;
++ char *repository;
++ char *where;
++
++ if (is_racl)
++ {
++ repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
++ + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
++
++ (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
++ where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
++ + 1);
++ (void) strcpy (where, argv[0]);
++
++ /* if mfile isn't null, we need to set up to do only part of the module */
++ if (mfile != NULL)
++ {
++ char *cp;
++ char *path;
++
++ /* if the portion of the module is a path, put the dir part on repos */
++ if ((cp = strrchr (mfile, '/')) != NULL)
++ {
++ *cp = '\0';
++ (void) strcat (repository, "/");
++ (void) strcat (repository, mfile);
++ (void) strcat (where, "/");
++ (void) strcat (where, mfile);
++ mfile = cp + 1;
++ }
++
++ /* take care of the rest */
++ path = xmalloc (strlen (repository) + strlen (mfile) + 5);
++ (void) sprintf (path, "%s/%s", repository, mfile);
++ if (isdir (path))
++ {
++ /* directory means repository gets the dir tacked on */
++ (void) strcpy (repository, path);
++ (void) strcat (where, "/");
++ (void) strcat (where, mfile);
++ }
++ else
++ {
++ myargv[0] = argv[0];
++ myargv[1] = mfile;
++ argc = 2;
++ argv = myargv;
++ }
++ free (path);
++ }
++
++ /* cd to the starting repository */
++ if ( CVS_CHDIR (repository) < 0)
++ {
++ error (0, errno, "cannot chdir to %s", repository);
++ free (repository);
++ return (1);
++ }
++
++ /* End section which is identical to patch_proc. */
++
++ which = W_REPOS | W_ATTIC;
++ }
++ else
++ {
++ where = NULL;
++ which = W_LOCAL | W_REPOS | W_ATTIC;
++ }
++ if (listacl)
++ err = start_recursion (acllist_fileproc, NULL, acllist_dirproc, NULL, NULL,
++ argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1,
++ repository);
++ else
++ err = start_recursion (acl_fileproc, NULL, acl_dirproc, NULL, NULL,
++ argc - 1, argv + 1, local, which, 0, 0, (char *) where, 1,
++ repository);
++
++ return (err);
++}
++
++
++static int
++acl_fileproc (callerdat, finfo)
++void *callerdat;
++struct file_info *finfo;
++{
++ Vers_TS *vers;
++
++ FILE *accessfp;
++
++ char *filefullname;
++
++ char *founduserpart = NULL;
++ char *newuserpart = NULL;
++ char *otheruserparts = NULL;
++ size_t otherslen = 0;
++
++ const char *frepository;
++ int foundline = 0;
++
++ char *line = NULL;
++ size_t line_allocated = 0;
++ int linelen;
++
++ char *part_type = NULL;
++ char *part_object = NULL;
++ char *part_tag = NULL;
++ char *part_perms = NULL;
++ char *wperms;
++ char *userpart;
++
++ char *errmsg;
++
++ int pos;
++
++ if (!aclfile)
++ return (0);
++
++ frepository = Short_Repository (finfo->repository);
++
++ filefullname = xmalloc (strlen (frepository)
++ + strlen (finfo->file)
++ + 2);
++ strcpy (filefullname, frepository);
++ strcat (filefullname, "/");
++ strcat (filefullname, finfo->file);
++
++ if (!access_allowed (finfo->file, finfo->repository, tag, 8, &line, &pos, 0))
++ error (1,0,"You do not have acl admin rights on '%s'", frepository);
++
++ if (line != NULL)
++ {
++ part_type = strtok (line, ":\t");
++ part_object = strtok (NULL, ":\t");
++ part_tag = strtok (NULL, ":\t");
++ part_perms = strtok (NULL, ":\t");
++
++ foundline = 1;
++ userpart = strtok (part_perms, ",\t");
++
++ if (strstr (userpart, muser))
++ founduserpart = xstrdup (userpart);
++ else
++ {
++ otheruserparts = xstrdup (userpart);
++ otherslen = strlen (otheruserparts);
++ }
++
++ while ((userpart = strtok (NULL, ",\t")) != NULL)
++ {
++ if (strncmp (userpart, muser, strlen (muser)) == 0)
++ founduserpart = xstrdup (userpart);
++ else
++ {
++ if (otheruserparts != NULL)
++ {
++ xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
++ xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
++ }
++ else
++ {
++ otheruserparts = xstrdup (userpart);
++ otherslen = strlen (otheruserparts);
++ }
++ }
++ }
++ }
++
++ wperms = make_perms (mperms, founduserpart, &errmsg);
++ if (wperms == NULL)
++ {
++ if (errmsg)
++ error (0, 0, "%s %s", filefullname, errmsg);
++
++ return (0);
++ }
++ else
++ {
++ cvs_output ("X ", 0);
++ cvs_output (filefullname, 0);
++ cvs_output ("\n", 0);
++
++ write_perms (muser, wperms, founduserpart, foundline,
++ otheruserparts, "f", filefullname, tag, pos, finfo->repository);
++ }
++
++ return (0);
++}
++
++static Dtype
++acl_dirproc (callerdat, dir, repos, update_dir, entries)
++void *callerdat;
++const char *dir;
++const char *repos;
++const char *update_dir;
++List *entries;
++{
++ const char *drepository;
++ char *founduserpart = NULL;
++ char *newuserpart = NULL;
++ char *otheruserparts = NULL;
++ size_t otherslen = 0;
++ int foundline = 0;
++
++ char *line = NULL;
++ size_t line_allocated = 0;
++ int linelen;
++
++ FILE *accessfp;
++ char *part_type = NULL;
++ char *part_object = NULL;
++ char *part_tag = NULL;
++ char *part_perms = NULL;
++ char *wperms;
++ int i = 0;
++ char *userpart;
++ int pos;
++
++ char *errmsg;
++
++ if (repos[0] == '\0')
++ repos = Name_Repository (dir, NULL);
++
++ if (!acldir)
++ return (0);
++
++ if (!access_allowed (NULL, repos, tag, 8, &line, &pos, 0))
++ error (1,0,"You do not have admin rights on '%s'", Short_Repository (repos));
++
++ drepository = Short_Repository (repos);
++
++ if (line != NULL)
++ {
++ part_type = strtok (line, ":\t");
++ part_object = strtok (NULL, ":\t");
++ part_tag = strtok (NULL, ":\t");
++ part_perms = strtok (NULL, ":\t");
++
++ foundline = 1;
++ userpart = strtok (part_perms, ",\t");
++
++ if (strstr (userpart, muser))
++ founduserpart = xstrdup (userpart);
++ else
++ {
++ otheruserparts = xstrdup (userpart);
++ otherslen = strlen (otheruserparts);
++ }
++
++ while ((userpart = strtok (NULL, ",\t")) != NULL)
++ {
++ if (strncmp (userpart, muser, strlen (muser)) == 0)
++ founduserpart = xstrdup (userpart);
++ else
++ {
++ if (otheruserparts != NULL)
++ {
++ xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
++ xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
++ }
++ else
++ {
++ otheruserparts = xstrdup (userpart);
++ otherslen = strlen (otheruserparts);
++ }
++ }
++ }
++ }
++
++ wperms = make_perms (mperms, founduserpart, &errmsg);
++ if (wperms == NULL)
++ {
++ if (errmsg)
++ error (0, 0, "%s %s", drepository, errmsg);
++
++ return (0);
++ }
++ else
++ {
++ if (defaultperms)
++ {
++ cvs_output ("X ", 0);
++ cvs_output ("ALL", 0);
++ cvs_output ("\n", 0);
++ write_perms (muser, wperms, founduserpart, foundline,
++ otheruserparts, "d", "ALL", tag, pos, repos);
++
++ }
++ else
++ {
++ cvs_output ("X ", 0);
++ cvs_output (drepository, 0);
++ cvs_output ("\n", 0);
++ write_perms (muser, wperms, founduserpart, foundline,
++ otheruserparts, "d", drepository, tag, pos, repos);
++ }
++ }
++
++ return (0);
++}
++
++/* Open CVSROOT/access or defined CVSACLFileLocation file. */
++FILE *
++open_accessfile (fmode, adir, fname)
++char *fmode;
++char *adir;
++char **fname;
++{
++ char *accessfile;
++ FILE *accessfp;
++
++ if (!use_seperate_acl_file_for_each_dir)
++ {
++ if (cvs_acl_file_location == NULL)
++ {
++ accessfile = xmalloc (strlen (current_parsed_root->directory)
++ + sizeof (CVSROOTADM)
++ + sizeof (CVSROOTADM_ACCESS)
++ + 3);
++
++ strcpy (accessfile, current_parsed_root->directory);
++ strcat (accessfile, "/");
++ strcat (accessfile, CVSROOTADM);
++ strcat (accessfile, "/");
++ strcat (accessfile, CVSROOTADM_ACCESS);
++ }
++ else
++ {
++ accessfile = xmalloc (strlen (cvs_acl_file_location));
++ strcpy (accessfile, cvs_acl_file_location);
++ }
++ }
++ else
++ {
++ accessfile = xmalloc (strlen (current_parsed_root->directory)
++ + strlen (adir)
++ + strlen ("access")
++ + 3);
++
++ strcpy (accessfile, current_parsed_root->directory);
++ strcat (accessfile, "/");
++ strcat (accessfile, adir);
++ strcat (accessfile, "/");
++ strcat (accessfile, "access");
++ }
++
++ accessfp = CVS_FOPEN (accessfile, fmode);
++
++ if (accessfp == NULL)
++ error (0, 0, "cannot open file: %s", accessfile);
++
++ if (fname != NULL)
++ *fname = xstrdup (accessfile);
++
++ free (accessfile);
++
++ return (accessfp);
++}
++
++/* Open /etc/group file if UseSystemGroups=yes in config file. */
++/* Open CVSROOT/group file if UseCVSGroups=yes in config file. */
++FILE *
++open_groupfile (fmode)
++char *fmode;
++{
++ char *groupfile;
++ FILE *groupfp;
++
++ if (use_cvs_groups)
++ {
++ if (cvs_groups_file_location != NULL)
++ {
++ groupfile = xmalloc (strlen (cvs_groups_file_location));
++ strcpy (groupfile, cvs_groups_file_location);
++ }
++ else
++ {
++ groupfile = xmalloc (strlen (current_parsed_root->directory)
++ + sizeof (CVSROOTADM)
++ + sizeof (CVSROOTADM_GROUP)
++ + 3);
++
++ strcpy (groupfile, current_parsed_root->directory);
++ strcat (groupfile, "/");
++ strcat (groupfile, CVSROOTADM);
++ strcat (groupfile, "/");
++ strcat (groupfile, CVSROOTADM_GROUP);
++ }
++ }
++
++ else
++ {
++ return (NULL);
++ }
++
++ groupfp = CVS_FOPEN (groupfile, "r");
++
++ if (groupfp == NULL)
++ error (0, 0, "cannot open file: %s", groupfile);
++
++ free (groupfile);
++
++ return (groupfp);
++}
++
++
++/* Check whether given permissions are valid or not. */
++/* Returns 1 if permissions are valid. */
++/* Returns 0 if permissions are NOT valid. */
++int
++given_perms_valid (cperms)
++char *cperms;
++{
++ int cperms_len;
++ int retval;
++ int index, i;
++
++ if (cperms[0] == '+' || cperms[0] == '-')
++ index = 1;
++ else
++ index = 0;
++
++ cperms_len = strlen (cperms);
++
++ switch (cperms[index])
++ {
++ case 'x':
++ if ((cperms_len - index) == 1 && cperms_len == 1)
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'n':
++ if ((cperms_len - index) == 1 && cperms_len == 1)
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'p':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'a':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 'p')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'r':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'w':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 't':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 'w' || cperms[i] == 'c' || cperms[i] == 'd')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'c':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 't' || cperms[i] == 'w' || cperms[i] == 'd')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ case 'd':
++ if ((cperms_len - index) == 1)
++ retval = 1;
++ else
++ for (i = index + 1; i < cperms_len; i++)
++ if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'w')
++ retval = 1;
++ else
++ retval = 0;
++ break;
++ default:
++ retval = 0;
++ break;
++ }
++ return (retval);
++}
++
++char *
++make_perms (perms, founduserpart, xerrmsg)
++char *perms;
++char *founduserpart;
++char **xerrmsg;
++{
++ char *fperms;
++ size_t perms_len;
++ size_t fperms_len;
++ char *retperms = NULL;
++ char *xperms;
++ int i, j;
++ int err = 0;
++ char *errmsg = NULL;
++ size_t retperms_len = 1;
++
++ retperms = xmalloc (retperms_len);
++ retperms[0] = '\0';
++
++ perms_len = strlen (perms);
++
++ if (perms[0] == '+' || perms[0] == '-')
++ {
++ if (founduserpart)
++ {
++ char *temp;
++ temp = strtok (founduserpart, "!\t");
++ fperms = strtok (NULL, "!\t");
++ fperms_len = strlen (fperms);
++
++ if (strncmp (fperms, "x", 1) == 0)
++ {
++ err = 1;
++ if (perms[0] == '+')
++ *xerrmsg = xstrdup ("cannot add default permission 'x'");
++ else
++ *xerrmsg = xstrdup ("cannot remove default permission 'x'");
++ }
++
++ for (i = 1; i < perms_len && !err; i++)
++ {
++ switch (perms[i])
++ {
++ case 'n':
++ err = 1;
++ break;
++ case 'p':
++ if (perms[0] == '+')
++ fperms = xstrdup ("p");
++ else if (perms[0] == '-')
++ {
++ fperms_len = 1;
++ fperms = xmalloc (fperms_len);
++ fperms[0] = '\0';
++ }
++ break;
++ case 'a':
++ for (j = 0; j < fperms_len; j++)
++ {
++ if (fperms[j] == 'p')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has admin rights, cannot use +/- permissions");
++ }
++ else if (fperms[j] == 'a' && perms[0] == '+')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user already has all ('a') permission");
++ }
++ else if (fperms[j] != 'a' && perms[0] == '-')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user does not have all ('a') permission");
++ }
++ }
++ if (perms[0] == '+')
++ {
++ fperms = xstrdup ("a");
++ fperms_len = strlen (fperms);
++ }
++ else if (perms[0] == '-')
++ {
++ fperms_len = 1;
++ fperms = xmalloc (fperms_len);
++ fperms[0] = '\0';
++ }
++
++ break;
++ case 'r':
++ for (i = 0; i < fperms_len; i++)
++ {
++ if (fperms[i] == 'n' && perms[0] == '+')
++ {
++ fperms = xstrdup ("r");
++ fperms_len = strlen (fperms);
++ }
++ else if (fperms[i] == 'r' && perms[0] == '-')
++ {
++ fperms_len = 1;
++ fperms = xmalloc (fperms_len);
++ fperms[0] = '\0';
++ }
++ else if (perms[0] == '-')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission");
++ }
++ else
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has other permissions, cannot remove read ('r') permission");
++ }
++ }
++ break;
++ case 'w':
++ {
++ char *tempfperms;
++ size_t tempfperms_len = 1;
++
++ tempfperms = xmalloc (tempfperms_len);
++ tempfperms[0] = '\0';
++
++ for (j = 0; j < fperms_len; j++)
++ {
++ if (fperms[j] == 't' || fperms[j] == 'c' || fperms[j] == 'd')
++ {
++ char *temp;
++ temp = xmalloc (2);
++ temp[0] = fperms[j];
++ temp[1] = '\0';
++
++ xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
++ free (temp);
++ }
++ else if (fperms[j] == 'a' || fperms[j] == 'p')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- write permissions");
++ }
++ else if (fperms[j] == 'n' || fperms[j] == 'r')
++ {
++ if (perms[0] == '-')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user does not have write ('w') permission");
++ }
++ }
++ }
++
++ fperms = xstrdup (tempfperms);
++ fperms_len = strlen (fperms);
++ free (tempfperms);
++
++ if (perms[0] == '+')
++ {
++ xrealloc_and_strcat (&fperms, &fperms_len, "w");
++ }
++ }
++ break;
++ case 't':
++ {
++ char *tempfperms;
++ size_t tempfperms_len = 1;
++
++ tempfperms = xmalloc (tempfperms_len);
++ tempfperms[0] = '\0';
++
++ for (j = 0; j < fperms_len; j++)
++ {
++ if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 'd')
++ {
++ char *temp;
++ temp = xmalloc (2);
++ temp[0] = fperms[j];
++ temp[1] = '\0';
++
++ xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
++ free (temp);
++ }
++ else if (fperms[j] == 'a' || fperms[j] == 'p')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- tag permissions");
++ }
++ else if (fperms[i] == 'n' || fperms[i] == 'r')
++ {
++ if (perms[0] == '-')
++ *xerrmsg = xstrdup ("user does not have tag ('t') permission");
++ }
++ }
++
++ fperms = xstrdup (tempfperms);
++ fperms_len = strlen (fperms);
++ free (tempfperms);
++
++ if (perms[0] == '+')
++ {
++ xrealloc_and_strcat (&fperms, &fperms_len, "t");
++ }
++ }
++ break;
++ case 'c':
++ {
++ char *tempfperms;
++ size_t tempfperms_len = 1;
++
++ tempfperms = xmalloc (tempfperms_len);
++ tempfperms[0] = '\0';
++
++ for (j = 0; j < fperms_len; j++)
++ {
++ if (fperms[j] == 'w' || fperms[j] == 't' || fperms[j] == 'd')
++ {
++ char *temp;
++ temp = xmalloc (2);
++ temp[0] = fperms[j];
++ temp[1] = '\0';
++
++ xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
++ free (temp);
++ }
++ else if (fperms[j] == 'a' || fperms[j] == 'p')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- create permissions");
++ }
++ else if (fperms[i] == 'n' || fperms[i] == 'r')
++ {
++ if (perms[0] == '-')
++ err = 1;
++ *xerrmsg = xstrdup ("user does not have create ('c') permission");
++ }
++
++ }
++
++ fperms = xstrdup (tempfperms);
++ fperms_len = strlen (fperms);
++ free (tempfperms);
++
++ if (perms[0] == '+')
++ {
++ xrealloc_and_strcat (&fperms, &fperms_len, "c");
++ }
++ }
++ break;
++ case 'd':
++ {
++ char *tempfperms;
++ size_t tempfperms_len = 1;
++
++ tempfperms = xmalloc (tempfperms_len);
++ tempfperms[0] = '\0';
++
++ for (j = 0; j < fperms_len; j++)
++ {
++ if (fperms[j] == 'w' || fperms[j] == 'c' || fperms[j] == 't')
++ {
++ char *temp;
++ temp = xmalloc (2);
++ temp[0] = fperms[j];
++ temp[1] = '\0';
++
++ xrealloc_and_strcat (&tempfperms, &tempfperms_len, temp);
++ free (temp);
++ }
++ else if (fperms[j] == 'a' || fperms[j] == 'p')
++ {
++ err = 1;
++ *xerrmsg = xstrdup ("user has higher permissions, cannot use +/- delete permissions");
++ }
++ else if (fperms[i] == 'n' || fperms[i] == 'r')
++ {
++ if (perms[0] == '-')
++ err = 1;
++ *xerrmsg = xstrdup ("user does not have delete ('d') permission");
++ }
++ }
++
++ fperms = xstrdup (tempfperms);
++ fperms_len = strlen (fperms);
++ free (tempfperms);
++
++ if (perms[0] == '+')
++ {
++ xrealloc_and_strcat (&fperms, &fperms_len, "d");
++ }
++ }
++ break;
++ default:
++ err = 1;
++ *xerrmsg = xstrdup ("error in 'access' file format");
++ break;
++ }
++ if (fperms[0] == '\0')
++ retperms = xstrdup ("none");
++ else
++ retperms = xstrdup (fperms);
++ }
++ }
++ else
++ {
++ err = 1;
++ *xerrmsg = xstrdup("user is not given any permissions to remove/add");
++ }
++ }
++
++ else
++ {
++ retperms = xstrdup (perms);
++ }
++
++ if (err)
++ return (NULL);
++ else
++ return (retperms);
++}
++
++int
++write_perms (user, perms, founduserpart, foundline, otheruserparts,
++ part_type, part_object, part_tag, pos, arepos)
++char *user;
++char *perms;
++char *founduserpart;
++int foundline;
++char *otheruserparts;
++char *part_type;
++char *part_object;
++char *part_tag;
++int pos;
++char *arepos;
++{
++
++ char *accessfile;
++ char *tmpaccessout;
++ FILE *accessfpin;
++ FILE *accessfpout;
++
++ char *newline = NULL;
++ size_t newlinelen = 1;
++
++ char *line = NULL;
++ size_t line_allocated = 0;
++
++ newline = xmalloc (newlinelen);
++ newline[0] = '\0';
++
++ if (!cvs_casecmp (part_tag, "ALL"))
++ part_tag = xstrdup ("ALL");
++
++ xrealloc_and_strcat (&newline, &newlinelen, part_type);
++ xrealloc_and_strcat (&newline, &newlinelen, ":");
++ xrealloc_and_strcat (&newline, &newlinelen, part_object);
++ xrealloc_and_strcat (&newline, &newlinelen, ":");
++ xrealloc_and_strcat (&newline, &newlinelen, part_tag);
++ xrealloc_and_strcat (&newline, &newlinelen, ":");
++
++ if (strncmp (perms, "none", 4) != 0)
++ {
++ xrealloc_and_strcat (&newline, &newlinelen, user);
++ xrealloc_and_strcat (&newline, &newlinelen, "!");
++ xrealloc_and_strcat (&newline, &newlinelen, perms);
++ if (otheruserparts != NULL)
++ xrealloc_and_strcat (&newline, &newlinelen, ",");
++ }
++
++ if (otheruserparts != NULL)
++ {
++ if (otheruserparts[strlen (otheruserparts) - 1] == '\n')
++ otheruserparts[strlen (otheruserparts) - 1] = '\0';
++
++ xrealloc_and_strcat (&newline, &newlinelen, otheruserparts);
++ }
++
++ xrealloc_and_strcat (&newline, &newlinelen, ":");
++
++ if (foundline)
++ {
++ accessfpout = cvs_temp_file (&tmpaccessout);
++ accessfpin = open_accessfile ("r", arepos, &accessfile);
++
++ while (getline (&line, &line_allocated, accessfpin) >= 0)
++ {
++ if (pos != ftell (accessfpin))
++ {
++ if (fprintf (accessfpout, line) < 0)
++ error (1, errno, "writing temporary file: %s", tmpaccessout);
++ }
++ else
++ {
++ if (fprintf (accessfpout, "%s\n", newline) < 0)
++ error (1, errno, "writing temporary file: %s", tmpaccessout);
++ }
++
++ }
++ if (fclose (accessfpin) == EOF)
++ error (1, errno, "cannot close access file: %s", accessfile);
++
++ if (fclose (accessfpout) == EOF)
++ error (1, errno, "cannot close temporary file: %s", tmpaccessout);
++
++ if (CVS_UNLINK (accessfile) < 0)
++ error (0, errno, "cannot remove %s", accessfile);
++
++ copy_file (tmpaccessout, accessfile);
++
++ if (CVS_UNLINK (tmpaccessout) < 0)
++ error (0, errno, "cannot remove temporary file: %s", tmpaccessout);
++ }
++ else
++ {
++ accessfpout = open_accessfile ("r+", arepos, &accessfile);
++
++ if (accessfpout == NULL)
++ {
++ if (existence_error (errno))
++ {
++ accessfpout = open_accessfile ("w+", arepos, &accessfile);
++ }
++ }
++
++
++ if (fseek (accessfpout, 0, 2) != 0)
++ error (1, errno, "cannot fseek access file: %s", accessfile);
++
++ if (fprintf (accessfpout, "%s\n", newline) < 0)
++ error (1, errno, "writing access file: %s", accessfile);
++
++ if (fclose (accessfpout) == EOF)
++ error (1, errno, "cannot close access file: %s", accessfile);
++ }
++
++ free (newline);
++
++ chmod(accessfile, 0644);
++
++ return (0);
++}
++
++
++static int
++acllist_fileproc (callerdat, finfo)
++void *callerdat;
++struct file_info *finfo;
++{
++
++ char *filefullname;
++ const char *frepository;
++ char *line = NULL;
++ int pos;
++
++ if (!aclfile)
++ return (0);
++
++ frepository = Short_Repository (finfo->repository);
++
++ filefullname = xmalloc (strlen (frepository)
++ + strlen (finfo->file)
++ + 2);
++ strcpy (filefullname, frepository);
++ strcat (filefullname, "/");
++ strcat (filefullname, finfo->file);
++
++ if (!access_allowed (finfo->file, finfo->repository, tag, 5, &line, &pos, 0))
++ error (1,0,"You do not have admin rights on '%s'", frepository);
++
++ /*if (line == NULL) {
++ acldir = 1;
++ inherited = 1;
++ if (!access_allowed (NULL, finfo->repository, tag, 5, &line, &pos, 0))
++ error (1, 0, "You do not have admin rights on '%s'", frepository);
++ acldir = 0;
++ }
++*/
++ acllist_print (line, filefullname);
++
++ return (0);
++}
++
++static Dtype
++acllist_dirproc (callerdat, dir, repos, update_dir, entries)
++void *callerdat;
++const char *dir;
++const char *repos;
++const char *update_dir;
++List *entries;
++{
++
++ char *line = NULL;
++ const char *drepository;
++ int pos;
++
++ if (repos[0] == '\0')
++ repos = Name_Repository (dir, NULL);
++
++ if (!acldir)
++ return (0);
++
++ drepository = Short_Repository (repos);
++
++ if (!access_allowed (NULL, repos, tag, 5, &line, &pos, 0))
++ error (1, 0, "You do not have admin rights on '%s'", drepository);
++
++ acllist_print (line, drepository);
++
++ return (0);
++}
++
++
++void
++acllist_print (line, obj)
++char *line;
++const char *obj;
++{
++ char *temp;
++ char *temp2;
++ int x;
++ int c = 0;
++
++ char *printedusers[255];
++ printedusers[0] = NULL;
++
++ if (line != NULL)
++ {
++ temp = strtok (line, ":\t");
++
++ if (acldir)
++ cvs_output ("d ", 0);
++ else if (aclfile)
++ cvs_output ("f ", 0);
++
++ temp = strtok (NULL, ":\t");
++
++ cvs_output(obj, 0);
++ cvs_output (" | ", 0);
++
++ temp = strtok (NULL, ":\t");
++ cvs_output (temp, 0);
++ cvs_output (" | ", 0);
++
++ while ((temp = strtok (NULL, "!\t")) != NULL)
++ {
++ if (strncmp (temp, ":", 1) == 0)
++ break;
++
++ if (strcmp (temp, "ALL") == 0)
++ {
++ temp = strtok (NULL, ",\t");
++ continue;
++ }
++
++ cvs_output (temp, 0);
++ cvs_output (":", 0);
++
++ while (printedusers[c] != NULL)
++ c++;
++
++ printedusers[c] = xstrdup (temp);
++ c++;
++ printedusers[c] = NULL;
++
++ temp = strtok (NULL, ",\t");
++
++ if (temp != NULL && temp[strlen (temp) - 2] == ':')
++ temp[strlen (temp) - 2] = '\0';
++
++ cvs_output (temp, 0);
++ cvs_output (" ", 0);
++ }
++
++ if (default_perms_object)
++ {
++ cvs_output ("| defaults ", 0);
++ cvs_output ("ALL:", 0);
++ cvs_output (default_perms_object, 0);
++ }
++ else if (default_part_perms_accessfile)
++ {
++ size_t i;
++ i = strlen (default_part_perms_accessfile);
++ xrealloc_and_strcat (&default_part_perms_accessfile, &i, ",");
++
++ free(line);
++ line = xstrdup(default_part_perms_accessfile);
++
++ cvs_output ("| defaults ", 0);
++
++ temp = strtok (line, "!\t");
++ cvs_output (temp, 0);
++ cvs_output (":", 0);
++
++ temp = strtok (NULL, ",\t");
++
++ cvs_output (temp, 0);
++ cvs_output (" ", 0);
++
++ while ((temp = strtok (NULL, "!\t")) != NULL)
++ {
++ int printed = 0;
++ int c2 = 0;
++ while (printedusers[c2] != NULL && printed == 0)
++ {
++ if (strcmp (printedusers[c2], temp) == 0)
++ {
++ printed = 1;
++ break;
++ }
++ c2++;
++ }
++
++ if (printed == 0)
++ {
++ cvs_output (temp, 0);
++ cvs_output (":", 0);
++ }
++
++ temp = strtok (NULL, ",\t");
++
++ if (temp[strlen (temp) - 2] == ':')
++ temp[strlen (temp) - 2] = '\0';
++
++ if (printed == 0)
++ {
++ cvs_output (temp, 0);
++ cvs_output (" ", 0);
++ }
++ }
++ }
++ else if (cvs_acl_default_permissions)
++ {
++ cvs_output ("| defaults ", 0);
++ cvs_output ("ALL: ", 0);
++ cvs_output (cvs_acl_default_permissions, 0);
++ }
++
++ cvs_output ("\n", 0);
++
++ }
++ else
++ {
++ if (acldir)
++ cvs_output ("d ", 0);
++ else if (aclfile)
++ cvs_output ("f ", 0);
++ cvs_output (obj, 0);
++ cvs_output (" | ", 0);
++ cvs_output (tag, 0);
++ cvs_output (" | ", 0);
++
++ if (default_perms_object)
++ {
++ cvs_output ("| defaults ", 0);
++ cvs_output ("ALL:", 0);
++ cvs_output (default_perms_object, 0);
++ cvs_output ("\n", 0);
++ }
++ else if (default_part_perms_accessfile)
++ {
++ free(line);
++ line = xstrdup(default_part_perms_accessfile);
++
++ cvs_output ("| defaults ", 0);
++
++ temp = strtok (line, "!\t");
++ cvs_output (temp, 0);
++ cvs_output (":", 0);
++
++ temp = strtok (NULL, ",\t");
++
++ if (temp[strlen (temp) - 2] == ':')
++ temp[strlen (temp) - 2] = '\0';
++
++ cvs_output (temp, 0);
++ cvs_output (" ", 0);
++
++ while ((temp = strtok (NULL, "!\t")) != NULL)
++ {
++ cvs_output (temp, 0);
++ cvs_output (":", 0);
++
++ temp = strtok (NULL, ",\t");
++
++ if (temp[strlen (temp) - 2] == ':')
++ temp[strlen (temp) - 2] = '\0';
++
++ cvs_output (temp, 0);
++ cvs_output (" ", 0);
++ }
++ cvs_output ("\n", 0);
++ }
++ else if (cvs_acl_default_permissions)
++ {
++ cvs_output ("| defaults ", 0);
++ cvs_output ("ALL: ", 0);
++ cvs_output (cvs_acl_default_permissions, 0);
++ cvs_output ("\n", 0);
++ }
++ else
++ cvs_output ("default:p (no perms)\n", 0);
++ }
++
++}
+diff -urN cvs-1.11.15/src/add.c cvs-1.11.15-cvsacl/src/add.c
+--- cvs-1.11.15/src/add.c 2004-03-22 16:44:26.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/add.c 2004-04-25 20:04:51.687856256 +0200
+@@ -410,6 +410,24 @@
+ }
+ else
+ {
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo.file, repository, vers->tag, 6,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo.repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo.repository), finfo.file);
++
++ return (0);
++ }
++ }
++#endif
+ /* There is a user file, so build the entry for it */
+ if (build_entry (repository, finfo.file, vers->options,
+ message, entries, vers->tag) != 0)
+@@ -666,6 +684,26 @@
+ && isdir (finfo.file)
+ && !wrap_name_has (finfo.file, WRAP_TOCVS))
+ {
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (NULL, repository, NULL, 6,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo.repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo.repository), finfo.file);
++
++ return (0);
++ }
++ }
++#endif
++
+ err += add_directory (&finfo);
+ }
+ else
+diff -urN cvs-1.11.15/src/annotate.c cvs-1.11.15-cvsacl/src/annotate.c
+--- cvs-1.11.15/src/annotate.c 2004-03-22 16:44:27.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/annotate.c 2004-04-25 20:04:51.689855952 +0200
+@@ -276,6 +276,25 @@
+ if (version == NULL)
+ return 0;
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, version, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ /* Distinguish output for various files if we are processing
+ several files. */
+ cvs_outerr ("\nAnnotations for ", 0);
+diff -urN cvs-1.11.15/src/commit.c cvs-1.11.15-cvsacl/src/commit.c
+--- cvs-1.11.15/src/commit.c 2004-04-01 20:53:22.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/commit.c 2004-04-25 20:04:51.696854888 +0200
+@@ -1276,6 +1276,34 @@
+ return (0);
+
+ ci = p->data;
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ int whichperm;
++ if (ci->status == T_MODIFIED)
++ whichperm = 3;
++ else if (ci->status == T_ADDED)
++ whichperm = 6;
++ else if (ci->status == T_REMOVED)
++ whichperm = 7;
++
++ if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ if (ci->status == T_MODIFIED)
+ {
+ if (finfo->rcs == NULL)
+diff -urN cvs-1.11.15/src/cvs.h cvs-1.11.15-cvsacl/src/cvs.h
+--- cvs-1.11.15/src/cvs.h 2004-04-01 20:53:22.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/cvs.h 2004-04-25 20:05:38.416752384 +0200
+@@ -191,6 +191,11 @@
+ #define CVSROOTADM_PASSWD "passwd"
+ #define CVSROOTADM_CONFIG "config"
+
++/* cvsacl patch */
++#define CVSROOTADM_ACLCONFIG "aclconfig"
++#define CVSROOTADM_ACCESS "access"
++#define CVSROOTADM_GROUP "group"
++
+ #define CVSNULLREPOS "Emptydir" /* an empty directory */
+
+ /* Other CVS file names */
+@@ -567,6 +572,18 @@
+ /* LockDir setting from CVSROOT/config. */
+ extern char *lock_dir;
+
++/* cvsacl patch */
++/* ACL Patch settings from CVSROOT/config */
++extern int use_cvs_acl;
++extern char *cvs_acl_default_permissions;
++extern int use_cvs_groups;
++extern int use_system_groups;
++extern int use_seperate_acl_file_for_each_dir;
++extern char *cvs_acl_file_location;
++extern char *cvs_groups_file_location;
++extern char *cvs_server_run_as;
++extern int stop_at_first_permission_denied;
++
+ void Scratch_Entry PROTO((List * list, const char *fname));
+ void ParseTag PROTO((char **tagp, char **datep, int *nonbranchp));
+ void WriteTag PROTO ((const char *dir, const char *tag, const char *date,
+@@ -863,6 +880,10 @@
+ int editors PROTO ((int argc, char **argv));
+ int watchers PROTO ((int argc, char **argv));
+ extern int annotate PROTO ((int argc, char **argv));
++
++/* cvsacl patch */
++extern int cvsacl PROTO ((int argc, char **argv));
++
+ extern int add PROTO ((int argc, char **argv));
+ extern int admin PROTO ((int argc, char **argv));
+ extern int checkout PROTO ((int argc, char **argv));
+diff -urN cvs-1.11.15/src/diff.c cvs-1.11.15-cvsacl/src/diff.c
+--- cvs-1.11.15/src/diff.c 2004-03-20 03:06:45.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/diff.c 2004-04-25 20:04:51.708853064 +0200
+@@ -474,6 +474,43 @@
+ {
+ /* Skip all the following checks regarding the user file; we're
+ not using it. */
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (diff_rev1)
++ {
++ if (!access_allowed (NULL, finfo->repository, diff_rev1, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++ if (diff_rev2)
++ {
++ if (!access_allowed (NULL, finfo->repository, diff_rev2, 5))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++ }
++#endif
++
+ }
+ else if (vers->vn_user == NULL)
+ {
+@@ -827,6 +864,42 @@
+ if (!isdir (dir))
+ return (R_SKIP_ALL);
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (diff_rev1)
++ {
++ if (!access_allowed (NULL, update_dir, diff_rev1, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (update_dir));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (update_dir), update_dir);
++
++ return (0);
++ }
++ }
++ if (diff_rev2)
++ {
++ if (!access_allowed (NULL, update_dir, diff_rev2, 5))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (update_dir));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (update_dir), update_dir);
++
++ return (0);
++ }
++ }
++ }
++#endif
++
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return (R_PROCESS);
+diff -urN cvs-1.11.15/src/import.c cvs-1.11.15-cvsacl/src/import.c
+--- cvs-1.11.15/src/import.c 2004-04-02 20:55:49.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/import.c 2004-04-25 20:04:51.714852152 +0200
+@@ -304,6 +304,20 @@
+ error (1, 0, "attempt to import the repository");
+ }
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (NULL, repository, argv[1], 6, NULL, NULL, 1))
++ {
++ error (stop_at_first_permission_denied, 0, "permission denied for %s",
++ Short_Repository (repository));
++
++ return (0);
++ }
++ }
++#endif
++
+ /*
+ * Make all newly created directories writable. Should really use a more
+ * sophisticated security mechanism here.
+diff -urN cvs-1.11.15/src/log.c cvs-1.11.15-cvsacl/src/log.c
+--- cvs-1.11.15/src/log.c 2004-04-06 20:54:52.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/log.c 2004-04-25 20:04:51.722850936 +0200
+@@ -837,6 +837,25 @@
+ return 1;
+ }
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, NULL, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ if (log_data->sup_header || !log_data->nameonly)
+ {
+
+diff -urN cvs-1.11.15/src/main.c cvs-1.11.15-cvsacl/src/main.c
+--- cvs-1.11.15/src/main.c 2004-03-22 16:44:27.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/main.c 2004-04-25 20:04:51.726850328 +0200
+@@ -104,6 +104,10 @@
+ } cmds[] =
+
+ {
++ /* cvsacl patch */
++ { "acl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
++ { "racl", NULL, NULL, cvsacl, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
++
+ { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
+ { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
+ { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR },
+@@ -974,6 +978,9 @@
+ if we didn't, then there would be no way to check in a new
+ CVSROOT/config file to fix the broken one! */
+ parse_config (current_parsed_root->directory);
++
++ /* cvsacl patch */
++ parse_aclconfig (current_parsed_root->directory);
+ }
+
+ #ifdef CLIENT_SUPPORT
+diff -urN cvs-1.11.15/src/Makefile.am cvs-1.11.15-cvsacl/src/Makefile.am
+--- cvs-1.11.15/src/Makefile.am 2004-02-03 20:05:36.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/Makefile.am 2004-04-25 20:04:51.728850024 +0200
+@@ -29,6 +29,7 @@
+
+ # The cvs executable
+ cvs_SOURCES = \
++ acl.c \
+ add.c \
+ admin.c \
+ annotate.c \
+diff -urN cvs-1.11.15/src/Makefile.in cvs-1.11.15-cvsacl/src/Makefile.in
+--- cvs-1.11.15/src/Makefile.in 2004-04-14 04:42:00.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/Makefile.in 2004-04-25 20:04:51.732849416 +0200
+@@ -153,6 +153,7 @@
+
+ # The cvs executable
+ cvs_SOURCES = \
++ acl.c \
+ add.c \
+ admin.c \
+ annotate.c \
+@@ -253,11 +254,11 @@
+ bin_PROGRAMS = cvs$(EXEEXT)
+ PROGRAMS = $(bin_PROGRAMS)
+
+-am_cvs_OBJECTS = add.$(OBJEXT) admin.$(OBJEXT) annotate.$(OBJEXT) \
+- buffer.$(OBJEXT) checkin.$(OBJEXT) checkout.$(OBJEXT) \
+- classify.$(OBJEXT) client.$(OBJEXT) commit.$(OBJEXT) \
+- create_adm.$(OBJEXT) cvsrc.$(OBJEXT) diff.$(OBJEXT) \
+- edit.$(OBJEXT) entries.$(OBJEXT) error.$(OBJEXT) \
++am_cvs_OBJECTS = acl.$(OBJEXT) add.$(OBJEXT) admin.$(OBJEXT) \
++ annotate.$(OBJEXT) buffer.$(OBJEXT) checkin.$(OBJEXT) \
++ checkout.$(OBJEXT) classify.$(OBJEXT) client.$(OBJEXT) \
++ commit.$(OBJEXT) create_adm.$(OBJEXT) cvsrc.$(OBJEXT) \
++ diff.$(OBJEXT) edit.$(OBJEXT) entries.$(OBJEXT) error.$(OBJEXT) \
+ expand_path.$(OBJEXT) fileattr.$(OBJEXT) filesubr.$(OBJEXT) \
+ find_names.$(OBJEXT) hardlink.$(OBJEXT) hash.$(OBJEXT) \
+ history.$(OBJEXT) ignore.$(OBJEXT) import.$(OBJEXT) \
+@@ -280,34 +281,34 @@
+ DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+ depcomp = $(SHELL) $(top_srcdir)/depcomp
+ am__depfiles_maybe = depfiles
+-@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/add.Po ./$(DEPDIR)/admin.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/annotate.Po ./$(DEPDIR)/buffer.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/checkin.Po ./$(DEPDIR)/checkout.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/classify.Po ./$(DEPDIR)/client.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/commit.Po ./$(DEPDIR)/create_adm.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/cvsrc.Po ./$(DEPDIR)/diff.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/edit.Po ./$(DEPDIR)/entries.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/error.Po ./$(DEPDIR)/expand_path.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/fileattr.Po ./$(DEPDIR)/filesubr.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/find_names.Po ./$(DEPDIR)/hardlink.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/hash.Po ./$(DEPDIR)/history.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/ignore.Po ./$(DEPDIR)/import.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/lock.Po ./$(DEPDIR)/log.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/login.Po ./$(DEPDIR)/logmsg.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/main.Po ./$(DEPDIR)/mkmodules.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/modules.Po ./$(DEPDIR)/myndbm.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/no_diff.Po ./$(DEPDIR)/parseinfo.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/patch.Po ./$(DEPDIR)/rcs.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/rcscmds.Po ./$(DEPDIR)/recurse.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/release.Po ./$(DEPDIR)/remove.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/repos.Po ./$(DEPDIR)/root.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/run.Po ./$(DEPDIR)/scramble.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/server.Po ./$(DEPDIR)/stack.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/status.Po ./$(DEPDIR)/subr.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/tag.Po ./$(DEPDIR)/update.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/vers_ts.Po ./$(DEPDIR)/version.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/watch.Po ./$(DEPDIR)/wrapper.Po \
+-@AMDEP_TRUE@ ./$(DEPDIR)/zlib.Po
++@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/acl.Po ./$(DEPDIR)/add.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/admin.Po ./$(DEPDIR)/annotate.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/buffer.Po ./$(DEPDIR)/checkin.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/checkout.Po ./$(DEPDIR)/classify.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/client.Po ./$(DEPDIR)/commit.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/create_adm.Po ./$(DEPDIR)/cvsrc.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/diff.Po ./$(DEPDIR)/edit.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/entries.Po ./$(DEPDIR)/error.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/expand_path.Po ./$(DEPDIR)/fileattr.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/filesubr.Po ./$(DEPDIR)/find_names.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/hardlink.Po ./$(DEPDIR)/hash.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/history.Po ./$(DEPDIR)/ignore.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/import.Po ./$(DEPDIR)/lock.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/log.Po ./$(DEPDIR)/login.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/logmsg.Po ./$(DEPDIR)/main.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/mkmodules.Po ./$(DEPDIR)/modules.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/myndbm.Po ./$(DEPDIR)/no_diff.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/parseinfo.Po ./$(DEPDIR)/patch.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/rcs.Po ./$(DEPDIR)/rcscmds.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/recurse.Po ./$(DEPDIR)/release.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/remove.Po ./$(DEPDIR)/repos.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/root.Po ./$(DEPDIR)/run.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/scramble.Po ./$(DEPDIR)/server.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/stack.Po ./$(DEPDIR)/status.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/subr.Po ./$(DEPDIR)/tag.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/update.Po ./$(DEPDIR)/vers_ts.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/version.Po ./$(DEPDIR)/watch.Po \
++@AMDEP_TRUE@ ./$(DEPDIR)/wrapper.Po ./$(DEPDIR)/zlib.Po
+ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+ CCLD = $(CC)
+@@ -381,6 +382,7 @@
+ distclean-compile:
+ -rm -f *.tab.c
+
++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/admin.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/annotate.Po@am__quote@
+diff -urN cvs-1.11.15/src/mkmodules.c cvs-1.11.15-cvsacl/src/mkmodules.c
+--- cvs-1.11.15/src/mkmodules.c 2004-02-03 15:39:40.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/mkmodules.c 2004-04-25 20:04:51.736848808 +0200
+@@ -311,6 +311,44 @@
+ NULL
+ };
+
++/* cvsacl patch */
++static const char *const aclconfig_contents[] = {
++ "# Set `UseCVSACL' to yes to use CVSACL feature.\n",
++ "UseCVSACL=yes\n",
++ "\n",
++ "# Default CVSACL Permission to use.\n",
++ "#CVSACLDefaultPermissions=p\n",
++ "\n",
++ "# Default file location for CVS ACL file (access) is CVSROOT/access.\n",
++ "# If you want to use a different location, define it below.\n",
++ "#CVSACLFileLocation=/path/to/cvs/access\n",
++ "\n",
++ "# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).\n",
++ "UseSystemGroups=yes\n",
++ "\n",
++ "# Set `UseCVSGroups' to yes to use another group file.\n",
++ "#UseCVSGroups=yes\n",
++ "\n",
++ "# Default file location for CVS groups file is CVSROOT/group.\n",
++ "# If you want to use a different location, define it below.\n",
++ "#CVSGroupsFileLocation=/path/to/cvs/group\n",
++ "\n",
++ "# Set UseSeperateACLFileForEachDir to yes in order to use a\n",
++ "# seperate 'access' file for each directory.\n",
++ "# This increased the performance if you have really big repository.\n",
++ "#UseSeperateACLFileForEachDir=no\n",
++ "\n",
++ "# If StopAtFirstPermissionDenied is set to yes\n",
++ "# operation will stop at first permission denied message.\n",
++ "# Default is no.\n",
++ "#StopAtFirstPermissionDenied=no\n",
++ "\n",
++ "# Set CVSServerRunAsUser to a system user, in order CVS server\n",
++ "# to run as.\n",
++ "#CVSServerRunAsUser=runascvsuser",
++ NULL
++};
++
+ static const struct admin_file filelist[] = {
+ {CVSROOTADM_LOGINFO,
+ "no logging of 'cvs commit' messages is done without a %s file",
+@@ -373,7 +411,12 @@
+ {CVSROOTADM_CONFIG,
+ "a %s file configures various behaviors",
+ config_contents},
+- {NULL, NULL, NULL}
++
++ /* cvsacl patch */
++ {CVSROOTADM_ACLCONFIG,
++ "a %s file configures Access Control List behaviors",
++ aclconfig_contents},
++ {NULL, NULL, NULL}
+ };
+
+ /* Rebuild the checked out administrative files in directory DIR. */
+@@ -957,6 +1000,26 @@
+ because xchmod() is too shy. */
+ chmod (info, 0666);
+ }
++
++ /* cvsacl patch */
++ /* Make an empty 'CVSROOT/access' file */
++ strcpy (info, adm);
++ strcat (info, "/");
++ strcat (info, CVSROOTADM_ACCESS);
++ if (!isfile (info))
++ {
++ FILE *fp;
++
++ fp = open_file (info, "w");
++ if (fputs ("# CVS ACL definitions file. DO NOT EDIT MANUALLY\n",
++ fp) < 0)
++ error (1, errno, "cannot write %s", info);
++
++ if (fclose (fp) < 0)
++ error (1, errno, "cannot close %s", info);
++
++ chmod (info, 0644);
++ }
+
+ /* Make an empty val-tags file to prevent problems creating it later. */
+ strcpy (info, adm);
+diff -urN cvs-1.11.15/src/parseinfo.c cvs-1.11.15-cvsacl/src/parseinfo.c
+--- cvs-1.11.15/src/parseinfo.c 2004-03-19 21:36:39.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/parseinfo.c 2004-04-25 20:04:51.740848200 +0200
+@@ -448,3 +448,192 @@
+ free (line);
+ return -1;
+ }
++/* cvsacl patch */
++int
++parse_aclconfig (cvsroot)
++ char *cvsroot;
++{
++ char *infopath;
++ FILE *fp_info;
++ char *line = NULL;
++ size_t line_allocated = 0;
++ size_t len;
++ char *p;
++ /* FIXME-reentrancy: If we do a multi-threaded server, this would need
++ to go to the per-connection data structures. */
++ static int parsed = 0;
++
++ /* Authentication code and serve_root might both want to call us.
++ Let this happen smoothly. */
++ if (parsed)
++ return 0;
++ parsed = 1;
++
++ infopath = xmalloc (strlen (cvsroot)
++ + sizeof (CVSROOTADM_ACLCONFIG)
++ + sizeof (CVSROOTADM)
++ + 10);
++ if (infopath == NULL)
++ {
++ error (0, 0, "out of memory; cannot allocate infopath");
++ goto error_return;
++ }
++
++ strcpy (infopath, cvsroot);
++ strcat (infopath, "/");
++ strcat (infopath, CVSROOTADM);
++ strcat (infopath, "/");
++ strcat (infopath, CVSROOTADM_ACLCONFIG);
++
++ fp_info = CVS_FOPEN (infopath, "r");
++ if (fp_info == NULL)
++ {
++ /* If no file, don't do anything special. */
++ if (!existence_error (errno))
++ {
++ /* Just a warning message; doesn't affect return
++ value, currently at least. */
++ error (0, errno, "cannot open %s", infopath);
++ }
++ free (infopath);
++ return 0;
++ }
++
++ while (getline (&line, &line_allocated, fp_info) >= 0)
++ {
++ /* Skip comments. */
++ if (line[0] == '#')
++ continue;
++
++ len = strlen (line) - 1;
++ if (line[len] == '\n')
++ line[len] = '\0';
++
++ /* Skip blank lines. */
++ if (line[0] == '\0')
++ continue;
++
++ /* The first '=' separates keyword from value. */
++ p = strchr (line, '=');
++ if (p == NULL)
++ {
++ /* Probably should be printing line number. */
++ error (0, 0, "syntax error in %s: line '%s' is missing '='",
++ infopath, line);
++ goto error_return;
++ }
++
++ *p++ = '\0';
++
++ if (strcmp (line, "UseCVSACL") == 0)
++ {
++ if (strcmp (p, "no") == 0)
++ use_cvs_acl = 0;
++ else if (strcmp (p, "yes") == 0)
++ use_cvs_acl = 1;
++ else
++ {
++ error (0, 0, "unrecognized value '%s' for UseCVSACL", p);
++ goto error_return;
++ }
++ }
++ else if (strcmp (line, "UseSeperateACLFileForEachDir") == 0)
++ {
++ if (strcmp (p, "no") == 0)
++ use_seperate_acl_file_for_each_dir = 0;
++ else if (strcmp (p, "yes") == 0)
++ use_seperate_acl_file_for_each_dir = 1;
++ else
++ {
++ error (0, 0, "unrecognized value '%s' for UseSeperateACLFileForEachDir", p);
++ goto error_return;
++ }
++ }
++ else if (strcmp (line, "StopAtFirstPermissionDenied") == 0)
++ {
++ if (strcmp (p, "no") == 0)
++ stop_at_first_permission_denied = 0;
++ else if (strcmp (p, "yes") == 0)
++ stop_at_first_permission_denied = 1;
++ else
++ {
++ error (0, 0, "unrecognized value '%s' for StopAtFirstPermissionDenied", p);
++ goto error_return;
++ }
++ }
++ else if (strcmp (line, "CVSACLDefaultPermissions") == 0)
++ {
++ if (cvs_acl_default_permissions != NULL)
++ free (cvs_acl_default_permissions);
++ if (!given_perms_valid (p))
++ error (1,0,"Invalid CVS ACL Default Permissions: '%s' in CVSROOT/aclconfig", p);
++ cvs_acl_default_permissions = xstrdup (p);
++ }
++ else if (strcmp (line, "UseCVSGroups") == 0)
++ {
++ if (strcmp (p, "no") == 0)
++ use_cvs_groups = 0;
++ else if (strcmp (p, "yes") == 0)
++ use_cvs_groups = 1;
++ else
++ {
++ error (0, 0, "unrecognized value '%s' for UseCVSGroups", p);
++ goto error_return;
++ }
++ }
++ else if (strcmp (line, "UseSystemGroups") == 0)
++ {
++ if (strcmp (p, "no") == 0)
++ use_system_groups = 0;
++ else if (strcmp (p, "yes") == 0)
++ use_system_groups = 1;
++ else
++ {
++ error (0, 0, "unrecognized value '%s' for UseSystemGroups", p);
++ goto error_return;
++ }
++ }
++ else if (strcmp (line, "CVSACLFileLocation") == 0)
++ {
++ if (cvs_acl_file_location != NULL)
++ free (cvs_acl_file_location);
++ cvs_acl_file_location = xstrdup (p);
++ }
++ else if (strcmp (line, "CVSGroupsFileLocation") == 0)
++ {
++ if (cvs_groups_file_location != NULL)
++ free (cvs_groups_file_location);
++ cvs_groups_file_location = xstrdup (p);
++ }
++ else if (strcmp (line, "CVSServerRunAsUser") == 0)
++ {
++ if (cvs_server_run_as != NULL)
++ free (cvs_server_run_as);
++ cvs_server_run_as = xstrdup (p);
++ }
++
++ }
++
++ if (ferror (fp_info))
++ {
++ error (0, errno, "cannot read %s", infopath);
++ goto error_return;
++ }
++ if (fclose (fp_info) < 0)
++ {
++ error (0, errno, "cannot close %s", infopath);
++ goto error_return;
++ }
++ free (infopath);
++ if (line != NULL)
++ free (line);
++ return 0;
++
++ error_return:
++ if (infopath != NULL)
++ free (infopath);
++ if (line != NULL)
++ free (line);
++ return -1;
++}
++
+diff -urN cvs-1.11.15/src/patch.c cvs-1.11.15-cvsacl/src/patch.c
+--- cvs-1.11.15/src/patch.c 2004-04-02 21:25:32.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/patch.c 2004-04-25 20:04:51.744847592 +0200
+@@ -498,6 +498,43 @@
+ goto out2;
+ }
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (rev1)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, rev1, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++ if (rev2)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, rev2, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++ }
++#endif
++
+ /* Create 3 empty files. I'm not really sure there is any advantage
+ * to doing so now rather than just waiting until later.
+ *
+diff -urN cvs-1.11.15/src/remove.c cvs-1.11.15-cvsacl/src/remove.c
+--- cvs-1.11.15/src/remove.c 2004-03-20 03:34:32.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/remove.c 2004-04-25 20:04:51.746847288 +0200
+@@ -250,6 +250,25 @@
+ {
+ char *fname;
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, vers->tag, 7,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ /* Re-register it with a negative version number. */
+ fname = xmalloc (strlen (vers->vn_user) + 5);
+ (void) strcpy (fname, "-");
+diff -urN cvs-1.11.15/src/server.c cvs-1.11.15-cvsacl/src/server.c
+--- cvs-1.11.15/src/server.c 2004-04-06 22:20:55.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/server.c 2004-04-25 20:30:27.889318136 +0200
+@@ -773,6 +773,10 @@
+ nothing. But for rsh, we need to do it now. */
+ parse_config (current_parsed_root->directory);
+
++ /* cvsacl patch */
++ parse_aclconfig (current_parsed_root->directory);
++
++
+ path = xmalloc (strlen (current_parsed_root->directory)
+ + sizeof (CVSROOTADM)
+ + 2);
+@@ -3667,6 +3671,23 @@
+ do_cvs_command ("rlog", cvslog);
+ }
+
++/* cvsacl patch */
++static void
++serve_acl (arg)
++ char *arg;
++{
++ do_cvs_command ("acl", cvsacl);
++}
++
++/* cvsacl patch */
++static void
++serve_racl (arg)
++ char *arg;
++{
++ cvs_cmd_name = "racl";
++ do_cvs_command ("racl", cvsacl);
++}
++
+ static void
+ serve_add (arg)
+ char *arg;
+@@ -4726,6 +4747,11 @@
+ REQ_LINE("diff", serve_diff, 0),
+ REQ_LINE("log", serve_log, 0),
+ REQ_LINE("rlog", serve_rlog, 0),
++
++ /* cvsacl patch */
++ REQ_LINE("acl", serve_acl, 0),
++ REQ_LINE("racl", serve_racl, 0),
++
+ REQ_LINE("add", serve_add, 0),
+ REQ_LINE("remove", serve_remove, 0),
+ REQ_LINE("update-patches", serve_ignore, 0),
+@@ -5199,6 +5225,10 @@
+ {
+ struct passwd *pw;
+
++ /* cvsacl patch */
++ if (use_cvs_acl && cvs_server_run_as)
++ username = cvs_server_run_as;
++
+ pw = getpwnam (username);
+ if (pw == NULL)
+ {
+@@ -5781,6 +5811,9 @@
+ in a new CVSROOT/config file to fix the broken one! */
+ parse_config (repository);
+
++ /* cvsacl patch */
++ parse_aclconfig (repository);
++
+ /* We need the real cleartext before we hash it. */
+ descrambled_password = descramble (password);
+ host_user = check_password (username, descrambled_password, repository);
+diff -urN cvs-1.11.15/src/status.c cvs-1.11.15-cvsacl/src/status.c
+--- cvs-1.11.15/src/status.c 2004-03-20 02:40:12.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/status.c 2004-04-25 20:04:51.767844096 +0200
+@@ -128,7 +128,27 @@
+
+ status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
+ 1, 0, &vers, 0);
+- sstat = "Classify Error";
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
++ sstat = "Classify Error";
+ switch (status)
+ {
+ case T_UNKNOWN:
+diff -urN cvs-1.11.15/src/tag.c cvs-1.11.15-cvsacl/src/tag.c
+--- cvs-1.11.15/src/tag.c 2004-04-06 20:37:10.000000000 +0200
++++ cvs-1.11.15-cvsacl/src/tag.c 2004-04-25 20:04:51.772843336 +0200
+@@ -660,6 +660,25 @@
+ * correctly without breaking your link!
+ */
+
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, numtag, 4,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ if (delete_flag)
+ return (rtag_delete (rcsfile));
+
+@@ -880,6 +899,21 @@
+ if (nversion == NULL)
+ goto free_vars_and_return;
+ }
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, vers->tag, 4,
++ NULL, NULL, 1))
++ {
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++ return (0);
++ }
++ }
++#endif
++
+ if (delete_flag)
+ {
+
+diff -urN cvs-1.11.15/src/update.c cvs-1.11.15-cvsacl/src/update.c
+--- cvs-1.11.15/src/update.c 2004-03-22 18:20:26.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/update.c 2004-04-25 20:04:51.782841816 +0200
+@@ -594,6 +594,26 @@
+ status = Classify_File (finfo, tag, date, options, force_tag_match,
+ aflag, &vers, pipeout);
+
++
++/* cvsacl patch */
++#ifdef SERVER_SUPPORT
++ if (use_cvs_acl && server_active)
++ {
++ if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5,
++ NULL, NULL, 1))
++ {
++ if (stop_at_first_permission_denied)
++ error (1, 0, "permission denied for %s",
++ Short_Repository (finfo->repository));
++ else
++ error (0, 0, "permission denied for %s/%s",
++ Short_Repository (finfo->repository), finfo->file);
++
++ return (0);
++ }
++ }
++#endif
++
+ /* Keep track of whether TAG is a branch tag.
+ Note that if it is a branch tag in some files and a nonbranch tag
+ in others, treat it as a nonbranch tag. It is possible that case
+diff -urN cvs-1.11.15/src/version.c cvs-1.11.15-cvsacl/src/version.c
+--- cvs-1.11.15/src/version.c 2004-02-03 15:37:56.000000000 +0100
++++ cvs-1.11.15-cvsacl/src/version.c 2004-04-25 20:04:51.783841664 +0200
+@@ -26,7 +26,8 @@
+ #endif
+ #endif
+
+-
++/* cvsacl patch */
++char *aclpatch_string = "with CVSACL Patch 1.2.0 (cvsacl.sourceforge.net)\n";
+
+ static const char *const version_usage[] =
+ {
+@@ -63,6 +64,8 @@
+ released. */
+ (void) fputs (PACKAGE_STRING, stdout);
+ (void) fputs (config_string, stdout);
++ /* cvsacl patch */
++ (void) fputs (aclpatch_string, stdout);
+
+ #ifdef CLIENT_SUPPORT
+ if (current_parsed_root && current_parsed_root->isremote)