{
  # Git Repositories Httpd Template
  #
  # This configures the Apache 2.x webserver to be able to function
  # as a Git repository server using git-http-backend with gitweb
  # repository viewing.
  #
  # Required packages: git gitweb
  #
  # This setup provides "dual URLS", where URL like
  # <http://example.com/git/my_repository.git>
  # loads Gitweb in the browser and the same URL
  # can be used in commands like `git clone` and
  # `git remote add`.
  #
  # Please see documentation for:
  # 1. `git-http-backend`
  #    http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html
  # 2. `gitweb`
  #    http://repo.or.cz/w/alt-git.git?a=blob_plain;f=gitweb/README
  #
  # A lot of inspriration for this contrib came
  # from https://git.jim.sh/gitweb/README.html
  #
  # Also see Scott Chacon's "Smart HTTP Transport"
  # http://progit.org/2010/03/04/smart-http.html
  #
  # Access Rules:
  # - Global:  * gitweb view from the Internet and the local
  #              network with public repositories
  #            * repository read from the Internet
  #              and the local network
  #            * repository write from the Internet
  #              but authenticated with HTTPS
  # - Local:   * accessible only from the local
  #              network (incl. gitweb)

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # Copyright (C) 2012-2015 Marco Hess <marco.hess@through-ip.com>
  #
  # This file should not be edited. If you want to make changes to it
  # copy it to the /etc/e-smith/templates-custom directory and make
  # your modifications in the new copy. This way modifications are
  # saved when the system is restored from a backup or configuration
  # changes are made.

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Libraries

  use esmith::ConfigDB;      # General smeserver-git configuration
  use esmith::NetworksDB;    # Get the network IP address configuration
  use esmith::AccountsDB;    # Collect users that are members of a group
  use esmith::DomainsDB;     #
  use esmith::GitDB;         # Contains all git repositories configuration data

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Minimal Base Configuration Requirements

  my $config_db = esmith::ConfigDB->open_ro() or
    die "Couldn't open ConfigDB\n";

  my $status = $git{'status'} || "disabled";
  return "    # git is disabled.\n    # use 'config setprop git status enabled' to enable HTTP access for git on your server.\n"
    unless $status eq 'enabled';

  my $access = $git{'gitweb_access_from'} || "disabled";
  return "    # no gitweb_access_from setting for git defined!\n    # use 'config setprop git gitweb_access_from [local|internet]' to set network access level for gitweb.\n"
    if $access eq 'disabled';

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Open additional databases for configuring individual repositories

  my $networks_db = esmith::NetworksDB->open_ro() or
    die "Couldn't open NetworksDB\n";

  my $accounts_db = esmith::AccountsDB->open_ro() or
    die "Couldn't open AccountsDB\n";

  my $git_db = esmith::GitDB->open_ro() or
    die "Couldn't open GitDB\n";

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Option Configuration

  my $debugging         = $git{'debugging'}        || "disabled";

  my $user_repositories = $git{'UserRepositories'} || "disabled";

  my $gitweb            = $git{'GitWeb'}           || "disabled";

  my $gitweb_theme      = $git{'GitWebTheme'}      || "disabled";

  my $gitpath = "";
  if( $git{'GitPath'} ) {
    $gitpath = "/" . $git{'GitPath'};
  }

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Gitweb default access permissions for viewing

  my $gitweb_allow = 'ip 127.0.0.1'; # Catch incorrect values, including empty ones
  # Setup the rules from which address range we allow access
  if( $git{'gitweb_access_from'} ) {
    if ($git{'gitweb_access_from'} eq 'internet') {
      $gitweb_allow = 'all granted';
    } elsif ($git{'gitweb_access_from'} eq 'local') {
      $gitweb_allow = "ip $localAccess";
    }
  }

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ Default installation locations

  # Root of the git repositories directory tree.
  my $GitRepositoryRoot = "/home/e-smith/files/git";

  # Find installation location of the gitweb executables
  # and gitweb.css files can vary a bit between git versions.

  my $GitWebRoot = "";
  my $GitWebCSS = "";
  if( $gitweb eq 'enabled' ) {
    if( -e "/usr/share/gitweb/gitweb.cgi" ) { 
      # Git 1.7
      $GitWebRoot = "/usr/share/gitweb";
      if( -e "/usr/share/gitweb/static/gitweb.css" ) { 
        $GitWebCSS  = "/static";
      }  
      elsif( -e -e "/usr/share/gitweb/gitweb.css" ) { 
        $GitWebCSS  = "";
      }
      else {
	$gitweb_theme="disabled";
        warn "Gitweb is enabled in /usr/share/gitweb, but could not find 'gitweb.css'!Please install smeserver-gitweb-theme if you need it.\n";
      }
    }
    elsif( -e "/var/www/git/gitweb.cgi" ) { 
      # Git 1.8+
      $GitWebRoot = "/var/www/git";
      # check if smeserver-gitweb-theme is installed
      if( -e "/var/www/git/static/gitweb.css" ) { 
        $GitWebCSS  = "/static";
      }  
      elsif( -e -e "/var/www/git/gitweb.css" ) { 
        $GitWebCSS  = "";
      }
      else {
	$gitweb_theme="disabled";	
        warn "Gitweb is enabled in /var/www/git, but could not find 'gitweb.css'! Please install smeserver-gitweb-theme if you need it.\n";
      }
    }
    else {
      $gitweb="disabled";
      warn "Gitweb is enabled in your configuration, but could not find 'gitweb.cgi'! Considering it as disabled. Please install smeserver-gitweb if you need it.\n";
    }
  }

  # Installation location of the git-http-backend executables
  # Location seems to have shifted between git 1.7 and git 1.8

  my $GitHttpBackendPath = "";
  if( -e "/usr/libexec/git-core/git-http-backend" ) {
    $GitHttpBackendPath = "/usr/libexec/git-core";
  }
  elsif( -e "/usr/bin/git-http-backend" ) {
    $GitHttpBackendPath = "/usr/bin";
  }
  else {
    die "Couldn't find 'git-http-backend'\n";
  }

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ HTTP Git Configuration

  $OUT .= "\n";
  if( $port eq 80 ) {
    $OUT .= "    # ~~~ GIT HTTP CONFIGURATION BEGIN ~~~\n";
    $OUT .= "\n";
    $OUT .= "    # Ensure all /git access is using HTTPS\n";
    $OUT .= "    RewriteEngine   on\n";
    $OUT .= "    RewriteCond %{HTTPS} =off [NC]\n";
    $OUT .= "    RewriteRule ^/git(/.*|\$)    https://%{HTTP_HOST}/git\$1 [L,R]\n";
    $OUT .= "    <Location \"/git\">\n";
    $OUT .= "      Require ssl\n";
    $OUT .= "    </Location>\n";
    $OUT .= "\n";
    $OUT .= "    # ~~~ GIT HTTP CONFIGURATION END ~~~\n";
  }

  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #~~~ HTTPS Git Configuration

  if( $port eq 443 ) {
    $OUT .= "    # ~~~ GIT HTTPS CONFIGURATION BEGIN ~~~\n";
    $OUT .= "\n";

    $OUT .= "    # Enable rewriting (may have been enabled earlier)\n";
    $OUT .= "    RewriteEngine   on\n";
    $OUT .= "\n";

    if( $debugging eq 'enabled' ) {
      $OUT .= "    # Set logging options - mainly for debugging\n";
      $OUT .= "    LogLevel        debug\n";
      $OUT .= "    # for debugging rewrite rules\n";
      $OUT .= "    RewriteLog      /var/log/httpd/rewrite_log\n";
      $OUT .= "    RewriteLogLevel 9\n";
      $OUT .= "\n";
    }

    $OUT .= "    # Setup password authentication method\n";
    $OUT .= "    DefineExternalAuth pwauth pipe /usr/bin/pwauth\n";
    $OUT .= "\n";

    $OUT .= "    # ~~~ GIT BACKEND CONFIGURATION ~~~\n";
    $OUT .= "\n";
    $OUT .= "    SetEnv GIT_PROJECT_ROOT $GitRepositoryRoot\n";
    $OUT .= "    SetEnv GIT_HTTP_EXPORT_ALL\n";
    #  $OUT .= "    SetEnv REMOTE_USER REDIRECT_REMOTE_USER\n";
    $OUT .= "\n";

    $OUT .= "    # For access to git-receive-pack items (i.e. Git PUSH\n";
    $OUT .= "    # operations) rewrite the URL to an internal virtual\n";
    $OUT .= "    # push directory that can be handled in a separate\n";
    $OUT .= "    # 'Location' directive. In that way we can handle\n";
    $OUT .= "    # the PUSH authorisation separately from the\n";
    $OUT .= "    # PULL authorisation.\n";
    $OUT .= "\n";
    $OUT .= "    RewriteCond %\{IS_SUBREQ\}    f\n";
    $OUT .= "    RewriteCond %\{REQUEST_URI\}  !^/push/\n";
    $OUT .= "    RewriteCond %\{QUERY_STRING\} service=git-receive-pack [OR]\n";
    $OUT .= "    RewriteCond %\{REQUEST_URI\}  /git-receive-pack\$\n";
    $OUT .= "    RewriteRule ^\(.*\)\$          /push%\{REQUEST_URI\} [PT]\n";
    $OUT .= "\n";

    $OUT .= "    # We use ScriptAliasMatch to match those URLs that\n";
    $OUT .= "    # git-http-backend can handle and forward the rest\n";
    $OUT .= "    # to gitweb. In this first one we catch the PUSH\n";
    $OUT .= "    # virtual directory and pass the URL without the\n";
    $OUT .= "    # /push/git/ to git-http-backend\n";
    $OUT .= "    ScriptAliasMatch \\\n";
    $OUT .= "            \"\(\?x\)\^\/push$gitpath\/\(\[^/\]+\.+?\\.git/\(HEAD | \\\n";
    $OUT .= "                                 info/refs \| \\\n";
    $OUT .= "                                 objects/\(info/\[^/\]+ \| \\\n";
    $OUT .= "                                          \[0-9a-f\]\{2\}/\[0-9a-f\]\{38\} \| \\\n";
    $OUT .= "                                          pack/pack-\[0-9a-f\]\{40\}\\.\(pack|idx\)\) \| \\\n";
    $OUT .= "                                 git-\(upload\|receive\)-pack\)\)\$\" \\\n";
    $OUT .= "            $GitHttpBackendPath/git-http-backend/\$1\n";
    $OUT .= "\n";

    $OUT .= "    # In this second one we catch all the normal\n";
    $OUT .= "    # Git URLs for git-http-backend\n";
    $OUT .= "    ScriptAliasMatch \\\n";
    $OUT .= "            \"\(\?x\)\^$gitpath\/\(\[^/\]+\.+?\\.git/\(HEAD | \\\n";
    $OUT .= "                            info/refs \| \\\n";
    $OUT .= "                            objects/\(info/\[^/\]+ \| \\\n";
    $OUT .= "                                     \[0-9a-f\]\{2\}/\[0-9a-f\]\{38\} \| \\\n";
    $OUT .= "                                     pack/pack-\[0-9a-f\]\{40\}\\.\(pack|idx\)\) \| \\\n";
    $OUT .= "                            git-\(upload\|receive\)-pack\)\)\$\" \\\n";
    $OUT .= "            $GitHttpBackendPath/git-http-backend/\$1\n";
    $OUT .= "\n";

    $OUT .= "    # Access permissions for the git root directory\n";
    $OUT .= "    <Directory \"$GitRepositoryRoot\">\n";
    $OUT .= "      Options        +ExecCGI\n";
    $OUT .= "      Require all granted\n";
    $OUT .= "    </Directory>\n";
    $OUT .= "\n";

    $OUT .= "    # Access permissions for git backend scripts\n";
    $OUT .= "    <Directory \"$GitHttpBackendPath\">\n";
    $OUT .= "      Options        +ExecCGI +Indexes\n";
    $OUT .= "      Require all granted\n";
    $OUT .= "    </Directory>\n";
    $OUT .= "\n";

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #~~~ Setup Gitweb and optional Github theme

    $OUT .= "    # ~~~ GITWEB CONFIGURATION ~~~\n\n";
    if( $gitweb eq 'enabled' ) {
      $OUT .= "    # config getprop git GitWeb == enabled\n\n";

      $OUT .= "    # If HTTP:Authorization was send in the request,\n";
      $OUT .= "    # ensure we make it available as the HTTP_AUTHORIZATION\n";
      $OUT .= "    # environment variable for use in authentication\n";
      $OUT .= "    # checks by gitweb.\n";
      $OUT .= "\n";
      $OUT .= "    RewriteCond %\{HTTP:Authorization\} ^(.+)\n";
      $OUT .= "    RewriteRule .? - [e=HTTP_AUTHORIZATION:%1]\n";
      $OUT .= "\n";

      $OUT .= "    # Load configuration for Gitweb\n";
      $OUT .= "    SetEnv GITWEB_CONFIG /etc/gitweb.conf\n";
      $OUT .= "\n";

      $OUT .= "    # Ensure all /git access is using HTTPS\n";
      $OUT .= "    <Location \"/git\">\n";
      $OUT .= "      DirectorySlash On\n";
      $OUT .= "      SSLRequireSSL\n";
      $OUT .= "    </Location>\n";
      $OUT .= "\n";

      $OUT .= "    # Ensure we can browse the repo with http://server.com/git/repo.git\n";
      $OUT .= "    # instead of http://server.com/git?p=repo.git;a=summary.\n";
      #$OUT .= "    RewriteCond %{REQUEST_FILENAME} !-f\n";
      #$OUT .= "    RewriteCond %{REQUEST_FILENAME} !-d\n";
      #$OUT .= "    RewriteRule ^$gitpath\/gitweb.cgi\(\.\*\\.git)\$ $gitpath/gitweb.cgi\$1 [PT]\n";
      $OUT .= "    RewriteRule ^/git\$ /git/ [R]\n";

      $OUT .= "    RewriteCond %{REQUEST_URI} !=$gitpath/gitweb.*\n";
      $OUT .= "    RewriteCond %{REQUEST_FILENAME} !-f\n";
      $OUT .= "    RewriteCond %{REQUEST_FILENAME} !-d\n";
      $OUT .= "    RewriteRule ^$gitpath(.*\\.git)?\$ $gitpath/gitweb.cgi\$1 [L,PT]\n\n";

      #$OUT .= "    RewriteCond %{REQUEST_URI} !=$gitpath/gitweb.*\n";
      #$OUT .= "    RewriteCond %{REQUEST_FILENAME} !-f\n";
      #$OUT .= "    RewriteCond %{REQUEST_FILENAME} !-d\n";
      #$OUT .= "    RewriteRule ^$gitpath(.*\\.git/(?!/?(HEAD|info|objects|refs)).*)?\$ $gitpath/gitweb.cgi%{REQUEST_URI} [L,PT]\n\n";
 

      if( $gitweb_theme eq 'enabled' ) {
        $OUT .= "    # config getprop git GitWebTheme == enabled\n";
        $OUT .= "    # Overide the standard gitweb CSS, JS and image files to enable the theme.\n";
        $OUT .= "    Alias  $gitpath$GitWebCSS/gitweb.css      /etc/e-smith/web/common/gitweb/gitweb.css\n";
        $OUT .= "    Alias  $gitpath$GitWebCSS/git-favicon.png /etc/e-smith/web/common/gitweb/git-favicon.png\n";
        $OUT .= "    Alias  $gitpath$GitWebCSS/git-logo.png    /etc/e-smith/web/common/gitweb/git-logo.png\n";
        $OUT .= "\n";
      }
      $OUT .= "    # Static & CGI files used by Gitweb. \n";
      $OUT .= "    Alias        $gitpath                 $GitWebRoot\n";
      $OUT .= "\n";

      $OUT .= "    # Access permissions for gitweb scripts\n";
      $OUT .= "    <Directory \"$GitWebRoot\">\n";
      $OUT .= "      Options        +ExecCGI\n";
      $OUT .= "      AllowOverride  None\n";
      $OUT .= "      AddHandler     cgi-script .cgi\n";
      $OUT .= "      DirectoryIndex gitweb.cgi\n";
      $OUT .= "      Require $gitweb_allow\n";
      $OUT .= "    </Directory>\n\n";

      if( $gitweb_theme eq 'enabled' ) {
        $OUT .= "    # Access permissions for additional gitweb theme files\n";
        $OUT .= "    <Directory \"/etc/e-smith/web/common/gitweb\">\n";
        $OUT .= "      AllowOverride  None\n";
        $OUT .= "      Require $gitweb_allow\n";
        $OUT .= "    </Directory>\n\n";
      }
    } else {
      $OUT .= "    # Gitweb disabled or not installed\n\n";
    }

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #~~~ Setup access for individual Git repositories

    $OUT .= "    # ~~~ GIT REPOSITORIES CONFIGURATION START ~~~\n\n";

    my @repositories = $git_db->get_all_by_prop('type' => 'repository');
    foreach my $repository (@repositories)
    {
      my $git_repository          = $repository->key;
      my %properties              = $repository->props;

      # Retrieve the network access rules for the repository
      my $satisfy                 = 'All';
      my $allow_from_network = 'ip 127.0.0.1'; # Catch incorrect values, including empty ones
      if( $properties{'allow_access_from'} ) {
        if( $properties{'allow_access_from'} eq 'internet' ) {
          $allow_from_network = 'all granted';
        } elsif ($properties{'allow_access_from'} eq 'local') {
          $allow_from_network = "ip $localAccess";
        }
      }

      # Determine the effective users list from the listed groups and individual users
      my $effective_push_users =
        $git_db->effective_users_list_from( $properties{'push_groups'},
                                            $properties{'push_users'} );

      my $effective_pull_users =
        $git_db->effective_users_list_from( $properties{'pull_groups'},
                                            $properties{'pull_users'} );

      my $effective_pull_push_users =
        $git_db->effective_users_list_from( $properties{'push_groups'},
                                            $properties{'push_users'},
                                            $properties{'pull_groups'},
                                            $properties{'pull_users'} );

      $OUT .= "    # ~~~~~~~~~~~~~~~~~~~~~~~~\n";
      $OUT .= "    # Git Repository         : $gitpath/$git_repository.git (" . ($properties{'description'} || "ERROR - DESCRIPTION NOT CONFIGURED!") . ")\n";
      $OUT .= "    #  - Allow access from   : " . ($properties{'allow_access_from'} || "ERROR - ALLOW_ACCESS_FROM NOT CONFIGURED!") . " => Network allow from $allow_from_network\n";
      $OUT .= "    #  - Effective PULL Users: '" . ($effective_pull_users || "Anonymous") . "' <= (" . ($properties{'pull_groups'} || "none") . "/" . ($properties{'pull_users'}  || "none") . ")\n";
      $OUT .= "    #  - Effective PUSH Users: '" . ($effective_push_users || "Anonymous") . "' <= (" . ($properties{'push_groups'} || "none") . "/" . ($properties{'push_users'}  || "none") . ")\n";
      $OUT .= "    #  - Effective PULL/PUSH : '" . ($effective_pull_push_users || "Anonymous") . "'\n\n";

      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      #~~~ PULL Access Setup

      $OUT .= "    <LocationMatch \"^$gitpath/(gitweb.cgi/|)$git_repository.git\"> # PULL access to $gitpath/$git_repository.git\n";

      $OUT .= "        <RequireAll>\n";

      if( $effective_pull_users ) {
        $OUT .= "        # PULL Access Control\n";
        $OUT .= "        AuthName          \"Git repository: $git_repository\.git (" . ($properties{'description'} || "ERROR - DESCRIPTION NOT CONFIGURED!"). ")\"\n";
        $OUT .= "        AuthType          Basic\n";
        $OUT .= "        AuthBasicProvider external\n";
        $OUT .= "        AuthExternal      pwauth\n";
        $OUT .= "        Require           user  $effective_pull_push_users\n";
        $OUT .= "        SSLRequireSSL     # Non-Anonymous PULL requires SSL\n";
      } else {
        $OUT .= "        # Anonymous PULL Access\n";
      }
      if( $allow_from_network ne 'all granted' ) {
        $OUT .= "        # Restricted network access\n";
        $OUT .= "        Require $allow_from_network\n";
      } else {
        $OUT .= "        Require all granted \n        # Internet access enabled\n";
      }
      $OUT .= "        </RequireAll>\n";
    
      $OUT .= "    </LocationMatch> # $gitpath/$git_repository.git\n\n";

      #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      #~~~ PUSH Access Setup - uses a virtual /push directory in the path that was
      #                        setup using previus RewriteCond rules looking for the
      #                        git-receive-pack items that Git requests when pushing.

      $OUT .= "    <Location \"/push$gitpath/$git_repository.git\">  # PUSH access to $gitpath/$git_repository.git\n";

      $OUT .= "        <RequireAll>\n";
      if( $effective_push_users ) {
        $OUT .= "        # PUSH Access Control\n";
        $OUT .= "        AuthName          \"Git repository: $git_repository\.git (" . ($properties{'description'} || "ERROR - DESCRIPTION NOT CONFIGURED!"). ")\"\n";
        $OUT .= "        AuthType          Basic\n";
        $OUT .= "        AuthBasicProvider external\n";
        $OUT .= "        AuthExternal      pwauth\n";
        $OUT .= "        Require           user  $effective_push_users\n";
        $OUT .= "        SSLRequireSSL     # Non-Anonymous PUSH requires SSL\n";
      } else {
        $OUT .= "        # Anonymous PUSH Access\n";
      }
      if( $allow_from_network ne 'all granted' ) {
        $OUT .= "        # Restricted network access\n";
        $OUT .= "        Require $allow_from_network\n";
      } else {
        $OUT .= "        Require all granted \n        # Internet access enabled\n";
      }
      $OUT .= "        </RequireAll>\n";
    
      $OUT .= "    </Location> # /push$gitpath/$git_repository.git\n\n";
    }

    $OUT .= "    # ~~~ GIT REPOSITORIES CONFIGURATION END ~~~\n";
    $OUT .= "    # ~~~ GIT HTTPS CONFIGURATION END ~~~\n";
  }
}
