URI: 
       tmade repo name independent from user name (thx loupgaroublond), renamed some config variables, gitzone-shell: default repo dir changed to ~/zones/$LOGNAME - gitzone - git-based zone management tool for static and dynamic domains
  HTML git clone https://git.parazyd.org/gitzone
   DIR Log
   DIR Files
   DIR Refs
       ---
   DIR commit 35bdb5882b1690a23e3ef13439e144d17b3a5af4
   DIR parent b2e775044d4def727650a3f599955c7459fe7e38
  HTML Author: tg(x) <*@tg-x.net>
       Date:   Sun, 27 Feb 2011 01:12:30 +0100
       
       made repo name independent from user name (thx loupgaroublond), renamed some config variables, gitzone-shell: default repo dir changed to ~/zones/$LOGNAME
       
       Diffstat:
         M README.org                          |      10 ++++++----
         M bin/gitzone                         |      60 +++++++++++++++-----------------
         M bin/gitzone-shell                   |      11 ++++++++---
         M etc/gitzone.conf                    |      15 +++++++++------
       
       4 files changed, 51 insertions(+), 45 deletions(-)
       ---
   DIR diff --git a/README.org b/README.org
       t@@ -28,10 +28,10 @@ key management.
        - create a zones repo for each user and set receive.denyCurrentBranch to ignore,
          this allows pushing to a checked out repository. The checked out files are
          used for incrementing serials and validating the zones with named-checkzone.
       -  : # cd ~$user
       -  : # git init zones
       -  : # ln -s zones $username  # needed for named-checkzone
       -  : # cd zones
       +  : # mkdir -p ~$user/zones
       +  : # cd ~$user/zones
       +  : # git init $user
       +  : # cd $user
          : # git config receive.denyCurrentBranch ignore
          : # cd hooks
          : # ln -s /usr/libexec/gitzone/pre-receive
       t@@ -80,6 +80,8 @@ use the auto increment feature you also need to pull after a push as the receive
        hooks on the server make commits to the repository during a push.
        
        #+BEGIN_EXAMPLE
       +  % git clone ns.example.net:zones/$user zones
       +  % # or if you're using gitzone-shell you can use any path:
          % git clone ns.example.net:zones
          % cd zones
          % # edit files
   DIR diff --git a/bin/gitzone b/bin/gitzone
       t@@ -12,14 +12,17 @@ use warnings;
        use strict;
        use POSIX qw/strftime/;
        use Cwd qw/cwd realpath/;
       -use File::Basename qw/fileparse/;
       +use File::Basename qw/fileparse basename/;
        
       -our ($zone_dir, $git, $named_checkzone, $rndc, $class, $default_view, $update_record, $user_includes, $max_depth, $zones, $verbosity);
       +@ARGV >= 2 or die "Usage: gitzone /path/to/gitzone.conf <command>\n";
       +basename(realpath) eq '.git' or die "gitzone has to be run from a .git directory\n";
       +chdir '..';
       +
       +our ($zone_dir, $git, $named_checkzone, $rndc, $class, $default_view, $update_record, $unrestricted_includes, $max_depth, $repos, $verbosity);
        our $user = getpwuid $<;
       +our $repo = basename realpath;
        
       -@ARGV >= 2 or die "Usage: gitzone /path/to/gitzone.conf <command>\n";
        my ($config_file, $cmd) = @ARGV;
       -
        do $config_file or die "Can't load config: $!\n";
        
        my $lock_file = realpath '.gitzone-lock';
       t@@ -58,20 +61,20 @@ sub git {
          return $_;
        }
        
       -# Load BIND config files specified in the $zones config variable.
       -# First load the -default key, then the $user key.
       -sub load_zones_config {
       +# Load BIND config files specified in the $repos config variable.
       +# First load the -default key, then the $repo key.
       +sub load_repos_config {
          my $key = shift || '-default';
        
          # move files not in a dir to a . dir for easier processing
       -  for my $file (keys %{$zones->{$key}}) {
       -    next if ref $zones->{$key}->{$file} eq 'HASH';
       -    $zones->{$key}->{'.'}->{$file} = $zones->{$key}->{$file};
       -    delete $zones->{$key}->{$file};
       +  for my $file (keys %{$repos->{$key}}) {
       +    next if ref $repos->{$key}->{$file} eq 'HASH';
       +    $repos->{$key}->{'.'}->{$file} = $repos->{$key}->{$file};
       +    delete $repos->{$key}->{$file};
          }
        
       -  for my $dir (keys %{$zones->{$key}}) {
       -    my $d = $zones->{$key}->{$dir};
       +  for my $dir (keys %{$repos->{$key}}) {
       +    my $d = $repos->{$key}->{$dir};
            for my $file (keys %$d) {
              $d->{$file} = $default_view if $d->{$file} eq 1;
              $d->{$file} = [$d->{$file}] if ref $d->{$file} ne 'ARRAY';
       t@@ -80,7 +83,7 @@ sub load_zones_config {
                open FILE, '<', $file or die $!;
                while (<FILE>) {
                  if (/^\s*zone\s+"([^"]+)"/) {
       -            $zones->{$user}->{$dir}->{$1} = $d->{$file};
       +            $repos->{$repo}->{$dir}->{$1} = $d->{$file};
                  }
                }
                close FILE;
       t@@ -89,7 +92,7 @@ sub load_zones_config {
            }
          }
        
       -  load_zones_config($user) if $key eq '-default';
       +  load_zones_config($repo) if $key eq '-default';
        }
        
        sub process_files {
       t@@ -122,11 +125,11 @@ sub process_file {
              $changed = 1;
            } elsif (/^(\W*\$INCLUDE\W+)(\S+)(.*)$/) {
              my ($a,$inc_file,$z) = ($1,$2,$3);
       -      if ($user_includes) {
       -        # check $INCLUDE lines for files outside the user dir
       -        unless ($inc_file =~ m,^$user/, && $inc_file !~ /\.\./) {
       +      unless ($unrestricted_includes) {
       +        # check $INCLUDE lines for files outside the repo dir
       +        unless ($inc_file =~ m,^$repo/, && $inc_file !~ /\.\./) {
                  close FILE;
       -          die "Error in $file:$n: invalid included file name, it should start with: $user/\n";
       +          die "Error in $file:$n: invalid included file name, it should start with: $repo/\n";
                }
              }
            } else {
       t@@ -186,13 +189,9 @@ sub check_zones {
            # skip files with errors and those that are not in the config
            my ($zone, $dir) = fileparse $file;
            $dir = substr $dir, 0, -1;
       -    next unless $files{$file} > 0 && exists $zones->{$user}->{$dir}->{$zone};
       +    next unless $files{$file} > 0 && exists $repos->{$repo}->{$dir}->{$zone};
        
       -    if ($user_includes) {
       -      print `$named_checkzone -w .. '$zone' '$user/$file'`;
       -    } else {
       -      print `$named_checkzone '$zone' '$file'`;
       -    }
       +    print `$named_checkzone -w .. '$zone' '$repo/$file'`;
            clean_exit 1 if $?; # error, reject push
            push @zones, $file;
          }
       t@@ -206,7 +205,7 @@ sub install_zones {
          git 'checkout -f master';
          git 'reset --hard new';
        
       -  chdir "$zone_dir/$user" or die $!;
       +  chdir "$zone_dir/$repo" or die $!;
          git "clone $cwd ." unless -d '.git';
          git 'fetch';
          git 'reset --hard remotes/origin/master';
       t@@ -214,7 +213,7 @@ sub install_zones {
          for my $file (@zones) {
            my ($zone, $dir) = fileparse $file;
            $dir = substr $dir, 0, -1;
       -    my $view = $zones->{$user}->{$dir}->{$zone};
       +    my $view = $repos->{$repo}->{$dir}->{$zone};
            print "$_/$zone: ", `$rndc reload '$zone' $class $_` for @$view;
          }
        
       t@@ -223,7 +222,6 @@ sub install_zones {
        
        sub pre_receive {
          my ($old, $new, $ref);
       -  chdir '..';
        
          while (<STDIN>) { # <old-value> SP <new-value> SP <ref-name> LF
            print if $verbosity >= 1;
       t@@ -246,7 +244,7 @@ sub pre_receive {
          # parse diff output, add only valid zone names to %files for parsing
          $files{$1} = 0 while m,^:(?:[\w.]+\s+){5}([a-z0-9./-]+)$,gm;
        
       -  load_zones_config;
       +  load_repos_config;
          process_files;
        
          if (@zones) {
       t@@ -266,13 +264,12 @@ sub pre_receive {
        
        sub post_receive {
          print "\n";
       -  chdir '..';
        
          open FILE, '<', $list_file or die $!;
          push @zones, split /[\s\n\r]+/ while <FILE>;
          close FILE;
        
       -  load_zones_config;
       +  load_repos_config;
          install_zones;
          print "Done. Don't forget to pull if you use auto increment.\n";
        }
       t@@ -286,7 +283,6 @@ sub update_record {
          my $changed = 0;
          my @newfile;
        
       -  chdir $user;
          git 'checkout -f master';
        
          open FILE, '<', $file or die "$file: $!";
   DIR diff --git a/bin/gitzone-shell b/bin/gitzone-shell
       t@@ -1,7 +1,11 @@
        #!/bin/sh
        
        # only repo allowed for git pull/push
       -repo='zones'
       +repo=$LOGNAME
       +# directory the repo is in, relative to $HOME
       +repo_dir='zones'
       +#repo_dir='.'
       +
        # allow ssh key add/del/list commands if this file exists
        allow_key_mgmt_file='.ssh/authorized_keys_edit_allowed'
        
       t@@ -20,10 +24,11 @@ if [ "$1" != "-c" ]; then error; fi
        cmd=$2
        
        if [[ "$cmd" == git-upload-pack* ]]; then
       -  $git upload-pack $repo
       +  $git upload-pack $repo_dir/$repo
        elif [[ "$cmd" == git-receive-pack* ]]; then
       -  $git receive-pack $repo
       +  $git receive-pack $repo_dir/$repo
        elif [[ "$cmd" == update-record* ]]; then
       +  cd $repo_dir/$repo/.git
          $gitzone $config update-record "$cmd"
        elif [ -f $allow_key_mgmt_file ]; then
          if [ "$cmd" == list-keys ]; then
   DIR diff --git a/etc/gitzone.conf b/etc/gitzone.conf
       t@@ -4,6 +4,7 @@
        #
        # this file is parsed as Perl code and you can use the following variables:
        #   $user - name of the user gitzone is invoked by
       +#   $repo - name of the repository gitzone is invoked for
        
        # directory where the zone files are copied to (no trailing slash)
        # there should be one directory for each user here chowned to them
       t@@ -17,9 +18,10 @@ $rndc = '/usr/sbin/rndc';
        # update-record command: 1 = enabled, 0 = disabled
        $update_record = 1;
        
       -# restrict includes to user directories
       -# $INCLUDE file names should be prefixed with <username>/ in this case
       -$user_includes = 1;
       +# unrestricted includes: 1 = enabled, 0 = disabled (default)
       +# by default a restriction applies to $INCLUDE file names,
       +# they should be prefixed with <repo>/ and nothing else is allowed in parent dirs
       +$unrestricted_includes = 0;
        
        # max depth to follow INCLUDED_BY files
        $max_depth = 256;
       t@@ -31,6 +33,7 @@ $verbosity = 0;
        $class = 'IN';
        # default view of the zones (optional)
        $default_view = '';
       +#$default_view = $repo;
        
        # $zones defines which files in a user's repo can be loaded as zone files.
        #
       t@@ -48,11 +51,11 @@ $default_view = '';
        #
        # The -default key is tried first for every user, then it's merged with the user-specific config.
        
       -$zones = {
       +$repos = {
        #  -default => {
       -#    "/etc/bind/users/$user.conf" => 1,               # allow every zone from this file, use the default view for them
       +#    "/etc/bind/repos/$repo.conf" => 1,               # allow every zone from this file, use the default view for them
        #  },
       -#  user1 => { # /etc/bind/users/user1.conf is loaded first and merged with the config below, as specified in -default above
       +#  user1 => { # /etc/bind/repos/user1.conf is loaded first and merged with the config below, as specified in -default above
        #    'example.com' => 1,                              # allow example.com, use the default view for it
        #    'example.net' => 'extern',                       # allow example.net, use the extern view for it
        #    'example.org' => [qw(view1 view2)],              # allow example.org, use both view1 & view2 for it