<?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 $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) { 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['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_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) { if (preg_match('/^[' . $allowed_regexp . ']*$/uUD', $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 { //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 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) { global $rg_theme_dir; global $rg_theme; rg_prof_start('template'); //rg_log_enter('template: ' . $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; } } $body = rg_file_get_contents($xfile); 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 */ function rg_exec($cmd) { rg_prof_start("exec"); rg_log_enter("Executing [$cmd]..."); $ret = array(); $ret['ok'] = 0; $ret['errmsg'] = ""; $ret['code'] = 65000; // fake code while (1) { $desc = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w") ); $a = proc_open($cmd, $desc, $pipes); if ($a === FALSE) { $ret['errmsg'] = "cannot call proc_open"; break; } $stderr = ""; $ret['data'] = ""; $rx = array($pipes[1], $pipes[2]); while (!empty($rx)) { $revents = $rx; $wevents = NULL; $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)); foreach ($revents as $fd) { if (!empty($ret['errmsg'])) break; //rg_log("Event on fd $fd!"); if (feof($fd)) { //rg_log("eof on fd $fd!"); foreach ($rx as $_key => $_fd) if ($_fd == $fd) unset($rx[$_key]); continue; } $_d = fread($fd, 32 * 4096); if ($_d === FALSE) { $ret['errmsg'] = "cannot read"; break; } //rg_log("DEBUG: got data from fd $fd [$_d]"); if ($fd === $pipes[2]) $stderr .= $_d; else if ($fd === $pipes[1]) $ret['data'] .= $_d; } if (!empty($ret['errmsg'])) break; } $stderr = trim($stderr); $ret['data'] = trim($ret['data']); for ($i = 0; $i < 3; $i++) fclose($pipes[$i]); $err = proc_close($a); if ($err != 0) { $ret['code'] = $err; $ret['errmsg'] = "code " . $err . ": " . $stderr; } if (empty($ret['errmsg'])) { $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)"); $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)"; $key = md5($msg); rg_error_core($msg); if ($no == E_ERROR) die(); $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 (" . 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 $s; } ?>