<?php
require_once($INC . "/prof.inc.php");
require_once($INC . "/log.inc.php");
set_error_handler("rg_error_handler");
register_shutdown_function("rg_error_shutdown");
define('RG_SOCKET_NO_WAIT', 0x01);
$rg_util_error = "";
function rg_util_set_error($str)
{
global $rg_util_error;
$rg_util_error = $str;
}
function rg_util_error()
{
global $rg_util_error;
return $rg_util_error;
}
/*
* This array will keep all registered function for templates
*/
$rg_template_functions = array();
/*
* Register a function to be called when a @@func:func_name:var@@ construction
* is found in a template.
* Please note that the function is called once per template.
*/
function rg_template_func($name, $real_func_name)
{
global $rg_template_functions;
$rg_template_functions[$name] = $real_func_name;
}
function sha512($m)
{
return hash('sha512', $m);
}
function rg_1024($v)
{
if ($v <= 9999)
return number_format($v) . 'B';
$v /= 1024;
if ($v <= 9999)
return number_format($v) . "KiB";
$v /= 1024;
if ($v <= 9999)
return number_format($v) . "MiB";
$v /= 1024;
if ($v <= 9999)
return number_format($v) . "GiB";
$v /= 1024;
return number_format($v) . "TiB";
}
/*
* Returns a binary string of random bytes
*/
function rg_random_bytes($len)
{
rg_prof_start('random_bytes');
$ret = FALSE;
$f = @fopen('/dev/urandom', 'r');
if ($f !== NULL) {
$ret = @fread($f, $len);
fclose($f);
}
if ($ret === FALSE) {
$ret = '';
for ($i = 0; $i < $len; $i++)
$ret .= rand(0, 255);
}
rg_prof_end('random_bytes');
return $ret;
}
/*
* Unique ID generator
*/
function rg_id($len)
{
rg_prof_start('id');
$id = rg_random_bytes(($len + 1) / 2);
$id = bin2hex($id);
$id = substr($id, 0, $len);
rg_prof_end('id');
return $id;
}
/*
* Locks a file
*/
$_lock = array();
function rg_lock($file)
{
global $php_errormsg;
global $_lock;
global $rg_lock_dir;
if (!isset($rg_lock_dir))
$rg_lock_dir = "/var/lib/rocketgit/locks";
// Double locking?
if (isset($_lock[$file])) {
rg_internal_error("Double locking [$file]: "
. rg_array2string($_lock));
return FALSE;
}
$f = @fopen($rg_lock_dir . "/" . $file, "w");
if ($f === FALSE) {
rg_internal_error('Cannot open lock [' . $rg_lock_dir
. '/' . $file . '] (' . $php_errormsg . ').');
return FALSE;
}
if (!flock($f, LOCK_EX | LOCK_NB)) {
fclose($f);
return FALSE;
}
fwrite($f, getmypid() . "\n");
$_lock[$file] = $f;
return TRUE;
}
function rg_lock_or_exit($file)
{
if (rg_lock($file) === FALSE)
exit(0);
}
function rg_unlock($file)
{
global $_lock;
if (isset($_lock[$file]))
fclose($_lock[$file]);
}
function rg_load()
{
return intval(file_get_contents("/proc/loadavg"));
}
/*
* Outputs a string to browser, XSS safe
* Thanks OWASP!
*/
function rg_xss_safe($str)
{
if (!defined('ENT_HTML401'))
define('ENT_HTML401', 0);
return htmlspecialchars($str, ENT_QUOTES | ENT_HTML401, 'UTF-8');
}
/*
* Builds URLs
*/
function rg_re_url($area)
{
return $area;
// TODO: not clear why this. related to the rewrite engine
if (isset($_REQUEST['rwe']))
return $area;
return '/?vv=' . $area;
}
function rg_re_userpage($ui)
{
if (isset($ui['uid']) && ($ui['uid'] == 0))
return '';
if (!isset($ui['organization'])) {
rg_internal_error("rg_re_userpage called with wrong ui (no org)!");
rg_log("ui: " . print_r($ui, TRUE));
exit(1);
}
$prefix = '';
if ($ui['organization'] == 0)
$prefix = '/user';
$s = $prefix . '/' . rawurlencode($ui['username']);
return rg_re_url($s);
}
function rg_re_repopage($ui, $repo_name)
{
$s = rg_re_userpage($ui) . "/" . rawurlencode($repo_name);
return rg_re_url($s);
}
function rg_re_bugpage($ui, $repo_name, $bug_id)
{
return rg_re_repopage($ui, $repo_name) . "/bug/" . $bug_id;
}
function rg_base_url()
{
global $rg_web_url;
if (!empty($rg_web_url))
return $rg_web_url;
if (!isset($_SERVER['SERVER_NAME']))
return 'http://' . php_uname('n');
$port = '';
if (isset($_SERVER['HTTPS'])) {
$proto = 'https';
if ($_SERVER['SERVER_PORT'] != 443)
$port = ':' . $_SERVER['SERVER_PORT'];
} else {
$proto = 'http';
if ($_SERVER['SERVER_PORT'] != 80)
$port = ':' . $_SERVER['SERVER_PORT'];
}
return $proto . '://' . $_SERVER['SERVER_NAME'] . $port;
}
function rg_re_repo_ssh($organization, $user, $repo)
{
global $rg_ssh_host;
global $rg_ssh_port;
if ($rg_ssh_port == 22)
$port = "";
else
$port = ":" . $rg_ssh_port;
$prefix = "";
if ($organization == 0)
$prefix = "/user";
return "ssh://rocketgit@" . $rg_ssh_host . $port
. $prefix . "/" . rawurlencode($user) . "/" . rawurlencode($repo);
}
function rg_re_repo_git($organization, $user, $repo)
{
global $rg_git_host;
global $rg_git_port;
if ($rg_git_port == 9418)
$port = "";
else
$port = ":" . $rg_git_port;
$prefix = "";
if ($organization == 0)
$prefix = "/user";
return "git://" . $rg_git_host . $port
. $prefix . "/" . rawurlencode($user) . "/" . rawurlencode($repo);
}
function rg_re_repo_http($organization, $user, $repo)
{
$prefix = '';
if ($organization == 0)
$prefix = '/user';
return rg_base_url() . $prefix . "/"
. rawurlencode($user) . "/" . rawurlencode($repo);
}
function rg_var_get($name)
{
if (isset($_SERVER[$name]))
$ret = $_SERVER[$name];
else if (isset($_POST[$name]))
$ret = $_POST[$name];
else if (isset($_GET[$name]))
$ret = $_GET[$name];
else
return FALSE;
return str_replace("\r", "", $ret);
}
function rg_var_is_set($name)
{
$c = rg_var_get($name);
if ($c === FALSE)
return 0;
return 1;
}
function rg_var_str($name)
{
$c = rg_var_get($name);
if ($c === FALSE)
return '';
return $c;
}
function rg_var_int($name)
{
$r = rg_var_str($name);
if (is_array($r)) {
$ret2 = array();
foreach ($r as $k => $v)
$ret2[$k] = sprintf("%d", $v);
return $ret2;
}
return sprintf("%d", $r);
}
function rg_var_uint($name)
{
$r = rg_var_str($name);
if (is_array($r)) {
$ret2 = array();
foreach ($r as $k => $v)
$ret2[$k] = sprintf("%u", $v);
return $ret2;
}
return sprintf("%u", $r);
}
function rg_var_bool($name)
{
$r = rg_var_str($name);
if (strcmp($r, '1') == 0)
return 1;
return 0;
}
/*
* Allow only @re chars
*/
function rg_var_re($name, $re)
{
$a = rg_var_str($name);
return preg_replace('/[^' . $re . ']/', '', $a);
}
/*
* Extract a cookie from $_COOKIE
*/
function rg_var_cookie_re($name, $re)
{
if (!isset($_COOKIE[$name]))
return "";
return preg_replace($re, '', $_COOKIE[$name]);
}
/*
* Gets data from request and transforms it into an array
* Usefull for checkboxes.
* (a2s = array2string)
*/
function rg_var_a2s($var)
{
$r = rg_var_str($var);
if (!is_array($r))
return $r;
$ret = '';
foreach ($r as $s => $junk)
$ret .= $s;
return $ret;
}
/*
* Enforce chars in a name. It is used for user and repo.
*/
function rg_chars_allow($name, $allowed_regexp, &$invalid)
{
// Modifiers: u = UTF8, D = PCRE_DOLLAR_ENDONLY
// http://php.net/manual/en/reference.pcre.pattern.modifiers.php
if (preg_match('/^[' . $allowed_regexp . ']*$/uD', $name) !== 1) {
$invalid = preg_replace('/[' . $allowed_regexp . ']/', '', $name);
rg_log("chars_allow: [$name] does not match [$allowed_regexp]");
return FALSE;
}
return TRUE;
}
/*
* Deletes a folder and the files inside it
*/
function rg_rmdir($dir)
{
global $php_errormsg;
if (!is_dir($dir)) {
rg_util_set_error("WARN: asked to remove a non-existing dir ($dir)");
return TRUE;
}
$scan = glob($dir . "/*");
if ($scan === FALSE) {
rg_util_set_error("invalid pattern [$dir/*]");
return FALSE;
}
if (count($scan) > 0) {
$all_good = TRUE;
foreach ($scan as $junk => $path) {
if (is_dir($path)) {
$r = rg_rmdir($path);
if ($r !== TRUE)
return FALSE;
continue;
}
if (!unlink($path)) {
rg_util_set_error("cannot remove [$path] ($php_errormsg)");
return FALSE;
}
}
}
if (!rmdir($dir)) {
rg_util_set_error("cannot remove main dir ($php_errormsg)");
return FALSE;
}
return TRUE;
}
/*
* Lookup a path in the current theme with fallback to default
* Returns the correct path
*/
function rg_theme_resolve_path($path)
{
global $rg_theme, $rg_theme_dir;
$url = "/themes/" . $rg_theme . "/" . $path;
$xfile = $rg_theme_dir . "/" . $rg_theme . "/" . $path;
if (!is_file($xfile))
$url = "/themes/default/" . $path;
return $url;
}
/*
* Loads a file if exists, else return ""
*/
function rg_file_get_contents($f)
{
global $php_errormsg;
if (!file_exists($f))
return "";
$c = @file_get_contents($f);
if ($c === FALSE) {
rg_internal_error("Could not load file [$f] ($php_errormsg).");
return "";
}
return $c;
}
/*
* Merges an array (a) into another (src), using a namespace
* Protects modifiers (HTML: etc.).
*/
function rg_array_merge($src, $namespace, $a)
{
$ret = $src;
if (!empty($namespace))
if (!isset($ret[$namespace]))
$ret[$namespace] = array();
foreach ($a as $k => $v) {
if (empty($namespace))
$ret[$k] = $v;
else
$ret[$namespace][$k] = $v;
}
return $ret;
}
/*
* Performs a lookup of a var of type 'a::b::c' into an array and
* returns FALSE or the value
*/
function rg_template_tree_lookup($var, &$data, $xss_protection)
{
$tree = &$data;
$t = explode('::', $var);
$v = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
return FALSE;
$tree = &$tree[$token];
}
// We prefer the HTML version
$hv = 'HTML:' . $v;
if (isset($tree[$hv]))
return $tree[$hv];
if (isset($tree[$v])) {
if ($xss_protection)
return rg_xss_safe($tree[$v]);
else
return $tree[$v];
}
return FALSE;
}
/*
* Evaluates a condition
*/
function rg_template_eval_cond($cond, &$data)
{
$t = explode('!=', $cond);
if (count($t) == 2) {
$negate = TRUE;
} else {
$t = explode('==', $cond);
if (count($t) != 2) {
rg_log("invalid condition [$cond]!");
return FALSE;
}
$negate = FALSE;
}
$left = trim($t[0]);
$left = rg_template_string($left, 0, $data, FALSE);
$right = trim($t[1]);
$right = rg_template_string($right, 0, $data, FALSE);
$ret = strcmp($left, $right);
if ($ret === 0) {
if ($negate === TRUE)
return FALSE;
} else {
if (!$negate)
return FALSE;
}
return TRUE;
}
/*
* Finds matching }} for an {{
* We assume @off points to the byte after '{{'
* Returns the offset of the byte before '}}'
*/
function rg_template_find_closing(&$s, $off)
{
$nesting_level = 0;
while (1) {
$end = strpos($s, '}}', $off);
if ($end === FALSE)
return -1;
$start = strpos($s, '{{', $off);
if (($start === FALSE) || ($start >= $end)) {
if ($nesting_level == 0)
return $end - 1;
$nesting_level--;
$off = $end + 2;
} else {
$nesting_level++;
$off = $start + 2;
}
}
}
/*
* "Decodes" an 'if', returning 'true_start/end' and false_start/end'
* s + off must point after ')'
* Returns -1 on error, 0 on success
*/
function rg_template_find_true_and_false(&$s, $off, &$true_start, &$true_end,
&$false_start, &$false_end)
{
//rg_log_enter("DEBUG: template_find_true_and_false s+off=[" . substr($s, $off) . "]");
$true_start = strpos($s, '{{', $off);
if ($true_start === FALSE) {
//rg_log("DEBUG: no '{{'!");
//rg_log_exit();
return -1;
}
$true_start += 2;
if (strncmp(substr($s, $true_start, 1), "\n", 1) == 0) {
//rg_log("DEBUG: true starts with CR, remove it");
$true_start++;
}
$true_end = rg_template_find_closing($s, $true_start);
if ($true_end == -1) {
//rg_log("DEBUG: no true_end!");
//rg_log_exit();
return -1;
}
//rg_log("DEBUG: true_start=$true_start true_end=$true_end [" . substr($s, $true_end, 3) . "...]"
// . " true=[" . substr($s, $true_start, $true_end - $true_start + 1) . "]");
// We try to detect if we have an else
$false_start = -1; $false_end = -1;
$x = strpos($s, '{{', $true_end);
if ($x !== FALSE) {
$gap = substr($s, $true_end + 3, $x - $true_end - 3);
$gap = trim($gap);
//rg_log("DEBUG: gap = [$gap]");
if (empty($gap)) {
$false_start = $x + 2;
if (strncmp(substr($s, $false_start, 1), "\n", 1) == 0) {
//rg_log("DEBUG: false starts with CR, remove it");
$false_start++;
}
$false_end = rg_template_find_closing($s, $x + 2);
//rg_log("DEBUG: false=[" . substr($s, $false_start, $false_end - $false_start + 1) . "]");
} else {
//rg_log("DEBUG: gap prevents parsing stuff as false, we have only true part");
}
} else {
//rg_log("DEBUG: cannot find '{{'");
}
//rg_log_exit();
return 0;
}
/*
* Helper for rg_tempalte_string to deal with 'if's
* Returns how many bytes used from string @s in @next
*/
function rg_template_string_if(&$s, $off, &$data, &$next, $xss_protection)
{
rg_prof_start("template_string_if");
//rg_log_enter("DEBUG: template_string_if s+off=[" . substr($s, $off) . "]");
$ret = '';
$next = $off;
$off += 5; /* skip '@@if(' */
$pos = strpos($s, ')', $off);
if ($pos === FALSE) {
rg_log("no closing ')' in [" . substr($s, $off) . "]!");
rg_log_exit();
rg_prof_end("template_string_if");
return '';
}
$cond = substr($s, $off, $pos - $off); $off = $pos + 1;
$eval_cond = rg_template_eval_cond($cond, $data);
//rg_log("DEBUG: cond=[$cond] eval_cond=" . ($eval_cond ? "true" : "false"));
// TODO: Between ')' and '{{' must be only space, else ignore anything??
$r = rg_template_find_true_and_false($s, $off, $true_start, $true_end,
$false_start, $false_end);
if ($r == -1) {
rg_log("no if skeleton found [" . substr($s, $off) . "]!");
//rg_log_exit();
rg_prof_end("template_string_if");
return -1;
}
$x = '';
if ($eval_cond === TRUE) {
$x = substr($s, $true_start, $true_end - $true_start + 1);
} else {
if ($false_start != -1)
$x = substr($s, $false_start, $false_end - $false_start + 1);
}
//rg_log("DEBUG: x=[$x]");
$ret .= rg_template_string($x, 0, $data, $xss_protection);
if ($false_start != -1)
$next = $false_end + 3;
else
$next = $true_end + 3;
if (strncmp(substr($s, $next, 1), "\n", 1) == 0)
$next++;
//rg_log("DEBUG: next: [" . substr($s, $next) . "]");
//rg_log_exit();
rg_prof_end("template_string_if");
return $ret;
}
/*
* Replace all known variables in string @s
* Example @data: a->a2->a3, b->b2; @s='@@a::a2@@ @@b@@' => 'a3 b2'
* @xss_protection - TRUE if you want to apply rg_xss_safe on the value of vars
*/
function rg_template_string(&$s, $off, &$data, $xss_protection)
{
global $rg_template_functions;
rg_prof_start('template_string');
//rg_log_enter("DEBUG: template_string: s+off=[" . substr($s, $off) . "]");
$ret = '';
while (strlen(substr($s, $off, 1)) == 1) {
//rg_log("DEBUG: template_string: s+off=[" . substr($s, $off) . "]");
$pos = strpos($s, '@@', $off);
if ($pos === FALSE) {
$ret .= substr($s, $off);
break;
}
$var_start = $pos + 2;
// copy everything before '@@'
$ret .= substr($s, $off, $pos - $off);
//rg_log("DEBUG: after copy all before @@, ret=[$ret]");
$off = $pos;
$s2 = substr($s, $off, 5);
if (strcmp($s2, '@@if(') == 0) {
$ret .= rg_template_string_if($s, $off, $data, $next,
$xss_protection);
$off = $next;
continue;
}
$off += 2; /* skip start '@@' */
$pos2 = strpos($s, '@@', $off);
if ($pos2 === FALSE) {
// We have only start '@@'
$ret .= substr($s, $off);
break;
}
$var_end = $pos2 - 1;
$off = $pos2 + 2;
$var = substr($s, $var_start, $var_end - $var_start + 1);
//rg_log("DEBUG: var=[$var]");
$value = rg_template_tree_lookup($var, $data, $xss_protection);
if ($value === FALSE) {
$value = '@@' . $var . '@@';
if (strncmp($var, 'IMG:', 4) == 0) {
$path = substr($var, 4);
$path = rg_template_string($path, 0, $data, $xss_protection);
//rg_log("DEBUG: found an img tag path=[$path]!");
$value = rg_theme_resolve_path($path);
} else if (strncmp($var, 'FUNC:', 5) == 0) {
$rest = substr($var, 5);
//rg_log("DEBUG: found a function call rest=[$rest]!");
$fpos = strpos($rest, ':');
if ($fpos === FALSE) {
// no params
$_param = '';
} else {
$_param = '@@' . substr($rest, $fpos + 1) . '@@';
}
$func = substr($rest, 0, $fpos);
//rg_log("DEBUG: func=$func _param=$_param");
// out var may be with '@@'
$param = rg_template_string($_param, 0, $data, $xss_protection);
if (isset($rg_template_functions[$func]))
$value = $rg_template_functions[$func]($param);
} else if (strncmp($var, 'TEMPLATE:', 9) == 0) {
$path = substr($var, 9);
rg_log("DEBUG: found a template path=[$path]!");
$value = rg_template($path, $data, $xss_protection);
} else if (strncmp($var, 'SET:', 4) == 0) {
$rest = substr($var, 4);
rg_log("DEBUG: found a set rest=[$rest]!");
$_t = explode('=', $rest, 2);
if (isset($_t[1]))
$data[$_t[0]] = $_t[1];
$value = '';
} else {
//rg_log("DEBUG: VAR [$var] NOT FOUND!");
}
}
//rg_log("DEBUG: value=[$value]");
$ret .= $value;
}
//rg_log("DEBUG: ret=[$ret]");
//rg_log_exit();
rg_prof_end('template_string');
return $ret;
}
/*
* Loads a template from disk without replacing vars or acting on 'ifs'
*/
function rg_template_blind($file)
{
global $rg_theme_dir;
global $rg_theme;
rg_prof_start('template_blind');
//rg_log_enter('template_blind: ' . $file);
$ret = '';
while (1) {
$xfile = $rg_theme_dir . "/" . $rg_theme . "/" . $file;
if (!is_file($xfile)) {
$xfile = $rg_theme_dir . "/default/" . $file;
if (!is_file($xfile))
$xfile = $file;
if (!is_file($xfile)) {
rg_log("Cannot find $file!");
break;
}
}
$ret = rg_file_get_contents($xfile);
break;
}
rg_prof_end('template_blind');
return $ret;
}
/*
* Loads a template from disk and replase all known variables
* @xss_protection - TRUE if you want to apply rg_xss_safe on the value of vars
*/
function rg_template($file, &$data, $xss_protection)
{
rg_prof_start('template');
//rg_log_enter('template: ' . $file);
$ret = '';
while (1) {
$body = rg_template_blind($file);
if (empty($body))
break;
$ret = rg_template_string($body, 0, $data, $xss_protection);
break;
}
//rg_log("DEBUG: rg_template returns [$ret]");
//rg_log_exit();
rg_prof_end('template');
return $ret;
}
/*
* Builds a html output based on a template with header, footer and line
* @data - in array of data for every out line: index 0 is line 1, index 1 is line 2...
*/
function rg_template_table($dir, &$data, $more)
{
global $rg_theme_dir;
global $rg_theme;
rg_prof_start('template_table');
//rg_log('template_table: ' . $dir);
$xdir = $rg_theme_dir . "/" . $rg_theme . "/" . $dir;
if (!is_dir($xdir)) {
rg_log("$xdir not found.");
$xdir = $rg_theme_dir . "/default/" . $dir;
rg_log("Using [$xdir]");
}
if (!is_array($data) || empty($data)) {
$ret = rg_template($xdir . "/nodata.html", $more, TRUE /* xss */);
rg_prof_end('template_table');
return $ret;
}
$head = rg_template($xdir . "/header.html", $more, TRUE /* xss */);
$foot = rg_template($xdir . "/footer.html", $more, TRUE /* xss */);
$line = rg_file_get_contents($xdir . "/line.html");
$body = '';
foreach ($data as $index => $info) {
$more2 = array_merge($more, $info);
$body .= rg_template_string($line, 0, $more2, TRUE /* xss */);
}
rg_prof_end('template_table');
return $head . $body . $foot;
}
/*
* Outputs a numbered list
*/
function rg_template_list($c)
{
$a = explode("\n", $c);
if (count($a) == 0)
return "";
$ret = "";
$i = 1;
$add = "";
foreach ($a as $line) {
$ret .= $add . $i . " " . rg_xss_safe($line);
$add = "<br />";
$i++;
}
return $ret;
}
/*
* Show errors using a template
*/
function rg_template_errmsg($a)
{
if (empty($a))
return "";
$b = array();
foreach ($a as $junk => $err)
$b[] = array("error" => $err);
return rg_template_table("errmsg", $b, array());
}
/*
* Show a warning using a template
*/
function rg_warning($msg)
{
if (empty($msg))
return "";
rg_log("Warning: $msg");
$x = array("msg" => $msg);
return rg_template("warning.html", $x, TRUE /* xss */);
}
/*
* Show an OK message using a template
* TODO: OBSOLETE? Because we want the files to be in templates?
*/
function rg_ok($msg)
{
if (empty($msg))
return "";
$x = array("msg" => $msg);
return rg_template("ok.html", $x, TRUE /* xss */);
}
/*
* Execute $cmd and returns the output as a string, binary safe
* @input: some data to be sent to the process and received as stdin
* @cb_stdout - call back called when there is something to be send to stdout
* if @cb_stdout is FALSE, stdout output will be returned in $ret['data']
* cb_stderr - call back called when there is something to be send to stderr
* if @cb_stderr is FALSE, stderr output will be returned in $ret['stderr']
*/
function rg_exec($cmd, $input, $cb_stdout, $cb_stderr)
{
rg_prof_start('exec');
rg_log_enter('Executing [' . $cmd . ']');
$ret = array();
$ret['ok'] = 0;
$ret['errmsg'] = '';
$ret['code'] = 65000; // fake code
$ret['data'] = '';
$ret['stderr'] = '';
while (1) {
$desc = array(
0 => array("pipe", "r"),
1 => array('pipe', 'w'),
2 => array("pipe", "w")
);
//rg_log_ml('DEBUG: desc: ' . print_r($desc, TRUE));
$a = proc_open($cmd, $desc, $pipes);
if ($a === FALSE) {
$ret['errmsg'] = "cannot call proc_open";
break;
}
//rg_log_ml('DEBUG: proc_open pipes: ' . print_r($pipes, TRUE));
$rx = array($pipes[1], $pipes[2]);
$wx = array();
if (!empty($input))
$wx[] = $pipes[0];
while (!empty($rx)) {
$revents = $rx;
$wevents = $wx;
$ex = NULL;
$r = stream_select($revents, $wevents, $ex, 10, 0);
if ($r === FALSE) {
$ret['errmsg'] = "cannot select";
break;
}
//rg_log('DEBUG: stream_select returned ' . $r
// . ', revents: ' . rg_array2string($revents)
// . ', wevents: ' . rg_array2string($wevents));
foreach ($wevents as $fd) {
if (!empty($ret['errmsg']))
break;
//rg_log('DEBUG: write event on fd ' . $fd . '!');
//rg_log('DEBUG: Writing to fd ' . $fd
// . ' [' . $input . ']...');
$r = @fwrite($fd, $input);
if ($r === FALSE) {
$ret['ermsg'] = 'cannot write';
break;
}
//rg_log('DEBUG: fwrite returned ' . $r . '.');
$input = substr($input, $r);
if (empty($input))
$wx = array();
}
foreach ($revents as $fd) {
if (!empty($ret['errmsg']))
break;
//rg_log('DEBUG: read event on fd ' . $fd . '!');
$_d = fread($fd, 32 * 4096);
if ($_d === FALSE) {
$ret['errmsg'] = "cannot read";
break;
}
if (empty($_d)) {
//rg_log('DEBUG: stream ' . $fd . ' returned no data.');
foreach ($rx as $_k => $_fd) {
if ($_fd === $fd) {
unset($rx[$_k]);
break;
}
}
continue;
}
if ($fd === $pipes[2]) {
if ($cb_stderr === FALSE) {
//rg_log('DEBUG: fd is pipes[2], append to stderr var: ' . $_d);
$ret['stderr'] .= $_d;
} else {
//rg_log('DEBUG: fd is pipes[2], call stdout cb:' . $_d);
$cb_stderr($_d);
// We want the last error message to be able to log something
$ret['stderr'] = $_d;
}
} else if ($fd === $pipes[1]) {
if ($cb_stdout === FALSE) {
//rg_log('DEBUG: fd is pipes[1], append to stdout var: ' . $_d);
$ret['data'] .= $_d;
} else {
//rg_log('DEBUG: fd is pipes[1], call stdout cb: ' . $_d);
$cb_stdout($_d);
}
} else {
rg_internal_error('invalid fd');
}
}
if (!empty($ret['errmsg']))
break;
}
$ret['stderr'] = trim($ret['stderr']);
$ret['data'] = trim($ret['data']);
for ($i = 0; $i < 3; $i++)
if (isset($pipes[$i]))
fclose($pipes[$i]);
$err = proc_close($a);
if ($err != 0) {
rg_log('DEBUG: exec returned ' . $err);
$ret['code'] = $err;
$ret['errmsg'] = 'task returned code ' . $err
. ' (' . $ret['stderr'] . ')';
break;
}
//rg_log('DEBUG: exec returned ok');
$ret['code'] = 0;
$ret['ok'] = 1;
break;
}
rg_log_exit();
rg_prof_end("exec");
return $ret;
}
/*
* Force browser to redirect to another page
*/
function rg_redirect($url)
{
rg_log("Redirecting to [$url]");
header("Location: $url");
exit(0);
}
/*
* Force browser to redirect to another page, using a HTML header
*/
function rg_redirect_html($seconds, $url)
{
global $rg;
$rg['rg_redirect_html'] = 1;
$rg['rg_redirect_html_seconds'] = $seconds;
$rg['rg_redirect_html_url'] = $url;
}
/*
* Transforms strange chars to hexa
*/
function rg_callback_hexa($matches)
{
$n = pack("a*", $matches[0]);
$tmp = unpack("H*", $n);
return "[" . $tmp[1] . "]";
}
/*
* Transforms an array in a string
*/
function rg_array2string($a)
{
$what = array("/[^\pL\pN\pP\pS ]/uU");
if (!is_array($a))
return preg_replace_callback($what, "rg_callback_hexa", $a);
if (empty($a))
return "";
$ret = "";
$add = "";
foreach ($a as $k => $v) {
if (is_array($v))
$s = rg_array2string($v);
else
$s = preg_replace_callback($what, "rg_callback_hexa", $v);
$ret .= $add . "$k=[$s]";
$add = " ";
}
return $ret;
}
/*
* Load files from a folder using a pattern for match
*/
function rg_dir_load_pattern($dir, $pattern)
{
global $php_errormsg;
$ret = FALSE;
if (!file_exists($dir)) {
rg_util_set_error("$dir does not exists");
return $ret;
}
$d = @scandir($dir);
if ($d === FALSE) {
rg_util_set_error("cannot scan dir $dir ($php_errormsg)");
return $ret;
}
$ret = array();
foreach ($d as $file) {
if ((strcmp($file, ".") == 0) || (strcmp($file, "..") == 0))
continue;
if (preg_match("/" . $pattern . "/", $file) !== 1)
continue;
$ret[] = $file;
}
return $ret;
}
/*
* Load all files from a folder
*/
function rg_dir_load($dir)
{
return rg_dir_load_pattern($dir, ".*");
}
/*
* Recursive dir load (used for references)
*/
function rg_dir_load_deep($dir)
{
$ret = array();
if (is_file($dir))
return array($dir);
$d = rg_dir_load($dir);
foreach ($d as $obj) {
if (is_dir($dir . "/" . $obj)) {
$c = rg_dir_load_deep($dir . "/" . $obj);
foreach ($c as $obj2)
$ret[] = $obj . "/" . $obj2;
} else {
$ret[] = $obj;
}
}
return $ret;
}
/*
* Copy a fs tree to another place
*/
function rg_copy_tree($src, $dst, $mask)
{
global $php_errormsg;
rg_prof_start("copy_tree");
rg_log_enter("copy_tree($src, $dst, mask=$mask)");
$ret = FALSE;
while (1) {
if (!is_dir($dst)) {
$r = @mkdir($dst, $mask, TRUE);
if ($r !== TRUE) {
rg_log("ERROR: Cannot mkdir [$dst] ($php_errormsg).");
break;
}
}
$d = rg_dir_load($src);
if ($d === FALSE)
break;
$err = FALSE;
foreach ($d as $obj) {
if (is_dir($src . "/" . $obj)) {
if (!is_dir($dst . "/" . $obj)) {
$r = @mkdir($dst . "/" . $obj, $mask);
if ($r !== TRUE) {
rg_log("ERROR: Cannot mkdir [$dst/$obj]"
. " ($php_errormsg).");
$err = TRUE;
break;
}
}
$r = rg_copy_tree($src . "/" . $obj, $dst . "/" . $obj, $mask);
if ($r !== TRUE) {
$err = TRUE;
break;
}
} else {
$r = @copy($src . "/" . $obj, $dst . "/" . $obj);
if ($r !== TRUE) {
rg_log("ERROR: Cannot copy file ($php_errormsg).");
$err = TRUE;
break;
}
}
}
if (!$err)
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("copy_tree");
return $ret;
}
/*
* Recursively deletes a tree
*/
function rg_del_tree($dst)
{
global $php_errormsg;
rg_prof_start('del_tree');
$ret = FALSE;
while (1) {
if (!is_dir($dst)) {
$ret = TRUE;
break;
}
$d = rg_dir_load($dst);
if ($d === FALSE)
break;
$err = FALSE;
foreach ($d as $obj) {
if (is_dir($dst . '/' . $obj)) {
$r = rg_del_tree($dst . '/' . $obj);
if ($r !== TRUE) {
$err = TRUE;
break;
}
} else {
$r = @unlink($dst . '/' . $obj);
if ($r !== TRUE) {
rg_log("ERROR: Cannot del file [" . $dst . '/' . $obj . "] ($php_errormsg).");
$err = TRUE;
break;
}
}
}
$r = @rmdir($dst);
if ($r !== TRUE) {
rg_log("ERROR: Cannot del dir [" . $dst . "] ($php_errormsg).");
$err = TRUE;
break;
}
if (!$err)
$ret = TRUE;
break;
}
rg_prof_end('del_tree');
return $ret;
}
/*
* Called by PHP in case of error
*/
function rg_error_handler($no, $str, $file, $line)
{
if ($no == 0)
return;
// call was prepended with '@'
if (error_reporting() == 0)
return;
$msg = "PHP ERROR: $file:$line: $str (errno=$no)";
rg_error_core($msg);
if ($no == E_ERROR)
die();
$key = md5($msg);
$rg_error_seen[$key] = 1;
return FALSE;
}
/*
* Shutdown function to log fatal errors
*/
function rg_error_shutdown()
{
$a = error_get_last();
if ($a === NULL)
return;
rg_error_handler($a['type'], $a['message'], $a['file'], $a['line']);
}
/*
* YYYY-MM-DD -> timestamp
*/
function rg_date2ts($s)
{
rg_log("rg_date2ts s=[$s]");
if (strlen($s) != 10)
return FALSE;
$f = explode("-", $s);
if (count($f) != 3)
return FALSE;
return gmmktime(0, 0, 0, $f[1], $f[2], $f[0]);
}
/*
* YYYY-MM-DD -> timestamp (last second of the day)
*/
function rg_date2ts_last_second($s)
{
rg_log("rg_date2ts_last_second s=[$s]");
if (strlen($s) != 10)
return FALSE;
$f = explode("-", $s);
if (count($f) != 3)
return FALSE;
return gmmktime(0, 0, 0, $f[1], $f[2] + 1, $f[0]) - 1;
}
/*
* Special implode, with prefix/postfix
*/
function rg_implode($prefix, $a, $postfix)
{
if (!is_array($a))
return $a;
if (empty($a))
return "";
$ret = array();
foreach ($a as $index => $data)
$ret[] = $prefix . $data;
return implode($postfix, $ret);
}
/*
* Here we will cache the connections
*/
$rg_socket_cache = array();
/*
* Receives buffers and test if @wait string is present.
* @timeout - in miliseconds, NULL=forever, 0=no_wait
*/
function rg_socket_recv_wait($socket, $wait, $timeout)
{
rg_prof_start('sock_recv_wait');
$ret = FALSE;
if ($timeout === NULL) {
$tv_sec = NULL;
$tv_usec = NULL;
} else {
$tv_sec = $timeout / 1000;
$tv_usec = ($timeout % 1000) * 1000;
}
$ret_buf = '';
while (1) {
$reads = array($socket); $writes = array(); $ex = array();
$r = @socket_select($reads, $writes, $ex, $tv_sec, $tv_usec);
if ($r === FALSE) {
rg_log('Cannot select(' . socket_strerror(socket_last_error()) . ')!');
break;
}
if ($r === 0) { // timeout
rg_log('Timeout in reading!');
break;
}
if (!in_array($socket, $reads)) {
rg_log('Select returned > 0 and my socket is not in reads');
break;
}
$r = @socket_recv($socket, $buf, 32 * 4096, 0);
if ($r === FALSE) {
rg_log('Cannot receive (' . socket_strerror(socket_last_error()) . ')!');
break;
}
//rg_log("Received [$buf]");
$ret_buf .= $buf;
$pos = strpos($buf, $wait);
if ($pos !== FALSE) {
$ret = $ret_buf;
break;
}
break;
}
rg_prof_end('sock_recv_wait');
return $ret;
}
/*
* Sends a full buffer
* TODO: Take timeout in consideration.
*/
function rg_socket_send($socket, $buf)
{
rg_prof_start('socket_send');
$ret = FALSE;
$len = strlen($buf);
$off = 0;
while (1) {
$r = @socket_send($socket, substr($buf, $off), $len - $off, 0);
if ($r === FALSE) {
rg_log("Could not send (" . socket_strerror(socket_last_error()) . ")!");
break;
}
//rg_log("Sent $r bytes (" . substr($buf, $off, $r) . ").");
$len -= $r; $off += $r;
if ($len == 0) {
$ret = TRUE;
break;
}
}
rg_prof_end('socket_send');
return $ret;
}
/*
* Connects to a socket, send @buf and returns the answer.
* @timeout: NULL=forever, 0=no_wait
* @tries - how many time to retry if it fails
*/
function rg_socket($path, $buf, $timeout, $tries, $flags)
{
global $rg_socket_cache;
rg_prof_start('socket');
$ret = FALSE;
while ($tries > 0) {
if (isset($rg_socket_cache[$path])) {
$socket = $rg_socket_cache[$path];
} else {
rg_prof_start('socket-connect');
$socket = @socket_create(AF_UNIX, SOCK_STREAM, 0);
if ($socket === FALSE) {
rg_log("Could not create socket (" . socket_strerror(socket_last_error()) . ")!");
break;
}
while ($tries > 0) {
$r = @socket_connect($socket, $path);
if ($r === FALSE) {
$tries--;
usleep(50 * 1000);
continue;
}
break;
}
if ($r === FALSE) {
rg_log('Could not connect the socket [' . $path . ']'
. '(' . socket_strerror(socket_last_error()) . ')!');
break;
}
rg_prof_end('socket-connect');
$rg_socket_cache[$path] = $socket;
}
$r = rg_socket_send($socket, $buf);
if ($r !== TRUE) {
socket_close($socket);
unset($rg_socket_cache[$path]);
continue;
}
if ($flags & RG_SOCKET_NO_WAIT) {
//rg_log('We do not have to wait. Exit.');
$ret = '';
break;
}
$ret = rg_socket_recv_wait($socket, "\n", $timeout);
break;
}
rg_prof_end("socket");
return $ret;
}
/*
* Check if referer matchces current website
*/
function rg_valid_referer()
{
$ref0 = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "";
// If not provided, we can do nothing about
if (empty($ref0))
return TRUE;
$ref = preg_replace('|http(s)?://|', '', $ref0);
$ref = preg_replace('|/.*|', '', $ref);
$we = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : "";
if (strcasecmp($we, $ref) == 0)
return TRUE;
rg_security_violation_no_exit("invalid referer for form submission"
. " we=[$we] ref=[$ref] ref0=[$ref0]");
return FALSE;
}
/*
* Returns the age of an object
*/
function rg_age($ts)
{
$diff = time() - $ts;
$ret = ($diff % 60) . 's';
$rest = intval($diff / 60);
if ($rest == 0)
return $ret;
$ret = ($diff % 60) . 'm' . $ret;
$rest = intval($diff / 60);
if ($rest == 0)
return $ret;
$ret = ($diff % 24) . 'h' . $ret;
$rest = intval($diff / 24);
if ($rest == 0)
return $ret;
$ret = ($diff % 12) . 'm' . $ret;
$rest = intval($diff / 12);
if ($rest == 0)
return $ret;
$ret = $rest . 'y' . $ret;
return $ret;
}
/*
* Returns the path into the temporary area
*/
function rg_tmp_path($file)
{
global $rg_state_dir;
return $rg_state_dir . '/tmp/' . $file;
}
/*
* Creates and stores content into a temporary file
*/
function rg_tmp_file($file, $content)
{
global $rg_state_dir;
$final_name = $rg_state_dir . '/tmp/' . $file;
$r = @file_put_contents($final_name, $content);
if ($r === FALSE)
return FALSE;
// TODO: log error messege?
return $final_name;
}
/*
* Function to short the certificates
*/
function rg_cert_short($s)
{
if (empty($s))
return 'n/a';
if (strlen($s) < 12)
return $s;
$s = str_replace('-----BEGIN CERTIFICATE-----', '', $s);
$s = str_replace('-----END CERTIFICATE-----', '', $s);
$s = trim($s);
return substr($s, 0, 4) . '...' . substr($s, -4, 4);
}
/*
* Remove ::ffff: prefix
*/
function rg_fix_ip($ip)
{
if (strncasecmp($ip, "::ffff:", 7) == 0)
$ip = substr($ip, 7);
return $ip;
}
/*
* Escape json fields - TODO - for now do nothing
*/
function rg_json_escape($s)
{
return json_encode($s);
}
/*
* Escapes texts inside XML tags
* Example: <a>&</a> -> <a>&</a>
*/
function fn_xml_escape($s)
{
return strtr($s,
array(
'<' => '<',
'>' => '>',
'"' => '"',
'\'' => ''',
'&' => '&',
)
);
}
/*
* Removes from a string all non-alpha characters
*/
function rg_force_alpha($s)
{
return preg_replace('/[^A-Za-z]/', '', $s);
}
/*
* Removes from a string all non-alpha/num and _ characters
*/
function rg_force_alphanum($s)
{
return preg_replace('/[^A-Za-z0-9_]/', '', $s);
}
/*
* Dummy function to be used in rg_exec callback
*/
function rg_echo($s)
{
echo $s;
}
?>