Benjamin Renard
6fdc5447f1
* Code cleaning and fix some small errors using Phpstan * Configure pre-commit to run Phpstan before each commit * Some little improvments and logging, mail, smarty & URL libs * Add Sentry integration * Add Webstat JS code inclusion * Install Smarty dependency using composer Breaking changes: * Rename Event class as HookEvent to avoid conflict with PECL event * URL with refresh GET parameter now automatically trigger redirection without it after page loading to avoid to keep it in URL
388 lines
10 KiB
PHP
388 lines
10 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Check values helpers
|
|
*/
|
|
function check_name($name) {
|
|
if (preg_match('/^[\w \-]{2,}$/iu',$name))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
function check_id(&$id) {
|
|
if (is_int($id))
|
|
return true;
|
|
if (preg_match('/^[0-9]+$/', $id)) {
|
|
$id = intval($id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function check_search_pattern($pattern) {
|
|
foreach(preg_split('/\s+/', trim($pattern)) as $word) {
|
|
if (!check_id($word) && !check_name($word))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function check_time(&$time) {
|
|
if (!is_int($time)) {
|
|
if (preg_match('/^[0-9]+$/', $time))
|
|
$time = intval($time);
|
|
else
|
|
return false;
|
|
}
|
|
return ($time >= 1577833200); // 2020-01-01 - date of birth of this soft
|
|
}
|
|
|
|
function check_status($status) {
|
|
global $status_list;
|
|
return array_key_exists($status, $status_list);
|
|
}
|
|
|
|
function check_description($comment) {
|
|
if (preg_match("/^[\p{L}0-9\p{P}\p{Zs}\p{Zl}\p{Sc}\=\+]+$/uim", $comment))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
function check_email($value, $domain=NULL, $checkDns=true) {
|
|
$regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/';
|
|
|
|
if (!preg_match($regex, $value)) {
|
|
return false;
|
|
}
|
|
|
|
$nd = explode('@', $value);
|
|
$nd=$nd[1];
|
|
|
|
if ($domain) {
|
|
if(is_array($domain)) {
|
|
if (!in_array($nd,$domain)) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if($nd!=$domain) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($checkDns && function_exists('checkdnsrr')) {
|
|
if (!(checkdnsrr($nd, 'MX') || checkdnsrr($nd, 'A'))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Handling item POST data
|
|
*/
|
|
function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=null, &$item=null,
|
|
&$changes=null) {
|
|
$field_errors=array();
|
|
if (isset($_POST['submit'])) {
|
|
logging('DEBUG', 'POST data : '.vardump($_POST));
|
|
// Name
|
|
if (!$enabled_fields || in_array('name', $enabled_fields)) {
|
|
if (isset($_POST['name'])) {
|
|
if (check_name($_POST['name'])) {
|
|
$info['name'] = $_POST['name'];
|
|
}
|
|
else {
|
|
$field_errors['name'] = "Ce nom est invalide.";
|
|
}
|
|
}
|
|
else {
|
|
$field_errors['name'] = "Cette information est obligatoire.";
|
|
}
|
|
}
|
|
|
|
// status
|
|
if (!$enabled_fields || in_array('status', $enabled_fields)) {
|
|
if (isset($_POST['status']) && check_status($_POST['status'])) {
|
|
$info['status'] = $_POST['status'];
|
|
}
|
|
else {
|
|
$field_errors['status'] = "Cette information est obligatoire.";
|
|
}
|
|
}
|
|
|
|
// description
|
|
if (
|
|
isset($_POST['description']) &&
|
|
(!$enabled_fields || in_array('description', $enabled_fields))
|
|
) {
|
|
if (check_is_empty(trim($_POST['description']))) {
|
|
$info['description'] = null;
|
|
}
|
|
else if (check_description($_POST['description'])) {
|
|
$info['description'] = $_POST['description'];
|
|
}
|
|
else {
|
|
$field_errors['description'] = "Cette description est invalide.";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check custom required fields
|
|
if (is_array($required_fields)) {
|
|
foreach ($required_fields as $field) {
|
|
if (array_key_exists($field, $field_errors))
|
|
continue;
|
|
if (array_key_exists($field, $info) && !is_null($info[$field]) && !check_is_empty($info))
|
|
continue;
|
|
$field_errors[$field] = "Cette information est obligatoire.";
|
|
}
|
|
}
|
|
|
|
if (empty($field_errors) && is_array($item) && !is_null($changes)) {
|
|
$changes = array();
|
|
foreach ($info as $key => $value) {
|
|
if ($value != $item[$key])
|
|
$changes[$key] = $value;
|
|
}
|
|
}
|
|
return $field_errors;
|
|
}
|
|
|
|
/*
|
|
* Parser/formater values helpers
|
|
*/
|
|
$_date_format = "%d/%m/%Y";
|
|
$_date_time_format = "%d/%m/%Y %H:%M:%S";
|
|
function format_time($time, $with_time=true) {
|
|
global $_date_format, $_date_time_format;
|
|
if ($with_time)
|
|
return strftime($_date_time_format, $time);
|
|
return strftime($_date_format, $time);
|
|
}
|
|
|
|
function parse_date($date, $with_time=true) {
|
|
global $_date_format, $_date_time_format;
|
|
if ($with_time)
|
|
$ptime = strptime($date, $_date_time_format);
|
|
else
|
|
$ptime = strptime($date, $_date_format);
|
|
if(is_array($ptime)) {
|
|
return mktime(
|
|
$ptime['tm_hour'],
|
|
$ptime['tm_min'],
|
|
$ptime['tm_sec'],
|
|
$ptime['tm_mon']+1,
|
|
$ptime['tm_mday'],
|
|
$ptime['tm_year']+1900
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function format_size($size, $digit=False) {
|
|
if (!$digit && $digit!==0) $digit=2;
|
|
if ($size>=1099511627776)
|
|
return number_format($size/1099511627776,$digit)."To";
|
|
elseif ($size>=1073741824)
|
|
return number_format($size/1073741824,$digit)."Go";
|
|
else if ($size>=1048576)
|
|
return number_format($size/1048576,$digit)."Mo";
|
|
else if ($size>=1024)
|
|
return number_format($size/1024,$digit)."Ko";
|
|
else
|
|
return $size."o";
|
|
}
|
|
|
|
function can_modify($item) {
|
|
return can_do(
|
|
$item,
|
|
array('pending')
|
|
);
|
|
}
|
|
|
|
function can_archive($item) {
|
|
return can_do(
|
|
$item,
|
|
array('refused', 'validated')
|
|
);
|
|
}
|
|
|
|
function can_delete($item) {
|
|
return can_do(
|
|
$item,
|
|
array('archived')
|
|
);
|
|
}
|
|
|
|
function can_do($item, $status=array()) {
|
|
return in_array($item['status'], $status);
|
|
}
|
|
|
|
/*
|
|
* Generic Data/value helpers
|
|
*/
|
|
function vardump($data) {
|
|
ob_start();
|
|
var_dump($data);
|
|
$data = ob_get_contents();
|
|
ob_end_clean();
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Format a callable object for logging
|
|
* @param string|array $callable The callable object
|
|
* @return string The callable object string representation
|
|
*/
|
|
function format_callable($callable) {
|
|
if (is_string($callable))
|
|
return $callable."()";
|
|
if (is_array($callable))
|
|
if (is_string($callable[0]))
|
|
return $callable[0]."::".$callable[1]."()";
|
|
elseif (is_object($callable[0]))
|
|
return get_class($callable[0])."->".$callable[1]."()";
|
|
else
|
|
return "Unkown->".$callable[1]."()";
|
|
// @phpstan-ignore-next-line
|
|
return vardump($callable);
|
|
}
|
|
|
|
function check_is_empty($val) {
|
|
switch(gettype($val)) {
|
|
case "boolean":
|
|
case "integer":
|
|
case "double":
|
|
case "object":
|
|
case "resource":
|
|
return False;
|
|
case "array":
|
|
case "string":
|
|
if ($val == "0") return false;
|
|
return empty($val);
|
|
case "NULL":
|
|
return True;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generic file/directory helpers
|
|
*/
|
|
function dump_file($file_path, $max_age=3600) {
|
|
if (is_file($file_path)) {
|
|
header('Content-Type: '.mime_content_type($file_path));
|
|
$last_modified_time = filemtime($file_path);
|
|
$etag = md5_file($file_path);
|
|
header("Cache-Control: max-age=$max_age, must-revalidate");
|
|
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
|
|
header("Etag: $etag");
|
|
|
|
if (
|
|
(
|
|
isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
|
@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time
|
|
) || (
|
|
isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
|
|
trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag
|
|
)
|
|
) {
|
|
header("HTTP/1.1 304 Not Modified");
|
|
exit();
|
|
}
|
|
|
|
header('Pragma: public');
|
|
header('Content-Length: ' . filesize($file_path));
|
|
readfile($file_path);
|
|
exit();
|
|
}
|
|
header("HTTP/1.1 404 Not found");
|
|
exit();
|
|
}
|
|
|
|
function delete_directory($dir, $recursive=true) {
|
|
$files = array_diff(scandir($dir), array('.','..'));
|
|
if ($recursive) {
|
|
foreach ($files as $file) {
|
|
if (is_dir("$dir/$file")) {
|
|
if (!delete_directory("$dir/$file", true)) {
|
|
logging('ERROR', "delete_directory($dir) : Fail to delete sub-directory '$dir/$file'.");
|
|
return false;
|
|
}
|
|
}
|
|
else if (!unlink("$dir/$file")) {
|
|
logging('ERROR', "delete_directory($dir) : Fail to delete '$dir/$file'.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (!empty($files)) {
|
|
logging('ERROR', "delete_directory($dir) : Directory is not empty.");
|
|
return false;
|
|
}
|
|
return rmdir($dir);
|
|
}
|
|
|
|
/*
|
|
* Run external command helper
|
|
*/
|
|
/**
|
|
* Run external command
|
|
*
|
|
* @param $command string|array The command. It's could be an array of the command with its
|
|
* arguments.
|
|
* @param $data_stdin string|null The command arguments (optional, default: null)
|
|
* @param $escape_command_args boolean If true, the command will be escaped
|
|
* (optional, default: true)
|
|
*
|
|
* @return false|array An array of return code, stdout and stderr result or False in case of fatal
|
|
* error
|
|
**/
|
|
function run_external_command($command, $data_stdin=null, $escape_command_args=true) {
|
|
if (is_array($command))
|
|
$command = implode(' ', $command);
|
|
if ($escape_command_args)
|
|
$command = escapeshellcmd($command);
|
|
logging('DEBUG', "Run external command: '$command'");
|
|
$descriptorspec = array(
|
|
0 => array("pipe", "r"), // stdin
|
|
1 => array("pipe", "w"), // stdout
|
|
2 => array("pipe", "w"), // stderr
|
|
);
|
|
$process = proc_open($command, $descriptorspec, $pipes);
|
|
|
|
if (!is_resource($process)) {
|
|
logging('ERROR', "Fail to run external command: '$command'");
|
|
return false;
|
|
}
|
|
|
|
if (!is_null($data_stdin)) {
|
|
fwrite($pipes[0], $data_stdin);
|
|
}
|
|
fclose($pipes[0]);
|
|
|
|
$stdout = stream_get_contents($pipes[1]);
|
|
fclose($pipes[1]);
|
|
|
|
$stderr = stream_get_contents($pipes[2]);
|
|
fclose($pipes[2]);
|
|
|
|
$return_value = proc_close($process);
|
|
|
|
$error = (!empty($stderr) || $return_value != 0);
|
|
logging(
|
|
($error?'ERROR':'DEBUG'),
|
|
"External command ".($error?"error":"result").":\n".
|
|
"\tCommand : $command\n".
|
|
"\tReturn code: $return_value\n".
|
|
"\tOutput:\n".
|
|
"\t\t- Stdout :\n$stdout\n\n".
|
|
"\t\t- Stderr :\n$stderr"
|
|
);
|
|
|
|
return array($return_value, $stdout, $stderr);
|
|
}
|
|
|
|
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|