726 lines
18 KiB
Perl
726 lines
18 KiB
Perl
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
|
|
use Config::Simple;
|
|
use DBI;
|
|
use File::Basename;
|
|
|
|
use SVN::Core;
|
|
use SVN::Repos;
|
|
use SVN::Fs;
|
|
|
|
my $CFG_FILE = '@CFGDIR@/codepot.ini';
|
|
my $REPOFS = $ARGV[0];
|
|
my $REPOBASE = basename($REPOFS);
|
|
my $TRANSACTION = $ARGV[1];
|
|
|
|
my $QC = '';
|
|
|
|
my %SVN_ACTIONS =
|
|
(
|
|
'A ' => 'add',
|
|
'U ' => 'update',
|
|
'D ' => 'delete',
|
|
'_U' => 'propset',
|
|
'UU' => 'update/propset'
|
|
);
|
|
|
|
my %SVN_ACTION_VERBS =
|
|
(
|
|
$SVN::Fs::PathChange::modify => 'modify',
|
|
$SVN::Fs::PathChange::add => 'add',
|
|
$SVN::Fs::PathChange::delete => 'delete',
|
|
$SVN::Fs::PathChange::replace => 'replace'
|
|
);
|
|
|
|
sub get_config
|
|
{
|
|
my $cfg = new Config::Simple();
|
|
|
|
if (!$cfg->read ($CFG_FILE))
|
|
{
|
|
return undef;
|
|
}
|
|
|
|
my $config = {
|
|
database_hostname => $cfg->param ('database_hostname'),
|
|
database_port => $cfg->param ("database_port"),
|
|
database_username => $cfg->param ('database_username'),
|
|
database_password => $cfg->param ('database_password'),
|
|
database_name => $cfg->param ('database_name'),
|
|
database_driver => $cfg->param ('database_driver'),
|
|
database_prefix => $cfg->param ('database_prefix'),
|
|
|
|
svn_min_commit_message_length => $cfg->param ('svn_min_commit_message_length'),
|
|
svn_restricted_topdirs => $cfg->param('svn_restricted_topdirs'),
|
|
svn_restriction_allowed_subdir_depth_min => $cfg->param('svn_restriction_allowed_subdir_depth_min'),
|
|
svn_restriction_allowed_subdir_depth_max => $cfg->param('svn_restriction_allowed_subdir_depth_max')
|
|
};
|
|
|
|
return $config;
|
|
}
|
|
|
|
sub open_database
|
|
{
|
|
my ($cfg) = @_;
|
|
|
|
my $dbtype = $cfg->{database_driver};
|
|
my $dbname = $cfg->{database_name};
|
|
my $dbhost = $cfg->{database_hostname};
|
|
my $dbport = $cfg->{database_port};
|
|
|
|
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
|
|
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
|
|
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
|
|
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
|
|
|
|
my $dbstr;
|
|
my $dbuser;
|
|
my $dbpass;
|
|
if ($dbtype eq 'Oracle')
|
|
{
|
|
$QC = '"';
|
|
$dbstr = "DBI:$dbtype:";
|
|
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
|
|
$dbpass = '';
|
|
}
|
|
elsif ($dbtype eq 'SQLite')
|
|
{
|
|
$dbstr = "DBI:$dbtype:database=$dbhost;";
|
|
$dbuser = $cfg->{database_username};
|
|
$dbpass = $cfg->{database_password};
|
|
}
|
|
else
|
|
{
|
|
$dbstr = "DBI:$dbtype:database=$dbname;";
|
|
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
|
|
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
|
|
|
|
$dbuser = $cfg->{database_username};
|
|
$dbpass = $cfg->{database_password};
|
|
}
|
|
|
|
my $dbh = DBI->connect(
|
|
$dbstr, $dbuser, $dbpass,
|
|
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
|
|
);
|
|
|
|
return $dbh;
|
|
}
|
|
|
|
sub close_database
|
|
{
|
|
my ($dbh) = @_;
|
|
$dbh->disconnect ();
|
|
}
|
|
|
|
sub is_project_member
|
|
{
|
|
my ($dbh, $prefix, $projectid, $userid) = @_;
|
|
|
|
my $query = $dbh->prepare ("SELECT ${QC}projectid${QC} FROM ${QC}${prefix}project_membership${QC} WHERE ${QC}userid${QC}=? AND ${QC}projectid${QC}=?");
|
|
if (!$query || !$query->execute ($userid, $projectid))
|
|
{
|
|
return (-1, $dbh->errstr());
|
|
}
|
|
|
|
my @row = $query->fetchrow_array;
|
|
$query->finish ();
|
|
return (((scalar(@row) > 0)? 1: 0), undef);
|
|
}
|
|
|
|
sub is_project_commitable
|
|
{
|
|
my ($dbh, $prefix, $projectid) = @_;
|
|
|
|
my $query = $dbh->prepare ("SELECT ${QC}commitable${QC} FROM ${QC}${prefix}project${QC} WHERE ${QC}id${QC}=?");
|
|
if (!$query || !$query->execute ($projectid))
|
|
{
|
|
return (-1, $dbh->errstr());
|
|
}
|
|
|
|
my @row = $query->fetchrow_array;
|
|
$query->finish ();
|
|
return (((scalar(@row) > 0 && $row[0] eq 'Y')? 1: 0), undef);
|
|
}
|
|
|
|
sub contains_repeated_chars
|
|
{
|
|
my ($str, $limit) = @_;
|
|
|
|
my $len = length($str);
|
|
my $lc = '';
|
|
my $count = 1;
|
|
|
|
for (my $i = 0; $i < $len; $i++)
|
|
{
|
|
my $c = substr($str, $i, 1);
|
|
if ($lc eq $c)
|
|
{
|
|
$count++;
|
|
if ($count > $limit) { return 1; }
|
|
}
|
|
else
|
|
{
|
|
$count = 1;
|
|
$lc = $c;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub check_commit_message
|
|
{
|
|
my ($minlen) = @_;
|
|
|
|
my $pool = SVN::Pool->new(undef);
|
|
#my $config = SVN::Core::config_get_config(undef);
|
|
#my $fs = eval { SVN::Fs::open ($REPOFS, $config, $pool) };
|
|
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
|
|
if (!defined($svn))
|
|
{
|
|
print (STDERR "Cannot open svn - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $fs = $svn->fs ();
|
|
if (!defined($fs))
|
|
{
|
|
print (STDERR "Cannot open fs - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $txn = eval { $fs->open_txn ($TRANSACTION) };
|
|
if (!defined($txn))
|
|
{
|
|
print (STDERR "Cannot open transaction - $TRANSACTION\n");
|
|
return -1;
|
|
}
|
|
|
|
my $log = $txn->prop ('svn:log');
|
|
# TODO: block a certain message patterns. create a configuration item
|
|
# for this
|
|
#if ($log =~ /[[:punct:]]{5,}/ || $log =~ /[[:alpha:]]{40,}/ || contains_repeated_chars($log, 4))
|
|
#{
|
|
# print (STDERR "Commit message rejected\n");
|
|
# return 0;
|
|
#}
|
|
|
|
$log =~ s/\s{2,}/ /g;
|
|
$log =~ s/([[:punct:]]{1,2}\s+){3,}/ /g;
|
|
$log =~ s/[[:punct:]]{3,}/ /g;
|
|
$log =~ s/^\s+|\s+$//g; # trim leading spaces and trailing spaces
|
|
if (length($log) < $minlen)
|
|
{
|
|
print (STDERR "Commit message too short. meaningful part must be >= $minlen\n");
|
|
return 0;
|
|
}
|
|
|
|
if ($log =~ /^[[:punct:][:space:]]+$/)
|
|
{
|
|
print (STDERR "Commit message meaningless\n");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub restrict_changes_in_directory_old
|
|
{
|
|
my ($dir, $min_level, $max_level) = @_;
|
|
|
|
my @change_info = `svnlook changed --copy-info -t "${TRANSACTION}" "${REPOFS}"`;
|
|
|
|
# 'A ' Item added to repository
|
|
# 'D ' Item deleted from repository
|
|
# 'U ' File contents changed
|
|
# '_U' Properties of item changed; note the leading underscore
|
|
# 'UU' File contents and properties changed
|
|
# ------------------------------------------------------------
|
|
# + on the third column to indicate copy
|
|
# fourth column is empty.
|
|
# ------------------------------------------------------------
|
|
# When copy-info is used, the source of the copy is shown
|
|
# on the next line aligned at the file name part and
|
|
# begins with spaces.
|
|
#
|
|
# A + y/t/
|
|
# (from c/:r2)
|
|
# ------------------------------------------------------------
|
|
#
|
|
# Renaming a file in the copied directory looks like this.
|
|
# D tags/xxx-1.2.3/2/screenrc
|
|
# A + tags/xxx-1.2.3/2/screenrc.x
|
|
# (from tags/xxx-1.2.3/2/screenrc:r10)
|
|
#
|
|
# If the deletion of the file is disallowed, the whole
|
|
# transaction is blocked. so I don't need to care about
|
|
# copied addition.
|
|
# ------------------------------------------------------------
|
|
|
|
foreach my $line(@change_info)
|
|
{
|
|
chomp ($line);
|
|
print (STDERR "... CHANGE INFO => $line\n");
|
|
}
|
|
|
|
my $disallowed = 0;
|
|
|
|
while (@change_info) #foreach my $line(@change_info)
|
|
{
|
|
my $line = shift (@change_info);
|
|
chomp ($line);
|
|
|
|
if ($line =~ /^(A |U |D |_U|UU) ${dir}\/(.*)$/)
|
|
{
|
|
my $action = "${1}";
|
|
my $affected_file = "${dir}/${2}";
|
|
my $affected_file_nodir = "${2}";
|
|
|
|
my $action_verb = $SVN_ACTIONS{$action};
|
|
|
|
if (rindex($affected_file, '/') + 1 == length($affected_file))
|
|
{
|
|
# the last character is a slash. so it's a directory.
|
|
# let's allow most of the operations on a directory.
|
|
#if ($action eq 'D ')
|
|
#{
|
|
my @segs = split ('/', $affected_file_nodir);
|
|
my $num_segs = scalar(@segs);
|
|
# NOTE: for a string like abc/def/, split() seems to return 2 segments only.
|
|
|
|
if ($affected_file_nodir eq '')
|
|
{
|
|
# it is the main directory itself.
|
|
# allow operation on it.
|
|
}
|
|
elsif ($num_segs < $min_level || $num_segs > $max_level)
|
|
{
|
|
# disallow deletion if the directory name to be deleted
|
|
# matches a tag pattern
|
|
print (STDERR "Disallowed to ${action_verb} a directory - ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
#}
|
|
}
|
|
else
|
|
{
|
|
print (STDERR "Disallowed to ${action_verb} a file - ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
}
|
|
elsif ($line =~ /^(A )\+ ${dir}\/(.*)$/)
|
|
{
|
|
my $action = "${1}";
|
|
my $affected_file = "${dir}/${2}";
|
|
|
|
# copying
|
|
#
|
|
# A + tags/xxx-1.2.3/2/smi.conf.2
|
|
# (from tags/xxx-1.2.3/2/smi.conf:r10)
|
|
#
|
|
my $source_line = shift (@change_info);
|
|
chomp ($source_line);
|
|
|
|
if ($source_line =~ /
|
|
^ # beginning of string
|
|
\W* # 0 or more white-spaces
|
|
\( # opening parenthesis
|
|
\S+ # 1 or more non-space characters
|
|
\W+ # 1 or more space characters
|
|
(.+) # 1 or more characters
|
|
:r[0-9]+ # :rXXX where XXX is digits
|
|
\) # closing parenthesis
|
|
$ # end of string
|
|
/x)
|
|
{
|
|
my $source_file = "${1}";
|
|
|
|
if (rindex($affected_file, '/') + 1 != length($affected_file))
|
|
{
|
|
# the file beging added by copyiung is not a directory.
|
|
# it disallows individual file copying.
|
|
# copy a whole directory at one go.
|
|
print (STDERR "Disallowed to copy $source_file to $affected_file\n");
|
|
$disallowed++;
|
|
}
|
|
elsif ($source_file =~ /^${dir}\/(.*)$/)
|
|
{
|
|
# i don't want to be a copied file or directory to be
|
|
# a source of another copy operation.
|
|
print (STDERR "Disallowed to copy $source_file to $affected_file\n");
|
|
$disallowed++;
|
|
}
|
|
else
|
|
{
|
|
# Assume xxx is a directory.
|
|
# Assume min_level is 1 and max_level is 2.
|
|
#
|
|
# If the following two commans are executed,
|
|
# svn copy trunk/xxx tags/my-4.0.0
|
|
# svn copy trunk/xxx tags/my-4.0.0/1
|
|
#
|
|
# svnlook returns the following text.
|
|
# A + tags/my-4.0.0/
|
|
# (from trunk/xxx/:r16)
|
|
# A + tags/my-4.0.0/1/
|
|
# (from trunk/xxx/:r16)
|
|
#
|
|
# if the script knows that tags/my-4.0.0 is created via copying,
|
|
# i want this script to prevent copying other sources into it.
|
|
# this case is not fully handled by this script.
|
|
|
|
# TODO: DISALLOW THIS if the parent directory is a copied directory
|
|
my $pardir = dirname ($affected_file);
|
|
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#{
|
|
# print (STDERR "OK ... ${line}\n");
|
|
#}
|
|
}
|
|
|
|
return ($disallowed > 0)? -1: 0;
|
|
}
|
|
|
|
sub restrict_changes_in_topdirs
|
|
{
|
|
my ($min_level, $max_level, @topdirs) = @_;
|
|
my $disallowed = 0;
|
|
|
|
my $pool = SVN::Pool->new(undef);
|
|
#my $config = SVN::Core::config_get_config(undef);
|
|
#my $fs = eval { SVN::Fs::open ($REPOFS, $config, $pool) };
|
|
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
|
|
if (!defined($svn))
|
|
{
|
|
print (STDERR "Cannot open svn - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $fs = $svn->fs ();
|
|
if (!defined($fs))
|
|
{
|
|
print (STDERR "Cannot open fs - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $txn = eval { $fs->open_txn ($TRANSACTION) };
|
|
if (!defined($txn))
|
|
{
|
|
print (STDERR "Cannot open transaction - $TRANSACTION\n");
|
|
return -1;
|
|
}
|
|
|
|
my $root = eval { $txn->root() };
|
|
if (!defined($root))
|
|
{
|
|
print (STDERR "Cannot open root of transaction - $TRANSACTION\n");
|
|
return -1;
|
|
}
|
|
|
|
my $paths_changed = eval { $root->paths_changed() };
|
|
if (!defined($paths_changed))
|
|
{
|
|
# no change information found. return ok
|
|
$root->close_root ();
|
|
return 0;
|
|
}
|
|
|
|
foreach my $affected_file(keys(%$paths_changed))
|
|
{
|
|
my $chg = $paths_changed->{$affected_file};
|
|
my $action = $chg->change_kind();
|
|
my $action_verb = $SVN_ACTION_VERBS{$action};
|
|
if (length($action_verb) <= 0) { $action_verb = "work on"; }
|
|
|
|
my $is_source_file_dir = 0;
|
|
my $is_affected_file_dir = eval { $root->is_dir($affected_file) };
|
|
#$chg->text_mod(), $chg->prop_mod()
|
|
|
|
#my $affected_rev_id = eval { SVN::Fs::unparse_id($chg->node_rev_id()) };
|
|
my $source_file = undef;
|
|
#my $source_id = undef;
|
|
|
|
if ($action == $SVN::Fs::PathChange::add)
|
|
{
|
|
$source_file = eval { $root->copied_from($affected_file) };
|
|
#if ($source_file)
|
|
#{
|
|
# $source_id = eval { SVN::Fs::unparse_id($root->node_id($source_file)) };
|
|
#}
|
|
}
|
|
elsif ($action == $SVN::Fs::PathChange::delete)
|
|
{
|
|
# when a file is deleted, $root->is_dir() doesn't seem to
|
|
# return the right type. use the revision root to determine it.
|
|
my $rev_root = $fs->revision_root($fs->youngest_rev());
|
|
$is_affected_file_dir = eval { $rev_root->is_dir ($affected_file) };
|
|
$rev_root->close_root();
|
|
}
|
|
|
|
#print STDERR "@@@@@ [$affected_file] [$action_verb] [$source_file] [$is_source_file_dir] [$is_affected_file_dir]\n";
|
|
|
|
foreach my $topdir(@topdirs)
|
|
{
|
|
if ($affected_file =~ /^\/${topdir}\/(.*)$/)
|
|
{
|
|
# the affected file is located under the given directory.
|
|
my $affected_file_nodir = "${1}";
|
|
|
|
if (defined($source_file))
|
|
{
|
|
# it's being copied.
|
|
if (!$is_affected_file_dir)
|
|
{
|
|
# the file beging added by copying is not a directory.
|
|
# it disallows individual file copying.
|
|
# copy a whole directory at one go.
|
|
print (STDERR "Disallowed to copy ${source_file} to ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
elsif ($source_file =~ /^\/${topdir}\/(.*)$/)
|
|
{
|
|
# i don't want to be a copied file or directory to be
|
|
# a source of another copy operation.
|
|
print (STDERR "Disallowed to copy ${source_file} to ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
else
|
|
{
|
|
# TODO: DISALLOW THIS if the parent directory is a copied directory
|
|
#my $pardir = dirname ($affected_file);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ($is_affected_file_dir)
|
|
{
|
|
my @segs = split ('/', $affected_file_nodir);
|
|
my $num_segs = scalar(@segs);
|
|
# NOTE: for a string like abc/def/, split() seems to return 2 segments only.
|
|
|
|
if ($affected_file_nodir eq '')
|
|
{
|
|
# it is the main directory itself.
|
|
# allow operation on it.
|
|
}
|
|
elsif ($num_segs < $min_level || $num_segs > $max_level)
|
|
{
|
|
# disallow deletion if the directory name to be deleted
|
|
# matches a tag pattern
|
|
print (STDERR "Disallowed to ${action_verb} a directory - ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
print (STDERR "Disallowed to ${action_verb} a file - ${affected_file}\n");
|
|
$disallowed++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# 'svn rename' within the restricted directory is disallowed because
|
|
# it splits to deletion and addition. for this reason, you're supposed
|
|
# to copy from the trunk or branch source again.
|
|
#
|
|
# $ svn rename tags/my-1.0.0 tags/my-2.0.0
|
|
# $ svn commit -m "XXXXXXXXXXXX"
|
|
# Deleting tags/my-1.0.0
|
|
# Adding tags/my-2.0.0
|
|
#
|
|
|
|
$root->close_root ();
|
|
return ($disallowed > 0)? -1: 0;
|
|
}
|
|
|
|
sub restrict_read_only_files()
|
|
{
|
|
my $disallowed = 0;
|
|
|
|
my $pool = SVN::Pool->new(undef);
|
|
#my $config = SVN::Core::config_get_config(undef);
|
|
#my $fs = eval { SVN::Fs::open($REPOFS, $config, $pool) };
|
|
my $svn = eval { SVN::Repos::open($REPOFS, $pool) };
|
|
if (!defined($svn))
|
|
{
|
|
print (STDERR "Cannot open svn - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $fs = $svn->fs();
|
|
if (!defined($fs))
|
|
{
|
|
print (STDERR "Cannot open fs - $REPOFS\n");
|
|
return -1; # error
|
|
}
|
|
|
|
my $txn = eval { $fs->open_txn ($TRANSACTION) };
|
|
if (!defined($txn))
|
|
{
|
|
print (STDERR "Cannot open transaction - $TRANSACTION\n");
|
|
return -1;
|
|
}
|
|
|
|
my $root = eval { $txn->root() };
|
|
if (!defined($root))
|
|
{
|
|
print (STDERR "Cannot open root of transaction - $TRANSACTION\n");
|
|
return -1;
|
|
}
|
|
|
|
my $paths_changed = eval { $root->paths_changed() };
|
|
if (!defined($paths_changed))
|
|
{
|
|
# no change information found. return ok
|
|
$root->close_root ();
|
|
return 0;
|
|
}
|
|
|
|
my $fs_root = $fs->revision_root($fs->youngest_rev());
|
|
|
|
foreach my $affected_file(keys(%$paths_changed))
|
|
{
|
|
my $chg = $paths_changed->{$affected_file};
|
|
my $action = $chg->change_kind();
|
|
my $action_verb = $SVN_ACTION_VERBS{$action};
|
|
if (length($action_verb) <= 0) { $action_verb = "work on"; }
|
|
|
|
## check codepot:readonly only if there is content change.
|
|
## property-only change is always allowed.
|
|
## directory addition is probably allowed. ## TODO: prevent this?
|
|
next if ($action != $SVN::Fs::PathChange::delete && !$chg->text_mod());
|
|
|
|
my $file = $affected_file;
|
|
my $readonly = eval { $fs_root->node_prop($file, 'codepot:readonly') };
|
|
if ($readonly eq 'yes')
|
|
{
|
|
$disallowed = 1;
|
|
print (STDERR "Unable to $action_verb $file - read-only\n");
|
|
}
|
|
elsif ($readonly eq 'no')
|
|
{
|
|
## no is set explicitly on the node itself.
|
|
## don't proceed to check the parent side.
|
|
## change is granted immediately.
|
|
## DO NOTHING HERE
|
|
}
|
|
else
|
|
{
|
|
## check permission in the parent side
|
|
while ((my $slash = rindex($file, "/")) >= 0)
|
|
{
|
|
$file = substr($file, 0, $slash);
|
|
my $tmp = $file;
|
|
$tmp = '/' if ($tmp eq '');
|
|
$readonly = eval { $fs_root->node_prop($tmp, 'codepot:readonly') };
|
|
if ($readonly eq 'yes')
|
|
{
|
|
$disallowed = 1;
|
|
print (STDERR "Unable to $action_verb $affected_file - $tmp set to read-only\n");
|
|
}
|
|
elsif ($readonly eq 'no')
|
|
{
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$root->close_root ();
|
|
return ($disallowed > 0)? -1: 0;
|
|
}
|
|
|
|
#------------------------------------------------------------
|
|
# MAIN
|
|
#------------------------------------------------------------
|
|
|
|
my $cfg = get_config ();
|
|
if (!defined($cfg))
|
|
{
|
|
print (STDERR "Cannot load codepot configuration file\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (check_commit_message ($cfg->{svn_min_commit_message_length}) <= 0)
|
|
{
|
|
exit (1);
|
|
}
|
|
|
|
|
|
# TODO: enable per-project settings for topdir restriction
|
|
my $min_level = $cfg->{svn_restriction_allowed_subdir_depth_min};
|
|
if (!defined($min_level)) { $min_level = 0; }
|
|
my $max_level = $cfg->{svn_restriction_allowed_subdir_depth_max};
|
|
if (!defined($max_level)) { $max_level = 0; }
|
|
|
|
my $topdirs = $cfg->{svn_restricted_topdirs};
|
|
if (defined($topdirs))
|
|
{
|
|
my @topdir_array = split (/\s*,\s*/, $topdirs);
|
|
if (scalar(@topdir_array) > 0)
|
|
{
|
|
if (restrict_changes_in_topdirs ($min_level, $max_level, @topdir_array) <= -1)
|
|
{
|
|
exit (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (restrict_read_only_files() <= -1)
|
|
{
|
|
exit (1);
|
|
}
|
|
|
|
#my $dbh = open_database ($cfg);
|
|
#if (!defined($dbh))
|
|
#{
|
|
# printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
|
|
# exit (1);
|
|
#}
|
|
#
|
|
#my $member;
|
|
#my $commitable;
|
|
#my $errstr;
|
|
#
|
|
#($member, $errstr) = is_project_member (
|
|
# $dbh, $cfg->{database_prefix}, $REPOBASE, $USER);
|
|
#if ($member <= -1)
|
|
#{
|
|
# print (STDERR "Cannot check membership - $errstr\n");
|
|
# close_database ($dbh);
|
|
# exit (1);
|
|
#}
|
|
#
|
|
#($commitable, $errstr) = is_project_commitable (
|
|
# $dbh, $cfg->{database_prefix}, $REPOBASE);
|
|
#if ($commitable <= -1)
|
|
#{
|
|
# print (STDERR "Cannot check commitability - $errstr\n");
|
|
# close_database ($dbh);
|
|
# exit (1);
|
|
#}
|
|
#
|
|
#close_database ($dbh);
|
|
#
|
|
#if ($member == 0)
|
|
#{
|
|
# print (STDERR "$USER doesn't belong to the $REPOBASE project\n");
|
|
# exit (1);
|
|
#}
|
|
#
|
|
#if ($commitable == 0)
|
|
#{
|
|
# print (STDERR "The $REPOBASE project is not commitable\n");
|
|
# exit (1);
|
|
#}
|
|
#
|
|
exit (0);
|