diff --git a/api/src/Etemplate/Widget/Customfields.php b/api/src/Etemplate/Widget/Customfields.php index b3f1691cfe..b3dec7cfa4 100644 --- a/api/src/Etemplate/Widget/Customfields.php +++ b/api/src/Etemplate/Widget/Customfields.php @@ -330,7 +330,7 @@ class Customfields extends Transformer $widget->attrs['multiple'] = $field['rows'] > 1; // fall through case 'radio': - if (count($field['values']) == 1 && isset($field['values']['@'])) + if (!empty($field['values']) && count($field['values']) == 1 && isset($field['values']['@'])) { $field['values'] = Api\Storage\Customfields::get_options_from_file($field['values']['@']); } diff --git a/api/src/Session.php b/api/src/Session.php index 72b9475566..55c45c23b4 100644 --- a/api/src/Session.php +++ b/api/src/Session.php @@ -73,12 +73,6 @@ class Session */ const EGW_SESSION_NAME = 'sessionid'; - /** - * Used mcrypt algorithm and mode - */ - const MCRYPT_ALGO = MCRYPT_TRIPLEDES; - const MCRYPT_MODE = MCRYPT_MODE_ECB; - /** * current user login (account_lid@domain) * @@ -266,8 +260,10 @@ class Session */ function __wakeup() { - ini_set('session.gc_maxlifetime', $GLOBALS['egw_info']['server']['sessions_timeout']); - + if (!empty($GLOBALS['egw_info']['server']['sessions_timeout']) && session_status() === PHP_SESSION_NONE) + { + ini_set('session.gc_maxlifetime', $GLOBALS['egw_info']['server']['sessions_timeout']); + } $this->action = null; } @@ -399,13 +395,13 @@ class Session /** * Check if session encryption is configured, possible and initialise it * + * If mcrypt extension is not available (eg. in PHP 7.2+ no longer contains it) fail gracefully. + * * @param string $kp3 mcrypt key transported via cookie or get parameter like the session id, * unlike the session id it's not know on the server, so only the client-request can decrypt the session! - * @param string $algo =self::MCRYPT_ALGO - * @param string $mode =self::MCRYPT_MODE * @return boolean true if encryption is used, false otherwise */ - static private function init_crypt($kp3,$algo=self::MCRYPT_ALGO,$mode=self::MCRYPT_MODE) + static private function init_crypt($kp3) { if(!$GLOBALS['egw_info']['server']['mcrypt_enabled']) { @@ -422,9 +418,9 @@ class Session error_log(__METHOD__."() required PHP extension mcrypt not loaded and can not be loaded, sessions get NOT encrypted!"); return false; } - if (!(self::$mcrypt = mcrypt_module_open($algo, '', $mode, ''))) + if (!(self::$mcrypt = mcrypt_module_open(MCRYPT_TRIPLEDES, '', MCRYPT_MODE_ECB, ''))) { - error_log(__METHOD__."() could not mcrypt_module_open(algo='$algo','',mode='$mode',''), sessions get NOT encrypted!"); + error_log(__METHOD__."() could not mcrypt_module_open(MCRYPT_TRIPLEDES,'',MCRYPT_MODE_ECB,''), sessions get NOT encrypted!"); return false; } $iv_size = mcrypt_enc_get_iv_size(self::$mcrypt); @@ -927,10 +923,19 @@ class Session return false; } - session_name(self::EGW_SESSION_NAME); - session_id($this->sessionid); - self::cache_control(); - session_start(); + switch (session_status()) + { + case PHP_SESSION_DISABLED: + throw new ErrorException('EGroupware requires the PHP session extension!'); + case PHP_SESSION_NONE: + session_name(self::EGW_SESSION_NAME); + session_id($this->sessionid); + self::cache_control(); + session_start(); + break; + case PHP_SESSION_ACTIVE: + // session already started eg. by managementserver_client + } // check if we have a eGroupware session --> return false if not (but dont destroy it!) if (is_null($_SESSION) || !isset($_SESSION[self::EGW_SESSION_VAR])) @@ -1586,16 +1591,22 @@ class Session */ public static function init_handler() { - ini_set('session.use_cookies',0); // disable the automatic use of cookies, as it uses the path / by default - session_name(self::EGW_SESSION_NAME); - if (($sessionid = self::get_sessionid())) + switch(session_status()) { - session_id($sessionid); - self::cache_control(); - $ok = session_start(); - self::decrypt(); - if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."() sessionid=$sessionid, _SESSION[".self::EGW_SESSION_VAR.']='.array2string($_SESSION[self::EGW_SESSION_VAR])); - return $ok; + case PHP_SESSION_DISABLED: + throw new \ErrorException('EGroupware requires PHP session extension!'); + case PHP_SESSION_NONE: + ini_set('session.use_cookies',0); // disable the automatic use of cookies, as it uses the path / by default + session_name(self::EGW_SESSION_NAME); + if (($sessionid = self::get_sessionid())) + { + session_id($sessionid); + self::cache_control(); + $ok = session_start(); + self::decrypt(); + if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."() sessionid=$sessionid, _SESSION[".self::EGW_SESSION_VAR.']='.array2string($_SESSION[self::EGW_SESSION_VAR])); + return $ok; + } } if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."() no active session!"); diff --git a/api/src/Storage/Merge.php b/api/src/Storage/Merge.php index dca3d49ec0..b0a95ce145 100644 --- a/api/src/Storage/Merge.php +++ b/api/src/Storage/Merge.php @@ -996,7 +996,7 @@ abstract class Merge // Tags we can replace with the target document's version $replace_tags = array(); // only keep tags, if we have xsl extension available - if (class_exists(XSLTProcessor) && class_exists(DOMDocument) && $this->parse_html_styles) + if (class_exists('XSLTProcessor') && class_exists('DOMDocument') && $this->parse_html_styles) { switch($mimetype.$mso_application_progid) { diff --git a/api/src/Vfs/Links/StreamWrapper.php b/api/src/Vfs/Links/StreamWrapper.php index a1f59456b1..5dd2ef8746 100644 --- a/api/src/Vfs/Links/StreamWrapper.php +++ b/api/src/Vfs/Links/StreamWrapper.php @@ -180,7 +180,7 @@ class StreamWrapper extends LinksParent ); } // if entry directory does not exist --> return fake directory - elseif (!($ret = parent::url_stat($url,$flags,$eacl_check)) && $eacl_check) + elseif (!($ret = parent::url_stat($url,$flags)) && $eacl_check) { list(,/*$apps*/,/*$app*/,$id,$rel_path) = explode('/', Vfs::parse_url($url, PHP_URL_PATH), 5); if ($id && !isset($rel_path)) diff --git a/api/src/Vfs/Sqlfs/StreamWrapper.php b/api/src/Vfs/Sqlfs/StreamWrapper.php index 6eb6f73e6f..b0e083b91e 100644 --- a/api/src/Vfs/Sqlfs/StreamWrapper.php +++ b/api/src/Vfs/Sqlfs/StreamWrapper.php @@ -157,6 +157,11 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface */ static public $extra_columns = ',fs_link'; + /** + * @var array $overwrite_new =null if set create new file with values overwriten by the given ones + */ + protected $overwrite_new; + /** * Clears our stat-cache * @@ -184,10 +189,9 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface * - STREAM_REPORT_ERRORS If this flag is set, you are responsible for raising errors using trigger_error() during opening of the stream. * If this flag is not set, you should not raise any errors. * @param string &$opened_path full path of the file/resource, if the open was successfull and STREAM_USE_PATH was set - * @param array $overwrite_new =null if set create new file with values overwriten by the given ones * @return boolean true if the ressource was opened successful, otherwise false */ - function stream_open ( $url, $mode, $options, &$opened_path, array $overwrite_new=null ) + function stream_open ($url, $mode, $options, &$opened_path) { if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)"); @@ -201,7 +205,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface parse_str(parse_url($url, PHP_URL_QUERY), $this->dir_url_params); - if (!is_null($overwrite_new) || !($stat = $this->url_stat($path,STREAM_URL_STAT_QUIET)) || $mode[0] == 'x') // file not found or file should NOT exist + if (!is_null($this->overwrite_new) || !($stat = $this->url_stat($path,STREAM_URL_STAT_QUIET)) || $mode[0] == 'x') // file not found or file should NOT exist { if (!$dir || $mode[0] == 'r' || // does $mode require the file to exist (r,r+) $mode[0] == 'x' && $stat || // or file should not exist, but does @@ -239,7 +243,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface 'fs_size' => 0, 'fs_active' => self::_pdo_boolean(true), ); - if ($overwrite_new) $values = array_merge($values,$overwrite_new); + if ($this->overwrite_new) $values = array_merge($values, $this->overwrite_new); if (!$stmt->execute($values) || !($this->opened_fs_id = self::$pdo->lastInsertId('egw_sqlfs_fs_id_seq'))) { $this->opened_stream = $this->opened_path = $this->opened_mode = null; @@ -543,18 +547,15 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface * @param string $url * @return boolean TRUE on success or FALSE on failure */ - function unlink ( $url, $parent_stat=null ) + function unlink ( $url ) { if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); $path = Vfs::parse_url($url,PHP_URL_PATH); // need to get parent stat from Sqlfs, not Vfs - if (!isset($parent_stat)) - { - $parent_stat = !($dir = Vfs::dirname($path)) ? false : - $this->url_stat($dir, STREAM_URL_STAT_LINK); - } + $parent_stat = !($dir = Vfs::dirname($path)) ? false : + $this->url_stat($dir, STREAM_URL_STAT_LINK); if (!$parent_stat || !($stat = $this->url_stat($path,STREAM_URL_STAT_LINK)) || !$dir || !Vfs::check_access($dir, Vfs::WRITABLE, $parent_stat)) @@ -1123,10 +1124,9 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface * - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set, * you are responsible for reporting errors using the trigger_error() function during stating of the path. * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! - * @param boolean $eacl_access =null allows extending classes to pass the value of their check_extended_acl() method (no lsb!) * @return array */ - function url_stat ( $url, $flags, $eacl_access=null ) + function url_stat ( $url, $flags ) { static $max_subquery_depth=null; if (is_null($max_subquery_depth)) @@ -1134,7 +1134,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface $max_subquery_depth = $GLOBALS['egw_info']['server']['max_subquery_depth']; if (!$max_subquery_depth) $max_subquery_depth = 7; // setting current default of 7, if nothing set } - if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags,$eacl_access)"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags)"); $path = Vfs::parse_url($url,PHP_URL_PATH); @@ -1148,7 +1148,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface return false; // is invalid and gives sql error } // check if we already have the info from the last dir_open call, as the old vfs reads it anyway from the db - if (self::$stat_cache && isset(self::$stat_cache[$path]) && (is_null($eacl_access) || self::$stat_cache[$path] !== false)) + if (self::$stat_cache && isset(self::$stat_cache[$path]) && self::$stat_cache[$path] !== false) { return self::$stat_cache[$path] ? self::_vfsinfo2stat(self::$stat_cache[$path]) : false; } @@ -1162,11 +1162,9 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface ' AND fs_name'.self::$case_sensitive_equal.'? AND fs_dir='; $parts = explode('/',$path); - // if we have extendes acl access to the url, we dont need and can NOT include the sql for the readable check - if (is_null($eacl_access)) - { - $eacl_access = self::check_extended_acl($path,Vfs::READABLE); // should be static::check_extended_acl, but no lsb! - } + // if we have extended acl access to the url, we dont need and can NOT include the sql for the readable check + // using $this->check_extended_acl instead of self::check_extended_acl to ensure we call Vfs\Links\StreamWraper::check_extended_stat() + $eacl_access = self::check_extended_acl($path,Vfs::READABLE); // should be static::check_extended_acl, but no lsb! try { foreach($parts as $n => $name) @@ -1211,7 +1209,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface $query = str_replace('fs_name'.self::$case_sensitive_equal.'?','fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name),$base_query).'('.$query.')'; } } - if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__."($url,$flags,$eacl_access)".' */ '.$query; + if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__."($url,$flags) eacl_access=$eacl_access".' */ '.$query; //if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query; if (!($result = self::$pdo->query($query)) || !($info = $result->fetch(\PDO::FETCH_ASSOC))) @@ -1235,7 +1233,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface error_log(__METHOD__."() decremented max_subquery_depth to $max_subquery_depth"); Api\Config::save_value('max_subquery_depth', $max_subquery_depth, 'phpgwapi'); if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) $GLOBALS['egw']->invalidate_session_cache(); - return $this->url_stat($url, $flags, $eacl_access); + return $this->url_stat($url, $flags); } self::$stat_cache[$path] = $info; diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 7f13b47628..198c485a0c 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -582,8 +582,8 @@ class calendar_bo } } - if (!isset($params['users']) || !$params['users'] || - count($params['users']) == 1 && isset($params['users'][0]) && !$params['users'][0]) // null or '' casted to an array + if (empty($params['users']) || + is_array($params['users']) && count($params['users']) == 1 && empty($params['users'][0])) // null or '' casted to an array { // for a search use all account you have read grants from $params['users'] = $params['query'] ? array_keys($this->grants) : $this->user; diff --git a/calendar/inc/class.calendar_export_ical.inc.php b/calendar/inc/class.calendar_export_ical.inc.php index 9ef2f32a39..f1e5a4e052 100644 --- a/calendar/inc/class.calendar_export_ical.inc.php +++ b/calendar/inc/class.calendar_export_ical.inc.php @@ -127,7 +127,8 @@ class calendar_export_ical extends calendar_export_csv { * return html for options. * */ - public function get_options_etpl() { + public function get_options_etpl($definition = null) { + unset($definition); // not used, but required by function signature } /** diff --git a/calendar/inc/class.calendar_rrule.inc.php b/calendar/inc/class.calendar_rrule.inc.php index 6ee2fd2455..ce3d313fd0 100644 --- a/calendar/inc/class.calendar_rrule.inc.php +++ b/calendar/inc/class.calendar_rrule.inc.php @@ -574,7 +574,7 @@ class calendar_rrule implements Iterator if($this->type != self::NONE) { $str = lang(self::$types[$this->type]); - + $str_extra = array(); switch ($this->type) { @@ -756,7 +756,7 @@ class calendar_rrule implements Iterator self::rrule2tz($event, $time, $to_tz); $time->setTimezone(self::$tz_cache[$to_tz]); - + if ($event['recur_enddate']) { $enddate = is_a($event['recur_enddate'],'DateTime') ? clone $event['recur_enddate'] : new Api\DateTime($event['recur_enddate'],$timestamp_tz); @@ -863,7 +863,7 @@ class calendar_rrule implements Iterator $enddate = new DateTime($date); } } - + return new calendar_rrule($time,$type_id,$interval,$enddate,$weekdays,$exceptions); } /** @@ -897,7 +897,7 @@ class calendar_rrule implements Iterator if (!is_array($event) || !isset($event['recur_type']) || $event['recur_type'] == self::NONE || - empty($event['recur_data']) || $event['recur_data'] == ALLDAYS || + empty($event['recur_data']) || $event['recur_data'] == self::ALLDAYS || empty($event['tzid']) || empty($to_tz) || $event['tzid'] == $to_tz) return; diff --git a/importexport/inc/class.importexport_helper_functions.inc.php b/importexport/inc/class.importexport_helper_functions.inc.php index fcb59f58a6..f0ade1de28 100755 --- a/importexport/inc/class.importexport_helper_functions.inc.php +++ b/importexport/inc/class.importexport_helper_functions.inc.php @@ -571,7 +571,7 @@ class importexport_helper_functions { foreach($definitions as $appname => $_types) { $definitions[$appname] = array_intersect_key($definitions[$appname], array_flip($types)); } - return count($definitions[$appname]) > 0; + return !empty($definitions[$appname]); } // Api\Cache needs this public diff --git a/infolog/inc/class.infolog_ui.inc.php b/infolog/inc/class.infolog_ui.inc.php index 99ff4badbd..d240b25ea5 100644 --- a/infolog/inc/class.infolog_ui.inc.php +++ b/infolog/inc/class.infolog_ui.inc.php @@ -470,13 +470,13 @@ class infolog_ui if ($query['action_id'] && $query['csv_export'] !== 'children') { $parents = $query['action'] == 'sp' && $query['action_id'] ? (array)$query['action_id'] : array(); - if (count($parents) == 1 && is_array($query['action_id'])) + if (!empty($parents) && count($parents) == 1 && is_array($query['action_id'])) { $query['action_id'] = array_shift($query['action_id']); // display single parent as app_header } } - $parent_first = count($parents) == 1; + $parent_first = !empty($parents) && count($parents) == 1; $parent_index = 0; // et2 nextmatch listens to total, and only displays that many rows, so add parent in or we'll lose the last row if($parent_first || $query['action'] == 'sp' && is_array($query['action_id'])) $query['total']++; @@ -759,7 +759,7 @@ class infolog_ui } if (is_array($values) && !empty($values['nm']['multi_action'])) { - if (!count($values['nm']['selected']) && !$values['nm']['select_all']) + if (empty($values['nm']['selected']) && !$values['nm']['select_all']) { $msg = lang('You need to select some entries first'); } diff --git a/setup/check_install.php b/setup/check_install.php index ee39bd2854..2e35f6e643 100644 --- a/setup/check_install.php +++ b/setup/check_install.php @@ -226,6 +226,10 @@ $checks = array( 'func' => 'extension_check', 'warning' => lang('The tidy extension is need in merge-print to clean up html before inserting it in office documents.'), ), + 'xsl' => array( + 'func' => 'extension_check', + 'warning' => lang('The xsl extension is need in merge-print for processing html and office documents.'), + ), 'xmlreader' => array( 'func' => 'extension_check', 'error' => lang('The xmlreader extension is required by EGroupware in several applications.'), diff --git a/setup/inc/class.setup.inc.php b/setup/inc/class.setup.inc.php index 074853e0e2..01d8353e1a 100644 --- a/setup/inc/class.setup.inc.php +++ b/setup/inc/class.setup.inc.php @@ -249,15 +249,24 @@ class setup // make sure we have session extension available, otherwise fail with exception that we need it check_load_extension('session', true); - ini_set('session.use_cookie', true); - session_name(self::SESSIONID); - session_set_cookie_params(0, '/', self::cookiedomain(), - // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) - !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', true); + switch(session_status()) + { + case PHP_SESSION_DISABLED: + throw new \ErrorException('EGroupware requires PHP session extension!'); + case PHP_SESSION_NONE: + ini_set('session.use_cookie', true); + session_name(self::SESSIONID); + session_set_cookie_params(0, '/', self::cookiedomain(), + // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) + !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', true); - if (isset($_COOKIE[self::SESSIONID])) session_id($_COOKIE[self::SESSIONID]); + if (isset($_COOKIE[self::SESSIONID])) session_id($_COOKIE[self::SESSIONID]); - $ok = @session_start(); // suppress notice if session already started or warning in CLI + $ok = @session_start(); // suppress notice if session already started or warning in CLI + break; + case PHP_SESSION_ACTIVE: + $ok = true; + } // need to decrypt session, in case session encryption is switched on in header.inc.php Api\Session::decrypt(); //error_log(__METHOD__."() returning ".array2string($ok).' _SESSION='.array2string($_SESSION));