diff --git a/api/src/Egw.php b/api/src/Egw.php
new file mode 100644
index 0000000000..a16e79e240
--- /dev/null
+++ b/api/src/Egw.php
@@ -0,0 +1,601 @@
+ldap)
+ * {
+ * $GLOBALS['egw']->ldap = Api\Ldap::factory();
+ * }
+ * You can now simply use $GLOBALS['egw']->ldap, and the egw class instanciates it for you on demand.
+ */
+class Egw extends Egw\Base
+{
+ /**
+ * Turn on debug mode. Will output additional data for debugging purposes.
+ * @var string
+ * @access public
+ */
+ var $debug = 0; // This will turn on debugging information.
+ /**
+ * Instance of the account object
+ *
+ * @var Accounts
+ */
+ var $accounts;
+
+ /**
+ * Constructor: Instantiates the sub-classes
+ *
+ * @author RalfBecker@outdoor-training.de
+ * @param array $domain_names array with valid egw-domain names
+ */
+ function __construct($domain_names=null)
+ {
+ $GLOBALS['egw'] =& $this; // we need to be immediately available there for the other classes we instantiate
+ $this->setup($domain_names,True);
+ }
+
+ /**
+ * Called every time the constructor is called. Also called by sessions to ensure the correct db,
+ * in which case we do not recreate the session object.
+ * @author RalfBecker@outdoor-training.de (moved to setup() by milos@groupwhere.org
+ * @param array $domain_names array with valid egw-domain names
+ * @param boolean $createsessionobject True to create the session object (default=True)
+ */
+ function setup($domain_names,$createsessionobject=True)
+ {
+ // create the DB-object
+ // as SiteMgr, Wiki, KnowledgeBase and probably more still use eg next_record(), we stick with Db\Deprecated for now
+ $this->db = new Db\Deprecated($GLOBALS['egw_info']['server']);
+ if ($this->debug)
+ {
+ $this->db->Debug = 1;
+ }
+ $this->db->set_app(Db::API_APPNAME);
+
+ // check if eGW is already setup, if not redirect to setup/
+ try {
+ $this->db->connect();
+ $num_config = $this->db->select(Config::TABLE,'COUNT(config_name)',false,__LINE__,__FILE__)->fetchColumn();
+ }
+ catch(Db\Exception\Connection $e) {
+ // ignore exception, get handled below
+ }
+ catch(Db\Exception\InvalidSql $e1) {
+ unset($e1); // not used
+ try {
+ $phpgw_config = $this->db->select('phpgw_config','COUNT(config_name)',false,__LINE__,__FILE__)->fetchColumn();
+ }
+ catch (Db\Exception\InvalidSql $e2) {
+ unset($e2); // not used
+ // ignor error, get handled below
+ }
+ }
+ if (!$num_config)
+ {
+ // we check for the old table too, to not scare updating users ;-)
+ if ($phpgw_config)
+ {
+ throw new Exception('You need to update EGroupware before you can continue using it.',999);
+ }
+ if ($e)
+ {
+ throw new Db\Exception\Setup('Connection with '.$e->getMessage()."\n\n".
+ 'Maybe you not created a database for EGroupware yet.',999);
+ }
+ throw new Db\Exception\Setup('It appears that you have not created the database tables for EGroupware.',999);
+ }
+ // Set the DB's client charset if a system-charset is set and some other values needed by egw_cache (used in Config::read)
+ foreach($this->db->select(Config::TABLE,'config_name,config_value',array(
+ 'config_app' => 'phpgwapi',
+ 'config_name' => array('system_charset','install_id','temp_dir'),
+ ),__LINE__,__FILE__) as $row)
+ {
+ $GLOBALS['egw_info']['server'][$row['config_name']] = $row['config_value'];
+ }
+ if ($GLOBALS['egw_info']['server']['system_charset'] && $GLOBALS['egw_info']['server']['system_charset'] != 'utf-8')
+ {
+ $this->db->Link_ID->SetCharSet($GLOBALS['egw_info']['server']['system_charset']);
+ }
+ // load up the $GLOBALS['egw_info']['server'] array
+ $GLOBALS['egw_info']['server'] += Config::read('phpgwapi');
+
+ // if no server timezone set, use date_default_timezone_get() to determine it once
+ // it fills to log with deprecated warnings under 5.3 otherwise
+ if (empty($GLOBALS['egw_info']['server']['server_timezone']) ||
+ $GLOBALS['egw_info']['server']['server_timezone'] == 'System/Localtime') // treat invalid tz like empty!
+ {
+ try
+ {
+ $tz = new DateTimeZone(date_default_timezone_get());
+ Config::save_value('server_timezone',$GLOBALS['egw_info']['server']['server_timezone'] = $tz->getName(),'phpgwapi');
+ error_log(__METHOD__."() stored server_timezone=".$GLOBALS['egw_info']['server']['server_timezone']);
+ }
+ catch(Exception $e)
+ {
+ // do nothing if new DateTimeZone fails (eg. 'System/Localtime' returned), specially do NOT store it!
+ error_log(__METHOD__."() NO valid 'date.timezone' set in your php.ini!");
+ }
+ }
+ date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
+
+ // if phpgwapi exists we prefer accounts and egw_session, as they have some deprecated methods
+ if (file_exists(EGW_SERVER_ROOT.'/phpgwapi'))
+ {
+ $this->accounts = new accounts();
+ /* Do not create the session object if called by the sessions class. This way
+ * we ensure the correct db based on the user domain.
+ */
+ if($createsessionobject)
+ {
+ $this->session = new egw_session($domain_names);
+ }
+ }
+ else
+ {
+ $this->accounts = new Accounts();
+ /* Do not create the session object if called by the sessions class. This way
+ * we ensure the correct db based on the user domain.
+ */
+ if($createsessionobject)
+ {
+ $this->session = new Session($domain_names);
+ }
+ }
+ // setup the other subclasses
+ $this->acl = new Acl();
+ $this->preferences = new Preferences();
+ $this->applications = new Egw\Applications();
+
+ if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && $GLOBALS['egw_info']['flags']['currentapp'] != 'logout')
+ {
+ $this->verify_session();
+ $this->applications->read_installed_apps(); // to get translated app-titles, has to be after verify_session
+
+ $this->define_egw_constants();
+
+ $this->check_app_rights();
+
+ $this->load_optional_classes();
+ }
+ else // set the defines for login, in case it's more then just login
+ {
+ $this->define_egw_constants();
+ }
+ }
+
+ /**
+ * __wakeup function gets called by php while unserializing the egw-object, eg. reconnects to the DB
+ *
+ * @author RalfBecker@outdoor-training.de
+ */
+ function __wakeup()
+ {
+ $GLOBALS['egw'] =& $this; // we need to be immediately available there for the other classes we instantiate
+ // for the migration: reference us to the old phpgw object
+ $GLOBALS['phpgw'] =& $this;
+
+ if ($GLOBALS['egw_info']['server']['system_charset'])
+ {
+ $this->db->Link_ID->SetCharSet($GLOBALS['egw_info']['server']['system_charset']);
+ }
+ // restoring server timezone, to avoid warnings under php5.3
+ if (!empty($GLOBALS['egw_info']['server']['server_timezone']))
+ {
+ date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
+ }
+
+ $this->define_egw_constants();
+ }
+
+ /**
+ * wakeup2 function needs to be called after unserializing the egw-object
+ *
+ * It adapts the restored object/enviroment to the changed (current) application / page-request
+ *
+ * @author RalfBecker@outdoor-training.de
+ */
+ function wakeup2()
+ {
+ // do some application specific stuff, need to be done as we are different (current) app now
+ if (isset($this->template))
+ {
+ $this->template->set_root(EGW_APP_TPL);
+ }
+ // init the translation class, necessary as own wakeup would run before our's
+ Translation::init(isset($GLOBALS['egw_info']['flags']['load_translations']) ? $GLOBALS['egw_info']['flags']['load_translations'] : true);
+
+ $this->unset_datetime();
+
+ // verify the session
+ $GLOBALS['egw']->verify_session();
+ $GLOBALS['egw']->check_app_rights();
+
+ $this->load_optional_classes();
+ }
+
+ /**
+ * Unsetting datetime object, so time gets updated
+ */
+ function unset_datetime()
+ {
+ unset($this->datetime);
+ }
+
+ /**
+ * load optional classes by mentioning them in egw_info[flags][enable_CLASS_class] => true
+ *
+ * Also loads the template-class if not egw_info[flags][disable_Template_class] is set
+ *
+ * Maybe the whole thing should be depricated ;-)
+ */
+ function load_optional_classes()
+ {
+ // output the header unless the developer turned it off
+ if (!@$GLOBALS['egw_info']['flags']['noheader'])
+ {
+ echo $GLOBALS['egw']->framework->header();
+
+ if (!$GLOBALS['egw_info']['flags']['nonavbar'])
+ {
+ echo $GLOBALS['egw']->framework->navbar();
+ }
+ }
+
+ // Load the (depricated) app include files if they exists
+ if (EGW_APP_INC != "" && ! preg_match ('/phpgwapi/i', EGW_APP_INC) &&
+ file_exists(EGW_APP_INC . '/functions.inc.php') && !isset($_GET['menuaction']))
+ {
+ include(EGW_APP_INC . '/functions.inc.php');
+ }
+ if (!@$GLOBALS['egw_info']['flags']['noheader'] && !@$GLOBALS['egw_info']['flags']['noappheader'] &&
+ file_exists(EGW_APP_INC . '/header.inc.php') && !isset($_GET['menuaction']))
+ {
+ include(EGW_APP_INC . '/header.inc.php');
+ }
+ }
+
+ /**
+ * Verfiy there is a valid session
+ *
+ * One can specify a callback, which gets called if there's no valid session. If the callback returns true, the parameter
+ * containst account-details (in keys login, passwd and passwd_type) to automatic create an (anonymous session)
+ *
+ * It also checks if enforce_ssl is set in the DB and redirects to the https:// version of the site.
+ *
+ * If there is no valid session and none could be automatic created, the function will redirect to login and NOT return
+ */
+ function verify_session()
+ {
+ if($GLOBALS['egw_info']['server']['enforce_ssl'] === 'redirect' && !$_SERVER['HTTPS'])
+ {
+ Header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
+ exit;
+ }
+ // check if we have a session, if not try to automatic create one
+ if ($this->session->verify()) return true;
+
+ $account = null;
+ if (($account_callback = $GLOBALS['egw_info']['flags']['autocreate_session_callback']) && is_callable($account_callback) &&
+ ($sessionid = call_user_func_array($account_callback,array(&$account))) === true) // $account_call_back returns true, false or a session-id
+ {
+ $sessionid = $this->session->create($account);
+ }
+ if (!$sessionid)
+ {
+ //echo "
\n"; exit;
+ // we forward to the same place after the re-login
+ if ($GLOBALS['egw_info']['server']['webserver_url'] && $GLOBALS['egw_info']['server']['webserver_url'] != '/' &&
+ ($webserver_path = parse_url($GLOBALS['egw_info']['server']['webserver_url'],PHP_URL_PATH)) && $webserver_path != '/')
+ {
+ // we have to use only path component, to cope with domains like http://egroupware.domain.com and /egroupware
+ list(,$relpath) = explode($webserver_path,parse_url($_SERVER['PHP_SELF'],PHP_URL_PATH),2);
+ }
+ else // the webserver-url is empty or just a slash '/' (eGW is installed in the docroot and no domain given)
+ {
+ $matches = null;
+ if (preg_match('/^https?:\/\/[^\/]*\/(.*)$/',$relpath=$_SERVER['PHP_SELF'],$matches))
+ {
+ $relpath = $matches[1];
+ }
+ }
+ // this removes the sessiondata if its saved in the URL
+ $query = preg_replace('/[&]?sessionid(=|%3D)[^&]+&kp3(=|%3D)[^&]+&domain=.*$/','',$_SERVER['QUERY_STRING']);
+ if ($GLOBALS['egw_info']['server']['http_auth_types'])
+ {
+ $redirect = '/phpgwapi/ntlm/index.php?';
+ }
+ else
+ {
+ $redirect = '/login.php?';
+ // only add "your session could not be verified", if a sessionid is given (cookie or on url)
+ if (Session::get_sessionid()) $redirect .= 'cd=10&';
+ }
+ if ($relpath) $redirect .= 'phpgw_forward='.urlencode($relpath.(!empty($query) ? '?'.$query : ''));
+ self::redirect_link($redirect);
+ }
+ }
+
+ /**
+ * Verify the user has rights for the requested app
+ *
+ * If the user has no rights for the app (eg. called via URL) he get a permission denied page (this function does NOT return)
+ *
+ * @throws Exception\Redirect for anonymous user accessing something he has no rights to
+ * @throws Exception\NoPermission\Admin
+ * @throws Exception\NoPermission\App
+ */
+ function check_app_rights()
+ {
+ $this->currentapp = $GLOBALS['egw_info']['flags']['currentapp']; // some apps change it later
+
+ if (!in_array($GLOBALS['egw_info']['flags']['currentapp'], array('api', 'home'))) // give everyone implicit home rights
+ {
+ // This will need to use ACL in the future
+ if (!$GLOBALS['egw_info']['user']['apps'][$currentapp = $GLOBALS['egw_info']['flags']['currentapp']] ||
+ ($GLOBALS['egw_info']['flags']['admin_only'] && !$GLOBALS['egw_info']['user']['apps']['admin']))
+ {
+ // present a login page, if anon user has no right for an application
+ if ($this->session->session_flags == 'A')
+ {
+ // need to destroy a basic auth session here, because it will only be available on current url
+ if (($sessionid = Session::get_sessionid(true)))
+ {
+ $GLOBALS['egw']->session->destroy($sessionid);
+ }
+ throw new Exception\Redirect(egw::link('/logout.php'));
+ }
+ if ($currentapp == 'admin' || $GLOBALS['egw_info']['flags']['admin_only'])
+ {
+ throw new Exception\NoPermission\Admin();
+ }
+ throw new Exception\NoPermission\App($currentapp);
+ }
+ }
+ }
+
+ /**
+ * create all the defines / constants of the eGW-environment (plus the deprecated phpgw ones)
+ */
+ function define_egw_constants()
+ {
+ define('EGW_ACL_READ',1);
+ define('EGW_ACL_ADD',2);
+ define('EGW_ACL_EDIT',4);
+ define('EGW_ACL_DELETE',8);
+ define('EGW_ACL_PRIVATE',16);
+ define('EGW_ACL_GROUP_MANAGERS',32);
+ define('EGW_ACL_CUSTOM_1',64);
+ define('EGW_ACL_CUSTOM_2',128);
+ define('EGW_ACL_CUSTOM_3',256);
+ // and the old ones
+ define('PHPGW_ACL_READ',1);
+ define('PHPGW_ACL_ADD',2);
+ define('PHPGW_ACL_EDIT',4);
+ define('PHPGW_ACL_DELETE',8);
+ define('PHPGW_ACL_PRIVATE',16);
+ define('PHPGW_ACL_GROUP_MANAGERS',32);
+ define('PHPGW_ACL_CUSTOM_1',64);
+ define('PHPGW_ACL_CUSTOM_2',128);
+ define('PHPGW_ACL_CUSTOM_3',256);
+ // A few hacker resistant constants that will be used throught the program
+ define('EGW_TEMPLATE_DIR', $this->common->get_tpl_dir('phpgwapi'));
+ define('EGW_IMAGES_DIR', $this->common->get_image_path('phpgwapi'));
+ define('EGW_IMAGES_FILEDIR', $this->common->get_image_dir('phpgwapi'));
+ define('EGW_APP_ROOT', $this->common->get_app_dir());
+ define('EGW_APP_INC', $this->common->get_inc_dir());
+ define('EGW_APP_TPL', $this->common->get_tpl_dir());
+ define('EGW_IMAGES', $this->common->get_image_path());
+ define('EGW_APP_IMAGES_DIR', $this->common->get_image_dir());
+ // and the old ones
+ define('PHPGW_TEMPLATE_DIR',EGW_TEMPLATE_DIR);
+ define('PHPGW_IMAGES_DIR',EGW_IMAGES_DIR);
+ define('PHPGW_IMAGES_FILEDIR',EGW_IMAGES_FILEDIR);
+ define('PHPGW_APP_ROOT',EGW_APP_ROOT);
+ define('PHPGW_APP_INC',EGW_APP_INC);
+ define('PHPGW_APP_TPL',EGW_APP_TPL);
+ define('PHPGW_IMAGES',EGW_IMAGES);
+ define('PHPGW_APP_IMAGES_DIR',EGW_APP_IMAGES_DIR);
+ }
+
+ /**
+ * force the session cache to be re-created, because some of it's data changed
+ *
+ * Needs to be called if user-preferences, system-config or enabled apps of the current user have been changed and
+ * the change should have immediate effect
+ */
+ static function invalidate_session_cache()
+ {
+ unset($_SESSION['egw_info_cache']);
+ unset($_SESSION['egw_object_cache']);
+ }
+
+ /**
+ * run string through htmlspecialchars and stripslashes
+ *
+ * @param string $s
+ * @return string The string with html special characters replaced with entities
+ */
+ static function strip_html($s)
+ {
+ return htmlspecialchars(stripslashes($s));
+ }
+
+ /**
+ * Link url generator
+ *
+ * @param string $url url link is for
+ * @param string|array $extravars ='' extra params to be added to 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)
+ {
+ return $GLOBALS['egw']->framework->link($url, $extravars, $link_app);
+ }
+
+ /**
+ * Redirects direct to a generated link
+ *
+ * @param string $url url link is for
+ * @param string|array $extravars ='' extra params to be added to 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)
+ {
+ return $GLOBALS['egw']->framework->redirect_link($url, $extravars, $link_app);
+ }
+
+ /**
+ * Handles redirects under iis and apache, it does NOT return (calls exit)
+ *
+ * This function handles redirects under iis and apache it assumes that $phpgw->link() has already been called
+ *
+ * @param string $url url to redirect to
+ * @param string $link_app =null appname to redirect for, default currentapp
+ */
+ static function redirect($url, $link_app=null)
+ {
+ Framework::redirect($url, $link_app);
+ }
+
+ /**
+ * Shortcut to translation class
+ *
+ * This function is a basic wrapper to Translation::translate()
+ *
+ * @deprecated only used in the old timetracker
+ * @param string The key for the phrase
+ * @see Translation::translate()
+ */
+ static function lang($key,$args=null)
+ {
+ if (!is_array($args))
+ {
+ $args = func_get_args();
+ array_shift($args);
+ }
+ return Translation::translate($key,$args);
+ }
+
+ /**
+ * registered shutdown callbacks and optional arguments
+ *
+ * @var array
+ */
+ private static $shutdown_callbacks = array();
+
+ /**
+ * Register a callback to run on shutdown AFTER output send to user
+ *
+ * Allows eg. static classes (no destructor) to run on shutdown AND
+ * garanties to run AFTER output send to user.
+ *
+ * @param callable $callback use array($classname, $method) for static methods
+ * @param array $args =array()
+ */
+ public static function on_shutdown($callback, array $args=array())
+ {
+ array_unshift($args, $callback);
+
+ // prepend new callback, to run them in oposite order they are registered
+ array_unshift(self::$shutdown_callbacks, $args);
+ }
+
+ /**
+ * Shutdown handler running all registered on_shutdown callbacks and then disconnecting from db
+ */
+ function __destruct()
+ {
+ if (!defined('EGW_SHUTDOWN'))
+ {
+ define('EGW_SHUTDOWN',True);
+
+ // send json response BEFORE flushing output
+ if (Json\Request::isJSONRequest())
+ {
+ Json\Response::sendResult();
+ }
+
+ // run all on_shutdown callbacks with session in their name (eg. egw_link::save_session_cache), do NOT stop on exceptions
+ foreach(self::$shutdown_callbacks as $n => $data)
+ {
+ try {
+ //error_log(__METHOD__."() running ".array2string($data));
+ $callback = array_shift($data);
+ if (!is_array($callback) || strpos($callback[1], 'session') === false) continue;
+ call_user_func_array($callback, $data);
+ }
+ catch (\Exception $ex) {
+ _egw_log_exception($ex);
+ }
+ unset(self::$shutdown_callbacks[$n]);
+ }
+ // now we can close the session
+ // without closing the session fastcgi_finish_request() will NOT send output to user
+ if (isset($GLOBALS['egw']->session) && is_object($GLOBALS['egw']->session)) $GLOBALS['egw']->session->commit_session();
+
+ // flush all output to user
+ /* does NOT work on Apache :-(
+ for($i = 0; ob_get_level() && $i < 10; ++$i)
+ {
+ ob_end_flush();
+ }
+ flush();*/
+ // working for fastCGI :-)
+ if (function_exists('fastcgi_finish_request') && substr($_SERVER['PHP_SELF'], -32) != '/phpgwapi/cron/asyncservices.php')
+ {
+ fastcgi_finish_request();
+ }
+
+ // run all on_shutdown, do NOT stop on exceptions
+ foreach(self::$shutdown_callbacks as $data)
+ {
+ try {
+ //error_log(__METHOD__."() running ".array2string($data));
+ $callback = array_shift($data);
+ call_user_func_array($callback, $data);
+ }
+ catch (\Exception $ex) {
+ _egw_log_exception($ex);
+ }
+ }
+ // call the asyncservice check_run function if it is not explicitly set to cron-only
+ if (!$GLOBALS['egw_info']['server']['asyncservice']) // is default
+ {
+ $async = new Asyncservice();
+ $async->fallback();
+ }
+ $this->db->disconnect();
+ }
+ }
+}
diff --git a/api/src/Egw/Applications.php b/api/src/Egw/Applications.php
new file mode 100755
index 0000000000..dd3c08bf71
--- /dev/null
+++ b/api/src/Egw/Applications.php
@@ -0,0 +1,109 @@
+
+ * Copyright (C) 2001 Mark Peters
+ * @license http://opensource.org/licenses/lgpl-license.php LGPL - GNU Lesser General Public License
+ * @package api
+ * @subpackage egw
+ * @version $Id$
+ */
+
+namespace EGroupware\Api\Egw;
+
+/**
+ * Application (sub-)object of Egw-object used to load $GLOBALS['egw_info'](['user'])['apps']
+ */
+class Applications
+{
+ var $account_id;
+ var $data = Array();
+ /**
+ * Reference to the global db class
+ *
+ * @var egw_db
+ */
+ var $db;
+ var $table_name = 'egw_applications';
+
+ /**************************************************************************\
+ * Standard constructor for setting $this->account_id *
+ \**************************************************************************/
+
+ /**
+ * standard constructor for setting $this->account_id
+ *
+ * @param $account_id account id
+ */
+ function __construct($account_id = '')
+ {
+ if (is_object($GLOBALS['egw_setup']) && is_object($GLOBALS['egw_setup']->db))
+ {
+ $this->db = $GLOBALS['egw_setup']->db;
+ }
+ else
+ {
+ $this->db = $GLOBALS['egw']->db;
+ }
+
+ $this->account_id = get_account_id($account_id);
+ }
+
+ /**
+ * Get applications of user
+ *
+ * Used to populate $GLOBALS['egw_info']['user']['apps'] in Api\Session
+ */
+ function read_repository()
+ {
+ if (!isset($GLOBALS['egw_info']['apps']) || !is_array($GLOBALS['egw_info']['apps']))
+ {
+ $this->read_installed_apps();
+ }
+ $this->data = Array();
+ if(!$this->account_id)
+ {
+ return False;
+ }
+ $apps = $GLOBALS['egw']->acl->get_user_applications($this->account_id);
+ foreach(array_keys($GLOBALS['egw_info']['apps']) as $app)
+ {
+ if (isset($apps[$app]) && $apps[$app])
+ {
+ $this->data[$app] =& $GLOBALS['egw_info']['apps'][$app];
+ }
+ }
+ return $this->data;
+ }
+
+ /**
+ * populate array with a list of installed apps
+ *
+ */
+ function read_installed_apps()
+ {
+ foreach($this->db->select($this->table_name,'*',false,__LINE__,__FILE__,false,'ORDER BY app_order ASC') as $row)
+ {
+ $title = $app_name = $row['app_name'];
+
+ if (@is_array($GLOBALS['egw_info']['user']['preferences']) && ($t = lang($app_name)) != $app_name.'*')
+ {
+ $title = $t;
+ }
+ $GLOBALS['egw_info']['apps'][$app_name] = Array(
+ 'title' => $title,
+ 'name' => $app_name,
+ 'enabled' => True,
+ 'status' => $row['app_enabled'],
+ 'id' => (int)$row['app_id'],
+ 'order' => (int)$row['app_order'],
+ 'version' => $row['app_version'],
+ 'index' => $row['app_index'],
+ 'icon' => $row['app_icon'],
+ 'icon_app'=> $row['app_icon_app'],
+ );
+ }
+ }
+}
diff --git a/api/src/Egw/Base.php b/api/src/Egw/Base.php
new file mode 100644
index 0000000000..7fe105d29c
--- /dev/null
+++ b/api/src/Egw/Base.php
@@ -0,0 +1,125 @@
+ldap)
+ * {
+ * $GLOBALS['egw']->ldap = Api\Ldap::factory();
+ * }
+ * You can now simply use $GLOBALS['egw']->ldap, and the egw class instanciates it for you on demand.
+ */
+class Base
+{
+ /**
+ * Instance of the db-object
+ *
+ * @var Api\Db
+ */
+ var $db;
+ /**
+ * Current app at the instancation of the class
+ *
+ * @var string
+ */
+ var $currentapp;
+ /**
+ * Global ADOdb object, need to be defined here, to not call magic __get method
+ *
+ * @var ADOConnection
+ */
+ var $ADOdb;
+
+ /**
+ * Classes which get instanciated in a different name
+ *
+ * @var array
+ */
+ static $sub_objects = array(
+ 'log' => 'errorlog',
+ 'link' => 'bolink', // depricated use static egw_link methods
+ 'datetime' => 'egw_datetime',
+ 'template' => 'Template',
+ 'session' => 'egw_session', // otherwise $GLOBALS['egw']->session->appsession() fails
+ // classes moved to new api dir
+ 'framework' => true, // special handling in __get()
+ 'ldap' => true,
+ 'auth' => 'EGroupware\\Api\\Auth',
+ );
+
+ /**
+ * Magic function to check if a sub-object is set
+ *
+ * @param string $name
+ * @return boolean
+ */
+ function __isset($name)
+ {
+ //error_log(__METHOD__."($name)");
+ return isset($this->$name);
+ }
+
+ /**
+ * Magic function to return a sub-object
+ *
+ * @param string $name
+ * @return mixed
+ */
+ function __get($name)
+ {
+ //error_log(__METHOD__."($name)".function_backtrace());
+
+ if ($name == 'js') $name = 'framework'; // javascript class is integrated now into framework
+
+ if (isset($this->$name))
+ {
+ return $this->$name;
+ }
+
+ if (!isset(self::$sub_objects[$name]) && !class_exists($name))
+ {
+ if ($name != 'ADOdb') error_log(__METHOD__.": There's NO $name object! ".function_backtrace());
+ return null;
+ }
+ switch($name)
+ {
+ case 'framework':
+ return $this->framework = Api\Framework::factory();
+ case 'template': // need to be instancated for the current app
+ if (!($tpl_dir = common::get_tpl_dir($this->currentapp)))
+ {
+ return null;
+ }
+ return $this->template = new Api\Framework\Template($tpl_dir);
+ case 'ldap':
+ return $this->ldap = Api\Ldap::factory(false);
+ default:
+ $class = isset(self::$sub_objects[$name]) ? self::$sub_objects[$name] : $name;
+ break;
+ }
+ return $this->$name = new $class();
+ }
+}
diff --git a/api/src/autoload.php b/api/src/autoload.php
new file mode 100755
index 0000000000..3b84fa237b
--- /dev/null
+++ b/api/src/autoload.php
@@ -0,0 +1,104 @@
+
+ * @package api
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+// this is only neccessary, if header.inc.php is not included, but api/src/autoload.php directly
+if (!defined('EGW_SERVER_ROOT'))
+{
+ define('EGW_SERVER_ROOT', dirname(dirname(__DIR__)));
+ define('EGW_INCLUDE_ROOT', EGW_SERVER_ROOT);
+ define('EGW_API_INC', __DIR__);
+}
+
+/**
+ * New PSR-4 autoloader for EGroupware
+ *
+ * class_exists('\\EGroupware\\Api\\Vfs'); // /api/src/Vfs.php
+ * class_exists('\\EGroupware\\Api\\Vfs\\Dav\\Directory'); // /api/src/Vfs/Dav/Directory.php
+ * class_exists('\\EGroupware\\Api\\Vfs\\Sqlfs\\StreamWrapper'); // /api/src/Vfs/Sqlfs/StreamWrapper.php
+ * class_exists('\\EGroupware\\Api\\Vfs\\Sqlfs\\Utils'); // /api/src/Vfs/Sqlfs/Utils.php
+ * class_exists('\\EGroupware\\Stylite\\Versioning\\StreamWrapper'); // /stylite/src/Versioning/StreamWrapper.php
+ * class_exists('\\EGroupware\\Calendar\\Ui'); // /calendar/src/Ui.php
+ * class_exists('\\EGroupware\\Calendar\\Ui\\Lists'); // /calendar/src/Ui/Lists.php
+ * class_exists('\\EGroupware\\Calendar\\Ui\\Views'); // /calendar/src/Ui/Views.php
+ */
+spl_autoload_register(function($class)
+{
+ $parts = explode('\\', $class);
+ if (array_shift($parts) != 'EGroupware') return; // not our prefix
+
+ $app = lcfirst(array_shift($parts));
+ $base = EGW_INCLUDE_ROOT.'/'.$app.'/src/';
+ $path = $base.implode('/', $parts).'.php';
+
+ if (file_exists($path))
+ {
+ require_once $path;
+ //error_log("PSR4_autoload('$class') --> require_once($path) --> class_exists('$class')=".array2string(class_exists($class,false)));
+ }
+});
+
+/**
+ * Old autoloader for EGroupware understanding the following naming schema:
+ *
+ * 1. new (prefered) nameing schema: app_class_something loading app/inc/class.class_something.inc.php
+ * 2. API classes: classname loading phpgwapi/inc/class.classname.inc.php
+ * 2a.API classes containing multiple classes per file eg. egw_exception* in class.egw_exception.inc.php
+ * 3. eTemplate classes: classname loading etemplate/inc/class.classname.inc.php
+ *
+ * @param string $class name of class to load
+ */
+spl_autoload_register(function($class)
+{
+ // fixing warnings generated by php 5.3.8 is_a($obj) trying to autoload huge strings
+ if (strlen($class) > 64 || strpos($class, '.') !== false) return;
+
+ $components = explode('_',$class);
+ $app = array_shift($components);
+ // classes using the new naming schema app_class_name, eg. admin_cmd
+ if (file_exists($file = EGW_INCLUDE_ROOT.'/'.$app.'/inc/class.'.$class.'.inc.php') ||
+ // classes using the new naming schema app_class_name, eg. admin_cmd
+ isset($components[0]) && file_exists($file = EGW_INCLUDE_ROOT.'/'.$app.'/inc/class.'.$app.'_'.$components[0].'.inc.php') ||
+ // classes with an underscore in their name
+ !isset($GLOBALS['egw_info']['apps'][$app]) && isset($GLOBALS['egw_info']['apps'][$app . '_' . $components[0]]) &&
+ file_exists($file = EGW_INCLUDE_ROOT.'/'.$app.'_'.$components[0].'/inc/class.'.$class.'.inc.php') ||
+ // eGW api classes using the old naming schema, eg. html
+ file_exists($file = EGW_API_INC.'/class.'.$class.'.inc.php') ||
+ // eGW api classes containing multiple classes in on file, eg. egw_exception
+ isset($components[0]) && file_exists($file = EGW_API_INC.'/class.'.$app.'_'.$components[0].'.inc.php') ||
+ // eGW eTemplate classes using the old naming schema, eg. etemplate
+ file_exists($file = EGW_INCLUDE_ROOT.'/etemplate/inc/class.'.$class.'.inc.php') ||
+ // include PEAR and PSR0 classes from include_path
+ // need to use include (not include_once) as eg. a previous included EGW_API_INC/horde/Horde/String.php causes
+ // include_once('Horde/String.php') to return true, even if the former was included with an absolute path
+ // only use include_path, if no Composer vendor directory exists
+ !isset($GLOBALS['egw_info']['apps'][$app]) && !file_exists(EGW_SERVER_ROOT.'/vendor') &&
+ @include($file = strtr($class, array('_'=>'/','\\'=>'/')).'.php'))
+ {
+ include_once($file);
+ //if (!class_exists($class, false) && !interface_exists($class, false)) error_log("autoloading class $class by include_once($file) failed!");
+ }
+ // allow apps to define an own autoload method
+ elseif (isset($GLOBALS['egw_info']['flags']['autoload']) && is_callable($GLOBALS['egw_info']['flags']['autoload']))
+ {
+ call_user_func($GLOBALS['egw_info']['flags']['autoload'],$class);
+ }
+});
+
+// if we have a Composer vendor directory, also load it's autoloader, to allow manage our requirements with Composer
+if (file_exists(EGW_SERVER_ROOT.'/vendor'))
+{
+ require_once EGW_SERVER_ROOT.'/vendor/autoload.php';
+}
diff --git a/api/src/loader.php b/api/src/loader.php
new file mode 100644
index 0000000000..861801422a
--- /dev/null
+++ b/api/src/loader.php
@@ -0,0 +1,143 @@
+
+ * @package api
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+use EGroupware\Api\Session;
+use EGroupware\Api\Egw;
+
+// E_STRICT in PHP 5.4 gives various strict warnings in working code, which can NOT be easy fixed in all use-cases :-(
+// Only variables should be assigned by reference, eg. soetemplate::tree_walk()
+// Declaration of should be compatible with , varios places where method parameters change
+// --> switching it off for now, as it makes error-log unusable
+error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
+if (function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime())
+{
+ set_magic_quotes_runtime(false);
+}
+
+$egw_min_php_version = '5.4';
+if (!function_exists('version_compare') || version_compare(PHP_VERSION,$egw_min_php_version) < 0)
+{
+ die("EGroupware requires PHP $egw_min_php_version or greater. Please contact your System Administrator to upgrade PHP!");
+}
+
+if (!defined('EGW_API_INC')) define('EGW_API_INC',PHPGW_API_INC); // this is to support the header upgrade
+
+/* Make sure the header.inc.php is current. */
+if (!isset($GLOBALS['egw_domain']) || $GLOBALS['egw_info']['server']['versions']['header'] < $GLOBALS['egw_info']['server']['versions']['current_header'])
+{
+ echo '
You need to update your header.inc.php file to version '.
+ $GLOBALS['egw_info']['server']['versions']['current_header'].
+ ' by running setup/headeradmin.
';
+ exit;
+}
+
+/* Make sure the developer is following the rules. */
+if (!isset($GLOBALS['egw_info']['flags']['currentapp']))
+{
+ echo "
!!! YOU DO NOT HAVE YOUR \$GLOBALS['egw_info']['flags']['currentapp'] SET !!! \n";
+ echo '!!! PLEASE CORRECT THIS SITUATION !!!
';
+}
+
+require_once(__DIR__.'/loader/common.php');
+
+// init eGW's sessions-handler and check if we can restore the eGW enviroment from the php-session
+if (Session::init_handler())
+{
+ if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && $GLOBALS['egw_info']['flags']['currentapp'] != 'logout')
+ {
+ if (is_array($_SESSION[Session::EGW_INFO_CACHE]) && $_SESSION[Session::EGW_OBJECT_CACHE] && $_SESSION[Session::EGW_REQUIRED_FILES])
+ {
+ // marking the context as restored from the session, used by session->verify to not read the data from the db again
+ $GLOBALS['egw_info']['flags']['restored_from_session'] = true;
+
+ // restoring the egw_info-array
+ $GLOBALS['egw_info'] = array_merge($_SESSION[Session::EGW_INFO_CACHE],array('flags' => $GLOBALS['egw_info']['flags']));
+
+ // include required class-definitions
+ if (is_array($_SESSION[Session::EGW_REQUIRED_FILES])) // all classes, which can not be autoloaded
+ {
+ foreach($_SESSION[Session::EGW_REQUIRED_FILES] as $file)
+ {
+ require_once($file);
+ }
+ }
+ $GLOBALS['egw'] = unserialize($_SESSION[Session::EGW_OBJECT_CACHE]);
+
+ if (is_object($GLOBALS['egw']) && ($GLOBALS['egw'] instanceof Egw)) // only egw object has wakeup2, setups egw_minimal eg. has not!
+ {
+ $GLOBALS['egw']->wakeup2(); // adapt the restored egw-object/enviroment to this request (eg. changed current app)
+
+ $GLOBALS['egw_info']['flags']['session_restore_time'] = microtime(true) - $GLOBALS['egw_info']['flags']['page_start_time'];
+ if (is_object($GLOBALS['egw']->translation)) return; // exit this file, as the rest of the file creates a new egw-object and -enviroment
+ }
+ // egw object could NOT be restored from the session, create a new one
+ unset($GLOBALS['egw']);
+ $GLOBALS['egw_info'] = array('flags'=>$GLOBALS['egw_info']['flags']);
+ unset($GLOBALS['egw_info']['flags']['restored_from_session']);
+ unset($_SESSION[Session::EGW_INFO_CACHE]);
+ unset($_SESSION[Session::EGW_REQUIRED_FILES]);
+ unset($_SESSION[Session::EGW_OBJECT_CACHE]);
+ }
+ }
+ else // destroy the session-cache if called by login or logout
+ {
+ unset($_SESSION[Session::EGW_INFO_CACHE]);
+ unset($_SESSION[Session::EGW_REQUIRED_FILES]);
+ unset($_SESSION[Session::EGW_OBJECT_CACHE]);
+ }
+}
+
+/****************************************************************************\
+* Multi-Domain support *
+\****************************************************************************/
+
+$GLOBALS['egw_info']['user']['domain'] = Session::search_instance(
+ isset($_POST['login']) ? $_POST['login'] : (isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : $_SERVER['REMOTE_USER']),
+ Session::get_request('domain'),$GLOBALS['egw_info']['server']['default_domain'],
+ array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']),$GLOBALS['egw_domain']);
+
+$GLOBALS['egw_info']['server']['db_host'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_host'];
+$GLOBALS['egw_info']['server']['db_port'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_port'];
+$GLOBALS['egw_info']['server']['db_name'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_name'];
+$GLOBALS['egw_info']['server']['db_user'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_user'];
+$GLOBALS['egw_info']['server']['db_pass'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_pass'];
+$GLOBALS['egw_info']['server']['db_type'] = $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['db_type'];
+
+// the egw-object instanciates all sub-classes (eg. $GLOBALS['egw']->db) and the egw_info array
+$GLOBALS['egw'] = new Egw(array_keys($GLOBALS['egw_domain']));
+
+// store domain config user&pw as a hash (originals get unset)
+$GLOBALS['egw_info']['server']['config_hash'] = Session::user_pw_hash($GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['config_user'],
+ $GLOBALS['egw_domain'][$GLOBALS['egw_info']['user']['domain']]['config_passwd'],true);
+
+if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && !$GLOBALS['egw_info']['server']['show_domain_selectbox'])
+{
+ unset($GLOBALS['egw_domain']); // we kill this for security reasons
+ unset($GLOBALS['egw_info']['server']['header_admin_user']);
+ unset($GLOBALS['egw_info']['server']['header_admin_password']);
+}
+
+// saving the the egw_info array and the egw-object in the session
+if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login')
+{
+ $_SESSION[Session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
+ unset($_SESSION[Session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request
+
+ $_SESSION[Session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']);
+}
diff --git a/api/src/loader/common.php b/api/src/loader/common.php
new file mode 100755
index 0000000000..46aee5e66c
--- /dev/null
+++ b/api/src/loader/common.php
@@ -0,0 +1,417 @@
+
+ * @package api
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+use EGroupware\Api;
+
+require_once dirname(__DIR__).'/autoload.php';
+
+/**
+* applies stripslashes recursivly on each element of an array
+*
+* @param array &$var
+* @return array
+*/
+function array_stripslashes($var)
+{
+ if (!is_array($var))
+ {
+ return stripslashes($var);
+ }
+ foreach($var as $key => $val)
+ {
+ $var[$key] = is_array($val) ? array_stripslashes($val) : stripslashes($val);
+ }
+ return $var;
+}
+
+/**
+ * Return the number of bytes of a string, independent of mbstring.func_overload
+ * AND the availability of mbstring
+ *
+ * @param string $str
+ * @return int
+ */
+function bytes($str)
+{
+ static $func_overload = null;
+
+ if (is_null($func_overload)) $func_overload = extension_loaded('mbstring') ? ini_get('mbstring.func_overload') : 0;
+
+ return $func_overload & 2 ? mb_strlen($str,'ascii') : strlen($str);
+}
+
+/**
+ * mbstring.func_overload safe substr
+ *
+ * @param string $data
+ * @param int $offset
+ * @param int $len
+ * @return string
+ */
+function cut_bytes(&$data,$offset,$len=null)
+{
+ static $func_overload = null;
+
+ if (is_null($func_overload)) $func_overload = extension_loaded('mbstring') ? ini_get('mbstring.func_overload') : 0;
+
+ if (is_null($len))
+ {
+ return $func_overload & 2 ? mb_substr($data,$offset,bytes($data),'ascii') : substr($data,$offset);
+ }
+ return $func_overload & 2 ? mb_substr($data,$offset,$len,'ascii') : substr($data,$offset,$len);
+}
+
+if (!function_exists('imap_rfc822_parse_adrlist'))
+{
+ /**
+ * parses a (comma-separated) address string
+ *
+ * Examples:
+ * - Joe Doe
+ * - "Doe, Joe"
+ * - "\'Joe Doe\'" // actually not necessary to quote
+ * - postmaster@example.com
+ * - root
+ * - "Joe on its way Down Under :-\)"
+ * - "Giant; \"Big\" Box"
+ * - sysservices@example.net // this is wrong, because @ need to be quoted
+ *
+ * Invalid addresses, if detected, set host to '.SYNTAX-ERROR.'
+ *
+ * @param string $address - A string containing addresses
+ * @param string $default_host - The default host name
+ * @return array of objects. The objects properties are:
+ * mailbox - the mailbox name (username)
+ * host - the host name
+ * personal - the personal name
+ * adl - at domain source route
+ */
+ function imap_rfc822_parse_adrlist($address, $default_host)
+ {
+ $addresses = array();
+ $pending = '';
+ foreach(explode(',', $address) as $part)
+ {
+ $trimmed = trim(($pending ? $pending.',' : '').$part);
+ if (($trimmed[0] == '"' && substr($trimmed, -1) != '>')||strpos($part, '@')===false)
+ {
+ $pending .= ($pending ? $pending.',' : '').$part;
+ continue;
+ }
+ $pending = '';
+ $matches = $personal = $mailbox = $host = null;
+ if (preg_match('/^(.*)<([^>@]+)(@([^>]+))?>$/', $trimmed, $matches))
+ {
+ $personal = trim($matches[1]);
+ $mailbox = $matches[2];
+ $host = $matches[4];
+ }
+ elseif (strpos($trimmed, '@') !== false)
+ {
+ list($mailbox, $host) = explode('@', $trimmed);
+ }
+ else
+ {
+ $mailbox = $trimmed;
+ }
+ if ($personal[0] == '"' && substr($personal, -1) == '"')
+ {
+ $personal = str_replace('\\', '', substr($personal, 1, -1));
+ }
+ if (empty($host)) $host = $default_host;
+
+ $addresses[] = (object)array_diff(array(
+ 'mailbox' => $mailbox,
+ 'host' => $host,
+ 'personal' => $personal,
+ ), array(null, ''));
+ }
+ return $addresses;
+ }
+}
+
+if (!function_exists('imap_rfc822_write_address'))
+{
+ /**
+ * Returns a properly formatted email address given the mailbox, host, and personal info
+ * @param string $mailbox - The mailbox name, see imap_open() for more information
+ * @param string $host - The email host part
+ * @param string $personal - The name of the account owner
+ * @return string properly formatted email address as defined in » RFC2822.
+ */
+ function imap_rfc822_write_address($mailbox, $host, $personal)
+ {
+ if (is_array($personal)) $personal = implode(' ', $personal);
+
+ //if (!preg_match('/^[!#$%&\'*+/0-9=?A-Z^_`a-z{|}~-]+$/u', $personal)) // that's how I read the rfc(2)822
+ if ($personal && !preg_match('/^[0-9A-Z -]*$/iu', $personal)) // but quoting is never wrong, so quote more then necessary
+ {
+ $personal = '"'.str_replace(array('\\', '"'),array('\\\\', '\\"'), $personal).'"';
+ }
+ return ($personal ? $personal.' <' : '').$mailbox.($host ? '@'.$host : '').($personal ? '>' : '');
+ }
+}
+
+if (!function_exists('imap_mime_header_decode'))
+{
+ /**
+ * Decodes MIME message header extensions that are non ASCII text (RFC2047)
+ *
+ * Uses Horde_Mime::decode() and therefore always returns only a single array element!
+ *
+ * @param string $text
+ * @return array with single object with attribute text already in our internal encoding and charset
+ * @deprecated use Horde_Mime::decode()
+ */
+ function imap_mime_header_decode($text)
+ {
+ return array((object)array(
+ 'text' => Horde_Mime::decode($text),
+ 'charset' => Api\Translation::charset(), // is already in our internal encoding!
+ ));
+ }
+}
+
+if (!function_exists('mb_strlen'))
+{
+ /**
+ * Number of characters in a string
+ *
+ * @param string $str
+ * @return int
+ */
+ function mb_strlen($str)
+ {
+ return strlen($str);
+ }
+}
+
+if (!function_exists('mb_substr'))
+{
+ /**
+ * Return part of a string
+ *
+ * @param string $data
+ * @param int $offset
+ * @param int $len
+ * @return string
+ */
+ function mb_substr(&$data, $offset, $len=null)
+ {
+ return is_null($len) ? substr($data, $offset) : substr($data, $offset, $len);
+ }
+}
+
+/**
+ * Format array or other types as (one-line) string, eg. for error_log statements
+ *
+ * @param mixed $var variable to dump
+ * @return string
+ */
+function array2string($var)
+{
+ switch (($type = gettype($var)))
+ {
+ case 'boolean':
+ return $var ? 'TRUE' : 'FALSE';
+ case 'string':
+ return "'$var'";
+ case 'integer':
+ case 'double':
+ case 'resource':
+ return $var;
+ case 'NULL':
+ return 'NULL';
+ case 'object':
+ case 'array':
+ return str_replace(array("\n",' '/*,'Array'*/),'',print_r($var,true));
+ }
+ return 'UNKNOWN TYPE!';
+}
+
+/**
+ * Check if a given extension is loaded or load it if possible (requires sometimes disabled or unavailable dl function)
+ *
+ * @param string $extension
+ * @param boolean $throw =false should we throw an exception, if $extension could not be loaded, default false = no
+ * @return boolean true if loaded now, false otherwise
+ */
+function check_load_extension($extension,$throw=false)
+{
+ if (!defined('PHP_SHLIB_PREFIX'))
+ {
+ define('PHP_SHLIB_PREFIX',PHP_SHLIB_SUFFIX == 'dll' ? 'php_' : '');
+ }
+ // we check for the existens of 'dl', as multithreaded webservers dont have it and some hosters disable it !!!
+ $loaded = extension_loaded($extension) || function_exists('dl') && @dl($dl=PHP_SHLIB_PREFIX.$extension.'.'.PHP_SHLIB_SUFFIX);
+
+ if (!$loaded && $throw)
+ {
+ throw new Exception ("PHP extension '$extension' not loaded AND can NOT be loaded via dl('$dl')!");
+ }
+ return $loaded;
+}
+
+// include deprecated global functions, if phpgwapi is installed
+if (file_exists(EGW_SERVER_ROOT.'/phpgwapi'))
+{
+ include_once EGW_SERVER_ROOT.'/phpgwapi/inc/deprecated_functions.inc.php';
+}
+
+/**
+ * Return a properly formatted account_id.
+ *
+ * @author skeeter
+ * This function will return a properly formatted account_id. This can take either a name or an account_id as paramters. If a name is provided it will return the associated id.
+ * $account_id = get_account_id($accountid);
+ * @param int/string $account_id either a name or an id
+ * @param int/string $default_id either a name or an id
+ * @return int account_id
+ */
+function get_account_id($account_id = '',$default_id = '')
+{
+ if (gettype($account_id) == 'integer')
+ {
+ return $account_id;
+ }
+ elseif ($account_id == '')
+ {
+ if ($default_id == '')
+ {
+ return (isset($GLOBALS['egw_info']['user']['account_id'])?$GLOBALS['egw_info']['user']['account_id']:0);
+ }
+ elseif (is_string($default_id))
+ {
+ return $GLOBALS['egw']->accounts->name2id($default_id);
+ }
+ return (int)$default_id;
+ }
+ elseif (is_string($account_id))
+ {
+ if($GLOBALS['egw']->accounts->exists((int)$account_id) == True)
+ {
+ return (int)$account_id;
+ }
+ else
+ {
+ return $GLOBALS['egw']->accounts->name2id($account_id);
+ }
+ }
+}
+
+/**
+ * print an array or object as pre-formatted html
+ *
+ * @param mixed $array
+ * @param boolean $print =true print or return the content
+ * @return string if !$print
+ */
+function _debug_array($array,$print=True)
+{
+ $output = '
'.print_r($array,true)."
\n";
+
+ if ($print)
+ {
+ echo $output;
+ }
+ else
+ {
+ return $output;
+ }
+}
+
+/**
+ * backtrace of the calling functions for php4.3+ else menuaction/scriptname
+ *
+ * @author RalfBecker-AT-outdoor-training.de
+ * @param int $remove =0 number of levels to remove
+ * @return string function-names separated by slashes (beginning with the calling function not this one)
+ */
+function function_backtrace($remove=0)
+{
+ if (function_exists('debug_backtrace'))
+ {
+ $backtrace = debug_backtrace();
+ //echo "function_backtrace($remove)
".print_r($backtrace,True)."
\n";
+ foreach($backtrace as $n => $level)
+ {
+ if ($remove-- < 0)
+ {
+ $ret[] = (isset($level['class'])?$level['class'].$level['type']:'').$level['function'].
+ ($n > 0 && isset($backtrace[$n-1]['line']) ? ':'.$backtrace[$n-1]['line'] : ''). // add line number of call
+ (!$level['class'] && !is_object($level['args'][0]) && $level['function'] != 'unserialize' ?
+ '('.substr(str_replace(EGW_SERVER_ROOT,'',(string)$level['args'][0]),0,64).')' : '');
+ }
+ }
+ if (is_array($ret))
+ {
+ return implode(' / ',$ret);
+ }
+ }
+ return $_GET['menuaction'] ? $_GET['menuaction'] : str_replace(EGW_SERVER_ROOT,'',$_SERVER['SCRIPT_FILENAME']);
+}
+
+if (!function_exists('lang') || defined('NO_LANG')) // setup declares an own version
+{
+ /**
+ * function to handle multilanguage support
+ *
+ * @param string $key message in englich with %1, %2, ... placeholders
+ * @param string $vars =null multiple values to replace the placeholders
+ * @return string translated message with placeholders replaced
+ */
+ function lang($key,$vars=null)
+ {
+ if(!is_array($vars))
+ {
+ $vars = func_get_args();
+ array_shift($vars); // remove $key
+ }
+ return Api\Translation::translate($key,$vars);
+ }
+}
+
+require_once __DIR__.'/security.php';
+require_once __DIR__.'/exception.php';
+
+/**
+ * Public functions to be compatible with the exiting eGW framework
+ */
+if (!function_exists('parse_navbar'))
+{
+ /**
+ * echo's out the navbar
+ *
+ * @deprecated use $GLOBALS['egw']->framework->navbar() or $GLOBALS['egw']->framework::render()
+ */
+ function parse_navbar()
+ {
+ echo $GLOBALS['egw']->framework->navbar();
+ }
+}
+
+if (!function_exists('display_sidebox'))
+{
+ /**
+ * echo's out a sidebox menu
+ *
+ * @deprecated use $GLOBALS['egw']->framework->sidebox()
+ */
+ function display_sidebox($appname,$menu_title,$_file)
+ {
+ $file = str_replace('preferences.uisettings.index', 'preferences.preferences_settings.index', $_file);
+ $GLOBALS['egw']->framework->sidebox($appname,$menu_title,$file);
+ }
+}
diff --git a/api/src/loader/exception.php b/api/src/loader/exception.php
new file mode 100755
index 0000000000..ebe594d5de
--- /dev/null
+++ b/api/src/loader/exception.php
@@ -0,0 +1,195 @@
+
+ * @package api
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
+
+use EGroupware\Api;
+
+/**
+ * Translate message only if translation object is already loaded
+ *
+ * This function is usefull for exception handlers or early stages of the initialisation of the egw object,
+ * as calling lang would try to load the translations, evtl. cause more errors, eg. because there's no db-connection.
+ *
+ * @param string $key message in englich with %1, %2, ... placeholders
+ * @param string $vars =null multiple values to replace the placeholders
+ * @return string translated message with placeholders replaced
+ */
+function try_lang($key,$vars=null)
+{
+ static $varnames = array('%1','%2','%3','%4');
+
+ if(!is_array($vars))
+ {
+ $vars = func_get_args();
+ array_shift($vars); // remove $key
+ }
+ return class_exists('EGroupware\Api\Translations',false) ? Api\Translation::translate($key,$vars) : str_replace($varnames,$vars,$key);
+}
+
+/**
+ * Clasify exception for a headline and log it to error_log, if not running as cli
+ *
+ * @param Exception|Error $e
+ * @param string &$headline
+ */
+function _egw_log_exception($e,&$headline=null)
+{
+ $trace = explode("\n", $e->getTraceAsString());
+ if ($e instanceof Api\Exception\NoPermission)
+ {
+ $headline = try_lang('Permission denied!');
+ }
+ elseif ($e instanceof Api\Db\Exception)
+ {
+ $headline = try_lang('Database error');
+ }
+ elseif ($e instanceof Api\Exception\WrongUserinput)
+ {
+ $headline = ''; // message contains the whole message, it's usually no real error but some input validation
+ }
+ elseif ($e instanceof egw_exception_warning)
+ {
+ $headline = 'PHP Warning';
+ array_shift($trace);
+ }
+ else
+ {
+ $headline = try_lang('An error happened');
+ }
+ // log exception to error log, if not running as cli,
+ // which outputs the error_log to stderr and therefore output it twice to the user
+ if(isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] !== 'cli')
+ {
+ error_log($headline.($e instanceof egw_exception_warning ? ': ' : ' ('.get_class($e).'): ').$e->getMessage());
+ foreach($trace as $line)
+ {
+ error_log($line);
+ }
+ error_log('# Instance='.$GLOBALS['egw_info']['user']['domain'].', User='.$GLOBALS['egw_info']['user']['account_lid'].
+ ', Request='.$_SERVER['REQUEST_METHOD'].' '.($_SERVER['HTTPS']?'https://':'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].
+ ', User-agent='.$_SERVER['HTTP_USER_AGENT']);
+ }
+}
+
+/**
+ * Fail a little bit more gracefully then an uncought exception
+ *
+ * Does NOT return
+ *
+ * @param Exception|Error $e
+ */
+function egw_exception_handler($e)
+{
+ // handle redirects without logging
+ if (is_a($e, 'egw_exception_redirect'))
+ {
+ egw::redirect($e->url, $e->app);
+ }
+ // logging all exceptions to the error_log (if not cli) and get headline
+ $headline = null;
+ _egw_log_exception($e,$headline);
+
+ // exception handler for cli (command line interface) clients, no html, no logging
+ if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli')
+ {
+ echo ($headline ? $headline.': ' : '').$e->getMessage()."\n";
+ if ($GLOBALS['egw_info']['server']['exception_show_trace'])
+ {
+ echo $e->getTraceAsString()."\n";
+ }
+ exit($e->getCode() ? $e->getCode() : 9999); // allways give a non-zero exit code
+ }
+ // regular GUI exception
+ if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']))
+ {
+ header('HTTP/1.1 500 '.$headline);
+ $message = '
'.Api\Html::htmlspecialchars($headline)."
\n".
+ '
'.Api\Html::htmlspecialchars($e->getMessage())."\n\n";
+
+ // only show trace (incl. function arguments) if explicitly enabled, eg. on a development system
+ if ($GLOBALS['egw_info']['server']['exception_show_trace'])
+ {
+ $message .= Api\Html::htmlspecialchars($e->getTraceAsString());
+ }
+ $message .= "