diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index e635cb037..50e3a60f1 100755 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -917,7 +917,7 @@ class G * @param string $urlLink * @return string */ - static function parseURI($uri, $config = array()) + static function parseURI($uri, $isRestRequest = false) { //*** process the $_POST with magic_quotes enabled // The magic_quotes_gpc feature has been DEPRECATED as of PHP 5.3.0. @@ -926,82 +926,111 @@ class G } $aRequestUri = explode('/', $uri ); - - if ( substr ( $aRequestUri[1], 0, 3 ) == 'sys' ) { - define( 'SYS_TEMP', substr ( $aRequestUri[1], 3 ) ); + if ($isRestRequest) { + $args = self::parseRestUri($aRequestUri); + } else { + $args = self::parseNormalUri($aRequestUri); } - else { - define("ENABLE_ENCRYPT", 'yes' ); - define( 'SYS_TEMP', $aRequestUri[1] ); + define("SYS_LANG", $args['SYS_LANG']); + define("SYS_SKIN", $args['SYS_SKIN']); + define('SYS_COLLECTION', $args['SYS_COLLECTION']); + define('SYS_TARGET', $args['SYS_TARGET']); + + if ( $args['SYS_COLLECTION'] == 'js2' ) { + print "ERROR"; die; + } + } + + public function parseNormalUri($aRequestUri) + { + if (substr($aRequestUri[1], 0, 3) == 'sys') { + define('SYS_TEMP', substr($aRequestUri[1], 3)); + } else { + define("ENABLE_ENCRYPT", 'yes'); + define('SYS_TEMP', $aRequestUri[1]); $plain = '/sys' . SYS_TEMP; - for ($i = 2 ; $i < count($aRequestUri); $i++ ) { - $decoded = G::decrypt ( urldecode($aRequestUri[$i]) , URL_KEY ); - if ( $decoded == 'sWì›' ) $decoded = $VARS[$i]; //this is for the string "../" + for ($i = 2; $i < count($aRequestUri); $i++) { + $decoded = G::decrypt(urldecode($aRequestUri[$i]), URL_KEY); + if ( $decoded == 'sWì›' ) { + $decoded = $VARS[$i]; //this is for the string "../" + } $plain .= '/' . $decoded; } $_SERVER["REQUEST_URI"] = $plain; } - $CURRENT_PAGE = $_SERVER["REQUEST_URI"]; + $work = explode('?', $_SERVER["REQUEST_URI"]); - $work = explode('?', $CURRENT_PAGE); - if ( count($work) > 1 ) - define( 'SYS_CURRENT_PARMS', $work[1]); - else - define( 'SYS_CURRENT_PARMS', ''); - define( 'SYS_CURRENT_URI' , $work[0]); - - if (!defined('SYS_CURRENT_PARMS')) + if (count($work) > 1) { define('SYS_CURRENT_PARMS', $work[1]); + } else { + define('SYS_CURRENT_PARMS', ''); + } + + define('SYS_CURRENT_URI', $work[0]); + + if (!defined('SYS_CURRENT_PARMS')) { + define('SYS_CURRENT_PARMS', $work[1]); + } + $preArray = explode('&', SYS_CURRENT_PARMS); - $buffer = explode( '.', $work[0] ); - if ( count($buffer) == 1 ) $buffer[1]=''; + $buffer = explode('.', $work[0]); + + if (count($buffer) == 1) { + $buffer[1]=''; + } //request type define('REQUEST_TYPE', ($buffer[1] != "" ?$buffer[1] : 'html')); $toparse = substr($buffer[0], 1, strlen($buffer[0]) - 1); - $URL = ""; - $URI_VARS = explode('/', $toparse); - for ( $i=3; $i < count( $URI_VARS) ; $i++) - $URL .= $URI_VARS[$i].'/'; - - $URI_VARS = explode('/', $toparse); + $uriVars = explode('/', $toparse); unset($work); unset($buffer); unset($toparse); + array_shift($uriVars); - array_shift($URI_VARS); - - $SYS_LANG = array_shift($URI_VARS); - $SYS_SKIN = array_shift($URI_VARS); - - $SYS_COLLECTION = array_shift($URI_VARS); - $SYS_TARGET = array_shift($URI_VARS); + $args = array(); + $args['SYS_LANG'] = array_shift($uriVars); + $args['SYS_SKIN'] = array_shift($uriVars); + $args['SYS_COLLECTION'] = array_shift($uriVars); + $args['SYS_TARGET'] = array_shift($uriVars); //to enable more than 2 directories...in the methods structure - $exit = 0; - while ( count ( $URI_VARS ) > 0 && $exit == 0) { - $SYS_TARGET .= '/' . array_shift($URI_VARS); + while (count($uriVars) > 0) { + $args['SYS_TARGET'] .= '/' . array_shift($uriVars); } + /* Fix to prevent use uxs skin outside siplified interface, because that skin is not compatible with others interfaces*/ - if ($SYS_SKIN == 'uxs' && $SYS_COLLECTION != 'home' && $SYS_COLLECTION != 'cases') { + if ($args['SYS_SKIN'] == 'uxs' && $args['SYS_COLLECTION'] != 'home' && $args['SYS_COLLECTION'] != 'cases') { $config = System::getSystemConfiguration(); - $SYS_SKIN = $config['default_skin']; + $args['SYS_SKIN'] = $config['default_skin']; } - define("SYS_LANG", $SYS_LANG); - define("SYS_SKIN", $SYS_SKIN); - define('SYS_COLLECTION', $SYS_COLLECTION); - define('SYS_TARGET', $SYS_TARGET); + return $args; + } - if ( $SYS_COLLECTION == 'js2' ) { - print "ERROR"; die; + public function parseRestUri($requestUri) + { + $args = array(); + //$args['SYS_TEMP'] = $requestUri[1]; + define('SYS_TEMP', $requestUri[2]); + $restUri = ''; + + for ($i=3; $i < count($requestUri); $i++) { + $restUri .= '/' . $requestUri[$i]; } + + $args['SYS_LANG'] = 'en'; // TODO, this can be set from http header + $args['SYS_SKIN'] = ''; + $args['SYS_COLLECTION'] = ''; + $args['SYS_TARGET'] = $restUri; + + return $args; } function strip_slashes($vVar) { @@ -5134,7 +5163,43 @@ function getDirectorySize($path,$maxmtime=0) return false; } } -}; + + /** + * This method allow dispatch rest services using 'Restler' thirdparty library + * + * @author Erik Amaru Ortiz + */ + public function dispatchRestService($uri) + { + require_once 'restler/restler.php'; + + $rest = new Restler(); + $rest->setSupportedFormats('JsonFormat', 'XmlFormat'); + + // override global REQUEST_URI to pass to Restler library + $_SERVER['REQUEST_URI'] = $uri; + + // getting all services class + $srvClasses = glob(PATH_SERVICES_REST . '*.php'); + + foreach ($srvClasses as $classFile) { + require_once $classFile; + $className = str_replace('.php', '', basename($classFile)); + $reflClass = new ReflectionClass($className); + + // verify if there is an auth class implementing 'iAuthenticate' + if ($reflClass->implementsInterface('iAuthenticate')) { + // auth class found, set as restler authentication class handler + $rest->addAuthenticationClass($className); + } else { + // add api class + $rest->addAPIClass($className); + } + } + + $rest->handle(); + } +} /** * eprint diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Constants.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Constants.php new file mode 100644 index 000000000..f3bc4ee9c --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Constants.php @@ -0,0 +1,87 @@ +_stream->readByte(); + } + + switch($typeMarker) { + case Zend_Amf_Constants::AMF3_UNDEFINED: + return null; + case Zend_Amf_Constants::AMF3_NULL: + return null; + case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE: + return false; + case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE: + return true; + case Zend_Amf_Constants::AMF3_INTEGER: + return $this->readInteger(); + case Zend_Amf_Constants::AMF3_NUMBER: + return $this->_stream->readDouble(); + case Zend_Amf_Constants::AMF3_STRING: + return $this->readString(); + case Zend_Amf_Constants::AMF3_DATE: + return $this->readDate(); + case Zend_Amf_Constants::AMF3_ARRAY: + return $this->readArray(); + case Zend_Amf_Constants::AMF3_OBJECT: + return $this->readObject(); + case Zend_Amf_Constants::AMF3_XML: + case Zend_Amf_Constants::AMF3_XMLSTRING: + return $this->readXmlString(); + case Zend_Amf_Constants::AMF3_BYTEARRAY: + return $this->readString(); + default: + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker); + } + } + + /** + * Read and deserialize an integer + * + * AMF 3 represents smaller integers with fewer bytes using the most + * significant bit of each byte. The worst case uses 32-bits + * to represent a 29-bit number, which is what we would have + * done with no compression. + * - 0x00000000 - 0x0000007F : 0xxxxxxx + * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx + * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx + * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx + * - 0x40000000 - 0xFFFFFFFF : throw range exception + * + * 0x04 -> integer type code, followed by up to 4 bytes of data. + * + * Parsing integers on OSFlash for the AMF3 integer data format: + * @link http://osflash.org/amf3/parsing_integers + * @return int|float + */ + public function readInteger() + { + $count = 1; + $intReference = $this->_stream->readByte(); + $result = 0; + while ((($intReference & 0x80) != 0) && $count < 4) { + $result <<= 7; + $result |= ($intReference & 0x7f); + $intReference = $this->_stream->readByte(); + $count++; + } + if ($count < 4) { + $result <<= 7; + $result |= $intReference; + } else { + // Use all 8 bits from the 4th byte + $result <<= 8; + $result |= $intReference; + + // Check if the integer should be negative + if (($result & 0x10000000) != 0) { + //and extend the sign bit + $result |= ~0xFFFFFFF; + } + } + return $result; + } + + /** + * Read and deserialize a string + * + * Strings can be sent as a reference to a previously + * occurring String by using an index to the implicit string reference table. + * Strings are encoding using UTF-8 - however the header may either + * describe a string literal or a string reference. + * + * - string = 0x06 string-data + * - string-data = integer-data [ modified-utf-8 ] + * - modified-utf-8 = *OCTET + * + * @return String + */ + public function readString() + { + $stringReference = $this->readInteger(); + + //Check if this is a reference string + if (($stringReference & 0x01) == 0) { + // reference string + $stringReference = $stringReference >> 1; + if ($stringReference >= count($this->_referenceStrings)) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference); + } + // reference string found + return $this->_referenceStrings[$stringReference]; + } + + $length = $stringReference >> 1; + if ($length) { + $string = $this->_stream->readBytes($length); + $this->_referenceStrings[] = $string; + } else { + $string = ""; + } + return $string; + } + + /** + * Read and deserialize a date + * + * Data is the number of milliseconds elapsed since the epoch + * of midnight, 1st Jan 1970 in the UTC time zone. + * Local time zone information is not sent to flash. + * + * - date = 0x08 integer-data [ number-data ] + * + * @return Zend_Date + */ + public function readDate() + { + $dateReference = $this->readInteger(); + if (($dateReference & 0x01) == 0) { + $dateReference = $dateReference >> 1; + if ($dateReference>=count($this->_referenceObjects)) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference); + } + return $this->_referenceObjects[$dateReference]; + } + + $timestamp = floor($this->_stream->readDouble() / 1000); + + require_once 'Zend/Date.php'; + $dateTime = new Zend_Date((int) $timestamp); + $this->_referenceObjects[] = $dateTime; + return $dateTime; + } + + /** + * Read amf array to PHP array + * + * - array = 0x09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] ) + * + * @return array + */ + public function readArray() + { + $arrayReference = $this->readInteger(); + if (($arrayReference & 0x01)==0){ + $arrayReference = $arrayReference >> 1; + if ($arrayReference>=count($this->_referenceObjects)) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference); + } + return $this->_referenceObjects[$arrayReference]; + } + + // Create a holder for the array in the reference list + $data = array(); + $this->_referenceObjects[] =& $data; + $key = $this->readString(); + + // Iterating for string based keys. + while ($key != '') { + $data[$key] = $this->readTypeMarker(); + $key = $this->readString(); + } + + $arrayReference = $arrayReference >>1; + + //We have a dense array + for ($i=0; $i < $arrayReference; $i++) { + $data[] = $this->readTypeMarker(); + } + + return $data; + } + + /** + * Read an object from the AMF stream and convert it into a PHP object + * + * @todo Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo + * @return object|array + */ + public function readObject() + { + $traitsInfo = $this->readInteger(); + $storedObject = ($traitsInfo & 0x01)==0; + $traitsInfo = $traitsInfo >> 1; + + // Check if the Object is in the stored Objects reference table + if ($storedObject) { + $ref = $traitsInfo; + if (!isset($this->_referenceObjects[$ref])) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref); + } + $returnObject = $this->_referenceObjects[$ref]; + } else { + // Check if the Object is in the stored Definitions reference table + $storedClass = ($traitsInfo & 0x01) == 0; + $traitsInfo = $traitsInfo >> 1; + if ($storedClass) { + $ref = $traitsInfo; + if (!isset($this->_referenceDefinitions[$ref])) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref); + } + // Populate the reference attributes + $className = $this->_referenceDefinitions[$ref]['className']; + $encoding = $this->_referenceDefinitions[$ref]['encoding']; + $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames']; + } else { + // The class was not in the reference tables. Start reading rawdata to build traits. + // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal + $className = $this->readString(); + $encoding = $traitsInfo & 0x03; + $propertyNames = array(); + $traitsInfo = $traitsInfo >> 2; + } + + // We now have the object traits defined in variables. Time to go to work: + if (!$className) { + // No class name generic object + $returnObject = new stdClass(); + } else { + // Defined object + // Typed object lookup against registered classname maps + if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) { + $returnObject = new $loader(); + } else { + //user defined typed object + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Typed object not found: '. $className . ' '); + } + } + + // Add the Object to the reference table + $this->_referenceObjects[] = $returnObject; + + $properties = array(); // clear value + // Check encoding types for additional processing. + switch ($encoding) { + case (Zend_Amf_Constants::ET_EXTERNAL): + // Externalizable object such as {ArrayCollection} and {ObjectProxy} + if (!$storedClass) { + $this->_referenceDefinitions[] = array( + 'className' => $className, + 'encoding' => $encoding, + 'propertyNames' => $propertyNames, + ); + } + $returnObject->externalizedData = $this->readTypeMarker(); + break; + case (Zend_Amf_Constants::ET_DYNAMIC): + // used for Name-value encoding + if (!$storedClass) { + $this->_referenceDefinitions[] = array( + 'className' => $className, + 'encoding' => $encoding, + 'propertyNames' => $propertyNames, + ); + } + // not a reference object read name value properties from byte stream + do { + $property = $this->readString(); + if ($property != "") { + $propertyNames[] = $property; + $properties[$property] = $this->readTypeMarker(); + } + } while ($property !=""); + break; + default: + // basic property list object. + if (!$storedClass) { + $count = $traitsInfo; // Number of properties in the list + for($i=0; $i< $count; $i++) { + $propertyNames[] = $this->readString(); + } + // Add a reference to the class. + $this->_referenceDefinitions[] = array( + 'className' => $className, + 'encoding' => $encoding, + 'propertyNames' => $propertyNames, + ); + } + foreach ($propertyNames as $property) { + $properties[$property] = $this->readTypeMarker(); + } + break; + } + + // Add properties back to the return object. + foreach($properties as $key=>$value) { + if($key) { + $returnObject->$key = $value; + } + } + + + } + + if($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) { + if(isset($returnObject->externalizedData)) { + $returnObject = $returnObject->externalizedData; + } else { + $returnObject = get_object_vars($returnObject); + } + } + + return $returnObject; + } + + /** + * Convert XML to SimpleXml + * If user wants DomDocument they can use dom_import_simplexml + * + * @return SimpleXml Object + */ + public function readXmlString() + { + $xmlReference = $this->readInteger(); + $length = $xmlReference >> 1; + $string = $this->_stream->readBytes($length); + return simplexml_load_string($string); + } +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Amf3/Serializer.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Amf3/Serializer.php new file mode 100644 index 000000000..031ce7c4f --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Amf3/Serializer.php @@ -0,0 +1,507 @@ +_stream->writeByte($markerType); + + switch ($markerType) { + case Zend_Amf_Constants::AMF3_NULL: + break; + case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE: + break; + case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE: + break; + case Zend_Amf_Constants::AMF3_INTEGER: + $this->writeInteger($data); + break; + case Zend_Amf_Constants::AMF3_NUMBER: + $this->_stream->writeDouble($data); + break; + case Zend_Amf_Constants::AMF3_STRING: + $this->writeString($data); + break; + case Zend_Amf_Constants::AMF3_DATE: + $this->writeDate($data); + break; + case Zend_Amf_Constants::AMF3_ARRAY: + $this->writeArray($data); + break; + case Zend_Amf_Constants::AMF3_OBJECT: + $this->writeObject($data); + break; + case Zend_Amf_Constants::AMF3_BYTEARRAY: + $this->writeByteArray($data); + break; + case Zend_Amf_Constants::AMF3_XMLSTRING; + $this->writeXml($data); + break; + default: + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unknown Type Marker: ' . $markerType); + } + } else { + // Detect Type Marker + if(is_resource($data)) { + $data = Zend_Amf_Parse_TypeLoader::handleResource($data); + } + switch (true) { + case (null === $data): + $markerType = Zend_Amf_Constants::AMF3_NULL; + break; + case (is_bool($data)): + if ($data){ + $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_TRUE; + } else { + $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_FALSE; + } + break; + case (is_int($data)): + if (($data > 0xFFFFFFF) || ($data < -268435456)) { + $markerType = Zend_Amf_Constants::AMF3_NUMBER; + } else { + $markerType = Zend_Amf_Constants::AMF3_INTEGER; + } + break; + case (is_float($data)): + $markerType = Zend_Amf_Constants::AMF3_NUMBER; + break; + case (is_string($data)): + $markerType = Zend_Amf_Constants::AMF3_STRING; + break; + case (is_array($data)): + $markerType = Zend_Amf_Constants::AMF3_ARRAY; + break; + case (is_object($data)): + // Handle object types. + if (($data instanceof DateTime) || ($data instanceof Zend_Date)) { + $markerType = Zend_Amf_Constants::AMF3_DATE; + } else if ($data instanceof Zend_Amf_Value_ByteArray) { + $markerType = Zend_Amf_Constants::AMF3_BYTEARRAY; + } else if (($data instanceof DOMDocument) || ($data instanceof SimpleXMLElement)) { + $markerType = Zend_Amf_Constants::AMF3_XMLSTRING; + } else { + $markerType = Zend_Amf_Constants::AMF3_OBJECT; + } + break; + default: + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data)); + } + $this->writeTypeMarker($data, $markerType); + } + } + + /** + * Write an AMF3 integer + * + * @param int|float $data + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeInteger($int) + { + if (($int & 0xffffff80) == 0) { + $this->_stream->writeByte($int & 0x7f); + return $this; + } + + if (($int & 0xffffc000) == 0 ) { + $this->_stream->writeByte(($int >> 7 ) | 0x80); + $this->_stream->writeByte($int & 0x7f); + return $this; + } + + if (($int & 0xffe00000) == 0) { + $this->_stream->writeByte(($int >> 14 ) | 0x80); + $this->_stream->writeByte(($int >> 7 ) | 0x80); + $this->_stream->writeByte($int & 0x7f); + return $this; + } + + $this->_stream->writeByte(($int >> 22 ) | 0x80); + $this->_stream->writeByte(($int >> 15 ) | 0x80); + $this->_stream->writeByte(($int >> 8 ) | 0x80); + $this->_stream->writeByte($int & 0xff); + return $this; + } + + /** + * Send string to output stream, without trying to reference it. + * The string is prepended with strlen($string) << 1 | 0x01 + * + * @param string $string + * @return Zend_Amf_Parse_Amf3_Serializer + */ + protected function writeBinaryString($string){ + $ref = strlen($string) << 1 | 0x01; + $this->writeInteger($ref); + $this->_stream->writeBytes($string); + + return $this; + } + + /** + * Send string to output stream + * + * @param string $string + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeString($string) + { + $len = strlen($string); + if(!$len){ + $this->writeInteger(0x01); + return $this; + } + + $ref = array_search($string, $this->_referenceStrings, true); + if($ref === false){ + $this->_referenceStrings[] = $string; + $this->writeBinaryString($string); + } else { + $ref <<= 1; + $this->writeInteger($ref); + } + + return $this; + } + + /** + * Send ByteArray to output stream + * + * @param string|Zend_Amf_Value_ByteArray $data + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeByteArray($data){ + if($this->writeObjectReference($data)){ + return $this; + } + + if(is_string($data)) { + //nothing to do + } else if ($data instanceof Zend_Amf_Value_ByteArray) { + $data = $data->getData(); + } else { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Invalid ByteArray specified; must be a string or Zend_Amf_Value_ByteArray'); + } + + $this->writeBinaryString($data); + + return $this; + } + + /** + * Send xml to output stream + * + * @param DOMDocument|SimpleXMLElement $xml + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeXml($xml) + { + if($this->writeObjectReference($xml)){ + return $this; + } + + if(is_string($xml)) { + //nothing to do + } else if ($xml instanceof DOMDocument) { + $xml = $xml->saveXml(); + } else if ($xml instanceof SimpleXMLElement) { + $xml = $xml->asXML(); + } else { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Invalid xml specified; must be a DOMDocument or SimpleXMLElement'); + } + + $this->writeBinaryString($xml); + + return $this; + } + + /** + * Convert DateTime/Zend_Date to AMF date + * + * @param DateTime|Zend_Date $date + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeDate($date) + { + if($this->writeObjectReference($date)){ + return $this; + } + + if ($date instanceof DateTime) { + $dateString = $date->format('U') * 1000; + } elseif ($date instanceof Zend_Date) { + $dateString = $date->toString('U') * 1000; + } else { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Invalid date specified; must be a string DateTime or Zend_Date object'); + } + + $this->writeInteger(0x01); + // write time to stream minus milliseconds + $this->_stream->writeDouble($dateString); + return $this; + } + + /** + * Write a PHP array back to the amf output stream + * + * @param array $array + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeArray(array $array) + { + // arrays aren't reference here but still counted + $this->_referenceObjects[] = $array; + + // have to seperate mixed from numberic keys. + $numeric = array(); + $string = array(); + foreach ($array as $key => $value) { + if (is_int($key)) { + $numeric[] = $value; + } else { + $string[$key] = $value; + } + } + + // write the preamble id of the array + $length = count($numeric); + $id = ($length << 1) | 0x01; + $this->writeInteger($id); + + //Write the mixed type array to the output stream + foreach($string as $key => $value) { + $this->writeString($key) + ->writeTypeMarker($value); + } + $this->writeString(''); + + // Write the numeric array to ouput stream + foreach($numeric as $value) { + $this->writeTypeMarker($value); + } + return $this; + } + + /** + * Check if the given object is in the reference table, write the reference if it exists, + * otherwise add the object to the reference table + * + * @param mixed $object object to check for reference + * @return Boolean true, if the reference was written, false otherwise + */ + protected function writeObjectReference($object) + { + $ref = array_search($object, $this->_referenceObjects,true); + //quickly handle object references + if($ref !== false){ + $ref <<= 1; + $this->writeInteger($ref); + return true; + } + $this->_referenceObjects[] = $object; + return false; + } + + /** + * Write object to ouput stream + * + * @param mixed $data + * @return Zend_Amf_Parse_Amf3_Serializer + */ + public function writeObject($object) + { + if($this->writeObjectReference($object)){ + return $this; + } + + $className = ''; + + //Check to see if the object is a typed object and we need to change + switch (true) { + // the return class mapped name back to actionscript class name. + case ($className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object))): + break; + + // Check to see if the user has defined an explicit Action Script type. + case isset($object->_explicitType): + $className = $object->_explicitType; + break; + + // Check if user has defined a method for accessing the Action Script type + case method_exists($object, 'getASClassName'): + $className = $object->getASClassName(); + break; + + // No return class name is set make it a generic object + case ($object instanceof stdClass): + $className = ''; + break; + + // By default, use object's class name + default: + $className = get_class($object); + break; + } + + $writeTraits = true; + + //check to see, if we have a corresponding definition + if(array_key_exists($className, $this->_referenceDefinitions)){ + $traitsInfo = $this->_referenceDefinitions[$className]['id']; + $encoding = $this->_referenceDefinitions[$className]['encoding']; + $propertyNames = $this->_referenceDefinitions[$className]['propertyNames']; + + $traitsInfo = ($traitsInfo << 2) | 0x01; + + $writeTraits = false; + } else { + $propertyNames = array(); + + if($className == ''){ + //if there is no className, we interpret the class as dynamic without any sealed members + $encoding = Zend_Amf_Constants::ET_DYNAMIC; + } else { + $encoding = Zend_Amf_Constants::ET_PROPLIST; + + foreach($object as $key => $value) { + if( $key[0] != "_") { + $propertyNames[] = $key; + } + } + } + + $this->_referenceDefinitions[$className] = array( + 'id' => count($this->_referenceDefinitions), + 'encoding' => $encoding, + 'propertyNames' => $propertyNames, + ); + + $traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING; + $traitsInfo |= $encoding << 2; + $traitsInfo |= (count($propertyNames) << 4); + } + + $this->writeInteger($traitsInfo); + + if($writeTraits){ + $this->writeString($className); + foreach ($propertyNames as $value) { + $this->writeString($value); + } + } + + try { + switch($encoding) { + case Zend_Amf_Constants::ET_PROPLIST: + //Write the sealed values to the output stream. + foreach ($propertyNames as $key) { + $this->writeTypeMarker($object->$key); + } + break; + case Zend_Amf_Constants::ET_DYNAMIC: + //Write the sealed values to the output stream. + foreach ($propertyNames as $key) { + $this->writeTypeMarker($object->$key); + } + + //Write remaining properties + foreach($object as $key => $value){ + if(!in_array($key,$propertyNames) && $key[0] != "_"){ + $this->writeString($key); + $this->writeTypeMarker($value); + } + } + + //Write an empty string to end the dynamic part + $this->writeString(''); + break; + case Zend_Amf_Constants::ET_EXTERNAL: + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('External Object Encoding not implemented'); + break; + default: + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unknown Object Encoding type: ' . $encoding); + } + } catch (Exception $e) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unable to writeObject output: ' . $e->getMessage(), 0, $e); + } + + return $this; + } +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Deserializer.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Deserializer.php new file mode 100644 index 000000000..4f697bb1f --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/Deserializer.php @@ -0,0 +1,65 @@ +_stream = $stream; + } + + /** + * Checks for AMF marker types and calls the appropriate methods + * for deserializing those marker types. Markers are the data type of + * the following value. + * + * @param int $typeMarker + * @return mixed Whatever the data type is of the marker in php + */ + public abstract function readTypeMarker($markerType = null); +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/InputStream.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/InputStream.php new file mode 100644 index 000000000..03dff2f6b --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/InputStream.php @@ -0,0 +1,39 @@ +_stream = $stream; + } + + /** + * Find the PHP object type and convert it into an AMF object type + * + * @param mixed $content + * @param int $markerType + * @return void + */ + public abstract function writeTypeMarker($content, $markerType=null); +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/TypeLoader.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/TypeLoader.php new file mode 100644 index 000000000..85aab3212 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Parse/TypeLoader.php @@ -0,0 +1,231 @@ + 'Zend_Amf_Value_Messaging_AcknowledgeMessage', + 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_AsyncMessage', + 'flex.messaging.messages.CommandMessage' => 'Zend_Amf_Value_Messaging_CommandMessage', + 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_ErrorMessage', + 'flex.messaging.messages.RemotingMessage' => 'Zend_Amf_Value_Messaging_RemotingMessage', + 'flex.messaging.io.ArrayCollection' => 'Zend_Amf_Value_Messaging_ArrayCollection', + ); + + /** + * @var array Default class map + */ + protected static $_defaultClassMap = array( + 'flex.messaging.messages.AcknowledgeMessage' => 'Zend_Amf_Value_Messaging_AcknowledgeMessage', + 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_AsyncMessage', + 'flex.messaging.messages.CommandMessage' => 'Zend_Amf_Value_Messaging_CommandMessage', + 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_ErrorMessage', + 'flex.messaging.messages.RemotingMessage' => 'Zend_Amf_Value_Messaging_RemotingMessage', + 'flex.messaging.io.ArrayCollection' => 'Zend_Amf_Value_Messaging_ArrayCollection', + ); + + /** + * @var Zend_Loader_PluginLoader_Interface + */ + protected static $_resourceLoader = null; + + + /** + * Load the mapped class type into a callback. + * + * @param string $className + * @return object|false + */ + public static function loadType($className) + { + $class = self::getMappedClassName($className); + if(!$class) { + $class = str_replace('.', '_', $className); + } + if (!class_exists($class)) { + return "stdClass"; + } + return $class; + } + + /** + * Looks up the supplied call name to its mapped class name + * + * @param string $className + * @return string + */ + public static function getMappedClassName($className) + { + $mappedName = array_search($className, self::$classMap); + + if ($mappedName) { + return $mappedName; + } + + $mappedName = array_search($className, array_flip(self::$classMap)); + + if ($mappedName) { + return $mappedName; + } + + return false; + } + + /** + * Map PHP class names to ActionScript class names + * + * Allows users to map the class names of there action script classes + * to the equivelent php class name. Used in deserialization to load a class + * and serialiation to set the class name of the returned object. + * + * @param string $asClassName + * @param string $phpClassName + * @return void + */ + public static function setMapping($asClassName, $phpClassName) + { + self::$classMap[$asClassName] = $phpClassName; + } + + /** + * Reset type map + * + * @return void + */ + public static function resetMap() + { + self::$classMap = self::$_defaultClassMap; + } + + /** + * Set loader for resource type handlers + * + * @param Zend_Loader_PluginLoader_Interface $loader + */ + public static function setResourceLoader(Zend_Loader_PluginLoader_Interface $loader) + { + self::$_resourceLoader = $loader; + } + + /** + * Add directory to the list of places where to look for resource handlers + * + * @param string $prefix + * @param string $dir + */ + public static function addResourceDirectory($prefix, $dir) + { + if(self::$_resourceLoader) { + self::$_resourceLoader->addPrefixPath($prefix, $dir); + } + } + + /** + * Get plugin class that handles this resource + * + * @param resource $resource Resource type + * @return string Class name + */ + public static function getResourceParser($resource) + { + if(self::$_resourceLoader) { + $type = preg_replace("/[^A-Za-z0-9_]/", " ", get_resource_type($resource)); + $type = str_replace(" ","", ucwords($type)); + return self::$_resourceLoader->load($type); + } + return false; + } + + /** + * Convert resource to a serializable object + * + * @param resource $resource + * @return mixed + */ + public static function handleResource($resource) + { + if(!self::$_resourceLoader) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Unable to handle resources - resource plugin loader not set'); + } + try { + while(is_resource($resource)) { + $resclass = self::getResourceParser($resource); + if(!$resclass) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Can not serialize resource type: '. get_resource_type($resource)); + } + $parser = new $resclass(); + if(is_callable(array($parser, 'parse'))) { + $resource = $parser->parse($resource); + } else { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception("Could not call parse() method on class $resclass"); + } + } + return $resource; + } catch(Zend_Amf_Exception $e) { + throw new Zend_Amf_Exception($e->getMessage(), $e->getCode(), $e); + } catch(Exception $e) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Can not serialize resource type: '. get_resource_type($resource), 0, $e); + } + } +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Util/BinaryStream.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Util/BinaryStream.php new file mode 100644 index 000000000..faa532af4 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Util/BinaryStream.php @@ -0,0 +1,285 @@ +_stream = $stream; + $this->_needle = 0; + $this->_streamLength = strlen($stream); + $this->_bigEndian = (pack('l', 1) === "\x00\x00\x00\x01"); + } + + /** + * Returns the current stream + * + * @return string + */ + public function getStream() + { + return $this->_stream; + } + + /** + * Read the number of bytes in a row for the length supplied. + * + * @todo Should check that there are enough bytes left in the stream we are about to read. + * @param int $length + * @return string + * @throws Zend_Amf_Exception for buffer underrun + */ + public function readBytes($length) + { + if (($length + $this->_needle) > $this->_streamLength) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Buffer underrun at needle position: ' . $this->_needle . ' while requesting length: ' . $length); + } + $bytes = substr($this->_stream, $this->_needle, $length); + $this->_needle+= $length; + return $bytes; + } + + /** + * Write any length of bytes to the stream + * + * Usually a string. + * + * @param string $bytes + * @return Zend_Amf_Util_BinaryStream + */ + public function writeBytes($bytes) + { + $this->_stream.= $bytes; + return $this; + } + + /** + * Reads a signed byte + * + * @return int Value is in the range of -128 to 127. + */ + public function readByte() + { + if (($this->_needle + 1) > $this->_streamLength) { + require_once 'Zend/Amf/Exception.php'; + throw new Zend_Amf_Exception('Buffer underrun at needle position: ' . $this->_needle . ' while requesting length: ' . $length); + } + + return ord($this->_stream{$this->_needle++}); + } + + /** + * Writes the passed string into a signed byte on the stream. + * + * @param string $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeByte($stream) + { + $this->_stream.= pack('c', $stream); + return $this; + } + + /** + * Reads a signed 32-bit integer from the data stream. + * + * @return int Value is in the range of -2147483648 to 2147483647 + */ + public function readInt() + { + return ($this->readByte() << 8) + $this->readByte(); + } + + /** + * Write an the integer to the output stream as a 32 bit signed integer + * + * @param int $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeInt($stream) + { + $this->_stream.= pack('n', $stream); + return $this; + } + + /** + * Reads a UTF-8 string from the data stream + * + * @return string A UTF-8 string produced by the byte representation of characters + */ + public function readUtf() + { + $length = $this->readInt(); + return $this->readBytes($length); + } + + /** + * Wite a UTF-8 string to the outputstream + * + * @param string $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeUtf($stream) + { + $this->writeInt(strlen($stream)); + $this->_stream.= $stream; + return $this; + } + + + /** + * Read a long UTF string + * + * @return string + */ + public function readLongUtf() + { + $length = $this->readLong(); + return $this->readBytes($length); + } + + /** + * Write a long UTF string to the buffer + * + * @param string $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeLongUtf($stream) + { + $this->writeLong(strlen($stream)); + $this->_stream.= $stream; + } + + /** + * Read a long numeric value + * + * @return double + */ + public function readLong() + { + return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); + } + + /** + * Write long numeric value to output stream + * + * @param int|string $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeLong($stream) + { + $this->_stream.= pack('N', $stream); + return $this; + } + + /** + * Read a 16 bit unsigned short. + * + * @todo This could use the unpack() w/ S,n, or v + * @return double + */ + public function readUnsignedShort() + { + $byte1 = $this->readByte(); + $byte2 = $this->readByte(); + return (($byte1 << 8) | $byte2); + } + + /** + * Reads an IEEE 754 double-precision floating point number from the data stream. + * + * @return double Floating point number + */ + public function readDouble() + { + $bytes = substr($this->_stream, $this->_needle, 8); + $this->_needle+= 8; + + if (!$this->_bigEndian) { + $bytes = strrev($bytes); + } + + $double = unpack('dflt', $bytes); + return $double['flt']; + } + + /** + * Writes an IEEE 754 double-precision floating point number from the data stream. + * + * @param string|double $stream + * @return Zend_Amf_Util_BinaryStream + */ + public function writeDouble($stream) + { + $stream = pack('d', $stream); + if (!$this->_bigEndian) { + $stream = strrev($stream); + } + $this->_stream.= $stream; + return $this; + } + +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AbstractMessage.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AbstractMessage.php new file mode 100644 index 000000000..1974405a5 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AbstractMessage.php @@ -0,0 +1,92 @@ +clientId = $this->generateId(); + $this->destination = null; + $this->messageId = $this->generateId(); + $this->timestamp = time().'00'; + $this->timeToLive = 0; + $this->headers = new STDClass(); + $this->body = null; + + // correleate the two messages + if ($message && isset($message->messageId)) { + $this->correlationId = $message->messageId; + } + } +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AsyncMessage.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AsyncMessage.php new file mode 100644 index 000000000..0e02f2ee1 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/AsyncMessage.php @@ -0,0 +1,43 @@ +body + * of the message. + */ + const LOGIN_OPERATION = 8; + + /** + * This operation is used to log the user out of the current channel, and + * will invalidate the server session if the channel is HTTP based. + */ + const LOGOUT_OPERATION = 9; + + /** + * This operation is used to indicate that the client's subscription to a + * remote destination has been invalidated. + */ + const SESSION_INVALIDATE_OPERATION = 10; + + /** + * This operation is used by the MultiTopicConsumer to subscribe/unsubscribe + * from multiple subtopics/selectors in the same message. + */ + const MULTI_SUBSCRIBE_OPERATION = 11; + + /** + * This operation is used to indicate that a channel has disconnected + */ + const DISCONNECT_OPERATION = 12; + + /** + * This is the default operation for new CommandMessage instances. + */ + const UNKNOWN_OPERATION = 10000; + + /** + * The operation to execute for messages of this type + * @var int + */ + public $operation = self::UNKNOWN_OPERATION; +} diff --git a/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/ErrorMessage.php b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/ErrorMessage.php new file mode 100644 index 000000000..93da0e929 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/Zend/Amf/Value/Messaging/ErrorMessage.php @@ -0,0 +1,67 @@ +clientId = $this->generateId(); + $this->destination = null; + $this->messageId = $this->generateId(); + $this->timestamp = time().'00'; + $this->timeToLive = 0; + $this->headers = new stdClass(); + $this->body = null; + } +} diff --git a/gulliver/thirdparty/restler/amfformat/amfformat.php b/gulliver/thirdparty/restler/amfformat/amfformat.php new file mode 100644 index 000000000..d070d2143 --- /dev/null +++ b/gulliver/thirdparty/restler/amfformat/amfformat.php @@ -0,0 +1,70 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ + +class AmfFormat implements iFormat +{ + const PATH = '/Zend/Amf/Parse/'; + const MIME = 'application/x-amf'; + const EXTENSION = 'amf'; + + public $path = ''; + + public function init(){ + $this->path = dirname(__FILE__); + $include_path = get_include_path(); + if(strpos($include_path, 'Zend')===FALSE){ + set_include_path($this->path.':'.$include_path); + } + $this->path .= AmfFormat::PATH; + } + + public function getMIMEMap() + { + return array(AmfFormat::EXTENSION=>AmfFormat::MIME); + } + public function getMIME(){ + return AmfFormat::MIME; + } + public function getExtension(){ + return AmfFormat::EXTENSION; + } + public function setMIME($mime){ + //do nothing + } + public function setExtension($extension){ + //do nothing + } + + public function encode($data, $human_readable=false){ + $this->init(); + require_once $this->path . 'OutputStream.php'; + require_once $this->path . 'Amf3/Serializer.php'; + $stream = new Zend_Amf_Parse_OutputStream(); + $serializer = new Zend_Amf_Parse_Amf3_Serializer($stream); + $serializer->writeTypeMarker($data); + return $stream->getStream(); + } + + public function decode($data){ + $this->init(); + require_once $this->path .'InputStream.php'; + require_once $this->path .'Amf3/Deserializer.php'; + $stream = new Zend_Amf_Parse_InputStream(substr($data, 1)); + $deserializer = new Zend_Amf_Parse_Amf3_Deserializer($stream); + return $deserializer->readTypeMarker(); + } + + public function __toString(){ + return $this->getExtension(); + } +} \ No newline at end of file diff --git a/gulliver/thirdparty/restler/compat.php b/gulliver/thirdparty/restler/compat.php new file mode 100644 index 000000000..08a950d20 --- /dev/null +++ b/gulliver/thirdparty/restler/compat.php @@ -0,0 +1,58 @@ +0){ + die('Restler requires PHP 5.x.x'); +} +#requires 5.3.2 +if(!method_exists('ReflectionMethod', 'setAccessible')){ + #echo'RESTLER_METHOD_UNPROTECTION_MODE'; + function isRestlerCompatibilityModeEnabled(){ + return TRUE; + } + function unprotect($method_info){ + $class_name = $method_info->class_name; + $method = $method_info->method_name; + $params = $method_info->arguments; + $unique = uniqid('Dynamic')."_"; + $class_code = "class $unique$class_name extends $class_name {"; + $p = array(); + for ($i = 0; $i < count($params); $i++) { + $p[]='$'."P$i"; + } + $p = implode(',', $p); + $class_code .= "function $unique$method($p){return parent::$method($p);}"; + $class_code .= "}"; + #echo $class_code; + eval($class_code); + $method_info->class_name = "$unique$class_name"; + $method_info->method_name = "$unique$method"; + return $method_info; + } + function call_protected_user_method_array($class_name, $method, $params) + { + if(is_object($class_name))$class_name = get_class($class_name); + $unique = uniqid('Dynamic')."_"; + $class_code = "class $unique$class_name extends $class_name {"; + $p = array(); + for ($i = 0; $i < count($params); $i++) { + $p[]='$'."P$i"; + } + $p = implode(',', $p); + $class_code .= "function $unique$method($p){return parent::$method($p);}"; + $class_code .= "}"; + #echo $class_code; + eval($class_code); + $obj = $unique.$class_name; + $obj = new $obj(); + return call_user_func_array(array($obj,$unique.$method), $params); + } +} + +#requires 5.2.3 +#if(!method_exists('ReflectionParameter', 'getPosition')){ +#found fix! not using getPosition in restler 2.0 +#} + +#requires 5.3.0 +#if(!defined('__DIR__')){ +# avoided using it in Restler and replaced it with dirname(__FILE__) +#} diff --git a/gulliver/thirdparty/restler/plistformat/CFBinaryPropertyList.php b/gulliver/thirdparty/restler/plistformat/CFBinaryPropertyList.php new file mode 100644 index 000000000..f946193da --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/CFBinaryPropertyList.php @@ -0,0 +1,970 @@ + + * @author Christian Kruse + * @package plist + * @version $Id$ + */ + +/** + * Facility for reading and writing binary PropertyLists. Ported from {@link http://www.opensource.apple.com/source/CF/CF-476.15/CFBinaryPList.c CFBinaryPList.c}. + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @example example-read-02.php Read a Binary PropertyList + * @example example-read-03.php Read a PropertyList without knowing the type + */ +abstract class CFBinaryPropertyList { + /** + * Content of the plist (unparsed string) + * @var string + */ + protected $content = NULL; + + /** + * position in the (unparsed) string + * @var integer + */ + protected $pos = 0; + + /** + * Table containing uniqued objects + * @var array + */ + protected $uniqueTable = Array(); + + /** + * Number of objects in file + * @var integer + */ + protected $countObjects = 0; + + /** + * The length of all strings in the file (byte length, not character length) + * @var integer + */ + protected $stringSize = 0; + + /** + * The length of all ints in file (byte length) + * @var integer + */ + protected $intSize = 0; + + /** + * The length of misc objects (i.e. not integer and not string) in file + * @var integer + */ + protected $miscSize = 0; + + /** + * Number of object references in file (needed to calculate reference byte length) + * @var integer + */ + protected $objectRefs = 0; + + /** + * Number of objects written during save phase; needed to calculate the size of the object table + * @var integer + */ + protected $writtenObjectCount = 0; + + /** + * Table containing all objects in the file + */ + protected $objectTable = Array(); + + /** + * The size of object references + */ + protected $objectRefSize = 0; + + /** + * The „offsets” (i.e. the different entries) in the file + */ + protected $offsets = Array(); + + /** + * Read a „null type” (filler byte, true, false, 0 byte) + * @param $length The byte itself + * @return the byte value (e.g. CFBoolean(true), CFBoolean(false), 0 or 15) + * @throws PListException on encountering an unknown null type + */ + protected function readBinaryNullType($length) { + switch($length) { + case 0: return 0; // null type + case 8: return new CFBoolean(false); + case 9: return new CFBoolean(true); + case 15: return 15; // fill type + } + + throw new PListException("unknown null type: $length"); + } + + /** + * Create an 64 bit integer using bcmath or gmp + * @param int $hi The higher word + * @param int $lo The lower word + * @return mixed The integer (as int if possible, as string if not possible) + * @throws PListException if neither gmp nor bc available + */ + protected static function make64Int($hi,$lo) { + // on x64, we can just use int + if(PHP_INT_SIZE > 4) return (((int)$hi)<<32) | ((int)$lo); + + // lower word has to be unsigned since we don't use bitwise or, we use bcadd/gmp_add + $lo = sprintf("%u", $lo); + + // use GMP or bcmath if possible + if(function_exists("gmp_mul")) return gmp_strval(gmp_add(gmp_mul($hi, "4294967296"), $lo)); + + if(function_exists("bcmul")) return bcadd(bcmul($hi,"4294967296"), $lo); + + if(class_exists('Math_BigInteger')) { + $bi = new Math_BigInteger($hi); + return $bi->multiply("4294967296")->add($lo)->toString(); + } + + throw new PListException("either gmp or bc has to be installed, or the Math_BigInteger has to be available!"); + } + + /** + * Read an integer value + * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” + * @return CFNumber The integer value + * @throws PListException if integer val is invalid + * @throws IOException if read error occurs + * @uses make64Int() to overcome PHP's big integer problems + */ + protected function readBinaryInt($length) { + if($length > 3) throw new PListException("Integer greater than 8 bytes: $length"); + + $nbytes = 1 << $length; + + $val = null; + if(strlen($buff = substr($this->content, $this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); + $this->pos += $nbytes; + + switch($length) { + case 0: + $val = unpack("C", $buff); + $val = $val[1]; + break; + case 1: + $val = unpack("n", $buff); + $val = $val[1]; + break; + case 2: + $val = unpack("N", $buff); + $val = $val[1]; + break; + case 3: + $words = unpack("Nhighword/Nlowword",$buff); + //$val = $words['highword'] << 32 | $words['lowword']; + $val = self::make64Int($words['highword'],$words['lowword']); + break; + } + + return new CFNumber($val); + } + + /** + * Read a real value + * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” + * @return CFNumber The real value + * @throws PListException if real val is invalid + * @throws IOException if read error occurs + */ + protected function readBinaryReal($length) { + if($length > 3) throw new PListException("Real greater than 8 bytes: $length"); + + $nbytes = 1 << $length; + $val = null; + if(strlen($buff = substr($this->content,$this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); + $this->pos += $nbytes; + + switch($length) { + case 0: // 1 byte float? must be an error + case 1: // 2 byte float? must be an error + $x = $length + 1; + throw new PListException("got {$x} byte float, must be an error!"); + case 2: + $val = unpack("f", strrev($buff)); + $val = $val[1]; + break; + case 3: + $val = unpack("d", strrev($buff)); + $val = $val[1]; + break; + } + + return new CFNumber($val); + } + + /** + * Read a date value + * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” + * @return CFDate The date value + * @throws PListException if date val is invalid + * @throws IOException if read error occurs + */ + protected function readBinaryDate($length) { + if($length > 3) throw new PListException("Date greater than 8 bytes: $length"); + + $nbytes = 1 << $length; + $val = null; + if(strlen($buff = substr($this->content, $this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); + $this->pos += $nbytes; + + switch($length) { + case 0: // 1 byte CFDate is an error + case 1: // 2 byte CFDate is an error + $x = $length + 1; + throw new PListException("{$x} byte CFdate, error"); + + case 2: + $val = unpack("f", strrev($buff)); + $val = $val[1]; + break; + case 3: + $val = unpack("d", strrev($buff)); + $val = $val[1]; + break; + } + + return new CFDate($val,CFDate::TIMESTAMP_APPLE); + } + + /** + * Read a data value + * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” + * @return CFData The data value + * @throws IOException if read error occurs + */ + protected function readBinaryData($length) { + if($length == 0) $buff = ""; + else { + $buff = substr($this->content, $this->pos, $length); + if(strlen($buff) != $length) throw IOException::readError(""); + $this->pos += $length; + } + + return new CFData($buff,false); + } + + /** + * Read a string value, usually coded as utf8 + * @param integer $length The length (in bytes) of the string value + * @return CFString The string value, utf8 encoded + * @throws IOException if read error occurs + */ + protected function readBinaryString($length) { + if($length == 0) $buff = ""; + else { + if(strlen($buff = substr($this->content, $this->pos, $length)) != $length) throw IOException::readError(""); + $this->pos += $length; + } + + if(!isset($this->uniqueTable[$buff])) $this->uniqueTable[$buff] = true; + return new CFString($buff); + } + + /** + * Convert the given string from one charset to another. + * Trying to use MBString, Iconv, Recode - in that particular order. + * @param string $string the string to convert + * @param string $fromCharset the charset the given string is currently encoded in + * @param string $toCharset the charset to convert to, defaults to UTF-8 + * @return string the converted string + * @throws PListException on neither MBString, Iconv, Recode being available + */ + public static function convertCharset($string, $fromCharset, $toCharset='UTF-8') { + if(function_exists('mb_convert_encoding')) return mb_convert_encoding($string, $toCharset, $fromCharset); + if(function_exists('iconv')) return iconv($fromCharset, $toCharset, $string); + if(function_exists('recode_string')) return recode_string($fromCharset .'..'. $toCharset, $string); + + throw new PListException('neither iconv nor mbstring supported. how are we supposed to work on strings here?'); + } + + /** + * Count characters considering character set + * Trying to use MBString, Iconv - in that particular order. + * @param string $string the string to convert + * @param string $charset the charset the given string is currently encoded in + * @return integer The number of characters in that string + * @throws PListException on neither MBString, Iconv being available + */ + public static function charsetStrlen($string,$charset="UTF-8") { + if(function_exists('mb_strlen')) return mb_strlen($string, $charset); + if(function_exists('iconv_strlen')) return iconv_strlen($string,$charset); + + throw new PListException('neither iconv nor mbstring supported. how are we supposed to work on strings here?'); + } + + /** + * Read a unicode string value, coded as UTF-16BE + * @param integer $length The length (in bytes) of the string value + * @return CFString The string value, utf8 encoded + * @throws IOException if read error occurs + */ + protected function readBinaryUnicodeString($length) { + /* The problem is: we get the length of the string IN CHARACTERS; + since a char in UTF-16 can be 16 or 32 bit long, we don't really know + how long the string is in bytes */ + if(strlen($buff = substr($this->content, $this->pos, 2*$length)) != 2*$length) throw IOException::readError(""); + $this->pos += 2 * $length; + + if(!isset($this->uniqueTable[$buff])) $this->uniqueTable[$buff] = true; + return new CFString(self::convertCharset($buff, "UTF-16BE", "UTF-8")); + } + + /** + * Read an array value, including contained objects + * @param integer $length The number of contained objects + * @return CFArray The array value, including the objects + * @throws IOException if read error occurs + */ + protected function readBinaryArray($length) { + $ary = new CFArray(); + + // first: read object refs + if($length != 0) { + if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); + $this->pos += $length * $this->objectRefSize; + + $objects = unpack($this->objectRefSize == 1 ? "C*" : "n*", $buff); + + // now: read objects + for($i=0;$i<$length;++$i) { + $object = $this->readBinaryObjectAt($objects[$i+1]+1,$this->objectRefSize); + $ary->add($object); + } + } + + return $ary; + } + + /** + * Read a dictionary value, including contained objects + * @param integer $length The number of contained objects + * @return CFDictionary The dictionary value, including the objects + * @throws IOException if read error occurs + */ + protected function readBinaryDict($length) { + $dict = new CFDictionary(); + + // first: read keys + if($length != 0) { + if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); + $this->pos += $length * $this->objectRefSize; + $keys = unpack(($this->objectRefSize == 1 ? "C*" : "n*"), $buff); + + // second: read object refs + if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); + $this->pos += $length * $this->objectRefSize; + $objects = unpack(($this->objectRefSize == 1 ? "C*" : "n*"), $buff); + + // read real keys and objects + for($i=0;$i<$length;++$i) { + $key = $this->readBinaryObjectAt($keys[$i+1]+1); + $object = $this->readBinaryObjectAt($objects[$i+1]+1); + $dict->add($key->getValue(),$object); + } + } + + return $dict; + } + + /** + * Read an object type byte, decode it and delegate to the correct reader function + * @return mixed The value of the delegate reader, so any of the CFType subclasses + * @throws IOException if read error occurs + */ + function readBinaryObject() { + // first: read the marker byte + if(strlen($buff = substr($this->content,$this->pos,1)) != 1) throw IOException::readError(""); + $this->pos++; + + $object_length = unpack("C*", $buff); + $object_length = $object_length[1] & 0xF; + $buff = unpack("H*", $buff); + $buff = $buff[1]; + + $object_type = substr($buff, 0, 1); + if($object_type != "0" && $object_length == 15) { + $object_length = $this->readBinaryObject($this->objectRefSize); + $object_length = $object_length->getValue(); + } + + $retval = null; + switch($object_type) { + case '0': // null, false, true, fillbyte + $retval = $this->readBinaryNullType($object_length); + break; + case '1': // integer + $retval = $this->readBinaryInt($object_length); + break; + case '2': // real + $retval = $this->readBinaryReal($object_length); + break; + case '3': // date + $retval = $this->readBinaryDate($object_length); + break; + case '4': // data + $retval = $this->readBinaryData($object_length); + break; + case '5': // byte string, usually utf8 encoded + $retval = $this->readBinaryString($object_length); + break; + case '6': // unicode string (utf16be) + $retval = $this->readBinaryUnicodeString($object_length); + break; + case 'a': // array + $retval = $this->readBinaryArray($object_length); + break; + case 'd': // dictionary + $retval = $this->readBinaryDict($object_length); + break; + } + + return $retval; + } + + /** + * Read an object type byte at position $pos, decode it and delegate to the correct reader function + * @param integer $pos The table position in the offsets table + * @return mixed The value of the delegate reader, so any of the CFType subclasses + */ + function readBinaryObjectAt($pos) { + $this->pos = $this->offsets[$pos]; + return $this->readBinaryObject(); + } + + /** + * Parse a binary plist string + * @return void + * @throws IOException if read error occurs + */ + public function parseBinaryString() { + $this->uniqueTable = Array(); + $this->countObjects = 0; + $this->stringSize = 0; + $this->intSize = 0; + $this->miscSize = 0; + $this->objectRefs = 0; + + $this->writtenObjectCount = 0; + $this->objectTable = Array(); + $this->objectRefSize = 0; + + $this->offsets = Array(); + + // first, we read the trailer: 32 byte from the end + $buff = substr($this->content,-32); + + $infos = unpack("x6/Coffset_size/Cobject_ref_size/x4/Nnumber_of_objects/x4/Ntop_object/x4/Ntable_offset",$buff); + + // after that, get the offset table + $coded_offset_table = substr($this->content,$infos['table_offset'],$infos['number_of_objects'] * $infos['offset_size']); + if(strlen($coded_offset_table) != $infos['number_of_objects'] * $infos['offset_size']) throw IOException::readError(""); + $this->countObjects = $infos['number_of_objects']; + + // decode offset table + $formats = Array("","C*","n*",NULL,"N*"); + if($infos['offset_size'] == 3) { # since PHP does not support parenthesis in pack/unpack expressions, + # "(H6)*" does not work and we have to work round this by repeating the + # expression as often as it fits in the string + $this->offsets = array(NULL); + while($coded_offset_table) { + $str = unpack("H6",$coded_offset_table); + $this->offsets[] = hexdec($str[1]); + $coded_offset_table = substr($coded_offset_table,3); + } + } + else $this->offsets = unpack($formats[$infos['offset_size']],$coded_offset_table); + + $this->uniqueTable = Array(); + $this->objectRefSize = $infos['object_ref_size']; + + $top = $this->readBinaryObjectAt($infos['top_object']+1); + $this->add($top); + } + + /** + * Read a binary plist stream + * @param resource $stream The stream to read + * @return void + * @throws IOException if read error occurs + */ + function readBinaryStream($stream) { + $str = stream_get_contents($stream); + $this->parseBinary($str); + } + + /** + * parse a binary plist string + * @param string $content The stream to read, defaults to {@link $this->content} + * @return void + * @throws IOException if read error occurs + */ + function parseBinary($content=NULL) { + if($content !== NULL) $this->content = $content; + $this->pos = 0; + + $this->parseBinaryString(); + } + + /** + * Read a binary plist file + * @param string $file The file to read + * @return void + * @throws IOException if read error occurs + */ + function readBinary($file) { + if(!($fd = fopen($file,"rb"))) throw new IOException("Could not open file {$file}!"); + $this->readBinaryStream($fd); + fclose($fd); + } + + /** + * calculate the bytes needed for a size integer value + * @param integer $int The integer value to calculate + * @return integer The number of bytes needed + */ + public static function bytesSizeInt($int) { + $nbytes = 0; + + if($int > 0xE) $nbytes += 2; // 2 size-bytes + if($int > 0xFF) $nbytes += 1; // 3 size-bytes + if($int > 0xFFFF) $nbytes += 2; // 5 size-bytes + + return $nbytes; + } + + /** + * Calculate the byte needed for a „normal” integer value + * @param integer $int The integer value + * @return integer The number of bytes needed + 1 (because of the „marker byte”) + */ + public static function bytesInt($int) { + $nbytes = 1; + + if($int > 0xFF) $nbytes += 1; // 2 byte integer + if($int > 0xFFFF) $nbytes += 2; // 4 byte integer + if($int > 0xFFFFFFFF) $nbytes += 4; // 8 byte integer + if($int < 0) $nbytes += 7; // 8 byte integer (since it is signed) + + return $nbytes + 1; // one „marker” byte + } + + /** + * „pack” a value (i.e. write the binary representation as big endian to a string) with the specified size + * @param integer $nbytes The number of bytes to pack + * @param integer $int the integer value to pack + * @return string The packed value as string + */ + public static function packItWithSize($nbytes,$int) { + $formats = Array("C", "n", "N", "N"); + $format = $formats[$nbytes-1]; + $ret = ''; + + if($nbytes == 3) return substr(pack($format, $int), -3); + return pack($format, $int); + } + + /** + * Calculate the bytes needed to save the number of objects + * @param integer $count_objects The number of objects + * @return integer The number of bytes + */ + public static function bytesNeeded($count_objects) { + $nbytes = 0; + + while($count_objects >= 1) { + $nbytes++; + $count_objects /= 256; + } + + return $nbytes; + } + + /** + * Code an integer to byte representation + * @param integer $int The integer value + * @return string The packed byte value + */ + public static function intBytes($int) { + $intbytes = ""; + + if($int > 0xFFFF) $intbytes = "\x12".pack("N", $int); // 4 byte integer + elseif($int > 0xFF) $intbytes = "\x11".pack("n", $int); // 2 byte integer + else $intbytes = "\x10".pack("C", $int); // 8 byte integer + + return $intbytes; + } + + /** + * Code an type byte, consisting of the type marker and the length of the type + * @param string $type The type byte value (i.e. "d" for dictionaries) + * @param integer $type_len The length of the type + * @return string The packed type byte value + */ + public static function typeBytes($type,$type_len) { + $optional_int = ""; + + if($type_len < 15) $type .= sprintf("%x", $type_len); + else { + $type .= "f"; + $optional_int = self::intBytes($type_len); + } + + return pack("H*", $type).$optional_int; + } + + /** + * Count number of objects and create a unique table for strings + * @param $value The value to count and unique + * @return void + */ + protected function uniqueAndCountValues($value) { + // no uniquing for other types than CFString and CFData + if($value instanceof CFNumber) { + $val = $value->getValue(); + if(intval($val) == $val && !is_float($val) && strpos($val,'.') === false) $this->intSize += self::bytesInt($val); + else $this->miscSize += 9; // 9 bytes (8 + marker byte) for real + $this->countObjects++; + return; + } + elseif($value instanceof CFDate) { + $this->miscSize += 9; // since date in plist is real, we need 9 byte (8 + marker byte) + $this->countObjects++; + return; + } + elseif($value instanceof CFBoolean) { + $this->countObjects++; + $this->miscSize += 1; + return; + } + elseif($value instanceof CFArray) { + $cnt = 0; + foreach($value as $v) { + ++$cnt; + $this->uniqueAndCountValues($v); + $this->objectRefs++; // each array member is a ref + } + + $this->countObjects++; + $this->intSize += self::bytesSizeInt($cnt); + $this->miscSize++; // marker byte for array + return; + } + elseif($value instanceof CFDictionary) { + $cnt = 0; + foreach($value as $k => $v) { + ++$cnt; + if(!isset($this->uniqueTable[$k])) { + $this->uniqueTable[$k] = 0; + $len = self::binaryStrlen($k); + $this->stringSize += $len + 1; + $this->intSize += self::bytesSizeInt(self::charsetStrlen($k,'UTF-8')); + } + + $this->objectRefs += 2; // both, key and value, are refs + $this->uniqueTable[$k]++; + $this->uniqueAndCountValues($v); + } + + $this->countObjects++; + $this->miscSize++; // marker byte for dict + $this->intSize += self::bytesSizeInt($cnt); + return; + } + elseif($value instanceOf CFData) { + $val = $value->getValue(); + $len = strlen($val); + $this->intSize += self::bytesSizeInt($len); + $this->miscSize += $len + 1; + $this->countObjects++; + return; + } + else $val = $value->getValue(); + + if(!isset($this->uniqueTable[$val])) { + $this->uniqueTable[$val] = 0; + $len = self::binaryStrlen($val); + $this->stringSize += $len + 1; + $this->intSize += self::bytesSizeInt(self::charsetStrlen($val,'UTF-8')); + } + $this->uniqueTable[$val]++; + } + + /** + * Convert CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray + * @return string The binary plist content + */ + public function toBinary() { + $this->uniqueTable = Array(); + $this->countObjects = 0; + $this->stringSize = 0; + $this->intSize = 0; + $this->miscSize = 0; + $this->objectRefs = 0; + + $this->writtenObjectCount = 0; + $this->objectTable = Array(); + $this->objectRefSize = 0; + + $this->offsets = Array(); + + $binary_str = "bplist00"; + $value = $this->getValue(true); + $this->uniqueAndCountValues($value); + + $this->countObjects += count($this->uniqueTable); + $this->objectRefSize = self::bytesNeeded($this->countObjects); + $file_size = $this->stringSize + $this->intSize + $this->miscSize + $this->objectRefs * $this->objectRefSize + 40; + $offset_size = self::bytesNeeded($file_size); + $table_offset = $file_size - 32; + + $this->objectTable = Array(); + $this->writtenObjectCount = 0; + $this->uniqueTable = Array(); // we needed it to calculate several values + $value->toBinary($this); + + $object_offset = 8; + $offsets = Array(); + + for($i=0;$iobjectTable);++$i) { + $binary_str .= $this->objectTable[$i]; + $offsets[$i] = $object_offset; + $object_offset += strlen($this->objectTable[$i]); + } + + for($i=0;$iobjectRefSize); + $binary_str .= pack("x4N", $this->countObjects); + $binary_str .= pack("x4N", 0); + $binary_str .= pack("x4N", $table_offset); + + return $binary_str; + } + + /** + * Counts the number of bytes the string will have when coded; utf-16be if non-ascii characters are present. + * @param string $val The string value + * @return integer The length of the coded string in bytes + */ + protected static function binaryStrlen($val) { + for($i=0;$i= 128) { + $val = self::convertCharset($val, 'UTF-8', 'UTF-16BE'); + return strlen($val); + } + } + + return strlen($val); + } + + /** + * Uniques and transforms a string value to binary format and adds it to the object table + * @param string $val The string value + * @return integer The position in the object table + */ + public function stringToBinary($val) { + $saved_object_count = -1; + + if(!isset($this->uniqueTable[$val])) { + $saved_object_count = $this->writtenObjectCount++; + $this->uniqueTable[$val] = $saved_object_count; + $utf16 = false; + + for($i=0;$i= 128) { + $utf16 = true; + break; + } + } + + if($utf16) { + $bdata = self::typeBytes("6", mb_strlen($val,'UTF-8')); // 6 is 0110, unicode string (utf16be) + $val = self::convertCharset($val, 'UTF-8', 'UTF-16BE'); + $this->objectTable[$saved_object_count] = $bdata.$val; + } + else { + $bdata = self::typeBytes("5", strlen($val)); // 5 is 0101 which is an ASCII string (seems to be ASCII encoded) + $this->objectTable[$saved_object_count] = $bdata.$val; + } + } + else $saved_object_count = $this->uniqueTable[$val]; + + return $saved_object_count; + } + + /** + * Codes an integer to binary format + * @param integer $value The integer value + * @return string the coded integer + */ + protected function intToBinary($value) { + $nbytes = 0; + if($value > 0xFF) $nbytes = 1; // 1 byte integer + if($value > 0xFFFF) $nbytes += 1; // 4 byte integer + if($value > 0xFFFFFFFF) $nbytes += 1; // 8 byte integer + if($value < 0) $nbytes = 3; // 8 byte integer, since signed + + $bdata = self::typeBytes("1", $nbytes); // 1 is 0001, type indicator for integer + $buff = ""; + + if($nbytes < 3) { + if($nbytes == 0) $fmt = "C"; + elseif($nbytes == 1) $fmt = "n"; + else $fmt = "N"; + + $buff = pack($fmt, $value); + } + else { + if(PHP_INT_SIZE > 4) { + // 64 bit signed integer; we need the higher and the lower 32 bit of the value + $high_word = $value >> 32; + $low_word = $value & 0xFFFFFFFF; + } + else { + // since PHP can only handle 32bit signed, we can only get 32bit signed values at this point - values above 0x7FFFFFFF are + // floats. So we ignore the existance of 64bit on non-64bit-machines + if($value < 0) $high_word = 0xFFFFFFFF; + else $high_word = 0; + $low_word = $value; + } + $buff = pack("N", $high_word).pack("N", $low_word); + } + + return $bdata.$buff; + } + + /** + * Codes a real value to binary format + * @param float $val The real value + * @return string The coded real + */ + protected function realToBinary($val) { + $bdata = self::typeBytes("2", 3); // 2 is 0010, type indicator for reals + return $bdata.strrev(pack("d", (float)$val)); + } + + /** + * Converts a numeric value to binary and adds it to the object table + * @param numeric $value The numeric value + * @return integer The position in the object table + */ + public function numToBinary($value) { + $saved_object_count = $this->writtenObjectCount++; + + $val = ""; + if(intval($value) == $value && !is_float($value) && strpos($value,'.') === false) $val = $this->intToBinary($value); + else $val = $this->realToBinary($value); + + $this->objectTable[$saved_object_count] = $val; + return $saved_object_count; + } + + /** + * Convert date value (apple format) to binary and adds it to the object table + * @param integer $value The date value + * @return integer The position of the coded value in the object table + */ + public function dateToBinary($val) { + $saved_object_count = $this->writtenObjectCount++; + + $hour = gmdate("H",$val); + $min = gmdate("i",$val); + $sec = gmdate("s",$val); + $mday = gmdate("j",$val); + $mon = gmdate("n",$val); + $year = gmdate("Y",$val); + + $val = gmmktime($hour,$min,$sec,$mon,$mday,$year) - CFDate::DATE_DIFF_APPLE_UNIX; // CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT + + $bdata = self::typeBytes("3", 3); // 3 is 0011, type indicator for date + $this->objectTable[$saved_object_count] = $bdata.strrev(pack("d", $val)); + + return $saved_object_count; + } + + /** + * Convert a bool value to binary and add it to the object table + * @param bool $val The boolean value + * @return integer The position in the object table + */ + public function boolToBinary($val) { + $saved_object_count = $this->writtenObjectCount++; + $this->objectTable[$saved_object_count] = $val ? "\x9" : "\x8"; // 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false + return $saved_object_count; + } + + /** + * Convert data value to binary format and add it to the object table + * @param string $val The data value + * @return integer The position in the object table + */ + public function dataToBinary($val) { + $saved_object_count = $this->writtenObjectCount++; + + $bdata = self::typeBytes("4", strlen($val)); // a is 1000, type indicator for data + $this->objectTable[$saved_object_count] = $bdata.$val; + + return $saved_object_count; + } + + /** + * Convert array to binary format and add it to the object table + * @param CFArray $val The array to convert + * @return integer The position in the object table + */ + public function arrayToBinary($val) { + $saved_object_count = $this->writtenObjectCount++; + + $bdata = self::typeBytes("a", count($val->getValue())); // a is 1010, type indicator for arrays + + foreach($val as $v) { + $bval = $v->toBinary($this); + $bdata .= self::packItWithSize($this->objectRefSize, $bval); + } + + $this->objectTable[$saved_object_count] = $bdata; + return $saved_object_count; + } + + /** + * Convert dictionary to binary format and add it to the object table + * @param CFDictionary $val The dict to convert + * @return integer The position in the object table + */ + public function dictToBinary($val) { + $saved_object_count = $this->writtenObjectCount++; + $bdata = self::typeBytes("d", count($val->getValue())); // d=1101, type indicator for dictionary + + foreach($val as $k => $v) { + $str = new CFString($k); + $key = $str->toBinary($this); + $bdata .= self::packItWithSize($this->objectRefSize, $key); + } + + foreach($val as $k => $v) { + $bval = $v->toBinary($this); + $bdata .= self::packItWithSize($this->objectRefSize, $bval); + } + + $this->objectTable[$saved_object_count] = $bdata; + return $saved_object_count; + } + +} + + +?> diff --git a/gulliver/thirdparty/restler/plistformat/CFPropertyList.php b/gulliver/thirdparty/restler/plistformat/CFPropertyList.php new file mode 100644 index 000000000..2f53a3b48 --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/CFPropertyList.php @@ -0,0 +1,586 @@ + + * @author Christian Kruse + * @package plist + * @version $Id$ + * @example example-read-01.php Read an XML PropertyList + * @example example-read-02.php Read a Binary PropertyList + * @example example-read-03.php Read a PropertyList without knowing the type + * @example example-create-01.php Using the CFPropertyList API + * @example example-create-02.php Using {@link CFTypeDetector} + * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} + * @example example-modify-01.php Read, modify and save a PropertyList + */ + +/** + * Require IOException, PListException, CFType and CFBinaryPropertyList + */ +require_once('IOException.php'); +require_once('PListException.php'); +require_once('CFType.php'); +require_once('CFBinaryPropertyList.php'); +require_once('CFTypeDetector.php'); + +/** + * Property List + * Interface for handling reading, editing and saving Property Lists as defined by Apple. + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @example example-read-01.php Read an XML PropertyList + * @example example-read-02.php Read a Binary PropertyList + * @example example-read-03.php Read a PropertyList without knowing the type + * @example example-create-01.php Using the CFPropertyList API + * @example example-create-02.php Using {@link CFTypeDetector} + * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} + * @example example-create-04.php Using and extended {@link CFTypeDetector} + */ +class CFPropertyList extends CFBinaryPropertyList implements Iterator { + /** + * Format constant for binary format + * @var integer + */ + const FORMAT_BINARY = 1; + + /** + * Format constant for xml format + * @var integer + */ + const FORMAT_XML = 2; + + /** + * Format constant for automatic format recognizing + * @var integer + */ + const FORMAT_AUTO = 0; + + /** + * Path of PropertyList + * @var string + */ + protected $file = null; + + /** + * Path of PropertyList + * @var integer + */ + protected $format = null; + + /** + * CFType nodes + * @var array + */ + protected $value = array(); + + /** + * Position of iterator {@link http://php.net/manual/en/class.iterator.php} + * @var integer + */ + protected $iteratorPosition = 0; + + /** + * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php} + * @var array + */ + protected $iteratorKeys = null; + + /** + * List of NodeNames to ClassNames for resolving plist-files + * @var array + */ + protected static $types = array( + 'string' => 'CFString', + 'real' => 'CFNumber', + 'integer' => 'CFNumber', + 'date' => 'CFDate', + 'true' => 'CFBoolean', + 'false' => 'CFBoolean', + 'data' => 'CFData', + 'array' => 'CFArray', + 'dict' => 'CFDictionary' + ); + + + /** + * Create new CFPropertyList. + * If a path to a PropertyList is specified, it is loaded automatically. + * @param string $file Path of PropertyList + * @param integer $format he format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link FORMAT_AUTO} + * @throws IOException if file could not be read by {@link load()} + * @uses $file for storing the current file, if specified + * @uses load() for loading the plist-file + */ + public function __construct($file=null,$format=self::FORMAT_AUTO) { + $this->file = $file; + $this->format = $format; + if($this->file) $this->load(); + } + + /** + * Load an XML PropertyList. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @return void + * @throws IOException if file could not be read + * @throws DOMException if XML-file could not be read properly + * @uses load() to actually load the file + */ + public function loadXML($file=null) { + $this->load($file,CFPropertyList::FORMAT_XML); + } + + /** + * Load an XML PropertyList. + * @param resource $stream A stream containing the xml document. + * @return void + * @throws IOException if stream could not be read + * @throws DOMException if XML-stream could not be read properly + */ + public function loadXMLStream($stream) { + if(($contents = stream_get_contents($stream)) === FALSE) throw IOException::notReadable(''); + $this->parse($content,CFPropertyList::FORMAT_XML); + } + + /** + * Load an binary PropertyList. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @return void + * @throws IOException if file could not be read + * @throws PListException if binary plist-file could not be read properly + * @uses load() to actually load the file + */ + public function loadBinary($file=null) { + $this->load($file,CFPropertyList::FORMAT_BINARY); + } + + /** + * Load an binary PropertyList. + * @param stream $stream Stream containing the PropertyList + * @return void + * @throws IOException if file could not be read + * @throws PListException if binary plist-file could not be read properly + * @uses parse() to actually load the file + */ + public function loadBinaryStream($stream) { + if(($contents = stream_get_contents($stream)) === FALSE) throw IOException::notReadable(''); + $this->parse($content,CFPropertyList::FORMAT_BINARY); + } + + /** + * Load a plist file. + * Load and import a plist file. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format} + * @return void + * @throws PListException if file format version is not 00 + * @throws IOException if file could not be read + * @throws DOMException if plist file could not be parsed properly + * @uses $file if argument $file was not specified + * @uses $value reset to empty array + * @uses import() for importing the values + */ + public function load($file=null,$format=null) { + $file = $file ? $file : $this->file; + $format = $format !== null ? $format : $this->format; + $this->value = array(); + + if(!is_readable($file)) throw IOException::notReadable($file); + + switch($format) { + case CFPropertyList::FORMAT_BINARY: + $this->readBinary($file); + break; + case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format + $fd = fopen($file,"rb"); + if(($magic_number = fread($fd,8)) === false) throw IOException::notReadable($file); + fclose($fd); + + $filetype = substr($magic_number,0,6); + $version = substr($magic_number,-2); + + if($filetype == "bplist") { + if($version != "00") throw new PListException("Wrong file format version! Expected 00, got $version!"); + $this->readBinary($file); + break; + } + // else: xml format, break not neccessary + case CFPropertyList::FORMAT_XML: + $doc = new DOMDocument(); + if(!$doc->load($file)) throw new DOMException(); + $this->import($doc->documentElement, $this); + break; + } + } + + /** + * Parse a plist string. + * Parse and import a plist string. + * @param string $str String containing the PropertyList, defaults to {@link $content} + * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format} + * @return void + * @throws PListException if file format version is not 00 + * @throws IOException if file could not be read + * @throws DOMException if plist file could not be parsed properly + * @uses $content if argument $str was not specified + * @uses $value reset to empty array + * @uses import() for importing the values + */ + public function parse($str=NULL,$format=NULL) { + $format = $format !== null ? $format : $this->format; + $str = $str !== null ? $str : $this->content; + $this->value = array(); + + switch($format) { + case CFPropertyList::FORMAT_BINARY: + $this->parseBinary($str); + break; + case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format + if(($magic_number = substr($str,0,8)) === false) throw IOException::notReadable(""); + + $filetype = substr($magic_number,0,6); + $version = substr($magic_number,-2); + + if($filetype == "bplist") { + if($version != "00") throw new PListException("Wrong file format version! Expected 00, got $version!"); + $this->parseBinary($str); + break; + } + // else: xml format, break not neccessary + case CFPropertyList::FORMAT_XML: + $doc = new DOMDocument(); + if(!$doc->loadXML($str)) throw new DOMException(); + $this->import($doc->documentElement, $this); + break; + } + } + + /** + * Convert a DOMNode into a CFType. + * @param DOMNode $node Node to import children of + * @param CFDictionary|CFArray|CFPropertyList $parent + * @return void + */ + protected function import(DOMNode $node, $parent) { + // abort if there are no children + if(!$node->childNodes->length) return; + + foreach($node->childNodes as $n) { + // skip if we can't handle the element + if(!isset(self::$types[$n->nodeName])) continue; + + $class = self::$types[$n->nodeName]; + $key = null; + + // find previous if possible + $ps = $n->previousSibling; + while($ps && $ps->nodeName == '#text' && $ps->previousSibling) $ps = $ps->previousSibling; + + // read if possible + if($ps && $ps->nodeName == 'key') $key = $ps->firstChild->nodeValue; + + switch($n->nodeName) { + case 'date': + $value = new $class(CFDate::dateValue($n->nodeValue)); + break; + case 'data': + $value = new $class($n->nodeValue,true); + break; + case 'string': + $value = new $class($n->nodeValue); + break; + + case 'real': + case 'integer': + $value = new $class($n->nodeName == 'real' ? floatval($n->nodeValue) : intval($n->nodeValue)); + break; + + case 'true': + case 'false': + $value = new $class($n->nodeName == 'true'); + break; + + case 'array': + case 'dict': + $value = new $class(); + $this->import($n, $value); + break; + } + + // Dictionaries need a key + if($parent instanceof CFDictionary) $parent->add($key, $value); + // others don't + else $parent->add($value); + } + } + + /** + * Convert CFPropertyList to XML and save to file. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @return void + * @throws IOException if file could not be read + * @uses $file if $file was not specified + */ + public function saveXML($file) { + $this->save($file,CFPropertyList::FORMAT_XML); + } + + /** + * Convert CFPropertyList to binary format (bplist00) and save to file. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @return void + * @throws IOException if file could not be read + * @uses $file if $file was not specified + */ + public function saveBinary($file) { + $this->save($file,CFPropertyList::FORMAT_BINARY); + } + + /** + * Convert CFPropertyList to XML or binary and save to file. + * @param string $file Path of PropertyList, defaults to {@link $file} + * @param string $format Format of PropertyList, defaults to {@link $format} + * @return void + * @throws IOException if file could not be read + * @throws PListException if evaluated $format is neither {@link FORMAT_XML} nor {@link FORMAL_BINARY} + * @uses $file if $file was not specified + * @uses $format if $format was not specified + */ + public function save($file=null,$format=null) { + $file = $file ? $file : $this->file; + $format = $format ? $format : $this->format; + + if( !in_array( $format, array( self::FORMAT_BINARY, self::FORMAT_XML ) ) ) + throw new PListException( "format {$format} is not supported, use CFPropertyList::FORMAT_BINARY or CFPropertyList::FORMAT_XML" ); + + if(!file_exists($file)) { + // dirname("file.xml") == "" and is treated as the current working directory + if(!is_writable(dirname($file))) throw IOException::notWritable($file); + } + else if(!is_writable($file)) throw IOException::notWritable($file); + + $content = $format == self::FORMAT_BINARY ? $this->toBinary() : $this->toXML(); + + $fh = fopen($file, 'wb'); + fwrite($fh,$content); + fclose($fh); + } + + /** + * Convert CFPropertyList to XML + * @param bool $formatted Print plist formatted (i.e. with newlines and whitespace indention) if true; defaults to false + * @return string The XML content + */ + public function toXML($formatted=false) { + $domimpl = new DOMImplementation(); + // + $dtd = $domimpl->createDocumentType('plist', '-//Apple Computer//DTD PLIST 1.0//EN', 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'); + $doc = $domimpl->createDocument(null, "plist", $dtd); + $doc->encoding = "UTF-8"; + + // format output + if($formatted) { + $doc->formatOutput = true; + $doc->preserveWhiteSpace = true; + } + + // get documentElement and set attribs + $plist = $doc->documentElement; + $plist->setAttribute('version', '1.0'); + + // add PropertyList's children + $plist->appendChild($this->getValue(true)->toXML($doc)); + + return $doc->saveXML(); + } + + + /************************************************************************************************ + * M A N I P U L A T I O N + ************************************************************************************************/ + + /** + * Add CFType to collection. + * @param CFType $value CFType to add to collection + * @return void + * @uses $value for adding $value + */ + public function add(CFType $value=null) { + // anything but CFType is null, null is an empty string - sad but true + if( !$value ) + $value = new CFString(); + + $this->value[] = $value; + } + + /** + * Get CFType from collection. + * @param integer $key Key of CFType to retrieve from collection + * @return CFType CFType found at $key, null else + * @uses $value for retrieving CFType of $key + */ + public function get($key) { + if(isset($this->value[$key])) return $this->value[$key]; + return null; + } + + /** + * Generic getter (magic) + * + * @param integer $key Key of CFType to retrieve from collection + * @return CFType CFType found at $key, null else + * @author Sean Coates + * @link http://php.net/oop5.overloading + */ + public function __get($key) { + return $this->get($key); + } + + /** + * Remove CFType from collection. + * @param integer $key Key of CFType to removes from collection + * @return CFType removed CFType, null else + * @uses $value for removing CFType of $key + */ + public function del($key) { + if(isset($this->value[$key])) { + $t = $this->value[$key]; + unset($this->value[$key]); + return $t; + } + + return null; + } + + /** + * Empty the collection + * @return array the removed CFTypes + * @uses $value for removing CFType of $key + */ + public function purge() { + $t = $this->value; + $this->value = array(); + return $t; + } + + /** + * Get first (and only) child, or complete collection. + * @param string $cftype if set to true returned value will be CFArray instead of an array in case of a collection + * @return CFType|array CFType or list of CFTypes known to the PropertyList + * @uses $value for retrieving CFTypes + */ + public function getValue($cftype=false) { + if(count($this->value) === 1) { + $t = array_values( $this->value ); + return $t[0]; + } + if($cftype) { + $t = new CFArray(); + foreach( $this->value as $value ) { + if( $value instanceof CFType ) $t->add($value); + } + return $t; + } + return $this->value; + } + + /** + * Create CFType-structure from guessing the data-types. + * The functionality has been moved to the more flexible {@link CFTypeDetector} facility. + * @param mixed $value Value to convert to CFType + * @param boolean $autoDictionary if true {@link CFArray}-detection is bypassed and arrays will be returned as {@link CFDictionary}. + * @return CFType CFType based on guessed type + * @uses CFTypeDetector for actual type detection + * @deprecated + */ + public static function guess($value, $autoDictionary=false) { + static $t = null; + if( $t === null ) + $t = new CFTypeDetector( $autoDictionary ); + + return $t->toCFType( $value ); + } + + + /************************************************************************************************ + * S E R I A L I Z I N G + ************************************************************************************************/ + + /** + * Get PropertyList as array. + * @return mixed primitive value of first (and only) CFType, or array of primitive values of collection + * @uses $value for retrieving CFTypes + */ + public function toArray() { + $a = array(); + foreach($this->value as $value) $a[] = $value->toArray(); + if(count($a) === 1) return $a[0]; + + return $a; + } + + + /************************************************************************************************ + * I T E R A T O R I N T E R F A C E + ************************************************************************************************/ + + /** + * Rewind {@link $iteratorPosition} to first position (being 0) + * @link http://php.net/manual/en/iterator.rewind.php + * @return void + * @uses $iteratorPosition set to 0 + * @uses $iteratorKeys store keys of {@link $value} + */ + public function rewind() { + $this->iteratorPosition = 0; + $this->iteratorKeys = array_keys($this->value); + } + + /** + * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.current.php + * @return CFType current Item + * @uses $iteratorPosition identify current key + * @uses $iteratorKeys identify current value + */ + public function current() { + return $this->value[$this->iteratorKeys[$this->iteratorPosition]]; + } + + /** + * Get Iterator's current key identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.key.php + * @return string key of the current Item + * @uses $iteratorPosition identify current key + * @uses $iteratorKeys identify current value + */ + public function key() { + return $this->iteratorKeys[$this->iteratorPosition]; + } + + /** + * Increment {@link $iteratorPosition} to address next {@see CFType} + * @link http://php.net/manual/en/iterator.next.php + * @return void + * @uses $iteratorPosition increment by 1 + */ + public function next() { + $this->iteratorPosition++; + } + + /** + * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} + * @link http://php.net/manual/en/iterator.valid.php + * @return boolean true if current position is valid, false else + * @uses $iteratorPosition test if within {@link $iteratorKeys} + * @uses $iteratorPosition test if within {@link $value} + */ + public function valid() { + return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]); + } + +} + + +?> \ No newline at end of file diff --git a/gulliver/thirdparty/restler/plistformat/CFType.php b/gulliver/thirdparty/restler/plistformat/CFType.php new file mode 100644 index 000000000..246c22177 --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/CFType.php @@ -0,0 +1,742 @@ + + * @author Christian Kruse + * @package plist + * @subpackage plist.types + * @version $Id$ + */ + +/** + * Base-Class of all CFTypes used by CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + * @version $Id$ + * @example example-create-01.php Using the CFPropertyList API + * @example example-create-02.php Using CFPropertyList::guess() + * @example example-create-03.php Using CFPropertyList::guess() with {@link CFDate} and {@link CFData} + */ +abstract class CFType { + /** + * CFType nodes + * @var array + */ + protected $value = null; + + /** + * Create new CFType. + * @param mixed $value Value of CFType + */ + public function __construct($value=null) { + $this->setValue($value); + } + + /************************************************************************************************ + * M A G I C P R O P E R T I E S + ************************************************************************************************/ + + /** + * Get the CFType's value + * @return mixed CFType's value + */ + public function getValue() { + return $this->value; + } + + /** + * Set the CFType's value + * @return void + */ + public function setValue($value) { + $this->value = $value; + } + + /************************************************************************************************ + * S E R I A L I Z I N G + ************************************************************************************************/ + + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName Name of element to create + * @return DOMNode Node created based on CType + * @uses $value as nodeValue + */ + public function toXML(DOMDocument $doc, $nodeName) { + $text = $doc->createTextNode($this->value); + $node = $doc->createElement($nodeName); + $node->appendChild($text); + return $node; + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public abstract function toBinary(CFBinaryPropertyList &$bplist); + + /** + * Get CFType's value. + * @return mixed primitive value + * @uses $value for retrieving primitive of CFType + */ + public function toArray() { + return $this->getValue(); + } + +} + +/** + * String Type of CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFString extends CFType { + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <string>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + return parent::toXML($doc, 'string'); + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->stringToBinary($this->value); + } +} + +/** + * Number Type of CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFNumber extends CFType { + /** + * Get XML-Node. + * Returns <real> if $value is a float, <integer> if $value is an integer. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <real> or <integer>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + $ret = 'real'; + if(intval($this->value) == $this->value && !is_float($this->value) && strpos($this->value,'.') === false) { + $this->value = intval($this->value); + $ret = 'integer'; + } + return parent::toXML($doc, $ret); + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->numToBinary($this->value); + } +} + +/** + * Date Type of CFPropertyList + * Note: CFDate uses Unix timestamp (epoch) to store dates internally + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFDate extends CFType { + const TIMESTAMP_APPLE = 0; + const TIMESTAMP_UNIX = 1; + const DATE_DIFF_APPLE_UNIX = 978307200; + + /** + * Create new Date CFType. + * @param integer $value timestamp to set + * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_APPLE} + * @uses setValue() to convert the timestamp + */ + function __construct($value,$format=CFDate::TIMESTAMP_UNIX) { + $this->setValue($value,$format); + } + + /** + * Set the Date CFType's value. + * @param integer $value timestamp to set + * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX} + * @return void + * @uses TIMESTAMP_APPLE to determine timestamp type + * @uses TIMESTAMP_UNIX to determine timestamp type + * @uses DATE_DIFF_APPLE_UNIX to convert Apple-timestamp to Unix-timestamp + */ + function setValue($value,$format=CFDate::TIMESTAMP_UNIX) { + if($format == CFDate::TIMESTAMP_UNIX) $this->value = $value; + else $this->value = $value + CFDate::DATE_DIFF_APPLE_UNIX; + } + + /** + * Get the Date CFType's value. + * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX} + * @return integer Unix timestamp + * @uses TIMESTAMP_APPLE to determine timestamp type + * @uses TIMESTAMP_UNIX to determine timestamp type + * @uses DATE_DIFF_APPLE_UNIX to convert Unix-timestamp to Apple-timestamp + */ + function getValue($format=CFDate::TIMESTAMP_UNIX) { + if($format == CFDate::TIMESTAMP_UNIX) return $this->value; + else return $this->value - CFDate::DATE_DIFF_APPLE_UNIX; + } + + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <date>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + $text = $doc->createTextNode(gmdate("Y-m-d\TH:i:s\Z",$this->getValue())); + $node = $doc->createElement("date"); + $node->appendChild($text); + return $node; + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->dateToBinary($this->value); + } + + /** + * Create a UNIX timestamp from a PList date string + * @param string $val The date string (e.g. "2009-05-13T20:23:43Z") + * @return integer The UNIX timestamp + * @throws PListException when encountering an unknown date string format + */ + public static function dateValue($val) { + //2009-05-13T20:23:43Z + if(!preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/',$val,$matches)) throw new PListException("Unknown date format: $val"); + return gmmktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]); + } +} + +/** + * Boolean Type of CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFBoolean extends CFType { + /** + * Get XML-Node. + * Returns <true> if $value is a true, <false> if $value is false. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <true> or <false>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + return $doc->createElement($this->value ? 'true' : 'false'); + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->boolToBinary($this->value); + } + +} + +/** + * Data Type of CFPropertyList + * Note: Binary data is base64-encoded. + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFData extends CFType { + /** + * Create new Data CFType + * @param string $value data to be contained by new object + * @param boolean $already_coded if true $value will not be base64-encoded, defaults to false + */ + public function __construct($value=null,$already_coded=false) { + if($already_coded) $this->value = $value; + else $this->setValue($value); + } + + /** + * Set the CFType's value and base64-encode it. + * Note: looks like base64_encode has troubles with UTF-8 encoded strings + * @return void + */ + public function setValue($value) { + //if(function_exists('mb_check_encoding') && mb_check_encoding($value, 'UTF-8')) $value = utf8_decode($value); + $this->value = base64_encode($value); + } + + /** + * Get base64 encoded data + * @return string The base64 encoded data value + */ + public function getCodedValue() { + return $this->value; + } + + /** + * Get the base64-decoded CFType's value. + * @return mixed CFType's value + */ + public function getValue() { + return base64_decode($this->value); + } + + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <data>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + return parent::toXML($doc, 'data'); + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->dataToBinary($this->getValue()); + } +} + +/** + * Array Type of CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFArray extends CFType implements Iterator, ArrayAccess { + /** + * Position of iterator {@link http://php.net/manual/en/class.iterator.php} + * @var integer + */ + protected $iteratorPosition = 0; + + + /** + * Create new CFType. + * @param array $value Value of CFType + */ + public function __construct($value=array()) { + $this->value = $value; + } + + /** + * Set the CFType's value + * Note: this dummy does nothing + * @return void + */ + public function setValue($value) { + } + + /** + * Add CFType to collection. + * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString} + * @return void + * @uses $value for adding $value + */ + public function add(CFType $value=null) { + // anything but CFType is null, null is an empty string - sad but true + if( !$value ) + $value = new CFString(); + + $this->value[] = $value; + } + + /** + * Get CFType from collection. + * @param integer $key Key of CFType to retrieve from collection + * @return CFType CFType found at $key, null else + * @uses $value for retrieving CFType of $key + */ + public function get($key) { + if(isset($this->value[$key])) return $this->value[$key]; + return null; + } + + /** + * Remove CFType from collection. + * @param integer $key Key of CFType to removes from collection + * @return CFType removed CFType, null else + * @uses $value for removing CFType of $key + */ + public function del($key) { + if(isset($this->value[$key])) unset($this->value[$key]); + } + + + /************************************************************************************************ + * S E R I A L I Z I N G + ************************************************************************************************/ + + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <array>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + $node = $doc->createElement('array'); + + foreach($this->value as $value) $node->appendChild($value->toXML($doc)); + return $node; + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->arrayToBinary($this); + } + + /** + * Get CFType's value. + * @return array primitive value + * @uses $value for retrieving primitive of CFType + */ + public function toArray() { + $a = array(); + foreach($this->value as $value) $a[] = $value->toArray(); + return $a; + } + + + /************************************************************************************************ + * I T E R A T O R I N T E R F A C E + ************************************************************************************************/ + + /** + * Rewind {@link $iteratorPosition} to first position (being 0) + * @link http://php.net/manual/en/iterator.rewind.php + * @return void + * @uses $iteratorPosition set to 0 + */ + public function rewind() { + $this->iteratorPosition = 0; + } + + /** + * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.current.php + * @return CFType current Item + * @uses $iteratorPosition identify current key + */ + public function current() { + return $this->value[$this->iteratorPosition]; + } + + /** + * Get Iterator's current key identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.key.php + * @return string key of the current Item + * @uses $iteratorPosition identify current key + */ + public function key() { + return $this->iteratorPosition; + } + + /** + * Increment {@link $iteratorPosition} to address next {@see CFType} + * @link http://php.net/manual/en/iterator.next.php + * @return void + * @uses $iteratorPosition increment by 1 + */ + public function next() { + $this->iteratorPosition++; + } + + /** + * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} + * @link http://php.net/manual/en/iterator.valid.php + * @return boolean true if current position is valid, false else + * @uses $iteratorPosition test if within {@link $iteratorKeys} + * @uses $iteratorPosition test if within {@link $value} + */ + public function valid() { + return isset($this->value[$this->iteratorPosition]); + } + + /************************************************************************************************ + * ArrayAccess I N T E R F A C E + ************************************************************************************************/ + + /** + * Determine if the array's key exists + * @param string $key the key to check + * @return bool true if the offset exists, false if not + * @link http://php.net/manual/en/arrayaccess.offsetexists.php + * @uses $value to check if $key exists + * @author Sean Coates + */ + public function offsetExists($key) { + return isset($this->value[$key]); + } + + /** + * Fetch a specific key from the CFArray + * @param string $key the key to check + * @return mixed the value associated with the key; null if the key is not found + * @link http://php.net/manual/en/arrayaccess.offsetget.php + * @uses get() to get the key's value + * @author Sean Coates + */ + public function offsetGet($key) { + return $this->get($key); + } + + /** + * Set a value in the array + * @param string $key the key to set + * @param string $value the value to set + * @return void + * @link http://php.net/manual/en/arrayaccess.offsetset.php + * @uses setValue() to set the key's new value + * @author Sean Coates + */ + public function offsetSet($key, $value) { + return $this->setValue($value); + } + + /** + * Unsets a value in the array + * Note: this dummy does nothing + * @param string $key the key to set + * @return void + * @link http://php.net/manual/en/arrayaccess.offsetunset.php + * @author Sean Coates + */ + public function offsetUnset($key) { + + } + + +} + +/** + * Array Type of CFPropertyList + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + * @subpackage plist.types + */ +class CFDictionary extends CFType implements Iterator { + /** + * Position of iterator {@link http://php.net/manual/en/class.iterator.php} + * @var integer + */ + protected $iteratorPosition = 0; + + /** + * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php} + * @var array + */ + protected $iteratorKeys = null; + + + /** + * Create new CFType. + * @param array $value Value of CFType + */ + public function __construct($value=array()) { + $this->value = $value; + } + + /** + * Set the CFType's value + * Note: this dummy does nothing + * @return void + */ + public function setValue($value) { + } + + /** + * Add CFType to collection. + * @param string $key Key to add to collection + * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString} + * @return void + * @uses $value for adding $key $value pair + */ + public function add($key, CFType $value=null) { + // anything but CFType is null, null is an empty string - sad but true + if( !$value ) + $value = new CFString(); + + $this->value[$key] = $value; + } + + /** + * Get CFType from collection. + * @param string $key Key of CFType to retrieve from collection + * @return CFType CFType found at $key, null else + * @uses $value for retrieving CFType of $key + */ + public function get($key) { + if(isset($this->value[$key])) return $this->value[$key]; + return null; + } + + /** + * Generic getter (magic) + * @param integer $key Key of CFType to retrieve from collection + * @return CFType CFType found at $key, null else + * @link http://php.net/oop5.overloading + * @uses get() to retrieve the key's value + * @author Sean Coates + */ + public function __get($key) { + return $this->get($key); + } + + /** + * Remove CFType from collection. + * @param string $key Key of CFType to removes from collection + * @return CFType removed CFType, null else + * @uses $value for removing CFType of $key + */ + public function del($key) { + if(isset($this->value[$key])) unset($this->value[$key]); + } + + + /************************************************************************************************ + * S E R I A L I Z I N G + ************************************************************************************************/ + + /** + * Get XML-Node. + * @param DOMDocument $doc DOMDocument to create DOMNode in + * @param string $nodeName For compatibility reasons; just ignore it + * @return DOMNode <dict>-Element + */ + public function toXML(DOMDocument $doc,$nodeName="") { + $node = $doc->createElement('dict'); + + foreach($this->value as $key => $value) { + $node->appendChild($doc->createElement('key', $key)); + $node->appendChild($value->toXML($doc)); + } + + return $node; + } + + /** + * convert value to binary representation + * @param CFBinaryPropertyList The binary property list object + * @return The offset in the object table + */ + public function toBinary(CFBinaryPropertyList &$bplist) { + return $bplist->dictToBinary($this); + } + + /** + * Get CFType's value. + * @return array primitive value + * @uses $value for retrieving primitive of CFType + */ + public function toArray() { + $a = array(); + + foreach($this->value as $key => $value) $a[$key] = $value->toArray(); + return $a; + } + + + /************************************************************************************************ + * I T E R A T O R I N T E R F A C E + ************************************************************************************************/ + + /** + * Rewind {@link $iteratorPosition} to first position (being 0) + * @link http://php.net/manual/en/iterator.rewind.php + * @return void + * @uses $iteratorPosition set to 0 + * @uses $iteratorKeys store keys of {@link $value} + */ + public function rewind() { + $this->iteratorPosition = 0; + $this->iteratorKeys = array_keys($this->value); + } + + /** + * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.current.php + * @return CFType current Item + * @uses $iteratorPosition identify current key + * @uses $iteratorKeys identify current value + */ + public function current() { + return $this->value[$this->iteratorKeys[$this->iteratorPosition]]; + } + + /** + * Get Iterator's current key identified by {@link $iteratorPosition} + * @link http://php.net/manual/en/iterator.key.php + * @return string key of the current Item + * @uses $iteratorPosition identify current key + * @uses $iteratorKeys identify current value + */ + public function key() { + return $this->iteratorKeys[$this->iteratorPosition]; + } + + /** + * Increment {@link $iteratorPosition} to address next {@see CFType} + * @link http://php.net/manual/en/iterator.next.php + * @return void + * @uses $iteratorPosition increment by 1 + */ + public function next() { + $this->iteratorPosition++; + } + + /** + * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} + * @link http://php.net/manual/en/iterator.valid.php + * @return boolean true if current position is valid, false else + * @uses $iteratorPosition test if within {@link $iteratorKeys} + * @uses $iteratorPosition test if within {@link $value} + */ + public function valid() { + return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]); + } + +} + +?> \ No newline at end of file diff --git a/gulliver/thirdparty/restler/plistformat/CFTypeDetector.php b/gulliver/thirdparty/restler/plistformat/CFTypeDetector.php new file mode 100644 index 000000000..996439326 --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/CFTypeDetector.php @@ -0,0 +1,167 @@ + + * @author Christian Kruse + * @package plist + * @subpackage plist.types + * @example example-create-02.php Using {@link CFTypeDetector} + * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} + * @example example-create-04.php Using and extended {@link CFTypeDetector} + */ +class CFTypeDetector { + + /** + * flag stating if all arrays should automatically be converted to {@link CFDictionary} + * @var boolean + */ + protected $autoDictionary = false; + + /** + * flag stating if exceptions should be suppressed or thrown + * @var boolean + */ + protected $suppressExceptions = false; + + /** + * name of a method that will be used for array to object conversations + * @var boolean + */ + protected $objectToArrayMethod = false; + + + /** + * Create new CFTypeDetector + * @param boolean $autoDicitionary if set to true all arrays will be converted to {@link CFDictionary} + * @param boolean $suppressExceptions if set to true toCFType() will not throw any exceptions + * @param boolean $objectToArrayMethod if non-null, this method will be called on objects (if possible) to convert the object to an array + */ + public function __construct($autoDicitionary=false,$suppressExceptions=false,$objectToArrayMethod=null) { + $this->autoDicitionary = $autoDicitionary; + $this->suppressExceptions = $suppressExceptions; + $this->objectToArrayMethod = $objectToArrayMethod; + } + + /** + * Determine if an array is associative or numerical. + * Numerical Arrays have incrementing index-numbers that don't contain gaps. + * @param array $value Array to check indexes of + * @return boolean true if array is associative, false if array has numeric indexes + */ + protected function isAssociativeArray($value) { + $numericKeys = true; + $previousKey = null; + foreach($value as $key => $v) { + if(!is_numeric($key) || ($previousKey !== null && $previousKey != $key-1)) { + $numericKeys = false; + break; + } + + $previousKey = $key; + } + return !$numericKeys; + } + + /** + * Get the default value + * @return CFType the default value to return if no suitable type could be determined + */ + protected function defaultValue() { + return new CFString(); + } + + /** + * Create CFType-structure by guessing the data-types. + * {@link CFArray}, {@link CFDictionary}, {@link CFBoolean}, {@link CFNumber} and {@link CFString} can be created, {@link CFDate} and {@link CFData} cannot. + *
Note:Distinguishing between {@link CFArray} and {@link CFDictionary} is done by examining the keys. + * Keys must be strictly incrementing integers to evaluate to a {@link CFArray}. + * Since PHP does not offer a function to test for associative arrays, + * this test causes the input array to be walked twice and thus work rather slow on large collections. + * If you work with large arrays and can live with all arrays evaluating to {@link CFDictionary}, + * feel free to set the appropriate flag. + *
Note: If $value is an instance of CFType it is simply returned. + *
Note: If $value is neither a CFType, array, numeric, boolean nor string, it is omitted. + * @param mixed $value Value to convert to CFType + * @param boolean $autoDictionary if true {@link CFArray}-detection is bypassed and arrays will be returned as {@link CFDictionary}. + * @return CFType CFType based on guessed type + * @uses isAssociativeArray() to check if an array only has numeric indexes + */ + public function toCFType($value) { + switch(true) { + case $value instanceof CFType: + return $value; + break; + + case is_object($value): + // DateTime should be CFDate + if(class_exists( 'DateTime' ) && $value instanceof DateTime){ + return new CFDate($value->getTimestamp()); + } + + // convert possible objects to arrays, arrays will be arrays + if($this->objectToArrayMethod && is_callable(array($value, $this->objectToArrayMethod))){ + $value = call_user_func( array( $value, $this->objectToArrayMethod ) ); + } + + if(!is_array($value)){ + if($this->suppressExceptions) + return $this->defaultValue(); + + throw new PListException('Could not determine CFType for object of type '. get_class($value)); + } + /* break; omitted */ + + case $value instanceof Iterator: + case is_array($value): + // test if $value is simple or associative array + if(!$this->autoDictionary) { + if(!$this->isAssociativeArray($value)) { + $t = new CFArray(); + foreach($value as $v) $t->add($this->toCFType($v)); + return $t; + } + } + + $t = new CFDictionary(); + foreach($value as $k => $v) $t->add($k, $this->toCFType($v)); + + return $t; + break; + + case is_numeric($value): + return new CFNumber($value); + break; + + case is_bool($value): + return new CFBoolean($value); + break; + + case is_string($value): + return new CFString($value); + break; + + case is_null($value): + return new CFString(); + break; + + case is_resource($value): + if( $this->suppressExceptions ) + return $this->defaultValue(); + + throw new PListException('Could not determine CFType for resource of type '. get_resource_type($value)); + break; + + default: + if( $this->suppressExceptions ) + return $this->defaultValue(); + + throw new PListException('Could not determine CFType for '. gettype($value)); + break; + } + } + +} + +?> \ No newline at end of file diff --git a/gulliver/thirdparty/restler/plistformat/IOException.php b/gulliver/thirdparty/restler/plistformat/IOException.php new file mode 100644 index 000000000..991855394 --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/IOException.php @@ -0,0 +1,99 @@ + + * @author Christian Kruse + * @package plist + * @version $Id$ + */ + +/** + * Basic Input / Output Exception + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + */ +class IOException extends Exception { + /** + * Flag telling the File could not be found + */ + const NOT_FOUND = 1; + + /** + * Flag telling the File is not readable + */ + const NOT_READABLE = 2; + + /** + * Flag telling the File is not writable + */ + const NOT_WRITABLE = 3; + + /** + * Flag telling there was a read error + */ + const READ_ERROR = 4; + + /** + * Flag telling there was a read error + */ + const WRITE_ERROR = 5; + + /** + * Create new IOException + * @param string $path Source of the problem + * @param integer $type Type of the problem + */ + public function __construct($path, $type=null) { + parent::__construct( $path, $type ); + } + + /** + * Create new FileNotFound-Exception + * @param string $path Source of the problem + * @return IOException new FileNotFound-Exception + */ + public static function notFound($path) { + return new IOException( $path, self::NOT_FOUND ); + } + + /** + * Create new FileNotReadable-Exception + * @param string $path Source of the problem + * @return IOException new FileNotReadable-Exception + */ + public static function notReadable($path) { + return new IOException( $path, self::NOT_READABLE ); + } + + /** + * Create new FileNotWritable-Exception + * @param string $path Source of the problem + * @return IOException new FileNotWritable-Exception + */ + public static function notWritable($path) { + return new IOException( $path, self::NOT_WRITABLE ); + } + + /** + * Create new ReadError-Exception + * @param string $path Source of the problem + * @return IOException new ReadError-Exception + */ + public static function readError($path) { + return new IOException( $path, self::READ_ERROR ); + } + + /** + * Create new WriteError-Exception + * @param string $path Source of the problem + * @return IOException new WriteError-Exception + */ + public static function writeError($path) { + return new IOException( $path, self::WRITE_ERROR ); + } +} + + +?> \ No newline at end of file diff --git a/gulliver/thirdparty/restler/plistformat/PListException.php b/gulliver/thirdparty/restler/plistformat/PListException.php new file mode 100644 index 000000000..b738d2382 --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/PListException.php @@ -0,0 +1,22 @@ + + * @author Christian Kruse + * @package plist + * @version $Id$ + */ + +/** + * Exception for errors with the PList format + * @author Rodney Rehm + * @author Christian Kruse + * @package plist + */ +class PListException extends Exception { + +} + + +?> \ No newline at end of file diff --git a/gulliver/thirdparty/restler/plistformat/plistformat.php b/gulliver/thirdparty/restler/plistformat/plistformat.php new file mode 100644 index 000000000..a68c9e78d --- /dev/null +++ b/gulliver/thirdparty/restler/plistformat/plistformat.php @@ -0,0 +1,86 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class PlistFormat implements iFormat +{ + public static $binary_mode = false; + + const MIME_BINARY = 'application/x-plist'; + const MIME_XML = 'application/xml'; + + const EXTENSION_BINARY = 'bplist'; + const EXTENSION_XML = 'plist'; + + public function getMIMEMap(){ + return array( + PlistFormat::EXTENSION_BINARY=>PlistFormat::MIME_BINARY, + PlistFormat::EXTENSION_XML=>PlistFormat::MIME_XML + ); + } + + public function getMIME(){ + return PlistFormat::$binary_mode ? PlistFormat::MIME_BINARY : PlistFormat::MIME_XML; + } + public function getExtension(){ + return PlistFormat::$binary_mode ? PlistFormat::EXTENSION_BINARY : PlistFormat::EXTENSION_XML; + } + public function setMIME($mime){ + PlistFormat::$binary_mode = $mime==PlistFormat::MIME_BINARY; + } + public function setExtension($extension){ + PlistFormat::$binary_mode = $extension==PlistFormat::EXTENSION_BINARY; + } + + /** + * Encode the given data in plist format + * @param array $data resulting data that needs to + * be encoded in plist format + * @param boolean $human_readable set to true when restler + * is not running in production mode. Formatter has to + * make the encoded output more human readable + * @return string encoded string + */ + public function encode($data, $human_readable=false){ + require_once'CFPropertyList.php'; + if(!PlistFormat::$binary_mode) { + PlistFormat::$binary_mode = !$human_readable; + } else { + $human_readable=false; + } + /** + * @var CFPropertyList + */ + $plist = new CFPropertyList(); + $td = new CFTypeDetector(); + $guessedStructure = $td->toCFType(object_to_array($data)); + $plist->add( $guessedStructure ); + return $human_readable ? $plist->toXML(true) : $plist->toBinary(); + } + + /** + * Decode the given data from plist format + * @param string $data data sent from client to + * the api in the given format. + * @return array associative array of the parsed data + */ + public function decode($data){ + require_once'CFPropertyList.php'; + $plist = new CFPropertyList(); + $plist->parse($data); + return $plist->toArray(); + } + public function __toString(){ + return $this->getExtension(); + } +} \ No newline at end of file diff --git a/gulliver/thirdparty/restler/restler.php b/gulliver/thirdparty/restler/restler.php new file mode 100644 index 000000000..170dd695b --- /dev/null +++ b/gulliver/thirdparty/restler/restler.php @@ -0,0 +1,1454 @@ + + * + * @category Framework + * @package restler + * @author Jac Wright + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + * @version 2.2.0 + */ +class Restler +{ + // ================================================================== + // + // Public variables + // + // ------------------------------------------------------------------ + + const VERSION = '2.2.0'; + + /** + * URL of the currently mapped service + * @var string + */ + public $url; + + /** + * Http request method of the current request. + * Any value between [GET, PUT, POST, DELETE] + * @var string + */ + public $request_method; + + /** + * Requested data format. Instance of the current format class + * which implements the iFormat interface + * @var iFormat + * @example jsonFormat, xmlFormat, yamlFormat etc + */ + public $request_format; + + /** + * Data sent to the service + * @var array + */ + public $request_data = array(); + + /** + * Used in production mode to store the URL Map to disk + * @var string + */ + public $cache_dir; + + /** + * base directory to locate format and auth files + * @var string + */ + public $base_dir; + + /** + * Name of an iRespond implementation class + * @var string + */ + public $response = 'DefaultResponse'; + + /** + * Response data format. Instance of the current format class + * which implements the iFormat interface + * @var iFormat + * @example jsonFormat, xmlFormat, yamlFormat etc + */ + public $response_format; + + // ================================================================== + // + // Private & Protected variables + // + // ------------------------------------------------------------------ + + /** + * When set to FALSE, it will run in debug mode and parse the + * class files every time to map it to the URL + * @var boolean + */ + protected $production_mode; + + /** + * Associated array that maps urls to their respective class and method + * @var array + */ + protected $routes = array(); + + /** + * Associated array that maps formats to their respective format class name + * @var array + */ + protected $format_map = array(); + + /** + * Instance of the current api service class + * @var object + */ + protected $service_class_instance; + + /** + * Name of the api method being called + * @var string + */ + protected $service_method; + + /** + * list of authentication classes + * @var array + */ + protected $auth_classes = array(); + + /** + * list of error handling classes + * @var array + */ + protected $error_classes = array(); + + /** + * HTTP status codes + * @var array + */ + private $codes = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => '(Unused)', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + /** + * Caching of url map is enabled or not + * @var boolean + */ + protected $cached; + + // ================================================================== + // + // Public functions + // + // ------------------------------------------------------------------ + + /** + * Constructor + * @param boolean $production_mode When set to FALSE, it will run in + * debug mode and parse the class files every time to map it to the URL + */ + public function __construct($production_mode = FALSE) + { + $this->production_mode = $production_mode; + $this->cache_dir = getcwd(); + $this->base_dir = RESTLER_PATH; + } + + /** + * Store the url map cache if needed + */ + public function __destruct() + { + if ($this->production_mode && !($this->cached)) { + $this->saveCache(); + } + } + + /** + * Use it in production mode to refresh the url map cache + */ + public function refreshCache() + { + $this->routes = array(); + $this->cached = FALSE; + } + + /** + * Call this method and pass all the formats that should be + * supported by the API. Accepts multiple parameters + * @param string class name of the format class that implements iFormat + * @example $restler->setSupportedFormats('JsonFormat', 'XmlFormat'...); + */ + public function setSupportedFormats() + { + $args = func_get_args(); + $extensions = array(); + foreach ($args as $class_name) { + if (!is_string($class_name) || !class_exists($class_name)) { + throw new Exception("$class_name is not a vaild Format Class."); + } + $obj = new $class_name; + if (!($obj instanceof iFormat)) { + throw new Exception('Invalid format class; must implement ' + . 'iFormat interface'); + } + foreach ($obj->getMIMEMap() as $extension => $mime) { + if (!isset($this->format_map[$extension])) { + $this->format_map[$extension] = $class_name; + } + $mime = explode(',', $mime); + if (!is_array($mime)) { + $mime = array($mime); + } + foreach ($mime as $value) { + if (!isset($this->format_map[$value])) + $this->format_map[$value] = $class_name; + } + $extensions[".$extension"] = TRUE; + } + } + $this->format_map['default'] = $args[0]; + $this->format_map['extensions'] = array_keys($extensions); + } + + /** + * Add api classes throgh this method. All the public methods + * that do not start with _ (underscore) will be will be exposed + * as the public api by default. + * + * All the protected methods that do not start with _ (underscore) + * will exposed as protected api which will require authentication + * @param string $class name of the service class + * @param string $basePath optional url prefix for mapping, uses + * lowercase version of the class name when not specified + * @throws Exception when supplied with invalid class name + */ + public function addAPIClass($class_name, $base_path = NULL) + { + if (!class_exists($class_name)) { + throw new Exception("API class $class_name is missing."); + } + $this->loadCache(); + if (!$this->cached) { + if (is_null($base_path)) { + $base_path = strtolower($class_name); + $index = strrpos($class_name, '\\'); + if ($index !== FALSE) { + $base_path = substr($base_path, $index + 1); + } + } else { + $base_path = trim($base_path,'/'); + } + if (strlen($base_path) > 0) { + $base_path .= '/'; + } + $this->generateMap($class_name, $base_path); + } + } + + /** + * protected methods will need atleast one authentication class to be set + * in order to allow that method to be executed + * @param string $class_name of the authentication class + * @param string $base_path optional url prefix for mapping + */ + public function addAuthenticationClass($class_name, $base_path = NULL) + { + $this->auth_classes[] = $class_name; + $this->addAPIClass($class_name, $base_path); + } + + /** + * Add class for custom error handling + * @param string $class_name of the error handling class + */ + public function addErrorClass($class_name) + { + $this->error_classes[] = $class_name; + } + + /** + * Convenience method to respond with an error message + * @param int $statusCode http error code + * @param string $errorMessage optional custom error message + */ + public function handleError($status_code, $error_message = NULL) + { + $method = "handle$status_code"; + $handled = FALSE; + foreach ($this->error_classes as $class_name) { + if (method_exists($class_name, $method)) { + $obj = new $class_name(); + $obj->restler = $this; + $obj->$method(); + $handled = TRUE; + } + } + if ($handled) { + return; + } + $message = $this->codes[$status_code] + . (!$error_message ? '' : ': ' . $error_message); + $this->setStatus($status_code); + $responder = new $this->response(); + $responder->restler = $this; + $this->sendData($responder->__formatError($status_code, $message)); + } + + /** + * Main function for processing the api request + * and return the response + * @throws Exception when the api service class is missing + * @throws RestException to send error response + */ + public function handle() + { + if (empty($this->format_map)) { + $this->setSupportedFormats('JsonFormat'); + } + $this->url = $this->getPath(); + $this->request_method = $this->getRequestMethod(); + $this->response_format = $this->getResponseFormat(); + $this->request_format = $this->getRequestFormat(); + if (is_null($this->request_format)) { + $this->request_format = $this->response_format; + } + if ($this->request_method == 'PUT' || $this->request_method == 'POST') { + $this->request_data = $this->getRequestData(); + } + $o = $this->mapUrlToMethod(); + if (!isset($o->class_name)) { + $this->handleError(404); + } else { + try { + if ($o->method_flag) { + $auth_method = '__isAuthenticated'; + if (!count($this->auth_classes)) { + throw new RestException(401); + } + foreach ($this->auth_classes as $auth_class) { + $auth_obj = new $auth_class(); + $auth_obj->restler = $this; + $this->applyClassMetadata($auth_class, $auth_obj, $o); + if (!method_exists($auth_obj, $auth_method)) { + throw new RestException(401, 'Authentication Class ' + . 'should implement iAuthenticate'); + } else if (!$auth_obj->$auth_method()) { + throw new RestException(401); + } + } + } + $this->applyClassMetadata(get_class($this->request_format), + $this->request_format, $o); + $pre_process = '_' . $this->request_format->getExtension() . '_' + . $o->method_name; + $this->service_method = $o->method_name; + if ($o->method_flag == 2) { + $o = unprotect($o); + } + $object = $this->service_class_instance = new $o->class_name(); + $object->restler = $this; + if (method_exists($o->class_name, $pre_process)) { + call_user_func_array(array($object, $pre_process), + $o->arguments); + } + switch ($o->method_flag) { + case 3: + $reflection_method = new ReflectionMethod($object, + $o->method_name); + $reflection_method->setAccessible(TRUE); + $result = $reflection_method->invokeArgs($object, + $o->arguments); + break; + case 2: + case 1: + default: + $result = call_user_func_array(array( + $object, + $o->method_name), + $o->arguments + ); + break; + } + } catch (RestException $e) { + $this->handleError($e->getCode(), $e->getMessage()); + } + } + $responder = new $this->response(); + $responder->restler = $this; + $this->applyClassMetadata($this->response, $responder, $o); + if (isset($result) && $result !== NULL) { + $result = $responder->__formatResponse($result); + $this->sendData($result); + } + } + + /** + * Encodes the response in the prefered format + * and sends back + * @param $data array php data + */ + public function sendData($data) + { + $data = $this->response_format->encode($data, !($this->production_mode)); + $post_process = '_' . $this->service_method . '_' + . $this->response_format->getExtension(); + if (isset($this->service_class_instance) + && method_exists($this->service_class_instance,$post_process) + ) { + $data = call_user_func(array($this->service_class_instance, + $post_process), $data); + } + header("Cache-Control: no-cache, must-revalidate"); + header("Expires: 0"); + header('Content-Type: ' . $this->response_format->getMIME()); + //header('Content-Type: ' . $this->response_format->getMIME().'; charset=utf-8'); + header("X-Powered-By: Luracast Restler v" . Restler::VERSION); + die($data); + } + + /** + * Sets the HTTP response status + * @param int $code response code + */ + public function setStatus($code) + { + header("{$_SERVER['SERVER_PROTOCOL']} $code " . $this->codes[strval($code)]); + } + + /** + * Compare two strings and remove the common + * sub string from the first string and return it + * @param string $first + * @param string $second + * @param string $char optional, set it as + * blank string for char by char comparison + * @return string + */ + public function removeCommonPath($first, $second, $char = '/') + { + $first = explode($char, $first); + $second = explode($char, $second); + while (count($second)) { + if ($first[0] == $second[0]) { + array_shift($first); + } else { + break; + } + array_shift($second); + } + return implode($char, $first); + } + + /** + * Save cache to file + */ + public function saveCache() + { + $file = $this->cache_dir . '/routes.php'; + $s = '$o=array();' . PHP_EOL; + foreach ($this->routes as $key => $value) { + $s .= PHP_EOL . PHP_EOL . PHP_EOL . "############### $key ###############" + . PHP_EOL . PHP_EOL; + $s .= '$o[\'' . $key . '\']=array();'; + foreach ($value as $ke => $va) { + $s .= PHP_EOL . PHP_EOL . "#==== $key $ke" . PHP_EOL . PHP_EOL; + $s .= '$o[\'' . $key . '\'][\'' . $ke . '\']=' . str_replace( + PHP_EOL, + PHP_EOL . "\t", + var_export($va, TRUE) + ) . ';'; + } + } + $s .= PHP_EOL . 'return $o;'; + $r = @file_put_contents($file, "cache_dir' needs to have " + . "the permissions set to read/write/execute for everyone in order " + . "to save cache and improve performance."); + } + } + + // ================================================================== + // + // Protected functions + // + // ------------------------------------------------------------------ + + /** + * Parses the requst url and get the api path + * @return string api path + */ + protected function getPath() + { + $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], + $_SERVER['SCRIPT_NAME'])); + $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); + $path = str_replace($this->format_map['extensions'], '', $path); + return $path; + } + + /** + * Parses the request to figure out the http request type + * @return string which will be one of the following + * [GET, POST, PUT, DELETE] + * @example GET + */ + protected function getRequestMethod() + { + $method = $_SERVER['REQUEST_METHOD']; + if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { + $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; + } + //support for HEAD request + if ($method == 'HEAD') { + $method = 'GET'; + } + return $method; + } + + /** + * Parses the request to figure out format of the request data + * @return iFormat any class that implements iFormat + * @example JsonFormat + */ + protected function getRequestFormat() + { + $format = NULL; + //check if client has sent any information on request format + if (isset($_SERVER['CONTENT_TYPE'])) { + $mime = explode(';', $_SERVER['CONTENT_TYPE']); + $mime = $mime[0]; + if ($mime == UrlEncodedFormat::MIME) { + $format = new UrlEncodedFormat(); + } else { + if (isset($this->format_map[$mime])) { + $format = $this->format_map[$mime]; + $format = is_string($format) ? new $format : $format; + $format->setMIME($mime); + } + } + } + return $format; + } + + /** + * Parses the request to figure out the best format for response + * @return iFormat any class that implements iFormat + * @example JsonFormat + */ + protected function getResponseFormat() + { + //check if client has specified an extension + /** + * @var iFormat + */ + $format = NULL; + $extensions = explode('.', parse_url($_SERVER['REQUEST_URI'], + PHP_URL_PATH)); + while($extensions) { + $extension = array_pop($extensions); + $extension = explode('/', $extension); + $extension = array_shift($extension); + if ($extension && isset($this->format_map[$extension])) { + $format = $this->format_map[$extension]; + $format = is_string($format) ? new $format : $format; + $format->setExtension($extension); + //echo "Extension $extension"; + return $format; + } + } + //check if client has sent list of accepted data formats + if (isset($_SERVER['HTTP_ACCEPT'])) { + $acceptList = array(); + $accepts = explode(',', strtolower($_SERVER['HTTP_ACCEPT'])); + if (!is_array($accepts)) { + $accepts = array($accepts); + } + foreach ($accepts as $pos => $accept) { + $parts = explode(';q=', trim($accept)); + $type = array_shift($parts); + $quality = count($parts) + ? floatval(array_shift($parts)) + : (1000 - $pos) / 1000; + $acceptList[$type] = $quality; + } + arsort($acceptList); + foreach ($acceptList as $accept => $quality) { + if (isset($this->format_map[$accept])) { + $format = $this->format_map[$accept]; + $format = is_string($format) ? new $format : $format; + $format->setMIME($accept); + //echo "MIME $accept"; + header("Vary: Accept"); // Tell cache content is based on Accept header + return $format; + } + } + } else { + // RFC 2616: If no Accept header field is + // present, then it is assumed that the + // client accepts all media types. + $_SERVER['HTTP_ACCEPT'] = '*/*'; + } + if (strpos($_SERVER['HTTP_ACCEPT'], '*') !== FALSE) { + if (strpos($_SERVER['HTTP_ACCEPT'], 'application/*') !== FALSE) { + $format = new JsonFormat; + } + else if (strpos($_SERVER['HTTP_ACCEPT'], 'text/*') !== FALSE) { + $format = new XmlFormat; + } + else if (strpos($_SERVER['HTTP_ACCEPT'], '*/*') !== FALSE) { + $format = new $this->format_map['default']; + } + } + if (empty($format)) { + // RFC 2616: If an Accept header field is present, and if the server + // cannot send a response which is acceptable according to the combined + // Accept field value, then the server SHOULD send a 406 (not acceptable) + // response. + header('HTTP/1.1 406 Not Acceptable'); + die('406 Not Acceptable: The server was unable to negotiate content for this request.'); + } else { + header("Vary: Accept"); // Tell cache content is based ot Accept header + return $format; + } + } + + /** + * Parses the request data and returns it + * @return array php data + */ + protected function getRequestData() + { + try{ + $r = file_get_contents('php://input'); + if (is_null($r)) { + return $_GET; + } + $r = $this->request_format->decode($r); + return is_null($r) ? array() : $r; + } catch (RestException $e) { + $this->handleError($e->getCode(), $e->getMessage()); + } + } + + protected function mapUrlToMethod() + { + if (!isset($this->routes[$this->request_method])) { + return array(); + } + $urls = $this->routes[$this->request_method]; + if (!$urls) { + return array(); + } + + $found = FALSE; + $this->request_data += $_GET; + $params = array('request_data' => $this->request_data); + $params += $this->request_data; + $lc = strtolower($this->url); + foreach ($urls as $url => $call) { + //echo PHP_EOL.$url.' = '.$this->url.PHP_EOL; + $call = (object) $call; + if (strstr($url, ':')) { + $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', + preg_quote($url)); + if (preg_match(":^$regex$:i", $this->url, $matches)) { + foreach ($matches as $arg => $match) { + if (isset($call->arguments[$arg])) { + //flog("$arg => $match $args[$arg]"); + $params[$arg] = $match; + } + } + $found = TRUE; + break; + } + } else if ($url == $lc) { + $found = TRUE; + break; + } + } + if ($found) { + //echo PHP_EOL."Found $url "; + //print_r($call); + $p = $call->defaults; + foreach ($call->arguments as $key => $value) { + //echo "$key => $value \n"; + if (isset($params[$key])) { + $p[$value] = $params[$key]; + } + } + $call->arguments = $p; + return $call; + } + + } + + /** + * Apply static and non-static properties defined in + * the method information anotation + * @param String $class_name + * @param Object $instance instance of that class + * @param Object $method_info method information and metadata + */ + protected function applyClassMetadata($class_name, $instance, $method_info) + { + if (isset($method_info->metadata[$class_name]) + && is_array($method_info->metadata[$class_name]) + ) { + foreach ($method_info->metadata[$class_name] as $property => $value ) { + if (property_exists($class_name, $property)) { + $reflection_property = new ReflectionProperty($class_name, $property); + $reflection_property->setValue($instance, $value); + } + } + } + } + + protected function loadCache() + { + if ($this->cached !== NULL) { + return; + } + $file = $this->cache_dir . '/routes.php'; + $this->cached = FALSE; + + if ($this->production_mode) { + if (file_exists($file)) { + $routes = include($file); + } + if (isset($routes) && is_array($routes)) { + $this->routes = $routes; + $this->cached = TRUE; + } + } else { + //@unlink($this->cache_dir . "/$name.php"); + } + } + + /** + * Generates cachable url to method mapping + * @param string $class_name + * @param string $base_path + */ + protected function generateMap ($class_name, $base_path = '') + { + $reflection = new ReflectionClass($class_name); + $class_metadata = parse_doc($reflection->getDocComment()); + $methods = $reflection->getMethods( + ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED + ); + foreach ($methods as $method) { + $doc = $method->getDocComment(); + $arguments = array(); + $defaults = array(); + $metadata = $class_metadata + parse_doc($doc); + $params = $method->getParameters(); + $position = 0; + foreach ($params as $param) { + $arguments[$param->getName()] = $position; + $defaults[$position] = $param->isDefaultValueAvailable() + ? $param->getDefaultValue() : NULL; + $position++; + } + $method_flag = $method->isProtected() + ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) + : (isset($metadata['protected']) ? 1 : 0); + + //take note of the order + $call = array( + 'class_name' => $class_name, + 'method_name' => $method->getName(), + 'arguments' => $arguments, + 'defaults' => $defaults, + 'metadata' => $metadata, + 'method_flag' => $method_flag + ); + $method_url = strtolower($method->getName()); + if (preg_match_all( + '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', + $doc, + $matches, + PREG_SET_ORDER) + ) { + foreach ($matches as $match) { + $http_method = $match[1]; + $url = rtrim($base_path . $match[2],'/'); + $this->routes[$http_method][$url] = $call; + } + } else if ($method_url[0] != '_') { //not prefixed with underscore + // no configuration found so use convention + if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', + $method_url, + $matches) + ) { + $http_method = strtoupper($matches[0][0]); + $method_url = substr($method_url, strlen($http_method)); + } else { + $http_method = 'GET'; + } + $url = $base_path + . ($method_url == 'index' || $method_url == 'default' ? '' : $method_url); + $url = rtrim($url,'/'); + $this->routes[$http_method][$url] = $call; + foreach ($params as $param) { + if ($param->getName() == 'request_data') { + break; + } + $url .= $url == '' ? ':' : '/:'; + $url .= $param->getName(); + $this->routes[$http_method][$url] = $call; + } + } + } + } +} + +if (version_compare(PHP_VERSION, '5.3.0') < 0) { + require_once 'compat.php'; +} + +// ================================================================== +// +// Secondary classes +// +// ------------------------------------------------------------------ + +/** + * Special Exception for raising API errors + * that can be used in API methods + * @category Framework + * @package restler + * @subpackage exception + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class RestException extends Exception +{ + + public function __construct($http_status_code, $error_message = NULL) + { + parent::__construct($error_message, $http_status_code); + } + +} + +/** + * Interface for creating response classes + * @category Framework + * @package restler + * @subpackage result + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +interface iRespond +{ + /** + * Result of an api call is passed to this method + * to create a standard structure for the data + * @param unknown_type $result can be a primitive or array or object + */ + public function __formatResponse($result); + + /** + * When the api call results in RestException this method + * will be called to return the error message + * @param int $status_code + * @param String $message + */ + public function __formatError($status_code, $message); +} + +/** + * Default response formating class + * @category Framework + * @package restler + * @subpackage result + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class DefaultResponse implements iRespond +{ + function __formatResponse($result) + { + return $result; + } + function __formatError($statusCode, $message) + { + return array( + 'error' => array( + 'code' => $statusCode, + 'message' => $message + ) + ); + } +} + +/** + * Interface for creating authentication classes + * @category Framework + * @package restler + * @subpackage auth + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +interface iAuthenticate +{ + /** + * Auth function that is called when a protected method is requested + * @return boolean TRUE or FALSE + */ + public function __isAuthenticated(); +} + +/** + * Interface for creating custom data formats + * like xml, json, yaml, amf etc + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +interface iFormat +{ + /** + * Get Extension => MIME type mappings as an associative array + * @return array list of mime strings for the format + * @example array('json'=>'application/json'); + */ + public function getMIMEMap(); + + /** + * Set the selected MIME type + * @param string $mime MIME type + */ + public function setMIME($mime); + /** + * Get selected MIME type + */ + public function getMIME(); + + /** + * Set the selected file extension + * @param string $extension file extension + */ + public function setExtension($extension); + + /** + * Get the selected file extension + * @return string file extension + */ + public function getExtension(); + + /** + * Encode the given data in the format + * @param array $data resulting data that needs to + * be encoded in the given format + * @param boolean $human_readable set to TRUE when restler + * is not running in production mode. Formatter has to + * make the encoded output more human readable + * @return string encoded string + */ + public function encode($data, $human_readable = FALSE); + + /** + * Decode the given data from the format + * @param string $data data sent from client to + * the api in the given format. + * @return array associative array of the parsed data + */ + public function decode($data); +} + +/** + * URL Encoded String Format + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class UrlEncodedFormat implements iFormat +{ + const MIME = 'application/x-www-form-urlencoded'; + const EXTENSION = 'post'; + + public function getMIMEMap() + { + return array(self::EXTENSION => self::MIME); + } + + public function getMIME() + { + return self::MIME; + } + + public function getExtension() + { + return self::EXTENSION; + } + + public function setMIME($mime) + { + //do nothing + } + + public function setExtension($extension) + { + //do nothing + } + + public function encode($data, $human_readable = FALSE) + { + return http_build_query($data); + } + + public function decode($data) + { + parse_str($data,$r); + return $r; + } + + public function __toString() + { + return $this->getExtension(); + } +} + +/** + * Javascript Object Notation Format + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class JsonFormat implements iFormat +{ + const MIME = 'application/json,application/javascript'; + static $mime = 'application/json'; + const EXTENSION = 'json'; + + public function getMIMEMap() + { + return array(self::EXTENSION => self::MIME); + } + + public function getMIME() + { + return self::$mime; + } + + public function getExtension() + { + return self::EXTENSION; + } + + public function setMIME($mime) + { + self::$mime = $mime; + } + + public function setExtension($extension) + { + //do nothing + } + + public function encode($data, $human_readable = FALSE) + { + return $human_readable + ? $this->json_format(json_encode(object_to_array($data))) + : json_encode(object_to_array($data)); + } + + public function decode($data) + { + $decoded = json_decode ($data); + if (function_exists ('json_last_error')) { + $message = ''; + switch (json_last_error ()) { + case JSON_ERROR_NONE: + return object_to_array ($decoded); + break; + case JSON_ERROR_DEPTH: + $message = 'maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $message = 'underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $message = 'unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $message = 'malformed JSON'; + break; + case JSON_ERROR_UTF8: + $message = 'malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $message = 'unknown error'; + break; + } + throw new RestException (400, 'Error parsing JSON, ' . $message); + } else if (strlen ($data) && $decoded === NULL || $decoded === $data) { + throw new RestException (400, 'Error parsing JSON'); + } + return object_to_array ($decoded); + } + + /** + * Pretty print JSON string + * @param string $json + * @return string formated json + */ + private function json_format($json) + { + $tab = " "; + $new_json = ""; + $indent_level = 0; + $in_string = FALSE; + $len = strlen($json); + + for($c = 0; $c < $len; $c++) { + $char = $json[$c]; + switch($char) { + case '{': + case '[': + if (!$in_string) { + $new_json .= $char . "\n" . + str_repeat($tab, $indent_level + 1); + $indent_level++; + } else { + $new_json .= $char; + } + break; + case '}': + case ']': + if (!$in_string) { + $indent_level--; + $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; + } else { + $new_json .= $char; + } + break; + case ',': + if (!$in_string) { + $new_json .= ",\n" . str_repeat($tab, $indent_level); + } else { + $new_json .= $char; + } + break; + case ':': + if (!$in_string) { + $new_json .= ": "; + } else { + $new_json .= $char; + } + break; + case '"': + if ($c==0) { + $in_string = TRUE; + } else if ($c > 0 && $json[$c-1] != '\\') { + $in_string = !$in_string; + } + default: + $new_json .= $char; + break; + } + } + + return $new_json; + } + + public function __toString() + { + return $this->getExtension(); + } +} + +/** + * Parses the PHPDoc comments for metadata. Inspired by Documentor code base + * @category Framework + * @package restler + * @subpackage helper + * @author Murray Picton + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.gnu.org/licenses/ GNU General Public License + * @link https://github.com/murraypicton/Doqumentor + */ +class DocParser +{ + private $params = array(); + + function parse($doc = '') { + if ($doc == '') { + return $this->params; + } + //Get the comment + if (preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) { + return $this->params; + } + $comment = trim($comment[1]); + //Get all the lines and strip the * from the first character + if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) { + return $this->params; + } + $this->parseLines($lines[1]); + return $this->params; + } + + private function parseLines($lines) + { + foreach($lines as $line) { + $parsedLine = $this->parseLine($line); //Parse the line + + if ($parsedLine === false && !isset($this->params['description'])) { + if (isset($desc)) { + //Store the first line in the short description + $this->params['description'] = implode(PHP_EOL, $desc); + } + $desc = array(); + } else if ($parsedLine !== false) { + $desc[] = $parsedLine; //Store the line in the long description + } + } + $desc = implode(' ', $desc); + if (!empty($desc)) { + $this->params['long_description'] = $desc; + } + } + + private function parseLine($line) + { + //trim the whitespace from the line + $line = trim($line); + + if (empty($line)) { + return false; //Empty line + } + + if (strpos($line, '@') === 0) { + if (strpos($line, ' ') > 0) { + //Get the parameter name + $param = substr($line, 1, strpos($line, ' ') - 1); + $value = substr($line, strlen($param) + 2); //Get the value + } else { + $param = substr($line, 1); + $value = ''; + } + //Parse the line and return false if the parameter is valid + if ($this->setParam($param, $value)) return false; + } + return $line; + } + + private function setParam($param, $value) + { + if ($param == 'param' || $param == 'return') { + $value = $this->formatParamOrReturn($value); + } + if ($param == 'class') { + list($param, $value) = $this->formatClass($value); + } + + if (empty($this->params[$param])) { + $this->params[$param] = $value; + } else if ($param == 'param') { + $arr = array($this->params[$param], $value); + $this->params[$param] = $arr; + } else { + $this->params[$param] = $value + $this->params[$param]; + } + return true; + } + + private function formatClass($value) + { + $r = preg_split("[\(|\)]", $value); + if (count($r) > 1) { + $param = $r[0]; + parse_str($r[1],$value); + foreach ($value as $key => $val) { + $val = explode(',', $val); + if (count($val) > 1) { + $value[$key] = $val; + } + } + } else { + $param = 'Unknown'; + } + return array($param, $value); + } + + private function formatParamOrReturn($string) + { + $pos = strpos($string, ' '); + $type = substr($string, 0, $pos); + return '(' . $type . ')' . substr($string, $pos + 1); + } +} + + +// ================================================================== +// +// Individual functions +// +// ------------------------------------------------------------------ + +function parse_doc($php_doc_comment) +{ + $p = new DocParser(); + return $p->parse($php_doc_comment); + + $p = new Parser($php_doc_comment); + return $p; + + $php_doc_comment = preg_replace( + "/(^[\\s]*\\/\\*\\*) + |(^[\\s]\\*\\/) + |(^[\\s]*\\*?\\s) + |(^[\\s]*) + |(^[\\t]*)/ixm", "", $php_doc_comment); + $php_doc_comment = str_replace("\r", "", $php_doc_comment); + $php_doc_comment = preg_replace("/([\\t])+/", "\t", $php_doc_comment); + return explode("\n", $php_doc_comment); + + $php_doc_comment = trim(preg_replace('/\r?\n *\* */', ' ', + $php_doc_comment)); + return $php_doc_comment; + + preg_match_all('/@([a-z]+)\s+(.*?)\s*(?=$|@[a-z]+\s)/s', + $php_doc_comment, $matches); + return array_combine($matches[1], $matches[2]); +} + +/** + * Conveniance function that converts the given object + * in to associative array, leaves object alone if + * JsonSerializable interface is detected + * @param object $object that needs to be converted + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +function object_to_array($object, $utf_encode = FALSE) +{ + if (is_array($object) + || (is_object($object) + && !($object instanceof JsonSerializable)) + ) { + $array = array(); + foreach($object as $key => $value) { + $value = object_to_array($value, $utf_encode); + if ($utf_encode && is_string($value)) { + $value = utf8_encode($value); + } + $array[$key] = $value; + } + return $array; + } + return $object; +} + +/** + * an autoloader function for loading format classes + * @param String $class_name class name of a class that implements iFormat + */ +function autoload_formats($class_name) +{ + $class_name = strtolower($class_name); + $file = RESTLER_PATH . "/$class_name/$class_name.php"; + if (file_exists ($file)) { + require_once ($file); + } else { + $file = RESTLER_PATH . "/$class_name.php"; + if (file_exists ($file)) { + require_once ($file); + } elseif (file_exists ("$class_name.php")) { + require_once ("$class_name.php"); + } + } +} + +// ================================================================== +// +// Autoload +// +// ------------------------------------------------------------------ + +spl_autoload_register('autoload_formats'); + +/** + * Manage compatibility with PHP 5 < PHP 5.3 + */ +if (!function_exists('isRestlerCompatibilityModeEnabled')) { + function isRestlerCompatibilityModeEnabled() + { + return FALSE; + } +} +define('RESTLER_PATH', dirname(__FILE__)); diff --git a/gulliver/thirdparty/restler/xmlformat.php b/gulliver/thirdparty/restler/xmlformat.php new file mode 100644 index 000000000..67465d651 --- /dev/null +++ b/gulliver/thirdparty/restler/xmlformat.php @@ -0,0 +1,213 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class XmlFormat implements iFormat +{ + public static $parse_attributes=true; + public static $parse_namespaces=false; + public static $attribute_names=array('xmlns'); + /** + * Default name for the root node. + * @var string $rootNodeName + */ + public static $root_name='response'; + public static $default_tag_name='item'; + public static $mime = 'application/xml'; + const MIME = 'application/xml,text/xml'; + const EXTENSION = 'xml'; + + public function getMIMEMap() + { + return array(self::EXTENSION=>self::MIME); + } + public function getMIME(){ + return self::$mime; + } + public function getExtension(){ + return self::EXTENSION; + } + public function setMIME($mime){ + self::$mime = $mime; + } + public function setExtension($extension){ + //do nothing + } + + public function encode($data, $human_readable=false){ + return $this->toXML( object_to_array($data, false), + self::$root_name, $human_readable); + } + + public function decode($data){ + try { + if($data=='')return array(); + return $this->toArray($data); + } catch (Exception $e) { + throw new RestException(400, "Error decoding request. ". + $e->getMessage()); + } + } + + public function __toString(){ + return $this->getExtension(); + } + + /** + * determine if a variable is an associative array + */ + public function isAssoc( $array ) { + return (is_array($array) && 0 !== count(array_diff_key($array, + array_keys(array_keys($array))))); + } + + /** + * The main function for converting to an XML document. + * Pass in a multi dimensional array and this recrusively loops through + * and builds up an XML document. + * @param array $data + * @param string $rootNodeName - what you want the root node to be - + * defaults to data. + * @param SimpleXMLElement $xml - should only be used recursively + * @return string XML + * @link http://bit.ly/n85yLi + */ + public function toXML( $data, $root_node_name = 'result', + $human_readable=false, &$xml=null) { + // turn off compatibility mode as simple xml + //throws a wobbly if you don't. + if (ini_get('zend.ze1_compatibility_mode') == 1) + ini_set ('zend.ze1_compatibility_mode', 0); + if (is_null($xml)) + $xml = @simplexml_load_string("<$root_node_name/>"); + if(is_array($data)){ + $numeric=0; + // loop through the data passed in. + foreach( $data as $key => $value ) { + + // no numeric keys in our xml please! + if ( is_numeric( $key ) ) { + $numeric = 1; + $key = self::$root_name == $root_node_name ? + self::$default_tag_name : $root_node_name; + } + + // delete any char not allowed in XML element names + $key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key); + + // if there is another array found recrusively + //call this function + if ( is_array( $value ) ) { + $node = $this->isAssoc( $value ) || $numeric ? + $xml->addChild( $key ) : $xml; + + // recrusive call. + if ( $numeric ) $key = 'anon'; + $this->toXML($value, $key, $human_readable, $node); + } else { + // add single node or attribute + $value=htmlspecialchars($value); + in_array($key,self::$attribute_names) ? + $xml->addAttribute($key,$value) : + $xml->addChild( $key, $value); + } + } + }else{ //if given data is a string or number + //simply wrap it as text node to root + if(is_bool($data)) $data = $data ? 'true' : 'false'; + $xml = @simplexml_load_string( + "<$root_node_name>".htmlspecialchars($data).""); + //$xml->{0} = $data; + } + if(!$human_readable){ + return $xml->asXML(); + }else{ + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + return $dom->saveXML(); + } + } + + /** + * Convert an XML document to a multi dimensional array + * Pass in an XML document (or SimpleXMLElement object) and this + * recrusively loops through and builds a representative array + * + * @param string $xml - XML document - can optionally be a + * SimpleXMLElement object + * @return array ARRAY + * @link http://bit.ly/n85yLi + */ + public function toArray( $xml, $firstCall=true) { + if ( is_string( $xml ) ) $xml = new SimpleXMLElement( $xml ); + $children = $xml->children(); + if ( !$children ) { + $r = (string) $xml; + if($r=='true' || $r=='false')$r=$r=='true'; + return $r; + } + $arr = array(); + + if($firstCall){ + //reset the attribute names list + self::$attribute_names=array(); + self::$root_name = $xml->getName(); + if (self::$parse_namespaces){ + foreach($xml->getDocNamespaces(TRUE) as $namepace => $uri) { + $arr[$namepace=='' ? 'xmlns' : + 'xmlns:'.$namepace] = (string)$uri; + } + } + } + if(self::$parse_attributes){ + foreach($xml->attributes() as $attName => $attValue) { + $arr[$attName] = (string)$attValue; + //add to attribute list for round trip support + self::$attribute_names[]=$attName; + } + } + foreach ($children as $key => $node) { + $node = $this->toArray($node, false); + // support for 'anon' non-associative arrays + if ($key == 'anon') $key = count($arr); + + // if the node is already set, put it into an array + if (isset($arr[$key])) { + if ( !is_array($arr[$key]) || @$arr[$key][0] == null ) + $arr[$key] = array($arr[$key]); + $arr[$key][] = $node; + } else { + $arr[$key] = $node; + } + } + return $arr; + } + + /** + * When you decode an XML its structure is copied to the static vars + * we can use this function to echo them out and then copy paste inside + * our service methods + * @return string PHP source code to reproduce the configuration + */ + public static function exportCurrentSettings() { + $s = 'self::$root_name = "'. + (self::$root_name)."\";\n"; + $s .= 'self::$attribute_names = '. + (var_export(self::$attribute_names, true)).";\n"; + $s .= 'self::$default_tag_name = "'. + self::$default_tag_name."\";\n"; + $s .= 'self::$parse_attributes = '. + (self::$parse_attributes ? 'true' : 'false').";\n"; + $s .= 'self::$parse_namespaces = '. + (self::$parse_namespaces ? 'true' : 'false').";\n\n\n"; + return $s; + } + +} diff --git a/gulliver/thirdparty/restler/yamlformat/LICENSE b/gulliver/thirdparty/restler/yamlformat/LICENSE new file mode 100644 index 000000000..3cef85317 --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2008-2009 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/gulliver/thirdparty/restler/yamlformat/README.markdown b/gulliver/thirdparty/restler/yamlformat/README.markdown new file mode 100644 index 000000000..e4f80cfba --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/README.markdown @@ -0,0 +1,15 @@ +Symfony YAML: A PHP library that speaks YAML +============================================ + +Symfony YAML is a PHP library that parses YAML strings and converts them to +PHP arrays. It can also converts PHP arrays to YAML strings. Its official +website is at http://components.symfony-project.org/yaml/. + +The documentation is to be found in the `doc/` directory. + +Symfony YAML is licensed under the MIT license (see LICENSE file). + +The Symfony YAML library is developed and maintained by the +[symfony](http://www.symfony-project.org/) project team. It has been extracted +from symfony to be used as a standalone library. Symfony YAML is part of the +[symfony components project](http://components.symfony-project.org/). diff --git a/gulliver/thirdparty/restler/yamlformat/sfyaml.php b/gulliver/thirdparty/restler/yamlformat/sfyaml.php new file mode 100644 index 000000000..9e4b46203 --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/sfyaml.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +include_once 'sfyamlinline.php'; + +/** + * sfYaml offers convenience methods to load and dump YAML. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYaml.class.php 8988 2008-05-15 20:24:26Z fabien $ + */ +class sfYaml +{ + static protected + $spec = '1.2'; + + /** + * Sets the YAML specification version to use. + * + * @param string $version The YAML specification version + */ + static public function setSpecVersion($version) + { + if (!in_array($version, array('1.1', '1.2'))) + { + throw new InvalidArgumentException(sprintf('Version %s of the YAML specifications is not supported', $version)); + } + + self::$spec = $version; + } + + /** + * Gets the YAML specification version to use. + * + * @return string The YAML specification version + */ + static public function getSpecVersion() + { + return self::$spec; + } + + /** + * Loads YAML into a PHP array. + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. + * + * Usage: + * + * $array = sfYaml::load('config.yml'); + * print_r($array); + * + * + * @param string $input Path of YAML file or string containing YAML + * + * @return array The YAML converted to a PHP array + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public static function load($input) + { + $file = ''; + + // if input is a file, process it + if (strpos($input, "\n") === false && is_file($input)) + { + $file = $input; + + ob_start(); + $retval = include($input); + $content = ob_get_clean(); + + // if an array is returned by the config file assume it's in plain php form else in YAML + $input = is_array($retval) ? $retval : $content; + } + + // if an array is returned by the config file assume it's in plain php form else in YAML + if (is_array($input)) + { + return $input; + } + + require_once dirname(__FILE__).'/sfYamlParser.php'; + + $yaml = new sfYamlParser(); + + try + { + $ret = $yaml->parse($input); + } + catch (Exception $e) + { + throw new InvalidArgumentException(sprintf('Unable to parse %s: %s', $file ? sprintf('file "%s"', $file) : 'string', $e->getMessage())); + } + + return $ret; + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param integer $inline The level where you switch to inline YAML + * + * @return string A YAML string representing the original PHP array + */ + public static function dump($array, $inline = 2) + { + require_once dirname(__FILE__).'/sfyamldumper.php'; + + $yaml = new sfYamlDumper(); + + return $yaml->dump($array, $inline); + } +} + +/** + * Wraps echo to automatically provide a newline. + * + * @param string $string The string to echo with new line + */ +function echoln($string) +{ + echo $string."\n"; +} diff --git a/gulliver/thirdparty/restler/yamlformat/sfyamldumper.php b/gulliver/thirdparty/restler/yamlformat/sfyamldumper.php new file mode 100644 index 000000000..56e4a234f --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/sfyamldumper.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +//require_once(dirname(__FILE__).'/sfYamlInline.php'); + +/** + * sfYamlDumper dumps PHP variables to YAML strings. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlDumper.class.php 10575 2008-08-01 13:08:42Z nicolas $ + */ +class sfYamlDumper +{ + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param integer $inline The level where you switch to inline YAML + * @param integer $indent The level o indentation indentation (used internally) + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0) + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) + { + $output .= $prefix.sfYamlInline::dump($input); + } + else + { + $isAHash = array_keys($input) !== range(0, count($input) - 1); + + foreach ($input as $key => $value) + { + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? sfYamlInline::dump($key).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + 2) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} diff --git a/gulliver/thirdparty/restler/yamlformat/sfyamlinline.php b/gulliver/thirdparty/restler/yamlformat/sfyamlinline.php new file mode 100644 index 000000000..bc8397ab4 --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/sfyamlinline.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +//require_once dirname(__FILE__).'/sfYaml.php'; + +/** + * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $ + */ +class sfYamlInline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + /** + * Convert a YAML string to a PHP array. + * + * @param string $value A YAML string + * + * @return array A PHP array representing the YAML string + */ + static public function load($value) + { + $value = trim($value); + + if (0 == strlen($value)) + { + return ''; + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + switch ($value[0]) + { + case '[': + $result = self::parseSequence($value); + break; + case '{': + $result = self::parseMapping($value); + break; + default: + $result = self::parseScalar($value); + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * + * @return string The YAML string representing the PHP array + */ + static public function dump($value) + { + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case is_resource($value): + throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.'); + case is_object($value): + return '!!php/object:'.serialize($value); + case is_array($value): + return self::dumpArray($value); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + return is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ? "'$value'" : $value); + case false !== strpos($value, "\n") || false !== strpos($value, "\r"): + return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value)); + case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ - ? | < > = ! % @ ` ]/x', $value): + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + case '' == $value: + return "''"; + case preg_match(self::getTimestampRegex(), $value): + return "'$value'"; + case in_array(strtolower($value), $trueValues): + return "'$value'"; + case in_array(strtolower($value), $falseValues): + return "'$value'"; + case in_array(strtolower($value), array('null', '~')): + return "'$value'"; + default: + return $value; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * + * @return string The YAML string representing the PHP array + */ + static protected function dumpArray($value) + { + // array + $keys = array_keys($value); + if ( + (1 == count($keys) && '0' == $keys[0]) + || + (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2)) + { + $output = array(); + foreach ($value as $val) + { + $output[] = self::dump($val); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // mapping + $output = array(); + foreach ($value as $key => $val) + { + $output[] = sprintf('%s: %s', self::dump($key), self::dump($val)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a scalar to a YAML string. + * + * @param scalar $scalar + * @param string $delimiters + * @param array $stringDelimiter + * @param integer $i + * @param boolean $evaluate + * + * @return string A YAML string + */ + static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true) + { + if (in_array($scalar[$i], $stringDelimiters)) + { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + } + else + { + // "normal" string + if (!$delimiters) + { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (false !== $strpos = strpos($output, ' #')) + { + $output = rtrim(substr($output, 0, $strpos)); + } + } + else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) + { + $output = $match[1]; + $i += strlen($output); + } + else + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar)); + } + + $output = $evaluate ? self::evaluateScalar($output) : $output; + } + + return $output; + } + + /** + * Parses a quoted scalar to YAML. + * + * @param string $scalar + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + if ('"' == $scalar[$i]) + { + // evaluate the string + $output = str_replace(array('\\"', '\\n', '\\r'), array('"', "\n", "\r"), $output); + } + else + { + // unescape ' + $output = str_replace('\'\'', '\'', $output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a sequence to a YAML string. + * + * @param string $sequence + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseSequence($sequence, &$i = 0) + { + $output = array(); + $len = strlen($sequence); + $i += 1; + + // [foo, bar, ...] + while ($i < $len) + { + switch ($sequence[$i]) + { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $i); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $i); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i); + + if (!$isQuoted && false !== strpos($value, ': ')) + { + // embedded mapping? + try + { + $value = self::parseMapping('{'.$value.'}'); + } + catch (InvalidArgumentException $e) + { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence)); + } + + /** + * Parses a mapping to a YAML string. + * + * @param string $mapping + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseMapping($mapping, &$i = 0) + { + $output = array(); + $len = strlen($mapping); + $i += 1; + + // {foo: bar, bar:foo, ...} + while ($i < $len) + { + switch ($mapping[$i]) + { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + return $output; + } + + // key + $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + + // value + $done = false; + while ($i < $len) + { + switch ($mapping[$i]) + { + case '[': + // nested sequence + $output[$key] = self::parseSequence($mapping, $i); + $done = true; + break; + case '{': + // nested mapping + $output[$key] = self::parseMapping($mapping, $i); + $done = true; + break; + case ':': + case ' ': + break; + default: + $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + $done = true; + --$i; + } + + ++$i; + + if ($done) + { + continue 2; + } + } + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * + * @return string A YAML string + */ + static protected function evaluateScalar($scalar) + { + $scalar = trim($scalar); + + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case 'null' == strtolower($scalar): + case '' == $scalar: + case '~' == $scalar: + return null; + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return intval(self::parseScalar(substr($scalar, 2))); + case 0 === strpos($scalar, '!!php/object:'): + return unserialize(substr($scalar, 13)); + case ctype_digit($scalar): + $raw = $scalar; + $cast = intval($scalar); + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case in_array(strtolower($scalar), $trueValues): + return true; + case in_array(strtolower($scalar), $falseValues): + return false; + case is_numeric($scalar): + return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar); + case 0 == strcasecmp($scalar, '.inf'): + case 0 == strcasecmp($scalar, '.NaN'): + return -log(0); + case 0 == strcasecmp($scalar, '-.inf'): + return log(0); + case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + return floatval(str_replace(',', '', $scalar)); + case preg_match(self::getTimestampRegex(), $scalar): + return strtotime($scalar); + default: + return (string) $scalar; + } + } + + static protected function getTimestampRegex() + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } +} diff --git a/gulliver/thirdparty/restler/yamlformat/sfyamlparser.php b/gulliver/thirdparty/restler/yamlformat/sfyamlparser.php new file mode 100644 index 000000000..a10286b2d --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/sfyamlparser.php @@ -0,0 +1,622 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +//require_once(dirname(__FILE__).'/sfYamlInline.php'); + +if (!defined('PREG_BAD_UTF8_OFFSET_ERROR')) +{ + define('PREG_BAD_UTF8_OFFSET_ERROR', 5); +} + +/** + * sfYamlParser parses YAML strings to convert them to PHP arrays. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlParser.class.php 10832 2008-08-13 07:46:08Z fabien $ + */ +class sfYamlParser +{ + protected + $offset = 0, + $lines = array(), + $currentLineNb = -1, + $currentLine = '', + $refs = array(); + + /** + * Constructor + * + * @param integer $offset The offset of YAML document (used for line numbers in error messages) + */ + public function __construct($offset = 0) + { + $this->offset = $offset; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * + * @return mixed A PHP value + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public function parse($value) + { + $this->currentLineNb = -1; + $this->currentLine = ''; + $this->lines = explode("\n", $this->cleanup($value)); + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + } + + $data = array(); + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + continue; + } + + // tab? + if (preg_match('#^\t+#', $this->currentLine)) + { + throw new InvalidArgumentException(sprintf('A YAML file cannot contain tabs as indentation at line %d (%s).', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + $isRef = $isInPlace = $isProcessed = false; + if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) + { + if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[] = $parser->parse($this->getNextEmbedBlock()); + } + else + { + if (isset($values['leadspaces']) + && ' ' == $values['leadspaces'] + && preg_match('#^(?P'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"\{].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches)) + { + // this is a compact notation element, add to next block and parse + $c = $this->getRealCurrentLineNb(); + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + + $block = $values['value']; + if (!$this->isNextLineIndented()) + { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2); + } + + $data[] = $parser->parse($block); + } + else + { + $data[] = $this->parseValue($values['value']); + } + } + } + else if (preg_match('#^(?P'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values)) + { + $key = sfYamlInline::parseScalar($values['key']); + + if ('<<' === $key) + { + if (isset($values['value']) && '*' === substr($values['value'], 0, 1)) + { + $isInPlace = substr($values['value'], 1); + if (!array_key_exists($isInPlace, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist at line %s (%s).', $isInPlace, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + if (isset($values['value']) && $values['value'] !== '') + { + $value = $values['value']; + } + else + { + $value = $this->getNextEmbedBlock(); + } + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $parsed = $parser->parse($value); + + $merged = array(); + if (!is_array($parsed)) + { + throw new InvalidArgumentException(sprintf("YAML merge keys used with a scalar value instead of an array at line %s (%s)", $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + else if (isset($parsed[0])) + { + // Numeric array, merge individual elements + foreach (array_reverse($parsed) as $parsedItem) + { + if (!is_array($parsedItem)) + { + throw new InvalidArgumentException(sprintf("Merge items must be arrays at line %s (%s).", $this->getRealCurrentLineNb() + 1, $parsedItem)); + } + $merged = array_merge($parsedItem, $merged); + } + } + else + { + // Associative array, merge + $merged = array_merge($merged, $parsed); + } + + $isProcessed = $merged; + } + } + else if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($isProcessed) + { + // Merge keys + $data = $isProcessed; + } + // hash + else if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + // if next line is less indented or equal, then it means that the current value is null + if ($this->isNextLineIndented()) + { + $data[$key] = null; + } + else + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[$key] = $parser->parse($this->getNextEmbedBlock()); + } + } + else + { + if ($isInPlace) + { + $data = $this->refs[$isInPlace]; + } + else + { + $data[$key] = $this->parseValue($values['value']); + } + } + } + else + { + // 1-liner followed by newline + if (2 == count($this->lines) && empty($this->lines[1])) + { + $value = sfYamlInline::load($this->lines[0]); + if (is_array($value)) + { + $first = reset($value); + if ('*' === substr($first, 0, 1)) + { + $data = array(); + foreach ($value as $alias) + { + $data[] = $this->refs[substr($alias, 1)]; + } + $value = $data; + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) + { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error on line'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached on line'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached on line'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data on line'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point on line'; + break; + default: + $error = 'Unable to parse line'; + } + + throw new InvalidArgumentException(sprintf('%s %d (%s).', $error, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + if ($isRef) + { + $this->refs[$isRef] = end($data); + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return empty($data) ? null : $data; + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return integer The current line number + */ + protected function getRealCurrentLineNb() + { + return $this->currentLineNb + $this->offset; + } + + /** + * Returns the current line indentation. + * + * @return integer The current line indentation + */ + protected function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param integer $indentation The indent level at which the block is to be read, or null for default + * + * @return string A YAML string + */ + protected function getNextEmbedBlock($indentation = null) + { + $this->moveToNextLine(); + + if (null === $indentation) + { + $newIndent = $this->getCurrentLineIndentation(); + + if (!$this->isCurrentLineEmpty() && 0 == $newIndent) + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + $newIndent = $indentation; + } + + $data = array(substr($this->currentLine, $newIndent)); + + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + if ($this->isCurrentLineBlank()) + { + $data[] = substr($this->currentLine, $newIndent); + } + + continue; + } + + $indent = $this->getCurrentLineIndentation(); + + if (preg_match('#^(?P *)$#', $this->currentLine, $match)) + { + // empty line + $data[] = $match['text']; + } + else if ($indent >= $newIndent) + { + $data[] = substr($this->currentLine, $newIndent); + } + else if (0 == $indent) + { + $this->moveToPreviousLine(); + + break; + } + else + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + */ + protected function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) + { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + protected function moveToPreviousLine() + { + $this->currentLine = $this->lines[--$this->currentLineNb]; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * + * @return mixed A PHP value + */ + protected function parseValue($value) + { + if ('*' === substr($value, 0, 1)) + { + if (false !== $pos = strpos($value, '#')) + { + $value = substr($value, 1, $pos - 2); + } + else + { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist (%s).', $value, $this->currentLine)); + } + return $this->refs[$value]; + } + + if (preg_match('/^(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?$/', $value, $matches)) + { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers))); + } + else + { + return sfYamlInline::load($value); + } + } + + /** + * Parses a folded scalar. + * + * @param string $separator The separator that was used to begin this folded scalar (| or >) + * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) + * @param integer $indentation The indentation that was used to begin this folded scalar + * + * @return string The text value + */ + protected function parseFoldedScalar($separator, $indicator = '', $indentation = 0) + { + $separator = '|' == $separator ? "\n" : ' '; + $text = ''; + + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineBlank()) + { + $text .= "\n"; + + $notEOF = $this->moveToNextLine(); + } + + if (!$notEOF) + { + return ''; + } + + if (!preg_match('#^(?P'.($indentation ? str_repeat(' ', $indentation) : ' +').')(?P.*)$#u', $this->currentLine, $matches)) + { + $this->moveToPreviousLine(); + + return ''; + } + + $textIndent = $matches['indent']; + $previousIndent = 0; + + $text .= $matches['text'].$separator; + while ($this->currentLineNb + 1 < count($this->lines)) + { + $this->moveToNextLine(); + + if (preg_match('#^(?P {'.strlen($textIndent).',})(?P.+)$#u', $this->currentLine, $matches)) + { + if (' ' == $separator && $previousIndent != $matches['indent']) + { + $text = substr($text, 0, -1)."\n"; + } + $previousIndent = $matches['indent']; + + $text .= str_repeat(' ', $diff = strlen($matches['indent']) - strlen($textIndent)).$matches['text'].($diff ? "\n" : $separator); + } + else if (preg_match('#^(?P *)$#', $this->currentLine, $matches)) + { + $text .= preg_replace('#^ {1,'.strlen($textIndent).'}#', '', $matches['text'])."\n"; + } + else + { + $this->moveToPreviousLine(); + + break; + } + } + + if (' ' == $separator) + { + // replace last separator by a newline + $text = preg_replace('/ (\n*)$/', "\n$1", $text); + } + + switch ($indicator) + { + case '': + $text = preg_replace('#\n+$#s', "\n", $text); + break; + case '+': + break; + case '-': + $text = preg_replace('#\n+$#s', '', $text); + break; + } + + return $text; + } + + /** + * Returns true if the next line is indented. + * + * @return Boolean Returns true if the next line is indented, false otherwise + */ + protected function isNextLineIndented() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) + { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) + { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() <= $currentIndentation) + { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise + */ + protected function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return Boolean Returns true if the current line is blank, false otherwise + */ + protected function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return Boolean Returns true if the current line is a comment line, false otherwise + */ + protected function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + return $ltrimmedLine[0] === '#'; + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + protected function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + if (!preg_match("#\n$#", $value)) + { + $value .= "\n"; + } + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if ($count == 1) + { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if ($count == 1) + { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#s', '', $value); + } + + return $value; + } +} diff --git a/gulliver/thirdparty/restler/yamlformat/yamlformat.php b/gulliver/thirdparty/restler/yamlformat/yamlformat.php new file mode 100644 index 000000000..52abcbf1b --- /dev/null +++ b/gulliver/thirdparty/restler/yamlformat/yamlformat.php @@ -0,0 +1,47 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class YamlFormat implements iFormat +{ + const MIME ='text/plain'; + const EXTENSION = 'yaml'; + + public function getMIMEMap() + { + return array(YamlFormat::EXTENSION=>YamlFormat::MIME); + } + public function getMIME(){ + return YamlFormat::MIME; + } + public function getExtension(){ + return YamlFormat::EXTENSION; + } + public function setMIME($mime){ + //do nothing + } + public function setExtension($extension){ + //do nothing + } + + public function encode($data, $human_readable=false){ + require_once 'sfyaml.php'; + return sfYaml::dump(object_to_array($data)); + } + + public function decode($data){ + require_once 'sfyaml.php'; + return sfYaml::load($data); + } + + public function __toString(){ + return $this->getExtension(); + } +} \ No newline at end of file diff --git a/workflow/engine/config/paths.php b/workflow/engine/config/paths.php index 725321010..e85788a72 100755 --- a/workflow/engine/config/paths.php +++ b/workflow/engine/config/paths.php @@ -64,10 +64,11 @@ define( 'PATH_WORKFLOW_MSSQL_DATA', PATH_CORE . 'data' . PATH_SEP.'mssql'.PATH_SEP); define( 'PATH_RBAC_MSSQL_DATA', PATH_RBAC_CORE . 'data' . PATH_SEP.'mssql'.PATH_SEP); define( 'PATH_CONTROLLERS', PATH_CORE . 'controllers' . PATH_SEP ); + define( 'PATH_SERVICES_REST', PATH_CORE . 'services' . PATH_SEP . 'rest' . PATH_SEP); // include Gulliver Class require_once( PATH_GULLIVER . PATH_SEP . 'class.g.php'); - + if(file_exists(FILE_PATHS_INSTALLED)) { // backward compatibility; parsing old definitions in the compiled path constant $tmp = file_get_contents(FILE_PATHS_INSTALLED); @@ -76,28 +77,28 @@ @file_put_contents(FILE_PATHS_INSTALLED, str_replace('PATH_OUTTRUNK', 'PATH_DATA', $tmp)); } // end backward compatibility - + // include the workspace installed configuration require_once FILE_PATHS_INSTALLED; - + // defining system constant when a valid workspace environment exists define('PATH_LANGUAGECONT', PATH_DATA . "META-INF" . PATH_SEP); define('PATH_CUSTOM_SKINS', PATH_DATA . 'skins' . PATH_SEP); define('PATH_TEMPORAL', PATH_C . 'dynEditor/'); define('PATH_DB', PATH_DATA . 'sites' . PATH_SEP); - // smarty constants + // smarty constants define('PATH_SMARTY_C', PATH_C . 'smarty' . PATH_SEP . 'c'); define('PATH_SMARTY_CACHE', PATH_C . 'smarty' . PATH_SEP . 'cache'); - + if (!is_dir(PATH_SMARTY_C)) { G::mk_dir(PATH_SMARTY_C); } - if (!is_dir(PATH_SMARTY_CACHE)) { + if (!is_dir(PATH_SMARTY_CACHE)) { G::mk_dir(PATH_SMARTY_CACHE); } } - + // set include path set_include_path( PATH_CORE . PATH_SEPARATOR . @@ -110,7 +111,7 @@ /** * Global definitions, before it was the defines.php file */ - + // URL Key define("URL_KEY", 'c0l0s40pt1mu59r1m3' ); @@ -134,7 +135,7 @@ // Number of files per folder at PATH_UPLOAD (cases documents) define('APPLICATION_DOCUMENTS_PER_FOLDER', 1000); - // Server of ProcessMaker Library + // Server of ProcessMaker Library define('PML_SERVER' , 'http://library.processmaker.com'); define('PML_WSDL_URL' , PML_SERVER . '/syspmLibrary/en/green/services/wsdl'); define('PML_UPLOAD_URL', PML_SERVER . '/syspmLibrary/en/green/services/uploadProcess'); diff --git a/workflow/engine/services/rest/Author.php b/workflow/engine/services/rest/Author.php new file mode 100644 index 000000000..3326b28e9 --- /dev/null +++ b/workflow/engine/services/rest/Author.php @@ -0,0 +1,30 @@ +bmi = round($kg/($meter*$meter),2); + $lb = round($kg/0.45359237,2); + + if($result->bmi<18.5){ + $result->message = 'Underweight'; + }elseif ($result->bmi<=24.9){ + $result->message = 'Normal weight'; + }elseif ($result->bmi<=29.9){ + $result->message = 'Overweight'; + }else{ + $result->message = 'Obesity'; + } + $result->metric = array('height'=>"$cm centimeter", 'weight'=>"$weight kilograms"); + $result->imperial = array('height'=>"$feet feet $inches inches", 'weight'=>"$lb pounds"); + return $result; + } +} diff --git a/workflow/engine/services/rest/Say.php b/workflow/engine/services/rest/Say.php new file mode 100644 index 000000000..9397b0e0b --- /dev/null +++ b/workflow/engine/services/rest/Say.php @@ -0,0 +1,6 @@ +setLogger($logger); } @@ -467,12 +479,12 @@ // Session Initializations ini_set('session.auto_start', '1'); - + // The register_globals feature has been DEPRECATED as of PHP 5.3.0. default value Off. // ini_set( 'register_globals', 'Off' ); //session_start(); ob_start(); - + // Rebuild the base Workflow translations if not exists if( ! is_file(PATH_LANGUAGECONT . 'translation.en') ){ require_once ( "classes/model/Translation.php" ); @@ -563,8 +575,12 @@ $isControllerCall = true; } } + // var_dump(SYS_SYS); + // var_dump(SYS_TARGET); + // var_dump($isRestRequest); + // die; - if (!$isControllerCall && ! file_exists($phpFile)) { + if (!$isControllerCall && ! file_exists($phpFile) && ! $isRestRequest) { $_SESSION['phpFileNotFound'] = $_SERVER['REQUEST_URI']; header("location: /errors/error404.php?url=" . urlencode($_SERVER['REQUEST_URI'])); die; @@ -609,9 +625,9 @@ else { // this is the blank list to allow execute scripts with no login (without session started) $noLoginFiles = $noLoginFolders = array(); - $noLoginFiles[] = 'login'; + $noLoginFiles[] = 'login'; $noLoginFiles[] = 'authentication'; - $noLoginFiles[] = 'login_Ajax'; + $noLoginFiles[] = 'login_Ajax'; $noLoginFiles[] = 'dbInfo'; $noLoginFiles[] = 'sysLoginVerify'; $noLoginFiles[] = 'processes_Ajax'; @@ -619,7 +635,7 @@ $noLoginFiles[] = 'autoinstallProcesses'; $noLoginFiles[] = 'autoinstallPlugins'; $noLoginFiles[] = 'heartbeatStatus'; - $noLoginFiles[] = 'showLogoFile'; + $noLoginFiles[] = 'showLogoFile'; $noLoginFiles[] = 'forgotPassword'; $noLoginFiles[] = 'retrivePassword'; $noLoginFiles[] = 'defaultAjaxDynaform'; @@ -630,9 +646,13 @@ $noLoginFolders[] = 'installer'; // This sentence is used when you lost the Session - if ( !in_array(SYS_TARGET, $noLoginFiles) && !in_array(SYS_COLLECTION, $noLoginFolders) && $bWE != true && $collectionPlugin != 'services') { + if (! in_array(SYS_TARGET, $noLoginFiles) + && ! in_array(SYS_COLLECTION, $noLoginFolders) + && $bWE != true && $collectionPlugin != 'services' + && ! $isRestRequest + ) { $bRedirect = true; - + if (isset($_GET['sid'])) { G::LoadClass('sessions'); $oSessions = new Sessions(); @@ -660,7 +680,7 @@ else { $loginUrl = 'login/login'; // just set up the classic login } - + if (empty($_POST)) { header('location: ' . SYS_URI . $loginUrl . '?u=' . urlencode($_SERVER['REQUEST_URI'])); } @@ -686,8 +706,9 @@ $controller = new $controllerClass(); $controller->setHttpRequestData($_REQUEST); $controller->call($controllerAction); - } - else { + } elseif ($isRestRequest) { + G::dispatchRestService(SYS_TARGET); + } else { require_once $phpFile; } @@ -700,5 +721,5 @@ ob_end_flush(); if (DEBUG_TIME_LOG) { G::logTimeByPage(); //log this page - } + } }