Author: ralfbecker
New Revision: 55369
URL: http://svn.stylite.de/viewvc/egroupware?rev=55369&view=rev
Log:
move egw_framework::csp__source_attrs($attrs) to Api\Header\ContentSecurityPolicy::add(-src, $attrs) and egw_ckeditor_config to Api\Html\CkEditorConfig
Added:
trunk/egroupware/api/src/Header/ContentSecurityPolicy.php
- copied, changed from r55357, trunk/phpgwapi/inc/class.egw_framework.inc.php
trunk/egroupware/api/src/Html/CkEditorConfig.php
- copied, changed from r55357, trunk/phpgwapi/inc/class.egw_ckeditor_config.inc.php
Modified:
trunk/egroupware/api/src/Header/UserAgent.php
trunk/egroupware/api/src/Html.php
trunk/phpgwapi/inc/class.egw_ckeditor_config.inc.php
trunk/phpgwapi/inc/class.egw_framework.inc.php
— trunk/phpgwapi/inc/class.egw_framework.inc.php (original)
+++ trunk/egroupware/api/src/Header/ContentSecurityPolicy.php Sun Mar 13 15:08:31 2016
@@ -1,239 +1,117 @@
<?php
/**
- * EGroupware API - framework baseclass
+ * EGroupware API - Content Security Policy headers
*
* @link http://www.egroupware.org
- * @author Ralf Becker rewrite in 12/2006
- * @author Pim Snel author of the idots template set
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @author Ralf Becker
* @package api
- * @subpackage framework
+ * @subpackage header
* @access public
* @version $Id$
*/
+namespace EGroupware\Api\Header;
+
/**
- * eGW API - framework: virtual base class for all template sets
- *
- * This class creates / renders the eGW framework:
- * a) html header
- * b) navbar
- * c) sidebox menu
- * d) main application area
- * e) footer
- * It replaces several methods in the common class and the diverse templates.
- *
- * Existing apps either set $GLOBALS['egw_info']['flags']['noheader'] and call common::egw_header() and
- * (if $GLOBALS['egw_info']['flags']['nonavbar'] is true) parse_navbar() or it's done by the header.inc.php include.
- * The app's hook_sidebox then calls the public function display_sidebox().
- * And the app calls common::egw_footer().
- *
- * This are the authors (and their copyrights) of the original egw_header, egw_footer methods of the common class:
- * This file written by Dan Kuykendall
- * and Joseph Engo
- * and Mark Peters
- * and Lars Kneschke
- * Copyright (C) 2000, 2001 Dan Kuykendall
- * Copyright (C) 2003 Lars Kneschke
+ * Content Security Policy headers
*/
-abstract class egw_framework
+class ContentSecurityPolicy
{
/**
- * Name of the template set, eg. 'idots'
+ * Additional attributes or urls for CSP beside always added "self"
*
- * @var string
- */
- var $template;
-
- /**
- * Path relative to EGW_SERVER_ROOT for the template directory
- *
- * @var string
- */
- var $template_dir;
-
- /**
- * Application specific template directories to try in given order for CSS
- *
- * @var string
- */
- var $template_dirs = array();
-
- /**
- * true if $this->header() was called
- *
- * @var boolean
- */
- static $header_done = false;
- /**
- * true if $this->navbar() was called
- *
- * @var boolean
- */
- static $navbar_done = false;
-
- /**
- * Constructor
- *
- * The constructor instanciates the class in $GLOBALS['egw']->framework, from where it should be used
- *
- * @return egw_framework
- */
- function __construct($template)
- {
- $this->template = $template;
-
- if (!isset($GLOBALS['egw']->framework))
- {
- $GLOBALS['egw']->framework = $this;
- }
- $this->template_dir = '/phpgwapi/templates/'.$template;
-
- $this->template_dirs[] = $template;
- $this->template_dirs[] = 'default';
- }
-
- /**
- * Factory method to instanciate framework object
- *
- * @return egw_framwork
- */
- public static function factory()
- {
- if ((html::$ua_mobile || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile') &&
- file_exists(EGW_SERVER_ROOT.'/pixelegg'))
- {
- $GLOBALS['egw_info']['server']['template_set'] = 'pixelegg';
- }
- // default to idots, if no template_set set, to eg. not stall installations if settings use egw::link
- if (empty($GLOBALS['egw_info']['server']['template_set'])) $GLOBALS['egw_info']['server']['template_set'] = 'idots';
- // setup the new eGW framework (template sets)
- $class = $GLOBALS['egw_info']['server']['template_set'].'_framework';
- if (!class_exists($class)) // first try to autoload the class
- {
- require_once($file=EGW_INCLUDE_ROOT.'/phpgwapi/templates/'.$GLOBALS['egw_info']['server']['template_set'].'/class.'.$class.'.inc.php');
- if (!in_array($file,(array)$_SESSION['egw_required_files']))
- {
- $_SESSION['egw_required_files'][] = $file; // automatic load the used framework class, when the object get's restored
- }
- }
- // fall back to idots if a template does NOT support current user-agent
- if ($class != 'idots_framework' && method_exists($class,'is_supported_user_agent') &&
- !call_user_func(array($class,'is_supported_user_agent')))
- {
- $GLOBALS['egw_info']['server']['template_set'] = 'idots';
- return self::factory();
- }
- return new $class($GLOBALS['egw_info']['server']['template_set']);
- }
-
- /**
- * Additional attributes or urls for CSP script-src 'self'
- *
- * 'unsafe-eval' is currently allways added, as it is used in a couple of places.
+ * - "script-src 'self' 'unsafe-eval'" allows only self and eval (eg. ckeditor), but forbids inline scripts, onchange, etc
+ * - "connect-src 'self'" allows ajax requests only to self
+ * - "style-src 'self' 'unsafe-inline'" allows only self and inline style, which we need
+ * - "frame-src 'self' manual.egroupware.org" allows frame and iframe content only for self or manual.egroupware.org
*
* @var array
*/
- private static $csp_script_src_attrs = array("'unsafe-eval'");
+ private static $sources = array(
+ 'script-src' => array("'unsafe-eval'"),
+ 'style-src' => array("'unsafe-inline'"),
+ 'connect-src' => array(),
+ 'frame-src' => null, // NOT array(), to allow setting no default frame-src!
+ );
/**
- * Set/get Content-Security-Policy attributes for script-src: 'unsafe-eval' and/or 'unsafe-inline'
+ * Add Content-Security-Policy sources
*
- * Using CK-Editor currently requires both to be set :(
+ * Calling this method with an empty array for frame-src, sets no defaults but "'self'"!
*
- * Old pre-et2 apps might need to call egw_framework::csp_script_src_attrs(array('unsafe-eval','unsafe-inline'))
- *
- * EGroupware itself currently still requires 'unsafe-eval'!
- *
- * @param string|array $set =array() 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
- * @return string with attributes eg. "'unsafe-eval' 'unsafe-inline'"
+ * @param string|array $set =array() URL (incl. protocol!)
+ * @param string $source valid CSP source types like 'script-src', 'style-src', 'connect-src', 'frame-src', ...
+ * @param string|array $attrs 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
*/
- public static function csp_script_src_attrs($set=null)
+ public static function add($source, $attrs)
{
- foreach((array)$set as $attr)
+ if (!isset(self::$sources[$source]))
+ {
+ // set frame-src attrs of API and apps via hook
+ if ($source == 'frame-src' && !isset($attrs))
+ {
+ $attrs = array('manual.egroupware.org', 'www.egroupware.org');
+ if (($app_additional = $GLOBALS['egw']->hooks->process('csp-frame-src')))
+ {
+ foreach($app_additional as $addtional)
+ {
+ if ($addtional) $attrs = array_unique(array_merge($attrs, $addtional));
+ }
+ }
+ }
+ self::$sources[$source] = array();
+ }
+ foreach((array)$attrs as $attr)
{
if (in_array($attr, array('none', 'self', 'unsafe-eval', 'unsafe-inline')))
{
$attr = "'$attr'"; // automatic add quotes
}
- if (!in_array($attr, self::$csp_script_src_attrs))
+ if (!in_array($attr, self::$sources[$source]))
{
- self::$csp_script_src_attrs[] = $attr;
+ self::$sources[$source][] = $attr;
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
}
}
- //error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_script_src_attrs)).' '.function_backtrace());
- return implode(' ', self::$csp_script_src_attrs);
}
/**
- * Additional attributes or urls for CSP style-src 'self'
+ * Set Content-Security-Policy attributes for script-src: 'unsafe-eval' and/or 'unsafe-inline'
*
- * 'unsafe-inline' is currently allways added, as it is used in a couple of places.
+ * Using CK-Editor currently requires both to be set :(
*
- * @var array
+ * Old pre-et2 apps might need to call Api\Headers::script_src_attrs(array('unsafe-eval','unsafe-inline'))
+ *
+ * EGroupware itself currently still requires 'unsafe-eval'!
+ *
+ * @param string|array $set =array() 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
*/
- private static $csp_style_src_attrs = array("'unsafe-inline'");
+ public static function add_script_src($set=null)
+ {
+ self::add('script-src', $set);
+ }
/**
- * Set/get Content-Security-Policy attributes for style-src: 'unsafe-inline'
+ * Set Content-Security-Policy attributes for style-src: 'unsafe-inline'
*
* EGroupware itself currently still requires 'unsafe-inline'!
*
* @param string|array $set =array() 'unsafe-inline' (without quotes!) and/or URL (incl. protocol!)
- * @return string with attributes eg. "'unsafe-inline'"
*/
- public static function csp_style_src_attrs($set=null)
+ public static function add_style_src($set=null)
{
- foreach((array)$set as $attr)
- {
- if (in_array($attr, array('none', 'self', 'unsafe-inline')))
- {
- $attr = "'$attr'"; // automatic add quotes
- }
- if (!in_array($attr, self::$csp_style_src_attrs))
- {
- self::$csp_style_src_attrs[] = $attr;
- //error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
- }
- }
- //error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_script_src_attrs)).' '.function_backtrace());
- return implode(' ', self::$csp_style_src_attrs);
+ self::add('style-src', $set);
}
/**
- * Additional attributes or urls for CSP connect-src 'self'
- *
- * @var array
- */
- private static $csp_connect_src_attrs = array();
-
- /**
- * Set/get Content-Security-Policy attributes for connect-src:
+ * Set Content-Security-Policy attributes for connect-src:
*
* @param string|array $set =array() URL (incl. protocol!)
- * @return string with attributes eg. "'unsafe-inline'"
*/
- public static function csp_connect_src_attrs($set=null)
+ public static function add_connect_src($set=null)
{
- foreach((array)$set as $attr)
- {
- if (!in_array($attr, self::$csp_connect_src_attrs))
- {
- self::$csp_connect_src_attrs[] = $attr;
- //error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
- }
- }
- //error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_connect_src_attrs)).' '.function_backtrace());
- return implode(' ', self::$csp_connect_src_attrs);
+ self::add('connect-src', $set);
}
-
- /**
- * Additional attributes or urls for CSP frame-src 'self'
- *
- * @var array
- */
- private static $csp_frame_src_attrs;
/**
* Set/get Content-Security-Policy attributes for frame-src:
@@ -243,2317 +121,44 @@
* @param string|array $set =array() URL (incl. protocol!)
* @return string with attributes eg. "'unsafe-inline'"
*/
- public static function csp_frame_src_attrs($set=null)
+ public static function add_frame_src($set=null)
{
- // set frame-src attrs of API and apps via hook
- if (!isset(self::$csp_frame_src_attrs) && !isset($set))
- {
- $frame_src = array('manual.egroupware.org', 'www.egroupware.org');
- if (($app_additional = $GLOBALS['egw']->hooks->process('csp-frame-src')))
- {
- foreach($app_additional as $addtional)
- {
- if ($addtional) $frame_src = array_unique(array_merge($frame_src, $addtional));
- }
- }
- return self::csp_frame_src_attrs($frame_src);
- }
-
- if (!isset(self::$csp_frame_src_attrs)) self::$csp_frame_src_attrs = array();
-
- foreach((array)$set as $attr)
- {
- if (!in_array($attr, self::$csp_frame_src_attrs))
- {
- self::$csp_frame_src_attrs[] = $attr;
- //error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
- }
- }
- //error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_frame_src_attrs)).' '.function_backtrace());
- return implode(' ', self::$csp_frame_src_attrs);
+ self::add('frame-src', $set);
}
/**
- * Send HTTP headers: Content-Type and Content-Security-Policy
+ * Send Content-Security-Policy header
+ *
+ * @link http://content-security-policy.com/
*/
- public function send_headers()
+ public static function send()
{
- // add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv)
- header('Content-type: text/html; charset='.translation::charset());
+ self::add('frame-src', null); // set defaults for frame-src
- // content-security-policy header:
- // - "script-src 'self' 'unsafe-eval'" allows only self and eval (eg. ckeditor), but forbids inline scripts, onchange, etc
- // - "connect-src 'self'" allows ajax requests only to self
- // - "style-src 'self' 'unsave-inline'" allows only self and inline style, which we need
- // - "frame-src 'self' manual.egroupware.org" allows frame and iframe content only for self or manual.egroupware.org
- $csp = "script-src 'self' ".self::csp_script_src_attrs().
- "; connect-src 'self' ".self::csp_connect_src_attrs().
- "; style-src 'self' ".self::csp_style_src_attrs().
- "; frame-src 'self' ".self::csp_frame_src_attrs();
+ $policies = array();
+ foreach(self::$sources as $source => $urls)
+ {
+ $policies[] = "$source 'self' ".implode(' ', $urls);
+ }
+ $csp = implode('; ', $policies);
//$csp = "default-src * 'unsafe-eval' 'unsafe-inline'"; // allow everything
- header("Content-Security-Policy: $csp");
- header("X-Webkit-CSP: $csp"); // Chrome: <= 24, Safari incl. iOS
- header("X-Content-Security-Policy: $csp"); // FF <= 22
- // allow client-side to detect first load aka just logged in
- $reload_count =& egw_cache::getSession(__CLASS__, 'framework-reload');
- self::$extra['framework-reload'] = (int)(bool)$reload_count++;
- }
+ $user_agent = UserAgent::type();
+ $version = UserAgent::version();
- /**
- * Constructor for static variables
- */
- public static function init_static()
- {
- self::$js_include_mgr = new egw_include_mgr(array(
- // We need LABjs, but putting it through egw_include_mgr causes it to re-load itself
- //'/phpgwapi/js/labjs/LAB.src.js',
-
- // allways load jquery (not -ui) and egw_json first
- '/phpgwapi/js/jquery/jquery.js',
- // always include javascript helper functions
- '/phpgwapi/js/jsapi/jsapi.js',
- '/phpgwapi/js/jsapi/egw.js',
- ));
- }
-
- /**
- * PHP4-Constructor
- *
- * The constructor instanciates the class in $GLOBALS['egw']->framework, from where it should be used
- *
- * @deprecated use __construct()
- */
- function egw_framework($template)
- {
- self::__construct($template);
- }
-
- /**
- * Link url generator
- *
- * @param string $url The url the link is for
- * @param string|array $extravars Extra params to be passed to the url
- * @param string $link_app =null if appname or true, some templates generate a special link-handler url
- * @return string The full url after processing
- */
- static function link($url, $extravars = '', $link_app=null)
- {
- unset($link_app); // not used by required by function signature
- return $GLOBALS['egw']->session->link($url, $extravars);
- }
-
- /**
- * Redirects direct to a generated link
- *
- * @param string $url The url the link is for
- * @param string|array $extravars Extra params to be passed to the url
- * @param string $link_app =null if appname or true, some templates generate a special link-handler url
- * @return string The full url after processing
- */
- static function redirect_link($url, $extravars='', $link_app=null)
- {
- egw::redirect(self::link($url, $extravars), $link_app);
- }
-
- /**
- * Renders an applicaton page with the complete eGW framework (header, navigation and menu)
- *
- * This is the (new) prefered way to render a page in eGW!
- *
- * @param string $content html of the main application area
- * @param string $app_header =null application header, default what's set in $GLOBALS['egw_info']['flags']['app_header']
- * @param string $navbar =null show the navigation, default !$GLOBALS['egw_info']['flags']['nonavbar'], false gives a typical popu
- *
- */
- function render($content,$app_header=null,$navbar=null)
- {
- if (!is_null($app_header)) $GLOBALS['egw_info']['flags']['app_header'] = $app_header;
- if (!is_null($navbar)) $GLOBALS['egw_info']['flags']['nonavbar'] = !$navbar;
-
- echo $this->header();
-
- if (!isset($GLOBALS['egw_info']['flags']['nonavbar']) || !$GLOBALS['egw_info']['flags']['nonavbar'])
+ // recommendaton ist to not send regular AND deprecated headers together, as they can cause unexpected behavior
+ if ($user_agent == 'chrome' && $version < 25 || $user_agent == 'safari' && $version < 7)
{
- echo $this->navbar();
+ header("X-Webkit-CSP: $csp"); // Chrome: <= 24, Safari incl. iOS
}
- echo $content;
-
- echo $this->footer();
- }
-
- /**
- * Extra values send as data attributes to script tag of egw.js
- *
- * @var array
- */
- protected static $extra = array();
-
- /**
- * Refresh given application $targetapp display of entry $app $id, incl. outputting $msg
- *
- * Calling egw_refresh and egw_message on opener in a content security save way
- *
- * To provide more information about necessary refresh an automatic 9th parameter is added
- * containing an object with application-name as attributes containing an array of linked ids
- * (adding happens in get_extras to give apps time to link new entries!).
- *
- * @param string $msg message (already translated) to show, eg. 'Entry deleted'
- * @param string $app application name
- * @param string|int $id =null id of entry to refresh
- * @param string $type =null either 'update', 'edit', 'delete', 'add' or null
- * - update: request just modified data from given rows.
- * Sorting and filtering are not considered, so if the sort field is changed,
- * the row will not be moved. If the current filtering could include or exclude
- * the record, use edit.
- * - edit: rows changed, but sorting or filtering may be affected. Requires full reload.
- * - delete: just delete the given rows clientside (no server interaction neccessary)
- * - add: requires full reload for proper sorting
- * - null: full reload
- * @param string $targetapp =null which app's window should be refreshed, default current
- * @param string|RegExp $replace =null regular expression to replace in url
- * @param string $with =null
- * @param string $msg_type =null 'error', 'warning' or 'success' (default)
- */
- public static function refresh_opener($msg, $app, $id=null, $type=null, $targetapp=null, $replace=null, $with=null, $msg_type=null)
- {
- unset($msg, $app, $id, $type, $targetapp, $replace, $with, $msg_type); // used only via func_get_args();
- //error_log(__METHOD__.'('.array2string(func_get_args()).')');
- self::$extra['refresh-opener'] = func_get_args();
- }
-
- /**
- * Display an error or regular message
- *
- * Calls egw_message on client-side in a content security save way
- *
- * @param string $msg message to show
- * @param string $type ='success' 'error', 'warning' or 'success' (default)
- */
- public static function message($msg, $type='success')
- {
- unset($msg, $type); // used only via func_get_args();
- self::$extra['message'] = func_get_args();
- }
-
- /**
- * Open a popup independent if we run as json or regular request
- *
- * @param string $link
- * @param string $target
- * @param string $popup
- */
- public static function popup($link, $target='_blank', $popup='640x480')
- {
- unset($link, $target, $popup); // used only via func_get_args()
- // default params are not returned by func_get_args!
- $args = func_get_args()+array(null, '_blank', '640x480');
-
- if (egw_json_request::isJSONRequest())
+ elseif ($user_agent == 'firefox' && $version < 23 || $user_agent == 'msie') // Edge is reported as 'edge'!
{
- egw_json_response::get()->apply('egw.open_link', $args);
+ header("X-Content-Security-Policy: $csp");
}
else
{
- self::$extra['popup'] = $args;
+ header("Content-Security-Policy: $csp");
}
}
-
- /**
- * Close (popup) window, use to replace egw_framework::onload('window.close()') in a content security save way
- *
- * @param string $alert_msg ='' optional message to display as alert, before closing the window
- */
- public static function window_close($alert_msg='')
- {
- //error_log(__METHOD__."()");
- self::$extra['window-close'] = $alert_msg ? $alert_msg : true;
-
- // are we in ajax_process_content -> just return extra data, with close instructions
- if (preg_match('/etemplate(_new)?(::|\.)ajax_process_content/', $_GET['menuaction']))
- {
- $response = egw_json_response::get();
- $response->generic('et2_load', egw_framework::get_extra());
- }
- else
- {
- $GLOBALS['egw']->framework->render('', false, false);
- }
- common::egw_exit();
- }
-
- /**
- * Close (popup) window, use to replace egw_framework::onload('window.close()') in a content security save way
- */
- public static function window_focus()
- {
- //error_log(__METHOD__."()");
- self::$extra['window-focus'] = true;
- }
-
- /**
- * Allow app to store arbitray values in egw script tag
- *
- * Attribute name will be "data-$app-$name" and value will be json serialized, if not scalar.
- *
- * @param string $app
- * @param string $name
- * @param mixed $value
- */
- public static function set_extra($app, $name, $value)
- {
- self::$extra[$app.'-'.$name] = $value;
- }
-
- /**
- * Clear all extra data
- */
- public static function clear_extra()
- {
- self::$extra = array();
- }
-
- /**
- * Allow eg. ajax to query content set via refresh_opener or window_close
- *
- * @return array content of egw_framework::$extra
- */
- public static function get_extra()
- {
- // adding links of refreshed entry, to give others apps more information about necessity to refresh
- if (isset(self::$extra['refresh-opener']) && count(self::$extra['refresh-opener']) <= 8 && // do not run twice
- !empty(self::$extra['refresh-opener'][1]) && !empty(self::$extra['refresh-opener'][2])) // app/id given
- {
- $links = egw_link::get_links(self::$extra['refresh-opener'][1], self::$extra['refresh-opener'][2]);
- $apps = array();
- foreach($links as $link)
- {
- $apps[$link['app']][] = $link['id'];
- }
- while (count(self::$extra['refresh-opener']) < 8)
- {
- self::$extra['refresh-opener'][] = null;
- }
- self::$extra['refresh-opener'][] = $apps;
- }
- return self::$extra;
- }
-
- /**
- * Returns the html-header incl. the opening body tag
- *
- * @return string with html
- */
- abstract function header(array $extra=array());
-
- /**
- * Returns the html from the body-tag til the main application area (incl. opening div tag)
- *
- * If header has NOT been called, also return header content!
- * No need to manually call header, this allows to postpone header so navbar / sidebox can include JS or CSS.
- *
- * @return string with html
- */
- abstract function navbar();
-
- /**
- * Return true if we are rendering the top-level EGroupware window
- *
- * A top-level EGroupware window has a navbar: eg. no popup and for a framed template (jdots) only frameset itself
- *
- * @return boolean $consider_navbar_not_yet_called_as_true=true
- * @return boolean
- */
- abstract function isTop($consider_navbar_not_yet_called_as_true=true);
-
- /**
- * Returns the content of one sidebox
- *
- * @param string $appname
- * @param string $menu_title
- * @param array $file
- * @param string $type =null 'admin', 'preferences', 'favorites', ...
- */
- abstract function sidebox($appname,$menu_title,$file,$type=null);
-
- /**
- * Returns the html from the closing div of the main application area to the closing html-tag
- *
- * @return string
- */
- abstract function footer();
-
- /**
- * Displays the login screen
- *
- * @param string $extra_vars for login url
- * @param string $change_passwd =null string with message to render input fields for password change
- */
- function login_screen($extra_vars, $change_passwd=null)
- {
- self::csp_frame_src_attrs(array()); // array() no external frame-sources
-
- //error_log(__METHOD__."() this->template=$this->template, this->template_dir=$this->template_dir, get_class(this)=".get_class($this));
- $tmpl = new Template(EGW_SERVER_ROOT.$this->template_dir);
-
- $tmpl->set_file(array('login_form' => html::$ua_mobile?'login_mobile.tpl':'login.tpl'));
-
- $tmpl->set_var('lang_message',$GLOBALS['loginscreenmessage']);
-
- // hide change-password fields, if not requested
- if (!$change_passwd)
- {
- $tmpl->set_block('login_form','change_password');
- $tmpl->set_var('change_password', '');
- $tmpl->set_var('lang_password',lang('password'));
- $tmpl->set_var('cd',check_logoutcode($_GET['cd']));
- $tmpl->set_var('cd_class', isset($_GET['cd']) && $_GET['cd'] != 1 ? 'error' : '');
- $last_loginid = $_COOKIE['last_loginid'];
- $last_domain = $_COOKIE['last_domain'];
- $tmpl->set_var('passwd', '');
- $tmpl->set_var('autofocus_login', 'autofocus');
- }
- else
- {
- $tmpl->set_var('lang_password',lang('Old password'));
- $tmpl->set_var('lang_new_password',lang('New password'));
- $tmpl->set_var('lang_repeat_password',lang('Repeat password'));
- $tmpl->set_var('cd', $change_passwd);
- $tmpl->set_var('cd_class', 'error');
- $last_loginid = $_POST['login'];
- $last_domain = $_POST['domain'];
- $tmpl->set_var('passwd', $_POST['passwd']);
- $tmpl->set_var('autofocus_login', '');
- $tmpl->set_var('autofocus_new_passwd', 'autofocus');
- }
- if($GLOBALS['egw_info']['server']['show_domain_selectbox'])
- {
- foreach(array_keys($GLOBALS['egw_domain']) as $domain)
- {
- $domains[$domain] = $domain;
- }
- $tmpl->set_var(array(
- 'lang_domain' => lang('domain'),
- 'select_domain' => html::select('logindomain',$last_domain,$domains,true,'tabindex="2"',0,false),
- ));
- }
- else
- {
- /* trick to make domain section disapear */
- $tmpl->set_block('login_form','domain_selection');
- $tmpl->set_var('domain_selection',$GLOBALS['egw_info']['user']['domain'] ?
- html::input_hidden('logindomain',$GLOBALS['egw_info']['user']['domain']) : '');
-
- if($last_loginid !== '')
- {
- reset($GLOBALS['egw_domain']);
- list($default_domain) = each($GLOBALS['egw_domain']);
-
- if(!empty ($last_domain) && $last_domain != $default_domain)
- {
- $last_loginid .= '@' . $last_domain;
- }
- }
- }
-
- $config_reg = config::read('registration');
-
- if($config_reg['enable_registration'])
- {
- if ($config_reg['register_link'])
- {
- $reg_link='
'.lang('Not a user yet? Register now').'';
- }
- if ($config_reg['lostpassword_link'])
- {
- $lostpw_link='
'.lang('Lost password').'';
- }
- if ($config_reg['lostid_link'])
- {
- $lostid_link='
'.lang('Lost Login Id').'';
- }
-
- /* if at least one option of "registration" is activated display the registration section */
- if($config_reg['register_link'] || $config_reg['lostpassword_link'] || $config_reg['lostid_link'] )
- {
- $tmpl->set_var(array(
- 'register_link' => $reg_link,
- 'lostpassword_link' => $lostpw_link,
- 'lostid_link' => $lostid_link,
- ));
- }
- else
- {
- /* trick to make registration section disapear */
- $tmpl->set_block('login_form','registration');
- $tmpl->set_var('registration','');
- }
- }
-
- $tmpl->set_var('login_url', $GLOBALS['egw_info']['server']['webserver_url'] . '/login.php' . $extra_vars);
- $tmpl->set_var('version', $GLOBALS['egw_info']['server']['versions']['phpgwapi']);
- $tmpl->set_var('login', $last_loginid);
-
- $tmpl->set_var('lang_username',lang('username'));
- $tmpl->set_var('lang_login',lang('login'));
-
- $tmpl->set_var('website_title', $GLOBALS['egw_info']['server']['site_title']);
- $tmpl->set_var('template_set',$this->template);
-
- if (substr($GLOBALS['egw_info']['server']['login_logo_file'], 0, 4) == 'http' ||
- $GLOBALS['egw_info']['server']['login_logo_file'][0] == '/')
- {
- $var['logo_file'] = $GLOBALS['egw_info']['server']['login_logo_file'];
- }
- else
- {
- $var['logo_file'] = common::image('phpgwapi',$GLOBALS['egw_info']['server']['login_logo_file']?$GLOBALS['egw_info']['server']['login_logo_file']:'logo', '', null); // null=explicit allow svg
- }
- $var['logo_url'] = $GLOBALS['egw_info']['server']['login_logo_url']?$GLOBALS['egw_info']['server']['login_logo_url']:'http://www.eGroupWare.org';
- if (substr($var['logo_url'],0,4) != 'http')
- {
- $var['logo_url'] = 'http://'.$var['logo_url'];
- }
- $var['logo_title'] = $GLOBALS['egw_info']['server']['login_logo_title']?$GLOBALS['egw_info']['server']['login_logo_title']:'www.eGroupWare.org';
- $tmpl->set_var($var);
-
- /* language section if activated in site config */
- if (@$GLOBALS['egw_info']['server']['login_show_language_selection'])
- {
- $tmpl->set_var(array(
- 'lang_language' => lang('Language'),
- 'select_language' => html::select('lang',$GLOBALS['egw_info']['user']['preferences']['common']['lang'],
- translation::get_installed_langs(),true,'tabindex="1"',0,false),
- ));
- }
- else
- {
- $tmpl->set_block('login_form','language_select');
- $tmpl->set_var('language_select','');
- }
-
- /********************************************************\
- * Check if authentification via cookies is allowed *
- * and place a time selectbox, how long cookie is valid *
- \********************************************************/
-
- if($GLOBALS['egw_info']['server']['allow_cookie_auth'])
- {
- $tmpl->set_block('login_form','remember_me_selection');
- $tmpl->set_var('lang_remember_me',lang('Remember me'));
- $tmpl->set_var('select_remember_me',html::select('remember_me', '', array(
- '' => lang('not'),
- '1hour' => lang('1 Hour'),
- '1day' => lang('1 Day'),
- '1week'=> lang('1 Week'),
- '1month' => lang('1 Month'),
- 'forever' => lang('Forever'),
- ),true,'tabindex="3"',0,false));
- }
- else
- {
- /* trick to make remember_me section disapear */
- $tmpl->set_block('login_form','remember_me_selection');
- $tmpl->set_var('remember_me_selection','');
- }
- $tmpl->set_var('autocomplete', ($GLOBALS['egw_info']['server']['autocomplete_login'] ? 'autocomplete="off"' : ''));
-
- // load jquery for login screen too
- self::validate_file('jquery', 'jquery');
-
- $this->render($tmpl->fp('loginout','login_form'),false,false);
- }
-
- /**
- * displays a login denied message
- */
- function denylogin_screen()
- {
- $tmpl = new Template(EGW_SERVER_ROOT.$this->template_dir);
-
- $tmpl->set_file(array(
- 'login_form' => 'login_denylogin.tpl'
- ));
-
- $tmpl->set_var(array(
- 'template_set' => 'default',
- 'deny_msg' => lang('Oops! You caught us in the middle of system maintainance.').
- '
'.lang('Please, check back with us shortly.'),
- ));
-
- // load jquery for deny-login screen too
- self::validate_file('jquery', 'jquery');
-
- $this->render($tmpl->fp('loginout','login_form'),false,false);
- }
-
- /**
- * Get footer as array to eg. set as vars for a template (from idots' head.inc.php)
- *
- * @return array
- */
- public function _get_footer()
- {
- $var = Array(
- 'img_root' => $GLOBALS['egw_info']['server']['webserver_url'] . $this->template_dir.'/images',
- 'version' => $GLOBALS['egw_info']['server']['versions']['phpgwapi']
- );
- $var['page_generation_time'] = '';
- if($GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time'])
- {
- $totaltime = sprintf('%4.2lf',microtime(true) - $GLOBALS['egw_info']['flags']['page_start_time']);
-
- $var['page_generation_time'] = '
'.lang('Page was generated in %1 seconds',$totaltime);
- if ($GLOBALS['egw_info']['flags']['session_restore_time'])
- {
- $var['page_generation_time'] .= ' '.lang('(session restored in %1 seconds)',
- sprintf('%4.2lf',$GLOBALS['egw_info']['flags']['session_restore_time']));
- }
- $var['page_generation_time'] .= '
';
- }
- $var['powered_by'] = '
'.
- lang('Powered by').' Stylite\'s EGroupware '.
- $GLOBALS['egw_info']['server']['versions']['phpgwapi'].'';
-
- return $var;
- }
-
- /**
- * Get the (depricated) application footer
- *
- * @return string html
- */
- protected static function _get_app_footer()
- {
- ob_start();
- // Include the apps footer files if it exists
- if (EGW_APP_INC != EGW_API_INC && // this prevents an endless inclusion on the homepage
- // (some apps set currentapp in hook_home => it's not releyable)
- (file_exists (EGW_APP_INC . '/footer.inc.php') || isset($_GET['menuaction'])) &&
- $GLOBALS['egw_info']['flags']['currentapp'] != 'home' &&
- $GLOBALS['egw_info']['flags']['currentapp'] != 'login' &&
- $GLOBALS['egw_info']['flags']['currentapp'] != 'logout' &&
- !@$GLOBALS['egw_info']['flags']['noappfooter'])
- {
- list(, $class) = explode('.',(string)$_GET['menuaction']);
- if ($class && is_object($GLOBALS[$class]) && is_array($GLOBALS[$class]->public_functions) &&
- isset($GLOBALS[$class]->public_functions['footer']))
- {
- $GLOBALS[$class]->footer();
- }
- elseif(file_exists(EGW_APP_INC.'/footer.inc.php'))
- {
- include(EGW_APP_INC . '/footer.inc.php');
- }
- }
- $content = ob_get_contents();
- ob_end_clean();
-
- return $content;
- }
-
- /**
- * Get header as array to eg. set as vars for a template (from idots' head.inc.php)
- *
- * @param array $extra =array() extra attributes passed as data-attribute to egw.js
- * @return array
- */
- protected function _get_header(array $extra=array())
- {
- // display password expires in N days message once per session
- $message = null;
- if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' &&
- auth::check_password_change($message) !== true)
- {
- self::message($message, 'info');
- }
-
- // get used language code (with a little xss check, if someone tries to sneak something in)
- if (preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$GLOBALS['egw_info']['user']['preferences']['common']['lang']))
- {
- $lang_code = $GLOBALS['egw_info']['user']['preferences']['common']['lang'];
- }
- // IE specific fixes
- if (html::$user_agent == 'msie')
- {
- // tell IE to use it's own mode, not old compatibility modes (set eg. via group policy for all intranet sites)
- // has to be before any other header tags, but meta and title!!!
- $pngfix = ''."\n";
-
- // pngfix for IE6 defaults to yes
- if(!$GLOBALS['egw_info']['user']['preferences']['common']['disable_pngfix'] && html::$ua_version < 7)
- {
- $pngfix_src = $GLOBALS['egw_info']['server']['webserver_url'] . '/phpgwapi/templates/idots/js/pngfix.js';
- $pngfix .= '
- ';
- }
- }
-
- $app = $GLOBALS['egw_info']['flags']['currentapp'];
- $app_title = isset($GLOBALS['egw_info']['apps'][$app]) ? $GLOBALS['egw_info']['apps'][$app]['title'] : lang($app);
- $app_header = $GLOBALS['egw_info']['flags']['app_header'] ? $GLOBALS['egw_info']['flags']['app_header'] : $app_title;
- $site_title = strip_tags($GLOBALS['egw_info']['server']['site_title'].' ['.($app_header ? $app_header : $app_title).']');
-
- // send appheader to clientside
- $extra['app-header'] = $app_header;
-
- if($GLOBALS['egw_info']['flags']['currentapp'] != 'wiki') $robots ='';
- if (substr($GLOBALS['egw_info']['server']['favicon_file'],0,4) == 'http')
- {
- $var['favicon_file'] = $GLOBALS['egw_info']['server']['favicon_file'];
- }
- else
- {
- $var['favicon_file'] = common::image('phpgwapi',$GLOBALS['egw_info']['server']['favicon_file']?$GLOBALS['egw_info']['server']['favicon_file']:'favicon.ico');
- }
-
- if ($GLOBALS['egw_info']['flags']['include_wz_tooltip'] &&
- file_exists(EGW_SERVER_ROOT.($wz_tooltip = '/phpgwapi/js/wz_tooltip/wz_tooltip.js')))
- {
- $include_wz_tooltip = '';
- }
- return $this->_get_css()+array(
- 'img_icon' => $var['favicon_file'],
- 'img_shortcut' => $var['favicon_file'],
- 'pngfix' => $pngfix,
- 'lang_code' => $lang_code,
- 'charset' => translation::charset(),
- 'website_title' => $site_title,
- 'body_tags' => self::_get_body_attribs(),
- 'java_script' => self::_get_js($extra),
- 'meta_robots' => $robots,
- 'dir_code' => lang('language_direction_rtl') != 'rtl' ? '' : ' dir="rtl"',
- 'include_wz_tooltip'=> $include_wz_tooltip,
- 'webserver_url' => $GLOBALS['egw_info']['server']['webserver_url'],
- );
- }
-
- /**
- * Get navbar as array to eg. set as vars for a template (from idots' navbar.inc.php)
- *
- * @param array $apps navbar apps from _get_navbar_apps
- * @return array
- */
- protected function _get_navbar($apps)
- {
- $var['img_root'] = $GLOBALS['egw_info']['server']['webserver_url'] . '/phpgwapi/templates/'.$this->template.'/images';
-
- if(isset($GLOBALS['egw_info']['flags']['app_header']))
- {
- $var['current_app_title'] = $GLOBALS['egw_info']['flags']['app_header'];
- }
- else
- {
- $var['current_app_title']=$apps[$GLOBALS['egw_info']['flags']['currentapp']]['title'];
- }
- $var['currentapp'] = $GLOBALS['egw_info']['flags']['currentapp'];
-
- // current users for admins
- $var['current_users'] = $this->_current_users();
-
- // quick add selectbox
- $var['quick_add'] = $this->_get_quick_add();
-
- $var['user_info'] = $this->_user_time_info();
-
- if($GLOBALS['egw_info']['user']['account_lastpwd_change'] == 0)
- {
- $api_messages = lang('You are required to change your password during your first login').'
'.
- lang('Click this image on the navbar: %1','
![]()
');
- }
- elseif($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] && $GLOBALS['egw_info']['user']['account_lastpwd_change'] < time() - (86400*$GLOBALS['egw_info']['server']['change_pwd_every_x_days']))
- {
- $api_messages = lang('it has been more then %1 days since you changed your password',$GLOBALS['egw_info']['server']['change_pwd_every_x_days']);
- }
-
- if (substr($GLOBALS['egw_info']['server']['login_logo_file'],0,4) == 'http' ||
- $GLOBALS['egw_info']['server']['login_logo_file'][0] == '/')
- {
- $var['logo_file'] = $GLOBALS['egw_info']['server']['login_logo_file'];
- }
- else
- {
- $var['logo_file'] = common::image('phpgwapi',$GLOBALS['egw_info']['server']['login_logo_file']?$GLOBALS['egw_info']['server']['login_logo_file']:'logo', '', null); // null=explicit allow svg
- }
- $var['logo_url'] = $GLOBALS['egw_info']['server']['login_logo_url']?$GLOBALS['egw_info']['server']['login_logo_url']:'http://www.eGroupWare.org';
-
- if (substr($var['logo_url'],0,4) != 'http')
- {
- $var['logo_url'] = 'http://'.$var['logo_url'];
- }
- $var['logo_title'] = $GLOBALS['egw_info']['server']['login_logo_title']?$GLOBALS['egw_info']['server']['login_logo_title']:'www.eGroupWare.org';
-
- return $var;
- }
-
- /**
- * Returns html with user and time
- *
- * @return void
- */
- protected static function _user_time_info()
- {
- $now = new egw_time();
- $user_info = '
'.common::display_fullname() .''. ' - ' . lang($now->format('l')) . ' ' . $now->format(true);
-
- $user_tzs = egw_time::getUserTimezones();
- if (count($user_tzs) > 1)
- {
- $tz = $GLOBALS['egw_info']['user']['preferences']['common']['tz'];
- $user_info .= html::form(html::select('tz',$tz,$user_tzs,true),array(),
- '/index.php','','tz_selection',' style="display: inline;"','GET');
- }
- return $user_info;
- }
-
- /**
- * Prepare the current users
- *
- * @return string
- */
- protected static function _current_users()
- {
- if( $GLOBALS['egw_info']['user']['apps']['admin'] && $GLOBALS['egw_info']['user']['preferences']['common']['show_currentusers'])
- {
- $current_users = '
' .
- lang('Current users') . ': ' . $GLOBALS['egw']->session->session_count() . '';
- return $current_users;
- }
- }
-
- /**
- * Prepare the quick add selectbox
- *
- * @return string
- */
- protected static function _get_quick_add()
- {
- return '
';
- }
-
- /**
- * Prepare notification signal (blinking bell)
- *
- * @return string
- */
- protected static function _get_notification_bell()
- {
- return html::image('notifications', 'notificationbell', lang('notifications'),
- 'id="notificationbell" style="display: none"');
- }
-
- /**
- * URL to check for security or maintenance updates
- */
- const CURRENT_VERSION_URL = 'http://www.egroupware.org/currentversion';
- /**
- * How long to cache (in secs) / often to check for updates
- */
- const VERSIONS_CACHE_TIMEOUT = 7200;
- /**
- * After how many days of not applied security updates, start warning non-admins too
- */
- const WARN_USERS_DAYS = 3;
-
- /**
- * Check update status
- *
- * @return string
- * @todo Check from client-side, if server-side check fails
- */
- protected static function _get_update_notification()
- {
- $versions = egw_cache::getTree(__CLASS__, 'versions', function()
- {
- $versions = array();
- $security = null;
- if (($remote = file_get_contents(egw_framework::CURRENT_VERSION_URL, false, egw_framework::proxy_context())))
- {
- list($current, $security) = explode("\n", $remote);
- if (empty($security)) $security = $current;
- $versions = array(
- 'current' => $current, // last maintenance update
- 'security' => $security, // last security update
- );
- }
- return $versions;
- }, array(), self::VERSIONS_CACHE_TIMEOUT);
-
- $api = self::api_version();
-
- if ($versions)
- {
- if (version_compare($api, $versions['security'], '<'))
- {
- if (!$GLOBALS['egw_info']['user']['apps']['admin'] && !self::update_older($versions['security'], self::WARN_USERS_DAYS))
- {
- return null;
- }
- return html::a_href(html::image('phpgwapi', 'security-update', lang('EGroupware security update %1 needs to be installed!', $versions['security'])),
- 'http://www.egroupware.org/changelog', null, ' target="_blank"');
- }
- if ($GLOBALS['egw_info']['user']['apps']['admin'] && version_compare($api, $versions['current'], '<'))
- {
- return html::a_href(html::image('phpgwapi', 'update', lang('EGroupware maintenance update %1 available', $versions['current'])),
- 'http://www.egroupware.org/changelog', null, ' target="_blank"');
- }
- }
- elseif ($GLOBALS['egw_info']['user']['apps']['admin'])
- {
- $error = lang('Automatic update check failed, you need to check manually!');
- if (!ini_get('allow_url_fopen'))
- {
- $error .= "\n".lang('%1 setting "%2" = %3 disallows access via http!',
- 'php.ini', 'allow_url_fopen', array2string(ini_get('allow_url_fopen')));
- }
- return html::a_href(html::image('phpgwapi', 'update', $error),
- 'http://www.egroupware.org/changelog', null, ' target="_blank" data-api-version="'.$api.'"');
- }
- return null;
- }
-
- /**
- * Get context to use with file_get_context or fopen to use our proxy settings from setup
- *
- * @param string $username =null username for regular basic auth
- * @param string $password =null password --------- " ----------
- * @return resource|null context to use with file_get_context/fopen or null if no proxy configured
- */
- public static function proxy_context($userna