2 /*********************************************************************************
3 * SugarCRM is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
38 * Created on Mar 21, 2007
40 * To change the template for this generated file go to
41 * Window - Preferences - PHPeclipse - PHP - Code Templates
43 require_once('include/MVC/Controller/ControllerFactory.php');
44 require_once('include/MVC/View/ViewFactory.php');
46 class SugarApplication
48 var $controller = null;
49 var $headerDisplayed = false;
50 var $default_module = 'Home';
51 var $default_action = 'index';
53 function SugarApplication()
57 * Perform execution of the application. This method is called from index2.php
61 if(!empty($sugar_config['default_module']))
62 $this->default_module = $sugar_config['default_module'];
63 $module = $this->default_module;
64 if(!empty($_REQUEST['module']))$module = $_REQUEST['module'];
65 insert_charset_header();
67 $this->controller = ControllerFactory::getController($module);
68 // if the entry point is defined to not need auth, then don't authenicate
69 if( empty($_REQUEST['entryPoint'])
70 || $this->controller->checkEntryPointRequiresAuth($_REQUEST['entryPoint']) ){
74 $this->controller->preProcess();
77 SugarThemeRegistry::buildRegistry();
78 $this->loadLanguages();
79 $this->checkDatabaseVersion();
80 $this->loadDisplaySettings();
83 $this->setupResourceManagement($module);
84 $this->checkHTTPReferer();
85 $this->controller->execute();
90 * Load the authenticated user. If there is not an authenticated user then redirect to login screen.
93 global $authController, $sugar_config;
94 // Double check the server's unique key is in the session. Make sure this is not an attempt to hijack a session
95 $user_unique_key = (isset($_SESSION['unique_key'])) ? $_SESSION['unique_key'] : '';
96 $server_unique_key = (isset($sugar_config['unique_key'])) ? $sugar_config['unique_key'] : '';
97 $allowed_actions = (!empty($this->controller->allowed_actions)) ? $this->controller->allowed_actions : $allowed_actions = array('Authenticate', 'Login',);
99 if(($user_unique_key != $server_unique_key) && (!in_array($this->controller->action, $allowed_actions)) &&
100 (!isset($_SESSION['login_error'])))
103 $post_login_nav = '';
105 if(!empty($this->controller->module)){
106 $post_login_nav .= '&login_module='.$this->controller->module;
108 if(!empty($this->controller->action)){
109 if(in_array(strtolower($this->controller->action), array('delete')))
110 $post_login_nav .= '&login_action=DetailView';
111 elseif(in_array(strtolower($this->controller->action), array('save')))
112 $post_login_nav .= '&login_action=EditView';
113 elseif(isset($_REQUEST['massupdate'])|| isset($_GET['massupdate']) || isset($_POST['massupdate']))
114 $post_login_nav .= '&login_action=index';
116 $post_login_nav .= '&login_action='.$this->controller->action;
118 if(!empty($this->controller->record)){
119 $post_login_nav .= '&login_record='.$this->controller->record;
122 header('Location: index.php?action=Login&module=Users'.$post_login_nav);
126 $authController = new AuthenticationController((!empty($GLOBALS['sugar_config']['authenticationClass'])? $GLOBALS['sugar_config']['authenticationClass'] : 'SugarAuthenticate'));
127 $GLOBALS['current_user'] = new User();
128 if(isset($_SESSION['authenticated_user_id'])){
129 // set in modules/Users/Authenticate.php
130 if(!$authController->sessionAuthenticate()){
131 // if the object we get back is null for some reason, this will break - like user prefs are corrupted
132 $GLOBALS['log']->fatal('User retrieval for ID: ('.$_SESSION['authenticated_user_id'].') does not exist in database or retrieval failed catastrophically. Calling session_destroy() and sending user to Login page.');
134 SugarApplication::redirect('index.php?action=Login&module=Users');
137 }elseif(!($this->controller->module == 'Users' && in_array($this->controller->action, $allowed_actions))){
139 SugarApplication::redirect('index.php?action=Login&module=Users');
142 $GLOBALS['log']->debug('Current user is: '.$GLOBALS['current_user']->user_name);
145 if(isset($_SESSION['authenticated_user_id'])){
146 $GLOBALS['log']->debug("setting cookie ck_login_id_20 to ".$_SESSION['authenticated_user_id']);
147 self::setCookie('ck_login_id_20', $_SESSION['authenticated_user_id'], time() + 86400 * 90);
149 if(isset($_SESSION['authenticated_user_theme'])){
150 $GLOBALS['log']->debug("setting cookie ck_login_theme_20 to ".$_SESSION['authenticated_user_theme']);
151 self::setCookie('ck_login_theme_20', $_SESSION['authenticated_user_theme'], time() + 86400 * 90);
153 if(isset($_SESSION['authenticated_user_theme_color'])){
154 $GLOBALS['log']->debug("setting cookie ck_login_theme_color_20 to ".$_SESSION['authenticated_user_theme_color']);
155 self::setCookie('ck_login_theme_color_20', $_SESSION['authenticated_user_theme_color'], time() + 86400 * 90);
157 if(isset($_SESSION['authenticated_user_theme_font'])){
158 $GLOBALS['log']->debug("setting cookie ck_login_theme_font_20 to ".$_SESSION['authenticated_user_theme_font']);
159 self::setCookie('ck_login_theme_font_20', $_SESSION['authenticated_user_theme_font'], time() + 86400 * 90);
161 if(isset($_SESSION['authenticated_user_language'])){
162 $GLOBALS['log']->debug("setting cookie ck_login_language_20 to ".$_SESSION['authenticated_user_language']);
163 self::setCookie('ck_login_language_20', $_SESSION['authenticated_user_language'], time() + 86400 * 90);
165 //check if user can access
169 function ACLFilter(){
170 ACLController :: filterModuleList($GLOBALS['moduleList']);
171 ACLController :: filterModuleList($GLOBALS['modInvisListActivities']);
175 * setupResourceManagement
176 * This function initialize the ResourceManager and calls the setup method
177 * on the ResourceManager instance.
180 function setupResourceManagement($module) {
181 require_once('include/resource/ResourceManager.php');
182 $resourceManager = ResourceManager::getInstance();
183 $resourceManager->setup($module);
186 function setupPrint() {
187 $GLOBALS['request_string'] = '';
189 // merge _GET and _POST, but keep the results local
190 // this handles the issues where values come in one way or the other
191 // without affecting the main super globals
192 $merged = array_merge($_GET, $_POST);
193 foreach ($merged as $key => $val)
197 foreach ($val as $k => $v)
199 $GLOBALS['request_string'] .= urlencode($key).'[]='.urlencode($v).'&';
204 $GLOBALS['request_string'] .= urlencode($key).'='.urlencode($val).'&';
207 $GLOBALS['request_string'] .= 'print=true';
210 function preProcess(){
211 $config = new Administration;
212 $config->retrieveSettings();
213 if(!empty($_SESSION['authenticated_user_id'])){
214 if(isset($_SESSION['hasExpiredPassword']) && $_SESSION['hasExpiredPassword'] == '1'){
215 if( $this->controller->action!= 'Save' && $this->controller->action != 'Logout') {
216 $this->controller->module = 'Users';
217 $this->controller->action = 'ChangePassword';
218 $record = $GLOBALS['current_user']->id;
220 $this->handleOfflineClient();
223 $ut = $GLOBALS['current_user']->getPreference('ut');
225 && $this->controller->action != 'AdminWizard'
226 && $this->controller->action != 'EmailUIAjax'
227 && $this->controller->action != 'Wizard'
228 && $this->controller->action != 'SaveAdminWizard'
229 && $this->controller->action != 'SaveUserWizard'
230 && $this->controller->action != 'SaveTimezone'
231 && $this->controller->action != 'Logout') {
232 $this->controller->module = 'Users';
233 $this->controller->action = 'SetTimezone';
234 $record = $GLOBALS['current_user']->id;
236 if($this->controller->action != 'AdminWizard'
237 && $this->controller->action != 'EmailUIAjax'
238 && $this->controller->action != 'Wizard'
239 && $this->controller->action != 'SaveAdminWizard'
240 && $this->controller->action != 'SaveUserWizard'){
241 $this->handleOfflineClient();
246 $this->handleAccessControl();
249 function handleOfflineClient(){
250 if(isset($GLOBALS['sugar_config']['disc_client']) && $GLOBALS['sugar_config']['disc_client']){
251 if(isset($_REQUEST['action']) && $_REQUEST['action'] != 'SaveTimezone'){
252 if (!file_exists('modules/Sync/file_config.php')){
253 if($_REQUEST['action'] != 'InitialSync' && $_REQUEST['action'] != 'Logout' &&
254 ($_REQUEST['action'] != 'Popup' && $_REQUEST['module'] != 'Sync')){
255 //echo $_REQUEST['action'];
257 $this->controller->module = 'Sync';
258 $this->controller->action = 'InitialSync';
261 require_once ('modules/Sync/file_config.php');
262 if(isset($file_sync_info['is_first_sync']) && $file_sync_info['is_first_sync']){
263 if($_REQUEST['action'] != 'InitialSync' && $_REQUEST['action'] != 'Logout' &&
264 ( $_REQUEST['action'] != 'Popup' && $_REQUEST['module'] != 'Sync')){
265 $this->controller->module = 'Sync';
266 $this->controller->action = 'InitialSync';
271 global $moduleList, $sugar_config, $sync_modules;
272 require_once('modules/Sync/SyncController.php');
273 $GLOBALS['current_user']->is_admin = '0'; //No admins for disc client
278 * Handles everything related to authorization.
280 function handleAccessControl(){
281 if(is_admin($GLOBALS['current_user']) || is_admin_for_any_module($GLOBALS['current_user']))
283 if(!empty($_REQUEST['action']) && $_REQUEST['action']=="RetrieveEmail")
285 if(!is_admin($GLOBALS['current_user']) && !empty($GLOBALS['adminOnlyList'][$this->controller->module])
286 && !empty($GLOBALS['adminOnlyList'][$this->controller->module]['all'])
287 && (empty($GLOBALS['adminOnlyList'][$this->controller->module][$this->controller->action]) || $GLOBALS['adminOnlyList'][$this->controller->module][$this->controller->action] != 'allow')) {
288 $this->controller->hasAccess = false;
292 if(!empty($GLOBALS['current_user']) && empty($GLOBALS['modListHeader']))
293 $GLOBALS['modListHeader'] = query_module_access_list($GLOBALS['current_user']);
295 if(in_array($this->controller->module, $GLOBALS['modInvisList']) &&
296 ((in_array('Activities', $GLOBALS['moduleList']) &&
297 in_array('Calendar',$GLOBALS['moduleList'])) &&
298 in_array($this->controller->module, $GLOBALS['modInvisListActivities']))
300 $this->controller->hasAccess = false;
306 * Load only bare minimum of language that can be done before user init and MVC stuff
308 static function preLoadLanguages()
310 if(!empty($_SESSION['authenticated_user_language'])) {
311 $GLOBALS['current_language'] = $_SESSION['authenticated_user_language'];
314 $GLOBALS['current_language'] = $GLOBALS['sugar_config']['default_language'];
316 $GLOBALS['log']->debug('current_language is: '.$GLOBALS['current_language']);
317 //set module and application string arrays based upon selected language
318 $GLOBALS['app_strings'] = return_application_language($GLOBALS['current_language']);
322 * Load application wide languages as well as module based languages so they are accessible
325 function loadLanguages(){
326 if(!empty($_SESSION['authenticated_user_language'])) {
327 $GLOBALS['current_language'] = $_SESSION['authenticated_user_language'];
330 $GLOBALS['current_language'] = $GLOBALS['sugar_config']['default_language'];
332 $GLOBALS['log']->debug('current_language is: '.$GLOBALS['current_language']);
333 //set module and application string arrays based upon selected language
334 $GLOBALS['app_strings'] = return_application_language($GLOBALS['current_language']);
335 if(empty($GLOBALS['current_user']->id))$GLOBALS['app_strings']['NTC_WELCOME'] = '';
336 if(!empty($GLOBALS['system_config']->settings['system_name']))$GLOBALS['app_strings']['LBL_BROWSER_TITLE'] = $GLOBALS['system_config']->settings['system_name'];
337 $GLOBALS['app_list_strings'] = return_app_list_strings_language($GLOBALS['current_language']);
338 $GLOBALS['mod_strings'] = return_module_language($GLOBALS['current_language'], $this->controller->module);
341 * checkDatabaseVersion
342 * Check the db version sugar_version.php and compare to what the version is stored in the config table.
343 * Ensure that both are the same.
345 function checkDatabaseVersion($dieOnFailure = true)
347 $row_count = sugar_cache_retrieve('checkDatabaseVersion_row_count');
348 if ( empty($row_count) ) {
349 global $sugar_db_version;
350 $version_query = 'SELECT count(*) as the_count FROM config WHERE category=\'info\' AND name=\'sugar_version\'';
352 if($GLOBALS['db']->dbType == 'oci8'){
354 else if ($GLOBALS['db']->dbType == 'mssql'){
355 $version_query .= " AND CAST(value AS varchar(8000)) = '$sugar_db_version'";
358 $version_query .= " AND value = '$sugar_db_version'";
361 $result = $GLOBALS['db']->query($version_query);
362 $row = $GLOBALS['db']->fetchByAssoc($result, -1, true);
363 $row_count = $row['the_count'];
364 sugar_cache_put('checkDatabaseVersion_row_count', $row_count);
367 if($row_count == 0 && empty($GLOBALS['sugar_config']['disc_client'])){
368 $sugar_version = $GLOBALS['sugar_version'];
370 sugar_die("Sugar CRM $sugar_version Files May Only Be Used With A Sugar CRM $sugar_db_version Database.");
379 * Load the themes/images.
381 function loadDisplaySettings()
385 // load the user's default theme
386 $theme = $GLOBALS['current_user']->getPreference('user_theme');
388 if (is_null($theme)) {
389 $theme = $GLOBALS['sugar_config']['default_theme'];
390 if(!empty($_SESSION['authenticated_user_theme'])){
391 $theme = $_SESSION['authenticated_user_theme'];
393 else if(!empty($_COOKIE['sugar_user_theme'])){
394 $theme = $_COOKIE['sugar_user_theme'];
397 if(isset($_SESSION['authenticated_user_theme']) && $_SESSION['authenticated_user_theme'] != '') {
398 $_SESSION['theme_changed'] = false;
402 if(!is_null($theme) && !headers_sent())
404 setcookie('sugar_user_theme', $theme, time() + 31536000); // expires in a year
407 SugarThemeRegistry::set($theme);
408 require_once('include/utils/layout_utils.php');
409 $GLOBALS['image_path'] = SugarThemeRegistry::current()->getImagePath().'/';
410 if ( defined('TEMPLATE_URL') )
411 $GLOBALS['image_path'] = TEMPLATE_URL . '/'. $GLOBALS['image_path'];
413 if ( isset($GLOBALS['current_user']) ) {
414 $GLOBALS['gridline'] = (int) ($GLOBALS['current_user']->getPreference('gridline') == 'on');
415 $GLOBALS['current_user']->setPreference('user_theme', $theme, 0, 'global');
419 function loadLicense(){
421 global $user_unique_key, $server_unique_key;
422 $user_unique_key = (isset($_SESSION['unique_key'])) ? $_SESSION['unique_key'] : '';
423 $server_unique_key = (isset($sugar_config['unique_key'])) ? $sugar_config['unique_key'] : '';
426 function loadGlobals(){
427 global $currentModule;
428 $currentModule = $this->controller->module;
429 if($this->controller->module == $this->default_module){
430 $_REQUEST['module'] = $this->controller->module;
431 if(empty($_REQUEST['action']))
432 $_REQUEST['action'] = $this->default_action;
437 * Checks a request to ensure the request is coming from a valid source or it is for one of the white listed actions
439 function checkHTTPReferer(){
440 global $sugar_config;
441 $whiteListActions = (!empty($sugar_config['http_referer']['actions']))?$sugar_config['http_referer']['actions']:array('index', 'ListView', 'DetailView', 'EditView');
442 // Bug 39691 - Make sure localhost and 127.0.0.1 are always valid HTTP referers
443 $whiteListReferers = array('127.0.0.1','localhost');
444 if(!empty($_SERVER['SERVER_ADDR']))$whiteListReferers[] = $_SERVER['SERVER_ADDR'];
445 if ( !empty($sugar_config['http_referer']['list']) ) {
446 $whiteListReferers = array_merge($whiteListReferers,$sugar_config['http_referer']['list']);
448 if(!empty($_SERVER['HTTP_REFERER']) && !empty($_SERVER['SERVER_NAME'])){
449 $http_ref = parse_url($_SERVER['HTTP_REFERER']);
450 if($http_ref['host'] !== $_SERVER['SERVER_NAME'] && !in_array($this->controller->action, $whiteListActions) &&
451 (empty($whiteListReferers) || !in_array($http_ref['host'], $whiteListReferers))){
452 header("Cache-Control: no-cache, must-revalidate");
453 $whiteListActions[] = $this->controller->action;
454 $whiteListString = "'" . implode("', '", $whiteListActions) . "'";
457 <div align='center' style='background:lightgray'>
458 <h3 style='color:red'>Possible Cross Site Request Forgery (XSRF) Attack Detected</h3>
459 <h4>If you think this is a mistake please ask your administrator to add the following site to the acceptable referer list</h4>
460 <h3>{$http_ref['host']}</h3>
461 <h4><a href='javascript:void(0);' onclick='document.getElementById("directions").style.display="";'>Click here for directions to add this site to the acceptable referer list</a></h4>
463 <div id='directions' style='display:none'>
466 <li>On your file system go to the root of your SugarCRM instance
467 <li>Open the file config_override.php. If it does not exist, create it. (it should be at the same level as index.php and config.php)
468 <li>Make sure the file starts with <pre><?php</pre> followed by a new line
469 <li>Add the following line to your config_override.php file<br> <pre>\$sugar_config['http_referer']['list'][] = '{$http_ref['host']}';</pre>
470 <li>Save the file and it should work
472 <h3>Attempted action ({$this->controller->action})</h3>
473 If you feel this is a valid action that should be allowed from any referer, add the following to your config_override.php file
474 <ul><li><pre>\$sugar_config['http_referer']['actions'] =array( $whiteListString ); </pre></ul>
483 function startSession()
485 $sessionIdCookie = isset($_COOKIE['PHPSESSID']) ? $_COOKIE['PHPSESSID'] : null;
486 if(isset($_REQUEST['MSID'])) {
487 session_id($_REQUEST['MSID']);
489 if(isset($_SESSION['user_id']) && isset($_SESSION['seamless_login'])){
490 unset ($_SESSION['seamless_login']);
492 if(isset($_COOKIE['PHPSESSID'])){
493 self::setCookie('PHPSESSID', '', time()-42000, '/');
495 sugar_cleanup(false);
497 exit('Not a valid entry method');
500 if(can_start_session()){
505 if ( isset($_REQUEST['login_module']) && isset($_REQUEST['login_action'])
506 && !($_REQUEST['login_module'] == 'Home' && $_REQUEST['login_action'] == 'index') ) {
507 if ( !is_null($sessionIdCookie) && empty($_SESSION) ) {
508 self::setCookie('loginErrorMessage', 'LBL_SESSION_EXPIRED', time()+30, '/');
514 function endSession(){
518 * Redirect to another URL
521 * @param string $url The URL to redirect to
528 * If the headers have been sent, then we cannot send an additional location header
529 * so we will output a javascript redirect statement.
531 if (headers_sent()) {
532 echo "<script>document.location.href='$url';</script>\n";
534 //@ob_end_clean(); // clear output buffer
535 session_write_close();
536 header( 'HTTP/1.1 301 Moved Permanently' );
537 header( "Location: ". $url );
543 * Wrapper for the PHP setcookie() function, to handle cases where headers have
546 public static function setCookie(
556 if ( is_null($domain) )
557 if ( isset($_SERVER["HTTP_HOST"]) )
558 $domain = $_SERVER["HTTP_HOST"];
560 $domain = 'localhost';
563 setcookie($name,$value,$expire,$path,$domain,$secure,$httponly);
565 $_COOKIE[$name] = $value;