* Copyright (C) 2003 Jean-Louis Bergamo * Copyright (C) 2004-2022 Laurent Destailleur * Copyright (C) 2004 Sebastien Di Cintio * Copyright (C) 2004 Benoit Mortier * Copyright (C) 2004 Christophe Combelles * Copyright (C) 2005-2019 Regis Houssin * Copyright (C) 2008 Raphael Bertrand (Resultic) * Copyright (C) 2010-2018 Juanjo Menent * Copyright (C) 2013 Cédric Salvador * Copyright (C) 2013-2021 Alexandre Spangaro * Copyright (C) 2014 Cédric GROSS * Copyright (C) 2014-2015 Marcos García * Copyright (C) 2015 Jean-François Ferry * Copyright (C) 2018-2024 Frédéric France * Copyright (C) 2019-2023 Thibault Foucart * Copyright (C) 2020 Open-Dsi * Copyright (C) 2021 Gauthier VERDOL * Copyright (C) 2022 Anthony Berton * Copyright (C) 2022 Ferran Marcet * Copyright (C) 2022 Charlene Benke * Copyright (C) 2023 Joachim Kueter * Copyright (C) 2024 MDW * Copyright (C) 2024 Lenin Rivas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * or see https://www.gnu.org/ */ /** * \file htdocs/core/lib/functions.lib.php * \brief A set of functions for Dolibarr * This file contains all frequently used functions. */ //include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php'; // Function for better PHP x compatibility if (!function_exists('utf8_encode')) { /** * Implement utf8_encode for PHP that does not support it. * * @param mixed $elements PHP Object to json encode * @return string Json encoded string * @phan-suppress PhanRedefineFunctionInternal */ function utf8_encode($elements) { return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1'); } } if (!function_exists('utf8_decode')) { /** * Implement utf8_decode for PHP that does not support it. * * @param mixed $elements PHP Object to json encode * @return string Json encoded string * @phan-suppress PhanRedefineFunctionInternal */ function utf8_decode($elements) { return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8'); } } if (!function_exists('str_starts_with')) { /** * str_starts_with * * @param string $haystack haystack * @param string $needle needle * @return boolean * @phan-suppress PhanRedefineFunctionInternal */ function str_starts_with($haystack, $needle) { return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0; } } if (!function_exists('str_ends_with')) { /** * str_ends_with * * @param string $haystack haystack * @param string $needle needle * @return boolean * @phan-suppress PhanRedefineFunctionInternal */ function str_ends_with($haystack, $needle) { return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle; } } if (!function_exists('str_contains')) { /** * str_contains * * @param string $haystack haystack * @param string $needle needle * @return boolean * @phan-suppress PhanRedefineFunctionInternal */ function str_contains($haystack, $needle) { return $needle !== '' && mb_strpos($haystack, $needle) !== false; } } /** * Return the full path of the directory where a module (or an object of a module) stores its files, * Path may depends on the entity if a multicompany module is enabled. * * @param CommonObject $object Dolibarr common object * @param string $module Override object element, for example to use 'mycompany' instead of 'societe' * @param int $forobject Return the more complete path for the given object instead of for the module only. * @param string $mode 'output' (full main dir) or 'outputrel' (relative dir) or 'temp' (for temporary files) or 'version' (dir for archived files) * @return string|null The path of the relative directory of the module, ending with / * @since Dolibarr V18 */ function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output') { global $conf; if (!is_object($object) && empty($module)) { return null; } if (empty($module) && !empty($object->element)) { $module = $object->element; } // Special case for backward compatibility if ($module == 'fichinter') { $module = 'ficheinter'; } elseif ($module == 'invoice_supplier') { $module = 'supplier_invoice'; } elseif ($module == 'order_supplier') { $module = 'supplier_order'; } // Get the relative path of directory if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') { if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) { $s = ''; if ($mode != 'outputrel') { $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)]; } if ($forobject && $object->id > 0) { $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object); } return $s; } else { return 'error-diroutput-not-defined-for-this-object='.$module; } } elseif ($mode == 'temp') { if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) { return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)]; } else { return 'error-dirtemp-not-defined-for-this-object='.$module; } } else { return 'error-bad-value-for-mode'; } } /** * Return the full path of the directory where a module (or an object of a module) stores its temporary files. * Path may depends on the entity if a multicompany module is enabled. * * @param CommonObject $object Dolibarr common object * @param string $module Override object element, for example to use 'mycompany' instead of 'societe' * @param int $forobject Return the more complete path for the given object instead of for the module only. * @return string|null The path of the relative temp directory of the module */ function getMultidirTemp($object, $module = '', $forobject = 0) { return getMultidirOutput($object, $module, $forobject, 'temp'); } /** * Return the full path of the directory where a module (or an object of a module) stores its versioned files. * Path may depends on the entity if a multicompany module is enabled. * * @param CommonObject $object Dolibarr common object * @param string $module Override object element, for example to use 'mycompany' instead of 'societe' * @param int $forobject Return the more complete path for the given object instead of for the module only. * @return string|null The path of the relative version directory of the module */ function getMultidirVersion($object, $module = '', $forobject = 0) { return getMultidirOutput($object, $module, $forobject, 'version'); } /** * Return a Dolibarr global constant string value * * @param string $key Key to return value, return $default if not set * @param string|int|float $default Value to return if not defined * @return string Value returned * @see getDolUserString() */ function getDolGlobalString($key, $default = '') { global $conf; return (string) (isset($conf->global->$key) ? $conf->global->$key : $default); } /** * Return a Dolibarr global constant int value. * The constants $conf->global->xxx are loaded by the script master.inc.php included at begin of any PHP page. * * @param string $key Key to return value, return $default if not set * @param int $default Value to return if not defined * @return int Value returned * @see getDolUserInt() */ function getDolGlobalInt($key, $default = 0) { global $conf; return (int) (isset($conf->global->$key) ? $conf->global->$key : $default); } /** * Return a Dolibarr global constant boolean value. * The constants $conf->global->xxx are loaded by the script master.inc.php included at begin of any PHP page. * * @param string $key Key to return value, return $default if not set * @param bool $default Value to return if not defined * @return bool Value returned */ function getDolGlobalBool($key, $default = false) { global $conf; return (bool) ($conf->global->$key ?? $default); } /** * Return Dolibarr user constant string value * * @param string $key Key to return value, return '' if not set * @param string|int|float $default Value to return * @param User $tmpuser To get another user than current user * @return string * @see getDolGlobalString() */ function getDolUserString($key, $default = '', $tmpuser = null) { if (empty($tmpuser)) { global $user; $tmpuser = $user; } return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key); } /** * Return Dolibarr user constant int value * * @param string $key Key to return value, return 0 if not set * @param int $default Value to return * @param User $tmpuser To get another user than current user * @return int */ function getDolUserInt($key, $default = 0, $tmpuser = null) { if (empty($tmpuser)) { global $user; $tmpuser = $user; } return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key); } /** * This mapping defines the conversion to the current internal * names from the alternative allowed names (including effectively deprecated * and future new names (not yet used as internal names). * * This allows to map any temporary or future name to the effective internal name. * * The value is typically the name of module's root directory. */ define( 'MODULE_MAPPING', array( // Map deprecated names to new names 'adherent' => 'member', // Has new directory 'member_type' => 'adherent_type', // No directory, but file called adherent_type 'banque' => 'bank', // Has new directory 'contrat' => 'contract', // Has new directory 'entrepot' => 'stock', // Has new directory 'projet' => 'project', // Has new directory 'categorie' => 'category', // Has old directory 'commande' => 'order', // Has old directory 'expedition' => 'shipping', // Has old directory 'facture' => 'invoice', // Has old directory 'fichinter' => 'intervention', // Has old directory 'ficheinter' => 'intervention', // Backup for 'fichinter' 'propale' => 'propal', // Has old directory 'socpeople' => 'contact', // Has old directory 'fournisseur' => 'supplier', // Has old directory 'actioncomm' => 'agenda', // NO module directory (public dir agenda) 'product_price' => 'productprice', // NO directory 'product_fournisseur_price' => 'productsupplierprice', // NO directory ) ); /** * Is Dolibarr module enabled * * @param string $module Module name to check * @return boolean True if module is enabled */ function isModEnabled($module) { global $conf; // Fix old names (map to new names) $arrayconv = MODULE_MAPPING; $arrayconvbis = array_flip(MODULE_MAPPING); if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) { // Special cases: both use the same module. $arrayconv['supplier_order'] = 'fournisseur'; $arrayconv['supplier_invoice'] = 'fournisseur'; } // Special case. // @TODO Replace isModEnabled('delivery_note') with // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION') if ($module == 'delivery_note') { if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) { return false; } else { $module = 'shipping'; } } $module_alt = $module; if (!empty($arrayconv[$module])) { $module_alt = $arrayconv[$module]; } $module_bis = $module; if (!empty($arrayconvbis[$module])) { $module_bis = $arrayconvbis[$module]; } return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]); //return !empty($conf->$module->enabled); } /** * isDolTms check if a timestamp is valid. * * @param int|string|null $timestamp timestamp to check * @return bool */ function isDolTms($timestamp) { if ($timestamp === '') { dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"], LOG_NOTICE); return false; } if (is_null($timestamp) || !is_numeric($timestamp)) { return false; } return true; } /** * Return a DoliDB instance (database handler). * * @param string $type Type of database (mysql, pgsql...) * @param string $host Address of database server * @param string $user Authorized username * @param string $pass Password * @param string $name Name of database * @param int $port Port of database server * @return DoliDB A DoliDB instance */ function getDoliDBInstance($type, $host, $user, $pass, $name, $port) { require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php'; $class = 'DoliDB'.ucfirst($type); $db = new $class($type, $host, $user, $pass, $name, $port); return $db; } /** * Get list of entity id to use. * * @param string $element Current element * 'societe', 'socpeople', 'actioncomm', 'agenda', 'resource', * 'product', 'productprice', 'stock', 'bom', 'mo', * 'propal', 'supplier_proposal', 'invoice', 'supplier_invoice', 'payment_various', * 'categorie', 'bank_account', 'bank_account', 'adherent', 'user', * 'commande', 'supplier_order', 'expedition', 'intervention', 'survey', * 'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project', * 'email_template', 'event', 'donation' * 'c_paiement', 'c_payment_term', ... * @param int<0,1> $shared 0=Return id of current entity only, * 1=Return id of current entity + shared entities (default) * @param ?CommonObject $currentobject Current object if needed * @return string Entity id(s) to use ( eg. entity IN ('.getEntity(elementname).')' ) */ function getEntity($element, $shared = 1, $currentobject = null) { global $conf, $mc, $hookmanager, $object, $action, $db; if (!is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager = new HookManager($db); } // fix different element names (France to English) switch ($element) { case 'projet': $element = 'project'; break; case 'contrat': $element = 'contract'; break; // "/contrat/class/contrat.class.php" case 'order_supplier': $element = 'supplier_order'; break; // "/fourn/class/fournisseur.commande.class.php" case 'invoice_supplier': $element = 'supplier_invoice'; break; // "/fourn/class/fournisseur.facture.class.php" } if (is_object($mc)) { $out = $mc->getEntity($element, $shared, $currentobject); } else { $out = ''; $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans'); if (in_array($element, $addzero)) { $out .= '0,'; } $out .= ((int) $conf->entity); } // Manipulate entities to query on the fly $parameters = array( 'element' => $element, 'shared' => $shared, 'object' => $object, 'currentobject' => $currentobject, 'out' => $out ); $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks if (is_numeric($reshook)) { if ($reshook == 0 && !empty($hookmanager->resPrint)) { $out .= ','.$hookmanager->resPrint; // add } elseif ($reshook == 1) { $out = $hookmanager->resPrint; // replace } } return $out; } /** * Set entity id to use when to create an object * * @param CommonObject $currentobject Current object * @return int Entity id to use ( eg. entity = '.setEntity($object) ) */ function setEntity($currentobject) { global $conf, $mc; if (is_object($mc) && method_exists($mc, 'setEntity')) { return $mc->setEntity($currentobject); } else { return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity); } } /** * Return if string has a name dedicated to store a secret * * @param string $keyname Name of key to test * @return boolean True if key is used to store a secret */ function isASecretKey($keyname) { return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname); } /** * Return a numeric value into an Excel like column number. So 0 return 'A', 1 returns 'B'..., 26 return 'AA' * * @param int|string $n Numeric value * @return string Column in Excel format */ function num2Alpha($n) { for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) { $r = chr($n % 26 + 0x41) . $r; } return $r; } /** * Return information about user browser * * Returns array with the following format: * array( * 'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown) * 'browserversion' => Browser version. Empty if unknown * 'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown) * 'layout' => (tablet|phone|classic) * 'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile * 'tablet' => true/false * ) * * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable * @return array{browsername:string,browserversion:string,browseros:string,browserua:string,layout:string,phone:string,tablet:bool} Check function documentation */ function getBrowserInfo($user_agent) { include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php'; $name = 'unknown'; $version = ''; $os = 'unknown'; $phone = ''; $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent $detectmobile = new Mobile_Detect(null, $user_agent); $tablet = $detectmobile->isTablet(); if ($detectmobile->isMobile()) { $phone = 'unknown'; // If phone/smartphone, we set phone os name. if ($detectmobile->is('AndroidOS')) { $os = $phone = 'android'; } elseif ($detectmobile->is('BlackBerryOS')) { $os = $phone = 'blackberry'; } elseif ($detectmobile->is('iOS')) { $os = 'ios'; $phone = 'iphone'; } elseif ($detectmobile->is('PalmOS')) { $os = $phone = 'palm'; } elseif ($detectmobile->is('SymbianOS')) { $os = 'symbian'; } elseif ($detectmobile->is('webOS')) { $os = 'webos'; } elseif ($detectmobile->is('MaemoOS')) { $os = 'maemo'; } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) { $os = 'windows'; } } // OS if (preg_match('/linux/i', $user_agent)) { $os = 'linux'; } elseif (preg_match('/macintosh/i', $user_agent)) { $os = 'macintosh'; } elseif (preg_match('/windows/i', $user_agent)) { $os = 'windows'; } // Name $reg = array(); if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'firefox'; $version = empty($reg[2]) ? '' : $reg[2]; } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'edge'; $version = empty($reg[2]) ? '' : $reg[2]; } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) { $name = 'chrome'; $version = empty($reg[2]) ? '' : $reg[2]; } elseif (preg_match('/chrome/i', $user_agent, $reg)) { // we can have 'chrome (Mozilla...) chrome x.y' in one string $name = 'chrome'; } elseif (preg_match('/iceweasel/i', $user_agent)) { $name = 'iceweasel'; } elseif (preg_match('/epiphany/i', $user_agent)) { $name = 'epiphany'; } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'safari'; $version = empty($reg[2]) ? '' : $reg[2]; } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { // Safari is often present in string for mobile but its not. $name = 'opera'; $version = empty($reg[2]) ? '' : $reg[2]; } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name = 'ie'; $version = end($reg); } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { // MS products at end $name = 'ie'; $version = end($reg); } elseif (preg_match('/l[iy]n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { // MS products at end $name = 'lynxlinks'; $version = empty($reg[3]) ? '' : $reg[3]; } if ($tablet) { $layout = 'tablet'; } elseif ($phone) { $layout = 'phone'; } else { $layout = 'classic'; } return array( 'browsername' => $name, 'browserversion' => $version, 'browseros' => $os, 'browserua' => $user_agent, 'layout' => $layout, // tablet, phone, classic 'phone' => $phone, // deprecated 'tablet' => $tablet // deprecated ); } /** * Function called at end of web php process * * @return void */ function dol_shutdown() { global $db; $disconnectdone = false; $depth = 0; if (is_object($db) && !empty($db->connected)) { $depth = $db->transaction_opened; $disconnectdone = $db->close(); } dol_syslog("--- End access to ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]).(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO)); } /** * Return true if we are in a context of submitting the parameter $paramname from a POST of a form. * Warning: * For action=add, use: $var = GETPOST('var'); // No GETPOSTISSET, so GETPOST always called and default value is retrieved if not a form POST, and value of form is retrieved if it is a form POST. * For action=update, use: $var = GETPOSTISSET('var') ? GETPOST('var') : $object->var; * * @param string $paramname Name or parameter to test * @return boolean True if we have just submit a POST or GET request with the parameter provided (even if param is empty) */ function GETPOSTISSET($paramname) { $isset = false; $relativepathstring = $_SERVER["PHP_SELF"]; // Clean $relativepathstring if (constant('DOL_URL_ROOT')) { $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); } $relativepathstring = preg_replace('/^\//', '', $relativepathstring); $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); //var_dump($relativepathstring); //var_dump($user->default_values); // Code for search criteria persistence. // Retrieve values if restore_lastsearch_values if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); if (is_array($tmp)) { foreach ($tmp as $key => $val) { if ($key == $paramname) { // We are on the requested parameter $isset = true; break; } } } } // If there is saved contextpage, limit, page or mode if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) { $isset = true; } } else { $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here } return $isset; } /** * Return true if the parameter $paramname is submit from a POST OR GET as an array. * Can be used before GETPOST to know if the $check param of GETPOST need to check an array or a string * * @param string $paramname Name or parameter to test * @param int<0,3> $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) * @return bool True if we have just submit a POST or GET request with the parameter provided (even if param is empty) */ function GETPOSTISARRAY($paramname, $method = 0) { // for $method test need return the same $val as GETPOST if (empty($method)) { $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); } elseif ($method == 1) { $val = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; } elseif ($method == 2) { $val = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; } elseif ($method == 3) { $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); } else { $val = 'BadFirstParameterForGETPOST'; } return is_array($val); } /** * Return value of a param into GET or POST supervariable. * Use the property $user->default_values[path]['createform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] * Note: The property $user->default_values is loaded by main.php when loading the user. * * @param string $paramname Name of parameter to found * @param string $check Type of check * '' or 'none'=no check (deprecated) * 'password'=allow characters for a password * 'email'=allow characters for an email * 'array', 'array:restricthtml' or 'array:aZ09' to check it's an array * 'int'=check it's numeric (integer or float) * 'intcomma'=check it's integer+comma ('1,2,3,4...') * 'alpha'=Same than alphanohtml * 'alphawithlgt'=alpha with lgt * 'alphanohtml'=check there is no html content and no " and no ../ * 'aZ'=check it's a-z only * 'aZ09'=check it's simple alpha string (recommended for keys) * 'aZ09arobase'=check it's a string for an element type ('myobject@mymodule') * 'aZ09comma'=check it's a string for a sortfield or sortorder * 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string) * 'nohtml'=check there is no html content * 'restricthtml'=check html content is restricted to some tags only * 'custom'= custom filter specify $filter and $options) * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) * @param ?int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @param int $noreplace Force disable of replacement of __xxx__ strings. * @return string|array Value found (string or array), or '' if check fails */ function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0) { global $mysoc, $user, $conf; if (empty($paramname)) { // Explicit test for null for phan. return 'BadFirstParameterForGETPOST'; } if (empty($check)) { dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and a 2nd param that is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING); // Enable this line to know who call the GETPOST with '' $check parameter. //var_dump(debug_backtrace()[0]); } if (empty($method)) { $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); } elseif ($method == 1) { $out = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; } elseif ($method == 2) { $out = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; } elseif ($method == 3) { $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); } else { return 'BadThirdParameterForGETPOST'; } $relativepathstring = ''; // For static analysis - looks possibly undefined if not set. if (empty($method) || $method == 3 || $method == 4) { $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]); // Clean $relativepathstring if (constant('DOL_URL_ROOT')) { $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); } $relativepathstring = preg_replace('/^\//', '', $relativepathstring); $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); //var_dump($relativepathstring); //var_dump($user->default_values); // Code for search criteria persistence. // Retrieve saved values if restore_lastsearch_values is set if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); if (is_array($tmp)) { foreach ($tmp as $key => $val) { if ($key == $paramname) { // We are on the requested parameter $out = $val; break; } } } } // If there is saved contextpage, page or limit if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring]; } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { $out = $_SESSION['lastsearch_limit_'.$relativepathstring]; } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { $out = $_SESSION['lastsearch_page_'.$relativepathstring]; } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) { $out = $_SESSION['lastsearch_mode_'.$relativepathstring]; } } elseif (!isset($_GET['sortfield'])) { // Else, retrieve default values if we are not doing a sort // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Search default value from $object->field global $object; '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well if (is_object($object) && isset($object->fields[$paramname]['default'])) { // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset $out = $object->fields[$paramname]['default']; } } if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) { if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Now search in setup to overwrite default values if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values' if (isset($user->default_values[$relativepathstring]['createform'])) { foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) { $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified) { if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) { $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname]; break; } } } } } } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Management of default search_filters and sort order if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values' //var_dump($user->default_values[$relativepathstring]); if ($paramname == 'sortfield' || $paramname == 'sortorder') { // Sorted on which fields ? ASC or DESC ? if (isset($user->default_values[$relativepathstring]['sortorder'])) { // Even if paramname is sortfield, data are stored into ['sortorder...'] foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) { $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified) { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) { if ($out) { $out .= ', '; } if ($paramname == 'sortfield') { $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace); } if ($paramname == 'sortorder') { $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace); } } //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?) } } } } elseif (isset($user->default_values[$relativepathstring]['filters'])) { foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter continue; } $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) { // We must keep $_POST and $_GET here if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) { // We made a search from quick search menu, do we still use default filter ? if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); } } else { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); } break; } } } } } } } } // Substitution variables for GETPOST (used to get final url with variable parameters or final default value, when using variable parameters __XXX__ in the GET URL) // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ... // We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text. '@phan-var-force string $paramname'; if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) { $reg = array(); $maxloop = 20; $loopnb = 0; // Protection against infinite loop while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop)) { // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side. $loopnb++; $newout = ''; if ($reg[1] == 'DAY') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['mday']; } elseif ($reg[1] == 'MONTH') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['mon']; } elseif ($reg[1] == 'YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['year']; } elseif ($reg[1] == 'PREVIOUS_DAY') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; } elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; } elseif ($reg[1] == 'PREVIOUS_YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); } elseif ($reg[1] == 'NEXT_DAY') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; } elseif ($reg[1] == 'NEXT_MONTH') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; } elseif ($reg[1] == 'NEXT_YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') { $newout = $mysoc->country_id; } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') { $newout = $user->id; } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') { $newout = $user->fk_user; } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') { $newout = $conf->entity; } elseif ($reg[1] == 'ID') { $newout = '__ID__'; // We keep __ID__ we find into backtopage url } else { $newout = ''; // Key not found, we replace with empty string } //var_dump('__'.$reg[1].'__ -> '.$newout); $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out); } } // Check type of variable and make sanitization according to this if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma' if (!is_array($out) || empty($out)) { $out = array(); } else { $tmparray = explode(':', $check); if (!empty($tmparray[1])) { $tmpcheck = $tmparray[1]; } else { $tmpcheck = 'alphanohtml'; } foreach ($out as $outkey => $outval) { $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options); } } } else { // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags if (strpos($paramname, 'search_') === 0) { $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out); } // @phan-suppress-next-line UnknownSanitizeType $out = sanitizeVal($out, $check, $filter, $options); } // Sanitizing for special parameters. // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed. if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') { $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements. $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements. do { $oldstringtoclean = $out; $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out); $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc' $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL } while ($oldstringtoclean != $out); } // Code for search criteria persistence. // Save data into session if key start with 'search_' if (empty($method) || $method == 3 || $method == 4) { if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) { //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]); // We save search key only if $out not empty that means: // - posted value not empty, or // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not). if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out; } } } return $out; } /** * Return the value of a $_GET or $_POST supervariable, converted into integer. * Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] * Note: The property $user->default_values is loaded by main.php when loading the user. * * @param string $paramname Name of the $_GET or $_POST parameter * @param int<0,3> $method Type of method (0 = $_GET then $_POST, 1 = only $_GET, 2 = only $_POST, 3 = $_POST then $_GET) * @return int Value converted into integer */ function GETPOSTINT($paramname, $method = 0) { return (int) GETPOST($paramname, 'int', $method, null, null, 0); } /** * Return the value of a $_GET or $_POST supervariable, converted into float. * * @param string $paramname Name of the $_GET or $_POST parameter * @param string|int $rounding Type of rounding ('', 'MU', 'MT, 'MS', 'CU', 'CT', integer) {@see price2num()} * @return float Value converted into float * @since Dolibarr V20 */ function GETPOSTFLOAT($paramname, $rounding = '') { // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.) return (float) price2num(GETPOST($paramname), $rounding, 2); } /** * Helper function that combines values of a dolibarr DatePicker (such as Form::selectDate) for year, month, day (and * optionally hour, minute, second) fields to return a timestamp. * * @param string $prefix Prefix used to build the date selector (for instance using Form::selectDate) * @param string $hourTime 'getpost' to include hour, minute, second values from the HTTP request, 'XX:YY:ZZ' to set * hour, minute, second respectively (for instance '23:59:59') * @param string $gm Passed to dol_mktime * @return int|string Date as a timestamp, '' or false if error */ function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto') { $m = array(); if ($hourTime === 'getpost') { $hour = GETPOSTINT($prefix . 'hour'); $minute = GETPOSTINT($prefix . 'minute'); $second = GETPOSTINT($prefix . 'second'); } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) { $hour = intval($m[1]); $minute = intval($m[2]); $second = intval($m[3]); } else { $hour = $minute = $second = 0; } // normalize out of range values $hour = min($hour, 23); $minute = min($minute, 59); $second = min($second, 59); return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm); } /** * Return a sanitized or empty value after checking value against a rule. * * @deprecated * @param string|array $out Value to check/clear. * @param string $check Type of check/sanitizing * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @return string|array Value sanitized (string or array). It may be '' if format check fails. */ function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) { return sanitizeVal($out, $check, $filter, $options); } /** * Return a sanitized or empty value after checking value against a rule. * * @param string|array $out Value to check/clear. * @param string $check Type of check/sanitizing * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @return string|array Value sanitized (string or array). It may be '' if format check fails. */ function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) { // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize // Check is done after replacement switch ($check) { case 'none': case 'password': break; case 'int': // Check param is a numeric value (integer but also float or hexadecimal) if (!is_numeric($out)) { $out = ''; } break; case 'intcomma': if (is_array($out)) { $out = implode(',', $out); } if (preg_match('/[^0-9,-]+/i', $out)) { $out = ''; } break; case 'san_alpha': $out = filter_var($out, FILTER_SANITIZE_STRING); break; case 'email': $out = filter_var($out, FILTER_SANITIZE_EMAIL); break; case 'aZ': if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z]+/i', $out)) { $out = ''; } } break; case 'aZ09': if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) { $out = ''; } } break; case 'aZ09arobase': // great to sanitize $objecttype parameter if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) { $out = ''; } } break; case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh' if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) { $out = ''; } } break; case 'alpha': // No html and no ../ and " case 'alphanohtml': // Recommended for most scalar parameters and search parameters if (!is_array($out)) { $out = trim($out); do { $oldstringtoclean = $out; // Remove html tags $out = dol_string_nohtmltag($out, 0); // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok). $out = preg_replace('/\\\([0-9xu])/', '/\1', $out); // Remove also other dangerous string sequences // '../' or '..\' is dangerous because it allows dir transversals // '&', '&', '&'... is a the char '&' alone but there is no reason to accept such way to encode input char // '"' = '"' = '"' = '"' is dangerous because param in url can close the href= or src= and add javascript functions. // '/', '/', '/' is the char '/' but there is no reason to accept such way to encode this input char // '\' = '\' = '\' is the char '\' but there is no reason to accept such way to encode this input char $out = str_ireplace(array('../', '..\\', '&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '/', '\', '\', '\'), '', $out); } while ($oldstringtoclean != $out); // keep lines feed } break; case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name ". Less secured than 'alphanohtml' if (!is_array($out)) { $out = trim($out); do { $oldstringtoclean = $out; // Decode html entities $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8'); // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok). $out = preg_replace('/\\\([0-9xu])/', '/\1', $out); // Remove also other dangerous string sequences // '../' or '..\' is dangerous because it allows dir transversals // '&', '&', '&'... is a the char '&' alone but there is no reason to accept such way to encode input char // '"' = '"' = '"' = '"' is dangerous because param in url can close the href= or src= and add javascript functions. // '/', '/', '/' is the char '/' but there is no reason to accept such way to encode this input char // '\' = '\' = '\' is the char '\' but there is no reason to accept such way to encode this input char $out = str_ireplace(array('../', '..\\', '&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '/', '\', '\', '\'), '', $out); } while ($oldstringtoclean != $out); } break; case 'nohtml': // No html $out = dol_string_nohtmltag($out, 0); break; case 'restricthtmlnolink': case 'restricthtml': // Recommended for most html textarea case 'restricthtmlallowclass': case 'restricthtmlallowlinkscript': // Allow link and script tag for head section. case 'restricthtmlallowunvalid': $out = dol_htmlwithnojs($out, 1, $check); break; case 'custom': if (!empty($out)) { if (empty($filter)) { return 'BadParameterForGETPOST - Param 3 of sanitizeVal()'; } if (is_null($options)) { $options = 0; } $out = filter_var($out, $filter, $options); } break; default: dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR); $out = GETPOST($out, 'alphanohtml'); break; } return $out; } if (!function_exists('dol_getprefix')) { /** * Return a prefix to use for this Dolibarr instance, for session/cookie names or email id. * The prefix is unique for instance and avoid conflict between multi-instances, even when having two instances with same root dir * or two instances in same virtual servers. * This function must not use dol_hash (that is used for password hash) and need to have all context $conf loaded. * * @param string $mode '' (prefix for session name) or 'email' (prefix for email id) * @return string A calculated prefix * @phan-suppress PhanRedefineFunction - Also defined in webportal.main.inc.php */ function dol_getprefix($mode = '') { // If prefix is for email (we need to have $conf already loaded for this case) if ($mode == 'email') { global $conf; if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') { return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID; } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME' return $_SERVER["SERVER_NAME"]; } } // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions) if (!empty($conf->file->instance_unique_id)) { return sha1('dolibarr'.$conf->file->instance_unique_id); } // For backward compatibility when instance_unique_id is not set return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } // If prefix is for session (no need to have $conf loaded) global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance // The recommended value (may be not defined for old versions) if (!empty($tmp_instance_unique_id)) { return sha1('dolibarr'.$tmp_instance_unique_id); } // For backward compatibility when instance_unique_id is not set if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) { return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } else { return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } } } /** * Make an include_once using default root and alternate root if it fails. * To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile') * To link to a module file from a module file, use include './mymodulefile'; * To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages) * * @param string $relpath Relative path to file (Ie: mydir/myfile, ../myfile, ...) * @param string $classname Class name (deprecated) * @return bool True if load is a success, False if it fails */ function dol_include_once($relpath, $classname = '') { global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var'] $fullpath = dol_buildpath($relpath); if (!file_exists($fullpath)) { dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING); return false; } if (!empty($classname) && !class_exists($classname)) { return include $fullpath; } else { return include_once $fullpath; } } /** * Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound. * * @param string $path Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile * @param int $type 0=Used for a Filesystem path, 1=Used for an URL path (output relative), 2=Used for an URL path (output full path using same host that current url), 3=Used for an URL path (output full path using host defined into $dolibarr_main_url_root of conf file) * @param int $returnemptyifnotfound 0:If $type==0 and if file was not found into alternate dir, return default path into main dir (no test on it) * 1:If $type==0 and if file was not found into alternate dir, return empty string * 2:If $type==0 and if file was not found into alternate dir, test into main dir, return default path if found, empty string if not found * @return string Full filesystem path (if path=0) or '' if file not found, Full url path (if mode=1) */ function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0) { global $conf; $path = preg_replace('/^\//', '', $path); if (empty($type)) { // For a filesystem path $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path if (is_array($conf->file->dol_document_root)) { foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...) if ($key == 'main') { continue; } // if (@file_exists($dirroot.'/'.$path)) { if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn] $res = $dirroot.'/'.$path; return $res; } } } if ($returnemptyifnotfound) { // Not found into alternate dir if ($returnemptyifnotfound == 1 || !file_exists($res)) { return ''; } } } else { // For an url path // We try to get local path of file on filesystem from url // Note that trying to know if a file on disk exist by forging path on disk from url // works only for some web server and some setup. This is bugged when // using proxy, rewriting, virtual path, etc... $res = ''; if ($type == 1) { $res = DOL_URL_ROOT.'/'.$path; // Standard value } if ($type == 2) { $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value } if ($type == 3) { $res = DOL_URL_ROOT.'/'.$path; } foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...) if ($key == 'main') { if ($type == 3) { /*global $dolibarr_main_url_root;*/ // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root)); $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax } continue; } $regs = array(); preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?' if (!empty($regs[1])) { //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'
'."\n"; //if (file_exists($dirroot.'/'.$regs[1])) { if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn] if ($type == 1) { $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; } if ($type == 2) { $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; } if ($type == 3) { /*global $dolibarr_main_url_root;*/ // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root)); $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax } break; } } } } return $res; } /** * Get properties for an object - including magic properties when requested * * Only returns properties that exist * * @param object $obj Object to get properties from * @param string[] $properties Optional list of properties to get. * When empty, only gets public properties. * @return array Hash for retrieved values (key=name) */ function dol_get_object_properties($obj, $properties = []) { // Get real properties using get_object_vars() if $properties is empty if (empty($properties)) { return get_object_vars($obj); } $existingProperties = []; $realProperties = get_object_vars($obj); // Get the real or magic property values foreach ($properties as $property) { if (array_key_exists($property, $realProperties)) { // Real property, add the value $existingProperties[$property] = $obj->{$property}; } elseif (property_exists($obj, $property)) { // Magic property $existingProperties[$property] = $obj->{$property}; } } return $existingProperties; } /** * Create a clone of instance of object (new instance with same value for each properties) * With native = 0: Property that are references are different memory area in the new object (full isolation clone). This means $this->object of new object may not be valid (except this->db that is voluntarly kept). * With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db of new object is still valid but point to same this->db than original object. * With native = 2: Property that are reference are different memory area in the new object (full isolation clone). Only scalar and array values are cloned. This means method are not availables and $this->db of new object is not valid. * * @template T of object * * @param T $object Object to clone * @param int $native 0=Full isolation method, 1=Native PHP method, 2=Full isolation method keeping only scalar and array properties (recommended) * @return T Clone object * @see https://php.net/manual/language.oop5.cloning.php * @phan-suppress PhanTypeExpectedObjectPropAccess */ function dol_clone($object, $native = 0) { if ($native == 0) { // deprecated method, use the method with native = 2 instead $tmpsavdb = null; if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') { $tmpsavdb = $object->db; unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection') } $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields if (!empty($tmpsavdb)) { $object->db = $tmpsavdb; } } elseif ($native == 2) { // recommended method to have a full isolated cloned object $myclone = new stdClass(); $tmparray = get_object_vars($object); // return only public properties if (is_array($tmparray)) { foreach ($tmparray as $propertykey => $propertyval) { if (is_scalar($propertyval) || is_array($propertyval)) { $myclone->$propertykey = $propertyval; } } } } else { $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable) } return $myclone; } /** * Optimize a size for some browsers (phone, smarphone, ...) * * @param int $size Size we want * @param string $type Type of optimizing: * '' = function used to define a size for truncation * 'width' = function is used to define a width * @return int New size after optimizing */ function dol_size($size, $type = '') { global $conf; if (empty($conf->dol_optimize_smallscreen)) { return $size; } if ($type == 'width' && $size > 250) { return 250; } else { return 10; } } /** * Clean a string to use it as a file name. * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). * * @param string $str String to clean * @param string $newstr String to replace bad chars with. * @param int $unaccent 1=Remove also accent (default), 0 do not remove them * @return string String cleaned * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizePathName() */ function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1) { // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // Char '>' '<' '|' '$' and ';' are special chars for shells. // Char '/' and '\' are file delimiters. // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`'); $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); $tmp = preg_replace('/\-\-+/', '_', $tmp); $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); $tmp = preg_replace('/\s+\-$/', '', $tmp); $tmp = str_replace('..', '', $tmp); return $tmp; } /** * Clean a string to use it as a path name. Similar to dol_sanitizeFileName but accept / and \ chars. * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). * * @param string $str String to clean * @param string $newstr String to replace bad chars with * @param int $unaccent 1=Remove also accent (default), 0 do not remove them * @return string String cleaned * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName() */ function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1) { // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // Char '>' '<' '|' '$' and ';' are special chars for shells. // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`'); $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); $tmp = preg_replace('/\-\-+/', '_', $tmp); $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); $tmp = preg_replace('/\s+\-$/', '', $tmp); $tmp = str_replace('..', '', $tmp); return $tmp; } /** * Clean a string to use it as an URL (into a href or src attribute) * * @param string $stringtoclean String to clean * @param int $type 0=Accept all Url, 1=Clean external Url (keep only relative Url) * @return string Escaped string. */ function dol_sanitizeUrl($stringtoclean, $type = 1) { // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char) // We should use dol_string_nounprintableascii but function may not be yet loaded/available $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: onerror=alert(1) $stringtoclean = preg_replace('//', '', $stringtoclean); $stringtoclean = str_replace('\\', '/', $stringtoclean); if ($type == 1) { // removing : should disable links to external url like http:aaa) // removing ';' should disable "named" html entities encode into an url (we should not have this into an url) $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean); } do { $oldstringtoclean = $stringtoclean; // removing '&colon' should disable links to external url like http:aaa) // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url) $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean); } while ($oldstringtoclean != $stringtoclean); if ($type == 1) { // removing '//' should disable links to external url like //aaa or http//) $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean); } return $stringtoclean; } /** * Clean a string to use it as an Email. * * @param string $stringtoclean String to clean. Example 'abc@mycompany.com ' * @return string Escaped string. */ function dol_sanitizeEmail($stringtoclean) { do { $oldstringtoclean = $stringtoclean; $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean); } while ($oldstringtoclean != $stringtoclean); return $stringtoclean; } /** * Clean a string to use it as a key or code. So only char a-Z, A-Z, _ and 0-9 is kept. * * @param string $str String to clean * @return string String cleaned (a-zA-Z_) * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitize...() */ function dol_sanitizeKeyCode($str) { return preg_replace('/[^\w]+/', '', $str); } /** * Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName * * @param string $str String to clean * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_nospecial() */ function dol_string_unaccent($str) { if (is_null($str)) { return ''; } if (utf8_check($str)) { if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) { $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD); return $transliterator->transliterate($str); } // See http://www.utf8-chartable.de/ $string = rawurlencode($str); $replacements = array( '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A', '%C3%87' => 'C', '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E', '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I', '%C3%91' => 'N', '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%A0' => 'S', '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C3%9D' => 'Y', '%C5%B8' => 'y', '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a', '%C3%A7' => 'c', '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e', '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i', '%C3%B1' => 'n', '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%A1' => 's', '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C3%BD' => 'y', '%C3%BF' => 'y' ); $string = strtr($string, $replacements); return rawurldecode($string); } else { // See http://www.ascii-code.com/ $string = strtr( $str, "\xC0\xC1\xC2\xC3\xC4\xC5\xC7 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8 \xF9\xFA\xFB\xFC\xFD\xFF", "AAAAAAC EEEEIIIIDN OOOOOUUUY aaaaaaceeee iiiidnooooo uuuuyy" ); $string = strtr($string, array("\xC4" => "Ae", "\xC6" => "AE", "\xD6" => "Oe", "\xDC" => "Ue", "\xDE" => "TH", "\xDF" => "ss", "\xE4" => "ae", "\xE6" => "ae", "\xF6" => "oe", "\xFC" => "ue", "\xFE" => "th")); return $string; } } /** * Clean a string from all punctuation characters to use it as a ref or login. * This is a more complete function than dol_sanitizeFileName(). * * @param string $str String to clean * @param string $newstr String to replace forbidden chars with * @param array|string $badcharstoreplace Array of forbidden characters to replace. Use '' to keep default list. * @param array|string $badcharstoremove Array of forbidden characters to remove. Use '' to keep default list. * @param int $keepspaces 1=Do not treat space as a special char to replace or remove * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nounprintableascii() */ function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0) { $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName if (empty($keepspaces)) { $forbidden_chars_to_replace[] = " "; } $forbidden_chars_to_remove = array(); //$forbidden_chars_to_remove=array("(",")"); if (is_array($badcharstoreplace)) { $forbidden_chars_to_replace = $badcharstoreplace; } if (is_array($badcharstoremove)) { $forbidden_chars_to_remove = $badcharstoremove; } // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str)); } /** * Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F). It can also removes also Tab-CR-LF. UTF8 chars remains. * This can be used to sanitize a string and view its real content. Some hacks try to obfuscate attacks by inserting non printable chars. * Note, for information: UTF8 on 1 byte are: \x00-\7F * 2 bytes are: byte 1 \xc0-\xdf, byte 2 = \x80-\xbf * 3 bytes are: byte 1 \xe0-\xef, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf * 4 bytes are: byte 1 \xf0-\xf7, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf, byte 4 = \x80-\xbf * @param string $str String to clean * @param int $removetabcrlf Remove also CR-LF * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nospecial() */ function dol_string_nounprintableascii($str, $removetabcrlf = 1) { if ($removetabcrlf) { return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace } else { return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace } } /** * Returns text escaped for inclusion into javascript code * * @param string $stringtoescape String to escape * @param int<0,3> $mode 0=Escape also ' and " into ', 1=Escape ' but not " for usage into 'string', 2=Escape " but not ' for usage into "string", 3=Escape ' and " with \ * @param int $noescapebackslashn 0=Escape also \n. 1=Do not escape \n. * @return string Escaped string. Both ' and " are escaped into ' if they are escaped. */ function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0) { if (is_null($stringtoescape)) { return ''; } // escape quotes and backslashes, newlines, etc. $substitjs = array("'" => "\\'", "\r" => '\\r'); //$substitjs[' $stringforquotes 2=String for doublequotes, 1=String for simple quotes * @return string Escaped string for PHP content. */ function dol_escape_php($stringtoescape, $stringforquotes = 2) { if (is_null($stringtoescape)) { return ''; } if ($stringforquotes == 2) { return str_replace('"', "'", $stringtoescape); } elseif ($stringforquotes == 1) { // We remove the \ char. // If we allow the \ char, we can have $stringtoescape = // abc\';phpcodedanger; so the escapement will become // abc\\';phpcodedanger; and injecting this into // $a='...' will give $ac='abc\\';phpcodedanger; $stringtoescape = str_replace('\\', '', $stringtoescape); return str_replace("'", "\'", str_replace('"', "'", $stringtoescape)); } return 'Bad parameter for stringforquotes in dol_escape_php'; } /** * Returns text escaped for inclusion into a XML string * * @param string $stringtoescape String to escape * @return string Escaped string for XML content. */ function dol_escape_xml($stringtoescape) { return $stringtoescape; } /** * Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on HTML page. * To use text that is not HTML content inside an attribute, you can simply use only dol_escape_htmltag(). In doubt, use dolPrintHTMLForAttribute(). * * @param string $s String to print * @return string String ready for HTML output */ function dolPrintLabel($s) { return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1); } /** * Return a string label (possible on several lines and that should not contains any HTML) ready to be output on HTML page. * To use text that is not HTML content inside an attribute, you can simply use only dol_escape_htmltag(). In doubt, use dolPrintHTMLForAttribute(). * * @param string $s String to print * @return string String ready for HTML output */ function dolPrintText($s) { return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1); } /** * Return a string (that can be on several lines) ready to be output on a HTML page. * To output a text inside an attribute, you can use dolPrintHTMLForAttribute() or dolPrintHTMLForTextArea() inside a textarea * * @param string $s String to print * @param int $allowiframe Allow iframe tags * @return string String ready for HTML output (sanitized and escape) * @see dolPrintHTMLForAttribute(), dolPrintHTMLFortextArea() */ function dolPrintHTML($s, $allowiframe = 0) { return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1); } /** * Return a string ready to be output on an HTML attribute (alt, title, data-html, ...) * * @param string $s String to print * @return string String ready for HTML output * @see dolPrintHTML(), dolPrintHTMLFortextArea() */ function dolPrintHTMLForAttribute($s) { // The dol_htmlentitiesbr will convert simple text into html // The dol_escape_htmltag will escape html chars. return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1); } /** * Return a string ready to be output on input textarea. * Differs from dolPrintHTML because all tags are escape. With dolPrintHTML, all tags except common one are escaped. * * @param string $s String to print * @param int $allowiframe Allow iframe tags * @return string String ready for HTML output into a textarea * @see dolPrintHTML(), dolPrintHTMLForAttribute() */ function dolPrintHTMLForTextArea($s, $allowiframe = 0) { return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1); } /** * Return a string ready to be output on an HTML attribute (alt, title, ...) * * @param string $s String to print * @return string String ready for HTML output */ function dolPrintPassword($s) { return htmlspecialchars($s, ENT_COMPAT, 'UTF-8'); } /** * Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input fields. * When we need to output strings on pages, we should use: * - dolPrintLabel... * - dolPrintHTML... that is dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr(), 1, 1, 1)), 1, 1) for notes or descriptions into textarea, add 'common' if into a html content * - dolPrintPassword that is abelhtmlspecialchars( , ENT_COMPAT, 'UTF-8') for passwords. * * @param string $stringtoescape String to escape * @param int $keepb 1=Replace b tags with escaped value (except if in $noescapetags), 0=Remove them completely * @param int $keepn 1=Preserve \r\n strings, 0=Replace them with escaped value, -1=Remove them. Set to 1 when escaping for a