diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index d9eae69ea..951ed2005 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -13,15 +13,16 @@ class ModuleHandler extends Handler { var $method = 'GET'; - var $module = NULL; ///< Module - var $act = NULL; ///< action - var $mid = NULL; ///< Module ID - var $document_srl = NULL; ///< Document Number - var $module_srl = NULL; ///< Module Number - var $module_info = NULL; ///< Module Info - var $route = NULL; ///< Router result - var $error = NULL; ///< an error code. - var $httpStatusCode = NULL; ///< http status code. + var $module_info = null; + var $module_srl = null; + var $module = null; + var $act = null; + var $mid = null; + var $document_srl = null; + var $route = null; + var $error = null; + var $is_mobile = false; + var $httpStatusCode = 200; /** * Valid types and kinds of module instances. @@ -87,6 +88,7 @@ class ModuleHandler extends Handler $this->document_srl = $document_srl ? (int) $document_srl : (int) Context::get('document_srl'); $this->module_srl = $module_srl ? (int) $module_srl : (int) Context::get('module_srl'); $this->route = Context::getRouteInfo() ?: new stdClass; + $this->is_mobile = Mobile::isFromMobilePhone(); if($entry = Context::get('entry')) { $this->entry = Context::convertEncodingStr($entry); @@ -103,7 +105,7 @@ class ModuleHandler extends Handler // execute addon (before module initialization) $called_position = 'before_module_init'; $oAddonController = AddonController::getInstance(); - $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone() ? 'mobile' : 'pc'); + $addon_file = $oAddonController->getCacheFilePath($this->is_mobile ? 'mobile' : 'pc'); if(file_exists($addon_file)) include($addon_file); } @@ -143,42 +145,25 @@ class ModuleHandler extends Handler } } + // Check success_return_url and error_return_url to prevent dangerous redirects. + $urls = array('success_return_url', 'error_return_url'); + foreach($urls as $key) + { + $url = Context::get($key); + if ($url && !Rhymix\Framework\URL::isInternalURL($url)) + { + Context::set($key, null); + } + } + // If the Router returned an error earlier, show an error here. if($this->route && $this->route->status > 200) { $this->error = 'msg_module_is_not_exists'; - $this->httpStatusCode = '404'; + $this->httpStatusCode = 404; return true; } - // if success_return_url and error_return_url is incorrect - $urls = array(Context::get('success_return_url'), Context::get('error_return_url')); - foreach($urls as $url) - { - if(empty($url)) - { - continue; - } - - if($host = parse_url($url, PHP_URL_HOST)) - { - $defaultHost = parse_url(Context::getDefaultUrl(), PHP_URL_HOST); - if($host !== $defaultHost) - { - $siteModuleHost = $site_module_info->domain; - if(strpos($siteModuleHost, '/') !== false) - { - $siteModuleHost = parse_url($siteModuleHost, PHP_URL_HOST); - } - if($host !== $siteModuleHost) - { - Context::set('success_return_url', null); - Context::set('error_return_url', null); - } - } - } - } - // Convert document alias (entry) to document_srl if(!$this->document_srl && $this->mid && $this->entry) { @@ -189,65 +174,35 @@ class ModuleHandler extends Handler } } - // Get module's information based on document_srl, if it's specified + // Get module info from document_srl. if($this->document_srl) { - $module_info = ModuleModel::getModuleInfoByDocumentSrl($this->document_srl); - if($module_info) + $module_info = $this->_checkDocumentSrl(); + if ($module_info === false) { - // If it exists, compare mid based on the module information - // if mids are not matching, set it as the document's mid - if(!$this->mid || ($this->mid != $module_info->mid)) - { - if(Context::getRequestMethod() == 'GET') - { - Context::setCacheControl(0); - header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301); - return false; - } - else - { - $this->mid = $module_info->mid; - Context::set('mid', $this->mid); - } - } - // if requested module is different from one of the document, remove the module information retrieved based on the document number - if($this->module && $module_info->module != $this->module) - { - unset($module_info); - } - } - - // Block access to secret or temporary documents. - if(Context::getRequestMethod() == 'GET') - { - $oDocument = DocumentModel::getDocument($this->document_srl); - if($oDocument->isExists() && !$oDocument->isAccessible()) - { - $this->httpStatusCode = '403'; - } + return false; } } - - // If module_info is not set yet, and there exists mid information, get module information based on the mid + + // Get module info from mid. if(!$module_info && $this->mid) { - $module_info = ModuleModel::getModuleInfoByMid($this->mid, $site_module_info->site_srl); + $module_info = ModuleModel::getModuleInfoByMid($this->mid); } - // If module_info is not set still, and $module does not exist, find the default module + // Set module info as the default module for the domain. if(!$module_info && !$this->module && !$this->mid) { $module_info = $site_module_info; } - // Set index document + // Set the index document. if($site_module_info->index_document_srl && !$this->module && !$this->mid && !$this->document_srl && $this->method === 'GET' && !$this->route->args) { Context::set('document_srl', $this->document_srl = $site_module_info->index_document_srl, true); } - // redirect, if site start module + // Redirect if the index module was requested. if(!$site_module_info->index_document_srl && $this->method === 'GET' && isset($this->route->args['mid']) && $this->route->args['mid'] === $site_module_info->mid && count($this->route->args) === 1) { Context::setCacheControl(0); @@ -258,57 +213,18 @@ class ModuleHandler extends Handler // If module info was set, retrieve variables from the module information if($module_info) { + // Set instance variables and SEO info. $this->module = $module_info->module; $this->mid = $module_info->mid; $this->module_info = $module_info; - if ($module_info->mid == $site_module_info->mid) - { - $seo_title = config('seo.main_title') ?: '$SITE_TITLE - $SITE_SUBTITLE'; - } - else - { - $seo_title = config('seo.subpage_title') ?: '$SITE_TITLE - $SUBPAGE_TITLE'; - } + $this->_setModuleSEOInfo($module_info, $site_module_info); - ModuleController::getInstance()->replaceDefinedLangCode($seo_title); - Context::setBrowserTitle($seo_title, array( - 'site_title' => Context::getSiteTitle(), - 'site_subtitle' => Context::getSiteSubtitle(), - 'subpage_title' => $module_info->browser_title, - 'page' => Context::get('page') ?: 1, - )); - - $module_config = ModuleModel::getModuleConfig('module'); - if ($module_info->meta_keywords) - { - Context::addMetaTag('keywords', $module_info->meta_keywords); - } - elseif ($site_module_info->settings->meta_keywords) - { - Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords); - } - elseif ($module_config->meta_keywords) - { - Context::addMetaTag('keywords', $module_config->meta_keywords); - } - - if ($module_info->meta_description) - { - Context::addMetaTag('description', $module_info->meta_description); - } - elseif ($site_module_info->settings->meta_description) - { - Context::addMetaTag('description', $site_module_info->settings->meta_description); - } - elseif($module_config->meta_description) - { - Context::addMetaTag('description', $module_config->meta_description); - } - - $viewType = (Mobile::isFromMobilePhone()) ? 'M' : 'P'; + // Check if the current request is from a mobile device. + $this->is_mobile = Mobile::isFromMobilePhone(); + $viewType = $this->is_mobile ? 'M' : 'P'; $targetSrl = $viewType === 'M' ? 'mlayout_srl' : 'layout_srl'; - // use the site default layout. + // Apply default layouts. if($module_info->{$targetSrl} == -1) { $oLayoutAdminModel = getAdminModel('layout'); @@ -329,29 +245,28 @@ class ModuleHandler extends Handler $layoutSrl = $module_info->{$targetSrl}; } - // reset a layout_srl in module_info. + // Reset layout_srl in module_info. $module_info->{$targetSrl} = $layoutSrl; - + + // Add layout header script. $part_config = ModuleModel::getModulePartConfig('layout', $layoutSrl); Context::addHtmlHeader($part_config->header_script); } - - // Set module and mid into module_info - if(!isset($this->module_info)) + else { - $this->module_info = new stdClass(); + $this->module_info = new stdClass; + $this->module_info->module = $this->module; + $this->module_info->mid = $this->mid; } - $this->module_info->module = $this->module; - $this->module_info->mid = $this->mid; - // Set site_srl add 2011 08 09 + // Always overwrite site_srl (deprecated) $this->module_info->site_srl = $site_module_info->site_srl; // Still no module? it's an error if(!$this->module) { $this->error = 'msg_module_is_not_exists'; - $this->httpStatusCode = '404'; + $this->httpStatusCode = 404; return true; } @@ -381,21 +296,13 @@ class ModuleHandler extends Handler * */ public function procModule() { - $display_mode = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; + // Set the display mode for the current device type. + $this->is_mobile = Mobile::isFromMobilePhone(); // If error occurred while preparation, return a message instance if($this->error) { - self::_setInputErrorToContext(); - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - if($this->httpStatusCode) - { - $oMessageObject->setHttpStatusCode($this->httpStatusCode); - } - return $oMessageObject; + return self::_createErrorMessage(-1, $this->error, $this->httpStatusCode); } // Get action information with conf/module.xml @@ -419,19 +326,7 @@ class ModuleHandler extends Handler // still no act means error if(!$this->act) { - $this->error = 'msg_module_is_not_exists'; - $this->httpStatusCode = '404'; - - self::_setInputErrorToContext(); - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - if($this->httpStatusCode) - { - $oMessageObject->setHttpStatusCode($this->httpStatusCode); - } - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_module_is_not_exists', 404); } // get type, kind @@ -465,12 +360,7 @@ class ModuleHandler extends Handler if(!in_array(strtoupper($_SERVER['REQUEST_METHOD']), $allowedMethodList)) { - $this->error = 'msg_invalid_request'; - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_method_not_allowed', 405); } } @@ -479,13 +369,7 @@ class ModuleHandler extends Handler { if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF()) { - $this->_setInputErrorToContext(); - $this->error = 'msg_invalid_request'; - $oMessageObject = ModuleHandler::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_security_violation'); } } @@ -497,7 +381,7 @@ class ModuleHandler extends Handler $logged_info = Context::get('logged_info'); // if(type == view, and case for using mobilephone) - if($type == "view" && Mobile::isFromMobilePhone() && Context::isInstalled()) + if($type == "view" && $this->is_mobile && Context::isInstalled()) { $orig_type = "view"; $type = "mobile"; @@ -518,16 +402,7 @@ class ModuleHandler extends Handler if(!is_object($oModule)) { - self::_setInputErrorToContext(); - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - if($this->httpStatusCode) - { - $oMessageObject->setHttpStatusCode($this->httpStatusCode); - } - return $oMessageObject; + return self::_createErrorMessage(-1, $this->error, $this->httpStatusCode); } // If there is no such action in the module object @@ -535,17 +410,7 @@ class ModuleHandler extends Handler { if(!Context::isInstalled()) { - self::_setInputErrorToContext(); - $this->error = 'msg_invalid_request'; - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - if($this->httpStatusCode) - { - $oMessageObject->setHttpStatusCode($this->httpStatusCode); - } - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_invalid_request'); } // 1. Look for the module with action name @@ -565,13 +430,7 @@ class ModuleHandler extends Handler } else { - $this->error = 'msg_invalid_request'; - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_invalid_request'); } } @@ -599,13 +458,7 @@ class ModuleHandler extends Handler { if($this->module == 'admin' || empty($xml_info->action->{$this->act}->permission->target)) { - self::_setInputErrorToContext(); - $this->error = 'admin.msg_is_not_administrator'; - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; + return self::_createErrorMessage(-1, 'admin.msg_is_not_administrator'); } } @@ -626,12 +479,7 @@ class ModuleHandler extends Handler if(!in_array(strtoupper($_SERVER['REQUEST_METHOD']), $allowedMethodList)) { - $this->error = 'msg_security_violation'; - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_method_not_allowed', 405); } } @@ -640,17 +488,11 @@ class ModuleHandler extends Handler { if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF()) { - $this->_setInputErrorToContext(); - $this->error = 'msg_security_violation'; - $oMessageObject = ModuleHandler::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_security_violation'); } } - if($type == "view" && Mobile::isFromMobilePhone()) + if($type == "view" && $this->is_mobile) { $orig_type = "view"; $type = "mobile"; @@ -670,16 +512,7 @@ class ModuleHandler extends Handler if(!is_object($oModule)) { - self::_setInputErrorToContext(); - $oMessageObject = self::getModuleInstance('message', $display_mode); - $oMessageObject->setError(-1); - $oMessageObject->setMessage('msg_module_is_not_exists'); - $oMessageObject->dispMessage(); - if($this->httpStatusCode) - { - $oMessageObject->setHttpStatusCode($this->httpStatusCode); - } - return $oMessageObject; + return self::_createErrorMessage(-1, 'msg_module_is_not_exists', 404); } // Admin page layout @@ -841,6 +674,115 @@ class ModuleHandler extends Handler return $oModule; } + /** + * Check the value of $document_srl. This method is called during init(). + * + * @return object|false + */ + protected function _checkDocumentSrl() + { + // Get the module that the document belongs to. + $module_info = ModuleModel::getModuleInfoByDocumentSrl($this->document_srl); + if($module_info) + { + // Compare the current mid to the module that the document belongs to. + if(!$this->mid || ($this->mid !== $module_info->mid)) + { + // If this is a GET request, redirect to the correct mid. + if(Context::getRequestMethod() === 'GET') + { + Context::setCacheControl(0); + header('Location: ' . getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301); + return false; + } + // If this is NOT a GET request, don't redirect. Just overwrite the mid for the current request. + else + { + $this->mid = $module_info->mid; + Context::set('mid', $this->mid); + } + } + + // Remove module info if a different module has already been selected for the current request. + if($this->module && $module_info->module !== $this->module) + { + $module_info = null; + } + } + + // Block access to secret or temporary documents. + if(Context::getRequestMethod() === 'GET') + { + $oDocument = DocumentModel::getDocument($this->document_srl); + if($oDocument->isExists()) + { + $this->httpStatusCode = 404; + } + elseif(!$oDocument->isAccessible()) + { + $this->httpStatusCode = 403; + } + } + + // Return the module info for further processing. + return $module_info; + } + + /** + * Set SEO information to Context. + * + * @param object $module_info + * @param object $site_module_info + */ + protected function _setModuleSEOInfo($module_info, $site_module_info) + { + // Set the browser title. + if ($module_info->mid == $site_module_info->mid) + { + $seo_title = config('seo.main_title') ?: '$SITE_TITLE - $SITE_SUBTITLE'; + } + else + { + $seo_title = config('seo.subpage_title') ?: '$SITE_TITLE - $SUBPAGE_TITLE'; + } + ModuleController::getInstance()->replaceDefinedLangCode($seo_title); + Context::setBrowserTitle($seo_title, array( + 'site_title' => Context::getSiteTitle(), + 'site_subtitle' => Context::getSiteSubtitle(), + 'subpage_title' => $module_info->browser_title, + 'page' => Context::get('page') ?: 1, + )); + + // Set meta keywords. + $module_config = ModuleModel::getModuleConfig('module'); + if ($module_info->meta_keywords) + { + Context::addMetaTag('keywords', $module_info->meta_keywords); + } + elseif ($site_module_info->settings->meta_keywords) + { + Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords); + } + elseif ($module_config->meta_keywords) + { + Context::addMetaTag('keywords', $module_config->meta_keywords); + } + + // Set meta description. + if ($module_info->meta_description) + { + Context::addMetaTag('description', $module_info->meta_description); + } + elseif ($site_module_info->settings->meta_description) + { + Context::addMetaTag('description', $site_module_info->settings->meta_description); + } + elseif($module_config->meta_description) + { + Context::addMetaTag('description', $module_config->meta_description); + } + } + /** * set error message to Session. * @return void @@ -903,6 +845,28 @@ class ModuleHandler extends Handler } } + /** + * Create a message module instance with an error message. + */ + protected static function _createErrorMessage($error, $message, $status_code = 403, $location = null) + { + $display_mode = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; + if (!$location) + { + $backtrace = debug_backtrace(false); + $caller = array_shift($backtrace); + $location = $caller['file'] . ':' . $caller['line']; + } + + self::_setInputErrorToContext(); + $oMessageObject = self::getModuleInstance('message', $display_mode); + $oMessageObject->setError($error); + $oMessageObject->setMessage($message); + $oMessageObject->setHttpStatusCode($status_code ?: 403); + $oMessageObject->dispMessage('', $location); + return $oMessageObject; + } + /** * display contents from executed module * @param ModuleObject $oModule module instance @@ -910,11 +874,14 @@ class ModuleHandler extends Handler * */ public function displayContent($oModule = NULL) { + // Set the display mode for the current device type. + $this->is_mobile = Mobile::isFromMobilePhone(); + // If the module is not set or not an object, set error if(!$oModule || !is_object($oModule)) { $this->error = 'msg_module_is_not_exists'; - $this->httpStatusCode = '404'; + $this->httpStatusCode = 404; } // If connection to DB has a problem even though it's not install module, set error @@ -979,11 +946,7 @@ class ModuleHandler extends Handler if($this->error) { // display content with message module instance - $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; - $oMessageObject = self::getModuleInstance('message', $type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(null, $oModule->get('rx_error_location')); + $oMessageObject = self::_createErrorMessage(-1, $this->error, $this->httpStatusCode, $oModule->get('rx_error_location')); // display Error Page if(!in_array($oMessageObject->getHttpStatusCode(), array(200, 403))) @@ -997,8 +960,8 @@ class ModuleHandler extends Handler $oModule->setTemplatePath($oMessageObject->getTemplatePath()); $oModule->setTemplateFile($oMessageObject->getTemplateFile()); $oModule->setHttpStatusCode($oMessageObject->getHttpStatusCode()); - // Otherwise, set message instance as the target module } + // Otherwise, set message instance as the target module else { $oModule = $oMessageObject; @@ -1008,7 +971,7 @@ class ModuleHandler extends Handler } // Check if layout_srl exists for the module - $viewType = (Mobile::isFromMobilePhone()) ? 'M' : 'P'; + $viewType = $this->is_mobile ? 'M' : 'P'; if($viewType === 'M') { $layout_srl = $oModule->module_info->mlayout_srl; diff --git a/common/lang/en.php b/common/lang/en.php index 5ec21ffc4..7ed9ed903 100644 --- a/common/lang/en.php +++ b/common/lang/en.php @@ -234,6 +234,7 @@ $lang->msg_invalid_document = 'Invalid Article Number'; $lang->msg_invalid_request = 'Invalid Request'; $lang->msg_invalid_password = 'The password you entered is incorrect.'; $lang->msg_security_violation = 'Security Violation'; +$lang->msg_method_not_allowed = 'This HTTP method is not allowed for this action.'; $lang->msg_feature_disabled = 'This feature is disabled.'; $lang->msg_error_occured = 'An error has occured.'; $lang->msg_not_founded = 'Cannot find the target.'; diff --git a/common/lang/ko.php b/common/lang/ko.php index f6ba251ec..d8074d82e 100644 --- a/common/lang/ko.php +++ b/common/lang/ko.php @@ -236,6 +236,7 @@ $lang->msg_invalid_document = '잘못된 문서번호입니다.'; $lang->msg_invalid_request = '잘못된 요청입니다.'; $lang->msg_invalid_password = '비밀번호가 올바르지 않습니다.'; $lang->msg_security_violation = '보안정책상 허용되지 않습니다.'; +$lang->msg_method_not_allowed = '이 요청에 사용할 수 없는 HTTP 메소드입니다.'; $lang->msg_feature_disabled = '사용할 수 없는 기능입니다.'; $lang->msg_error_occured = '오류가 발생했습니다.'; $lang->msg_not_founded = '대상을 찾을 수 없습니다.';