<?php
/*
***************************************************************************
* Copyright (C) 2007-2008 by Sixdegrees *
* hide@address.com *
* "Working with freedom" *
* http://www.sixdegrees.com.br *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************
*/
$pwd = dirname(__FILE__);
require("${pwd}/define.php");
/**
* PHP CVS CLIENT
*
* This class is a CVS client. It can perform read operations
* to a CVS server (pserver).
* It can get directory files, file contents, logs. All the operaration
* could be done for a specific version or for the last version.
*
* @author Cesar D. Rodas <hide@address.com>
* @license BSD License
*/
class phpCVSclient {
/**
* Server hostname or ip
*
* @var string
* @access private
*/
var $server;
/**
* User
*
* @var string
* @access private
*/
var $user;
/**
* Password
*
* @var string
* @access private
*/
var $password;
/**
* Port to connect
*
* @var integer
* @access private
*/
var $port;
/**
* Server root directory
*
* @var string
* @access private
*/
var $root;
/**
* Module name
*
* @var string
* @access private
*/
var $module;
/**
* Are we connected to a CVS server?
*
* @var bool
* @access private
*/
var $conected;
/**
* Connection object.
*
* @var resource
* @access private
*/
var $fp;
/**
* Error string
*
* @var string
* @access public
*/
var $errorstr;
/**
* Debug
*
* @var bool
* @access private
*/
var $debug;
/**
* Temporary directory
*
* @var string
* @access private
*/
var $tmp="./";
/**
* Debug in this file.
*
* @var string
* @access private
*/
var $debugfile;
function phpCVSclient($server='',$password='',$module='') {
$this->setServer($server);
$this->setPassword($password);
$this->setModule($module);
}
/**
* Set password
*
* @param string $password Password
*/
function setPassword($password) {
$this->password=$password;
return true;
}
/**
* Returns actual password
*
* @return string Password
*/
function getPassword() {
return $this->password;
}
/**
* Set Temporary Directory
*
* In the temporary directory, in previous
* version, it was where the transaction file was
* saved.
*
* A transaction file was a file with all the transaction
* when on "checkout" for improve performance beacouse most
* all repositories are too big to keep on main memory. When
* the transactions end, and every file was extracted this file
* was deleted.
*
* In this version the "checkout" doesn't use transaction file,
* every file was saved into its destiny file, and deleted for
* main memory.
*
* @param string $tmp Directory path
*/
function setTmpDir($tmp) {
$this->tmp=$tmp;
}
/**
* Get Temporary Directory
* @return string
*/
function getTmpDir() {
return $this->tmp;
}
/**
* Set server
*
* Set a new CVS server to connect.
*
* <code>
*<?php
* include("phpcvsclient.php");
* $cvs = new phpCVSclient;
* $r=$cvs->setServer("hide@address.com:/repository");
* if ( !$r ) die("Invalid server");
*
*?>
* </code>
*
* @param string $server Server connection
* @return bool True if success
*/
function setServer($server) {
if ( $server == '') return false;
$port = 2401;
$info = parse_url($server);
$this->server = $info['host'];
$this->root = $info['path'];
$this->user = $info['user'];
$this->port = isset($info['port']) ? $info['port'] : 2401;
return true;
}
/**
* Return server information
*
* @return array
*/
function getServer() {
return array('server'=>$this->server,'root'=>$this->root,'user'=>$this->user,'port'=>$this->port);
}
/**
* Set Module.
*
* @param string $module Module name
* @return boolean True if success
*/
function setModule($module) {
if ( $module == '') return false;
$this->module = $module;
return true;
}
/**
* Get actual module name
*
* @return string Module name
*/
function getModule() {
return $this->module;
}
/**
* Login into a CVS server
*
* @return boolean True if success
*/
function login() {
$this->sendline(CVS_INIT_AUTH);
$this->sendline($this->root);
$this->sendline($this->user);
$this->sendline($this->encode($this->getPassword(),"A") );
$this->sendline(CVS_END_AUTH);
$r = $this->readline();
if ( $r != CVS_LOGIN ) {
if ( $r == CVS_WRONG_PASSWORD) {
$this->errorstr = "Bad password";
} else
$this->errorstr = $r;
return false;
}
$this->sendline("Root $this->root");
$this->sendline("gzip-file-contents 6");
return true;
}
/**
* Checkout
*
* Checkout a repository and download the head
* version to a folder in our hdd
*
* <code>
*<?php
* include("phpcvsclient.php");
* $cvs = new phpCVSclient;
* $r=$cvs->setServer("hide@address.com:/repository");
* if ( !$r ) die("Invalid server");
* $foo = $cvs->checkout( array(DST_FOLDER=>"folder-in-hdd/") );
* if ( $foo ) print_r($foo);
*?>
* </code>
*
* @param array $param
* @return array with files
*/
function checkout($param) {
$folder = isset($param[DST_FOLDER]) ? $param[DST_FOLDER] : false;
$this->addArgument($this->module);
$this->sendline(CVS_CHECKOUT);
$r = $this->processFiles($folder,true);
return $r;
}
/**
* Get File Logs
*
* Return an array with the change log.
*
* @param string $file File name
* @return array
*/
function getFileLogs($file) {
$this->addArgument($this->module."/".$file);
$this->sendline("rlog");
$f='';
while (1) {
$f.=$this->read(4096);
$fin = substr($f,-4,4);
if ($fin == "\nok\n" || $fin=="*ok\n") break;
}
$comments = explode("M ----------------------------\n",$f);
for($i=1; $i < count($comments);$i++)
$r[] = $this->parsecomments($comments[$i]);
return $r;
}
/**
* Parse comments.
*
* @param string $txt Text to parse
* @retun array parsed comments
* @access private
*/
function parsecomments($txt) {
$l = explode("\n",$txt);
$i=0;
$ret['comment']='';
unset($l[ count($l) -1]);
unset($l[ count($l) -1]);
unset($l[ count($l) -1]);
foreach($l as $line) {
switch($i) {
case 0:
$f = strpos($line,"revision ") + 9;
$ret['version'] = substr($line,$f);
break;
case 1:
$f = strpos($line,"date: ") + 6;
$fin = strpos($line,";",$f)-$f;
$ret['date'] = substr($line,$f,$fin);
$f = strpos($line,"author: ") + 8;
$fin = strpos($line,";",$f)-$f;
$ret['author'] = substr($line,$f,$fin);
$f = strpos($line,"lines: ") + 7;
$ret['lines'] = substr($line,$f);
break;
default:
if (substr($line,0,2) == "M ") $line=substr($line,2);
$ret['comment'] .= $line."\n";
}
$i++;
}
return $ret;
}
/**
* Process file
*
* Handle the file transaction, and save into hard disk.
*
* @param string $folder Destination folder
* @param string $infile If it is false all the file will be saved into main memory
* @return array File list and version
* @access private
*/
function processFiles($folder,$infile) {
$buffer='';
$files = array();
$fcnt=0;
$state = NOTHING;
while ( $r = $this->read(4096) ) {
$buffer .= $r;
/* buffer processing loop */
while ( $buffer != '') {
switch ( $state ) {
case NOTHING:
while ( substr($buffer,0,14) == "E cvs checkout" ) {
$i = strpos($buffer,"\n");
$buffer = substr($buffer,$i+1);
}
if ( substr($buffer,0,4) != "M U ")
break 2;
$fin = substr($buffer,-4,4);
if ( $fin== "\nok\n" || $fin=="*ok\n" || substr($buffer,-3,3)=="ok\n") {
break 3;
}
# Split the commands into lines
#
$f = explode("\n",$buffer,7);
if ( count($f) != 7) break 2;
$file = & $files[ $fcnt++ ] ;
$name = $f[0];
if ( substr($name,0,4) == "M U ")
$name = substr($name,4);
$name = $folder."/".$name;
$file['name'] = $name;
$version=explode("/",$f[3]);
$file['version'] = $version[2];
$file['compressed'] = $f[5][0] == "z";
$file['size'] = $file['compressed'] ? substr($f[5],1) : $f[5];
$t = strlen($f[6]);
$read = $file['size'] < $t ? $file['size'] : $t;
$content=substr( $f[6],0,$read);
if ( $infile ) {
$this->makefolder($name);
$fpn = fopen($name,"wb");
fwrite($fpn, $content);
} else {
$file['content'] = $content;
}
$file['missing'] = $file['size'] - strlen($content);
$buffer=substr($f[6],$read);
if ( $file['missing']>0 ) {
$buffer="";
$state = IN_FILE;
} else {
if ( $file['compressed'] ) {
if ( $infile ) {
$uncompress=gzinflate( substr(file_get_contents($file['name']),10) );
file_put_contents($file['name'], $uncompress );
$file['size'] = strlen($uncompress);
} else {
$file['content'] = gzinflate( substr($file['content'],10) );
$file['size'] = strlen($file['content']);
}
}
unset($file['missing']);
unset($file['compressed']);
}
break;
case IN_FILE:
$t = strlen($buffer);
$missing = $file['missing'] < $t ? $file['missing'] : $t;
$content = substr( $buffer,0,$missing);
if ( $infile )
fwrite($fpn,$content);
else
$file['content'] .= $content;
$file['missing'] -= $missing;
$buffer=substr($buffer,$missing);
if ( $file['missing']==0 ) {
if ( $infile ) {
fclose($fpn);
}
if ( $file['compressed'] ) {
if ( $infile ) {
$uncompress=gzinflate( substr(file_get_contents($file['name']),10) );
file_put_contents($file['name'], $uncompress );
$file['size'] = strlen($uncompress);
} else {
$file['content'] = gzinflate( substr($file['content'],10) );
$file['size'] = strlen($file['content']);
}
}
$state = NOTHING;
unset($file['missing']);
unset($file['compressed']);
}
break;
}
}
}
return $files;
}
/**
* Add argument.
*
* @access private
*/
function addArgument($arg) {
$this->sendline("Argument $arg");
}
/**
* Connect
*
* @return boolean
* @access private
*/
function connect() {
$fp = & $this->fp;
$conected = & $this->conected;
$errorstr = & $this->errorstr;
$fp = fsockopen( $this->server, $this->port,$e,$errorstr);
$conected=true;
if ( ! $fp ) {
$conected=false;
}
return $conected;
}
/**
* Return to see if it is connected
* @return boolean
* @access private
*/
function isConnected() {
return $this->conected;
}
/**
* Disconnect client.
*
*/
function disconnect() {
fclose($this->fp);
$this->conected=false;
}
/**
* Send a line to the socket
*
* @param string $txt
* @return boolean
* @access private
*/
function sendline($txt) {
return $this->send($txt."\n");
}
/**
* Write into socket
*
*
* @param string $txt
* @return boolean
* @access private
*/
function send($txt) {
if ( !$this->isConnected() ) {
$f=$this->connect();
if ( !$f ) return false;
}
fwrite($this->fp, $txt);
$this->debug("C",$txt);
return true;
}
/**
* Read one line from socket.
*
* @return string
* @access private
*/
function readline() {
$f = '';
while ( $t = $this->read(1024) ) {
$f .= $t;
if ( ($i=strpos($f,"\n")) !== false) break;
}
$f = substr($f,0,$i);
return $f;
}
/**
* Read from socket
*
*
* @param int $size Bytes to read.
* @return string
* @access private
*/
function read($size) {
if ( !$this->isConnected() ) {
$f=$this->connect();
if ( !$f ) return false;
}
$t=fread($this->fp,$size);
$this->debug("S",$t);
return $t;
}
/**
* Encode a password
*
* @param string $pass Password
* @param string $letter First letter
* @return string encoded password
* @access private
*/
function encode($pass, $letter) {
$code = explode(", ",CVS_AUTH_CODES);
$f=$letter;
for($i=0; $i < strlen($pass); $i++)
$f .=chr( $code[ ord($pass[$i]) ] );
return $f;
}
/**
* Set Debug
*
*
* @param int $dev NONE, TEXT,HTML
* @param boolean|string $file Debug in file.
*/
function setDebug($dev,$file=false) {
$this->debug = $dev;
if ( $file) {
file_put_contents($file,"");
}
$this->debugfile=$file;
}
/**
* Debug
*
* Debugging information
*
* @param string $whom Which module or function trigger the debbug
* @param string $what Debug text
* @access private
*/
function debug($whom,$what) {
if ( $this->debug == NONE) return;
$txt = ( $this->debug >= HTML ? nl2br(htmlentities("$whom: ".$what))."<br>" : "$whom: $what" );
if ( $this->debugfile ){
$fp = fopen($this->debugfile,"a");
fwrite($fp,$txt);
fclose($fp);
return;
}
echo $txt;
flush();
}
/**
* Return temporary name
*
* @access private
*/
function getTmpName() {
return tempnam($this->tmp,"phpcvs");
}
/**
* Create directories recursively
*
* @param string $fname Path
* @access private
*/
function makefolder($fname) {
$f=explode("/",$fname);
$acc = '';
for($i=0; $i < count($f)-1;$i++) {
$acc.=$f[$i]."/";
@mkdir($acc);
}
}
}
?>