2 /**********************************************************************
3 Copyright (C) FrontAccounting, LLC.
4 Released under the terms of the GNU General Public License, GPL,
5 as published by the Free Software Foundation, either version 3
6 of the License, or (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
15 function sessionStart($name, $limit = 0, $path = '/', $domain = null, $secure = null)
17 // Set the cookie name
21 $https = isset($secure) ? $secure : (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
23 // Set session cookie options
24 if (version_compare(PHP_VERSION, '5.2', '<')) // avoid failure on older php versions
25 session_set_cookie_params($limit, $path, $domain, $https);
27 session_set_cookie_params($limit, $path, $domain, $https, true);
31 // Make sure the session hasn't expired, and destroy it if it has
32 if ($this->validateSession())
34 // Check to see if the session is new or a hijacking attempt
35 if(!$this->preventHijacking())
37 // Reset session data and regenerate id
39 $_SESSION['IPaddress'] = $_SERVER['REMOTE_ADDR'];
40 $_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
41 $this->regenerateSession();
43 // Give a 5% chance of the session id changing on any request
45 elseif (rand(1, 100) <= 5)
47 $this->regenerateSession();
58 function preventHijacking()
60 if (!isset($_SESSION['IPaddress']) || !isset($_SESSION['userAgent']))
63 if ($_SESSION['IPaddress'] != $_SERVER['REMOTE_ADDR'])
66 if ( $_SESSION['userAgent'] != $_SERVER['HTTP_USER_AGENT'])
72 function regenerateSession()
74 // If this session is obsolete it means there already is a new id
75 if (isset($_SESSION['OBSOLETE']) && ($_SESSION['OBSOLETE'] == true))
78 // Set current session to expire in 10 seconds
79 $_SESSION['OBSOLETE'] = true;
80 $_SESSION['EXPIRES'] = time() + 10;
82 // Create new session without destroying the old one
83 session_regenerate_id();
84 // Grab current session ID and close both sessions to allow other scripts to use them
85 $newSession = session_id();
86 session_write_close();
87 // Set session ID to the new one, and start it back up again
89 session_id($newSession);
92 // Now we unset the obsolete and expiration values for the session we want to keep
93 unset($_SESSION['OBSOLETE']);
94 unset($_SESSION['EXPIRES']);
97 function validateSession()
99 if (isset($_SESSION['OBSOLETE']) && !isset($_SESSION['EXPIRES']) )
102 if (isset($_SESSION['EXPIRES']) && $_SESSION['EXPIRES'] < time())
109 function output_html($text)
111 global $before_box, $Ajax, $messages;
112 // Fatal errors are not send to error_handler,
113 // so we must check the output
114 if ($text && preg_match('/\bFatal error(<.*?>)?:(.*)/i', $text, $m)) {
115 $Ajax->aCommands = array(); // Don't update page via ajax on errors
116 $text = preg_replace('/\bFatal error(<.*?>)?:(.*)/i','', $text);
117 $messages[] = array(E_ERROR, $m[2], null, null);
120 return in_ajax() ? fmt_errors() : ($before_box.fmt_errors().$text);
122 //----------------------------------------------------------------------------------------
124 function kill_login()
129 //----------------------------------------------------------------------------------------
131 function login_fail()
133 global $path_to_root;
135 header("HTTP/1.1 401 Authorization Required");
136 echo "<center><br><br><font size='5' color='red'><b>" . _("Incorrect Password") . "<b></font><br><br>";
137 echo "<b>" . _("The user and password combination is not valid for the system.") . "<b><br><br>";
139 echo _("If you are not an authorized user, please contact your system administrator to obtain an account to enable you to use the system.");
140 echo "<br><a href='$path_to_root/index.php'>" . _("Try again") . "</a>";
147 function check_faillog()
149 global $login_delay, $login_faillog, $login_max_attempts;
151 $user = $_SESSION["wa_current_user"]->user;
153 if (@$login_delay && (@$login_faillog[$user][$_SERVER['REMOTE_ADDR']] >= @$login_max_attempts) && (time() < $login_faillog[$user]['last'] + $login_delay))
159 Simple brute force attack detection is performed before connection to company database is open. Therefore access counters have to be stored in file.
160 Login attempts counter is created for every new user IP, which partialy prevent DOS attacks.
162 function write_login_filelog($login, $result)
164 global $login_faillog, $login_max_attempts, $path_to_root;
166 $user = $_SESSION["wa_current_user"]->user;
168 $ip = $_SERVER['REMOTE_ADDR'];
170 if (!isset($login_faillog[$user][$ip]) || $result) // init or reset on successfull login
171 $login_faillog[$user] = array($ip => 0, 'last' => '');
175 if ($login_faillog[$user][$ip] < @$login_max_attempts) {
177 $login_faillog[$user][$ip]++;
179 $login_faillog[$user][$ip] = 0; // comment out to restart counter only after successfull login.
180 error_log(sprintf(_("Brute force attack on account '%s' detected. Access for non-logged users temporarily blocked." ), $login));
182 $login_faillog[$user]['last'] = time();
187 $msg .= "Login attempts info.\n";
189 $msg .= "\$login_faillog = " .var_export($login_faillog, true). ";\n";
191 $filename = $path_to_root."/tmp/faillog.php";
193 if ((!file_exists($filename) && is_writable($path_to_root)) || is_writable($filename))
195 file_put_contents($filename, $msg);
199 //----------------------------------------------------------------------------------------
201 function check_page_security($page_security)
207 if (!$_SESSION["wa_current_user"]->check_user_access())
209 // notification after upgrade from pre-2.2 version
210 $msg = $_SESSION["wa_current_user"]->old_db ?
211 _("Security settings have not been defined for your user account.")
212 . "<br>" . _("Please contact your system administrator.")
213 : _("Please remove \$security_groups and \$security_headings arrays from config.php file!");
214 } elseif (!$_SESSION['SysPrefs']->db_ok && !$_SESSION["wa_current_user"]->can_access('SA_SOFTWAREUPGRADE')) {
215 $msg = _('Access to application has been blocked until database upgrade is completed by system administrator.');
220 end_page(@$_REQUEST['popup']);
225 if (!$_SESSION["wa_current_user"]->can_access_page($page_security))
228 echo "<center><br><br><br><b>";
229 echo _("The security settings on your account do not permit you to access this function");
231 echo "<br><br><br><br></center>";
232 end_page(@$_REQUEST['popup']);
235 if (!$_SESSION['SysPrefs']->db_ok
236 && !in_array($page_security, array('SA_SOFTWAREUPGRADE', 'SA_OPEN', 'SA_BACKUP')))
238 display_error(_('System is blocked after source upgrade until database is updated on System/Software Upgrade page'));
245 Helper function for setting page security level depeding on
246 GET start variable and/or some value stored in session variable.
247 Before the call $page_security should be set to default page_security value.
249 function set_page_security($value=null, $trans = array(), $gtrans = array())
251 global $page_security;
253 // first check is this is not start page call
254 foreach($gtrans as $key => $area)
255 if (isset($_GET[$key])) {
256 $page_security = $area;
260 // then check session value
261 if (isset($trans[$value])) {
262 $page_security = $trans[$value];
267 //-----------------------------------------------------------------------------
268 // Removing magic quotes from nested arrays/variables
270 function strip_quotes($data)
272 if(get_magic_quotes_gpc()) {
273 if(is_array($data)) {
274 foreach($data as $k => $v) {
275 $data[$k] = strip_quotes($data[$k]);
278 return stripslashes($data);
283 function html_cleanup(&$parms)
285 foreach($parms as $name => $value) {
286 // $value = @html_entity_decode($value, ENT_QUOTES, $_SESSION['language']->encoding);
287 if (is_array($value))
288 html_cleanup($parms[$name]);
290 $parms[$name] = @htmlspecialchars($value, ENT_QUOTES, $_SESSION['language']->encoding);
292 reset($parms); // needed for direct key() usage later throughout the sources
295 //============================================================================
298 function login_timeout()
300 // skip timeout on logout page
301 if ($_SESSION["wa_current_user"]->logged) {
302 $tout = $_SESSION["wa_current_user"]->timeout;
303 if ($tout && (time() > $_SESSION["wa_current_user"]->last_act + $tout))
305 $_SESSION["wa_current_user"]->logged = false;
307 $_SESSION["wa_current_user"]->last_act = time();
310 //============================================================================
311 if (!isset($path_to_root))
316 //----------------------------------------------------------------------------------------
317 // set to reasonable values if not set in config file (pre-2.3.12 installations)
319 if ((!isset($login_delay)) || ($login_delay < 0))
322 if ((!isset($login_max_attempts)) || ($login_max_attempts < 0))
323 $login_max_attempts = 3;
326 // Prevent register_globals vulnerability
327 if (isset($_GET['path_to_root']) || isset($_POST['path_to_root']))
328 die("Restricted access");
330 include_once($path_to_root . "/includes/errors.inc");
331 // colect all error msgs
332 set_error_handler('error_handler' /*, errtypes */);
334 include_once($path_to_root . "/includes/current_user.inc");
335 include_once($path_to_root . "/frontaccounting.php");
336 include_once($path_to_root . "/admin/db/security_db.inc");
337 include_once($path_to_root . "/includes/lang/language.php");
338 include_once($path_to_root . "/config_db.php");
339 include_once($path_to_root . "/includes/ajax.inc");
340 include_once($path_to_root . "/includes/ui/ui_msgs.inc");
341 include_once($path_to_root . "/includes/prefs/sysprefs.inc");
343 include_once($path_to_root . "/includes/hooks.inc");
345 // include all extensions hook files.
347 foreach ($installed_extensions as $ext)
349 if (file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
350 include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
354 Uncomment the setting below when using FA on shared hosting
355 to avoid unexpeced session timeouts.
356 Make sure this directory exists and is writable!
358 // ini_set('session.save_path', dirname(__FILE__).'/../tmp/');
360 ini_set('session.gc_maxlifetime', 36000); // 10hrs
362 $Session_manager = new SessionManager();
363 $Session_manager->sessionStart('FA'.md5(dirname(__FILE__)));
365 // this is to fix the "back-do-you-want-to-refresh" issue - thanx PHPFreaks
366 header("Cache-control: private");
368 include_once($path_to_root . "/config.php");
371 if ($login_delay > 0)
372 @include_once($path_to_root . "/tmp/faillog.php");
374 // Page Initialisation
375 if (!isset($_SESSION['language']) || !method_exists($_SESSION['language'], 'set_language'))
377 $l = array_search_value($dflt_lang, $installed_languages, 'code');
378 $_SESSION['language'] = new language($l['name'], $l['code'], $l['encoding'],
379 (isset($l['rtl']) && $l['rtl'] === true) ? 'rtl' : 'ltr');
382 $_SESSION['language']->set_language($_SESSION['language']->code);
385 include_once($path_to_root . "/includes/access_levels.inc");
386 include_once($path_to_root . "/version.php");
387 include_once($path_to_root . "/includes/main.inc");
389 // Ajax communication object
392 // js/php validation rules container
394 // bindings for editors
396 // page help. Currently help for function keys.
399 $Refs = new references();
401 // intercept all output to destroy it in case of ajax call
402 register_shutdown_function('end_flush');
403 ob_start('output_html',0);
405 if (!isset($_SESSION["wa_current_user"]))
406 $_SESSION["wa_current_user"] = new current_user();
409 html_cleanup($_POST);
410 html_cleanup($_REQUEST);
411 html_cleanup($_SERVER);
413 // logout.php is the only page we should have always
414 // accessable regardless of access level and current login status.
415 if (!defined('FA_LOGOUT_PHP_FILE')){
419 if (!$_SESSION["wa_current_user"]->old_db)
420 include_once($path_to_root . '/company/'.user_company().'/installed_extensions.php');
424 if (!$_SESSION["wa_current_user"]->logged_in())
427 if (!isset($_POST["user_name_entry_field"]) or $_POST["user_name_entry_field"] == "")
429 // strip ajax marker from uri, to force synchronous page reload
430 $_SESSION['timeout'] = array( 'uri'=>preg_replace('/JsHttpRequest=(?:(\d+)-)?([^&]+)/s',
431 '', @htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, $_SESSION['language']->encoding)),
434 include($path_to_root . "/access/login.php");
436 $Ajax->activate('_page_body');
439 if (isset($_POST["company_login_nickname"]) && !isset($_POST["company_login_name"])) {
440 for ($i = 0; $i < count($db_connections); $i++) {
441 if ($db_connections[$i]["name"] == $_POST["company_login_nickname"]) {
442 $_POST["company_login_name"] = $i;
443 unset($_POST["company_login_nickname"]);
444 break 1; // cannot pass variables to break from PHP v5.4 onwards
448 $succeed = isset($db_connections[$_POST["company_login_name"]]) &&
449 $_SESSION["wa_current_user"]->login($_POST["company_login_name"],
450 $_POST["user_name_entry_field"], $_POST["password"]);
451 // select full vs fallback ui mode on login
452 $_SESSION["wa_current_user"]->ui_mode = $_POST['ui_mode'];
455 // Incorrect password
458 elseif(isset($_SESSION['timeout']) && !$_SESSION['timeout']['post'])
460 // in case of GET request redirect to avoid confirmation dialog
461 // after return from menu option
462 header("HTTP/1.1 303 See Other");
463 header("Location: ".$_SESSION['timeout']['uri']);
466 $lang = &$_SESSION['language'];
467 $lang->set_language($_SESSION['language']->code);
470 set_global_connection();
472 if (!isset($_SESSION["App"])) {
473 $_SESSION["App"] = new front_accounting();
474 $_SESSION["App"]->init();
478 $SysPrefs = &$_SESSION['SysPrefs'];
480 // POST vars cleanup needed for direct reuse.
481 // We quote all values later with db_escape() before db update.
482 $_POST = strip_quotes($_POST);