<?php /** * The Gateway class is the main facade for the AMFPHP remoting service. * * The developer will instantiate a new gateway instance and will interface with * the gateway instance to control how the gateway processes request, securing the * gateway with instance names and turning on additional functionality for the gateway * instance. * * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @copyright (c) 2003 amfphp.org * @package flashservices * @subpackage app * @author Musicman original design * @author Justin Watkins Gateway architecture, class structure, datatype io additions * @author John Cowen Datatype io additions, class structure, * @author Klaasjan Tukker Modifications, check routines, and register-framework * @version $Id$ */ /** * AMFPHP_BASE is the location of the flashservices folder in the files system. * It is used as the absolute path to load all other required system classes. */ define("AMFPHP_BASE", realpath(dirname(dirname(dirname(__FILE__)))) . "/"); /** * required classes for the application */ require_once(AMFPHP_BASE . "shared/app/Constants.php"); require_once(AMFPHP_BASE . "shared/app/Globals.php"); if(AMFPHP_PHP5) { require_once(AMFPHP_BASE . "shared/util/CompatPhp5.php"); } else { require_once(AMFPHP_BASE . "shared/util/CompatPhp4.php"); } require_once(AMFPHP_BASE . "shared/util/CharsetHandler.php"); require_once(AMFPHP_BASE . "shared/util/NetDebug.php"); require_once(AMFPHP_BASE . "shared/util/Headers.php"); require_once(AMFPHP_BASE . "shared/exception/MessageException.php"); require_once(AMFPHP_BASE . "shared/app/BasicActions.php"); require_once(AMFPHP_BASE . "amf/util/AMFObject.php"); require_once(AMFPHP_BASE . "amf/util/WrapperClasses.php"); require_once(AMFPHP_BASE . "amf/app/Filters.php"); require_once(AMFPHP_BASE . "amf/app/Actions.php"); class Gateway { var $_looseMode = false; var $_charsetMethod = "none"; var $_charsetPhp = ""; var $_charsetSql = ""; var $exec; var $filters; var $actions; var $outgoingMessagesFolder = NULL; var $incomingMessagesFolder = NULL; var $useSslFirstMethod = true; var $_enableGzipCompression = false; /** * The Gateway constructor method. * * The constructor method initializes the executive object so any configurations * can immediately propogate to the instance. */ function Gateway() { //Include right executive for php version //Try catch are not syntactically correct in PHP4, so we can't even include //them in PHP 4. if(AMFPHP_PHP5) { //Set gloriously nice error handling include_once(AMFPHP_BASE . "shared/app/php5Executive.php"); include_once(AMFPHP_BASE . "shared/exception/php5Exception.php"); } else { //Cry include_once(AMFPHP_BASE . "shared/app/php4Executive.php"); include_once(AMFPHP_BASE . "shared/exception/php4Exception.php"); } $this->exec = new Executive(); $this->filters = array(); $this->actions = array(); $this->registerFilterChain(); $this->registerActionChain(); } /** * Create the chain of filters * Subclass gateway and overwrite to create a custom gateway */ function registerFilterChain() { //filters $this->filters['deserial'] = 'deserializationFilter'; $this->filters['auth'] = 'authenticationFilter'; $this->filters['batch'] = 'batchProcessFilter'; $this->filters['debug'] = 'debugFilter'; $this->filters['serialize'] = 'serializationFilter'; } /** * Create the chain of actions * Subclass gateway and overwrite to create a custom gateway */ function registerActionChain() { $this->actions['adapter'] = 'adapterAction'; $this->actions['class'] = 'classLoaderAction'; $this->actions['security'] = 'securityAction'; $this->actions['exec'] = 'executionAction'; } /** * The service method runs the gateway application. It turns the gateway 'on'. You * have to call the service method as the last line of the gateway script after all of the * gateway configuration properties have been set. * * Right now the service method also includes a very primitive debugging mode that * just dumps the raw amf input and output to files. This may change in later versions. * The debugging implementation is NOT thread safe so be aware of file corruptions that * may occur in concurrent environments. */ function service() { //Set the parameters for the charset handler CharsetHandler::setMethod($this->_charsetMethod); CharsetHandler::setPhpCharset($this->_charsetPhp); CharsetHandler::setSqlCharset($this->_charsetSql); //Attempt to call charset handler to catch any uninstalled extensions $ch = new CharsetHandler('flashtophp'); $ch->transliterate('?'); $ch2 = new CharsetHandler('sqltophp'); $ch2->transliterate('?'); $GLOBALS['amfphp']['actions'] = $this->actions; if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])){ $GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents('php://input'); } if(isset($GLOBALS["HTTP_RAW_POST_DATA"]) && $GLOBALS["HTTP_RAW_POST_DATA"] != "") { //Start NetDebug NetDebug::initialize(); error_reporting($GLOBALS['amfphp']['errorLevel']); //Enable loose mode if requested if($this->_looseMode) { ob_start(); } $amf = new AMFObject($GLOBALS["HTTP_RAW_POST_DATA"]); // create the amf object if($this->incomingMessagesFolder != NULL) { $mt = microtime(); $pieces = explode(' ', $mt); file_put_contents($this->incomingMessagesFolder . 'in.' . $pieces[1] . '.' . substr($pieces[0], 2) . ".amf", $GLOBALS["HTTP_RAW_POST_DATA"]); } foreach($this->filters as $key => $filter) { $filter($amf); // invoke the first filter in the chain } $output = $amf->outputStream; // grab the output stream //Clear the current output buffer if requested if($this->_looseMode) { ob_end_clean(); } //Send content length header //Thanks to Alec Horley for pointing out the necessity //of this for FlashComm support header(AMFPHP_CONTENT_TYPE); // define the proper header if(Headers::getHeader('serviceBrowser') == true) { //Add the total time header $toAddPos = strpos($output, "\301\260\0\0\1\0\0\0"); $time = (int) ((microtime_float() - $GLOBALS['amfphp']['startTime'])*1000); $b = pack("d", $time); // pack the bytes if (AMFPHP_BIG_ENDIAN) { // if we are a big-endian processor $r = strrev($b); } else { // add the bytes to the output $r = $b; } $output = substr($output, 0, $toAddPos) . $r . substr($output, $toAddPos + 8); } //Send expire header, apparently helps for SSL //Thanks to Gary Rogers for that //And also to Lucas Filippi from openAMF list //And to Robert Reinhardt who appears to be the first who //documented the bug //Finally to Gary who appears to have find a solution which works even more reliably $dateStr = date("D, j M Y ") . date("H:i:s", strtotime("-2 days")); header("Expires: $dateStr GMT"); header("Pragma: no-store"); header("Cache-Control: no-store"); //else don't send any special headers at all if($this->outgoingMessagesFolder != NULL) { $mt = microtime(); $pieces = explode(' ', $mt); file_put_contents($this->outgoingMessagesFolder . 'out.' . $pieces[1] . '.' . substr($pieces[0], 2) . ".amf", $output); } $doCompress = false; $outputCompression = @ini_get("zlib.output_compression"); if(!$outputCompression) { if(strlen($output) > $this->_gzipCompressionThreshold && extension_loaded("zlib") && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE && $this->_enableGzipCompression) { $doCompress = true; ob_start(); ob_start('ob_gzhandler'); } else { header("Content-length: " . strlen($output)); } } print($output); // flush the binary data if($doCompress) { ob_end_flush(); header("Content-length: " . ob_get_length()); ob_end_flush(); } } else { echo("<p>amfphp and this gateway are installed correctly. You may now connect " . "to this gateway from Flash.</p>"); if(function_exists("amf_decode")) { echo("<p>AMF C Extension is loaded " . ($GLOBALS['amfphp']['native'] ? "and enabled." : "but disabled") . "</p>"); } echo "<p>Note: If you're reading an " . "old tutorial, it will tell you that you should see a download ". "window instead of this message. This confused people so this is " . "the new behaviour starting from amfphp 1.2.</p><p>" . "<a href='http://www.amfphp.org/docs'>View the amfphp documentation</p>" . "<p><a href='browser'>Load the service browser</a></p>"; echo "<pre>"; } } /** * Setter for error handling * * @param the error handling level */ function setErrorHandling($level) { $GLOBALS['amfphp']['errorLevel'] = $level; } /** * Sets the base path for loading service methods. * * Call this method to define the directory to look for service classes in. * Relative or full paths are acceptable * * @param string $path The path the the service class directory */ function setClassPath($value) { $path = realpath($value . '/') . '/'; $GLOBALS['amfphp']['classPath'] = $path; } /** * Sets the base path for loading service methods. * * Call this method to define the directory to look for service classes in. * Relative or full paths are acceptable * * @param string $path The path the the service class directory */ function setClassMappingsPath($value) { $path = realpath($value . '/') . '/'; $GLOBALS['amfphp']['customMappingsPath'] = $path; } /** * Sets the loose mode. This will enable outbut buffering * And flushing and set error_reporting to 0. The point is if set to true, a few * of the usual NetConnection.BadVersion error should disappear * Like if you try to echo directly from your function, if you are issued a * warning and such. Errors should still be logged to the error log though. * * @example In gateway.php, before $gateway->service(), use $gateway->setLooseMode(true) * @param bool $mode Enable or disable loose mode */ function setLooseMode($paramLoose = true) { $this->_looseMode = $paramLoose; } function enableGzipCompression($threshold = 30100) { $this->_enableGzipCompression = true; $this->_gzipCompressionThreshold = $threshold; } /** * Sets the charset handler. * The charset handler handles reencoding from and to a specific charset * for PHP and SQL resources. * * @param $method The method used for reencoding, either "none", "iconv" or "runtime" * @param $php The internal encoding that is assumed for PHP (typically ISO-8859-1) * @param $sql The internal encoding that is assumed for SQL resources */ function setCharsetHandler($method = "none", $php, $sql) { $this->_charsetMethod = $method; $this->_charsetPhp = $php; $this->_charsetSql = $sql; } /** * disableTrace will ignore any calls to NetDebug::trace * * @param bool $bool Whether to disable tracing */ function disableDebug($value = true) { $GLOBALS['amfphp']['disableDebug'] = $value; } /** * Disable native extension will disable the native C extension */ function disableNativeExtension() { $GLOBALS['amfphp']['native'] = false; } /** * Log incoming messages to the specified folder */ function logIncomingMessages($folder = NULL) { $this->incomingMessagesFolder = realpath($folder) . '/'; } /** * Log outgoing messages to the specified folder */ function logOutgoingMessages($folder = NULL) { $this->outgoingMessagesFolder = realpath($folder) . '/'; } /** * Dumps data to a file * * @param string $filepath The location of the dump file * @param string $data The data to insert into the dump file */ function _saveRawDataToFile($filepath, $data) { if (!$handle = fopen($filepath, 'w')) { exit; } if (!fwrite($handle, $data)) { exit; } fclose($handle); } /** * Appends data to a file * * @param string $filepath The location of the dump file * @param string $data The data to append to the dump file */ function _appendRawDataToFile($filepath, $data) { $handle = fopen($filepath, 'a'); fwrite($handle, $data); fclose($handle); } } ?>