diff --git a/core/amf/app/Actions.php b/core/amf/app/Actions.php new file mode 100755 index 0000000..dc92526 --- /dev/null +++ b/core/amf/app/Actions.php @@ -0,0 +1,240 @@ +targetURI; + + if (strpos($target, "http://") === false && strpos($target, "https://") === false) { // check for a http link which means web service + $lpos = strrpos($target, "."); + if ($lpos === false) { + //Check to see if this is in fact a RemotingMessage + $body = $amfbody->getValue(); + $handled = false; + + $messageType = $body[0]->_explicitType; + if($messageType == 'flex.messaging.messages.RemotingMessage') + { + $handled = true; + + //Fix for AMF0 mixed array bug in Flex 2 + if(isset($body[0]->body['length'])) + { + unset($body[0]->body['length']); + } + + $amfbody->setValue($body[0]->body); + $amfbody->setSpecialHandling("RemotingMessage"); + $amfbody->setMetadata("clientId", $body[0]->clientId); + $amfbody->setMetadata("messageId", $body[0]->messageId); + + $GLOBALS['amfphp']['lastMessageId'] = $body[0]->messageId; + + $methodname = $body[0]->operation; + $classAndPackage = $body[0]->source; + $lpos = strrpos($classAndPackage, "."); + if($lpos !== FALSE) + { + $classname = substr($classAndPackage, $lpos + 1); + } + else + { + $classname = $classAndPackage; + } + $uriclasspath = str_replace('.','/',$classAndPackage) . '.php'; + $classpath = $baseClassPath . $uriclasspath; + } + elseif($messageType == "flex.messaging.messages.CommandMessage") + { + if($body[0]->operation == 5) + { + $handled = true; + $amfbody->setSpecialHandling("Ping"); + $amfbody->setMetadata("clientId", $body[0]->clientId); + $amfbody->setMetadata("messageId", $body[0]->messageId); + $amfbody->noExec = true; + } + } + + if(!$handled) + { + $uriclasspath = "amfphp/Amf3Broker.php"; + $classpath = $baseClassPath . "amfphp/Amf3Broker.php"; + $classname = "Amf3Broker"; + $methodname = "handleMessage"; + } + } else { + $methodname = substr($target, $lpos + 1); + $trunced = substr($target, 0, $lpos); + $lpos = strrpos($trunced, "."); + if ($lpos === false) { + $classname = $trunced; + if ($classname == "PageAbleResult" && $methodname == 'getRecords') { + $val = $amfbody->getValue(); + $id = $val[0]; + $keys = explode("=", $id); + $currset = intval($keys[1]); + + $set = $_SESSION['amfphp_recordsets'][$currset]; + + $uriclasspath = $set['class']; + $classpath = $baseClassPath . $set['class']; + $methodname = $set['method']; + + $classname = substr(strrchr('/' . $set['class'], '/'), 1, -4); + + //Now set args for body + $amfbody->setValue(array_merge($set['args'], array($val[1], $val[2]))); + + //Tell amfbody that this is a dynamic paged resultset + $amfbody->setSpecialHandling('pageFetch'); + } + else if($classname == "PageAbleResult" && $methodname == 'release') + { + $amfbody->setSpecialHandling('pageRelease'); + $amfbody->noExec = true; + } + else { + $uriclasspath = $trunced . ".php"; + $classpath = $baseClassPath . $trunced . ".php"; + } + } else { + $classname = substr($trunced, $lpos + 1); + $classpath = $baseClassPath . str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here + $uriclasspath = str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here + } + } + } else { // This is a web service and is unsupported + trigger_error("Web services are not supported in this release", E_USER_ERROR); + } + + $amfbody->classPath = $classpath; + $amfbody->uriClassPath = $uriclasspath; + $amfbody->className = $classname; + $amfbody->methodName = $methodname; + + return true; +} + +/** + * ExecutionAction executes the required methods + */ +function executionAction (&$amfbody) +{ + $specialHandling = $amfbody->getSpecialHandling(); + + if (!$amfbody->isSpecialHandling() || $amfbody->isSpecialHandling(array('describeService', 'pageFetch', 'RemotingMessage'))) + { + $construct = &$amfbody->getClassConstruct(); + $method = $amfbody->methodName; + $args = $amfbody->getValue(); + + if($specialHandling == 'describeService') + { + include_once(AMFPHP_BASE . "util/DescribeService.php"); + $ds = new DescribeService(); + $results = $ds->describe($construct, $amfbody->className); + } + else if($specialHandling == 'pageFetch') + { + $args[count($args) - 2] = $args[count($args) - 2] - 1; + + $dataset = Executive::doMethodCall($amfbody, $construct, $method, $args); + $results = array("cursor" => $args[count($args) - 2] + 1, + "data" => $dataset); + $amfbody->setMetadata('type', '__DYNAMIC_PAGE__'); + } + else + { + /* + if(isset($construct->methodTable[$method]['pagesize'])) + { + //Check if counting method was overriden + if(isset($construct->methodTable[$method]['countMethod'])) + { + $counter = $construct->methodTable[$method]['countMethod']; + } + else + { + $counter = $method . '_count'; + } + + $dataset = Executive::doMethodCall($amfbody, $construct, $method, $args); // do the magic + $count = Executive::doMethodCall($amfbody, $construct, $counter, $args); + + //Include the wrapper + $results = array('class' => $amfbody->uriClassPath, + 'method' => $amfbody->methodName, + 'count' => $count, + "args" => $args, + "data" => $dataset); + $amfbody->setMetadata('type', '__DYNAMIC_PAGEABLE_RESULTSET__'); + $amfbody->setMetadata('pagesize', $construct->methodTable[$method]['pagesize']); + */ + //} + //else + //{ + //The usual + $time = microtime_float(); + $results = Executive::doMethodCall($amfbody, $construct, $method, $args); // do the magic + global $amfphp; + $amfphp['callTime'] += microtime_float() - $time; + //} + } + + if($results !== '__amfphp_error') + { + if($specialHandling == 'RemotingMessage') + { + + $wrapper = new AcknowledgeMessage($amfbody->getMetadata("messageId"), + $amfbody->getMetadata("clientId")); + $wrapper->body = $results; + $amfbody->setResults($wrapper); + } + else + { + $amfbody->setResults($results); + } + + $amfbody->responseURI = $amfbody->responseIndex . "/onResult"; + } + return false; + } + elseif($specialHandling == 'Ping') + { + $wrapper = new AcknowledgeMessage($amfbody->getMetadata("messageId"), + $amfbody->getMetadata("clientId")); + $amfbody->setResults($wrapper); + $amfbody->responseURI = $amfbody->responseIndex . "/onResult"; + } + else if($specialHandling == 'pageRelease') + { + //Ignore PageAbleResult.release + $amfbody->setResults(true); + $amfbody->setMetaData('type', 'boolean'); + $amfbody->responseURI = $amfbody->responseIndex . "/onResult"; + return false; + } + return true; +} +?> \ No newline at end of file diff --git a/core/amf/app/Filters.php b/core/amf/app/Filters.php new file mode 100755 index 0000000..9f58923 --- /dev/null +++ b/core/amf/app/Filters.php @@ -0,0 +1,210 @@ +rawData); // deserialize the data + } + else + { + include_once(AMFPHP_BASE . "amf/io/AMFDeserializer.php"); + include_once(AMFPHP_BASE . "amf/io/AMFSerializer.php"); + $deserializer = new AMFDeserializer($amf->rawData); // deserialize the data + } + + $deserializer->deserialize($amf); // run the deserializer + + //Add some headers + $headers = $amf->_headerTable; + if(isset($headers) && is_array($headers)) + { + foreach($headers as $key => $value) + { + Headers::setHeader($value->name, $value->value); + } + } + + //Set as a describe service + $describeHeader = $amf->getHeader(AMFPHP_SERVICE_BROWSER_HEADER); + + if ($describeHeader !== false) { + if($GLOBALS['amfphp']['disableDescribeService']) + { + //Exit + trigger_error("Service description not allowed", E_USER_ERROR); + die(); + } + $bodyCopy = &$amf->getBodyAt(0); + $bodyCopy->setSpecialHandling('describeService'); + $bodyCopy->noExec = true; + } +} + +/** + * AuthenticationFilter looks at the credential headers, starts sessions, etc. + */ +function authenticationFilter (&$amf) { + $authHeader = $amf->getHeader(AMFPHP_CREDENTIALS_HEADER); + + if ($authHeader !== false && $authHeader->value !== AMFPHP_CLEARED_CREDENTIALS) { + //In PHP5, objects are always pass-by-ref, hence this branch + if(AMFPHP_PHP5) + { + $bodyCopy = clone($amf->getBodyAt(0)); + } + else + { + $bodyCopy = $amf->getBodyAt(0); + } + + $uri = $bodyCopy->targetURI; + $lpos = strrpos($uri, "."); + $cp = substr($uri, 0, $lpos + 1) . "_authenticate"; + $bodyCopy->targetURI = $cp; + $bodyCopy->setSpecialHandling('auth'); + $val = $authHeader->value; + $bodyCopy->setValue($val); + $amf->addBodyAt(0, $bodyCopy); + + //Make it so that the data will stop being transmitted + $clearHeader = array('name' => 'Credentials', 'mustUnderstand' => false, + 'data' => AMFPHP_CLEARED_CREDENTIALS); + $outHeader = new MessageHeader("RequestPersistentHeader", true, $clearHeader); + $amf->addOutgoingHeader($outHeader); + } + + $sessionName = @ini_get('session.name'); + if($sessionName == "" || $sessionName == NULL) + { + //Fix for godaddy not allowing ini_get + $sessionName = "PHPSESSID"; + } + session_start(); + $session_id = session_id(); + if(!strpos($_SERVER['QUERY_STRING'], $session_id) !== FALSE) + { + /** + Instead of trying to guess if using https, + just use AppendTogGatewayUrl instead + of ReplaceGatewayUrl + */ + if(strrpos($_SERVER['QUERY_STRING'], "?") !== FALSE) + { + $joint = "&"; + } + else + { + $joint = "?"; + } + $outHeader = new MessageHeader("AppendToGatewayUrl", false, $joint . $sessionName . "=" . $session_id); + $amf->addOutgoingHeader($outHeader); + } +} + +/** + * Executes each of the bodys + */ +function batchProcessFilter(&$amf) +{ + $bodycount = $amf->numBody(); + + for ($i = 0; $i < $bodycount; $i++) { + $bodyObj = &$amf->getBodyAt($i); + $actions = $GLOBALS['amfphp']['actions']; + foreach($actions as $key => $action) + { + $results = $action($bodyObj); + if($results === false) + { + break; + } + } + } + + $bodycount = $amf->numBody(); + + for ($i = 0; $i < $bodycount; $i++) { + $bodyObj = &$amf->getBodyAt($i); + if($bodyObj->getSpecialHandling() == 'auth' && $bodyObj->getResults() === NULL) + { + $amf->removeBodyAt($i); + break; + } + } +} + +/** + * Adds debugging information to outgoing packet + */ +function debugFilter (&$amf) { + //Add trace headers before outputting + if(!$GLOBALS['amfphp']['isFlashComm'] && !$GLOBALS['amfphp']['disableTrace']) + { + $headerresults = array(); // create a result array + $headerresults[0] = array(); // create a sub array in results (CF seems to do this, don't know why) + + if(count(NetDebug::getTraceStack()) != 0) + { + $ts = NetDebug::getTraceStack(); + $headerresults[0][] = new TraceHeader($ts); + } + if(Headers::getHeader("serviceBrowser") == true) + { + global $amfphp; + $amfphp['totalTime'] = microtime_float() - $amfphp['startTime']; + $headerresults[0][] = new ProfilingHeader(); + } + + //Get the last body in the stack + if(count($headerresults[0]) > 0) + { + $body = &$amf->getBodyAt($amf->numBody() - 1); + + $headers = new MessageBody(NULL, $body->responseIndex, NULL); // create a new amf body + $headers->responseURI = $body->responseIndex . "/onDebugEvents"; // set the response uri of this body + + $headers->setResults($headerresults); // set the results. + $amf->addBodyAt(0, $headers); + } + } +} + +/** + * Serializes the object + */ +function serializationFilter (&$amf) { + if($GLOBALS['amfphp']['native'] === true && function_exists('amf_decode')) + { + $serializer = new AMFBaseSerializer(); // Create a serailizer around the output stream + } + else + { + $serializer = new AMFSerializer(); // Create a serailizer around the output stream + } + $result = $serializer->serialize($amf); // serialize the data + $amf->outputStream = $result; +} +?> \ No newline at end of file diff --git a/core/amf/app/Gateway.php b/core/amf/app/Gateway.php new file mode 100755 index 0000000..ac6edf0 --- /dev/null +++ b/core/amf/app/Gateway.php @@ -0,0 +1,410 @@ +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("
amfphp and this gateway are installed correctly. You may now connect " . + "to this gateway from Flash.
"); + + if(function_exists("amf_decode")) + { + echo("AMF C Extension is loaded " . + ($GLOBALS['amfphp']['native'] ? "and enabled." : "but disabled") . + "
"); + } + echo "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.
" . + "View the amfphp documentation
" . + ""; + echo "";
+ }
+ }
+
+ /**
+ * 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);
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/io/AMFBaseDeserializer.php b/core/amf/io/AMFBaseDeserializer.php
new file mode 100755
index 0000000..53aabe6
--- /dev/null
+++ b/core/amf/io/AMFBaseDeserializer.php
@@ -0,0 +1,400 @@
+isBigEndian = AMFPHP_BIG_ENDIAN;
+ $this->current_byte = 0;
+ $this->raw_data = $rd; // store the stream in this object
+ $this->content_length = strlen($this->raw_data); // grab the total length of this stream
+ $this->charsetHandler = new CharsetHandler('flashtophp');
+ $this->storedStrings = array();
+ $this->storedObjects = array();
+ $this->storedDefinitions = array();
+ $this->native = $GLOBALS['amfphp']['native'] && function_exists('amf_decode');
+ $this->decodeFlags = (AMFPHP_BIG_ENDIAN*2) | 4;
+ }
+
+ /**
+ * deserialize invokes this class to transform the raw data into valid object
+ *
+ * @param object $amfdata The object to put the deserialized data in
+ */
+ function deserialize (&$amfdata) {
+ $time = microtime_float();
+ $this->amfdata = &$amfdata;
+ $this->readHeader(); // read the binary header
+ $this->readBody(); // read the binary body
+ if($this->decodeFlags & 1 == 1)
+ {
+ //AMF3 mode
+ $GLOBALS['amfphp']['encoding'] = "amf3";
+ }
+ global $amfphp;
+ $amfphp['decodeTime'] = microtime_float() - $time;
+ }
+ /**
+ * returns the built AMFObject from the deserialization operation
+ *
+ * @return object The deserialized AMFObject
+ */
+ function getAMFObject() {
+ return $this->amfdata;
+ }
+
+ /**
+ * Decode callback is triggered when an object is encountered on decode
+ */
+ function decodeCallback($event, $arg)
+ {
+ if($event == 1) //Object
+ {
+ $type =$arg;
+ return $this->mapClass($type);
+ }
+ else if($event == 2) //Object post decode
+ {
+ $obj = $arg;
+ if(method_exists($obj, 'init'))
+ {
+ $obj->init();
+ }
+ return $obj;
+ }
+ else if($event == 3) //XML post-decode
+ {
+ return $arg;
+ }
+ else if($event == 4) //Serializable post-decode
+ {
+ if($type == 'flex.messaging.io.ArrayCollection' || $type == 'flex.messaging.io.ObjectProxy')
+ {
+ return;
+ }
+ else
+ {
+ trigger_error("Unable to read externalizable data type " . $type, E_USER_ERROR);
+ return "error";
+ }
+ }
+ else if($event == 5) //ByteArray post decode
+ {
+ return new ByteArray($arg);
+ }
+ }
+
+ /**
+ * readHeader converts that header section of the amf message into php obects.
+ * Header information typically contains meta data about the message.
+ */
+ function readHeader() {
+
+ $topByte = $this->readByte(); // ignore the first two bytes -- version or something
+ $secondByte = $this->readByte(); //0 for Flash,
+ //1 for FlashComm
+ //Disable debug events for FlashComm
+ $GLOBALS['amfphp']['isFlashComm'] = $secondByte == 1;
+
+ //If firstByte != 0, then the AMF data is corrupted, for example the transmission
+ //
+ if(!($topByte == 0 || $topByte == 3))
+ {
+ trigger_error("Malformed AMF message, connection may have dropped");
+ exit();
+ }
+ $this->header_count = $this->readInt(); // find the total number of header elements
+ while ($this->header_count--) { // loop over all of the header elements
+ $name = $this->readUTF();
+ $required = $this->readByte() == 1; // find the must understand flag
+ //$length = $this->readLong(); // grab the length of the header element
+ $this->current_byte += 4; // grab the length of the header element
+ if($this->native)
+ {
+ $content = amf_decode($this->raw_data, $this->decodeFlags, $this->current_byte, array(& $this, "decodeCallback"));
+ }
+ else
+ {
+ $type = $this->readByte(); // grab the type of the element
+ $content = $this->readData($type); // turn the element into real data
+ }
+
+ $this->amfdata->addHeader(new MessageHeader($name, $required, $content)); // save the name/value into the headers array
+ }
+
+ }
+
+ /**
+ * readBody converts the payload of the message into php objects.
+ */
+ function readBody() {
+ $this->body_count = $this->readInt(); // find the total number of body elements
+ while ($this->body_count--) { // loop over all of the body elements
+
+ $this->amf0storedObjects = array();
+ $this->storedStrings = array();
+ $this->storedObjects = array();
+ $this->storedDefinitions = array();
+
+ $target = $this->readUTF();
+ $response = $this->readUTF(); // the response that the client understands
+
+ //$length = $this->readLong(); // grab the length of the body element
+ $this->current_byte += 4;
+
+ if($this->native)
+ $data = amf_decode($this->raw_data, $this->decodeFlags, $this->current_byte, array(& $this, "decodeCallback"));
+ else
+ {
+ $type = $this->readByte(); // grab the type of the element
+ $data = $this->readData($type); // turn the element into real data
+ }
+
+ $this->amfdata->addBody(new MessageBody($target, $response, $data)); // add the body element to the body object
+
+ }
+ }
+
+ /********************************************************************************
+ * This used to be in AmfInputStream
+ ********************************************************************************
+
+ /**
+ * readByte grabs the next byte from the data stream and returns it.
+ *
+ * @return int The next byte converted into an integer
+ */
+ function readByte() {
+ return ord($this->raw_data[$this->current_byte++]); // return the next byte
+ }
+
+ /**
+ * readInt grabs the next 2 bytes and returns the next two bytes, shifted and combined
+ * to produce the resulting integer
+ *
+ * @return int The resulting integer from the next 2 bytes
+ */
+ function readInt() {
+ return ((ord($this->raw_data[$this->current_byte++]) << 8) |
+ ord($this->raw_data[$this->current_byte++])); // read the next 2 bytes, shift and add
+ }
+
+ /**
+ * readUTF first grabs the next 2 bytes which represent the string length.
+ * Then it grabs the next (len) bytes of the resulting string.
+ *
+ * @return string The utf8 decoded string
+ */
+ function readUTF() {
+ $length = $this->readInt(); // get the length of the string (1st 2 bytes)
+ //BUg fix:: if string is empty skip ahead
+ if($length == 0)
+ {
+ return "";
+ }
+ else
+ {
+ $val = substr($this->raw_data, $this->current_byte, $length); // grab the string
+ $this->current_byte += $length; // move the seek head to the end of the string
+ return $this->charsetHandler->transliterate($val); // return the string
+ }
+ }
+
+ /**
+ * readLong grabs the next 4 bytes shifts and combines them to produce an integer
+ *
+ * @return int The resulting integer from the next 4 bytes
+ */
+ function readLong() {
+ return ((ord($this->raw_data[$this->current_byte++]) << 24) |
+ (ord($this->raw_data[$this->current_byte++]) << 16) |
+ (ord($this->raw_data[$this->current_byte++]) << 8) |
+ ord($this->raw_data[$this->current_byte++])); // read the next 4 bytes, shift and add
+ }
+
+ /**
+ * readDouble reads the floating point value from the bytes stream and properly orders
+ * the bytes depending on the system architecture.
+ *
+ * @return float The floating point value of the next 8 bytes
+ */
+ function readDouble() {
+ $bytes = substr($this->raw_data, $this->current_byte, 8);
+ $this->current_byte += 8;
+ if ($this->isBigEndian) {
+ $bytes = strrev($bytes);
+ }
+ $zz = unpack("dflt", $bytes); // unpack the bytes
+ return $zz['flt']; // return the number from the associative array
+ }
+
+ /**
+ * readLongUTF first grabs the next 4 bytes which represent the string length.
+ * Then it grabs the next (len) bytes of the resulting in the string
+ *
+ * @return string The utf8 decoded string
+ */
+ function readLongUTF() {
+ $length = $this->readLong(); // get the length of the string (1st 4 bytes)
+ $val = substr($this->raw_data, $this->current_byte, $length); // grab the string
+ $this->current_byte += $length; // move the seek head to the end of the string
+ return $this->charsetHandler->transliterate($val); // return the string
+ }
+
+ function mapClass($typeIdentifier)
+ {
+ //Check out if class exists
+ if($typeIdentifier == "")
+ {
+ return NULL;
+ }
+ $clazz = NULL;
+ $mappedClass = str_replace('.', '/', $typeIdentifier);
+
+ if($typeIdentifier == "flex.messaging.messages.CommandMessage")
+ {
+ return new CommandMessage();
+ }
+ if($typeIdentifier == "flex.messaging.messages.RemotingMessage")
+ {
+ return new RemotingMessage();
+ }
+
+ if(isset($GLOBALS['amfphp']['incomingClassMappings'][$typeIdentifier]))
+ {
+ $mappedClass = str_replace('.', '/', $GLOBALS['amfphp']['incomingClassMappings'][$typeIdentifier]);
+ }
+
+ $include = FALSE;
+ if(file_exists($GLOBALS['amfphp']['customMappingsPath'] . $mappedClass . '.php'))
+ {
+ $include = $GLOBALS['amfphp']['customMappingsPath'] . $mappedClass . '.php';
+ }
+ elseif(file_exists($GLOBALS['amfphp']['customMappingsPath'] . $mappedClass . '.class.php'))
+ {
+ $include = $GLOBALS['amfphp']['customMappingsPath'] . $mappedClass . '.class.php';
+ }
+
+ if($include !== FALSE)
+ {
+ include_once($include);
+ $lastPlace = strrpos('/' . $mappedClass, '/');
+ $classname = substr($mappedClass, $lastPlace);
+ if(class_exists($classname))
+ {
+ $clazz = new $classname;
+ }
+ }
+
+ return $clazz; // return the object
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/io/AMFBaseSerializer.php b/core/amf/io/AMFBaseSerializer.php
new file mode 100755
index 0000000..e404d51
--- /dev/null
+++ b/core/amf/io/AMFBaseSerializer.php
@@ -0,0 +1,327 @@
+isBigEndian = AMFPHP_BIG_ENDIAN;
+ $this->outBuffer = ""; // the buffer
+ $this->charsetHandler = new CharsetHandler('phptoflash');
+ $this->rsCharsetHandler = new CharsetHandler('sqltoflash');
+ $this->resourceObjects = $GLOBALS['amfphp']['adapterMappings'];
+ $this->native = $GLOBALS['amfphp']['native'] && function_exists('amf_decode');
+ $this->encodeFlags = (AMFPHP_BIG_ENDIAN?2:0) |
+ ($GLOBALS['amfphp']['encoding'] == 'amf3' ? 1:0);
+ }
+
+ /**
+ * serialize is the run method of the class. When serialize is called
+ * the AMFObject passed in is read and converted into the amf binary
+ * representing the PHP data represented.
+ *
+ * @param object $d the AMFObject to serialize
+ */
+ function serialize(&$amfout) {
+ $encodeCallback = array(&$this,"encodeCallback");
+ $this->writeInt(0); // write the version ???
+ $count = $amfout->numOutgoingHeader();
+ $this->writeInt($count); // write header count
+ for ($i = 0; $i < $count; $i++) {
+ //write headers
+ $header = &$amfout->getOutgoingHeaderAt($i);
+ $this->writeUTF($header->name);
+ $this->writeByte(0);
+ $tempBuf = $this->outBuffer;
+ $this->outBuffer = "";
+ if($this->native)
+ $this->outBuffer .= amf_encode($header->value,$this->encodeFlags, $encodeCallback);
+ else
+ $this->writeData($header->value);
+ $tempBuf2 = $this->outBuffer;
+ $this->outBuffer = $tempBuf;
+ $this->writeLong(strlen($tempBuf2));
+ $this->outBuffer .= $tempBuf2;
+ }
+ $count = $amfout->numBody();
+ $this->writeInt($count); // write the body count
+ for ($i = 0; $i < $count; $i++) {
+ //write body
+ $this->amf0StoredObjects = array();
+ $this->storedStrings = array();
+ $this->storedObjects = array();
+ $this->encounteredStrings = 0;
+ $this->storedDefinitions = 0;
+ $body = &$amfout->getBodyAt($i);
+ $this->currentBody = & $body;
+ $this->writeUTF($body->responseURI); // write the responseURI header
+ $this->writeUTF($body->responseTarget); // write null, haven't found another use for this
+ $tempBuf = $this->outBuffer;
+ $this->outBuffer = "";
+
+ if($this->native)
+ $this->outBuffer .= amf_encode($body->getResults(),$this->encodeFlags, $encodeCallback);
+ else
+ $this->writeData($body->getResults());
+
+ $tempBuf2 = $this->outBuffer;
+ $this->outBuffer = $tempBuf;
+ $this->writeLong(strlen($tempBuf2));
+ $this->outBuffer .= $tempBuf2;
+ }
+
+ return $this->outBuffer;
+ }
+
+ function encodeCallback($value)
+ {
+ ///print_r($value);
+ if(is_object($value))
+ {
+ $className = strtolower(get_class($value));
+ if(AMFPHP_PHP5 && $className == 'domdocument')
+ {
+ return array($this->cleanXml($value->saveXml()),1);
+ }
+ else if(array_key_exists($className, $GLOBALS['amfphp']['adapterMappings']))
+ {
+ $subtype = $GLOBALS['amfphp']['adapterMappings'][strtolower($className)];
+
+ $classname = $subtype . "Adapter"; // full class name
+ $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
+ if (!$includeFile) {
+ trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
+ }
+ $recordSet = new $classname($value); // returns formatted recordset
+ return array(
+ array("__amf_recordset__" => 2,
+ "rows" => $recordSet->rows,
+ "columns" => $recordSet->columns),
+ 5);
+ }
+ else if(AMFPHP_PHP5 == 0 && $className == 'domdocument')
+ {
+ return array($this->cleanXml($value->dump_mem()),1);
+ }
+ else if($className == 'simplexmlelement')
+ {
+ return array($this->cleanXml($value->asXML()),1);
+ }
+ elseif($className == 'bytearray' && $this->encodeFlags & 1 == 1)
+ {
+ return array($value->data, 7);
+ }
+ else
+ {
+ $className = $this->getClassName($value);
+ return array($value,3,$className);
+ }
+ }
+ else
+ {
+ //A resource
+ $type = get_resource_type($value);
+ list($type, $subtype) = $this->sanitizeType($type);
+ $classname = $subtype . "Adapter"; // full class name
+ $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
+ if (!$includeFile) {
+ trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
+ }
+ $recordSet = new $classname($value); // returns formatted recordset
+ return array(
+ array("__amf_recordset__" => 2,
+ "rows" => $recordSet->rows,
+ "columns" => $recordSet->columns),
+ 5);
+ }
+ }
+
+ function cleanXml($d)
+ {
+ return preg_replace('/\>(\n|\r|\r\n| |\t)*\','><',trim($d));
+ }
+
+ /**********************************************************************************
+ * This code used to be in AMFOutputStream
+ ********************************************************************************/
+
+ /**
+ * writeByte writes a singe byte to the output stream
+ * 0-255 range
+ *
+ * @param int $b An int that can be converted to a byte
+ */
+ function writeByte($b) {
+ $this->outBuffer .= pack("c", $b); // use pack with the c flag
+ }
+
+ /**
+ * writeInt takes an int and writes it as 2 bytes to the output stream
+ * 0-65535 range
+ *
+ * @param int $n An integer to convert to a 2 byte binary string
+ */
+ function writeInt($n) {
+ $this->outBuffer .= pack("n", $n); // use pack with the n flag
+ }
+
+ /**
+ * writeLong takes an int, float or double and converts it to a 4 byte binary string and
+ * adds it to the output buffer
+ *
+ * @param long $l A long to convert to a 4 byte binary string
+ */
+ function writeLong($l) {
+ $this->outBuffer .= pack("N", $l); // use pack with the N flag
+ }
+
+ /**
+ * writeUTF takes and input string, writes the length as an int and then
+ * appends the string to the output buffer
+ *
+ * @param string $s The string less than 65535 characters to add to the stream
+ */
+ function writeUTF($s) {
+ $os = $this->charsetHandler->transliterate($s);
+ $this->writeInt(strlen($os)); // write the string length - max 65535
+ $this->outBuffer .= $os; // write the string chars
+ }
+
+ /**
+ * writeBinary takes and input string, writes the length as an int and then
+ * appends the string to the output buffer
+ *
+ * @param string $s The string less than 65535 characters to add to the stream
+ */
+ function writeBinary($s) {
+ $this->outBuffer .= $s; // write the string chars
+ }
+
+ /**
+ * writeLongUTF will write a string longer than 65535 characters.
+ * It works exactly as writeUTF does except uses a long for the length
+ * flag.
+ *
+ * @param string $s A string to add to the byte stream
+ */
+ function writeLongUTF($s) {
+ $os = $this->charsetHandler->transliterate($s);
+ $this->writeLong(strlen($os));
+ $this->outBuffer .= $os; // write the string chars
+ }
+
+ /**
+ * writeDouble takes a float as the input and writes it to the output stream.
+ * Then if the system is big-endian, it reverses the bytes order because all
+ * doubles passed via remoting are passed little-endian.
+ *
+ * @param double $d The double to add to the output buffer
+ */
+ function writeDouble($d) {
+ $b = pack("d", $d); // pack the bytes
+ if ($this->isBigEndian) { // if we are a big-endian processor
+ $r = strrev($b);
+ } else { // add the bytes to the output
+ $r = $b;
+ }
+
+ $this->outBuffer .= $r;
+ }
+
+ function sanitizeType($type)
+ {
+ $subtype = -1;
+ $type = strtolower($type);
+ if($type == NULL || trim($type) == "")
+ {
+ $type = -1;
+ }
+
+ if(strpos($type, ' ') !== false)
+ {
+ $str = explode(' ', $type);
+ if(in_array($str[1], array("result", 'resultset', "recordset", "statement")))
+ {
+ $type = "__RECORDSET__";
+ $subtype = $str[0];
+ }
+ }
+ return array($type, $subtype);
+ }
+
+ function getClassName(&$d)
+ {
+ $classname = get_class($d);
+ if(strtolower($classname) == 'stdclass' && !isset($d->_explicitType) )
+ {
+ return "";
+ }
+
+ if(isset($d->_explicitType))
+ {
+ $type = $d->_explicitType;
+ unset($d->_explicitType);
+ return $type;
+ }
+
+ if(isset($GLOBALS['amfphp']['outgoingClassMappings'][strtolower($classname)]))
+ {
+ return $GLOBALS['amfphp']['outgoingClassMappings'][strtolower($classname)];
+ }
+
+ if(class_exists("ReflectionClass")) //Another way of doing things, by Renaun Erickson
+ {
+ $reflectionClass = new ReflectionClass( $classname );
+ $fileName = $reflectionClass->getFileName();
+
+ $basePath = $GLOBALS['amfphp']['customMappingsPath'];
+ if( $basePath == "" )
+ $basePath = getcwd();
+
+ // Handle OS filesystem differences
+ if( DIRECTORY_SEPARATOR == "\\" && ( strpos( $basePath, DIRECTORY_SEPARATOR ) === false ) )
+ $basePath = str_replace( "/", DIRECTORY_SEPARATOR, $basePath );
+
+ if(strpos($fileName, $basePath) === FALSE)
+ {
+ return $classname;
+ }
+ $fullClassName = substr( $fileName, strpos( $fileName, $basePath ) );
+ $fullClassName = substr( $fullClassName, strlen( $basePath ) );
+ $fullClassName = substr( $fullClassName, 0, strlen( $fullClassName ) - 4 );
+ $fullClassName = str_replace( DIRECTORY_SEPARATOR, '.', $fullClassName );
+
+ return $fullClassName;
+ }
+ return $classname;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/amf/io/AMFDeserializer.php b/core/amf/io/AMFDeserializer.php
new file mode 100755
index 0000000..2acaa0c
--- /dev/null
+++ b/core/amf/io/AMFDeserializer.php
@@ -0,0 +1,644 @@
+amf0storedObjects[] = & $ret;
+ $key = $this->readUTF(); // grab the key
+ for ($type = $this->readByte(); $type != 9; $type = $this->readByte()) {
+ $val = $this->readData($type); // grab the value
+ $ret[$key] = $val; // save the name/value pair in the array
+ $key = $this->readUTF(); // get the next name
+ }
+ return $ret; // return the array
+ }
+
+ /**
+ * readMixedObject reads the name/value properties of the amf message and converts
+ * numeric looking keys to numeric keys
+ *
+ * @return array The php array with the object data
+ */
+ function readMixedObject() {
+ $ret = array(); // init the array
+ $this->amf0storedObjects[] = & $ret;
+ $key = $this->readUTF(); // grab the key
+ for ($type = $this->readByte(); $type != 9; $type = $this->readByte()) {
+ $val = $this->readData($type); // grab the value
+ if(is_numeric($key))
+ {
+ $key = (float) $key;
+ }
+ $ret[$key] = $val; // save the name/value pair in the array
+ $key = $this->readUTF(); // get the next name
+ }
+ return $ret; // return the array
+ }
+
+ /**
+ * readArray turns an all numeric keyed actionscript array into a php array.
+ *
+ * @return array The php array
+ */
+ function readArray() {
+ $ret = array(); // init the array object
+ $this->amf0storedObjects[] = & $ret;
+ $length = $this->readLong(); // get the length of the array
+ for ($i = 0; $i < $length; $i++) { // loop over all of the elements in the data
+ $type = $this->readByte(); // grab the type for each element
+ $ret[] = $this->readData($type); // grab each element
+ }
+ return $ret; // return the data
+
+ }
+
+ /**
+ * readMixedArray turns an array with numeric and string indexes into a php array
+ *
+ * @return array The php array with mixed indexes
+ */
+ function readMixedArray() {
+ //$length = $this->readLong(); // get the length property set by flash
+ $this->current_byte += 4;
+ return $this->readMixedObject(); // return the body of mixed array
+ }
+
+ /**
+ * readCustomClass reads the amf content associated with a class instance which was registered
+ * with Object.registerClass. In order to preserve the class name an additional property is assigned
+ * to the object "_explicitType". This property will be overwritten if it existed within the class already.
+ *
+ * @return object The php representation of the object
+ */
+ function readCustomClass() {
+ $typeIdentifier = str_replace('..', '', $this->readUTF());
+ $obj = $this->mapClass($typeIdentifier);
+ $isObject = true;
+ if($obj == NULL)
+ {
+ $obj = array();
+ $isObject = false;
+ }
+ $this->amf0storedObjects[] = & $obj;
+ $key = $this->readUTF(); // grab the key
+ for ($type = $this->readByte(); $type != 9; $type = $this->readByte()) {
+ $val = $this->readData($type); // grab the value
+ if($isObject)
+ {
+ $obj->$key = $val; // save the name/value pair in the array
+ }
+ else
+ {
+ $obj[$key] = $val; // save the name/value pair in the array
+ }
+ $key = $this->readUTF(); // get the next name
+ }
+ if(!$isObject)
+ {
+ $obj['_explicitType'] = $typeIdentifier;
+ }
+ return $obj; // return the array
+ }
+
+ /**
+ * readDate reads a date from the amf message and returns the time in ms.
+ * This method is still under development.
+ *
+ * @return long The date in ms.
+ */
+ function readDate() {
+ $ms = $this->readDouble(); // date in milliseconds from 01/01/1970
+ $int = $this->readInt(); // nasty way to get timezone
+ if ($int > 720) {
+ $int = - (65536 - $int);
+ }
+ $int *= -60;
+ //$int *= 1000;
+ //$min = $int % 60;
+ //$timezone = "GMT " . - $hr . ":" . abs($min);
+ // end nastiness
+
+ //We store the last timezone found in date fields in the request
+ //FOr most purposes, it's expected that the timezones
+ //don't change from one date object to the other (they change per client though)
+ DateWrapper::setTimezone($int);
+ return $ms;
+ }
+
+ /**
+ * readReference replaces the old readFlushedSO. It treats where there
+ * are references to other objects. Currently it does not resolve the
+ * object as this would involve a serious amount of overhead, unless
+ * you have a genius idea
+ *
+ * @return String
+ */
+ function readReference() {
+ $reference = $this->readInt();
+ return $this->amf0storedObjects[$reference];
+ }
+
+ function readAny()
+ {
+ if($this->native)
+ return amfphp_decode($this->raw_data, $this->decodeFlags, $this->current_byte, array(& $this, "decodeCallback"));
+ else
+ {
+ $type = $this->readByte(); // grab the type of the element
+ return $this->readData($type); // turn the element into real data
+ }
+ }
+
+ /**
+ * readData is the main switch for mapping a type code to an actual
+ * implementation for deciphering it.
+ *
+ * @param mixed $type The $type integer
+ * @return mixed The php version of the data in the message block
+ */
+ function readData($type) {
+ switch ($type) {
+ case 0: // number
+ $data = $this->readDouble();
+ break;
+ case 1: // boolean
+ $data = $this->readByte() == 1;
+ break;
+ case 2: // string
+ $data = $this->readUTF();
+ break;
+ case 3: // object Object
+ $data = $this->readObject();
+ break;
+ case 5: // null
+ $data = null;
+ break;
+ case 6: // undefined
+ $data = null;
+ break;
+ case 7: // Circular references are returned here
+ $data = $this->readReference();
+ break;
+ case 8: // mixed array with numeric and string keys
+ $data = $this->readMixedArray();
+ break;
+ case 10: // array
+ $data = $this->readArray();
+ break;
+ case 11: // date
+ $data = $this->readDate();
+ break;
+ case 12: // string, strlen(string) > 2^16
+ $data = $this->readLongUTF();
+ break;
+ case 13: // mainly internal AS objects
+ $data = null;
+ break;
+ case 15: // XML
+ $data = $this->readLongUTF();
+ break;
+ case 16: // Custom Class
+ $data = $this->readCustomClass();
+ break;
+ case 17: //AMF3-specific
+ $GLOBALS['amfphp']['encoding'] = "amf3";
+ $data = $this->readAmf3Data();
+ break;
+ default: // unknown case
+ trigger_error("Found unhandled type with code: $type");
+ exit();
+ break;
+ }
+ return $data;
+ }
+
+ /********************************************************************************
+ * This is the AMF3 specific stuff
+ ********************************************************************************/
+ function readAmf3Data()
+ {
+ $type = $this->readByte();
+ switch($type)
+ {
+ case 0x00 : return null; //undefined
+ case 0x01 : return null; //null
+ case 0x02 : return false; //boolean false
+ case 0x03 : return true; //boolean true
+ case 0x04 : return $this->readAmf3Int();
+ case 0x05 : return $this->readDouble();
+ case 0x06 : return $this->readAmf3String();
+ case 0x07 : return $this->readAmf3XmlString();
+ case 0x08 : return $this->readAmf3Date();
+ case 0x09 : return $this->readAmf3Array();
+ case 0x0A : return $this->readAmf3Object();
+ case 0x0B : return $this->readAmf3XmlString();
+ case 0x0C : return $this->readAmf3ByteArray();
+ default: trigger_error("undefined Amf3 type encountered: " . $type, E_USER_ERROR);
+ }
+ }
+
+ ///
+ /// Handle decoding of the variable-length representation
+ /// which gives seven bits of value per serialized byte by using the high-order bit
+ /// of each byte as a continuation flag.
+ ///
+ ///
+ function readAmf3Int()
+ {
+ $int = $this->readByte();
+ if($int < 128)
+ return $int;
+ else
+ {
+ $int = ($int & 0x7f) << 7;
+ $tmp = $this->readByte();
+ if($tmp < 128)
+ {
+ return $int | $tmp;
+ }
+ else
+ {
+ $int = ($int | ($tmp & 0x7f)) << 7;
+ $tmp = $this->readByte();
+ if($tmp < 128)
+ {
+ return $int | $tmp;
+ }
+ else
+ {
+ $int = ($int | ($tmp & 0x7f)) << 8;
+ $tmp = $this->readByte();
+ $int |= $tmp;
+
+ // Check if the integer should be negative
+ if (($int & 0x10000000) != 0) {
+ // and extend the sign bit
+ $int |= 0xe0000000;
+ }
+ return $int;
+ }
+ }
+ }
+ }
+
+ function readAmf3Date()
+ {
+ $dateref = $this->readAmf3Int();
+ if (($dateref & 0x01) == 0) {
+ $dateref = $dateref >> 1;
+ if ($dateref>=count($this->storedObjects)) {
+ trigger_error('Undefined date reference: ' . $dateref, E_USER_ERROR);
+ return false;
+ }
+ return $this->storedObjects[$dateref];
+ }
+ //$timeOffset = ($dateref >> 1) * 6000 * -1;
+ $ms = $this->readDouble();
+
+ //$date = $ms-$timeOffset;
+ $date = $ms;
+
+ $this->storedObjects[] = & $date;
+ return $date;
+ }
+
+ /**
+ * readString
+ *
+ * @return string
+ */
+ function readAmf3String() {
+
+ $strref = $this->readAmf3Int();
+
+ if (($strref & 0x01) == 0) {
+ $strref = $strref >> 1;
+ if ($strref >= count($this->storedStrings)) {
+ trigger_error('Undefined string reference: ' . $strref, E_USER_ERROR);
+ return false;
+ }
+ return $this->storedStrings[$strref];
+ } else {
+ $strlen = $strref >> 1;
+ $str = "";
+ if ($strlen > 0)
+ {
+ $str = $this->readBuffer($strlen);
+ $this->storedStrings[] = $str;
+ }
+ return $str;
+ }
+
+ }
+
+ function readAmf3XmlString()
+ {
+ $handle = $this->readAmf3Int();
+ $inline = (($handle & 1) != 0 ); $handle = $handle >> 1;
+ if( $inline )
+ {
+ $xml = $this->readBuffer($handle);
+ $this->storedStrings[] = $xml;
+ }
+ else
+ {
+ $xml = $this->storedObjects[$handle];
+ }
+ return $xml;
+ }
+
+ function readAmf3ByteArray()
+ {
+ $handle = $this->readAmf3Int();
+ $inline = (($handle & 1) != 0 );$handle = $handle >> 1;
+ if( $inline )
+ {
+ $ba = new ByteArray($this->readBuffer($handle));
+ $this->storedObjects[] = $ba;
+ }
+ else
+ {
+ $ba = $this->storedObjects[$handle];
+ }
+ return $ba;
+ }
+
+ function readAmf3Array()
+ {
+ $handle = $this->readAmf3Int();
+ $inline = (($handle & 1) != 0 ); $handle = $handle >> 1;
+ if( $inline )
+ {
+ $hashtable = array();
+ $this->storedObjects[] = & $hashtable;
+ $key = $this->readAmf3String();
+ while( $key != "" )
+ {
+ $value = $this->readAmf3Data();
+ $hashtable[$key] = $value;
+ $key = $this->readAmf3String();
+ }
+
+ for($i = 0; $i < $handle; $i++)
+ {
+ //Grab the type for each element.
+ $value = $this->readAmf3Data();
+ $hashtable[$i] = $value;
+ }
+ return $hashtable;
+ }
+ else
+ {
+ return $this->storedObjects[$handle];
+ }
+ }
+
+ function readAmf3Object()
+ {
+ $handle = $this->readAmf3Int();
+ $inline = (($handle & 1) != 0 ); $handle = $handle >> 1;
+
+ if( $inline )
+ {
+ //an inline object
+ $inlineClassDef = (($handle & 1) != 0 );$handle = $handle >> 1;
+ if( $inlineClassDef )
+ {
+ //inline class-def
+ $typeIdentifier = $this->readAmf3String();
+ $typedObject = !is_null($typeIdentifier) && $typeIdentifier != "";
+ //flags that identify the way the object is serialized/deserialized
+ $externalizable = (($handle & 1) != 0 );$handle = $handle >> 1;
+ $dynamic = (($handle & 1) != 0 );$handle = $handle >> 1;
+ $classMemberCount = $handle;
+
+ $classMemberDefinitions = array();
+ for($i = 0; $i < $classMemberCount; $i++)
+ {
+ $classMemberDefinitions[] = $this->readAmf3String();
+ }
+ //string mappedTypeName = typeIdentifier;
+ //if( applicationContext != null )
+ // mappedTypeName = applicationContext.GetMappedTypeName(typeIdentifier);
+
+ $classDefinition = array("type" => $typeIdentifier, "members" => $classMemberDefinitions,
+ "externalizable" => $externalizable, "dynamic" => $dynamic);
+ $this->storedDefinitions[] = $classDefinition;
+ }
+ else
+ {
+ //a reference to a previously passed class-def
+ $classDefinition = $this->storedDefinitions[$handle];
+ }
+ }
+ else
+ {
+ //an object reference
+ return $this->storedObjects[$handle];
+ }
+
+
+ $type = $classDefinition['type'];
+ $obj = $this->mapClass($type);
+
+ $isObject = true;
+ if($obj == NULL)
+ {
+ $obj = array();
+ $isObject = false;
+ }
+
+ //Add to references as circular references may search for this object
+ $this->storedObjects[] = & $obj;
+
+ if( $classDefinition['externalizable'] )
+ {
+ if($type == 'flex.messaging.io.ArrayCollection')
+ {
+ $obj = $this->readAmf3Data();
+ }
+ else if($type == 'flex.messaging.io.ObjectProxy')
+ {
+ $obj = $this->readAmf3Data();
+ }
+ else
+ {
+ trigger_error("Unable to read externalizable data type " . $type, E_USER_ERROR);
+ }
+ }
+ else
+ {
+ $members = $classDefinition['members'];
+ $memberCount = count($members);
+ for($i = 0; $i < $memberCount; $i++)
+ {
+ $val = $this->readAmf3Data();
+ $key = $members[$i];
+ if($isObject)
+ {
+ $obj->$key = $val;
+ }
+ else
+ {
+ $obj[$key] = $val;
+ }
+ }
+
+ if($classDefinition['dynamic']/* && obj is ASObject*/)
+ {
+ $key = $this->readAmf3String();
+ while( $key != "" )
+ {
+ $value = $this->readAmf3Data();
+ if($isObject)
+ {
+ $obj->$key = $value;
+ }
+ else
+ {
+ $obj[$key] = $value;
+ }
+ $key = $this->readAmf3String();
+ }
+ }
+
+ if($type != '' && !$isObject)
+ {
+ $obj['_explicitType'] = $type;
+ }
+ }
+
+ if($isObject && method_exists($obj, 'init'))
+ {
+ $obj->init();
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Taken from SabreAMF
+ */
+ function readBuffer($len)
+ {
+ $data = substr($this->raw_data,$this->current_byte,$len);
+ $this->current_byte += $len;
+ return $data;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/io/AMFSerializer.php b/core/amf/io/AMFSerializer.php
new file mode 100755
index 0000000..fffafb4
--- /dev/null
+++ b/core/amf/io/AMFSerializer.php
@@ -0,0 +1,1048 @@
+writeByte(1); // write the boolean flag
+ $this->writeByte($d); // write the boolean byte
+ }
+
+ /**
+ * writeString writes the string code (0x02) and the UTF8 encoded
+ * string to the output stream.
+ * Note: strings are truncated to 64k max length. Use XML as type
+ * to send longer strings
+ *
+ * @param string $d The string data
+ */
+ function writeString($d) {
+ $count = strlen($d);
+ if($count < 65536)
+ {
+ $this->writeByte(2);
+ $this->writeUTF($d);
+ }
+ else
+ {
+ $this->writeByte(12);
+ $this->writeLongUTF($d);
+ }
+ }
+
+ /**
+ * writeXML writes the xml code (0x0F) and the XML string to the output stream
+ * Note: strips whitespace
+ * @param string $d The XML string
+ */
+ function writeXML($d) {
+ if(!$this->writeReferenceIfExists($d))
+ {
+ $this->writeByte(15);
+ $this->writeLongUTF(preg_replace('/\>(\n|\r|\r\n| |\t)*\','><',trim($d)));
+ }
+ }
+
+ /**
+ * writeData writes the date code (0x0B) and the date value to the output stream
+ *
+ * @param date $d The date value
+ */
+ function writeDate($d) {
+ $this->writeByte(11); // write date code
+ $this->writeDouble($d); // write date (milliseconds from 1970)
+ /**
+ * write timezone
+ * ?? this is wierd -- put what you like and it pumps it back into flash at the current GMT ??
+ * have a look at the amf it creates...
+ */
+ $this->writeInt(0);
+ }
+
+ /**
+ * writeNumber writes the number code (0x00) and the numeric data to the output stream
+ * All numbers passed through remoting are floats.
+ *
+ * @param int $d The numeric data
+ */
+ function writeNumber($d) {
+ $this->writeByte(0); // write the number code
+ $this->writeDouble(floatval($d)); // write the number as a double
+ }
+
+ /**
+ * writeNull writes the null code (0x05) to the output stream
+ */
+ function writeNull() {
+ $this->writeByte(5); // null is only a 0x05 flag
+ }
+
+ /**
+ * writeArray first deterines if the PHP array contains all numeric indexes
+ * or a mix of keys. Then it either writes the array code (0x0A) or the
+ * object code (0x03) and then the associated data.
+ *
+ * @param array $d The php array
+ */
+ function writeArray($d)
+ {
+ if($this->writeReferenceIfExists($d))
+ {
+ return;
+ }
+
+ $numeric = array(); // holder to store the numeric keys
+ $string = array(); // holder to store the string keys
+ $len = count($d); // get the total number of entries for the array
+ $largestKey = -1;
+ foreach($d as $key => $data) { // loop over each element
+ if (is_int($key) && ($key >= 0)) { // make sure the keys are numeric
+ $numeric[$key] = $data; // The key is an index in an array
+ $largestKey = max($largestKey, $key);
+ } else {
+ $string[$key] = $data; // The key is a property of an object
+ }
+ }
+ $num_count = count($numeric); // get the number of numeric keys
+ $str_count = count($string); // get the number of string keys
+
+ if ( ($num_count > 0 && $str_count > 0) ||
+ ($num_count > 0 && $largestKey != $num_count - 1)) { // this is a mixed array
+
+ $this->writeByte(8); // write the mixed array code
+ $this->writeLong($num_count); // write the count of items in the array
+ $this->writeObjectFromArray($numeric + $string); // write the numeric and string keys in the mixed array
+ } else if ($num_count > 0) { // this is just an array
+
+ $num_count = count($numeric); // get the new count
+
+ $this->writeByte(10); // write the array code
+ $this->writeLong($num_count); // write the count of items in the array
+ for($i = 0 ; $i < $num_count ; $i++) { // write all of the array elements
+ $this->writeData($numeric[$i]);
+ }
+ } else if($str_count > 0) { // this is an object
+ $this->writeByte(3); // this is an object so write the object code
+ $this->writeObjectFromArray($string); // write the object name/value pairs
+ } else { //Patch submitted by Jason Justman
+
+ $this->writeByte(10); // make this an array still
+ $this->writeInt(0); // give it 0 elements
+ $this->writeInt(0); // give it an element pad, this looks like a bug in Flash,
+ //but keeps the next alignment proper
+ }
+ }
+
+ function writeReferenceIfExists($d)
+ {
+ if(count($this->amf0StoredObjects) >= MAX_STORED_OBJECTS)
+ {
+ return false;
+ }
+ if(is_array($d))
+ {
+ $this->amf0StoredObjects[] = "";
+ return false;
+ }
+ if(($key = patched_array_search($d, $this->amf0StoredObjects, true)) !== FALSE)
+ {
+ $this->writeReference($key);
+ return true;
+ }
+ else
+ {
+ $this->amf0StoredObjects[] = & $d;
+ return false;
+ }
+ }
+
+ function writeReference($num)
+ {
+ $this->writeByte(0x07);
+ $this->writeInt($num);
+ }
+
+ /**
+ * Write a plain numeric array without anything fancy
+ */
+ function writePlainArray($d)
+ {
+ if(!$this->writeReferenceIfExists($d))
+ {
+ $num_count = count($d);
+ $this->writeByte(10); // write the mixed array code
+ $this->writeLong($num_count); // write the count of items in the array
+ for($i = 0 ; $i < $num_count ; $i++) { // write all of the array elements
+ $this->writeData($d[$i]);
+ }
+ }
+ }
+
+ /**
+ * writeObject handles writing a php array with string or mixed keys. It does
+ * not write the object code as that is handled by the writeArray and this method
+ * is shared with the CustomClass writer which doesn't use the object code.
+ *
+ * @param array $d The php array with string keys
+ */
+ function writeObjectFromArray($d) {
+ foreach($d as $key => $data) { // loop over each element
+ $this->writeUTF($key); // write the name of the object
+ $this->writeData($data); // write the value of the object
+ }
+ $this->writeInt(0); // write the end object flag 0x00, 0x00, 0x09
+ $this->writeByte(9);
+ }
+
+ /**
+ * writeObject handles writing a php array with string or mixed keys. It does
+ * not write the object code as that is handled by the writeArray and this method
+ * is shared with the CustomClass writer which doesn't use the object code.
+ *
+ * @param array $d The php array with string keys
+ */
+ function writeAnonymousObject($d) {
+ if(!$this->writeReferenceIfExists($d))
+ {
+ $this->writeByte(3);
+ $objVars = (array) $d;
+ foreach($d as $key => $data) { // loop over each element
+ if($key[0] != "\0")
+ {
+ $this->writeUTF($key); // write the name of the object
+ $this->writeData($data); // write the value of the object
+ }
+ }
+ $this->writeInt(0); // write the end object flag 0x00, 0x00, 0x09
+ $this->writeByte(9);
+ }
+ }
+
+ /**
+ * writePHPObject takes an instance of a class and writes the variables defined
+ * in it to the output stream.
+ * To accomplish this we just blanket grab all of the object vars with get_object_vars
+ *
+ * @param object $d The object to serialize the properties
+ */
+ function writeTypedObject($d) {
+ if($this->writeReferenceIfExists($d))
+ {
+ return;
+ }
+
+ $this->writeByte(16); // write the custom class code
+ $classname = $this->getClassName($d);
+
+ $this->writeUTF($classname); // write the class name
+ if(AMFPHP_PHP5)
+ {
+ $objVars = $d;
+ }
+ else
+ {
+ $objVars = (array) $d;
+ }
+ foreach($objVars as $key => $data) { // loop over each element
+ if($key[0] != "\0")
+ {
+ $this->writeUTF($key); // write the name of the object
+ $this->writeData($data); // write the value of the object
+ }
+ }
+ $this->writeInt(0); // write the end object flag 0x00, 0x00, 0x09
+ $this->writeByte(9);
+ }
+
+ /**
+ * writeRecordSet is the abstracted method to write a custom class recordset object.
+ * Any recordset from any datasource can be written here, it just needs to be properly formatted
+ * beforehand.
+ *
+ * This was unrolled with at the expense of readability for a
+ * 10 fold increase in speed in large recordsets
+ *
+ * @param object $rs The formatted RecordSet object
+ */
+
+ function writeRecordSet(&$rs)
+ {
+ //Low-level everything here to make things faster
+ //This is the bottleneck of AMFPHP, hence the attention in making things faster
+ if($this->writeReferenceIfExists($rs))
+ {
+ return;
+ }
+
+ $ob = "";
+ $data = $rs->rows;
+
+ if($GLOBALS['amfphp']['encoding'] == 'amf0')
+ {
+
+ $this->writeByte(16); // write the custom class code
+ $this->writeUTF("RecordSet"); // write the class name
+ $this->writeUTF("serverInfo");
+
+ //Start writing inner object
+ $this->writeByte(3); // this is an object so write the object code
+
+ //Write total count
+ $this->writeUTF("totalCount");
+ $this->writeNumber($rs->getRowCount());
+
+ //Write initial data
+ $this->writeUTF("initialData");
+
+ //Inner numeric array
+ $colnames = $rs->columns;
+
+ $num_count = count($rs->rows);
+ $this->writeByte(10); // write the mixed array code
+ $this->writeLong($num_count); // write the count of items in the array
+
+ //Allow recordsets to create their own serialized data, which is faster
+ //since the recordset array is traversed only once
+ $numcols = count($colnames);
+
+ $ob = "";
+ $be = $this->isBigEndian;
+ $fc = pack('N', $numcols);
+
+ for($i = 0 ; $i < $num_count ; $i++)
+ {
+ // write all of the array elements
+ $ob .= "\12" . $fc;
+
+ for($j = 0; $j < $numcols; $j++) { // write all of the array elements
+
+ $d = $data[$i][$j];
+ if (is_string($d))
+ { // type as string
+ $os = $this->rsCharsetHandler->transliterate($d);
+ //string flag, string length, and string
+ $ob .= "\2" . pack('n', strlen($os)) . $os;
+ }
+ elseif (is_float($d) || is_int($d))
+ { // type as double
+ $ob .= "\0";
+ $b = pack('d', $d); // pack the bytes
+ if ($be) { // if we are a big-endian processor
+ $r = strrev($b);
+ } else { // add the bytes to the output
+ $r = $b;
+ }
+ $ob .= $r;
+ }
+ elseif (is_bool($d))
+ { //type as bool
+ $ob .= "\1";
+ $ob .= pack('c', $d);
+ }
+ elseif (is_null($d))
+ { // null
+ $ob .= "\5";
+ }
+ }
+ }
+ $this->outBuffer .= $ob;
+
+ //Write cursor
+ $this->writeUTF("cursor");
+ $this->writeNumber(1);
+
+ //Write service name
+ $this->writeUTF("serviceName");
+ $this->writeString("PageAbleResult");
+
+ //Write column names
+ $this->writeUTF("columnNames");
+ $this->writePlainArray($colnames, 'string');
+
+ //Write version number
+ $this->writeUTF("version");
+ $this->writeNumber(1);
+
+ //Write id
+ $this->writeUTF("id");
+ $this->writeString($rs->getID());
+
+ //End inner serverInfo object
+ $this->writeInt(0); // write the end object flag 0x00, 0x00, 0x09
+ $this->writeByte(9);
+
+ //End outer recordset object
+ $this->writeInt(0); // write the end object flag 0x00, 0x00, 0x09
+ $this->writeByte(9);
+
+ $this->paging = -1;
+ }
+ else
+ {
+ $numObjects= 0;
+ $this->writeAmf3ArrayCollectionPreamble();
+
+ //Amf3 array code
+ $this->writeByte(0x09);
+ $numObjects++;
+
+ $numRows = count($rs->rows);
+ $toPack = 2*$numRows + 1;
+
+ //Write the number of rows
+ $this->writeAmf3Int($toPack);
+
+ //No string keys in this array
+ $this->writeByte(0x01);
+
+ $numCols = count($rs->columns);
+
+ $columnStringOffsets = array();
+ if($numRows > 0)
+ {
+ $j = 0;
+ $colNames = array();
+ $rows = $rs->rows;
+
+ foreach ($rows as $key => $line) {
+
+ //Usually we don't use class defs in the serializer since we don't
+ //have sealed objects in php, but for recordsets we do use them
+ //since they are well suited for what we have to do (the same keys
+ //across all objects)
+ if($key == 0)
+ {
+ $this->outBuffer .= "\12";
+ $this->writeAmf3Int($numCols << 4 | 3);
+ $this->outBuffer .= "\1";
+ foreach($rs->columns as $key => $val)
+ {
+ $this->writeAmf3String($val);
+ }
+ $defOffset = $this->getAmf3Int(
+ ($this->storedDefinitions) << 2 | 1
+ );
+ $this->storedDefinitions++;
+ }
+ else
+ {
+ $this->outBuffer .= "\12" . $defOffset;
+ }
+ $numObjects++;
+
+ for($i = 0; $i < $numCols; $i++)
+ {
+ //Write the col name
+ $value = $line[$i];
+ if(is_string($value))
+ {
+ $this->outBuffer .= "\6";
+ $value = $this->rsCharsetHandler->transliterate($value);
+ $this->writeAmf3String($value, true);
+ }
+ elseif(is_int($value))
+ { //int
+ $this->writeAmf3Number($value);
+ }
+ elseif(is_float($value))
+ { //double
+ $this->outBuffer .= "\5";
+ $b = pack("d", $value); // pack the bytes
+ if ($this->isBigEndian) { // if we are a big-endian processor
+ $r = strrev($b);
+ } else { // add the bytes to the output
+ $r = $b;
+ }
+
+ $this->outBuffer .= $r;
+ }
+ elseif(is_bool($value))
+ {
+ $this->outBuffer .= $value ? "\3" : "\2";
+ }
+ else
+ {
+ $this->outBuffer .= "\1"; //null
+ }
+ }
+ //End object
+ }
+ }
+
+ //Add fake objects to make sure the object counter still works
+ for($i = 0; $i < $numObjects; $i++)
+ {
+ $this->storedObjects[] = "";
+ }
+ }
+ }
+
+ /**
+ * writeData checks to see if the type was declared and then either
+ * auto negotiates the type or relies on the user defined type to
+ * serialize the data into amf
+ *
+ * Note that autoNegotiateType was eliminated in order to tame the
+ * call stack which was getting huge and was causing leaks
+ *
+ * manualType allows the developer to explicitly set the type of
+ * the returned data. The returned data is validated for most of the
+ * cases when possible. Some datatypes like xml and date have to
+ * be returned this way in order for the Flash client to correctly serialize them
+ *
+ * recordsets appears top on the list because that will probably be the most
+ * common hit in this method. Followed by the
+ * datatypes that have to be manually set. Then the auto negotiatable types last.
+ * The order may be changed for optimization.
+ *
+ * @param mixed $d The data
+ * @param string $type The optional type
+ */
+ function writeData(& $d) {
+ if (is_int($d) || is_float($d))
+ { // double
+ $this->writeNumber($d);
+ return;
+ }
+ elseif (is_string($d))
+ { // string
+ $this->writeString($d);
+ return;
+ }
+ elseif (is_bool($d))
+ { // boolean
+ $this->writeBoolean($d);
+ return;
+ }
+ elseif (is_null($d))
+ { // null
+ $this->writeNull();
+ return;
+ }
+ elseif ($GLOBALS['amfphp']['encoding'] == 'amf3')
+ {
+ $this->writeByte(0x11);
+ $this->writeAmf3Data($d);
+ return;
+ }
+ elseif (is_array($d))
+ { // array
+ $this->writeArray($d);
+ return;
+ }
+ elseif (is_resource($d))
+ { // resource
+ $type = get_resource_type($d);
+ list($type, $subtype) = $this->sanitizeType($type);
+ }
+ elseif (is_object($d))
+ {
+ $className = strtolower(get_class($d));
+ if(array_key_exists($className, $this->resourceObjects))
+ {
+ $type = "__RECORDSET__";
+ $subtype = $this->resourceObjects[strtolower(get_class($d))];
+ }
+ else if(AMFPHP_PHP5 && $className == 'domdocument')
+ {
+ $this->writeXML($d->saveXml());
+ return;
+ }
+ else if(!AMFPHP_PHP5 && $className == 'domdocument')
+ {
+ $this->writeXML($d->dump_mem());
+ return;
+ }
+ elseif($className == "simplexmlelement")
+ {
+ $this->writeXML($d->asXML());
+ return;
+ }
+ else if($className == 'stdclass' && !isset($d->_explicitType))
+ {
+ $this->writeAnonymousObject($d);
+ return;
+ }
+ elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
+ {
+ $this->writeArray($d);
+ return;
+ }
+ else
+ {
+ $this->writeTypedObject($d);
+ return;
+ }
+ }
+ else
+ {
+ $type = gettype($d);
+ }
+
+ switch ($type) {
+ case "__RECORDSET__" :
+ $classname = $subtype . "Adapter"; // full class name
+ $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
+ if (!$includeFile) {
+ trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
+ }
+ $recordSet = new $classname($d); // returns formatted recordset
+ $this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
+ break;
+ default:
+ // non of the above so lets assume its a Custom Class thats defined in the client
+ $this->writeTypedObject($unsanitizedType, $d);
+ // trigger_error("Unsupported Datatype");
+ break;
+ }
+ }
+
+ /********************************************************************************
+ * AMF3 related code
+ *******************************************************************************/
+
+ function writeAmf3Data(& $d)
+ {
+ if (is_int($d))
+ { //int
+ $this->writeAmf3Number($d);
+ return;
+ }
+ elseif(is_float($d))
+ { //double
+ $this->outBuffer .= "\5";
+ $this->writeDouble($d);
+ return;
+ }
+ elseif (is_string($d))
+ { // string
+ $this->outBuffer .= "\6";
+ $this->writeAmf3String($d);
+ return;
+ }
+ elseif (is_bool($d))
+ { // boolean
+ $this->writeAmf3Bool($d);
+ return;
+ }
+ elseif (is_null($d))
+ { // null
+ $this->writeAmf3Null();
+ return;
+ }
+ elseif (is_array($d))
+ { // array
+ $this->writeAmf3Array($d);
+ return;
+ }
+ elseif (is_resource($d))
+ { // resource
+ $type = get_resource_type($d);
+ list($type, $subtype) = $this->sanitizeType($type);
+ }
+ elseif (is_object($d))
+ {
+ $className = strtolower(get_class($d));
+ if(array_key_exists($className, $this->resourceObjects))
+ {
+ $type = "__RECORDSET__";
+ $subtype = $this->resourceObjects[strtolower(get_class($d))];
+ }
+ else if(AMFPHP_PHP5 && $className == 'domdocument')
+ {
+ $this->writeAmf3Xml($d->saveXml());
+ return;
+ }
+ else if(!AMFPHP_PHP5 && $className == 'domdocument')
+ {
+ $this->writeAmf3Xml($d->dump_mem());
+ return;
+ }
+ elseif($className == "simplexmlelement")
+ {
+ $this->writeAmf3Xml($d->asXML());
+ return;
+ }
+ elseif($className == 'bytearray')
+ {
+ $this->writeAmf3ByteArray($d->data);
+ return;
+ }
+ elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
+ {
+ $this->writeAmf3Array($d, true);
+ return;
+ }
+ else
+ {
+ $this->writeAmf3Object($d);
+ return;
+ }
+ }
+ else
+ {
+ $type = gettype($d);
+ }
+
+ switch ($type) {
+ case "__RECORDSET__" :
+ $classname = $subtype . "Adapter"; // full class name
+ $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
+ if (!$includeFile) {
+ trigger_error("The recordset filter class " . $classname . " was not found");
+ }
+ $GLOBALS['amfphp']['stringOffset'] = count($this->storedStrings);
+ $recordSet = new $classname($d); // returns formatted recordset
+
+ $this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
+ break;
+ default:
+ // non of the above so lets assume its a Custom Class thats defined in the client
+ //$this->writeTypedObject($unsanitizedType, $d);
+ trigger_error("Unsupported Datatype: " . $type);
+ break;
+ }
+ }
+
+ /**
+ * Write an ArrayCollection
+ */
+ function writeAmf3ArrayCollectionPreamble()
+ {
+ $this->writeByte(0x0a);
+ $this->writeByte(0x07);
+ $this->writeAmf3String("flex.messaging.io.ArrayCollection");
+ $this->storedDefinitions++;
+ $this->storedObjects[] = "";
+ }
+
+ function writeAmf3Null()
+ {
+ //Write the null code (0x1) to the output stream.
+ $this->outBuffer .= "\1";
+ }
+
+ function writeAmf3Bool($d)
+ {
+ $this->outBuffer .= $d ? "\3" : "\2";
+ }
+
+ function writeAmf3Int($d)
+ {
+ //Sign contraction - the high order bit of the resulting value must match every bit removed from the number
+ //Clear 3 bits
+ $d &= 0x1fffffff;
+ if($d < 0x80)
+ {
+ $this->outBuffer .= chr($d);
+ }
+ elseif($d < 0x4000)
+ {
+ $this->outBuffer .= chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
+ }
+ elseif($d < 0x200000)
+ {
+ $this->outBuffer .= chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
+ }
+ else
+ {
+ $this->outBuffer .= chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) .
+ chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
+ }
+ }
+
+ function writeAmf3String($d, $raw = false)
+ {
+ if( $d == "" )
+ {
+ //Write 0x01 to specify the empty ctring
+ $this->outBuffer .= "\1";
+ }
+ else
+ {
+ if( !isset($this->storedStrings[$d]))
+ {
+ if(strlen($d) < 64)
+ {
+ $this->storedStrings[$d] = $this->encounteredStrings;
+ }
+ if(!$raw)
+ {
+ $d = $this->charsetHandler->transliterate($d);
+ }
+
+ $handle = strlen($d);
+ $this->writeAmf3Int($handle*2 + 1);
+ $this->outBuffer .= $d;
+ $this->encounteredStrings++;
+ return $this->encounteredStrings - 1;
+ }
+ else
+ {
+ $key = $this->storedStrings[$d];
+ $handle = $key << 1;
+ $this->writeAmf3Int($handle);
+ return $key;
+ }
+ }
+ }
+
+ function writeAmf3Array($d, $arrayCollectionable = false)
+ {
+ //Circular referencing is disabled in arrays
+ //Because if the array contains only primitive values,
+ //Then === will say that the two arrays are strictly equal
+ //if they contain the same values, even if they are really distinct
+ //if(($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE )
+ //{
+ if(count($this->storedObjects) < MAX_STORED_OBJECTS)
+ {
+ $this->storedObjects[] = & $d;
+ }
+
+ $numeric = array(); // holder to store the numeric keys
+ $string = array(); // holder to store the string keys
+ $len = count($d); // get the total number of entries for the array
+ $largestKey = -1;
+ foreach($d as $key => $data) { // loop over each element
+ if (is_int($key) && ($key >= 0)) { // make sure the keys are numeric
+ $numeric[$key] = $data; // The key is an index in an array
+ $largestKey = max($largestKey, $key);
+ } else {
+ $string[$key] = $data; // The key is a property of an object
+ }
+ }
+ $num_count = count($numeric); // get the number of numeric keys
+ $str_count = count($string); // get the number of string keys
+
+ if (($str_count > 0 && $num_count == 0) ||
+ ($num_count > 0 && $largestKey != $num_count - 1)) { // this is a mixed array
+ $this->writeAmf3ObjectFromArray($numeric + $string); // write the numeric and string keys in the mixed array
+ } else { // this is just an array
+ if($arrayCollectionable)
+ {
+ $this->writeAmf3ArrayCollectionPreamble();
+ }
+
+ $num_count = count($numeric);
+
+ $this->outBuffer .= "\11";
+ $handle = $num_count * 2 + 1;
+ $this->writeAmf3Int($handle);
+
+ foreach($string as $key => $val)
+ {
+ $this->writeAmf3String($key);
+ $this->writeAmf3Data($val);
+ }
+ $this->writeAmf3String(""); //End start hash
+
+ for($i = 0; $i < $num_count; $i++)
+ {
+ $this->writeAmf3Data($numeric[$i]);
+ }
+ }
+ //}
+ //else
+ //{
+ // $handle = $key << 1;
+ // $this->outBuffer .= "\11";
+ // $this->writeAmf3Int($handle);
+ //}
+ }
+
+ function writeAmf3ObjectFromArray($d)
+ {
+ //Type this as a dynamic object
+ $this->outBuffer .= "\12\13\1";
+
+ foreach($d as $key => $val)
+ {
+ $this->writeAmf3String($key);
+ $this->writeAmf3Data($val);
+ }
+ //Now we close the open object
+ $this->outBuffer .= "\1";
+ }
+
+ /*
+ public void WriteAMF3DateTime(DateTime value)
+ {
+ if( !_objectReferences.Contains(value) )
+ {
+ _objectReferences.Add(value, _objectReferences.Count);
+ int handle = 1;
+ WriteAMF3IntegerData(handle);
+
+ // Write date (milliseconds from 1970).
+ DateTime timeStart = new DateTime(1970, 1, 1, 0, 0, 0);
+
+ string timezoneCompensation = System.Configuration.ConfigurationSettings.AppSettings["timezoneCompensation"];
+ if( timezoneCompensation != null && ( timezoneCompensation.ToLower() == "auto" ) )
+ {
+ value = value.ToUniversalTime();
+ }
+
+ TimeSpan span = value.Subtract(timeStart);
+ long milliSeconds = (long)span.TotalMilliseconds;
+ long date = BitConverter.DoubleToInt64Bits((double)milliSeconds);
+ this.WriteLong(date);
+ }
+ else
+ {
+ int handle = (int)_objectReferences[value];
+ handle = handle << 1;
+ WriteAMF3IntegerData(handle);
+ }
+ }
+ */
+
+ function getAmf3Int($d)
+ {
+ $d &= 0x1fffffff;
+ if($d < 0x80)
+ {
+ return chr($d);
+ }
+ elseif($d < 0x4000)
+ {
+ return chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
+ }
+ elseif($d < 0x200000)
+ {
+ return chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
+ }
+ else
+ {
+ return chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) .
+ chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
+ }
+ }
+
+ function writeAmf3Number($d)
+ {
+ if($d >= -268435456 && $d <= 268435455)//check valid range for 29bits
+ {
+ $this->outBuffer .= "\4";
+ $this->writeAmf3Int($d);
+ }
+ else
+ {
+ //overflow condition would occur upon int conversion
+ $this->outBuffer .= "\5";
+ $this->writeDouble($d);
+ }
+ }
+
+ function writeAmf3Xml($d)
+ {
+ $d = preg_replace('/\>(\n|\r|\r\n| |\t)*\','><',trim($d));
+ $this->writeByte(0x07);
+ $this->writeAmf3String($d);
+ }
+
+ function writeAmf3ByteArray($d)
+ {
+ $this->writeByte(0x0C);
+ $this->writeAmf3String($d, true);
+ }
+
+ function writeAmf3Object($d)
+ {
+ //Write the object tag
+ $this->outBuffer .= "\12";
+ if( ($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE && $key === FALSE)
+ {
+ if(count($this->storedObjects) < MAX_STORED_OBJECTS)
+ {
+ $this->storedObjects[] = & $d;
+ }
+
+ $this->storedDefinitions++;
+
+ //Type the object as an array
+ if(AMFPHP_PHP5)
+ {
+ $obj = $d;
+ }
+ else
+ {
+ $obj = (array) $d;
+ }
+ $realObj = array();
+ foreach($obj as $key => $val)
+ {
+ if($key[0] != "\0" && $key != '_explicitType') //Don't show private members
+ {
+ $realObj[$key] = $val;
+ }
+ }
+
+ //Type this as a dynamic object
+ $this->outBuffer .= "\13";
+
+ $classname = $this->getClassName($d);
+
+ $this->writeAmf3String($classname);
+
+ foreach($realObj as $key => $val)
+ {
+ $this->writeAmf3String($key);
+ $this->writeAmf3Data($val);
+ }
+ //Now we close the open object
+ $this->outBuffer .= "\1";
+ }
+ else
+ {
+ $handle = $key << 1;
+ $this->writeAmf3Int($handle);
+ }
+ }
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/core/amf/readme.txt b/core/amf/readme.txt
new file mode 100755
index 0000000..7658f0a
--- /dev/null
+++ b/core/amf/readme.txt
@@ -0,0 +1,3 @@
+The files located here are part of the amfphp core functionality.
+
+Unless you want to patch or add functionality to amfphp, you probably don't want to play with these files.
\ No newline at end of file
diff --git a/core/amf/util/AMFObject.php b/core/amf/util/AMFObject.php
new file mode 100755
index 0000000..f7d7ebf
--- /dev/null
+++ b/core/amf/util/AMFObject.php
@@ -0,0 +1,207 @@
+rawData = $rawData;
+ $this->outputStream = "";
+ $this->_incomingHeaders = array();
+ $this->_outgoingHeaders = array();
+ $this->_bodys = array();
+ $this->_headerTable = array();
+ }
+
+ /**
+ * addHeader places a new header into the pool of headers.
+ *
+ * Each header has 3 properties, they header key, the required flag
+ * and the data associated with the header.
+ *
+ * @param object $header The AMFHeader object to add to the list
+ */
+ function addHeader(&$header) {
+ //$len = array_push($this->_incomingHeaders, $header);
+ $this->_incomingHeaders[] = $header;
+ $name = $header->name;
+ $this->_headerTable[$name] = $header;
+ }
+
+ /**
+ * addOutgoingHeader places a new header into the pool of outbound headers.
+ *
+ * Each header has 3 properties, they header key, the required flag
+ * and the data associated with the header.
+ *
+ * @param object $header The AMFHeader object to add to the list
+ */
+ function addOutgoingHeader(&$header) {
+ //$len = array_push($this->_outgoingHeaders, $header);
+ $this->_outgoingHeaders[] = $header;
+ }
+
+ /**
+ * getHeader returns a header record for a given key
+ *
+ * @param string $key The header key
+ * @return mixed The header record
+ */
+ function getHeader ($key) {
+ if (isset($this->_headerTable[$key])) {
+ return $this->_headerTable[$key];
+ }
+ return false;
+ }
+
+ /**
+ * Gets the number of headers for this amf packet
+ *
+ * @return int The header count
+ */
+ function numHeader() {
+ return count($this->_incomingHeaders);
+ }
+
+ /**
+ * Gets the number of outgoing headers for this amf packet
+ *
+ * @return int The header count
+ */
+ function numOutgoingHeader() {
+ return count($this->_outgoingHeaders);
+ }
+
+ /**
+ * Get the header at the specified position.
+ *
+ * If you pass an id this method will return the header
+ * located at that id, otherwise it will return the first header
+ *
+ * @param int $id Optional id field
+ * @return array The header object
+ */
+ function &getHeaderAt($id = 0) {
+ return $this->_incomingHeaders[$id];
+ }
+
+ /**
+ * Get the header at the specified position from the outgoing header queue.
+ *
+ * If you pass an id this method will return the header
+ * located at that id, otherwise it will return the first header
+ *
+ * @param int $id Optional id field
+ * @return array The header object
+ */
+ function &getOutgoingHeaderAt($id = 0) {
+ return $this->_outgoingHeaders[$id];
+ }
+
+ /**
+ * addBody has the job of adding a new body element to the bodys array.
+ *
+ * @param string $t The target URI
+ * @param string $r The response URI
+ * @param mixed $v The value of the object
+ * @param string $ty The type of the results
+ * @param int $ps The pagesize of a recordset
+ */
+ function addBody($body) {
+ $this->_bodys[] = $body;
+ }
+
+ /**
+ * addBodyAt provides an interface to push a body element to a desired
+ * position in the array.
+ *
+ * @param int $pos The position to add the body element
+ * @param AMFBody $body The body element to add
+ */
+ function addBodyAt($pos, $body) {
+ array_splice($this->_bodys, $pos, 0, array($body)); // splice the new body into the array
+ }
+
+ /**
+ * removeBodyAt provides an interface to remove a body element to a desired
+ * position in the array.
+ *
+ * @param int $pos The position to add the body element
+ * @param AMFBody $body The body element to add
+ */
+ function removeBodyAt($pos) {
+ array_splice($this->_bodys, $pos, 1); // splice the new body into the array
+ }
+
+ /**
+ * numBody returns the total number of body elements. There is one body
+ * element for each method call.
+ *
+ * @return int The number of body elements
+ */
+ function numBody() {
+ return count($this->_bodys);
+ }
+
+ /**
+ * getBodyAt returns the current body element the specified position.
+ *
+ * If a integer is passed this method will return the element at the given position.
+ * Otherwise the first element will be returned.
+ *
+ * @param int $id The id of the body element desired
+ * @return array The body element
+ */
+ function &getBodyAt($id = 0) {
+ return $this->_bodys[$id];
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/util/DateWrapper.php b/core/amf/util/DateWrapper.php
new file mode 100755
index 0000000..63e6f9e
--- /dev/null
+++ b/core/amf/util/DateWrapper.php
@@ -0,0 +1,83 @@
+_date = $input/1000;
+ }
+ else
+ {
+ $this->_date = time();
+ }
+ }
+
+ /**
+ * Get date according to client timezone
+ */
+ function getClientDate()
+ {
+ return $this->_date + DateWrapper::getTimezone();
+ }
+
+ /**
+ * Get date according to server timezone
+ */
+ function getServerDate()
+ {
+ return ($this->_date + date("Z"));
+ }
+
+ /**
+ * Get raw date
+ */
+ function getRawDate()
+ {
+ return $this->_date;
+ }
+
+ /**
+ * Set utc date
+ */
+ function setDate($input)
+ {
+ $this->_date = $input;
+ }
+
+ /**
+ * Get timezone
+ */
+ function getTimezone($val=NULL)
+ {
+ static $timezone = 0;
+ if($val != NULL)
+ {
+ $timezone = $val;
+ }
+ return $timezone;
+ }
+
+ /**
+ * Set timezone
+ */
+ function setTimezone($val=0){
+ return DateWrapper::getTimezone($val);
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/util/DescribeService.php b/core/amf/util/DescribeService.php
new file mode 100755
index 0000000..6a0a370
--- /dev/null
+++ b/core/amf/util/DescribeService.php
@@ -0,0 +1,74 @@
+methodTable as $key => $value) {
+ if ($value["access"] = "remote") {
+ $args = array();
+ if(is_array($value["arguments"]) && count($value["arguments"]) >= 1)
+ {
+ foreach($value["arguments"] as $key2 => $arg)
+ {
+ if(is_array($arg))
+ {
+ $args[] = array("name" => $key2,
+ "required" => $arg['required'] ? 'true' : 'false',
+ "type" => $arg['type'],
+ "description" => $arg['description']
+ );
+ }
+ else
+ {
+ $args[] = array("name" => $arg,
+ "required" => "true",
+ "type" => "undefined");
+ }
+ }
+ }
+
+ if( !isset( $value["returns"] ) )
+ {
+ $returns = 'undefined';
+ }
+ else
+ {
+ $returns = $value["returns"];
+ }
+
+ $description["functions"][] = array(
+ "description" => $value["description"],
+ "name" => $key,
+ "version" => "1.0",
+ "returns" => $returns,
+ "arguments" => $args
+ );
+ }
+ }
+ return $description;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/util/PageAbleResult.php b/core/amf/util/PageAbleResult.php
new file mode 100755
index 0000000..d1c0af0
--- /dev/null
+++ b/core/amf/util/PageAbleResult.php
@@ -0,0 +1,84 @@
+methodTable = array("getRecords" => array("access" => "remote",
+ "returns" => "__RECORDSETPAGE__"
+ ),
+ "release" => array("access" => "remote"
+ )
+ );
+ }
+ /**
+ * Collects the page of the recordset from the session and returns it along
+ * with the cursor position of the first record.
+ *
+ * @param string $id The session id
+ * @param int $c The cursor position
+ * @param int $ps The page size
+ * @return array Contains the cursor position of the first record and the page data
+ */
+ function getRecords($id, $c, $ps) {
+ $keys = explode("=", $id);
+ $currset = intval($keys[1]);
+ session_id($keys[0]);
+ session_start();
+ $pageData = array();
+ $pageData['Cursor'] = $c;
+ $pageData['Page'] = array_slice($_SESSION['amfphp_recordsets'][$currset]['data'], $c - 1, $ps);
+
+ for($i = 0; $i < $ps; $i++)
+ {
+ $_SESSION['amfphp_recordsets'][$currset]['indexes'][$c + $i] = true;
+ }
+ return $pageData;
+ }
+ /**
+ * Unsets the recordset data from the session
+ * Flash, for some reason does not give back the recordid, so it's difficult to see
+ * what exactly is going on, this is why we store sent data in another session var
+ *
+ */
+ function release() {
+ foreach($_SESSION['amfphp_recordsets'] as $key => $value)
+ {
+ $found = false;
+ foreach($value['indexes'] as $recordid => $recordsent)
+ {
+ if(!$recordsent)
+ {
+ $found = true;
+ break;
+ }
+ }
+ if(!$found)
+ {
+ //Release recordset
+ unset($_SESSION['amfphp_recordsets'][$key]);
+ }
+ }
+ return;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/amf/util/TraceHeader.php b/core/amf/util/TraceHeader.php
new file mode 100755
index 0000000..b0b19bc
--- /dev/null
+++ b/core/amf/util/TraceHeader.php
@@ -0,0 +1,38 @@
+EventType = "trace";
+ $this->Time = time();
+ $this->Source = "Server";
+ $this->Date = array(date("D M j G:i:s T O Y"));
+ $this->messages = $traceStack;
+ }
+}
+
+class ProfilingHeader {
+ function ProfilingHeader() {
+ global $amfphp;
+ $this->EventType = "profiling";
+
+ $this->includeTime = (int) ($amfphp['includeTime']*1000);
+ $this->decodeTime = (int) ($amfphp['decodeTime']*1000);
+ $this->callTime = (int) ($amfphp['callTime']*1000);
+ $this->totalTime = -268435457;
+ $this->frameworkTime = ((int) ($amfphp['totalTime']*1000))
+ - $this->includeTime
+ - $this->decodeTime
+ - $this->callTime;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/amf/util/WrapperClasses.php b/core/amf/util/WrapperClasses.php
new file mode 100755
index 0000000..0a605a3
--- /dev/null
+++ b/core/amf/util/WrapperClasses.php
@@ -0,0 +1,87 @@
+data = $data;
+ }
+}
+
+class RecordSet
+{
+ var $data;
+
+ function RecordSet($data)
+ {
+ $this->data = $data;
+ }
+}
+
+
+class PageableRecordSet
+{
+ var $data;
+ var $limit;
+
+ function PageableRecordSet($data, $limit = 15)
+ {
+ $this->data = $data;
+ $this->limit = $limit;
+ }
+}
+
+class AcknowledgeMessage
+{
+ var $_explicitType = "flex.messaging.messages.AcknowledgeMessage";
+
+ function AcknowledgeMessage($messageId = NULL, $clientId = NULL)
+ {
+ $this->messageId = $this->generateRandomId();
+ $this->clientId = $clientId != NULL ? $clientId : $this->generateRandomId();
+ $this->destination = null;
+ $this->body = null;
+ $this->timeToLive = 0;
+ $this->timestamp = (int) (time() . '00');
+ $this->headers = new stdClass();
+ $this->correlationId = $messageId;
+ }
+
+ function generateRandomId()
+ {
+ // version 4 UUID
+ return sprintf(
+ '%08X-%04X-%04X-%02X%02X-%012X',
+ mt_rand(),
+ mt_rand(0, 65535),
+ bindec(substr_replace(
+ sprintf('%016b', mt_rand(0, 65535)), '0100', 11, 4)
+ ),
+ bindec(substr_replace(sprintf('%08b', mt_rand(0, 255)), '01', 5, 2)),
+ mt_rand(0, 255),
+ mt_rand()
+ );
+ }
+}
+
+class CommandMessage
+{
+ var $_explicitType = 'flex.messaging.messages.CommandMessage';
+}
+
+class RemotingMessage
+{
+ var $_explicitType = 'flex.messaging.messages.RemotingMessage';
+}
+
+class ErrorMessage
+{
+ var $_explicitType = "flex.messaging.messages.ErrorMessage";
+ var $correlationId;
+ var $faultCode;
+ var $faultDetail;
+ var $faultString;
+}
+
+?>
\ No newline at end of file
diff --git a/core/json/app/Actions.php b/core/json/app/Actions.php
new file mode 100755
index 0000000..26e438f
--- /dev/null
+++ b/core/json/app/Actions.php
@@ -0,0 +1,119 @@
+getValue();
+ $target = $args[0];
+
+ $baseClassPath = $GLOBALS['amfphp']['classPath'];
+
+ $lpos = strrpos($target, '.');
+
+ $methodname = substr($target, $lpos + 1);
+ $trunced = substr($target, 0, $lpos);
+ $lpos = strrpos($trunced, ".");
+ if ($lpos === false) {
+ $classname = $trunced;
+ $uriclasspath = $trunced . ".php";
+ $classpath = $baseClassPath . $trunced . ".php";
+ } else {
+ $classname = substr($trunced, $lpos + 1);
+ $classpath = $baseClassPath . str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
+ $uriclasspath = str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
+ }
+
+ $body->methodName = $methodname;
+ $body->className = $classname;
+ $body->classPath = $classpath;
+ $body->uriClassPath = $uriclasspath;
+
+ //Now deserialize the arguments
+ array_shift($args);
+
+ $actualArgs = array();
+
+ foreach($args as $key => $value)
+ {
+ //Look at the value to see if it is JSON-encoded
+ $value = urldecode($value);
+ if($value != "")
+ {
+ if($value[0] != '[' && $value[0] != '{' && $value != "null" && $value != "false" && $value != "true")
+ {
+ //check to see if it is a number
+ $char1 = ord($value[0]);
+ if($char1 >= 0x30 && $char1 <= 0x39)
+ {
+ //Then this is a number
+ $value = json_decode($value, true);
+ } //Else leave value as is
+ }
+ else
+ {
+ $value = json_decode($value, true);
+ }
+ }
+ $actualArgs[] = $value;
+ }
+
+ $body->setValue($actualArgs);
+}
+
+function executionAction(& $body)
+{
+ $classConstruct = &$body->getClassConstruct();
+ $methodName = $body->methodName;
+ $args = $body->getValue();
+
+ $output = Executive::doMethodCall($body, &$classConstruct, $methodName, $args);
+
+ if($output !== "__amfphp_error")
+ {
+ $body->setResults($output);
+ }
+}
+
+
+function serializationAction(& $body)
+{
+ //Take the raw response
+ $rawResponse = & $body->getResults();
+
+ adapterMap($rawResponse);
+
+ //Now serialize it
+ $encodedResponse = json_encode($rawResponse);
+
+ if(count(NetDebug::getTraceStack()) > 0)
+ {
+ $trace = "/*" . implode("\n", NetDebug::getTraceStack()) . "*/";
+ $encodedResponse = $trace . "\n" . $encodedResponse;
+ }
+
+ $body->setResults($encodedResponse);
+}
+
+if(!function_exists("json_encode"))
+{
+ include_once(AMFPHP_BASE . "shared/util/JSON.php");
+
+ function json_encode($val)
+ {
+ $json = new Services_JSON();
+ return $json->encode($val);
+ }
+
+ function json_decode($val, $asAssoc = FALSE)
+ {
+ if($asAssoc)
+ {
+ $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
+ }
+ else
+ {
+ $json = new Services_JSON();
+ }
+ return $json->decode($val);
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/json/app/Gateway.php b/core/json/app/Gateway.php
new file mode 100755
index 0000000..2f49fe7
--- /dev/null
+++ b/core/json/app/Gateway.php
@@ -0,0 +1,48 @@
+setValue($rawArgs);
+ return $body;
+ }
+
+ /**
+ * Create the chain of actions
+ */
+ function registerActionChain()
+ {
+ $this->actions['deserialization'] = 'deserializationAction';
+ $this->actions['classLoader'] = 'classLoaderAction';
+ $this->actions['security'] = 'securityAction';
+ $this->actions['exec'] = 'executionAction';
+ $this->actions['serialization'] = 'serializationAction';
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/RecordSetAdapter.php b/core/shared/adapters/RecordSetAdapter.php
new file mode 100755
index 0000000..b07da32
--- /dev/null
+++ b/core/shared/adapters/RecordSetAdapter.php
@@ -0,0 +1,63 @@
+_resultResource = $d;
+ $this->_charsetHandler = new CharsetHandler('sqltophp');
+ $this->_directCharsetHandler = new CharsetHandler('sqltoflash');
+ $this->isBigEndian = AMFPHP_BIG_ENDIAN;
+ }
+
+ /**
+ * getter for the number of rows
+ *
+ * @return int The number of rows
+ */
+ function getRowCount () {
+ if($this->numRows == -1)
+ {
+ $this->numRows = count($this->rows);
+ }
+ return $this->numRows;
+ }
+
+ function getID() {
+ return md5(microtime());
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/adodbAdapter.php b/core/shared/adapters/adodbAdapter.php
new file mode 100755
index 0000000..dcff29c
--- /dev/null
+++ b/core/shared/adapters/adodbAdapter.php
@@ -0,0 +1,39 @@
+FieldCount(); // grab the number of fields
+
+ for($i = 0; $i < $fieldcount; $i++) { // loop over all of the fields
+ $fld = $d->FetchField($i);
+ $this->columns[] = $fld->name;
+ }
+
+ $d->MoveFirst();
+ $this->rows = $d->GetArray();
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/doctrineAdapter.php b/core/shared/adapters/doctrineAdapter.php
new file mode 100755
index 0000000..242c559
--- /dev/null
+++ b/core/shared/adapters/doctrineAdapter.php
@@ -0,0 +1,39 @@
+ 0)
+ {
+ $firstRow = true;
+ foreach($d as $row)
+ {
+ $this->rows[] = array_values($row->toArray());
+ if($firstRow)
+ {
+ $this->columns = array_keys($row->toArray());
+ $firstRow = false;
+ }
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/fbsqlAdapter.php b/core/shared/adapters/fbsqlAdapter.php
new file mode 100755
index 0000000..18a06f9
--- /dev/null
+++ b/core/shared/adapters/fbsqlAdapter.php
@@ -0,0 +1,43 @@
+columns[] = fbsql_field_name($d, $i);
+ }
+
+ if(fbsql_num_rows($d) > 0)
+ {
+ fbsql_data_seek($d, 0);
+ while ($line = fbsql_fetch_row($d)) {
+ $this->rows[] = $line;
+ }
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/informixAdapter.php b/core/shared/adapters/informixAdapter.php
new file mode 100755
index 0000000..322c91a
--- /dev/null
+++ b/core/shared/adapters/informixAdapter.php
@@ -0,0 +1,47 @@
+columns[$i] = key($properties);
+ next($properties);
+ }
+
+ if(ifx_num_rows($d) > 0)
+ {
+ $line = ifx_fetch_row($d,"FIRST");
+ do
+ {
+ $this->rows[] = $line;
+ } while ($line = ifx_fetch_row($d,"NEXT") );
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/mssqlAdapter.php b/core/shared/adapters/mssqlAdapter.php
new file mode 100755
index 0000000..864750e
--- /dev/null
+++ b/core/shared/adapters/mssqlAdapter.php
@@ -0,0 +1,41 @@
+columnNames[] = mssql_field_name($d, $i);
+ }
+
+ if(mssql_num_rows($d) > 0)
+ {
+ mssql_data_seek($d, 0);
+ while ($line = mssql_fetch_row($d)) {
+ $this->rows[] = $line;
+ }
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/mysqlAdapter.php b/core/shared/adapters/mysqlAdapter.php
new file mode 100755
index 0000000..d95e8e1
--- /dev/null
+++ b/core/shared/adapters/mysqlAdapter.php
@@ -0,0 +1,49 @@
+columns[] = mysql_field_name($d, $i);
+ $type = mysql_field_type($d, $i);
+ if(in_array($type, array('int', 'real', 'year')))
+ {
+ $intFields[] = $i;
+ }
+ }
+
+ while($row = mysql_fetch_row($d))
+ {
+ foreach($intFields as $key => $val)
+ {
+ $row[$val] = (float) $row[$val];
+ }
+ $this->rows[] = $row;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/mysqliAdapter.php b/core/shared/adapters/mysqliAdapter.php
new file mode 100755
index 0000000..812d103
--- /dev/null
+++ b/core/shared/adapters/mysqliAdapter.php
@@ -0,0 +1,41 @@
+columns[] = $field->name;
+ }
+
+ if(mysqli_num_rows($d) > 0)
+ {
+ mysqli_data_seek($d, 0);
+ while ($line = mysqli_fetch_row($d)) {
+ $this->rows[] = $line;
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/mysqliobjectAdapter.php b/core/shared/adapters/mysqliobjectAdapter.php
new file mode 100755
index 0000000..d2bf2db
--- /dev/null
+++ b/core/shared/adapters/mysqliobjectAdapter.php
@@ -0,0 +1,41 @@
+fetch_field())
+ {
+ $this->columns[] = $field->name;
+ }
+
+ if($d->num_rows > 0)
+ {
+ $d->data_seek(0);
+ while ($line = $d->fetch_row()) {
+ $this->rows[] = $line;
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/oci8Adapter.php b/core/shared/adapters/oci8Adapter.php
new file mode 100755
index 0000000..e6825c4
--- /dev/null
+++ b/core/shared/adapters/oci8Adapter.php
@@ -0,0 +1,37 @@
+columnNames[] = ocicolumnname($d, $j+1);
+ }
+
+ $i = 0;
+ while ( OCIFetchInto($d,$line,OCI_NUM+OCI_RETURN_LOBS+OCI_RETURN_NULLS)) {
+ $this->rows[] = $line;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/odbcAdapter.php b/core/shared/adapters/odbcAdapter.php
new file mode 100755
index 0000000..24d3828
--- /dev/null
+++ b/core/shared/adapters/odbcAdapter.php
@@ -0,0 +1,44 @@
+columns[] = odbc_field_name($d, $i + 1);
+ }
+
+ if(odbc_num_rows($d) > 0)
+ {
+ $line = odbc_fetch_row($d, 0);
+ do {
+ $this->rows[] = $line;
+ } while ($line = odbc_fetch_row($d));
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/pdoAdapter.php b/core/shared/adapters/pdoAdapter.php
new file mode 100755
index 0000000..0647579
--- /dev/null
+++ b/core/shared/adapters/pdoAdapter.php
@@ -0,0 +1,49 @@
+fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, 0);
+ if($line != null)
+ {
+ $colNum = 0;
+ $firstLine = array();
+ foreach($line as $k => $v)
+ {
+ $this->columns[$colNum] = $k;
+ $firstLine[] = $v;
+ $colNum++;
+ }
+
+ $lastLines = $d->fetchAll(PDO::FETCH_NUM);
+ if($lastLines == NULL)
+ {
+ $this->rows = array($firstLine);
+ }
+ else
+ {
+ array_unshift($lastLines, $firstLine);
+ $this->rows = $lastLines;
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/peardbAdapter.php b/core/shared/adapters/peardbAdapter.php
new file mode 100755
index 0000000..e505cae
--- /dev/null
+++ b/core/shared/adapters/peardbAdapter.php
@@ -0,0 +1,56 @@
+numCols();
+
+ $intFields = array();
+ $info = $d->dbh->tableInfo($d);
+ for($i = 0; $i < $fieldcount; $i++) {
+ $this->columnNames[$i] = $this->_charsetHandler->transliterate($info[$i]['name']);
+
+ $type = $info[$i]['type'];
+ if(in_array($type, array('int', 'real', 'year')))
+ {
+ $intFields[] = $i;
+ }
+ }
+
+ if($d->numRows() > 0)
+ {
+ $line = $d->fetchRow(DB_FETCHMODE_ORDERED, 0);
+ do {
+ foreach($intFields as $key => $val)
+ {
+ $line[$val] = (float) $line[$val];
+ }
+ $this->rows[] = $line;
+ } while ($line = $d->fetchRow(DB_FETCHMODE_ORDERED, $rows));
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/pgsqlAdapter.php b/core/shared/adapters/pgsqlAdapter.php
new file mode 100755
index 0000000..9be3d8a
--- /dev/null
+++ b/core/shared/adapters/pgsqlAdapter.php
@@ -0,0 +1,46 @@
+columns[] = pg_field_name($d, $i);
+ }
+
+ if(pg_num_rows($d) > 0)
+ {
+ pg_result_seek($d, 0);
+ while($line = pg_fetch_row($d)) {
+ $this->rows[] = $line;
+ }
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/plainrecordsetAdapter.php b/core/shared/adapters/plainrecordsetAdapter.php
new file mode 100755
index 0000000..49da450
--- /dev/null
+++ b/core/shared/adapters/plainrecordsetAdapter.php
@@ -0,0 +1,47 @@
+data;
+
+ parent::RecordSetAdapter($d);
+
+ if(count($d) > 0)
+ {
+ $columns = array_keys($d[0]);
+ }
+
+ $this->columns = $columns;
+
+ foreach($d as $key => $val)
+ {
+ $row = array();
+ foreach($columns as $key2 => $val2)
+ {
+ $row[] = $val[$val2];
+ }
+ $this->rows[] = $row;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/sqliteAdapter.php b/core/shared/adapters/sqliteAdapter.php
new file mode 100755
index 0000000..afe6f3c
--- /dev/null
+++ b/core/shared/adapters/sqliteAdapter.php
@@ -0,0 +1,50 @@
+columns[] = sqlite_field_name($d, $i);
+ }
+
+ if(sqlite_num_rows($d) > 0)
+ {
+ $this->rows = sqlite_fetch_all($d, SQLITE_NUM);
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/sqliteobjectAdapter.php b/core/shared/adapters/sqliteobjectAdapter.php
new file mode 100755
index 0000000..9c6876f
--- /dev/null
+++ b/core/shared/adapters/sqliteobjectAdapter.php
@@ -0,0 +1,50 @@
+numFields();
+
+ // loop over all of the fields
+ for($i=0; $i<$fieldcount; $i++) {
+ // decode each field name ready for encoding when it goes through serialization
+ // and save each field name into the array
+ $this->columns[] = $d->fieldName($i);
+ }
+
+ if($d->numRows() > 0)
+ {
+ $this->rows = $d->fetchAll(SQLITE_NUM);
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/zendrowAdapter.php b/core/shared/adapters/zendrowAdapter.php
new file mode 100755
index 0000000..0fccfe3
--- /dev/null
+++ b/core/shared/adapters/zendrowAdapter.php
@@ -0,0 +1,29 @@
+toArray();
+ $this->columns = array_keys($val);
+ $this->rows[] = array_values($val);
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/adapters/zendrowsetAdapter.php b/core/shared/adapters/zendrowsetAdapter.php
new file mode 100755
index 0000000..f51787d
--- /dev/null
+++ b/core/shared/adapters/zendrowsetAdapter.php
@@ -0,0 +1,41 @@
+count() > 0)
+ {
+ $d->rewind();
+ $firstRow = $d->current()->toArray();
+
+ $this->columns = array_keys($firstRow);
+
+ //Note: foreach resets array iterator pointer
+
+ foreach($d as $row)
+ {
+ $this->rows[] = array_values($row->toArray());
+ }
+ }
+ }
+}
+?
+>
\ No newline at end of file
diff --git a/core/shared/app/BasicActions.php b/core/shared/app/BasicActions.php
new file mode 100755
index 0000000..a9d9b64
--- /dev/null
+++ b/core/shared/app/BasicActions.php
@@ -0,0 +1,183 @@
+noExec)
+ {
+ // change to the gateway.php script directory
+ // now change to the directory of the classpath. Possible relative to gateway.php
+ $dirname = dirname($amfbody->classPath);
+ if(is_dir($dirname))
+ {
+ chdir($dirname);
+ }
+ else
+ {
+ $ex = new MessageException(E_USER_ERROR, "The classpath folder {" . $amfbody->classPath . "} does not exist. You probably misplaced your service." , __FILE__, __LINE__, "AMFPHP_CLASSPATH_NOT_FOUND");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ $fileExists = @file_exists(basename($amfbody->classPath)); // see if the file exists
+ if(!$fileExists)
+ {
+ $ex = new MessageException(E_USER_ERROR, "The class {" . $amfbody->className . "} could not be found under the class path {" . $amfbody->classPath . "}" , __FILE__, __LINE__, "AMFPHP_FILE_NOT_FOUND");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ global $amfphp;
+ $time = microtime_float();
+ $fileIncluded = Executive::includeClass($amfbody, "./" . basename($amfbody->classPath));
+ $amfphp['includeTime'] += microtime_float() - $time;
+
+ if (!$fileIncluded)
+ {
+ $ex = new MessageException(E_USER_ERROR, "The class file {" . $amfbody->className . "} exists but could not be included. The file may have syntax errors, or includes at the top of the file cannot be resolved.", __FILE__, __LINE__, "AMFPHP_FILE_NOT_INCLUDED");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ if (!class_exists($amfbody->className))
+ { // Just make sure the class name is the same as the file name
+
+ $ex = new MessageException(E_USER_ERROR, "The file {" . $amfbody->className . ".php} exists and was included correctly but a class by that name could not be found in that file. Perhaps the class is misnamed.", __FILE__, __LINE__, "AMFPHP_CLASS_NOT_FOUND");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ //Let executive handle building the class
+ //The executive can handle making exceptions and all that, that's why
+ $classConstruct = Executive::buildClass($amfbody, $amfbody->className);
+
+ if($classConstruct !== '__amfphp_error')
+ {
+ $amfbody->setClassConstruct($classConstruct);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * MetaDataAction loads the required info from the methodTable
+ */
+function securityAction (&$amfbody) {
+ if(!$amfbody->noExec)
+ {
+ $classConstruct = &$amfbody->getClassConstruct();
+ $methodName = $amfbody->methodName;
+ $className = $amfbody->className;
+
+ //Check if method exists
+ if (!method_exists($classConstruct, $methodName)) { // check to see if the method exists
+ $ex = new MessageException(E_USER_ERROR, "The method {" . $methodName . "} does not exist in class {" . $className . "}.", __FILE__, __LINE__, "AMFPHP_INEXISTANT_METHOD");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ //Check if method is private (PHP4)
+ if (strpos($methodName, '_') === 0) { // check to see if the method exists
+ $ex = new MessageException(E_USER_ERROR, "The method {" . $methodName . "} starts with an underscore and is therefore considered private, so it cannot be remotely called.", __FILE__, __LINE__, "AMFPHP_PRIVATE_METHOD");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+
+ //Check to see if method is private or protected (PHP5)
+ if(class_exists('ReflectionMethod'))
+ {
+ $method = new ReflectionMethod($className, $methodName);
+ if(!$method->isPublic())
+ {
+ $ex = new MessageException(E_USER_ERROR, "The method {" . $methodName . "} in {" . $className . "} is not public and therefore cannot be called.", __FILE__, __LINE__, "AMFPHP_PRIVATE_METHOD");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+ }
+
+ $classConstruct = &$amfbody->getClassConstruct();
+ $methodName = $amfbody->methodName;
+ $className = $amfbody->className;
+
+ if (method_exists($classConstruct, "beforeFilter")) {
+ //Pass throught the executive
+ $allow = Executive::doMethodCall($amfbody,
+ $classConstruct,
+ 'beforeFilter',
+ array($methodName));
+ if ($allow === '__amfphp_error' || $allow === false) {
+ $ex = new MessageException(E_USER_ERROR, "Method access blocked by beforeFilter in " . $className . " class", __FILE__, __LINE__, "AMFPHP_AUTHENTICATE_ERROR");
+ MessageException::throwException($amfbody, $ex);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+function adapterMap(&$results)
+{
+ if(is_array($results))
+ {
+ array_walk($results, 'adapterMap');
+ }
+ elseif(is_object($results))
+ {
+ $className = strtolower(get_class($results));
+ if(array_key_exists($className, $GLOBALS['amfphp']['adapterMappings']))
+ {
+ $type = $GLOBALS['amfphp']['adapterMappings'][$className];
+ $results = mapRecordSet($results, $type);
+ }
+ else
+ {
+ $vars = get_object_vars($results);
+
+ array_walk($vars, 'adapterMap');
+
+ foreach($vars as $key => $value)
+ {
+ $results->$key = $value;
+ }
+ }
+ }
+ elseif(is_resource($results))
+ {
+ $type = get_resource_type($results);
+ $str = explode(' ', $type);
+ if(in_array($str[1], array("result", 'resultset', "recordset", "statement")))
+ {
+ $results = mapRecordSet($results, $str[0]);
+ }
+ else
+ {
+ $results = false;
+ }
+ }
+ return $results;
+}
+
+function mapRecordSet($result, $type)
+{
+ $classname = $type . "Adapter"; // full class name
+ $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
+ if (!$includeFile) {
+ trigger_error("The recordset filter class " . $classname . " was not found");
+ }
+ $recordSet = new $classname($result); // returns formatted recordset
+
+ return array("columns" => $recordSet->columns, "rows" => $recordSet->rows);
+}
+?>
\ No newline at end of file
diff --git a/core/shared/app/BasicGateway.php b/core/shared/app/BasicGateway.php
new file mode 100755
index 0000000..f471d3d
--- /dev/null
+++ b/core/shared/app/BasicGateway.php
@@ -0,0 +1,95 @@
+registerActionChain();
+ }
+
+ /**
+ * 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 setBaseClassPath($value) {
+ $path = realpath($value . '/') . '/';
+ $GLOBALS['amfphp']['classPath'] = $path;
+ }
+
+ function service()
+ {
+ //Process the arguments
+ $body = $this->createBody();
+ foreach($this->actions as $key => $action)
+ {
+ $result = $action($body); // invoke the first filter in the chain
+ if($result === false)
+ {
+ //Go straight to serialization actions
+ $serAction = 'serializationAction';
+ $serAction($body);
+ break;
+ }
+ }
+
+ echo $body->getResults();
+ }
+
+ /**
+ * Add a class mapping for adapters
+ */
+ function addAdapterMapping($key, $value)
+ {
+ $GLOBALS['amfphp']['adapterMappings'][$key] = $value;
+ }
+
+ /**
+ * This function should overriden by the gateways
+ */
+ function createBody()
+ {
+
+ }
+
+ /**
+ * Create the chain of actions
+ * Subclass gateway and overwrite to create a custom gateway
+ */
+ function registerActionChain()
+ {
+
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/app/Constants.php b/core/shared/app/Constants.php
new file mode 100755
index 0000000..aff0241
--- /dev/null
+++ b/core/shared/app/Constants.php
@@ -0,0 +1,53 @@
+= 5 ? true : false);
+/**
+ * The Content Type String
+ */
+$tmp = pack("d", 1); // determine the multi-byte ordering of this machine temporarily pack 1
+define("AMFPHP_BIG_ENDIAN", $tmp == "\0\0\0\0\0\0\360\77");
+
+?>
diff --git a/core/shared/app/Globals.php b/core/shared/app/Globals.php
new file mode 100755
index 0000000..d3eeca1
--- /dev/null
+++ b/core/shared/app/Globals.php
@@ -0,0 +1,47 @@
+ 'peardb', //PEAR::DB
+ 'pdostatement' => 'pdo', //PDO
+ 'mysqli_result' => 'mysqliobject', //Mysqli
+ 'sqliteresult' => 'sqliteobject', //SQLite
+ 'sqliteunbuffered' => 'sqliteobject',
+ 'adorecordset' => 'adodb', //ADODB
+ 'zend_db_table_row' => 'zendrow', //Zend::DB
+ 'zend_db_table_rowset' => 'zendrowset',
+ 'recordset' => 'plainrecordset', //Plain recordset
+ 'doctrine_collection_immediate' => 'doctrine' //Doctrine table
+);
+
+?>
\ No newline at end of file
diff --git a/core/shared/app/php4Executive.php b/core/shared/app/php4Executive.php
new file mode 100755
index 0000000..be94f3b
--- /dev/null
+++ b/core/shared/app/php4Executive.php
@@ -0,0 +1,115 @@
+
diff --git a/core/shared/app/php5Executive.php b/core/shared/app/php5Executive.php
new file mode 100755
index 0000000..74fdb38
--- /dev/null
+++ b/core/shared/app/php5Executive.php
@@ -0,0 +1,181 @@
+code, $fault->getMessage(), $fault->file, $fault->line, 'AMFPHP_RUNTIME_ERROR');
+ }
+ else
+ {
+ $code = "AMFPHP_RUNTIME_ERROR";
+ if($fault->getCode() != 0)
+ {
+ $code = $fault->getCode();
+ }
+ $ex = new MessageException(E_USER_ERROR, $fault->getMessage(), $fault->getFile(), $fault->getLine(), $code);
+ }
+ MessageException::throwException($bodyObj, $ex);
+ $output = '__amfphp_error';
+ }
+ return $output;
+ }
+
+ /**
+ * Builds a class using a class name
+ * If there is a failure, catch the error and return to caller
+ */
+ function buildClass(&$bodyObj, $className)
+ {
+ global $amfphp;
+ if(isset($amfphp['classInstances'][$className]))
+ {
+ return $amfphp['classInstances'][$className];
+ }
+
+ try
+ {
+ $construct = new $className($className);
+ $amfphp['classInstances'][$className] = & $construct;
+ }
+ catch(Exception $fault)
+ {
+ //When constructing a class, getLine and getFile don't refer to the appropriate thing,
+ //hence this hack
+ $ex = new MessageException(E_USER_ERROR, $fault->getMessage(), $bodyObj->classPath, 'Undetermined line in constructor', 'AMFPHP_BUILD_ERROR');
+ MessageException::throwException($bodyObj, $ex);
+ $construct = '__amfphp_error';
+ }
+
+ return $construct;
+ }
+
+ /**
+ * We are using a deferred metho call instead of directly
+ * calling the method because of a strange bug with throwing exceptions within
+ * an error handler which seems to break the convential rule for working with exceptions
+ * Nesting function calls seems to solve the problem, but not nesting try...catch
+ */
+ function deferredMethodCall(&$bodyObj, &$object, $method, $args)
+ {
+ try
+ {
+ if($object === NULL)
+ {
+ $output = call_user_func_array ($method, $args);
+ }
+ else
+ {
+ $output = call_user_func_array (array(&$object, $method), $args);
+ }
+ }
+ catch(Exception $fault)
+ {
+ if(get_class($fault) == "VerboseException")
+ {
+ $ex = new MessageException($fault->code, $fault->getMessage(), $fault->file, $fault->line, 'AMFPHP_RUNTIME_ERROR');
+ }
+ else
+ {
+ $code = "AMFPHP_RUNTIME_ERROR";
+ if($fault->getCode() != 0)
+ {
+ $code = $fault->getCode();
+ }
+ $ex = new MessageException(E_USER_ERROR, $fault->getMessage(), $fault->getFile(), $fault->getLine(), $code);
+ }
+ $output = '__amfphp_error';
+ MessageException::throwException($bodyObj, $ex);
+
+ }
+
+ return $output;
+ }
+
+ /**
+ * Include a class
+ * If there is an error, catch and return to caller
+ */
+ function includeClass(&$bodyObj, $location)
+ {
+ $included = false;
+ try
+ {
+ include_once($location);
+ $included = true;
+ }
+ catch(Exception $fault)
+ {
+ $included = false;
+ if(get_class($fault) == "VerboseException")
+ {
+ $ex = new MessageException($fault->code, $fault->getMessage(), $fault->file, $fault->line, 'AMFPHP_INCLUDE_ERROR');
+ }
+ else
+ {
+ $ex = new MessageException(E_USER_ERROR, $fault->getMessage(), $fault->getFile(), $fault->getLine(), 'AMFPHP_INCLUDE_ERROR');
+ }
+ MessageException::throwException($bodyObj, $ex);
+ }
+ return $included;
+ }
+}
+?>
diff --git a/core/shared/exception/MessageException.php b/core/shared/exception/MessageException.php
new file mode 100755
index 0000000..7d6941b
--- /dev/null
+++ b/core/shared/exception/MessageException.php
@@ -0,0 +1,110 @@
+code = $detailCode;
+ $this->description = $description; // pass the description
+ $this->details = $file; // pass the details
+ $this->level = MessageException::getFriendlyError($code);
+ $this->line = $line; // pass the line number
+ }
+
+ /**
+ * throwException provides the means to raise an exception. This method will
+ * stop the further execution of the remote method, but not hault the execution
+ * of the entire process. Using the built in PHP exception system will stop
+ * the entire process and not allow us to report very detailed information back
+ * to the client, especially if there are multiple methods.
+ *
+ * When we upgrade to PHP 5, using the try...catch syntax will make this much easier.
+ *
+ * @static
+ * @param AMFBody $body The AMFBody object to apply the exception to.
+ * @param AMFException @exception The exception object to throw
+ * @see AMFBody
+ */
+ function throwException (&$body, $exception) {
+ $body->responseURI = $body->responseIndex . "/onStatus";
+ $results = &$body->getResults();
+
+ if($GLOBALS['amfphp']['encoding'] == 'amf3')
+ {
+ $results = new ErrorMessage();
+ $results->correlationId = $GLOBALS['amfphp']['lastMessageId'];
+ $results->faultCode = $exception->code;
+ $results->faultDetail = $exception->details . ' on line ' . $exception->line;
+ $results->faultString = $exception->description;
+ }
+ elseif($GLOBALS['amfphp']['encoding'] == 'amf0')
+ {
+ $results["description"] = $exception->description;
+ $results["details"] = $exception->details;
+ $results["level"] = $exception->level;
+ $results["line"] = $exception->line;
+ $results["code"] = $exception->code;
+ }
+ else
+ {
+ $results['faultCode'] = $exception->code;
+ $results['faultDetail'] = $exception->details . ' on line ' . $exception->line;
+ $results['faultString'] = $exception->description;
+ }
+ }
+
+ function getFriendlyError ($err) {
+ $errortype = array (1 => "Error",
+ 2 => "Warning",
+ 4 => "Parsing Error",
+ 8 => "Notice",
+ 16 => "Core Error",
+ 32 => "Core Warning",
+ 64 => "Compile Error",
+ 128 => "Compile Warning",
+ 256 => "User Error",
+ 512 => "User Warning",
+ 1024 => "User Notice",
+ 2048 => "Strict error",
+ );
+ if(isset($errortype[$err]))
+ {
+ return $errortype[$err];
+ }
+ else
+ {
+ return "Unknown error type";
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/exception/php4Exception.php b/core/shared/exception/php4Exception.php
new file mode 100755
index 0000000..c8dcd68
--- /dev/null
+++ b/core/shared/exception/php4Exception.php
@@ -0,0 +1,56 @@
+setResults($error);
+
+ if($GLOBALS['amfphp']['encoding'] == 'amf0' || $GLOBALS['amfphp']['encoding'] == 'amf3')
+ {
+ // build a new AMFObject
+ $amfout = new AMFObject("");
+
+ $amfout->addBody($amfbody);
+
+ // Add the trace headers we have so far while we're at it
+ debugFilter($amfout);
+
+ // create a new serializer
+ $serializer = new AMFSerializer();
+
+ // serialize the data
+ $data = $serializer->serialize($amfout);
+
+ // send the correct header
+ header('Content-type: application/x-amf');
+ // flush the amf data to the client.
+ print($data);
+
+ // kill the system after we find a single error
+ exit;
+ }
+ else
+ {
+ serializationAction($amfbody);
+ print($amfbody->getResults());
+ exit;
+ }
+
+}
+
+set_error_handler("reportExceptions");
+
+?>
\ No newline at end of file
diff --git a/core/shared/exception/php5Exception.php b/core/shared/exception/php5Exception.php
new file mode 100755
index 0000000..c41dd93
--- /dev/null
+++ b/core/shared/exception/php5Exception.php
@@ -0,0 +1,46 @@
+description = $string;
+ $this->level = $level;
+ $this->code = "AMFPHP_RUNTIME_ERROR";
+ $this->file = $file;
+ $this->line = $line;
+ Exception::__construct($string);
+ }
+}
+
+function amfErrorHandler($level, $string, $file, $line, $context)
+{
+ //forget about errors not defined at reported
+ $amfphpErrorLevel = $GLOBALS['amfphp']['errorLevel'];
+
+ if( error_reporting() != 0 && ($amfphpErrorLevel | $level) == $amfphpErrorLevel )
+ {
+ throw new VerboseException($string, $level, $file, $line);
+ }
+}
+
+set_error_handler("amfErrorHandler");
+?>
\ No newline at end of file
diff --git a/core/shared/util/Authenticate.php b/core/shared/util/Authenticate.php
new file mode 100755
index 0000000..4e58b97
--- /dev/null
+++ b/core/shared/util/Authenticate.php
@@ -0,0 +1,113 @@
+ $role) {
+ $methodRoles[$key] = strtolower(trim($role));
+ }
+ if(!isset($_SESSION['amfphp_roles']))
+ {
+ $_SESSION['amfphp_roles'] = "";
+
+ }
+ $userRoles = explode(",", $_SESSION['amfphp_roles']); // split the users session roles into an array
+
+ foreach($userRoles as $key => $role) {
+ $userRoles[$key] = strtolower(trim($role));
+ if (in_array($userRoles[$key], $methodRoles)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * login assumes that the user has verified the credentials and logs in the user.
+ *
+ * The login method hides the session implementation for storing the user credentials
+ *
+ * @param string $name The user name
+ * @param string $roles The comma delimited list of roles for the user
+ */
+ function login($name, $roles) {
+ if(!session_id())
+ {
+ session_start();
+ }
+ $_SESSION['amfphp_username'] = $name;
+ $_SESSION['amfphp_roles'] = $roles;
+ }
+
+ /**
+ * logout kills the user session and terminates the login properties
+ */
+ function logout() {
+ $_SESSION['amfphp_username'] = null;
+ $_SESSION['amfphp_roles'] = null;
+ if(isset($_SESSION['amfphp_username']))
+ {
+ unset($_SESSION['amfphp_username']);
+ }
+ if(isset($_SESSION['amfphp_roles']))
+ {
+ unset($_SESSION['amfphp_roles']);
+ }
+ return true;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/util/CharsetHandler.php b/core/shared/util/CharsetHandler.php
new file mode 100755
index 0000000..f3a9668
--- /dev/null
+++ b/core/shared/util/CharsetHandler.php
@@ -0,0 +1,130 @@
+_method = CharsetHandler::getMethod();
+ $this->_phpCharset = CharsetHandler::getPhpCharset();
+ $this->_sqlCharset = CharsetHandler::getSqlCharset();
+ $this->_mode = $mode;
+
+ $newset = "";
+ if($this->_mode == "flashtophp")
+ {
+ $this->_fromCharset = "utf-8";
+ $this->_toCharset = $this->_phpCharset;
+ $newset = $this->_phpCharset;
+ }
+ else if($this->_mode == "phptoflash")
+ {
+ $this->_fromCharset = $this->_phpCharset;
+ $this->_toCharset = "utf-8";
+ $newset = $this->_phpCharset;
+ }
+ else if($this->_mode == "sqltophp")
+ {
+ $this->_fromCharset = $this->_sqlCharset;
+ $this->_toCharset = $this->_phpCharset;
+ $newset = $this->_sqlCharset;
+ }
+ else if($this->_mode == "sqltoflash")
+ {
+ $this->_fromCharset = $this->_sqlCharset;
+ $this->_toCharset = "utf-8";
+ $newset = $this->_sqlCharset;
+ }
+
+ //Don't try to reencode charsets for nothing
+ if($this->_fromCharset == $this->_toCharset)
+ {
+ $this->_method = "none";
+ }
+ }
+
+ function transliterate($string)
+ {
+ switch($this->_method)
+ {
+ case "none" :
+ return $string;
+ break;
+ case "iconv":
+ return iconv($this->_fromCharset,$this->_toCharset, $string);
+ break;
+ case "utf8_decode":
+ return ($this->_mode == "flashtophp" ? utf8_decode($string) : utf8_encode($string));
+ break;
+ case "mbstring":
+ return mb_convert_encoding($string, $this->_toCharset, $this->_fromCharset);
+ break;
+ case "recode":
+ return recode_string($this->_fromCharset . ".." . $this->_toCharset, $string);
+ break;
+ default:
+ return $string;
+ break;
+ }
+ }
+
+ /**
+ * Sets the charset handling method
+ *
+ * @param string $location One of "none", "iconv", "mbstring", "recode"
+ */
+ function getMethod($val=NULL)
+ {
+ static $method = 0;
+ if($val != NULL)
+ {
+ if($val == 'utf8_encode')
+ {
+ $val = 'utf8_decode';
+ }
+ $method = $val;
+ }
+ return $method;
+ }
+
+ function setMethod($val=0){
+ return CharsetHandler::getMethod($val);
+ }
+
+ function getPhpCharset($val=NULL)
+ {
+ static $phpCharset = 0;
+ if($val != NULL)
+ {
+ $phpCharset = strtolower($val);
+ }
+ return $phpCharset;
+ }
+
+ function setPhpCharset($val=0){
+ return CharsetHandler::getPhpCharset($val);
+ }
+
+ function getSqlCharset($val=NULL)
+ {
+ static $sqlCharset = 0;
+ if($val != NULL)
+ {
+ $sqlCharset = strtolower($val);
+ }
+ return $sqlCharset;
+ }
+
+ function setSqlCharset($val=0){
+ return CharsetHandler::getSqlCharset($val);
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/util/CompatPhp4.php b/core/shared/util/CompatPhp4.php
new file mode 100755
index 0000000..f9241c4
--- /dev/null
+++ b/core/shared/util/CompatPhp4.php
@@ -0,0 +1,60 @@
+ $val) {
+ if ($needle === $val) {
+ return($key);
+ }
+ }
+ return FALSE;
+}
+
+function microtime_float()
+{
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+}
+
+if(!function_exists('is_a'))
+{
+ //We only use is_a as a replacement for PHP5-related stuff, so we always return false
+ //anyways
+ function is_a($obj, $d)
+ {
+ return false;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/util/CompatPhp5.php b/core/shared/util/CompatPhp5.php
new file mode 100755
index 0000000..aaaf11e
--- /dev/null
+++ b/core/shared/util/CompatPhp5.php
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/core/shared/util/Headers.php b/core/shared/util/Headers.php
new file mode 100755
index 0000000..692af8f
--- /dev/null
+++ b/core/shared/util/Headers.php
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/core/shared/util/JSON.php b/core/shared/util/JSON.php
new file mode 100755
index 0000000..d1f983f
--- /dev/null
+++ b/core/shared/util/JSON.php
@@ -0,0 +1,645 @@
+
+ * @author Matt Knapp
+ * @author Brett Stimmerman
+ * @copyright 2005 Michal Migurski
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+ *
+ * Note: this is a stripped version of JSON.php to remove pear dependencies and
+ * encoding capability
+ */
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_SLICE', 1);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_STR', 2);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_ARR', 3);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_OBJ', 4);
+
+/**
+ * Marker constant for Services_JSON::decode(), used to flag stack state
+ */
+define('SERVICES_JSON_IN_CMT', 5);
+
+/**
+ * Behavior switch for Services_JSON::decode()
+ */
+define('SERVICES_JSON_LOOSE_TYPE', 16);
+
+/**
+ * Behavior switch for Services_JSON::decode()
+ */
+define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
+
+class Services_JSON
+{
+ function Services_JSON($use = 0)
+ {
+ $this->use = $use;
+ }
+
+ function utf162utf8($utf16)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding'))
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
+
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
+
+ switch(true) {
+ case ((0x7F & $bytes) == $bytes):
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x7F & $bytes);
+
+ case (0x07FF & $bytes) == $bytes:
+ // return a 2-byte UTF-8 character
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
+ . chr(0x80 | ($bytes & 0x3F));
+
+ case (0xFFFF & $bytes) == $bytes:
+ // return a 3-byte UTF-8 character
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
+ . chr(0x80 | ($bytes & 0x3F));
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ function utf82utf16($utf8)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding'))
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+
+ switch(strlen($utf8)) {
+ case 1:
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return $ut8;
+
+ case 2:
+ // return a UTF-16 character from a 2-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x07 & (ord($utf8{0}) >> 2))
+ . chr((0xC0 & (ord($utf8{0}) << 6))
+ | (0x3F & ord($utf8{1})));
+
+ case 3:
+ // return a UTF-16 character from a 3-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr((0xF0 & (ord($utf8{0}) << 4))
+ | (0x0F & (ord($utf8{1}) >> 2)))
+ . chr((0xC0 & (ord($utf8{1}) << 6))
+ | (0x7F & ord($utf8{2})));
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ function name_value($name, $value)
+ {
+ $encoded_value = $this->encode($value);
+
+ return $this->encode(strval($name)) . ':' . $encoded_value;
+ }
+
+ function reduce_string($str)
+ {
+ $str = preg_replace(array(
+
+ // eliminate single line comments in '// ...' form
+ '#^\s*//(.+)$#m',
+
+ // eliminate multi-line comments in '/* ... */' form, at start of string
+ '#^\s*/\*(.+)\*/#Us',
+
+ // eliminate multi-line comments in '/* ... */' form, at end of string
+ '#/\*(.+)\*/\s*$#Us'
+
+ ), '', $str);
+
+ // eliminate extraneous space
+ return trim($str);
+ }
+
+ /**
+ * encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
+ * if var is a strng, note that encode() always expects it
+ * to be in ASCII or UTF-8 format!
+ *
+ * @return mixed JSON string representation of input var or an error if a problem occurs
+ * @access public
+ */
+ function encode($var)
+ {
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false';
+
+ case 'NULL':
+ return 'null';
+
+ case 'integer':
+ return (int) $var;
+
+ case 'double':
+ case 'float':
+ return (float) $var;
+
+ case 'string':
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+ $ascii = '';
+ $strlen_var = strlen($var);
+
+ /*
+ * Iterate over every character in the string,
+ * escaping with a slash or encoding to UTF-8 where necessary
+ */
+ for ($c = 0; $c < $strlen_var; ++$c) {
+
+ $ord_var_c = ord($var{$c});
+
+ switch (true) {
+ case $ord_var_c == 0x08:
+ $ascii .= '\b';
+ break;
+ case $ord_var_c == 0x09:
+ $ascii .= '\t';
+ break;
+ case $ord_var_c == 0x0A:
+ $ascii .= '\n';
+ break;
+ case $ord_var_c == 0x0C:
+ $ascii .= '\f';
+ break;
+ case $ord_var_c == 0x0D:
+ $ascii .= '\r';
+ break;
+
+ case $ord_var_c == 0x22:
+ case $ord_var_c == 0x2F:
+ case $ord_var_c == 0x5C:
+ // double quote, slash, slosh
+ $ascii .= '\\'.$var{$c};
+ break;
+
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+ // characters U-00000000 - U-0000007F (same as ASCII)
+ $ascii .= $var{$c};
+ break;
+
+ case (($ord_var_c & 0xE0) == 0xC0):
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+ $c += 1;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF0) == 0xE0):
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}));
+ $c += 2;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF8) == 0xF0):
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}));
+ $c += 3;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFC) == 0xF8):
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}));
+ $c += 4;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFE) == 0xFC):
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}),
+ ord($var{$c + 5}));
+ $c += 5;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+ }
+ }
+
+ return '"'.$ascii.'"';
+
+ case 'array':
+ /*
+ * As per JSON spec if any array key is not an integer
+ * we must treat the the whole array as an object. We
+ * also try to catch a sparsely populated associative
+ * array with numeric keys here because some JS engines
+ * will create an array with empty indexes up to
+ * max_index which can cause memory issues and because
+ * the keys, which may be relevant, will be remapped
+ * otherwise.
+ *
+ * As per the ECMA and JSON specification an object may
+ * have any string as a property. Unfortunately due to
+ * a hole in the ECMA specification if the key is a
+ * ECMA reserved word or starts with a digit the
+ * parameter is only accessible using ECMAScript's
+ * bracket notation.
+ */
+
+ // treat as a JSON object
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($var),
+ array_values($var));
+
+ return '{' . join(',', $properties) . '}';
+ }
+
+ // treat it like a regular array
+ $elements = array_map(array($this, 'encode'), $var);
+
+ return '[' . join(',', $elements) . ']';
+
+ case 'object':
+ $vars = get_object_vars($var);
+
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($vars),
+ array_values($vars));
+
+ return '{' . join(',', $properties) . '}';
+ case 'resource':
+ return null;
+ default:
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
+ ? 'null'
+ : trigger_error(gettype($var)." can not be encoded as JSON string", E_USER_ERROR);
+ }
+ }
+
+ function sanitizeType($type)
+ {
+ $subtype = -1;
+ $type = strtolower($type);
+ if($type == NULL || trim($type) == "")
+ {
+ $type = -1;
+ }
+
+ if(strpos($type, ' ') !== false)
+ {
+ $str = explode(' ', $type);
+ if(in_array($str[1], array("result", 'resultset', "recordset", "statement")))
+ {
+ $type = "__RECORDSET__";
+ $subtype = $str[0];
+ }
+ }
+ return array($type, $subtype);
+ }
+
+ function decode($str)
+ {
+ $str = $this->reduce_string($str);
+
+ switch (strtolower($str)) {
+ case 'true':
+ return true;
+
+ case 'false':
+ return false;
+
+ case 'null':
+ return null;
+
+ default:
+ if (is_numeric($str)) {
+ // Lookie-loo, it's a number
+
+ // Return float or int, as appropriate
+ return ((float)$str == (integer)$str)
+ ? (integer)$str
+ : (float)$str;
+
+ }
+ elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
+ // STRINGS RETURNED IN UTF-8 FORMAT
+ $delim = substr($str, 0, 1);
+ $chrs = substr($str, 1, -1);
+ $utf8 = '';
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
+
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+ $ord_chrs_c = ord($chrs{$c});
+
+ switch (true) {
+ case $substr_chrs_c_2 == '\b':
+ $utf8 .= chr(0x08);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\t':
+ $utf8 .= chr(0x09);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\n':
+ $utf8 .= chr(0x0A);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\f':
+ $utf8 .= chr(0x0C);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\r':
+ $utf8 .= chr(0x0D);
+ ++$c;
+ break;
+
+ case $substr_chrs_c_2 == '\\"':
+ case $substr_chrs_c_2 == '\\\'':
+ case $substr_chrs_c_2 == '\\\\':
+ case $substr_chrs_c_2 == '\\/':
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
+ $utf8 .= $chrs{++$c};
+ }
+ break;
+
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
+ // single, escaped unicode character
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+ . chr(hexdec(substr($chrs, ($c + 4), 2)));
+ $utf8 .= $this->utf162utf8($utf16);
+ $c += 5;
+ break;
+
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
+ $utf8 .= $chrs{$c};
+ break;
+
+ case ($ord_chrs_c & 0xE0) == 0xC0:
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 2);
+ ++$c;
+ break;
+
+ case ($ord_chrs_c & 0xF0) == 0xE0:
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 3);
+ $c += 2;
+ break;
+
+ case ($ord_chrs_c & 0xF8) == 0xF0:
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 4);
+ $c += 3;
+ break;
+
+ case ($ord_chrs_c & 0xFC) == 0xF8:
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 5);
+ $c += 4;
+ break;
+
+ case ($ord_chrs_c & 0xFE) == 0xFC:
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 6);
+ $c += 5;
+ break;
+
+ }
+
+ }
+
+ return $utf8;
+
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
+ // array, or object notation
+
+ if ($str{0} == '[') {
+ $stk = array(SERVICES_JSON_IN_ARR);
+ $arr = array();
+ } else {
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE ) {
+ $stk = array(SERVICES_JSON_IN_OBJ);
+ $obj = array();
+ } else {
+ $stk = array(SERVICES_JSON_IN_OBJ);
+ $obj = new stdClass();
+ }
+ }
+
+ array_push($stk, array('what' => SERVICES_JSON_SLICE,
+ 'where' => 0,
+ 'delim' => false));
+
+ $chrs = substr($str, 1, -1);
+ $chrs = $this->reduce_string($chrs);
+
+ if ($chrs == '') {
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ return $arr;
+
+ } else {
+ return $obj;
+
+ }
+ }
+
+ //print("\nparsing {$chrs}\n");
+
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
+
+ $top = end($stk);
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
+ // found a comma that is not inside a string, array, etc.,
+ // OR we've reached the end of the character list
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ // we are in an array, so just push an element onto the stack
+ array_push($arr, $this->decode($slice));
+
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+ // we are in an object, so figure
+ // out the property name and set an
+ // element in an associative array,
+ // for now
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // "name":value pair
+ $key = $this->decode($parts[1]);
+ $val = $this->decode($parts[2]);
+
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // name:value pair, where name is unquoted
+ $key = $parts[1];
+ $val = $this->decode($parts[2]);
+
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ }
+
+ }
+
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
+ // found a quote, and we are not inside a string
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
+ //print("Found start of string at {$c}\n");
+
+ } elseif (($chrs{$c} == $top['delim']) &&
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
+ (($chrs{$c - 1} != '\\') ||
+ ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) {
+ // found a quote, we're in a string, and it's not escaped
+ array_pop($stk);
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '[') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a left-bracket, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
+ //print("Found start of array at {$c}\n");
+
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
+ // found a right-bracket, and we're in an array
+ array_pop($stk);
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '{') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a left-brace, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
+ //print("Found start of object at {$c}\n");
+
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
+ // found a right-brace, and we're in an object
+ array_pop($stk);
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($substr_chrs_c_2 == '/*') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a comment start, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
+ $c++;
+ //print("Found start of comment at {$c}\n");
+
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
+ // found a comment end, and we're in one now
+ array_pop($stk);
+ $c++;
+
+ for ($i = $top['where']; $i <= $c; ++$i)
+ $chrs = substr_replace($chrs, ' ', $i, 1);
+
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ }
+
+ }
+
+
+
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ return $arr;
+
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+ return $obj;
+
+ }
+
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/util/MessageBody.php b/core/shared/util/MessageBody.php
new file mode 100755
index 0000000..90edc59
--- /dev/null
+++ b/core/shared/util/MessageBody.php
@@ -0,0 +1,143 @@
+responseIndex = $responseIndex;
+ $this->targetURI = $targetURI;
+ $this->responseURI = $this->responseIndex . "/onStatus"; // default to the onstatus method
+ $this->setValue($value);
+ }
+
+ /**
+ * setter for the results from the process execution
+ *
+ * @param mixed $results The returned results from the process execution
+ */
+ function setResults ($result) {
+ $this->_results = $result;
+ }
+
+ /**
+ * getter for the result of the process execution
+ *
+ * @return mixed The results
+ */
+ function &getResults () {
+ return $this->_results;
+ }
+
+ /**
+ * setter for the class construct
+ *
+ * @param object $classConstruct The instance of the service class
+ */
+ function setClassConstruct (&$classConstruct) {
+ $this->_classConstruct = &$classConstruct;
+ }
+
+ /**
+ * getter for the class construct
+ *
+ * @return object The class instance
+ */
+ function &getClassConstruct () {
+ return $this->_classConstruct;
+ }
+
+ /**
+ * setter for the value property
+ *
+ * @param mixed $value The value of the body object
+ */
+ function setValue ($value) {
+ $this->_value = $value;
+ }
+
+ /**
+ * getter for the value property
+ *
+ * @return mixed The value property
+ */
+ function &getValue () {
+ return $this->_value;
+ }
+
+ /**
+ * Set special handling type for this body
+ */
+ function setSpecialHandling($type)
+ {
+ $this->_specialHandling = $type;
+ }
+
+ /**
+ * Get special handling type for this body
+ */
+ function getSpecialHandling()
+ {
+ return $this->_specialHandling;
+ }
+
+ /**
+ * Check if this body is handled special against an array of special cases
+ */
+ function isSpecialHandling($against = NULL)
+ {
+ if($against !== NULL)
+ {
+ return in_array($this->_specialHandling, $against);
+ }
+ else
+ {
+ return ($this->_specialHandling != NULL);
+ }
+ }
+
+ function setMetaData($key, $val)
+ {
+ $this->_metaData[$key] = $val;
+ }
+
+ function getMetaData($key)
+ {
+ if(isset($this->_metaData[$key]))
+ {
+ return $this->_metaData[$key];
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+?>
diff --git a/core/shared/util/MessageHeader.php b/core/shared/util/MessageHeader.php
new file mode 100755
index 0000000..43bcee3
--- /dev/null
+++ b/core/shared/util/MessageHeader.php
@@ -0,0 +1,49 @@
+name = $name;
+ $this->required = $required;
+ $this->value = $value;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/shared/util/MethodTable.php b/core/shared/util/MethodTable.php
new file mode 100755
index 0000000..a52dc02
--- /dev/null
+++ b/core/shared/util/MethodTable.php
@@ -0,0 +1,552 @@
+methodTable = MethodTable::create($this);
+ * @author Christophe Herreman
+ * @since 05/01/2005
+ * @version $id$
+ *
+ * Special contributions by Allessandro Crugnola and Ted Milker
+ */
+
+if (!defined('T_ML_COMMENT')) {
+ define('T_ML_COMMENT', T_COMMENT);
+} else {
+ define('T_DOC_COMMENT', T_ML_COMMENT);
+}
+
+function strrstr($haystack, $needle)
+{
+ return substr($haystack, 0, strpos($haystack.$needle,$needle));
+}
+
+function strstrafter($haystack, $needle)
+{
+ return substr(strstr($haystack, $needle), strlen($needle));
+}
+
+class MethodTable
+{
+ /**
+ * Constructor.
+ *
+ * Since this class should only be accessed through the static create() method
+ * this constructor should be made private. Unfortunately, this is not possible
+ * in PHP4.
+ *
+ * @access private
+ */
+ function MethodTable(){
+ }
+
+
+ /**
+ * Creates the methodTable for a passed class.
+ *
+ * @static
+ * @access public
+ * @param $className(String) The name of the service class.
+ * May also simply be __FILE__
+ * @param $servicePath(String) The location of the classes (optional)
+ */
+ function create($className, $servicePath = NULL, &$classComment){
+
+ $methodTable = array();
+ if(file_exists($className))
+ {
+ //The new __FILE__ way of doing things was used
+ $sourcePath = $className;
+ $className = str_replace("\\", '/', $className);
+ $className = substr($className, strrpos($className, '/') + 1);
+ $className = str_replace('.php', '', $className);
+ }
+ else
+ {
+ $className = str_replace('.php', '', $className);
+ $fullPath = str_replace('.', '/', $className);
+ $className = $fullPath;
+ if(strpos($fullPath, '/') !== FALSE)
+ {
+ $className = substr(strrchr($fullPath, '/'), 1);
+ }
+
+ if($servicePath == NULL)
+ {
+ if(isset($GLOBALS['amfphp']['classPath']))
+ {
+ $servicePath = $GLOBALS['amfphp']['classPath'];
+ }
+ else
+ {
+ $servicePath = "../services/";
+ }
+ }
+ $sourcePath = $servicePath . $fullPath . ".php";
+ }
+
+ if(!file_exists($sourcePath))
+ {
+ trigger_error("The MethodTable class could not find {" .
+ $sourcePath . "}",
+ E_USER_ERROR);
+ }
+
+ if(class_exists('ReflectionClass'))
+ {
+ //PHP5
+ $classMethods = MethodTable::getClassMethodsReflection($sourcePath, $className, $classComment);
+ }
+ else
+ {
+ //PHP4
+
+ $classMethods = MethodTable::getClassMethodsTokenizer($sourcePath, $className, $classComment);
+ }
+
+ foreach ($classMethods as $key => $value) {
+ if($value['name'][0] == '_' || $value['name'] == 'beforeFilter')
+ {
+ continue;
+ }
+ $methodSignature = $value['args'];
+ $methodName = $value['name'];
+ $methodComment = $value['comment'];
+
+ $description = MethodTable::getMethodDescription($methodComment) . " " . MethodTable::getMethodCommentAttribute($methodComment, "desc");
+ $description = trim($description);
+ $access = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "access");
+ $roles = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "roles");
+ $instance = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "instance");
+ $returns = MethodTable::getMethodCommentAttributeFirstLine($methodComment, "returns");
+ $pagesize = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "pagesize");
+ $params = MethodTable::getMethodCommentArguments($methodComment);
+
+ //description, arguments, access, [roles, [instance, [returns, [pagesize]]]]
+ $methodTable[$methodName] = array();
+ //$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes
+ $methodTable[$methodName]["description"] = ($description == "") ? "No description given." : $description;
+ $methodTable[$methodName]["arguments"] = MethodTable::getMethodArguments($methodSignature, $params);
+ $methodTable[$methodName]["access"] = ($access == "") ? "private" : $access;
+
+ if($roles != "") $methodTable[$methodName]["roles"] = $roles;
+ if($instance != "") $methodTable[$methodName]["instance"] = $instance;
+ if($returns != "") $methodTable[$methodName]["returns"] = $returns;
+ if($pagesize != "") $methodTable[$methodName]["pagesize"] = $pagesize;
+ }
+
+ $classComment = trim(str_replace("\r\n", "\n", MethodTable::getMethodDescription($classComment)));
+
+ return $methodTable;
+ }
+
+ function getClassMethodsReflection($sourcePath, $className, & $classComment)
+ {
+ //Include the class in question
+ $dir = dirname($sourcePath);
+ if(!is_dir($dir))
+ {
+ return array();
+ }
+
+ chdir($dir);
+
+ if(!file_exists($sourcePath))
+ {
+ return array();
+ }
+
+ //HACK: eAccelerator
+ //Check if eAccelator is installed
+ if( extension_loaded( "eAccelerator" ))
+ {
+ //Touch the file so the results of getDocComment will be accurate
+ touch($sourcePath);
+ }
+
+ $included = include_once($sourcePath);
+ if($included === FALSE)
+ {
+ return array();
+ }
+
+ //Verify that the class exists
+ if(!class_exists($className))
+ {
+ return array();
+ }
+
+ $methodTable = array();
+
+ $class = new ReflectionClass($className);
+
+ $classComment = $class->getDocComment();
+ $methods = $class->getMethods();
+
+
+ foreach($methods as $reflectionMethod){
+
+ if($reflectionMethod->isPublic() && $method->name[0] != '_' && $method->name != 'beforeFilter')
+ {
+ if($reflectionMethod->isConstructor())
+ {
+ $classComment .= $reflectionMethod->getDocComment();
+ }
+ else
+ {
+ $reflectionParameter = $reflectionMethod->getParameters();
+
+ $methodTableEntry = array();
+ $parameters = array();
+
+ foreach($reflectionParameter as $parameter){
+ $parameters[] = $parameter->getName();
+ }
+
+ $methodTableEntry['args'] = '(' . implode(', ', $parameters);
+ $methodTableEntry['name'] = $reflectionMethod->name;
+ $methodTableEntry['comment'] = $reflectionMethod->getDocComment();
+
+ $methodTable[] = $methodTableEntry;
+ }
+ }
+ }
+
+ return $methodTable;
+ }
+
+ function getClassMethodsTokenizer($sourcePath, $className, & $classComment)
+ {
+ $source = file_get_contents($sourcePath);
+ $tokens = token_get_all($source);
+
+ $waitingForOpenParenthesis = false;
+ $waitingForFunction = false;
+ $waitingForClassName = false;
+ $bufferingArgs = false;
+ $argBuffer = "";
+ $lastFunction = "";
+ $lastFunctionComment = "";
+ $lastComment = "";
+ $classMethods = array();
+ $realClassName = "";
+
+ $openBraces = -10000;
+
+ $waitingForEndEncapsedString = false;
+ foreach($tokens as $token)
+ {
+ if (is_string($token)) {
+ if($token == '{')
+ {
+ $openBraces++;
+ }
+ if($token == '}')
+ {
+ if($waitingForEndEncapsedString)
+ {
+ $waitingForEndEncapsedString = false;
+ }
+ else
+ {
+ $lastComment = '';
+ $openBraces--;
+
+ if($openBraces == 0)
+ {
+ //break;
+ }
+ }
+ }
+ elseif($waitingForOpenParenthesis && $token == '(')
+ {
+ $bufferingArgs = true;
+ $argBuffer = "";
+ $waitingForOpenParenthesis = false;
+ }
+ elseif($bufferingArgs)
+ {
+ if($token != ')')
+ {
+ $argBuffer .= $token;
+ }
+ else
+ {
+ if($lastFunction != $realClassName && $lastFunction != "__construct")
+ {
+ $classMethods[] = array("name" => $lastFunction,
+ "comment" => $lastFunctionComment,
+ "args" => $argBuffer);
+ }
+ else
+ {
+ $classComment .= "\n\n" . $lastComment;
+ }
+
+ $bufferingArgs = false;
+ $argBuffer = "";
+ $lastFunction = "";
+ $lastFunctionComment = "";
+ }
+
+ }
+ } else {
+ // token array
+ list($id, $text) = $token;
+
+ if($bufferingArgs)
+ {
+ $argBuffer .= $text;
+ }
+ switch ($id)
+ {
+
+ case T_COMMENT:
+ case T_ML_COMMENT: // we've defined this
+ case T_DOC_COMMENT: // and this
+ // no action on comments
+ $lastComment = $text;
+ break;
+ case T_FUNCTION:
+ if($openBraces >= 1)
+ {
+ $waitingForFunction = true;
+ }
+ break;
+ case T_STRING:
+ if($waitingForFunction)
+ {
+ $waitingForFunction = false;
+ $waitingForOpenParenthesis = true;
+ $lastFunction = $text;
+ $lastFunctionComment = $lastComment;
+ $lastComment = "";
+ }
+ if($waitingForClassName)
+ {
+ $waitingForClassName = false;
+ if(strpos(strtolower($className), strtolower($text)) !== FALSE)
+ {
+ //Not the class we were looking for
+ $classComment = $lastComment;
+ $realClassName = $text;
+ }
+ }
+ break;
+ case T_CLASS:
+ $openBraces = 0;
+ $waitingForClassName = true;
+ break;
+ case T_CURLY_OPEN:
+ case T_DOLLAR_OPEN_CURLY_BRACES:
+ $waitingForEndEncapsedString = true;
+ break;
+ }
+ }
+ }
+ return $classMethods;
+ }
+
+ /**
+ *
+ */
+ function getMethodCommentArguments($comment)
+ {
+ $pieces = explode('@param', $comment);
+ $args = array();
+ if(is_array($pieces) && count($pieces) > 1)
+ {
+ for($i = 0; $i < count($pieces) - 1; $i++)
+ {
+ $ps = strrstr($pieces[$i + 1], '@');
+ $ps = strrstr($ps, '*/');
+ $args[] = MethodTable::cleanComment($ps);
+ }
+ }
+ return $args;
+ }
+
+
+ /**
+ * Returns the description from the comment.
+ * The description is(are) the first line(s) in the comment.
+ *
+ * @static
+ * @private
+ * @param $comment(String) The method's comment.
+ */
+ function getMethodDescription($comment){
+ $comment = MethodTable::cleanComment(strrstr($comment, "@"));
+ return trim($comment);
+ }
+
+
+ /**
+ * Returns the value of a comment attribute.
+ *
+ * @static
+ * @private
+ * @param $comment(String) The method's comment.
+ * @param $attribute(String) The name of the attribute to get its value from.
+ */
+ function getMethodCommentAttribute($comment, $attribute){
+ $pieces = strstrafter($comment, '@' . $attribute);
+ if($pieces !== FALSE)
+ {
+ $pieces = strrstr($pieces, '@');
+ $pieces = strrstr($pieces, '*/');
+ return MethodTable::cleanComment($pieces);
+ }
+ return "";
+ }
+
+ /**
+ * Returns the value of a comment attribute.
+ *
+ * @static
+ * @private
+ * @param $comment(String) The method's comment.
+ * @param $attribute(String) The name of the attribute to get its value from.
+ */
+ function getMethodCommentAttributeFirstLine($comment, $attribute){
+ $pieces = strstrafter($comment, '@' . $attribute);
+ if($pieces !== FALSE)
+ {
+ $pieces = strrstr($pieces, '@');
+ $pieces = strrstr($pieces, "*");
+ $pieces = strrstr($pieces, "/");
+ $pieces = strrstr($pieces, "-");
+ $pieces = strrstr($pieces, "\n");
+ $pieces = strrstr($pieces, "\r");
+ $pieces = strrstr($pieces, '*/');
+ return MethodTable::cleanComment($pieces);
+ }
+ return "";
+ }
+
+ function getMethodCommentAttributeFirstWord($comment, $attribute){
+ $pieces = strstrafter($comment, '@' . $attribute);
+ if($pieces !== FALSE)
+ {
+ $val = MethodTable::cleanComment($pieces);
+ return trim(strrstr($val, ' '));
+ }
+ return "";
+ }
+
+ /**
+ * Returns an array with the arguments of a method.
+ *
+ * @static
+ * @access private
+ * @param $methodSignature (String)The method's signatureg;
+ */
+ function getMethodArguments($methodSignature, $commentParams){
+ if(strlen($methodSignature) < 2){
+ //no arguments, return an empty array
+ $result = array();
+ }else{
+ //clean the arguments before returning them
+ $result = MethodTable::cleanArguments(explode(",", $methodSignature), $commentParams);
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Cleans the arguments array.
+ * This method removes all whitespaces and the leading "$" sign from each argument
+ * in the array.
+ *
+ * @static
+ * @access private
+ * @param $args(Array) The "dirty" array with arguments.
+ */
+ function cleanArguments($args, $commentParams){
+ $result = array();
+
+ foreach($args as $index => $arg){
+ $arg = strrstr(str_replace('(', '', $arg), '=');
+ if(!isset($commentParams[$index]))
+ {
+ $result[] = trim($arg);
+ }
+ else
+ {
+ $start = trim($arg);
+ $end = trim(str_replace('$', '', $commentParams[$index]));
+ //echo($start);
+ //echo($end);
+ if($end != "" && $start != "" && strpos(strtolower($end), strtolower($start)) === 0)
+ {
+ $end = substr($end, strlen($start));
+ }
+ $result[] = $start . ' - ' . trim($end);
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Cleans the comment string by removing all comment start and end characters.
+ *
+ * @static
+ * @private
+ * @param $comment(String) The method's comment.
+ */
+ function cleanComment($comment){
+ $comment = str_replace("/**", "", $comment);
+ $comment = str_replace("*/", "", $comment);
+ $comment = str_replace("*", "", $comment);
+ $comment = str_replace("\r", "", trim($comment));
+ $comment = eregi_replace("\n[ \t]+", "\n", trim($comment));
+ $comment = str_replace("\n", "\\n", trim($comment));
+ $comment = eregi_replace("[\t ]+", " ", trim($comment));
+
+ $comment = str_replace("\"", "\\\"", $comment);
+ return $comment;
+ }
+
+ /**
+ *
+ */
+ function showCode($methodTable){
+
+
+ foreach($methodTable as $methodName=>$methodProps){
+ $result .= "\n\t\"" . $methodName . "\" => array(";
+
+ foreach($methodProps as $key=>$value){
+ $result .= "\n\t\t\"" . $key . "\" => ";
+
+ if($key=="arguments"){
+ $result .= "array(";
+ for($i=0; $imethodTable = array(" . $result;
+ $result .= "\n);";
+
+ return $result;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/shared/util/NetDebug.php b/core/shared/util/NetDebug.php
new file mode 100755
index 0000000..f03f43c
--- /dev/null
+++ b/core/shared/util/NetDebug.php
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/core/shared/util/functions.php b/core/shared/util/functions.php
new file mode 100755
index 0000000..5fefdf8
--- /dev/null
+++ b/core/shared/util/functions.php
@@ -0,0 +1,44 @@
+
\ No newline at end of file
diff --git a/core/xmlrpc/app/Actions.php b/core/xmlrpc/app/Actions.php
new file mode 100755
index 0000000..7bfecd0
--- /dev/null
+++ b/core/xmlrpc/app/Actions.php
@@ -0,0 +1,104 @@
+getValue();
+
+ //Get the method that is being called
+ $description = xmlrpc_parse_method_descriptions($data);
+ $target = $description['methodName'];
+
+ $baseClassPath = $GLOBALS['amfphp']['classPath'];
+
+ $lpos = strrpos($target, '.');
+
+ $methodname = substr($target, $lpos + 1);
+ $trunced = substr($target, 0, $lpos);
+ $lpos = strrpos($trunced, ".");
+ if ($lpos === false) {
+ $classname = $trunced;
+ $uriclasspath = $trunced . ".php";
+ $classpath = $baseClassPath . $trunced . ".php";
+ } else {
+ $classname = substr($trunced, $lpos + 1);
+ $classpath = $baseClassPath . str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
+ $uriclasspath = str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
+ }
+
+ $body->methodName = $methodname;
+ $body->className = $classname;
+ $body->classPath = $classpath;
+ $body->uriClassPath = $uriclasspath;
+ $body->packageClassMethodName = $description['methodName'];
+}
+
+function executionAction(& $body)
+{
+ $classConstruct = $body->getClassConstruct();
+ $methodName = $body->methodName;
+ $className = $body->className;
+
+ $xmlrpc_server = xmlrpc_server_create();
+
+ $lambdaFunc = 'return adapterMap(call_user_func_array (array(&$userData[0], $userData[1]), $args));';
+ $func = create_function('$a,$args,$userData', $lambdaFunc);
+
+ xmlrpc_server_register_method($xmlrpc_server,
+ $body->packageClassMethodName,
+ $func);
+
+ $request_xml = $body->getValue();
+ $args = array($xmlrpc_server, $request_xml, array(&$classConstruct, $methodName));
+ $nullObj = NULL;
+ $response = Executive::doMethodCall($body, $nullObj, 'xmlrpc_server_call_method', $args);
+ //$response = xmlrpc_server_call_method();
+
+ if($response !== "__amfphp_error")
+ {
+ $body->setResults($response);
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/**
+ * Debug action
+ */
+function debugAction(& $body)
+{
+ if(count(NetDebug::getTraceStack()) != 0)
+ {
+ $previousResults = $body->getResults();
+ $debugInfo = NetDebug::getTraceStack();
+ $debugString = "";
+ $body->setResults($debugString . "\n" . $previousResults);
+ }
+}
+
+/**
+ * This won't ever be called unless there is an error
+ */
+function serializationAction(& $body)
+{
+ $request_xml = $body->getValue();
+ $toSerialize = $body->getResults();
+
+ $lambdaFunc = 'return $userData;';
+ $func = create_function('$a,$b,$userData', $lambdaFunc);
+
+ $xmlrpc_server = xmlrpc_server_create();
+
+ $request_xml = $body->getValue();
+
+ xmlrpc_server_register_method($xmlrpc_server,
+ $body->packageClassMethodName,
+ $func);
+
+ $response = xmlrpc_server_call_method($xmlrpc_server, $request_xml, $toSerialize);
+
+ $body->setResults($response);
+}
+
+?>
\ No newline at end of file
diff --git a/core/xmlrpc/app/Gateway.php b/core/xmlrpc/app/Gateway.php
new file mode 100755
index 0000000..9698304
--- /dev/null
+++ b/core/xmlrpc/app/Gateway.php
@@ -0,0 +1,34 @@
+setValue($GLOBALS["HTTP_RAW_POST_DATA"]);
+ return $body;
+ }
+
+ /**
+ * Create the chain of actions
+ */
+ function registerActionChain()
+ {
+ $this->actions['deserialization'] = 'deserializationAction';
+ $this->actions['classLoader'] = 'classLoaderAction';
+ $this->actions['security'] = 'securityAction';
+ $this->actions['exec'] = 'executionAction';
+ $this->actions['debug'] = 'debugAction';
+ }
+}
+?>
\ No newline at end of file