| <?php | |
| /** | |
| * Copyright 2007 Google Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** | |
| * Handles the /shell.do url. | |
| * It executes the received statement using the globals, constants and locals | |
| * stored for this user. Statements declaring classes and functions are | |
| * persisted and run every time. | |
| */ | |
| class Session { | |
| /** | |
| * Stores a serialized version of both local and global variables. | |
| */ | |
| private $globals = ""; | |
| private $locals = ""; | |
| private $statements = array(); | |
| private $use_statements = array(); | |
| private $constants = ""; | |
| function __construct() { | |
| $this->locals = serialize(array()); | |
| $this->storeGlobals(); | |
| $this->functions = get_defined_functions(); | |
| $this->classes = get_declared_classes(); | |
| $this->storeConstants(); | |
| } | |
| /** Stores a serialized version of the globals. */ | |
| function storeGlobals() { | |
| $this->globals = serialize($GLOBALS); | |
| } | |
| /** Replace the globals with the serialized stored ones. */ | |
| function loadGlobals() { | |
| $GLOBALS = unserialize($this->globals); | |
| } | |
| /** Stores a serialized version of the passed locals. */ | |
| function storeLocals($locals) { | |
| foreach (array("_shell_statement", | |
| "_shell_session") as $nonLocal) { | |
| unset($locals[$nonLocal]); | |
| } | |
| $this->locals = serialize($locals); | |
| } | |
| /** Returns an array with the locals. */ | |
| function getLocals() { | |
| return unserialize($this->locals); | |
| } | |
| /** | |
| * Stores a statement if it declares a function, a class or if it is a | |
| * use statment or a require/include statement. | |
| */ | |
| function storeStatementIfNeeded($statement) { | |
| $nonSerializableTokens = array(T_CLASS, T_FUNCTION, | |
| T_REQUIRE, T_REQUIRE_ONCE, | |
| T_INCLUDE, T_INCLUDE_ONCE); | |
| foreach(token_get_all("<?php $statement ?>") as $token) { | |
| if (in_array($token[0], $nonSerializableTokens)) { | |
| array_push($this->statements, $statement); | |
| } else if ($token[0] == T_USE) { | |
| array_push($this->use_statements, $statement); | |
| } | |
| } | |
| } | |
| /** Stores a serialized version of the constants. */ | |
| function storeConstants() { | |
| $this->constants = serialize(get_defined_constants()); | |
| } | |
| /** Replace the constants with the serialized stored ones. */ | |
| function loadConstants() { | |
| $constants = unserialize($this->constants); | |
| foreach(array_diff_key($constants, get_defined_constants()) as | |
| $constant=>$value) { | |
| define($constant, $value); | |
| } | |
| } | |
| static function scrubOutput($output) { | |
| return htmlentities($output); | |
| } | |
| /** Evaluate all saved statements.*/ | |
| function loadStatements() { | |
| foreach ($this->statements as $statement) { | |
| ob_start(['Session', 'scrubOutput']); | |
| eval($statement); | |
| ob_clean(); | |
| } | |
| } | |
| /** Prepend all the use statements to the given statement. */ | |
| function prependUseStatements($statement) { | |
| return implode("\n", $this->use_statements) . "\n$statement"; | |
| } | |
| /** Method to initialize user scope.*/ | |
| function start() { | |
| // Must goes first, or otherwise the unserialized objects will be incomplete. | |
| $this->loadStatements(); | |
| $this->loadGlobals(); | |
| $this->functions = get_defined_functions(); | |
| $this->classes = get_declared_classes(); | |
| $this->loadConstants(); | |
| } | |
| /** Method to save user scope.*/ | |
| function end($statement, $locals) { | |
| $this->storeGlobals(); | |
| $this->storeLocals($locals); | |
| $this->storeStatementIfNeeded($statement); | |
| $this->storeConstants(); | |
| } | |
| } | |
| /** | |
| * Handler to catch exceptions raised when evaluation the code. | |
| * We just return the error and not the line, as they are not meaningful in this | |
| * context. | |
| */ | |
| function error_handler($errno, $errstr, $errfile, $errline) { | |
| echo $errstr, "\n"; | |
| } | |
| /** | |
| * Handler to catch fatal errors (like function not defined) and print them | |
| * nicely. | |
| */ | |
| function shutdown_handler() { | |
| $error = error_get_last(); | |
| if($error !== NULL){ | |
| echo $error["message"], "\n"; | |
| } | |
| } | |
| /** | |
| * Executes a statement for the given session. | |
| * All locals must be prefixed with _shell_, to avoid messing up with the user's | |
| * local. | |
| */ | |
| function shell($_shell_statement, $_shell_session) { | |
| $_shell_session->start(); | |
| header("Content-Type: text/html; charset=utf-8"); | |
| extract($_shell_session->getLocals(), EXTR_SKIP); | |
| // Disable all error reporting, otherwise it mess with the output. | |
| error_reporting(0); | |
| // Errors are handled with an error handler and a fatal error handler, because | |
| // exceptions are not catchable when evaluating code. | |
| register_shutdown_function('shutdown_handler'); | |
| set_error_handler('error_handler'); | |
| ob_start(['Session', 'scrubOutput']); | |
| eval($_shell_session->prependUseStatements($_shell_statement)); | |
| ob_end_flush(); | |
| $_shell_session->end($_shell_statement, get_defined_vars()); | |
| } | |
| session_start(); | |
| if (!isset($_SESSION["session"])) { | |
| $_SESSION["session"] = new Session(); | |
| } | |
| if (isset($_SESSION['token']) && ($_GET['token'] === $_SESSION['token'])) { | |
| // Append a semi-colon just in case the statement doen't have one. An extra | |
| // semi-colon makes no harm. | |
| shell($_GET["statement"] . ";", $_SESSION["session"]); | |
| } else if (!isset($_SESSION['token'])) { | |
| syslog(LOG_ERR, 'Missing session token'); | |
| echo "Session token missing - Please reset your session."; | |
| } else { | |
| syslog(LOG_ERR, 'Mismatch session token.'); | |
| echo "Invalid session token - Please reset your session."; | |
| } |