diff --git a/do.php b/do.php index c76cbbb..015c2a3 100644 --- a/do.php +++ b/do.php @@ -1,474 +1,471 @@ * @copyright 2010 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource */ //////////////////////////////////////////////////////////////////////////////// /// /// Constants /// //We define one negative number constant by standard erroneous return value. /** * Magic number which indicates the user is not logged in. */ define('USER_NOT_LOGGED', -9); /** * Magic number which indicates the user is logged in, but haven't selected its perso. */ define('PERSO_NOT_SELECTED', -7); //////////////////////////////////////////////////////////////////////////////// /// /// Initialization /// include('includes/core.php'); //Session $IP = encode_ip($_SERVER["REMOTE_ADDR"]); require_once('includes/story/story.php'); //this class can be stored in session session_start(); $_SESSION[ID] = session_id(); session_update(); //updates or creates the session //Gets current perso require_once('includes/objects/perso.php'); $CurrentUser = get_logged_user(); //Gets current user infos if ($perso_id = $CurrentUser->session['perso_id']) { $CurrentPerso = new Perso($perso_id); } //Requires user and perso if ($CurrentUser->id < 1000) { echo USER_NOT_LOGGED; exit; } if (!$CurrentPerso) { echo PERSO_NOT_SELECTED; exit; } //Loads Smarty (as it handles l10n, it will be used by lang_get) require('includes/Smarty/Smarty.class.php'); $smarty = new Smarty(); $current_dir = dirname(__FILE__); $smarty->template_dir = $current_dir . '/skins/zed'; $smarty->compile_dir = $current_dir . '/cache/compiled'; $smarty->cache_dir = $current_dir . '/cache'; $smarty->config_dir = $current_dir; //Loads language files initialize_lang(); lang_load('core.conf'); //////////////////////////////////////////////////////////////////////////////// /// /// Actions definitions /// /** * Actions class * * Each method is called by first part of your URL, other parts are arguments * e.g. /do.php/validate_quux_request/52 = Actions::validate_quux_request(52); * * You can also use $_GET, $_POST or better $_REQUEST. * * Don't print the value but return it, so we can in the future implement custom * formats like api_output(); */ class Actions { /** * Checks the arguments hash and determines whether it is valid. * * @param Array $args the arguments, the last being the hash * @return boolean true if the hash is valid ; otherwise, false. */ static private function is_hash_valid ($args) { global $Config; return array_pop($args) == md5($_SESSION['ID'] . $Config['SecretKey'] . implode('', $args)); } /** * Handles a allow/deny perso request. * * @param string $request_flag the request flag to clear * @param string $store 'perso' or 'registry' * @param string $key the perso flag or registry key * @param string $value the value to store * @param string $hash the security hash * @return boolean true if the request is valid and have been processed ; otherwise, false. */ - static function perso_request ($request_flag, $store, $key, $value, $hash) - { + static function perso_request ($request_flag, $store, $key, $value, $hash) { global $CurrentPerso; //Ensures we've the correct amount of arguments if (func_num_args() < 4) { return false; } //Checks hash $args = func_get_args(); if (!self::is_hash_valid($args)) { return false; } //Sets flag switch ($store) { case 'perso': $CurrentPerso->set_flag($key, $value); break; case 'registry': registry_set($key, $value); break; default: //Unknown storage location return false; } //Clears request flag if ((string)$request_flag !== "0") { $CurrentPerso->delete_flag($request_flag); } return true; } /** * Sets current perso's local location. * * We don't require a security hash. If the users want to play with it, no problem. * You generally move inside a global location as you wish. * So, if you write a story capturing a perso, use flags to handle this escape! * * @param string $location_local the local location * @return GeoLocation the current perso's GeoLocation object */ - static function set_local_location ($location_local) - { + static function set_local_location ($location_local) { global $CurrentPerso; //Ensures we've the correct amount of arguments if (func_num_args() < 1) { return null; } //Moves current perso to specified location $location_local = urldecode($location_local); $CurrentPerso->move_to(null, $location_local); //Returns GeoLocation relevant instance return $CurrentPerso->location; } /** * Moves the current perso's, setting a new local location. * * We don't require a security hash. If the users want to play with it, no problem. * You generally move inside a global location as you wish. * So, if you write a story capturing a perso, use flags to handle this escape! * * @param string $move the move (coordinates or direction) * @param int $factor a number multiplying the specified move [optional] * @return GeoLocation the current perso's GeoLocation object * * e.g. to move from 2 units to east, you can use one of those instructions: * local_move('east', 2); * local_move('2,0,0'); * local_move('1,0,0', 2); * * Valid moves string are north, east, south, west, up and down. * Valid moves coordinates are x,y,z (3 integers, comma as separator) */ - static function local_move ($move, $factor = 1) - { + static function local_move ($move, $factor = 1) { global $CurrentPerso; //Ensures we've the correct amount of arguments if (func_num_args() < 1) { return null; } //Parses $move switch ($move) { case 'north': $move = [0, 1, 0]; break; case 'east': $move = [1, 0, 0]; break; case 'south': $move = [0, -1, 0]; break; case 'west': $move = [-1, 0, 0]; break; case 'up': $move = [0, 0, 1]; break; case 'down': $move = [0, 0, -1]; break; default: $move = split(',', $move, 3); foreach ($move as $coordinate) { if (!is_numeric($coordinate)) { return null; } } } //Moves current perso to specified location if ($location_local = GeoPoint3D::fromString($CurrentPerso->location->local)) { $location_local->translate($move[0] * $factor, $move[1] * $factor, $move[2] * $factor); $CurrentPerso->move_to(null, $location_local->sprintf("(%d, %d, %d)")); //Returns GeoLocation relevant instance return $CurrentPerso->location; } //Old local location weren't a GeoPoint3D return null; } /** * Moves the current perso's, setting a new local location, using polar+z coordinates. * Polar+z coordinates are polar coordinates, plus a cartesian z dimension. * * We don't require a security hash. If the users want to play with it, no problem. * You generally move inside a global location as you wish. * So, if you write a story capturing a perso, use flags to handle this escape! * * @param string $move the move (coordinates or direction) * @param int $factor a number multiplying the specified move [optional] * @return GeoLocation the current perso's GeoLocation object * * Valid moves string are cw, ccw, out, in, up and down. * r: out = +12 in = -12 * °: cw = +20° ccw = -20 * Valid moves coordinates are r,°,z (3 integers, comma as separator) * (the medium value can also be integer + °) * * e.g. to move of two units (the unit is 20°) clockwise: * polarz_local_move('cw', 2); * polarz_local_move('(0, 20°, 0)', 2); * polarz_local_move('(0, 40°, 0)'); * Or if you really want to use radians (PI/9 won't be parsed): * polarz_local_move('(0, 0.6981317007977318, 0)'; * */ static function polarz_local_move ($move, $factor = 1) { global $CurrentPerso; //Ensures we've the correct amount of arguments if (func_num_args() < 1) { return null; } //Parses $move $move = urldecode($move); switch ($move) { case 'cw': $move = [0, '20°', 0]; break; case 'ccw': $move = [0, '-20°', 0]; break; case 'in': $move = [+12, 0, 0]; break; case 'out': $move = [-12, 0, 0]; break; case 'up': $move = [0, 0, 1]; break; case 'down': $move = [0, 0, -1]; break; default: $move = split(',', $move, 3); foreach ($move as $coordinate) { if (!is_numeric($coordinate) && !preg_match("/^[0-9]+ *°$/", $coordinate)) { return null; } } } dieprint_r($move); //Moves current perso to specified location if ($location_local = GeoPoint3D::fromString($CurrentPerso->location->local)) { $location_local->translate($move[0] * $factor, $move[1] * $factor, $move[2] * $factor); $CurrentPerso->move_to(null, $location_local->sprintf("(%d, %d, %d)")); //Returns GeoLocation relevant instance return $CurrentPerso->location; } //Old local location weren't a GeoPoint3D return null; } /** * Moves the current perso's, setting a new global and local location. * * @param string $location_global The global location * @param string $location_local The local location * @return GeoLocation the current perso's GeoLocation object */ static function global_move ($location_global, $location_local = null) { //Ensures we've the correct amount of arguments if (func_num_args() < 1) { return null; } //Checks hash $args = func_get_args(); if (!self::is_hash_valid($args)) { return false; } //Moves global $CurrentPerso; $CurrentPerso->move_to($location_global, $location_local); return $CurrentPerso->location; } /** * Handles upload content form. * * @return string new content path */ static function upload_content () { global $CurrentPerso, $CurrentUser; require_once('includes/objects/content.php'); //Initializes a new content instance $content = new Content(); $content->load_from_form(); $content->user_id = $CurrentUser->id; $content->perso_id = $CurrentPerso->id; $content->location_global = $CurrentPerso->location_global; //Saves file if ($content->handle_uploaded_file($_FILES['artwork'])) { $content->save_to_database(); $content->generate_thumbnail(); return true; } return false; } /** * Gets multimedia content for the specified location * * @param string $location_global The global location (local is to specified in ?location_local parameter) * @return Array an array of Content instances */ static function get_content ($location_global) { //Ensures we've the correct amount of arguments if (func_num_args() < 1) { return null; } //Checks hash $args = func_get_args(); if (!self::is_hash_valid($args)) { return false; } //Checks local location is specified somewhere (usually in $_GET) if (!array_key_exists('location_local', $_REQUEST)) { return false; } //Gets content require_once('includes/objects/content.php'); return Content::get_local_content($location_global, $_REQUEST['location_local']); } } //////////////////////////////////////////////////////////////////////////////// /// /// Handles request /// //Parses URL $Config['SiteURL'] = get_server_url() . $_SERVER["PHP_SELF"]; $args = get_current_url_fragments(); $method = array_shift($args); if ($_REQUEST['debug']) { //Debug version //Most of E_STRICT errors are evaluated at the compile time thus such errors //are not reported ini_set('display_errors', 'stderr'); error_reporting(-1); if (method_exists('Actions', $method)) { $result = call_user_func_array(['Actions', $method], $args); echo json_encode($result); } else { echo "
Method doesn't exist: $method
"; } if (array_key_exists('redirectTo', $_REQUEST)) { //If user JS disabled, you can add ?redirectTo= followed by an URL echo "Instead to print a callback value, redirects to $_REQUEST[redirectTo]
"; } } else { //Prod version doesn't prints warning <== silence operator if (method_exists('Actions', $method)) { $result = @call_user_func_array(['Actions', $method], $args); if (array_key_exists('redirectTo', $_REQUEST)) { //If user JS disabled, you can add ?redirectTo= followed by an URL header("location: " . $_REQUEST['redirectTo']); } else { echo json_encode($result); } } } diff --git a/includes/api/api_helpers.php b/includes/api/api_helpers.php index 0161d98..4684a34 100755 --- a/includes/api/api_helpers.php +++ b/includes/api/api_helpers.php @@ -1,172 +1,171 @@ * @copyright 2010 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource */ /** * The main function for converting to an XML document. * * Pass in a multi dimensional array and this recursively loops through * and builds up an XML document. * * @param mixed $data * @param string $rootNodeName What you want the root node to be - defaultsto data. * @param SimpleXMLElement $xml Should only be used recursively * @param string $unknownNodeName Name to give to unknown (numeric) keys * @return string XML */ -function toXml($data, $rootNodeName = 'data', $xml = null, $unknownNodeName = 'unknownNode') -{ +function toXml($data, $rootNodeName = 'data', $xml = null, $unknownNodeName = 'unknownNode') { if (!$rootNodeName) { $rootNodeName = 'data'; } if (!$unknownNodeName) { $unknownNodeName = 'unknownNode'; } // turn off compatibility mode as simple xml throws a wobbly if you don't. if (ini_get('zend.ze1_compatibility_mode') == 1) { ini_set('zend.ze1_compatibility_mode', 0); } if ($xml == null) { if (!is_array($data) && !is_object($data)) { //We've a singleton if (is_bool($data)) { $data = $data ? 'true' : 'false'; } return "<$rootNodeName>$data$rootNodeName>"; } //Starts with simple document $xml = simplexml_load_string("<$rootNodeName />"); } // loop through the data passed in. foreach ($data as $key => $value) { // no numeric keys in our xml please! if (is_numeric($key)) { // make string key... $key = $unknownNodeName . '_'. (string) $key; } // replace anything not alpha numeric $key = preg_replace('/[^a-z]/i', '', $key); //If there is another array found recursively call this function if (is_array($value)) { $node = $xml->addChild($key); //Recursive call. toXml($value, $rootNodeName, $node, $unknownNodeName); } elseif (is_object($value)) { $node = $xml->addChild($key); foreach ($value as $subkey => $subvalue) { if ($subkey == "lastError") { continue; } if ($subvalue === null) { //Ignore null values continue; } elseif (is_array($subvalue) || is_object($subvalue)) { //TODO: test this //Recursive call. $subnode = $node->addChild($subkey); toXml($subvalue, $rootNodeName, $subnode, $unknownNodeName); } elseif (is_bool($subvalue)) { $node->addChild($subkey, $subvalue ? 'true' : 'false'); } else { $node->addChild($subkey, htmlentities($subvalue)); } } //die(); //$array = array(); //$node = $xml->addChild($key); //toXml($value, $rootNodeName, $node, $unknownNodeName); } elseif (is_bool($value)) { $xml->addChild($key, $value ? 'true' : 'false'); } else { //Adds single node. if ($value || $value === 0) { $value = htmlentities($value); $xml->addChild($key,$value); } } } // pass back as string. or simple xml object if you want! return $xml->asXML(); } /** * Outputs API reply, printing it in the specified format. * * The format will be read form $_REQUEST['format']. * * @param mixed $reply the reply to format * @param string $xmlRoot the XML root element name (optional, default value is 'data'). * @param string $xmlChildren the XML children elements name (optional, will be deducted from the context if omitted, or, if not possible, will be unknownNode) */ function api_output ($reply, $xmlRoot = null, $xmlChildren = null) { $format = isset($_REQUEST['format']) ? $_REQUEST['format'] : 'preview'; switch ($format) { case 'preview': echo ''; print_r($reply); echo ''; break; case 'php': echo serialize($reply); break; case 'wddx': require_once('BeautyXML.class.php'); $bc = new BeautyXML(); echo $bc->format(wddx_serialize_value($reply)); break; case 'json': echo json_encode($reply); break; case 'xml': require_once('BeautyXML.class.php'); $bc = new BeautyXML(); echo ''; echo "\n"; echo $bc->format(toXml($reply, $xmlRoot, null, $xmlChildren)); break; case 'string': echo $reply; break; default: echo "Unknown API format: $_GET[format]"; break; } } diff --git a/includes/error.php b/includes/error.php index 5520d84..bfdae33 100755 --- a/includes/error.php +++ b/includes/error.php @@ -1,196 +1,195 @@ * @copyright 2010 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource * * @todo delete old_message_die method and write alternative HTML textual output * in the message_die method */ /// /// Error constants /// /** * SQL_ERROR is the constant meaning the error is a SQL error. * * As a message_die function parameter, it allows to add SQL specific debug information. */ define ("SQL_ERROR", 65); /** * HACK_ERROR is the constant meaning access is non authorized to the resource. * * It encompasses two problematics: * the URL points to a resource belonging to another user or for the current user have no access right (for malformed URL, pick instead GENERAL_ERROR) ; * the user is anonymous, instead to be logged in. * * A suggested way to handle the second problematic is to store in hidden input * fields or better in the session the previous form data, and to print a login * form. * * If you implement this, you don't even need to distinguishes between the two * cases, as once logged in, the regular HACK_ERROR could also be printed. */ define ("HACK_ERROR", 99); /** * GENERAL_ERROR is the constant meaning the error is general, ie not covered by * another more specific error constant. */ define ("GENERAL_ERROR", 117); /// /// Error helper functions /// /** * Output a general error, with human-readable information about the specified * expression as error message ; terminates the current script. * * @see message_die * * @param mixed $expression the expression to be printed * @param string $title the message title (optional, default will be 'Debug') */ function dieprint_r ($expression, $title = '') { if (!$title) { $title = 'Debug'; //if title is omitted or false/null, default title } message_die(GENERAL_ERROR, '
' . print_r($expression, true) .'', $title); } /** * Outputs an error message and terminates the current script. * * Error will be output through Smarty one of the following templates : * error_block.tpl if the header have already been printed ; * error.tpl if the error occurred before the header were called and printed. * * If smarty couldn't be loaded, old_message_die method will be called, which * produces a table output. * * @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR) * @param string $msg_text the error message text (optional, but recommended) * @param string $msg_title the error message title (optional) * @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__) * @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__) * @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR) */ function message_die ($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') { global $smarty, $db; if ($smarty) { $debug_text = $msg_text; if ($err_line && $err_file) $debug_text .= ' — ' . $err_file. ', ' . lang_get('line') . ' ' . $err_line ; switch ($msg_code) { case HACK_ERROR: $smarty->assign('TITLE', lang_get('UnauthorizedAccess')); break; case SQL_ERROR: $smarty->assign('TITLE', lang_get('SQLError')); $sql_error = $db->sql_error(); if ($sql_error['message'] != '') { $debug_text .= '
$sql";
break;
default:
$smarty->assign('WAP', "Message code error.
Expected: HACK_ERROR, SQL_ERROR, GENERAL_ERROR");
//Falls to GENERAL_ERROR
case GENERAL_ERROR:
if ($msg_title)
$smarty->assign('TITLE', $msg_title);
else
$smarty->assign('TITLE', lang_get('GeneralError'));
break;
}
$smarty->assign('ERROR_TEXT', $debug_text);
$template = (defined('HEADER_PRINTED') && HEADER_PRINTED) ? "error_block.tpl" : "error.tpl";
$smarty->display($template);
exit;
} else {
old_message_die($msg_code, $msg_text, $msg_title, $err_line, $err_file, $sql);
}
}
/**
* Outputs an error message and terminates the current script.
*
* This is the message_die method from Espace Win, used on Zed as fallback if Smarty isn't initialized yet.
* Former "german style" error HTML markups have been removed.
*
* @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR)
* @param string $msg_text the error message text (optional, but recommended)
* @param string $msg_title the error message title (optional)
* @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__)
* @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__)
* @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR)
*
* @deprecated since 0.1
*/
-function old_message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '')
-{
+function old_message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') {
global $db, $Utilisateur;
$sql_store = $sql;
if ($msg_code == HACK_ERROR && $Utilisateur[user_id] < 1000) {
die("You must be logged in to access to this resource.");
} elseif ($msg_code == HACK_ERROR) {
$title = "You aren't allowed to access this resource.";
$debug_text = $msg_text;
} elseif ($msg_code == SQL_ERROR) {
$title = "SQL error";
$sql_error = $db->sql_error();
$debug_text = $msg_text;
if ($err_line != '' && $err_file != '') {
$debug_text .= ' in ' . $err_file. ', line ' . $err_line ;
}
if ($sql_error['message'] != '') {
$debug_text .= '
Error #' . $sql_error['code'] . ': ' . $sql_error['message'];
}
if ($sql_store != '') {
$debug_text .= "
$sql_store";
}
} elseif ($msg_code == GENERAL_ERROR) {
$title = $msg_title;
$debug_text = $msg_text;
if ($err_line && $err_file) {
$debug_text .= "
$err_file, line $err_line";
}
}
echo '