/** * Injects csrf tokens into each form found on a webpage. * * @param string $data The page being output to the browser. * * @return string The modified page to output to the browser. */ public static function injector($data) { preg_match_all("/<form([^>]*)>(.*?)<\\/form>/is", $data, $matches, PREG_SET_ORDER); if (is_array($matches)) { self::removeExpiredTokens(); //page name - used to invalidate other csrf tokens on the same page //if( self::$pageName === null ) $name = self::generateName(); //uniqid("csrf_".md5(mt_rand()), true); foreach ($matches as $m) { if (preg_match("/<input\\s+[^>]*(?:name\\=[\"']csrf_token[\"'])[^>]*>/is", $m[2]) || strpos($m[1], 'nocsrf') !== false) { continue; } $token = self::generate_token($name); $data = str_replace($m[0], "<form{$m[1]}><input type=\"hidden\" name=\"csrf_token\" value=\"{$token}\" />{$m[2]}</form>", $data); } } preg_match_all("/<\\/body>/is", $data, $bmatches, PREG_SET_ORDER); if (is_array($bmatches)) { $kaTimeout = (self::$timeout - 30) * 1000; //generate keep alive javascript $js = sprintf('<script type="text/javascript"> function csrfKeepAlive(kurl, ktoken){ var config = {url: kurl,data: "token="+ktoken}; var req; try{ req = new XMLHttpRequest(); }catch(e){ req = new ActiveXObject("Microsoft.XMLHTTP"); } var change = function(){ if( req.readyState == 4 && req.responseText != "false" && req.responseText.length > 0 ){ csrfKeepAliveToken = req.responseText; setTimeout(function(){ csrfKeepAlive(csrfKeepAliveUrl, csrfKeepAliveToken); }, %1$d); } }; req.open("POST", config.url, true); req.setRequestHeader("X_REQUESTED_WITH", "XMLHttpRequest"); req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); req.onreadystatechange = change; req.send(config.data); } var csrfKeepAliveUrl = "%2$s"; var csrfKeepAliveToken = "%3$s"; setTimeout(function(){ csrfKeepAlive(csrfKeepAliveUrl, csrfKeepAliveToken); }, %1$d); </script>', $kaTimeout, get::url('csrf/keepalive/' . self::generateName()), self::generate_keepAlive()); foreach ($bmatches as $m) { $data = str_replace($m[0], $js . '</body>', $data); } } return $data; }
/** * Redirects the user to the requested page under the specified * security context (https or http). * * If the current context matches the requested context, then processing will * continue as normal. * * @param bool $ssl * A boolean indicating whether to switch to https or http. */ public static function ssl($ssl = true) { if (!is_bool($ssl)) { throw new InvalidArgumentException('Invalid argument passed to go::ssl(). Must be a boolean value.'); } if (is::ssl() == $ssl) { return; } go::url(get::url($ssl)); }
/** * Gets the last url remembered by the application. * * @return string Returns the url, or if no url is remembered the url for the homepage. */ public static function last_url() { if (isset(munla::$session['lastpage']) && strlen(munla::$session['lastpage']) > 0) { return munla::$session['lastpage']; } return get::url('index', false); }
/** * Routes requests and sets return data */ public function __construct() { if (class_exists('mvc')) { mvc::$view = '#moadmin'; } $this->mongo['dbs'] = self::$model->listDbs(); if (isset($_GET['db'])) { if (strpos($_GET['db'], '.') !== false) { $_GET['db'] = $_GET['newdb']; } self::$model->setDb($_GET['db']); } if (isset($_POST['limit'])) { $_SESSION['limit'] = (int) $_POST['limit']; } else { if (!isset($_SESSION['limit'])) { $_SESSION['limit'] = OBJECT_LIMIT; } } if (isset($_FILES['import']) && is_uploaded_file($_FILES['import']['tmp_name']) && isset($_GET['collection'])) { $data = json_decode(file_get_contents($_FILES['import']['tmp_name'])); self::$model->import($_GET['collection'], $data, $_POST['importmethod']); } $action = isset($_GET['action']) ? $_GET['action'] : 'listCollections'; if (isset($_POST['object'])) { if (self::$model->saveObject($_GET['collection'], $_POST['object'])) { return $this->_dumpFormVals(); } else { $action = 'editObject'; $_POST['errors']['object'] = 'Error: object could not be saved - check your array syntax.'; } } else { if ($action == 'createCollection') { self::$model->{$action}($_GET['collection']); } else { if ($action == 'renameCollection' && isset($_POST['collectionto']) && $_POST['collectionto'] != $_POST['collectionfrom']) { self::$model->{$action}($_POST['collectionfrom'], $_POST['collectionto']); $_GET['collection'] = $_POST['collectionto']; $action = 'listRows'; } } } if (isset($_GET['sort'])) { self::$model->sort = array($_GET['sort'] => $_GET['sortdir']); } $this->mongo['listCollections'] = self::$model->listCollections(); if ($action == 'editObject') { $this->mongo[$action] = isset($_GET['_id']) ? self::$model->{$action}($_GET['collection'], $_GET['_id'], $_GET['idtype']) : ''; return; } else { if ($action == 'removeObject') { self::$model->{$action}($_GET['collection'], $_GET['_id'], $_GET['idtype']); return $this->_dumpFormVals(); } else { if ($action == 'ensureIndex') { foreach ($_GET['index'] as $key => $field) { $indexes[$field] = isset($_GET['isdescending'][$key]) && $_GET['isdescending'][$key] ? -1 : 1; } self::$model->{$action}($_GET['collection'], $indexes, $_GET['unique'] == 'Unique' ? array('unique' => true) : array()); $action = 'listCollections'; } else { if ($action == 'deleteIndex') { self::$model->{$action}($_GET['collection'], unserialize($_GET['index'])); return $this->_dumpFormVals(); } else { if ($action == 'getStats') { $this->mongo[$action] = self::$model->{$action}(); unset($this->mongo['listCollections']); } else { if ($action == 'repairDb' || $action == 'getStats') { $this->mongo[$action] = self::$model->{$action}(); $action = 'listCollections'; } else { if ($action == 'dropDb') { self::$model->{$action}(); load::redirect(get::url()); return; } } } } } } } if (isset($_GET['collection']) && $action != 'listCollections' && method_exists(self::$model, $action)) { $this->mongo[$action] = self::$model->{$action}($_GET['collection']); $this->mongo['count'] = self::$model->count; $this->mongo['colKeys'] = self::$model->colKeys; } if ($action == 'listRows') { $this->mongo['listIndexes'] = self::$model->listIndexes($_GET['collection']); } else { if ($action == 'dropCollection') { return load::redirect(get::url() . '?db=' . urlencode($_GET['db'])); } } }
</div> <div id="new-database" class="hidden"> <form method = "POST" data-type="database"> <input type="hidden" name="db" value="new.database" /> <input type="text" name="newdb" class="form-control input-lg" placeholder="Database name" /> </form> </div> <ul id="export" class="hidden"> <div> <?php echo $html->link(get::url(array('get' => true)) . '&export=limited', '<icon class="icon-download"></icon> Export exactly the results visible on this page', ['class' => "btn btn-success btn-lg btn-block"]); ?> <?php echo $html->link(get::url(array('get' => true)) . '&export=nolimit', '<icon class="icon-cloud-download"></icon> Export full results of this query <small>(ignoring limit and skip clauses)</small>', ['class' => "btn btn-default btn-lg btn-block"]); ?> </div> </ul> <div id="import" class="hidden"> <?php echo $form->open(['upload' => true, 'role' => 'form']); ?> <fieldset> <div class="form-group"> <label for="exampleInputFile">Browse / Choose your file</label> <input type="file" name="import" accept="application/json"> <p class="help-block"><small>File ending with ".json".</small></p> </div> <div id="importmethod">
/** * Generates an anchor element. * * @param mixed $name The content of the anchor element. * @param string $webpath The path following the domain name. * @param string|bool $https Whether the url should be secure or not. Valid values: true, false, 'http', 'https'. * @param int $port The port number that should be used (ex. http://www.google.com:57/). * @param string|array $get The query string that should be appended to the url. * @param string $argSeparator The separator that should splite arguements in the query string. * @param array $attributes,... OPTIONAL Any number of associative arrays containing html attributes as the keys. * * @return he_a */ public function a($content, $webpath) { $args = func_get_args(); array_shift($args); array_shift($args); $https = null; $port = null; $get = null; $argSeparator = '&'; $attributes = array(); $allowed = he_a::acceptedAttributes(); if (count($args) == 1 && is_array($args[0])) { $vargs = array('https', 'port', 'get', 'argSeparator', 'attributes'); $is_assoc = false; //must use this method rather than is::assoc_array because GET can be an assoc array foreach ($vargs as $v) { if (is::existset($args[0], $v)) { $is_assoc = true; break; } } if ($is_assoc) { //arguements were passed as associative array if (is::existset($args[0], 'https')) { $https = $args[0]['https'] == 'https' || $args[0]['https'] === true; } if (is::existset($args[0], 'port')) { $port = $args[0]['port']; } if (is::existset($args[0], 'get')) { $get = $args[0]['get']; } if (is::existset($args[0], 'argSeparator')) { $argSeparator = $args[0]['argSep']; } if (is::existset($args[0], 'attributes') && is_array($args[0]['attributes'])) { $attributes = $args[0]['attributes']; } } else { if ($this->hasHtmlAttributes($args[0], $allowed)) { $attributes = $args[0]; } else { $get = $args[0]; } //not an associative array, the array is meant for get } } else { // cycle through the arguments and assign them based on type // remember to not go out of order // https, port, get, argSeparator, attributes // bool|string, int, string|array, string, array $argPos = 0; while (count($args) > 0) { $arg = array_shift($args); if (is_string($arg)) { $argl = strtolower($arg); if ($argPos < 1 && ($argl == 'https' || $argl == 'http')) { $argPos = 1; $https = $argl == 'https'; continue; } if ($argPos > 0 && $argPos <= 2) { if (strlen($arg) > 1) { $get = $arg; } $argPos = 3; continue; } if ($argPos > 2) { $argSeparator = $arg; break; } } if ($argPos < 1 && is_bool($arg)) { $https = $arg; $argPos = 1; } if ($argPos <= 1 && is_int($arg)) { $port = $arg; $argPos = 2; } if (is_array($arg)) { if ($argPos <= 2) { //must check for attributes if ($this->hasHtmlAttributes($arg, $allowed)) { foreach ($arg as $n => $v) { $n = strtolower($n); if (!array_key_exists($n, $attributes)) { if (array_key_exists($n, htmlElement::$enumAttributes) && !in_array($v, htmlElement::$enumAttributes[$n]) && array_key_exists($v, htmlElement::$enumAttributes[$n])) { $v = htmlElement::$enumAttributes[$n][$v]; } elseif (in_array($n, htmlElement::$boolAttributes)) { if (is_bool($v) && !$v || !is_bool($v) && $v !== 'true') { continue; } $v = $n; } $attributes[$n] = $v; } } } else { $get = $arg; } } if ($argPos > 2) { //must be attributes foreach ($arg as $n => $v) { $n = strtolower($n); if (!array_key_exists($n, $attributes)) { if (array_key_exists($n, htmlElement::$enumAttributes) && !in_array($v, htmlElement::$enumAttributes[$n]) && array_key_exists($v, htmlElement::$enumAttributes[$n])) { $v = htmlElement::$enumAttributes[$n][$v]; } elseif (in_array($n, htmlElement::$boolAttributes)) { if (is_bool($v) && !$v || !is_bool($v) && $v !== 'true') { continue; } $v = $n; } $attributes[$n] = $v; } } $argPos = 4; } } } } $attributes['href'] = get::url($webpath, $https, $port, $get, $argSeparator); //, $attributes); $e = new he_a($content, $attributes); if (!$this->echoOff) { echo $e; } return $e; }
/** * Sends a redirects header and disables view rendering * This redirects via a browser command, this is not the same as changing controllers which is handled within MVC * * @param string $url Optional, if undefined this will refresh the page (mostly useful for dumping post values) */ public static function redirect($url = null) { header('Location: ' . ($url ? $url : get::url(array('get' => true)))); }
/** * Starts the application. * * @return void */ public static function run() { self::$starttime = microtime(true); error_reporting(config::ERROR_LEVEL); if (isset(config::$session_cookie_domain)) { ini_set('session.cookie_domain', config::$session_cookie_domain); } if (class_exists('formHelper')) { formHelper::fixArrays(); } if (class_exists('csrfHelper')) { injector::register(array('csrfHelper', 'injector')); } if (is::ssl() && isset(config::$https_domain) && !isset(config::$http_domain)) { if (is::existset($_GET, 'r_domain')) { config::$http_domain = get::fulldomain($_GET['r_domain']); } else { config::$http_domain = get::fulldomain('www'); } } session_start(); if (config::$isolated_subdomains) { // find the domain $domain = isset(config::$http_domain) ? config::$http_domain : get::fulldomain(); // kill any subdomain sessions that have been transfered to another subdomain $existing = array_keys($_SESSION); foreach ($existing as $d) { if (!is_array($_SESSION[$d])) { continue; } if (array_key_exists('kill_munla_session', $_SESSION[$d]) && $_SESSION[$d]['kill_munla_session']) { unset($_SESSION[$d]); } } // initialize and setup the session for this subdomain if (!array_key_exists($domain, $_SESSION)) { $_SESSION[$domain] = array(); } munla::$session =& $_SESSION[$domain]; } else { munla::$session =& $_SESSION; } if (class_exists('singleUseArray')) { if (!is::existset(munla::$session, 'MUNLA_SINGLE_USE')) { munla::$singleUse = new singleUseArray(); } else { munla::$singleUse = unserialize(munla::$session['MUNLA_SINGLE_USE']); } } $route = get::route(); if (is_array($route) && $route['controller'] == 'csrf' && $route['action'] == 'keepalive' && class_exists('csrfHelper') && is::existset($route, 'params') && count($route['params']) > 0) { if (isset($_POST['token'])) { echo csrfHelper::keepAlive($route['params'][0], $_POST['token']); } exit; } if (class_exists('user') && is_subclass_of('user', 'userBase')) { if (!is::existset(munla::$session, 'MUNLA_USER')) { munla::$session['MUNLA_USER'] = new userWrapper(new user()); } } injector::start(); if (class_exists('app') && is_callable(array('app', 'setup'))) { app::setup(); } if (!isset(munla::$user) && is::existset(munla::$session, 'MUNLA_USER')) { munla::$user =& munla::$session['MUNLA_USER']; } if (!is::ajax()) { $submittedForm = formHelper::process(); if (isset($submittedForm)) { formHelper::process($submittedForm); } } if (class_exists('app') && is_callable(array('app', 'start'))) { $route = app::start($route); } if ($route === null) { $route = array('controller' => 'index', 'action' => 'index', 'params' => null); } $controller = get::controller($route['controller']); if (!isset($controller) && $route['controller'] != 'index') { //push action to params, controller to action, and set controller to index and try again. if ($route['action'] != 'index') { if (!isset($route['params'])) { $route['params'] = array(); } array_unshift($route['params'], $route['action']); } $route['action'] = $route['controller']; $route['controller'] = 'index'; $controller = get::controller('index'); } $view = null; if (isset($controller)) { $action = $controller->getAction(array($route['action'], $route['params'])); if (isset($action)) { try { $viewParams = call_user_func_array(array($controller, $action['action']), $action['params']); //various things could happen here... if (!isset($viewParams)) { $viewParams = array(); } elseif (!is_array($viewParams)) { $viewParams = array($viewParams); } $view = get::view($route, $controller, $viewParams); } catch (SSLException $e) { go::ssl(!is::ssl()); } catch (PermissionException $e) { munla::$nohistory = true; if (isset(munla::$user) && !munla::$user->is_logged_in() && munla::$user->getLoginView()) { $view = munla::$user->getLoginView(); } else { $view = get::view('errors/generic', 'default', array('error_msg' => $e->getMessage())); } } catch (Exception $e) { munla::$nohistory = true; $view = get::view('errors/generic', 'default', array('error_msg' => $e->getMessage())); } } else { $view = get::view($route, $controller); } } else { $view = get::view($route); } if ($view != null) { $view->render(); } else { throw new Exception('View not found!'); } if (class_exists('app', false)) { munla::$nohistory = app::finish(munla::$nohistory); } if (munla::$nohistory === false) { munla::$session['lastpage'] = get::url(); } if (isset(munla::$singleUse)) { munla::$session['MUNLA_SINGLE_USE'] = serialize(munla::$singleUse); } }
/** * Opens a new form tag. * * Has variable parameters. May either pass an associative array with the parameters and HTML attributes mixed together * or the paramters may be passed in order, with an additional array parameter containing the HTML attributes. * * @param callable $callback The callback function for handling the form. * @param bool $nocsrf Indicator to use CSRF or not. * @param string $class The HTML class attribute. * @param string $method The HTML method attribute. * @param string $action The HTML action attribute. * @param array $attributes Other HTML attributes for the tag. * * @return string|bool */ public function open($arg) { if (isset($this->openForm)) { log::warning('Unable to open a new form - a form is already open.'); return false; } $args = func_get_args(); if (count($args) == 1 && is_array($arg) && !is_callable(get::form_callback($arg))) { $args = $arg; } if (count($args) > 0) { $n = array('callback' => null, 'nocsrf' => false, 'class' => null, 'method' => null, 'action' => null); $attributes = null; if (is_array(end($args))) { $eargs = array_pop($args); //could be params or only as callback if (is_callable(get::form_callback($eargs))) { $n['callback'] = get::form_callback($eargs); } else { $attributes = $eargs; } } $did = 0; //$dargs = array('callback', 'class', 'method', 'action', 'nocsrf'); while (count($args) > 0) { $a = array_shift($args); if ($did < 2 && (is_string($a) || is_array($a)) && is_callable(get::form_callback($a))) { $n['callback'] = get::form_callback($a); $did++; continue; } if ($did < 2 && is_bool($a)) { $n['nocsrf'] = $a; $did++; continue; } if (is_string($a)) { if ($did < 3) { $n['class'] = $a; $did = 3; continue; } if ($did < 4) { $n['method'] = $a; $did = 4; continue; } if ($did < 5) { $n['action'] = $a; $did = 5; continue; } } } if (is_array($attributes)) { foreach ($attributes as $ak => $av) { $n[$ak] = $av; } } $args = $n; } set::array_default($args, 'method', 'post', array('get', 'post', 'GET', 'POST')); set::array_default($args, 'action', get::url()); set::array_default($args, 'nocsrf', false, array(true, false)); $formid = get::array_default($args, 'id', 'form_' . ++self::$nextFormIndex, array(), true); $callback = get::array_default($args, 'callback'); if (!isset($callback) || !is_callable($callback)) { log::warning('Form callback is missing or invalid. The form callback should be a static member of a class that extends formHandler.'); } $html = ''; $args = array_filter($args, function ($v) { return !is_null($v); }); if (is::existset($args, 'nocsrf') && $args['nocsrf']) { $args['nocsrf'] = 'nocsrf'; if (isset($callback) && is_callable($callback)) { $mnlAction = self::generateAction($callback); $html = sprintf('<form%s><input%s />', get::formattedAttributes($args), get::formattedAttributes(array('type' => 'hidden', 'name' => 'mnl_formaction', 'value' => $mnlAction))); $this->openForm = $callback; } else { $html = sprintf('<form%s>', get::formattedAttributes($args)); $this->openForm = true; } } else { $csrfName = csrfHelper::generateName(); if (!is::existset(munla::$session, 'forms')) { munla::$session['forms'] = array(); } if (!is::existset(munla::$session['forms'], $csrfName)) { munla::$session['forms'][$csrfName] = array(); } munla::$session['forms'][$csrfName][$formid] = array(); $this->openForm =& munla::$session['forms'][$csrfName][$formid]; $this->openForm['formid'] = $formid; $this->openForm['callback'] = $callback; $this->openForm['method'] = $args['method']; $this->openForm['fields'] = array(); $this->openForm['nocsrf'] = $args['nocsrf']; unset($args['nocsrf']); $html = sprintf('<form%s><input%s />', get::formattedAttributes($args), get::formattedAttributes(array('type' => 'hidden', 'name' => 'mnl_formid', 'value' => $csrfName . '-' . $formid))); } formElement::$fieldNameArrays = array(); if (!$this->echoOff) { echo $html; } return $html; }