| <?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."; |
| } |