<?php
/*
 * GranJefe PHP emulator v0.93
 * © 2006-2007 Matías Software Group
 * $Id: phpgjemulator 1466 2008-08-17 23:44:02Z sog $
 */
define('GJ_EMERG',0);
define('GJ_ALERT',1);
define('GJ_CRIT',2);
define('GJ_ERR',3);
define('GJ_WARN',4);
define('GJ_NOTICE',5);
define('GJ_INFO',6);
define('GJ_DEBUG',7);

require_once 'Zend/Json.php';

class Template {
    private static $doctypes = array(
    'xstrict' => array(
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:GJ="http://www.msg.com.mx/GJ">' ),
    'xtransi' => array(
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:GJ="http://www.msg.com.mx/GJ">' ),
    'hstrict' => array(
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns:GJ="http://www.msg.com.mx/GJ">' ),
    'htransi' => array(
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:GJ="http://www.msg.com.mx/GJ">' ),
    'hframes' => array(
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
    "http://www.w3.org/TR/html4/frameset.dtd">
<html xmlns:GJ="http://www.msg.com.mx/GJ">' ),
    );
    private $defal = array();
    private $stack = array();
    private $root;
    private static function _genheader(
	&$self, $rtc, $var, $title='', $dtype='htransi', $chrset='UTF-8'
    ) {
	$frame =& $self->getframe($rtc);
	if(preg_match('/^x/',$dtype)) {
	    $mime = 'application/xhtml+xml';
	} else {
	    $mime = 'text/html';
	}
	$frame['gjf'] = array(
	    'doctype' => Template::$doctypes[$dtype],
	    'charset' => $chrset,
	    'mime'    => $mime,
	    'title'   => $title,
	    'ispage'  => preg_match('/page/',$var),
	);
	return 'gjf_stdheaders.html';
    }
    public function &getframe($ctx, $frameid=false) {
	foreach($this->stack as &$frm) {
	    if(array_key_exists("_$ctx",$frm) &&
	       (!$frameid || preg_match("/^$frameid/",$frm["_$ctx"]))) {
		return $frm;
	    }
	}
    }
    public function assign() {
	$vars = func_get_args();
	while(count($vars) > 0) {
	    if(is_array($vars[0])) {
		array_push($this->stack, array_shift($vars));
		continue;
	    }
	    $key = array_shift($vars);
	    $this->defal[$key] = array_shift($vars);
	}
    }
    public function stack_mark() {
	return count($this->stack);
    }
    public function clear_stack($msp) {
	while($this->stack_mark() > $msp) {
	    array_pop($this->stack);
	}
    }
    public function rvalue($key) {
	for($c = $this->stack_mark() - 1; $c >= 0; $c--) {
	    // GJ::Log(GJ_DEBUG," Check for $key in ".
	    //   implode(',',array_keys($this->stack[$c])));
	    if(array_key_exists($key,$this->stack[$c]))
		return $this->stack[$c][$key];
	}
	if(array_key_exists($key,$this->defal)) {
	    return $this->defal[$key];
	}
	return null;
    }
    public function value($var, &$ctx, $warn) {
	$parms = array();
	if(empty($ctx)) $ctx = 'VAL';
	do {
	    if(preg_match('/^{(.*)}$/',$var,$mat)) { //Recurse
		$nv = $this->value($mat[1],$ctx,false);
		if(isset($nv)) {
		    $var = $nv;
		    continue;
		}
	    } elseif(preg_match('/^(\w+)\((.*)\)$/',$var,$mat)) { // Params
		$var = $mat[1];
		foreach(explode(',',$mat[2]) as $par) {
		    $t = $this->value($par,$ctx,$warn);
		    if(is_null($t)) $t = '';
		    $parms[] = $t;
		}
		break;
	    } elseif(preg_match('/\+/',$var)) {
		$v = '';
		foreach(explode('+',$var) as $par) {
		    $t = $this->value($par,$ctx,$warn);
		    if(is_null($t)) $t = '';
		    $v .= $t;
		}
		break;
	    }elseif(preg_match('/^([\'\"])(.*)\1$/',$var,$mat)) { // Lit
		$v = $mat[2];
		break;
	    } elseif(preg_match('/^(\w+)\[(.+)\]$/',$var,$mat)) { // Index
		if(!is_numeric($mat[2]))
		    $mat[2] = $this->value($mat[2],$ctx,false);
		$v = $this->rvalue($mat[1]);
		if(is_array($v)) $v = $v[$mat[2]];
		break;
	    }
	    break;
	} while(true);
	if(!isset($v)) {
	    $v = $this->rvalue($var);
	}
	if(is_array($v) && is_callable($v, false, $callname)) {
	    // GJ::Log(GJ_DEBUG,"Callable $var: $callname");
	    $tv = call_user_func_array($v,
		array_merge(array($this, $ctx, $var),$parms));
	    if(isset($tv)) $v = $tv;
	}
	if(!isset($v)) {
	    if($warn) GJ::Log(GJ_WARN,
		"[GJ::Template] No value for variable '$var' in $ctx");
	    return null;
	}
	return $v;
    }
    public function proctempl($action) {
	if(file_exists("{$this->root}/$action")) {
	    $content = file("{$this->root}/$action");
	} elseif(file_exists("lib/templates/$action")) {
	    $content = file("lib/templates/$action");
	} elseif(file_exists("{$this->root}/$action.html")) {
	    $content = file("{$this->root}/$action.html");
	} elseif(file_exists("lib/templates/$action.html")) {
	    $content = file("lib/templates/$action.html");
	} else {
	    GJ::Log(GJ_WARN,"Missing template $action");
	    echo "Missing template $action";
	    exit(1);
	}
	return $this->subparse($content);
    }
    public function emit($action) {
	foreach($this->proctempl($action) as $line) {
	    echo $line;
	}
    }
    private function parseiter($iter,$pass,$c,&$t,$li,&$tmp,&$tt) {
	$t['_counter'] = $iter;
	$t['_first'] = $iter == 0;
	$t['_last'] = $iter == $li;
	$t['_next'] = $iter == $li ? '' : ',';
	$t['_toggle'] = $iter % 2;
	$t['_raw'] = isset($pass) ? $pass : '';
	$mark = $this->stack_mark();
	if(is_array($pass)) {
	    $this->assign($pass);
	}
	$tt = array_merge($tt,$this->subparse(array_slice($tmp,$c)));
	$this->clear_stack($mark);
    }
    private function subparse($tmp) {
	$eat = 0;
	$smark = 0;
	$cond = array();
	for($c = 0; $c < count($tmp); $c++) {
	    // GJ::Log(GJ_DEBUG,"Parsing line ".$tmp[$c]);
	    $tt = array();
	    $orig = -1;
	    while(!(($pos = strpos($tmp[$c], '${', ++$orig)) === false)) { //}
		$head = '';
		$var = '';
		$v = '';
		if(!$eat) $head = substr($tmp[$c], 0, $pos);
		$past = 2;
		do {
		    $end = strpos($tmp[$c],'}',$pos+$past);
		    if($end === false) {
			$context = substr($tmp[$c],$pos,10);
			GJ::Log(GJ_WARN,"Missing '}' in '$context'");
			continue 3;
		    }
		    $other = strpos($tmp[$c], '{', $pos+$past);
		    if($other && ($other < $end)) {
			$past = $end-$pos+1;
			continue;
		    }
		    break;
		} while(true);
		$orig = $end;
		$tail = substr($tmp[$c], $end+1);
		$var = substr($tmp[$c], $pos+2, $end-$pos-2);
		if($var == '') continue;
		// Begin on VARIABLE evaluation
		if(preg_match('/(?:NEXT|FI|END)\b/i',$var)) {
		    if(count($cond)) { // Nested
			$eat = array_pop($cond);
			if($eat <> 0) continue;
			if($smark) {
			    $this->clear_stack($smark - 1);
			    $smark = 0;
			}
			$tmp[$c] = "$head$tail";
			$orig = strlen($head) - 1;
		    } else {
			return array_merge(
			    $c>0 ? array_slice($tmp, 0, $c-1) : array(),
			    $tt,
			    $head <> '' ? (array)$head : array()
			);
		    }
		    continue;
		}
		if(preg_match('/^ELSE\b/i',$var)) {
		    if(count($cond) && $cond[count($cond)-1]) continue;
		    if($head <> '') array_push($tt,$head);
		    $tmp[$c] = $tail;
		    $orig = -1;
		    $eat = 1 - $eat;
		    continue;
		}
		$ctx = false;
		if(preg_match('/^(IF(?:NOT)?)\s+(.+)/i',$var,$mat)) {
		    $key = $mat[2];
		    $ctx = 'IF';
		}
		if(preg_match('/^FOR\s+(.+)/i',$var,$mat)) {
		    $key = $mat[1];
		    $ctx = 'FOR';
		}
		if($ctx) {
		    // GJ::Log(GJ_DEBUG,"CTX: $ctx");
		    array_push($cond,$eat);
		    if($eat) continue;
		    $eat = 1;
		    $t = array("_$ctx" => $key);
		    $smark = $this->stack_mark() + 1;
		    $this->assign($t);
		    $v = $this->value($key,$ctx,false);
		    if($head <> '') array_push($tt,$head);
		    $tmp[$c] = $tail;
		    $orig = -1;
		    if($ctx == 'IF') {
			$type = '';
		    } elseif(is_array($v)) {
			$type = 'ARRAY';
		    } else {
			$type = 'SEPA';
		    }
		    if(preg_match('/NOT/i',$var)) $v = !$v;
		    if(!$v) continue;
		    if($type == 'ARRAY') {
			if(empty($v)) continue;
		    } else { // Scalar
			$iter = 0;
			$this->parseiter($iter,$v,$c,$t,0,$tmp,$tt);
		    }
		    $eat = -1;
		}
		if($eat) continue;
		if(preg_match('/^INCLUDE\s+(.+)/i',$var,$mat)) {
		    $key = $mat[1];
		    $ctx = 'INC';
		    $mark = $this->stack_mark();
		    $this->assign(array('_INC' => $key));
		    $template = $this->value($key,$ctx,true);
		    // GJ::Log(GJ_DEBUG,"Include $template($mark)");
		    if($template) {
			if($head <> '') array_push($tt,$head);
			$tmp[$c] = $tail;
			$orig = -1;
			if(is_array($template)) {
			    if($ctx == 'VAL') {
				foreach($template as $templ) {
				    $tt = array_merge($tt,
					$this->proctempl($temp));
				}
			    } else {
				$tt = array_merge($tt,
				    $this->subparse($template));
			    }
			} else {
			    $tt = array_merge($tt, $this->proctempl($template));
			}
		    }
		    $this->clear_stack($mark);
		    // GJ::Log(GJ_DEBUG,"END include: ".$this->stack_mark());
		    continue;
		}
		if(preg_match('/^\?(.+)/',$var,$mat)) {
		    $v = $this->value($mat[1],$ctx,false);
		    if(!isset($v)) $v = '';
		} else {
		    $v = $this->value($var,$ctx,true);
		}
		if(!isset($v)) continue;
		if(is_array($v)) {
		    if($head <> '') array_push($tt,$head);
		    $tt = array_merge($tt,$v);
		    $tmp[$c] = $tail;
		    $orig = -1;
		} else {
		    $tmp[$c] = "$head$v$tail";
		    $orig = strlen($head)+strlen($v)-1;
		}
	    }
	    if(count($tt)) {
		array_splice($tmp,$c,0,$tt);
		$c += count($tt);
	    }
	    if($eat) {
		array_splice($tmp,$c,1);
		$c--;
	    }
	}
	return $tmp;
    }
    public function parse($template) {
	$res = $this->subparse(explode("\n",$template));	
	return join("\n",$res);
    }
    public function __construct($root) {
	$this->root = $root;
    }
}

class GJ {
    const Version = '0.92';
    private static $debug_level = GJ_WARN;
    private static $Root;
    public static $Config;
    public $name = ''; 
    public $session;
    public $action;
    public $extras;
    public $request = array();
    public $tpl;
    public $DB;
    public static function SetLogLevel($sev) {
	if(is_string($sev)) {
	    if(defined('GJ_'.$sev)) {
		$sev = constant('GJ_'.$sev);
	    } else {
		return;
	    }
	}
	GJ::$debug_level = $sev;
    }
    public static function Log($sev,$mess) {
	if($sev > GJ::$debug_level) 
	    return;
	error_log($mess);
    }
    public static function &Configure($SRoot) {
	if(file_exists("$SRoot/conf/gj.ini")) {
	    GJ::$Config = parse_ini_file("$SRoot/conf/gj.ini",true);
	} else {
	    exit("Can't open $SRoot/conf/gj.ini, sorry");
	}
	GJ::$Root = isset(GJ::$Config['GranJefe']['RootDir'])
	    ? GJ::$Config['GranJefe']['RootDir']
	    : $SRoot;
	$Modules = explode(';',GJ::$Config['GranJefe']['Modules']);
	GJ::Log(GJ_DEBUG,"PI: {$_SERVER['PATH_INFO']}");
	$rurl = explode('/',$_SERVER['PATH_INFO']);
	array_shift($rurl); // First empty
	if($rurl[0] == 'magic') {
	    array_shift($rurl);
	}
	$module = strtolower(array_shift($rurl));
	// GJ::Log(GJ_DEBUG,"Root is ".GJ::$Root);
	chdir(GJ::$Root);
	// GJ::Log(GJ_DEBUG,"Request for $module");
	if($module == 'phpinfo') {
	    phpinfo();
	    die;
	}
	ini_set('include_path',
	    dirname($_SERVER['SCRIPT_FILENAME']) .
		":./plib/{$module}.inc" .
		':./plib'
	);
	if(in_array($module,$Modules)) {
	    require_once "$module";
	    $inst =& new $module;
	    $inst->request['session'] = array_shift($rurl);
	    if(empty($inst->request['session']))
		$inst->request['session'] = 'null';
	    $inst->request['action'] = array_shift($rurl);
	    if(empty($inst->request['action'])) 
		$inst->request['action'] = '_start';
	    $inst->extras = $rurl;
	    return $inst;
	} else {
	    GJ::Log(GJ_ERR,'Request for undefined module');
	    $inst->Done(404);
	}
    }
    public function Done($code) {
	if(!headers_sent()) header("HTTP/1.0 $code");
	exit(0);
    }
    public function &Template() {
	return $this->tpl;
    }
    public function MagicBase() {
	return "/magic/{$this->name}";
    }
    public function OnLibDir($file='') {
	$root = GJ::$Root;
	return "$root/lib/{$this->name}/$file";
    }
    public function Param($key) {
	return GJ::$Config["{$this->name}"][$key];
    }
    public function PerRequest() {
	$args = func_get_args();
	if(count($args) == 2)
	    $this->request[$args[0]] = $args[1];
	return $this->request[$args[0]];
    }
    public function SendStatic($file) {
	if(file_exists($file)) {
	    //if(function_exists('finfo_open') &&
	    //   ($fi = finfo_open(FILEINFO_MIME,'/usr/share/file/magic'))) {
	    //	$mime = finfo_file($fi,$file);
	    // } else {
	    //	$mime = exec("file -i -b $file");
	    // }
	    // GJ::Log(GJ_DEBUG,"File $file is $mime");
	    // header("Content-type: $mime");
	    readfile($file);
	    exit(0);
	} else {
	    GJ::Log(GJ_DEBUG,"[GJ::SendStatic] File $file not found");
	    $this->Done(404);
	}
    }
    public function ProcessRequest() {
	$action = $this->request['action'];
	if($this->request['session'] <> 'null') {
	    session_name("SES_{$this->name}");
	    session_id($this->request['session']);
	    session_start();
	    $this->tpl->assign(
		'USER', '',
		'AUTHMODE', 'Untrusted',
		'FULL_NAME', ''
	    );
	}
	// GJ::Log(GJ_DEBUG,"Work $action in {$this->name}");
	// Work
	while(method_exists($this,"AH_$action")) {
	    $method = "AH_$action";
	    $naction = $this->$method($this->extras);
	    if(empty($naction) || $naction == $action) break;
	    $this->request['action'] = $naction;
	    $action = $naction;
	}
	// End Work
	// GJ::Log(GJ_DEBUG,"Sending $action");
	if($action == '_bye' || $action == '_destroy') {
	    session_destroy();
	    $this->request['session'] = 'null';
	    GJ::Log(GJ_NOTICE,"Logout for {$_SESSION['user']}");
	}
	$this->tpl->assign(
	    'CONTINUE', $this->MagicBase() . "/{$this->request['session']}/"
	);
	$this->tpl->emit($action);
    }
    public function __construct() {
	$this->name = strtolower(get_class($this));
        // GJ::Log(GJ_DEBUG,"In GJ constructor for a {$this->name}");
	$this->tpl =& new Template($this->OnLibDir('templates'));
	$this->DB =& new DataBase(
	    $this->Param('DBName'),
	    $this->Param('DBPasswd'),
	    $this->Param('DBInst')
	);
	if(!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
	    $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
	} else {
	    $host = $_SERVER['HTTP_HOST'];
	}
	$ownu = "http://$host" . $this->MagicBase();
	if(!empty($_SERVER['HTTP_REFERER'])) {
	    $refer = $_SERVER['HTTP_REFERER'];
	    // GJ::Log(GJ_DEBUG,"Ref1: $refer($ownu)");
	    $refer = str_replace($ownu,'',$refer);
	} else {
	    $refer = null;
	}
	$this->PerRequest('Host',$host);
	$this->PerRequest('FromAction', $refer);
	// GJ::Log(GJ_DEBUG,"Ref: $refer($ownu)");
	$this->tpl->assign(
	    'STATIC', $this->MagicBase() . '/null/',
	    'APLWNAME', $this->name,
	    'SIGNATURE', 'PHP GJ v' . GJ::Version,
	    'HTTP_HOST', $host,
	    '_ISMIE', strpos($_SERVER['HTTP_USER_AGENT'],'MSIE') > 0 ? 1 : 0,
	    'application_main', 'application_main.html',
	    'application_styles', 'application_styles.html',
	    '_FEATURES', Zend_Json::encode($this->Features),
	    'gjf_pageheader', array('Template','_genheader'),
	    'TITLE', $this->Name . ' v'. $this->Version,
	    $this->Variables
	);
    }
    public function AH__static() {
	$this->SendStatic($this->PerRequest('OutFile'));
    }
    public function AH__start() {
	if(!is_null($this->PerRequest('FromAction'))) {
	    // GJ::Log(GJ_DEBUG,"Fix _start!");
	    // return '_framework';
	}
	return null;
    }
    public function AH__login() {
	session_name("SES_{$this->name}");
	session_start();
	session_regenerate_id();
	$this->PerRequest('session',session_id());
	$_SESSION['user'] = 'Anonymous';
	$_SESSION['cn'] = '';
	GJ::Log(GJ_NOTICE,"Login {$_SESSION['user']}");
	return '_enter';
    }
    public function AH_images($extras) {
	$path = implode('/',$extras);
	if(empty($path)) $this->Done(404);
	$this->PerRequest('OutFile',$this->OnLibDir("images/$path"));
	return '_static';
    }
    public function SessionHelp($topic) {
	$_SESSION['SES_HELP'] = $topic;
    }
    public function AH_help($extras) {
	$topic = implode('/',$extras);
	if(empty($topic)) $topic = 'index';
	if($topic == 'ctx' && isset($_SESSION['SES_HELP'])) {
	    header("Location: http://{$_SERVER['HTTP_HOST']}" .
		$this->MagicBase() . '/null/' .
		"help/{$_SESSION['SES_HELP']}");
	    $this->Done(302);
	}
	$this->tpl->emit('helptop');
	readfile($this->OnLibDir("help/$topic.html"));
	$this->tpl->emit('helpbot');
	exit(0);
    }
}

class Query {
    private $stid = null;
    public function open($con,$sql,$binds) {
	$stid = oci_parse($con,$sql);
	if(!$stid) DataBase::Error('parse',$con);
	if(!empty($binds)) {
	    foreach(array_keys($binds) as $key) {
		oci_bind_by_name($stid, $key, $binds[$key]);
	    }
	}
	if(!oci_execute($stid,OCI_DEFAULT)) DataBase::Error('execute',$stid);
	$this->stdid = $stid;
    }
    public function fetch_all(&$res,$mode = OCI_FETCHSTATEMENT_BY_COLUMN) {
	$num = oci_fetch_all($this->stdid,$res,0,-1,$mode);
	if($num === false) DataBase::Error('fetch_all',$this->stdid);
	return $num;
    }
    public function fetch_all_hash(&$hash,$key=null,
				   $preserve=false, $compress=false) {
	$nr = $this->fetch_all($rows,OCI_FETCHSTATEMENT_BY_ROW);
	$hash = array();
	if(is_null($key) || !array_key_exists($key,$rows[0])) {
	    $key = key($rows[0]);
	    // GJ::Log(GJ_DEBUG,"FAH: key $key");
	}
	if($compress) {
	    for($r = 0; $r < $nr; $r++) {
		$hash[$rows[$r][$key]] = $rows[$r];
		if(!$preserve) unset($hash[$rows[$r][$key]][$key]);
	    }
	} else {
	    for($r = 0; $r < $nr; $r++) {
		$kh = $rows[$r][$key];
		if(!$preserve) unset($rows[$r][$key]);
		if(!isset($hash[$kh])) $hash[$kh] = array();
		array_push($hash[$kh], $rows[$r]);
	    }
	}
	return array_keys($hash);
    }
    public function fetch_array() {
	return oci_fetch_array($this->stdid);
    }
}

class DataBase {
    private $conex = null;
    private $dbparms = null;
    static function Error($func,$hand=null) {
	if(is_null($hand)) {
	    $e = oci_error();
	} else {
	    $e = oci_error($hand);
	}
	GJ::Log(GJ_ERR,"DataBase error: $func, {$e['message']}");
	exit(1);
    }
    public function Handler() {
	if(is_null($this->conex)) {
	    // $c = oci_connect('sinaica','sinadmin','//localhost/XE');
	    $c = oci_connect(
		$this->dbparms[0],
		$this->dbparms[1],
		$this->dbparms[2]
	    );
	    if(!$c) DataBase::Error('connect');
	    $this->conex = $c;
	}
	return $this->conex;
    }
    public function __destructor() {
	GJ::Log(GJ_DEBUG,'Closing DB connextion');
	if(!is_null($this->conex)) oci_close($this->conex);
    }
    public function &Query($sql,$binds=null) {
	$con = $this->Handler();
	$qry =& new Query;
	$qry->open($con,$sql,$binds);
	return $qry;
    }
    public function __construct($dbname,$dbpasswd,$dbinst) {
	// GJ::Log(GJ_DEBUG,"DB Constructor for $dbname");
	$this->dbparms = array($dbname,$dbpasswd,$dbinst);
    }
}

// $GLOBAL['Debug'] = getenv('GJ_DEBUG');
GJ::SetLogLevel('DEBUG');
$SRoot = getenv('SERVER_ROOT') ? getenv('SERVER_ROOT') : '/etc/httpd';
if((isset($_SERVER['REDIRECT_HANDLER']) && 
    $_SERVER['REDIRECT_HANDLER'] == 'gj') ||
   (isset($_SERVER['REDIRECT_URL']) &&
    strpos($_SERVER['REDIRECT_URL'],'magic/') == 1)) {
    $req =& GJ::Configure($SRoot);
    $req->ProcessRequest();
} else {
?>
<html>
<head><title>GranJefe handler</title>
<body>
<h1>PHP GranJefe emulator v<?php echo GJ::Version ?></h1>
<h2>© 2006-2007 Matías Software Group</h2>
<h3>This is the <a href="http://www.msg.com.mx/GranJefe">GranJefe</a> 
emulator service in PHP by 
<a href="http://www.msg.com.mx">Matías Software Group.</a></h3>
</body>
</html>
<?php
}
// vim: syntax=php
?>
