<?

/*

PHPOS, The PHP Persistent Object Store
======================================

Copyright (C) 2003 Vincent Negrier aka. sIX <six@aegis-corp.org>

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

define("PHPOS_VERSION", "0.5.4a");

define("PHPOS_OMP_VERSION", 1);
define("PHPOS_OMP_TIMEOUT", 10);
define("PHPOS_OMP_HDRLEN", 15);

define("PHPOS_DEFAULT_HOST", "localhost");
define("PHPOS_DEFAULT_PORT", 20100);

define("PHPOS_MSG_NEWINST",	1);
define("PHPOS_MSG_BIND", 2);
define("PHPOS_MSG_PROPGET", 3);
define("PHPOS_MSG_PROPSET", 4);
define("PHPOS_MSG_MCALL", 5);
define("PHPOS_MSG_UNBIND", 6);

define("PHPOS_AC_OWNER", 0);
define("PHPOS_AC_AUTH", 1);
define("PHPOS_AC_WORLD", 2);

define("PHPOS_EOK", 0);
define("PHPOS_ENOTFOUND", 1);
define("PHPOS_ENOTALLOWED", 2);

class PHPOS_Long_OID {

	var $login;
	var $password;
	var $hostname;
	var $port;
	var $oid;
	
	function PHPOS_Long_OID($oid) {

		$tmp=array();

		if (ereg("^(([^@:/]+)(:([^@:/]+))?@)?(([^@:/]+)(:([^@:/]+))?/)?(.+)$", $oid, $tmp)) {

			$this->login=$tmp[2];
			$this->password=$tmp[4];
			$this->hostname=$tmp[6]?$tmp[6]:PHPOS_DEFAULT_HOST;
			$this->port=$tmp[8]?$tmp[8]:PHPOS_DEFAULT_PORT;
			$this->oid=$tmp[9];
			
		}
	
	}

}

class PHPOS_OMP {

	function Build_Packet($request_id, $message_type, $message_content) {

		$scontent=serialize($message_content);
		
		$pkt ="PHPOS";
		$pkt.=pack("CVCV", PHPOS_OMP_VERSION, $request_id, $message_type, strlen($scontent));
		$pkt.=$scontent;

		return ($pkt);
	
	}

	function Decode_Packet($pkt) {

		if (!($dcp=PHPOS_OMP::Decode_Packet_Header($pkt))) return false;
		$dcp["message"]=unserialize(substr($pkt, PHPOS_OMP_HDRLEN));

		if ($dcp["omp_version"]!=PHPOS_OMP_VERSION) return false;
		
		return ($dcp);
	
	}
	
	function Decode_Packet_Header($pkt) {

		if (substr($pkt, 0, 5)!="PHPOS") return false;
		$hdr=unpack("Comp_version/Vrequest_id/Cmessage_type/Vmessage_length", substr($pkt, 5, (PHPOS_OMP_HDRLEN-5)));

		return($hdr);
	
	}

}

class PHPOS_Clients_Pool {

	var $clients = array();

	function &New_Client($hostname, $port) {

		if ($c =& new PHPOS_Client($hostname, $port)) $this->clients[$c->chash] =& $c;

		return ($c);
	
	}

	function Dispose_Client($hostname, $port) {

		$chash=$hostname.$port;

		if ($this->clients[$chash]) {
		
			$this->clients[$chash]->Disconnect();
			unset($this->clients[$chash]);

		}

	}
	
	function &Get_Connected_Client($hostname, $port) {

		$chash=$hostname.$port;
		
		if (is_object($this->clients[$chash])) {
			
			if ((!$this->clients[$chash]->connected) && (!$this->clients[$chash]->Connect())) $this->Dispose_Client($hostname, $port);
			return ($this->clients[$chash]);

		} else {

			$c =& $this->New_Client($hostname, $port);
			if (!$c->Connect()) $this->Dispose_Client($hostname, $port);
			return($c);

		}
	
	}

	function Ask_To($hostname, $port, $message_type, $params, $timeout=false) {

		$c =& $this->Get_Connected_Client($hostname, $port);
		return ($c->Ask($message_type, $params, $timeout));

	}

}

class PHPOS_Client {

	var $hostname;
	var $port;
	var $chash;
	var $fd;
	var $connected;
	var $reqid;

	function PHPOS_Client($hostname, $port) {

		$this->hostname=$hostname;
		$this->port=$port;
		$this->chash=$hostname.$port;
	
	}

	function Connect() {

		$errno=$errstr=NULL;
		
		if ($f=@fsockopen($this->hostname, $this->port, $errno, $errstr, PHPOS_OMP_TIMEOUT)) {

			stream_set_timeout($f, PHPOS_OMP_TIMEOUT);
			$this->fd=$f;
			$this->connected=true;

		}

		return($f);
			
	}

	function Ask($message_type, $params, $timeout=false) {

		++$this->reqid;
		
		if ($timeout) stream_set_timeout($this->fd, $timeout);
		
		$pkt=PHPOS_OMP::Build_Packet($this->reqid, $message_type, $params);
		@fwrite($this->fd, $pkt);
		
		while (!$answered) {
		
			if (!$hdrp=@fread($this->fd, PHPOS_OMP_HDRLEN)) return false;
			$hdr=PHPOS_OMP::Decode_Packet_Header($hdrp);

			if (!$msgp=@fread($this->fd, $hdr["message_length"])) return false;
			$msg=unserialize($msgp);
		
			if ($hdr["request_id"]==$this->reqid) $answered=true;
		
		}

		if ($timeout) stream_set_timeout($this->fd, PHPOS_OMP_TIMEOUT);

		return($msg);
	
	}
	
	function Disconnect() {

		if ($this->connected) fclose($this->fd);
	
	}

}

class PHPOS_Remote_Object {

	var $loid;
	
	var $properties;
	var $methods;

	function PHPOS_Remote_Object($loid) {

		$this->loid = new PHPOS_Long_OID($loid);

	}

	function __get($pname, &$value) {

		$p=array(	"oid"		=> $this->loid->oid,
					"property"	=> $pname);

		if ($propget_ret=$GLOBALS["PHPOS_Pool"]->Ask_To($this->loid->hostname, $this->loid->port, PHPOS_MSG_PROPGET, $p)) {

			$value=$propget_ret["value"];
			return true;

		} else {
			
			return false;

		}
	
	}

	function __set($pname, $value) {

		$p=array(	"oid"		=> $this->loid->oid,
					"property"	=> $pname,
					"value"		=> $value);

		$propset_ret=$GLOBALS["PHPOS_Pool"]->Ask_To($this->loid->hostname, $this->loid->port, PHPOS_MSG_PROPSET, $p);

		return ($propset_ret["error"]==0);

	}

	function __call($mname, $params, &$retval) {

		$p=array(	"oid"		=> $this->loid->oid,
					"method"	=> $mname,
					"parameters"=> $params);

		if (in_array($mname, $this->methods)) {
		
			$mcall_ret=$GLOBALS["PHPOS_Pool"]->Ask_To($this->loid->hostname, $this->loid->port, PHPOS_MSG_MCALL, $p);

			if ($mcall_ret["error"]==0) {

				$retval=$mcall_ret["return"];
				return true;
			
			} else {

				return false;
			
			}
				
		} else {

			// Method does not exists

			return false;
		
		}
	
	}

}


class PHPOS_Bind extends PHPOS_Remote_Object {

	function PHPOS_Bind($loid) {

		parent::PHPOS_Remote_Object($loid);
		
		$p=array();
		if ($this->loid->login) $p["login"]=$this->loid->login;
		if ($this->loid->password) $p["password"]=$this->loid->password;
		$p["oid"]=$this->loid->oid;
		
		$bind_ret=$GLOBALS["PHPOS_Pool"]->Ask_To($this->loid->hostname, $this->loid->port, PHPOS_MSG_BIND, $p);

		if ($bind_ret["bound"]) {
		
			$this->methods=$bind_ret["methods"];
			$this->properties=$bind_ret["properties"];

		} else {

			// Can't bind
		
		}
	
	}

}

class PHPOS_Create extends PHPOS_Remote_Object {

	function PHPOS_New($classname, &$oid, $parameters=false, $mode=PHPOS_AC_OWNER, $owner=false) {

		parent::PHPOS_Remote_Object($classname);

		$p=array();
		if ($this->loid->login) $p["login"]=$this->loid->login;
		if ($this->loid->password) $p["password"]=$this->loid->password;
		$p["oid"]=$oid;
		$p["owner"]=$owner?$owner:$this->loid->login;
		$p["access_mode"]=$mode;
		$p["parameters"]=func_get_args();
		
		$newinst_ret=$GLOBALS["PHPOS_Pool"]->Ask_To($this->loid->hostname, $this->loid->port, PHPOS_MSG_NEWINST, $p);

	}

}

overload("PHPOS_Bind");
overload("PHPOS_Create");

$PHPOS_Pool =& new PHPOS_Clients_Pool;

?>