<?php
class IF_SVNList
{
var $path = "";
var $entries = array();
var $curEntry = null;
}
class IF_SVNListEntry
{
var $rev = 1;
var $author = "";
var $date = "";
var $size = 0;
var $name = "";
var $isdir = false;
}
class IF_SVNClientC
{
var $is_windows_server = false;
var $svnExe = null;
var $curList = null;
var $curTag = "";
var $error_string = "";
// Command line options which should be used
// for the next command.
var $trust_server_cert = true;
var $non_interactive = true;
var $username = "";
var $password = "";
function __construct()
{
// Server operating system.
$soft = $_SERVER["SERVER_SOFTWARE"];
$soft = strtoupper($soft);
if (strpos($soft, "WIN") !== FALSE)
{
$this->is_windows_server = true;
}
}
function init($svn_exe)
{
$this->svnExe = $svn_exe;
if (!file_exists($svn_exe))
{
$this->error_string = "Path to the svn-executable doesn't exist.";
return false;
}
if (!is_executable($svn_exe))
{
$this->error_string = "The svn-executable is not executable. Missing permissions?";
return false;
}
return true;
}
function encode_path($uri)
{
$uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri);
// Convert to UTF-8 string.
if (function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding"))
$path = mb_convert_encoding($uri, "UTF-8", mb_detect_encoding($uri));
// Skip encoding of 'svn+ssh://' part.
$parts = explode("/", $uri);
$partsCount = count($parts);
for ($i=0; $i<$partsCount; $i++)
{
if ($i != 0 || $parts[$i] != 'svn+ssh:')
{
$parts[$i] = rawurlencode($parts[$i]);
}
}
$uri = implode("/", $parts);
// Subversion bug?
$uri = str_replace("%3A", ":", $uri);
// Quick fix for Windows share names.
if ($this->is_windows_server)
{
if (substr($uri, 0, 2) == "//")
{
$uri = '\\'.substr($uri, 2);
}
if (substr($uri, 0, 10) == "file://///")
{
$uri = "file:///\\\\".substr($uri, 10);
}
}
return $uri;
}
function encode_string($s, $dest_enc = "UTF-8")
{
if (function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding"))
{
$s = mb_convert_encoding($s, "UTF-8", mb_detect_encoding($str));
}
return $s;
}
function create_svn_command($command, $repo_path, $args=null, $asXml=true)
{
$cmd = "\"".$this->svnExe."\" ".$command;
if ($asXml === true)
$cmd.= " --xml";
if ($this->non_interactive)
$cmd.= " --non-interactive";
if ($this->trust_server_cert)
$cmd.= " --trust-server-cert";
if (!empty($this->username))
$cmd.= " --username ".$this->username;
if (!empty($this->password))
$cmd.= " --password ".$this->password;
// Handle custom args.
if (!empty($args))
{
foreach ($args as $key => &$val)
{
$cmd.= " ".$key;
if (!empty($val))
$cmd.= " ".escapeshellarg($val);
}
}
// Automatic prepend the "file://" prefix, if nothing
// else is given.
if (preg_match('/^[a-z0-9+]+:\/\//i', $repo_path))
;
else
{
if (strpos($repo_path, "/") === 0)
$repo_path = "file://".$repo_path;
else
$repo_path = "file:///".$repo_path;
}
$cmd.= " ".$repo_path;
return ''.$cmd.'';
}
function svn_mkdir($path, $parents=true, $commitMessage=null)
{
if (empty($path))
return false;
$args = array();
if ($parents)
$args["--parents"] = "";
if (!empty($commitMessage))
$args["--message"] = $commitMessage;
$command = self::create_svn_command("mkdir", self::encode_path($path), $args, false);
$command = substr($command, 1, -1); // Remove leading and ending slash for "exec" function.
$output = null;
$return_var = 0;
exec($command, $output, $return_var);
if ($return_var != 0)
return false;
return true;
}
function svn_list($path)
{
if (empty($path))
{
$this->error_string = "Empty path for svn_list() command.";
return false;
}
$command = self::create_svn_command("ls", self::encode_path($path), null, true);
$proc_descr = array(
0 => array("pipe", "r"), // STDIN
1 => array("pipe", "w"), // STDOUT
2 => array("pipe", "w") // STDERR
);
$resource = proc_open($command, $proc_descr, $pipes);
if (!is_resource($resource))
return false;
// Create XML-Parser.
$xml_parser = xml_parser_create("UTF-8");
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, array($this, "xml_svn_list_start_element"), array($this, "xml_svn_list_end_element"));
xml_set_character_data_handler($xml_parser, array($this, "xml_svn_list_character_data"));
// Read the XML stream now.
$data_handle = $pipes[1];
while (!feof($data_handle))
{
$line = fgets($data_handle);
if (!xml_parse($xml_parser, $line, feof($data_handle)))
{
// Error.
$this->error_string = "XML parse error: (code=".xml_get_error_code($xml_parser).") ".xml_error_string(xml_get_error_code($xml_parser));
}
}
$error_handle = $pipes[2];
$error_message = "";
while (!feof($error_handle))
{
$error_message.= fgets($error_handle);
}
if (!empty($error_message))
$this->error_string.= "(".$this->svnExe." error=".$error_message.")";
// Free resources.
xml_parser_free($xml_parser);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($resource);
return $this->curList;
}
/////
//
// XML Handler Functions
//
/////
function xml_svn_list_start_element($xml_parser, $tagname, $attrs)
{
//echo "xml_svn_list_start_element($xml_parser, $tagname, $attrs)\n";
switch($tagname)
{
case "LIST":
$this->curList = new IF_SVNList();
if (count($attrs))
foreach ($attrs as $aName => $aVal)
switch($aName)
{
case "PATH":
$this->curList->path = self::encode_string($aVal);
break;
}
break;
case "ENTRY":
$this->curList->curEntry = new IF_SVNListEntry;
if (count($attrs))
{
foreach ($attrs as $aName => $aVal)
{
switch($aName)
{
case "KIND":
if ($aVal == "dir")
$this->curList->curEntry->isdir = true;
else
$this->curList->curEntry->isdir = false;
break;
}
}
}
break;
case "COMMIT":
if (count($attrs))
{
foreach ($attrs as $aName => $aVal)
{
switch ($aName)
{
case "REVISION":
$this->curList->curEntry->rev = $aVal;
break;
}
}
}
break;
}
$this->curTag = $tagname;
}
function xml_svn_list_character_data($xml_parser, $tagdata)
{
switch ($this->curTag)
{
case "NAME";
if ($tagdata === false || $tagdata === "")
return;
$this->curList->curEntry->name.= self::encode_string(trim($tagdata));
break;
case "AUTHOR":
if ($tagdata === false || $tagdata === "")
return;
$this->curList->curEntry->author.= trim($tagdata);
break;
case "DATE":
if ($tagdata === false || $tagdata === "")
return;
$this->curList->curEntry->date.= trim($tagdata);
break;
}
}
function xml_svn_list_end_element($xml_parser, $tagname)
{
switch ($tagname)
{
// Add the created entry to the list as child.
case "ENTRY":
$this->curList->entries[] = $this->curList->curEntry;
break;
case "LISTS":
unset($this->curList->curEntry);
break;
}
}
}
?>