From fa73ad5339946091159060c4565ce20ebfe0b3ca Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 26 Oct 2008 12:13:01 +0000 Subject: [PATCH] Improved exception handling: - exceptions get now always logged to the error_log - in the webgui it's now configurable, if the message contains a stacktrace (incl. function arguments) - default no (security) - command line interfaces get detected and contain no html anymore - webdav and groupdav send the exceptions as basic auth realms to the client - webdav and groupdav login failures contain the reason as part of the basic auth realm --- admin/admin-cli.php | 86 +++++++++++---------------- admin/setup/egw_de.lang | 3 + admin/setup/egw_en.lang | 1 + admin/templates/default/config.tpl | 20 +++++-- groupdav.php | 12 +++- phpgwapi/inc/common_functions.inc.php | 47 +++++++++++---- setup/setup-cli.php | 18 +----- webdav.php | 20 +++++-- 8 files changed, 117 insertions(+), 90 deletions(-) diff --git a/admin/admin-cli.php b/admin/admin-cli.php index 95f50899b1..6228846fbb 100755 --- a/admin/admin-cli.php +++ b/admin/admin-cli.php @@ -1,4 +1,4 @@ -#!/usr/bin/php -qC +#!/usr/bin/php -qC 'admin', 'noheader' => true, 'autocreate_session_callback' => 'user_pass_from_argv', + 'no_exception_handler' => 'cli', ) ); include('../header.inc.php'); -// set our own exception handler, to not get the html from eGW's default one -set_exception_handler('admin_cli_exception_handler'); - switch($action) { case '--edit-user': return do_edit_user($arg0s); - + case '--change-pw': return do_change_pw($arg0s); case '--delete-user': return do_delete_account($arg0s[2],$arg0s[3]); - + case '--edit-group': return do_edit_group($arg0s); - + case '--delete-group': return do_delete_account($arg0s[2],0,false); - + case '--allow-app': case '--deny-app': return do_account_app($arg0s,$action == '--allow-app'); - + case '--change-account-id': return do_change_account_id($arg0s); - + case '--subscribe-other': return do_subscribe_other($arg0s[2],$arg0s[3]); - + case '--check-acl'; return do_check_acl(); - + case '--show-header'; return run_command(new setup_cmd_showheader($arg0s[2])); - + case '--exit-codes': return list_exit_codes(); @@ -95,7 +93,7 @@ exit(0); /** * run a command object, after checking for additional arguments: sheduled, requested or comment - * + * * Does not return! Echos success or error messsage and exits with either 0 (success) or the numerical error-code * * @param admin_cmd $cmd @@ -103,7 +101,7 @@ exit(0); function run_command(admin_cmd $cmd) { global $arguments; - + $skip_checks = false; while ($arguments && ($extra = array_shift($arguments))) { @@ -112,24 +110,24 @@ function run_command(admin_cmd $cmd) case '--schedule': // schedule the command instead of running it directly $time = admin_cmd::parse_date(array_shift($arguments)); break; - + case '--requested': // note who requested to run the command $cmd->requested = 0; $cmd->requested_email = array_shift($arguments); break; - + case '--comment': // note a comment $cmd->comment = array_shift($arguments); break; - + case '--remote': // run the command on a remote install $cmd->remote_id = admin_cmd::parse_remote(array_shift($arguments)); break; - + case '--skip-checks': //do not yet run the checks for scheduled local commands $skip_checks = true; break; - + case '--header-access': if ($cmd instanceof setup_cmd) { @@ -154,7 +152,7 @@ function run_command(admin_cmd $cmd) /** * callback to authenticate with the user/pw specified on the commandline - * + * * @param array &$account account_info with keys 'login', 'passwd' and optional 'passwd_type' * @return boolean/string true if we allow the access and account is set, a sessionid or false otherwise */ @@ -191,7 +189,7 @@ function usage($action=null,$ret=0) { $cmd = basename($_SERVER['argv'][0]); echo "Usage: $cmd --command admin-account[@domain],admin-password,options,... [--schedule {YYYY-mm-dd|+1 week|+5 days}] [--requested 'Name '] [--comment 'comment ...'] [--remote {id|name}] [--skip-checks]\n\n"; - + echo "--edit-user admin-account[@domain],admin-password,account[=new-account-name],first-name,last-name,password,email,expires{never(default)|YYYY-MM-DD|already},can-change-pw{yes(default)|no},anon-user{yes|no(default)},primary-group{Default(default)|...}[,groups,...]\n"; echo " Edit or add a user to eGroupWare. If you specify groups, they *replace* the exiting memberships!\n"; echo "--change-pw admin-account[@domain],admin-password,account,password\n"; @@ -212,7 +210,7 @@ function usage($action=null,$ret=0) echo "--exit-codes admin-account[@domain],admin-password\n"; echo " List all exit codes of the command line interface\n"; - exit($ret); + exit($ret); } /** @@ -227,7 +225,7 @@ function do_account_app($args,$allow) array_shift($args); // admin-account array_shift($args); // admin-pw $account = array_shift($args); - + include_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd_account_app.inc.php'); run_command(new admin_cmd_account_app($allow,$account,$args)); } @@ -242,7 +240,7 @@ function do_edit_group($args) array_shift($args); // admin-account array_shift($args); // admin-pw list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name] - + $data = array( 'account_lid' => $new_account_name, 'account_email' => array_shift($args), @@ -265,7 +263,7 @@ function do_edit_group($args) /** * Change/Set Password for a given user - * 1: 2: 3: 4: + * 1: 2: 3: 4: * @param array $args admin-account[@domain],admin-password,account,password */ function do_change_pw($args) @@ -274,7 +272,7 @@ function do_change_pw($args) array_shift($args); // admin-pw $account = array_shift($args); // account $password = array_shift($args); // pw - + run_command(new admin_cmd_change_pw($account,$password)); } @@ -288,7 +286,7 @@ function do_edit_user($args) array_shift($args); // admin-account array_shift($args); // admin-pw list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name] - + $data = array( 'account_lid' => $new_account_name, 'account_firstname' => array_shift($args), @@ -331,7 +329,7 @@ function do_delete_account($account,$new_user=0,$is_user=true) /** * Deletes ACL entries of not longer existing accounts - * + * * @return int 0 allways */ function do_check_acl() @@ -348,7 +346,7 @@ function do_check_acl() function do_change_account_id($args) { if (count($args) < 4) usage(); // 4 means at least user,pw,from1,to1 - + $ids2change = array(); for($n = 2; $n < count($args); $n += 2) { @@ -359,24 +357,12 @@ function do_change_account_id($args) run_command(new admin_cmd_change_account_id($ids2change)); } -/** - * Exit the script with a numeric exit code and an error-message, does NOT return - * - * @param int $exit_code - * @param string $message - */ -function admin_cli_exception_handler(Exception $e) -{ - echo $e->getMessage()."\n"; - exit($e->getCode()); -} - /** * List all exit codes used by the command line interface * - * The list is generated by "greping" this file for calls to the fail() function. + * The list is generated by "greping" this file for calls to the fail() function. * Calls to fail() have to be in one line, to be recogniced! - * + * * @ToDo adapt it to the exceptions */ function list_exit_codes() @@ -409,7 +395,7 @@ function do_subscribe_other($account_lid,$pw=null) { if (!($account_id = $GLOBALS['egw']->accounts->name2id($account_lid))) { - throw new egw_exception_wrong_userinput(lang("Unknown account: %1 !!!",$account_lid),15); + throw new egw_exception_wrong_userinput(lang("Unknown account: %1 !!!",$account_lid),15); } $GLOBALS['egw_info']['user'] = array( 'account_id' => $account_id, @@ -421,20 +407,20 @@ function do_subscribe_other($account_lid,$pw=null) $emailadmin = new bo(); $user_profile = $emailadmin->getUserProfile('felamimail'); unset($emailadmin); - + $icServer = new cyrusimap(); //$icServer =& $user_profile->ic_server[0]; //print_r($icServer); - + $icServer->openConnection(!$pw); - + $delimiter = $icServer->getHierarchyDelimiter(); $mailboxes = $icServer->getMailboxes(); //print_r($mailboxes); - + $own_mbox = 'user'.$delimiter.$account_lid; - + foreach($mailboxes as $n => $mailbox) { // if ($n < 1) continue; diff --git a/admin/setup/egw_de.lang b/admin/setup/egw_de.lang index 12a6dda069..aa5bea5d58 100644 --- a/admin/setup/egw_de.lang +++ b/admin/setup/egw_de.lang @@ -232,6 +232,7 @@ error: %1 not found or other error !!! admin de Fehler: %1 nicht gefunden oder a expires admin de abgelaufen explanation of ldapman admin de Dieses Modul ist derzeit nur für folgende Konfiguration getestet: Postfix, LDAP, Courier-Imap, Procmail und erfordert die Schemas: core und qmail(OID 7914). Weitere Konfigurationshinweise sind im README.ldapman im DOC Verzeichnis des Moduls ADMIN zu finden. fallback (after each pageview) admin de Ausweichmöglichkeit (nach jedem Seitenaufbau) +false admin de Falsch field '%1' already exists !!! admin de Feld '%1' existiert bereits !!! file space admin de Dateiraum file space must be an integer admin de Speicherplatz muss eine Zahl sein @@ -398,6 +399,7 @@ server type(mode) admin de Server-Typ (Modus) server url admin de Server-URL server username admin de Server-Benutzername set preference values. admin de Einstellungswert wurde geändert +should exceptions contain a trace (including function arguments) admin de Sollen Ausnahmefehler eine Rückverfolgung enthalten (einschl. Funktionsargumente) should the login page include a language selectbox (useful for demo-sites) ? admin de Soll die Anmeldeseite eine Sprachauswahl beinhalten (nützlich für Demosites) ? show 'powered by' logo on admin de Zeige "powered by" Logo show access log admin de Zugangsprotokoll anzeigen @@ -450,6 +452,7 @@ times admin de Zeiten top admin de oben total of %1 id's changed. admin de Die Gesamtanzahl von %1 Ids wurde geändert. total records admin de Anzahl Datensätze insgesamt +true admin de Wahr trust level admin de Grad des Vertrauens trust relationship admin de Vertrauensverhältnis type '%1' already exists !!! admin de Typ '%1' existiert bereits !!! diff --git a/admin/setup/egw_en.lang b/admin/setup/egw_en.lang index 88f5b9e622..6c9e976dc8 100644 --- a/admin/setup/egw_en.lang +++ b/admin/setup/egw_en.lang @@ -397,6 +397,7 @@ server type(mode) admin en Server Type(mode) server url admin en Server URL server username admin en Server Username set preference values. admin en Set preference values. +should exceptions contain a trace (including function arguments) admin en Should exceptions contain a trace (including function arguments) should the login page include a language selectbox (useful for demo-sites) ? admin en Should the login page include a language selectbox (useful for demo-sites) ? show 'powered by' logo on admin en Show 'powered by' logo on show access log admin en Show access log diff --git a/admin/templates/default/config.tpl b/admin/templates/default/config.tpl index 95b92854b7..f8a4c80ece 100644 --- a/admin/templates/default/config.tpl +++ b/admin/templates/default/config.tpl @@ -115,7 +115,7 @@ - + {lang_Enable_the_spellcheck_in_the_ritch_text_editor_?}: @@ -124,7 +124,7 @@ - + {lang_Complete_path_to_aspell_program}: @@ -213,21 +213,21 @@ - + {lang_After_how_many_unsuccessful_attempts_to_login,_an_IP_should_be_blocked_(default_3)_?}: - + {lang_How_many_minutes_should_an_account_or_IP_be_blocked_(default_30)_?}: - + {lang_Admin_email_addresses_(comma-separated)_to_be_notified_about_the_blocking_(empty_for_no_notify)}: @@ -273,6 +273,16 @@ {lang_Allow_remote_administration_from_following_install_ID's_(comma_separated)}:
{lang_Own_install_ID:_}{value_install_id} + + {lang_Should_exceptions_contain_a_trace_(including_function_arguments)}: + + + + + diff --git a/groupdav.php b/groupdav.php index 3ec58c02c0..698a87d0a4 100644 --- a/groupdav.php +++ b/groupdav.php @@ -13,6 +13,8 @@ * @version $Id$ */ +$starttime = microtime(true); + /** * check if the given user has access * @@ -41,9 +43,11 @@ function check_access(&$account) } //error_log("GroupDAV PHP_AUTH_USER={$_SERVER['PHP_AUTH_USER']}, HTTP_USER_AGENT={$_SERVER['HTTP_USER_AGENT']} --> no_session=".(int)$no_session); - if (!($sessionid = $GLOBALS['egw']->session->create($account,'','',$no_session))) + if (!isset($_SERVER['PHP_AUTH_USER']) || !($sessionid = $GLOBALS['egw']->session->create($account,'','',$no_session))) { - header('WWW-Authenticate: Basic realm="'.groupdav::REALM.'"'); + header('WWW-Authenticate: Basic realm="'.groupdav::REALM. + // if the session class gives a reason why the login failed --> append it to the REALM + ($GLOBALS['egw']->session->reason ? ': '.$GLOBALS['egw']->session->reason : '').'"'); header('HTTP/1.1 401 Unauthorized'); header('X-WebDAV-Status: 401 Unauthorized', true); exit; @@ -55,9 +59,13 @@ $GLOBALS['egw_info']['flags'] = array( 'noheader' => True, 'currentapp' => 'groupdav', 'autocreate_session_callback' => 'check_access', + 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) ); // if you move this file somewhere else, you need to adapt the path to the header! include(dirname(__FILE__).'/header.inc.php'); +$headertime = microtime(true); + $groupdav = new groupdav(); $groupdav->ServeRequest(); +//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime)); diff --git a/phpgwapi/inc/common_functions.inc.php b/phpgwapi/inc/common_functions.inc.php index 4762488c9a..33b732be36 100755 --- a/phpgwapi/inc/common_functions.inc.php +++ b/phpgwapi/inc/common_functions.inc.php @@ -1404,21 +1404,48 @@ function egw_exception_handler(Exception $e) { $headline = try_lang('An error happend'); } - $message = '

'.$headline."

\n". - '
'.$e->getMessage()."\n\n".
-		$e->getTraceAsString()."
\n"; + // logging all exceptions to the error_log + error_log($headline.': '.$e->getMessage()); + foreach(explode("\n",$e->getTraceAsString()) as $line) error_log($line); - if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link')) + // exception handler for cli (command line interface) clients, no html + if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli') { - $message .= '

'.try_lang('Click here to resume your eGroupWare Session.').'

'; + echo $headline.': '.$e->getMessage()."\n"; + echo $e->getTraceAsString()."\n"; + exit($e->getCode() ? $e->getCode() : 9999); // allways give a non-zero exit code } - if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->framework)) + // regular GUI exception + elseif (!isset($GLOBALS['egw_info']['flags']['no_exception_handler'])) { - $GLOBALS['egw']->framework->render($message,$headline); + $message = '

'.$headline."

\n". + '
'.$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 .= $e->getTraceAsString();
+		}
+		$message .= "
\n"; + if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link')) + { + $message .= '

'.try_lang('Click here to resume your eGroupWare Session.').'

'; + } + if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->framework)) + { + $GLOBALS['egw']->framework->render($message,$headline); + } + else + { + echo "\n\n$headline\n\n\n$message\n\n\n"; + } } - else + // exception handler sending message back to the client as basic auth message + elseif($GLOBALS['egw_info']['flags']['no_exception_handler'] == 'basic_auth') { - echo "\n\n$headline\n\n\n$message\n\n\n"; + header('WWW-Authenticate: Basic realm="'.$headline.' '.$e->getMessage().'"'); + header('HTTP/1.1 401 Unauthorized'); + header('X-WebDAV-Status: 401 Unauthorized', true); } if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->common)) { @@ -1427,7 +1454,7 @@ function egw_exception_handler(Exception $e) exit; } -if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']) || !$GLOBALS['egw_info']['flags']['no_exception_handler']) +if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] !== true) { set_exception_handler('egw_exception_handler'); } diff --git a/setup/setup-cli.php b/setup/setup-cli.php index e0f77ca84d..b4bb2428ef 100755 --- a/setup/setup-cli.php +++ b/setup/setup-cli.php @@ -41,27 +41,11 @@ $GLOBALS['egw_info'] = array( 'flags' => array( 'currentapp' => 'home', 'noapi' => true, + 'no_exception_handler' => 'cli', )); include('inc/functions.inc.php'); -$GLOBALS['egw_setup']->translation->no_translation_marker = ''; $GLOBALS['egw_setup']->system_charset = $charset; -/** - * Echo the exception message and exit the script with a numeric code, does NOT return - * - * @param Exception $e - */ -function cli_exception_handler(Exception $e) -{ - echo $e->getMessage()."\n"; -// if ($e instanceof egw_exception_assertion_failed && !($e instanceof egw_exception_wrong_userinput)) - { - echo $e->getTraceAsString()."\n"; - } - exit($e->getCode() ? $e->getCode() : 9999); // always give a non-zero exist status -} -set_exception_handler('cli_exception_handler'); - if ((float) PHP_VERSION < $GLOBALS['egw_setup']->required_php_version) { fail(98,lang('You are using PHP version %1. eGroupWare now requires %2 or later, recommended is PHP %3.',PHP_VERSION,$GLOBALS['egw_setup']->required_php_version,$GLOBALS['egw_setup']->recommended_php_version)); diff --git a/webdav.php b/webdav.php index 06e1623537..afaa56fbf9 100644 --- a/webdav.php +++ b/webdav.php @@ -13,6 +13,8 @@ * @version $Id$ */ +$starttime = microtime(true); + /** * check if the given user has access * @@ -28,12 +30,14 @@ function check_access(&$account) 'passwd' => $_SERVER['PHP_AUTH_PW'], 'passwd_type' => 'text', ); - if (!($sessionid = $GLOBALS['egw']->session->create($account))) + if (!isset($_SERVER['PHP_AUTH_USER']) || !($sessionid = $GLOBALS['egw']->session->create($account))) { - header('WWW-Authenticate: Basic realm="'.vfs_webdav_server::REALM.'"'); - header("HTTP/1.1 401 Unauthorized"); - header("X-WebDAV-Status: 401 Unauthorized", true); - exit; + header('WWW-Authenticate: Basic realm="'.vfs_webdav_server::REALM. + // if the session class gives a reason why the login failed --> append it to the REALM + ($GLOBALS['egw']->session->reason ? ': '.$GLOBALS['egw']->session->reason : '').'"'); + header("HTTP/1.1 401 Unauthorized"); + header("X-WebDAV-Status: 401 Unauthorized", true); + exit; } return $sessionid; } @@ -48,10 +52,14 @@ $GLOBALS['egw_info'] = array( 'noheader' => True, 'currentapp' => $app, 'autocreate_session_callback' => 'check_access', + 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) ) ); // if you move this file somewhere else, you need to adapt the path to the header! include(dirname(__FILE__).'/header.inc.php'); +$headertime = microtime(true); + $webdav_server = new vfs_webdav_server(); -$webdav_server->ServeRequest(); \ No newline at end of file +$webdav_server->ServeRequest(); +//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime));