diff options
Diffstat (limited to 'Zotlabs/Web')
-rw-r--r-- | Zotlabs/Web/CheckJS.php | 36 | ||||
-rw-r--r-- | Zotlabs/Web/HttpMeta.php | 66 | ||||
-rw-r--r-- | Zotlabs/Web/Router.php | 203 | ||||
-rw-r--r-- | Zotlabs/Web/Session.php | 160 | ||||
-rw-r--r-- | Zotlabs/Web/SessionHandler.php | 88 |
5 files changed, 553 insertions, 0 deletions
diff --git a/Zotlabs/Web/CheckJS.php b/Zotlabs/Web/CheckJS.php new file mode 100644 index 000000000..3ad5fc1ed --- /dev/null +++ b/Zotlabs/Web/CheckJS.php @@ -0,0 +1,36 @@ +<?php + +namespace Zotlabs\Web; + + +class CheckJS { + + private static $jsdisabled = 0; + + function __construct($test = 0) { + if(intval($_REQUEST['jsdisabled'])) + $this->jsdisabled = 1; + if(intval($_COOKIE['jsdisabled'])) + $this->jsdisabled = 1; + + if(! $this->jsdisabled) { + $page = urlencode(\App::$query_string); + + if($test) { + \App::$page['htmlhead'] .= "\r\n" . '<meta http-equiv="refresh" content="0; url=' . z_root() . '/nojs?f=&redir=' . $page . '">' . "\r\n"; + } + else { + \App::$page['htmlhead'] .= "\r\n" . '<noscript><meta http-equiv="refresh" content="0; url=' . z_root() . '/nojs?f=&redir=' . $page . '"></noscript>' . "\r\n"; + } + } + + } + + function disabled() { + return self::$jsdisabled; + } + + +} + + diff --git a/Zotlabs/Web/HttpMeta.php b/Zotlabs/Web/HttpMeta.php new file mode 100644 index 000000000..469a9ed8b --- /dev/null +++ b/Zotlabs/Web/HttpMeta.php @@ -0,0 +1,66 @@ +<?php + +namespace Zotlabs\Web; + + +class HttpMeta { + + private $vars = null; + private $og = null; + + function __construct() { + + $this->vars = array(); + $this->og = array(); + + } + + function set($property,$value) { + if(strpos($property,'og:') === 0) + $this->og[$property] = $value; + else + $this->vars[$property] = $value; + } + + function check_required() { + if( + ($this->og) + && array_key_exists('og:title',$this->og) + && array_key_exists('og:type', $this->og) + && array_key_exists('og:image',$this->og) + && array_key_exists('og:url', $this->og) + ) + return true; + return false; + } + + function get_field($field) { + if(strpos($field,'og:') === 0) + $arr = $this->og; + else + $arr = $this->vars; + + if($arr && array_key_exists($field,$arr) && $arr[$field]) + return $arr[$field]; + return false; + } + + + function get() { + $o = ''; + if($this->vars) { + foreach($this->vars as $k => $v) { + $o .= '<meta property="' . $k . '" content="' . urlencode($v) . '" />' . "\r\n" ; + } + } + if($this->check_required()) { + foreach($this->og as $k => $v) { + $o .= '<meta property="' . $k . '" content="' . urlencode($v) . '" />' . "\r\n" ; + } + } + if($o) + return "\r\n" . $o; + return $o; + } + +}
\ No newline at end of file diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php new file mode 100644 index 000000000..29f2b5206 --- /dev/null +++ b/Zotlabs/Web/Router.php @@ -0,0 +1,203 @@ +<?php + +namespace Zotlabs\Web; + + +class Router { + + function __construct(&$a) { + + /** + * + * We have already parsed the server path into App::$argc and App::$argv + * + * App::$argv[0] is our module name. We will load the file mod/{App::$argv[0]}.php + * and use it for handling our URL request. + * The module file contains a few functions that we call in various circumstances + * and in the following order: + * + * "module"_init + * "module"_post (only called if there are $_POST variables) + * "module"_content - the string return of this function contains our page body + * + * Modules which emit other serialisations besides HTML (XML,JSON, etc.) should do + * so within the module init and/or post functions and then invoke killme() to terminate + * further processing. + */ + + $module = \App::$module; + + if(strlen($module)) { + + /** + * + * We will always have a module name. + * First see if we have a plugin which is masquerading as a module. + * + */ + + if(is_array(\App::$plugins) && in_array($module,\App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { + include_once("addon/{$module}/{$module}.php"); + if(function_exists($module . '_module')) + \App::$module_loaded = true; + } + + if((strpos($module,'admin') === 0) && (! is_site_admin())) { + \App::$module_loaded = false; + notice( t('Permission denied.') . EOL); + goaway(z_root()); + } + + /** + * If the site has a custom module to over-ride the standard module, use it. + * Otherwise, look for the standard program module in the 'mod' directory + */ + + if(! (\App::$module_loaded)) { + if(file_exists("mod/site/{$module}.php")) { + include_once("mod/site/{$module}.php"); + \App::$module_loaded = true; + } + elseif(file_exists("mod/{$module}.php")) { + include_once("mod/{$module}.php"); + \App::$module_loaded = true; + } + else logger("mod/{$module}.php not found."); + } + + + /** + * This provides a place for plugins to register module handlers which don't otherwise exist on the system. + * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if + * there is no specific module file or matching plugin name. + * The plugin should catch at least one of the module hooks for this URL. + */ + + $x = array('module' => $module, 'installed' => false); + call_hooks('module_loaded', $x); + if($x['installed']) + \App::$module_loaded = true; + + /** + * The URL provided does not resolve to a valid module. + * + * On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'. + * We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic - + * we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page + * this will often succeed and eventually do the right thing. + * + * Otherwise we are going to emit a 404 not found. + */ + + if(! (\App::$module_loaded)) { + + // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit. + if((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { + killme(); + } + + if((x($_SERVER, 'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && \App::$config['system']['dreamhost_error_hack']) { + logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); + goaway(z_root() . $_SERVER['REQUEST_URI']); + } + + logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); + header($_SERVER['SERVER_PROTOCOL'] . ' 404 ' . t('Not Found')); + $tpl = get_markup_template('404.tpl'); + \App::$page['content'] = replace_macros($tpl, array( + '$message' => t('Page not found.') + )); + + // pretend this is a module so it will initialise the theme + \App::$module = '404'; + \App::$module_loaded = true; + } + } + } + + + function Dispatch(&$a) { + + /** + * Call module functions + */ + + if(\App::$module_loaded) { + \App::$page['page_title'] = \App::$module; + $placeholder = ''; + + /** + * No theme has been specified when calling the module_init functions + * For this reason, please restrict the use of templates to those which + * do not provide any presentation details - as themes will not be able + * to over-ride them. + */ + + if(function_exists(\App::$module . '_init')) { + $arr = array('init' => true, 'replace' => false); + call_hooks(\App::$module . '_mod_init', $arr); + if(! $arr['replace']) { + $func = \App::$module . '_init'; + $func($a); + } + } + + /** + * Do all theme initialiasion here before calling any additional module functions. + * The module_init function may have changed the theme. + * Additionally any page with a Comanche template may alter the theme. + * So we'll check for those now. + */ + + + /** + * In case a page has overloaded a module, see if we already have a layout defined + * otherwise, if a PDL file exists for this module, use it + * The member may have also created a customised PDL that's stored in the config + */ + + load_pdl($a); + + /** + * load current theme info + */ + + $theme_info_file = 'view/theme/' . current_theme() . '/php/theme.php'; + if (file_exists($theme_info_file)){ + require_once($theme_info_file); + } + + if(function_exists(str_replace('-', '_', current_theme()) . '_init')) { + $func = str_replace('-', '_', current_theme()) . '_init'; + $func($a); + } + elseif (x(\App::$theme_info, 'extends') && file_exists('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php')) { + require_once('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php'); + if(function_exists(str_replace('-', '_', \App::$theme_info['extends']) . '_init')) { + $func = str_replace('-', '_', \App::$theme_info['extends']) . '_init'; + $func($a); + } + } + + if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! \App::$error) + && (function_exists(\App::$module . '_post')) + && (! x($_POST, 'auth-params'))) { + call_hooks(\App::$module . '_mod_post', $_POST); + $func = \App::$module . '_post'; + $func($a); + } + + if((! \App::$error) && (function_exists(\App::$module . '_content'))) { + $arr = array('content' => \App::$page['content'], 'replace' => false); + call_hooks(\App::$module . '_mod_content', $arr); + \App::$page['content'] = $arr['content']; + if(! $arr['replace']) { + $func = \App::$module . '_content'; + $arr = array('content' => $func($a)); + } + call_hooks(\App::$module . '_mod_aftercontent', $arr); + \App::$page['content'] .= $arr['content']; + } + } + } +}
\ No newline at end of file diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php new file mode 100644 index 000000000..f998df396 --- /dev/null +++ b/Zotlabs/Web/Session.php @@ -0,0 +1,160 @@ +<?php + +namespace Zotlabs\Web; + +/** + * + * @brief This file includes session related functions. + * + * Session management functions. These provide database storage of PHP + * session info. + */ + + +class Session { + + private static $handler = null; + private static $session_started = false; + + function init() { + + $gc_probability = 50; + + ini_set('session.gc_probability', $gc_probability); + ini_set('session.use_only_cookies', 1); + ini_set('session.cookie_httponly', 1); + + /* + * Set our session storage functions. + */ + + $handler = new \Zotlabs\Web\SessionHandler(); + self::$handler = $handler; + + $x = session_set_save_handler($handler,true); + if(! $x) + logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); + + // Force cookies to be secure (https only) if this site is SSL enabled. + // Must be done before session_start(). + + $arr = session_get_cookie_params(); + session_set_cookie_params( + ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0), + ((isset($arr['path'])) ? $arr['path'] : '/'), + ((isset($arr['domain'])) ? $arr['domain'] : App::get_hostname()), + ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), + ((isset($arr['httponly'])) ? $arr['httponly'] : true) + ); + } + + function start() { + session_start(); + self::$session_started = true; + } + + /** + * @brief Resets the current session. + * + * @return void + */ + + function nuke() { + self::new_cookie(0); // 0 means delete on browser exit + if($_SESSION && count($_SESSION)) { + foreach($_SESSION as $k => $v) { + unset($_SESSION[$k]); + } + } + } + + function new_cookie($xtime) { + + $newxtime = (($xtime> 0) ? (time() + $xtime) : 0); + + $old_sid = session_id(); + + if(self::$handler && self::$session_started) { + session_regenerate_id(true); + + // force SessionHandler record creation with the new session_id + // which occurs as a side effect of read() + + self::$handler->read(session_id()); + } + else + logger('no session handler'); + + if (x($_COOKIE, 'jsdisabled')) { + setcookie('jsdisabled', $_COOKIE['jsdisabled'], $newxtime); + } + setcookie(session_name(),session_id(),$newxtime); + + $arr = array('expire' => $xtime); + call_hooks('new_cookie', $arr); + + } + + function extend_cookie() { + + // if there's a long-term cookie, extend it + + $xtime = (($_SESSION['remember_me']) ? (60 * 60 * 24 * 365) : 0 ); + + if($xtime) + setcookie(session_name(),session_id(),(time() + $xtime)); + $arr = array('expire' => $xtime); + call_hooks('extend_cookie', $arr); + + } + + + function return_check() { + + // check a returning visitor against IP changes. + // If the change results in being blocked from re-entry with the current cookie + // nuke the session and logout. + // Returning at all indicates the session is still valid. + + // first check if we're enforcing that sessions can't change IP address + // @todo what to do with IPv6 addresses + + if($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) { + logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + + $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.')); + $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.')); + + $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia')); + + if(! $paranoia) + $paranoia = intval(get_config('system', 'paranoia')); + + switch($paranoia) { + case 0: + // no IP checking + break; + case 2: + // check 2 octets + $partial1 = substr($partial1, 0, strrpos($partial1, '.')); + $partial2 = substr($partial2, 0, strrpos($partial2, '.')); + if($partial1 == $partial2) + break; + case 1: + // check 3 octets + if($partial1 == $partial2) + break; + case 3: + default: + // check any difference at all + logger('Session address changed. Paranoid setting in effect, blocking session. ' + . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); + self::nuke(); + goaway(z_root()); + break; + } + } + return true; + } + +} diff --git a/Zotlabs/Web/SessionHandler.php b/Zotlabs/Web/SessionHandler.php new file mode 100644 index 000000000..6980a6408 --- /dev/null +++ b/Zotlabs/Web/SessionHandler.php @@ -0,0 +1,88 @@ +<?php + +namespace Zotlabs\Web; + + +class SessionHandler implements \SessionHandlerInterface { + + + function open ($s, $n) { + return true; + } + + // IMPORTANT: if we read the session and it doesn't exist, create an empty record. + // We rely on this due to differing PHP implementation of session_regenerate_id() + // some which call read explicitly and some that do not. So we call it explicitly + // just after sid regeneration to force a record to exist. + + function read ($id) { + + if($id) { + $r = q("SELECT `data` FROM `session` WHERE `sid`= '%s'", dbesc($id)); + + if($r) { + return $r[0]['data']; + } + else { + q("INSERT INTO `session` (sid, expire) values ('%s', '%s')", + dbesc($id), + dbesc(time() + 300) + ); + } + } + + return ''; + } + + + function write ($id, $data) { + + if(! $id || ! $data) { + return false; + } + + // Unless we authenticate somehow, only keep a session for 5 minutes + // The viewer can extend this by performing any web action using the + // original cookie, but this allows us to cleanup the hundreds or + // thousands of empty sessions left around from web crawlers which are + // assigned cookies on each page that they never use. + + $expire = time() + 300; + + if($_SESSION) { + if(array_key_exists('remember_me',$_SESSION) && intval($_SESSION['remember_me'])) + $expire = time() + (60 * 60 * 24 * 365); + elseif(local_channel()) + $expire = time() + (60 * 60 * 24 * 3); + elseif(remote_channel()) + $expire = time() + (60 * 60 * 24 * 1); + } + + q("UPDATE `session` + SET `data` = '%s', `expire` = '%s' WHERE `sid` = '%s'", + dbesc($data), + dbesc($expire), + dbesc($id) + ); + + return true; + } + + + function close() { + return true; + } + + + function destroy ($id) { + q("DELETE FROM `session` WHERE `sid` = '%s'", dbesc($id)); + return true; + } + + + function gc($expire) { + q("DELETE FROM session WHERE expire < %d", dbesc(time())); + return true; + } + +} |