code.dunae.ca

svn2zip.php

<?php
/*
    = SVN2ZIP

    Exports an SVN repository and creates a GZIP'd TAR (tgz) file for download.

    Once the download file has been created it is cached until the repository's 
    revision number changes.

    Only repositories that allow public access are accessible.  There is
    no support for password-protected repositories.


    == Parameters

      * src - either a relative or full public path to an
              SVN repository


    == Usage

    * http://example.com/svn2zip.php?src=/myrepo

      Would download http://example.com/myrepo


    * http://example.com/svn2zip.php?src=code/myrepo/trunk

      Would download http://example.com/code/myrepo/trunk


    * http://example.com/svn2zip.php?src=http://code.example.com/myrepo

      Would download http://code.example.com/myrepo but would only work 
      if ALLOW_REMOTE_SVN_DOWNLOAD was set to true (since it is on a
      different domain)


    Most likely you would add a rewrite rule to your .htaccess so that you
    could have URLs like http://example.com/svn2zip/myrepo

        RewriteRule ^svn2zip/(.+)$      /svn-to-zip.php?src=/$1      [L,NC]



    Code by Alex Dunae (http://code.dunae.ca)
*/



// constants
    
define('CACHE_PATH'$_SERVER['DOCUMENT_ROOT'] . '/_cache/');

    
define('TMP_PATH'$_SERVER['DOCUMENT_ROOT'] . '/_tmp/');

    
// allow this script to download remote repositories?
    
define('ALLOW_REMOTE_SVN_DOWNLOAD'false);



// script begins
    
ob_start();

    
$src = isset($_REQUEST['src']) && !empty($_REQUEST['src']) ? trim($_REQUEST['src']) : null;
    
    if(!
$src)
        
exit_with_error('No repository was specified');

    
// parse and verify the repository's path
    
$src get_svn_path($src);
   
    
// get current repository name and revision number
    
$info get_svn_info($src);

    
// name of the download file
    
$filename $info['path'] . '.rev' $info['revision'] . '.tgz';

    
// temporary path used during export
    
$tmp_path TMP_PATH md5($src);

    
// calculate the cache file's name
    
$target get_cache_file_name($filename);

    
// if possible, serve the file from the cache
    // and exit
    
if(file_exists($target)) {
        
push_zip_and_exit($target$filename);
        exit(
0);
    }
    
    
// export the repository to the temporary path
    
$cmd sprintf("svn export %s %s --force --quiet --ignore-externals"
                    
escapeshellarg($src), 
                    
escapeshellarg($tmp_path)
                  );
    
    
system($cmd$ret);

    if(
$ret != 0)
        
exit_with_error('Unable to export repository');

    
    
// create the archive in the cache
    
$cmd sprintf("tar -czf %s -C %s ."
                    
escapeshellarg($target), 
                    
escapeshellarg($tmp_path)
                  );

    
system($cmd$ret);

    if(
$ret != 0)
        
exit_with_error('Unable to create archive');

    
// serve the file from the cache
    
push_zip_and_exit($target$filename);

    exit(
0);



// functions
    
function get_svn_path($src) {
        global 
$allow_remote_svn_download;

        if(
preg_match('/^(http[s]?)/i'$src) > 0) {
            
// check if repository is hosted on this server or if
            // remote repository downloads are allowed
            
if(!ALLOW_REMOTE_SVN_DOWNLOAD && strcasecmp(parse_url($srcPHP_URL_HOST), $_SERVER['HTTP_HOST']) != 0) {
                
$src null;
            }
        } else {
            
// add preceding slash if missing
            
if(preg_match('/^\//'$src) == 0)
                
$src '/' $src;

            
// build repository name based on current host
            
$src = ($_SERVER['SERVER_PORT'] == 443 'https://' 'http://') . $_SERVER['HTTP_HOST'] . $src;
        }
        
        if(!
$src)
            
exit_with_error('Unable to access the repository');

        return 
$src;
    }

    function 
get_svn_info($src) {
        
$info = array();

        
// run 'svn info' on the repository
        
$cmd sprintf("svn info %s"escapeshellarg($src), escapeshellarg($tmp_path));

        
exec($cmd$out$ret);
        
        if(
$ret != 0)
            
exit_with_error('Unable to get repository info');

        
$out implode($out"\n");

        
// extract the revision number
        
if(preg_match('/^revision\:[ ]?([0-9]+)[ ]?$/im'$out$matches) > 0)
            
$info['revision'] = $matches[1];

        
// extract the path
        
if(preg_match('/^path\:[ ]?([a-z0-9_-]+)[ ]?$/im'$out$matches) > 0)
            
$info['path'] = $matches[1];

        return 
$info;
    }

    function 
push_zip_and_exit($zip_file$filename) {
        
ob_clean();
        
header("Content-Type: application/x-compressed-tar\n");
        
header("Content-Disposition: attachment; filename=\"$filename\"\n");
        
header("Content-Length: " filesize($zip_file) . "\n");
        
readfile($zip_file); 
        exit(
0);
    }



// additional functions from http://code.dunae.ca/web_toolkit.html
    
function exit_with_error($message 'An error occurred') {
        
ob_clean();
        
header('HTTP/1.1 500 Internal Server Error');
        print 
$message;
        exit(
1);
    }

    function 
get_cache_file_name($src$params '') {
        return 
CACHE_PATH basename($src) . '.' md5($src $params) . '.cache';
    }

?>