diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php index 5ac8c46bba1b795d0c523ed58b04fa3b57c979d2..18a332b51415af4f96bd89c4f4c26b6dc892a19c 100644 --- a/lib/SimpleSAML/Module.php +++ b/lib/SimpleSAML/Module.php @@ -129,6 +129,23 @@ class SimpleSAML_Module { return $className; } + + /** + * Get absolute URL to a specified module resource. + * + * This function creates an absolute URL to a resource stored under ".../modules/<module>/www/". + * + * @param string $resource Resource path, on the form "<module name>/<resource>" + * @return string The absolute URL to the given resource. + */ + public static function getModuleURL($resource) { + assert('is_string($resource)'); + assert('$resource[0] !== "/"'); + + $config = SimpleSAML_Configuration::getInstance(); + return SimpleSAML_Utilities::selfURLhost() . '/' . $config->getBaseURL() . 'module.php/' . $resource; + } + } ?> \ No newline at end of file diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php index e32086e2c6feac5d6df6384fa185b735ee54bc22..d87c62a720102b91b5d03a84b2d00b9a7b348aed 100644 --- a/lib/SimpleSAML/XHTML/Template.php +++ b/lib/SimpleSAML/XHTML/Template.php @@ -190,21 +190,8 @@ class SimpleSAML_XHTML_Template { */ private function includeAtTemplateBase($file) { $data = $this->data; - $filename = $this->configuration->getPathValue('templatedir') . $this->configuration->getValue('theme.use') . '/' . $file; - if (!file_exists($filename)) { - - SimpleSAML_Logger::error($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $file . - '] at [' . $filename . '] - Now trying at base'); - - $filename = $this->configuration->getPathValue('templatedir') . $this->configuration->getValue('theme.base') . '/' . $file; - if (!file_exists($filename)) { - SimpleSAML_Logger::error($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $file . - '] at [' . $filename . ']'); - throw new Exception('Could not load template file [' . $file . ']'); - } - - } + $filename = $this->findTemplatePath($file); include($filename); } @@ -223,8 +210,16 @@ class SimpleSAML_XHTML_Template { assert('is_string($name)'); if(!array_key_exists($name, $this->dictionaries)) { - $dictDir = $this->configuration->getPathValue('dictionarydir'); - $this->dictionaries[$name] = $this->readDictionaryFile($dictDir . $name . '.php'); + $sepPos = strpos($name, ':'); + if($sepPos !== FALSE) { + $module = substr($name, 0, $sepPos); + $fileName = substr($name, $sepPos + 1); + $dictDir = SimpleSAML_Module::getModuleDir($module) . '/dictionaries/'; + } else { + $dictDir = $this->configuration->getPathValue('dictionarydir'); + $fileName = $name; + } + $this->dictionaries[$name] = $this->readDictionaryFile($dictDir . $fileName . '.php'); } return $this->dictionaries[$name]; @@ -249,7 +244,7 @@ class SimpleSAML_XHTML_Template { } /* Check whether we should use the default dictionary or a dictionary specified in the tag. */ - if(substr($tag, 0, 1) === '{' && preg_match('/^{(\w+?):(.*)}$/', $tag, $matches)) { + if(substr($tag, 0, 1) === '{' && preg_match('/^{((?:\w+:)?\w+?):(.*)}$/', $tag, $matches)) { $dictionary = $matches[1]; $tag = $matches[2]; } else { @@ -460,31 +455,99 @@ class SimpleSAML_XHTML_Template { * Show the template to the user. */ public function show() { - - $filename = $this->configuration->getPathValue('templatedir') . - $this->configuration->getValue('theme.use') . '/' . $this->template; - - if (!file_exists($filename)) { - SimpleSAML_Logger::warning($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $this->template . '] at [' . $filename . '] - now trying the base template'); - - - $filename = $this->configuration->getPathValue('templatedir') . - $this->configuration->getValue('theme.base') . '/' . $this->template; - - - if (!file_exists($filename)) { - SimpleSAML_Logger::critical($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $this->template . '] at [' . $filename . ']'); - - echo 'Fatal error: Could not find template file [' . $this->template . '] at [' . $filename . ']'; - exit(0); - } - } - + $filename = $this->findTemplatePath($this->template); require_once($filename); } - - + + + /** + * Find template path. + * + * This function locates the given template based on the template name. + * It will first search for the template in the current theme directory, and + * then the default theme. + * + * The template name may be on the form <module name>:<template path>, in which case + * it will search for the template file in the given module. + * + * An error will be thrown if the template file couldn't be found. + * + * @param string $template The relative path from the theme directory to the template file. + * @return string The absolute path to the template file. + */ + private function findTemplatePath($template) { + assert('is_string($template)'); + + $tmp = explode(':', $template, 2); + if (count($tmp) === 2) { + $templateModule = $tmp[0]; + $templateName = $tmp[1]; + } else { + $templateModule = 'default'; + $templateName = $tmp[0]; + } + + $tmp = explode(':', $this->configuration->getValue('theme.use'), 2); + if (count($tmp) === 2) { + $themeModule = $tmp[0]; + $themeName = $tmp[1]; + } else { + $themeModule = NULL; + $themeName = $tmp[0]; + } + + + /* First check the current theme. */ + if ($themeModule !== NULL) { + /* .../module/<themeModule>/themes/<themeName>/<templateModule>/<templateName> */ + + $filename = SimpleSAML_Module::getModuleDir($themeModule) . '/themes/' . $themeName . '/' . + $templateModule . '/' . $templateName; + } elseif ($templateModule !== 'default') { + /* .../module/<templateModule>/templates/<themeName>/<templateName> */ + $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . + $themeName . '/' . $templateName; + } else { + /* .../templates/<theme>/<templateName> */ + $filename = $this->configuration->getPathValue('templatedir') . $themeName . '/' . + $templateName; + } + + if (file_exists($filename)) { + return $filename; + } + + + /* Not found in current theme. */ + SimpleSAML_Logger::info($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . + $template . '] at [' . $filename . '] - now trying the base template'); + + + /* Try default theme. */ + $baseTheme = $this->configuration->getValue('theme.base'); + if ($templateModule !== 'default') { + /* .../module/<templateModule>/templates/<baseTheme>/<templateName> */ + $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . + $baseTheme . '/' . $templateName; + } else { + /* .../templates/<baseTheme>/<templateName> */ + $filename = $this->configuration->getPathValue('templatedir') . $baseTheme . '/' . + $templateName; + } + + if (file_exists($filename)) { + return $filename; + } + + + /* Not found in default template - log error and throw exception. */ + $error = 'Template: Could not find template file [' . $template . '] at [' . $filename . ']'; + SimpleSAML_Logger::critical($_SERVER['PHP_SELF'] . ' - ' . $error); + + throw new Exception($error); + } + } ?> \ No newline at end of file diff --git a/www/module.php b/www/module.php new file mode 100644 index 0000000000000000000000000000000000000000..73c36b21d37c69462aceeb589f87cbe045492f11 --- /dev/null +++ b/www/module.php @@ -0,0 +1,146 @@ +<?php +/** + * Handler for module requests. + * + * This web page receives requests for web-pages hosted by modules, and directs them to + * the RequestHandler in the module. + * + * @author Olav Morken, UNINETT AS. + * @package simpleSAMLphp + * @version $Id$ + */ + +require_once('_include.php'); + +SimpleSAML_Error_Assertion::installHandler(); + +/* Index pages - filenames to attempt when accessing directories. */ +$indexFiles = array('index.php', 'index.html', 'index.htm', 'index.txt'); + +/* MIME types - key is file extension, value is MIME type. */ +$mimeTypes = array( + 'bml' => 'image/x-ms-bmp', + 'css' => 'text/css', + 'gif' => 'image/gif', + 'htm' => 'text/html', + 'html' => 'text/html', + 'shtml' => 'text/html', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'pdf' => 'application/pdf', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'swfl' => 'application/x-shockwave-flash', + 'txt' => 'text/plain', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + ); + +try { + + if (empty($_SERVER['PATH_INFO'])) { + throw new SimpleSAML_Error_NotFound('No PATH_INFO to module.php'); + } + + $url = $_SERVER['PATH_INFO']; + assert('substr($url, 0, 1) === "/"'); + + $modEnd = strpos($url, '/', 1); + if ($modEnd === FALSE) { + /* The path must always be on the form /module/. */ + throw new SimpleSAML_Error_NotFound('The URL must at least contain a module name followed by a slash.'); + } + + $module = substr($url, 1, $modEnd - 1); + $url = substr($url, $modEnd + 1); + if ($url === FALSE) { + $url = ''; + } + + if (!SimpleSAML_Module::isModuleEnabled($module)) { + throw new SimpleSAML_Error_NotFound('The module \'' . $module . + '\' was either not found, or wasn\'t enabled.'); + } + + /* Make sure that the request isn't suspicious (contains references to current + * directory or parent directory or anything like that. Searching for './' in the + * URL will detect both '../' and './'. Searching for '\' will detect attempts to + * use Windows-style paths. + */ + if (strpos($url, '\\')) { + throw new SimpleSAML_Error_BadRequest('Requested URL contained a backslash.'); + } elseif (strpos($url, './')) { + throw new SimpleSAML_Error_BadRequest('Requested URL contained \'./\'.'); + } + + $path = SimpleSAML_Module::getModuleDir($module) . '/www/' . $url; + + if ($path[strlen($path)-1] === '/') { + /* Path ends with a slash - directory reference. Attempt to find index file + * in directory. + */ + foreach ($indexFiles as $if) { + if (file_exists($path . $if)) { + $path .= $if; + break; + } + } + } + + if (is_dir($path)) { + /* Path is a directory - maybe no index file was found in the previous step, or + * maybe the path didn't end with a slash. Either way, we don't do directory + * listings. + */ + throw new SimpleSAML_Error_NotFound('Directory listing not available.'); + } + + if (!file_exists($path)) { + /* File not found. */ + SimpleSAML_Logger::info('Could not find file \'' . $path . '\'.'); + throw new SimpleSAML_Error_NotFound('The URL wasn\'t found in the module.'); + } + + if (preg_match('#\.php$#', $path)) { + /* PHP file - attempt to run it. */ + require($path); + exit(); + } + + /* Some other file type - attempt to serve it. */ + + /* Find MIME type for file, based on extension. */ + if (preg_match('#\.([^/]+)$#', $path, $type)) { + $type = strtolower($type[1]); + if (array_key_exists($type, $mimeTypes)) { + $contentType = $mimeTypes[$type]; + } else { + $contentType = mime_content_type($path); + } + } else { + $contentType = mime_content_type($path); + } + + $contentLength = sprintf('%u', filesize($path)); /* Force filesize to an unsigned number. */ + + header('Content-Type: ' . $contentType); + header('Content-Length: ' . $contentLength); + readfile($path); + exit(); + +} catch(SimpleSAML_Error_Error $e) { + + $e->show(); + +} catch(Exception $e) { + + $e = new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $e); + $e->show(); + +} + +?> \ No newline at end of file