summaryrefslogtreecommitdiff
path: root/extlib
diff options
context:
space:
mode:
Diffstat (limited to 'extlib')
-rw-r--r--extlib/DB/DataObject.php17
-rw-r--r--extlib/HTTP/Request.php1521
-rw-r--r--extlib/HTTP/Request/Listener.php106
-rw-r--r--extlib/MIME/Type.php523
-rw-r--r--extlib/MIME/Type/Extension.php298
-rw-r--r--extlib/MIME/Type/Parameter.php163
-rw-r--r--extlib/Net/URL2.php813
-rw-r--r--extlib/Services/oEmbed.php357
-rw-r--r--extlib/Services/oEmbed/Exception.php65
-rw-r--r--extlib/Services/oEmbed/Exception/NoSupport.php63
-rw-r--r--extlib/Services/oEmbed/Object.php126
-rw-r--r--extlib/Services/oEmbed/Object/Common.php139
-rw-r--r--extlib/Services/oEmbed/Object/Exception.php65
-rw-r--r--extlib/Services/oEmbed/Object/Link.php73
-rw-r--r--extlib/Services/oEmbed/Object/Photo.php89
-rw-r--r--extlib/Services/oEmbed/Object/Rich.php82
-rw-r--r--extlib/Services/oEmbed/Object/Video.php82
-rw-r--r--extlib/Stomp.php594
-rw-r--r--extlib/Stomp/Exception.php57
-rw-r--r--extlib/Stomp/Frame.php80
-rw-r--r--extlib/Stomp/Message.php37
-rw-r--r--extlib/Stomp/Message/Bytes.php47
-rw-r--r--extlib/Stomp/Message/Map.php55
-rw-r--r--extlib/Validate.php2167
-rw-r--r--extlib/facebook/facebook.php147
-rw-r--r--extlib/facebook/facebook_desktop.php2
-rwxr-xr-x[-rw-r--r--]extlib/facebook/facebookapi_php5_restlib.php1026
27 files changed, 7587 insertions, 1207 deletions
diff --git a/extlib/DB/DataObject.php b/extlib/DB/DataObject.php
index b1a1a4e21..0c6a13dc2 100644
--- a/extlib/DB/DataObject.php
+++ b/extlib/DB/DataObject.php
@@ -2357,6 +2357,8 @@ class DB_DataObject extends DB_DataObject_Overload
$t= explode(' ',microtime());
$_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1];
+
+ do {
if ($_DB_driver == 'DB') {
$result = $DB->query($string);
@@ -2374,8 +2376,19 @@ class DB_DataObject extends DB_DataObject_Overload
break;
}
}
-
-
+
+ // try to reconnect, at most 3 times
+ $again = false;
+ if (is_a($result, 'PEAR_Error')
+ AND $result->getCode() == DB_ERROR_NODBSELECTED
+ AND $cpt++<3) {
+ $DB->disconnect();
+ sleep(1);
+ $DB->connect($DB->dsn);
+ $again = true;
+ }
+
+ } while ($again);
if (is_a($result,'PEAR_Error')) {
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
diff --git a/extlib/HTTP/Request.php b/extlib/HTTP/Request.php
new file mode 100644
index 000000000..42eac3b14
--- /dev/null
+++ b/extlib/HTTP/Request.php
@@ -0,0 +1,1521 @@
+<?php
+/**
+ * Class for performing HTTP requests
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @copyright 2002-2007 Richard Heyes
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
+ * @link http://pear.php.net/package/HTTP_Request/
+ */
+
+/**
+ * PEAR and PEAR_Error classes (for error handling)
+ */
+require_once 'PEAR.php';
+/**
+ * Socket class
+ */
+require_once 'Net/Socket.php';
+/**
+ * URL handling class
+ */
+require_once 'Net/URL.php';
+
+/**#@+
+ * Constants for HTTP request methods
+ */
+define('HTTP_REQUEST_METHOD_GET', 'GET', true);
+define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
+define('HTTP_REQUEST_METHOD_POST', 'POST', true);
+define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
+define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
+define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
+define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP request error codes
+ */
+define('HTTP_REQUEST_ERROR_FILE', 1);
+define('HTTP_REQUEST_ERROR_URL', 2);
+define('HTTP_REQUEST_ERROR_PROXY', 4);
+define('HTTP_REQUEST_ERROR_REDIRECTS', 8);
+define('HTTP_REQUEST_ERROR_RESPONSE', 16);
+define('HTTP_REQUEST_ERROR_GZIP_METHOD', 32);
+define('HTTP_REQUEST_ERROR_GZIP_READ', 64);
+define('HTTP_REQUEST_ERROR_GZIP_DATA', 128);
+define('HTTP_REQUEST_ERROR_GZIP_CRC', 256);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP protocol versions
+ */
+define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
+define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
+/**#@-*/
+
+if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
+ /**
+ * Whether string functions are overloaded by their mbstring equivalents
+ */
+ define('HTTP_REQUEST_MBSTRING', true);
+} else {
+ /**
+ * @ignore
+ */
+ define('HTTP_REQUEST_MBSTRING', false);
+}
+
+/**
+ * Class for performing HTTP requests
+ *
+ * Simple example (fetches yahoo.com and displays it):
+ * <code>
+ * $a = &new HTTP_Request('http://www.yahoo.com/');
+ * $a->sendRequest();
+ * echo $a->getResponseBody();
+ * </code>
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.4
+ */
+class HTTP_Request
+{
+ /**#@+
+ * @access private
+ */
+ /**
+ * Instance of Net_URL
+ * @var Net_URL
+ */
+ var $_url;
+
+ /**
+ * Type of request
+ * @var string
+ */
+ var $_method;
+
+ /**
+ * HTTP Version
+ * @var string
+ */
+ var $_http;
+
+ /**
+ * Request headers
+ * @var array
+ */
+ var $_requestHeaders;
+
+ /**
+ * Basic Auth Username
+ * @var string
+ */
+ var $_user;
+
+ /**
+ * Basic Auth Password
+ * @var string
+ */
+ var $_pass;
+
+ /**
+ * Socket object
+ * @var Net_Socket
+ */
+ var $_sock;
+
+ /**
+ * Proxy server
+ * @var string
+ */
+ var $_proxy_host;
+
+ /**
+ * Proxy port
+ * @var integer
+ */
+ var $_proxy_port;
+
+ /**
+ * Proxy username
+ * @var string
+ */
+ var $_proxy_user;
+
+ /**
+ * Proxy password
+ * @var string
+ */
+ var $_proxy_pass;
+
+ /**
+ * Post data
+ * @var array
+ */
+ var $_postData;
+
+ /**
+ * Request body
+ * @var string
+ */
+ var $_body;
+
+ /**
+ * A list of methods that MUST NOT have a request body, per RFC 2616
+ * @var array
+ */
+ var $_bodyDisallowed = array('TRACE');
+
+ /**
+ * Methods having defined semantics for request body
+ *
+ * Content-Length header (indicating that the body follows, section 4.3 of
+ * RFC 2616) will be sent for these methods even if no body was added
+ *
+ * @var array
+ */
+ var $_bodyRequired = array('POST', 'PUT');
+
+ /**
+ * Files to post
+ * @var array
+ */
+ var $_postFiles = array();
+
+ /**
+ * Connection timeout.
+ * @var float
+ */
+ var $_timeout;
+
+ /**
+ * HTTP_Response object
+ * @var HTTP_Response
+ */
+ var $_response;
+
+ /**
+ * Whether to allow redirects
+ * @var boolean
+ */
+ var $_allowRedirects;
+
+ /**
+ * Maximum redirects allowed
+ * @var integer
+ */
+ var $_maxRedirects;
+
+ /**
+ * Current number of redirects
+ * @var integer
+ */
+ var $_redirects;
+
+ /**
+ * Whether to append brackets [] to array variables
+ * @var bool
+ */
+ var $_useBrackets = true;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether to save response body in response object property
+ * @var bool
+ */
+ var $_saveBody = true;
+
+ /**
+ * Timeout for reading from socket (array(seconds, microseconds))
+ * @var array
+ */
+ var $_readTimeout = null;
+
+ /**
+ * Options to pass to Net_Socket::connect. See stream_context_create
+ * @var array
+ */
+ var $_socketOptions = null;
+ /**#@-*/
+
+ /**
+ * Constructor
+ *
+ * Sets up the object
+ * @param string The url to fetch/access
+ * @param array Associative array of parameters which can have the following keys:
+ * <ul>
+ * <li>method - Method to use, GET, POST etc (string)</li>
+ * <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
+ * <li>user - Basic Auth username (string)</li>
+ * <li>pass - Basic Auth password (string)</li>
+ * <li>proxy_host - Proxy server host (string)</li>
+ * <li>proxy_port - Proxy server port (integer)</li>
+ * <li>proxy_user - Proxy auth username (string)</li>
+ * <li>proxy_pass - Proxy auth password (string)</li>
+ * <li>timeout - Connection timeout in seconds (float)</li>
+ * <li>allowRedirects - Whether to follow redirects or not (bool)</li>
+ * <li>maxRedirects - Max number of redirects to follow (integer)</li>
+ * <li>useBrackets - Whether to append [] to array variable names (bool)</li>
+ * <li>saveBody - Whether to save response body in response object property (bool)</li>
+ * <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
+ * <li>socketOptions - Options to pass to Net_Socket object (array)</li>
+ * </ul>
+ * @access public
+ */
+ function HTTP_Request($url = '', $params = array())
+ {
+ $this->_method = HTTP_REQUEST_METHOD_GET;
+ $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
+ $this->_requestHeaders = array();
+ $this->_postData = array();
+ $this->_body = null;
+
+ $this->_user = null;
+ $this->_pass = null;
+
+ $this->_proxy_host = null;
+ $this->_proxy_port = null;
+ $this->_proxy_user = null;
+ $this->_proxy_pass = null;
+
+ $this->_allowRedirects = false;
+ $this->_maxRedirects = 3;
+ $this->_redirects = 0;
+
+ $this->_timeout = null;
+ $this->_response = null;
+
+ foreach ($params as $key => $value) {
+ $this->{'_' . $key} = $value;
+ }
+
+ if (!empty($url)) {
+ $this->setURL($url);
+ }
+
+ // Default useragent
+ $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
+
+ // We don't do keep-alives by default
+ $this->addHeader('Connection', 'close');
+
+ // Basic authentication
+ if (!empty($this->_user)) {
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
+ }
+
+ // Proxy authentication (see bug #5913)
+ if (!empty($this->_proxy_user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
+ }
+
+ // Use gzip encoding if possible
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
+ $this->addHeader('Accept-Encoding', 'gzip');
+ }
+ }
+
+ /**
+ * Generates a Host header for HTTP/1.1 requests
+ *
+ * @access private
+ * @return string
+ */
+ function _generateHostHeader()
+ {
+ if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } else {
+ $host = $this->_url->host;
+ }
+
+ return $host;
+ }
+
+ /**
+ * Resets the object to its initial state (DEPRECATED).
+ * Takes the same parameters as the constructor.
+ *
+ * @param string $url The url to be requested
+ * @param array $params Associative array of parameters
+ * (see constructor for details)
+ * @access public
+ * @deprecated deprecated since 1.2, call the constructor if this is necessary
+ */
+ function reset($url, $params = array())
+ {
+ $this->HTTP_Request($url, $params);
+ }
+
+ /**
+ * Sets the URL to be requested
+ *
+ * @param string The url to be requested
+ * @access public
+ */
+ function setURL($url)
+ {
+ $this->_url = &new Net_URL($url, $this->_useBrackets);
+
+ if (!empty($this->_url->user) || !empty($this->_url->pass)) {
+ $this->setBasicAuth($this->_url->user, $this->_url->pass);
+ }
+
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
+ $this->addHeader('Host', $this->_generateHostHeader());
+ }
+
+ // set '/' instead of empty path rather than check later (see bug #8662)
+ if (empty($this->_url->path)) {
+ $this->_url->path = '/';
+ }
+ }
+
+ /**
+ * Returns the current request URL
+ *
+ * @return string Current request URL
+ * @access public
+ */
+ function getUrl()
+ {
+ return empty($this->_url)? '': $this->_url->getUrl();
+ }
+
+ /**
+ * Sets a proxy to be used
+ *
+ * @param string Proxy host
+ * @param int Proxy port
+ * @param string Proxy username
+ * @param string Proxy password
+ * @access public
+ */
+ function setProxy($host, $port = 8080, $user = null, $pass = null)
+ {
+ $this->_proxy_host = $host;
+ $this->_proxy_port = $port;
+ $this->_proxy_user = $user;
+ $this->_proxy_pass = $pass;
+
+ if (!empty($user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+ }
+
+ /**
+ * Sets basic authentication parameters
+ *
+ * @param string Username
+ * @param string Password
+ */
+ function setBasicAuth($user, $pass)
+ {
+ $this->_user = $user;
+ $this->_pass = $pass;
+
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+
+ /**
+ * Sets the method to be used, GET, POST etc.
+ *
+ * @param string Method to use. Use the defined constants for this
+ * @access public
+ */
+ function setMethod($method)
+ {
+ $this->_method = $method;
+ }
+
+ /**
+ * Sets the HTTP version to use, 1.0 or 1.1
+ *
+ * @param string Version to use. Use the defined constants for this
+ * @access public
+ */
+ function setHttpVer($http)
+ {
+ $this->_http = $http;
+ }
+
+ /**
+ * Adds a request header
+ *
+ * @param string Header name
+ * @param string Header value
+ * @access public
+ */
+ function addHeader($name, $value)
+ {
+ $this->_requestHeaders[strtolower($name)] = $value;
+ }
+
+ /**
+ * Removes a request header
+ *
+ * @param string Header name to remove
+ * @access public
+ */
+ function removeHeader($name)
+ {
+ if (isset($this->_requestHeaders[strtolower($name)])) {
+ unset($this->_requestHeaders[strtolower($name)]);
+ }
+ }
+
+ /**
+ * Adds a querystring parameter
+ *
+ * @param string Querystring parameter name
+ * @param string Querystring parameter value
+ * @param bool Whether the value is already urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ $this->_url->addQueryString($name, $value, $preencoded);
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string The querystring data. Should be of the format foo=bar&x=y etc
+ * @param bool Whether data is already urlencoded or not, default = already encoded
+ * @access public
+ */
+ function addRawQueryString($querystring, $preencoded = true)
+ {
+ $this->_url->addRawQueryString($querystring, $preencoded);
+ }
+
+ /**
+ * Adds postdata items
+ *
+ * @param string Post data name
+ * @param string Post data value
+ * @param bool Whether data is already urlencoded or not, default = not
+ * @access public
+ */
+ function addPostData($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->_postData[$name] = $value;
+ } else {
+ $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
+ }
+ }
+
+ /**
+ * Recursively applies the callback function to the value
+ *
+ * @param mixed Callback function
+ * @param mixed Value to process
+ * @access private
+ * @return mixed Processed value
+ */
+ function _arrayMapRecursive($callback, $value)
+ {
+ if (!is_array($value)) {
+ return call_user_func($callback, $value);
+ } else {
+ $map = array();
+ foreach ($value as $k => $v) {
+ $map[$k] = $this->_arrayMapRecursive($callback, $v);
+ }
+ return $map;
+ }
+ }
+
+ /**
+ * Adds a file to form-based file upload
+ *
+ * Used to emulate file upload via a HTML form. The method also sets
+ * Content-Type of HTTP request to 'multipart/form-data'.
+ *
+ * If you just want to send the contents of a file as the body of HTTP
+ * request you should use setBody() method.
+ *
+ * @access public
+ * @param string name of file-upload field
+ * @param mixed file name(s)
+ * @param mixed content-type(s) of file(s) being uploaded
+ * @return bool true on success
+ * @throws PEAR_Error
+ */
+ function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
+ {
+ if (!is_array($fileName) && !is_readable($fileName)) {
+ return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
+ } elseif (is_array($fileName)) {
+ foreach ($fileName as $name) {
+ if (!is_readable($name)) {
+ return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
+ }
+ }
+ }
+ $this->addHeader('Content-Type', 'multipart/form-data');
+ $this->_postFiles[$inputName] = array(
+ 'name' => $fileName,
+ 'type' => $contentType
+ );
+ return true;
+ }
+
+ /**
+ * Adds raw postdata (DEPRECATED)
+ *
+ * @param string The data
+ * @param bool Whether data is preencoded or not, default = already encoded
+ * @access public
+ * @deprecated deprecated since 1.3.0, method setBody() should be used instead
+ */
+ function addRawPostData($postdata, $preencoded = true)
+ {
+ $this->_body = $preencoded ? $postdata : urlencode($postdata);
+ }
+
+ /**
+ * Sets the request body (for POST, PUT and similar requests)
+ *
+ * @param string Request body
+ * @access public
+ */
+ function setBody($body)
+ {
+ $this->_body = $body;
+ }
+
+ /**
+ * Clears any postdata that has been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios.
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearPostData()
+ {
+ $this->_postData = null;
+ }
+
+ /**
+ * Appends a cookie to "Cookie:" header
+ *
+ * @param string $name cookie name
+ * @param string $value cookie value
+ * @access public
+ */
+ function addCookie($name, $value)
+ {
+ $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
+ $this->addHeader('Cookie', $cookies . $name . '=' . $value);
+ }
+
+ /**
+ * Clears any cookies that have been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearCookies()
+ {
+ $this->removeHeader('Cookie');
+ }
+
+ /**
+ * Sends the request
+ *
+ * @access public
+ * @param bool Whether to store response body in Response object property,
+ * set this to false if downloading a LARGE file and using a Listener
+ * @return mixed PEAR error on error, true otherwise
+ */
+ function sendRequest($saveBody = true)
+ {
+ if (!is_a($this->_url, 'Net_URL')) {
+ return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
+ }
+
+ $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
+ $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
+
+ if (strcasecmp($this->_url->protocol, 'https') == 0) {
+ // Bug #14127, don't try connecting to HTTPS sites without OpenSSL
+ if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) {
+ return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
+ HTTP_REQUEST_ERROR_URL);
+ } elseif (isset($this->_proxy_host)) {
+ return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
+ }
+ $host = 'ssl://' . $host;
+ }
+
+ // magic quotes may fuck up file uploads and chunked response processing
+ $magicQuotes = ini_get('magic_quotes_runtime');
+ ini_set('magic_quotes_runtime', false);
+
+ // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
+ // connection token to a proxy server...
+ if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
+ 'Keep-Alive' == $this->_requestHeaders['connection'])
+ {
+ $this->removeHeader('connection');
+ }
+
+ $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
+ (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
+ $sockets = &PEAR::getStaticProperty('HTTP_Request', 'sockets');
+ $sockKey = $host . ':' . $port;
+ unset($this->_sock);
+
+ // There is a connected socket in the "static" property?
+ if ($keepAlive && !empty($sockets[$sockKey]) &&
+ !empty($sockets[$sockKey]->fp))
+ {
+ $this->_sock =& $sockets[$sockKey];
+ $err = null;
+ } else {
+ $this->_notify('connect');
+ $this->_sock =& new Net_Socket();
+ $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
+ }
+ PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
+
+ if (!PEAR::isError($err)) {
+ if (!empty($this->_readTimeout)) {
+ $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
+ }
+
+ $this->_notify('sentRequest');
+
+ // Read the response
+ $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
+ $err = $this->_response->process(
+ $this->_saveBody && $saveBody,
+ HTTP_REQUEST_METHOD_HEAD != $this->_method
+ );
+
+ if ($keepAlive) {
+ $keepAlive = (isset($this->_response->_headers['content-length'])
+ || (isset($this->_response->_headers['transfer-encoding'])
+ && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
+ if ($keepAlive) {
+ if (isset($this->_response->_headers['connection'])) {
+ $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
+ } else {
+ $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
+ }
+ }
+ }
+ }
+
+ ini_set('magic_quotes_runtime', $magicQuotes);
+
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ if (!$keepAlive) {
+ $this->disconnect();
+ // Store the connected socket in "static" property
+ } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
+ $sockets[$sockKey] =& $this->_sock;
+ }
+
+ // Check for redirection
+ if ( $this->_allowRedirects
+ AND $this->_redirects <= $this->_maxRedirects
+ AND $this->getResponseCode() > 300
+ AND $this->getResponseCode() < 399
+ AND !empty($this->_response->_headers['location'])) {
+
+
+ $redirect = $this->_response->_headers['location'];
+
+ // Absolute URL
+ if (preg_match('/^https?:\/\//i', $redirect)) {
+ $this->_url = &new Net_URL($redirect);
+ $this->addHeader('Host', $this->_generateHostHeader());
+ // Absolute path
+ } elseif ($redirect{0} == '/') {
+ $this->_url->path = $redirect;
+
+ // Relative path
+ } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $redirect = Net_URL::resolvePath($redirect);
+ $this->_url->path = $redirect;
+
+ // Filename, no path
+ } else {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $this->_url->path = $redirect;
+ }
+
+ $this->_redirects++;
+ return $this->sendRequest($saveBody);
+
+ // Too many redirects
+ } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnect the socket, if connected. Only useful if using Keep-Alive.
+ *
+ * @access public
+ */
+ function disconnect()
+ {
+ if (!empty($this->_sock) && !empty($this->_sock->fp)) {
+ $this->_notify('disconnect');
+ $this->_sock->disconnect();
+ }
+ }
+
+ /**
+ * Returns the response code
+ *
+ * @access public
+ * @return mixed Response code, false if not set
+ */
+ function getResponseCode()
+ {
+ return isset($this->_response->_code) ? $this->_response->_code : false;
+ }
+
+ /**
+ * Returns the response reason phrase
+ *
+ * @access public
+ * @return mixed Response reason phrase, false if not set
+ */
+ function getResponseReason()
+ {
+ return isset($this->_response->_reason) ? $this->_response->_reason : false;
+ }
+
+ /**
+ * Returns either the named header or all if no name given
+ *
+ * @access public
+ * @param string The header name to return, do not set to get all headers
+ * @return mixed either the value of $headername (false if header is not present)
+ * or an array of all headers
+ */
+ function getResponseHeader($headername = null)
+ {
+ if (!isset($headername)) {
+ return isset($this->_response->_headers)? $this->_response->_headers: array();
+ } else {
+ $headername = strtolower($headername);
+ return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
+ }
+ }
+
+ /**
+ * Returns the body of the response
+ *
+ * @access public
+ * @return mixed response body, false if not set
+ */
+ function getResponseBody()
+ {
+ return isset($this->_response->_body) ? $this->_response->_body : false;
+ }
+
+ /**
+ * Returns cookies set in response
+ *
+ * @access public
+ * @return mixed array of response cookies, false if none are present
+ */
+ function getResponseCookies()
+ {
+ return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
+ }
+
+ /**
+ * Builds the request string
+ *
+ * @access private
+ * @return string The request string
+ */
+ function _buildRequest()
+ {
+ $separator = ini_get('arg_separator.output');
+ ini_set('arg_separator.output', '&');
+ $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
+ ini_set('arg_separator.output', $separator);
+
+ $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
+ $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
+ $path = $this->_url->path . $querystring;
+ $url = $host . $port . $path;
+
+ if (!strlen($url)) {
+ $url = '/';
+ }
+
+ $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
+
+ if (in_array($this->_method, $this->_bodyDisallowed) ||
+ (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
+ (empty($this->_postData) && empty($this->_postFiles)))))
+ {
+ $this->removeHeader('Content-Type');
+ } else {
+ if (empty($this->_requestHeaders['content-type'])) {
+ // Add default content-type
+ $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
+ } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
+ $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
+ $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
+ }
+ }
+
+ // Request Headers
+ if (!empty($this->_requestHeaders)) {
+ foreach ($this->_requestHeaders as $name => $value) {
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+ $request .= $canonicalName . ': ' . $value . "\r\n";
+ }
+ }
+
+ // Method does not allow a body, simply add a final CRLF
+ if (in_array($this->_method, $this->_bodyDisallowed)) {
+
+ $request .= "\r\n";
+
+ // Post data if it's an array
+ } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
+ (!empty($this->_postData) || !empty($this->_postFiles))) {
+
+ // "normal" POST request
+ if (!isset($boundary)) {
+ $postdata = implode('&', array_map(
+ create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
+ $this->_flattenArray('', $this->_postData)
+ ));
+
+ // multipart request, probably with file uploads
+ } else {
+ $postdata = '';
+ if (!empty($this->_postData)) {
+ $flatData = $this->_flattenArray('', $this->_postData);
+ foreach ($flatData as $item) {
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
+ $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
+ }
+ }
+ foreach ($this->_postFiles as $name => $value) {
+ if (is_array($value['name'])) {
+ $varname = $name . ($this->_useBrackets? '[]': '');
+ } else {
+ $varname = $name;
+ $value['name'] = array($value['name']);
+ }
+ foreach ($value['name'] as $key => $filename) {
+ $fp = fopen($filename, 'r');
+ $basename = basename($filename);
+ $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
+
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
+ $postdata .= "\r\nContent-Type: " . $type;
+ $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n";
+ fclose($fp);
+ }
+ }
+ $postdata .= '--' . $boundary . "--\r\n";
+ }
+ $request .= 'Content-Length: ' .
+ (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
+ "\r\n\r\n";
+ $request .= $postdata;
+
+ // Explicitly set request body
+ } elseif (0 < strlen($this->_body)) {
+
+ $request .= 'Content-Length: ' .
+ (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
+ "\r\n\r\n";
+ $request .= $this->_body;
+
+ // No body: send a Content-Length header nonetheless (request #12900),
+ // but do that only for methods that require a body (bug #14740)
+ } else {
+
+ if (in_array($this->_method, $this->_bodyRequired)) {
+ $request .= "Content-Length: 0\r\n";
+ }
+ $request .= "\r\n";
+ }
+
+ return $request;
+ }
+
+ /**
+ * Helper function to change the (probably multidimensional) associative array
+ * into the simple one.
+ *
+ * @param string name for item
+ * @param mixed item's values
+ * @return array array with the following items: array('item name', 'item value');
+ * @access private
+ */
+ function _flattenArray($name, $values)
+ {
+ if (!is_array($values)) {
+ return array(array($name, $values));
+ } else {
+ $ret = array();
+ foreach ($values as $k => $v) {
+ if (empty($name)) {
+ $newName = $k;
+ } elseif ($this->_useBrackets) {
+ $newName = $name . '[' . $k . ']';
+ } else {
+ $newName = $name;
+ }
+ $ret = array_merge($ret, $this->_flattenArray($newName, $v));
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * Adds a Listener to the list of listeners that are notified of
+ * the object's events
+ *
+ * Events sent by HTTP_Request object
+ * - 'connect': on connection to server
+ * - 'sentRequest': after the request was sent
+ * - 'disconnect': on disconnection from server
+ *
+ * Events sent by HTTP_Response object
+ * - 'gotHeaders': after receiving response headers (headers are passed in $data)
+ * - 'tick': on receiving a part of response body (the part is passed in $data)
+ * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
+ * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
+ *
+ * @param HTTP_Request_Listener listener to attach
+ * @return boolean whether the listener was successfully attached
+ * @access public
+ */
+ function attach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param HTTP_Request_Listener listener to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::attach()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+}
+
+
+/**
+ * Response class to complement the Request class
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.4
+ */
+class HTTP_Response
+{
+ /**
+ * Socket object
+ * @var Net_Socket
+ */
+ var $_sock;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $_protocol;
+
+ /**
+ * Return code
+ * @var string
+ */
+ var $_code;
+
+ /**
+ * Response reason phrase
+ * @var string
+ */
+ var $_reason;
+
+ /**
+ * Response headers
+ * @var array
+ */
+ var $_headers;
+
+ /**
+ * Cookies set in response
+ * @var array
+ */
+ var $_cookies;
+
+ /**
+ * Response body
+ * @var string
+ */
+ var $_body = '';
+
+ /**
+ * Used by _readChunked(): remaining length of the current chunk
+ * @var string
+ */
+ var $_chunkLength = 0;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Bytes left to read from message-body
+ * @var null|int
+ */
+ var $_toRead;
+
+ /**
+ * Constructor
+ *
+ * @param Net_Socket socket to read the response from
+ * @param array listeners attached to request
+ */
+ function HTTP_Response(&$sock, &$listeners)
+ {
+ $this->_sock =& $sock;
+ $this->_listeners =& $listeners;
+ }
+
+
+ /**
+ * Processes a HTTP response
+ *
+ * This extracts response code, headers, cookies and decodes body if it
+ * was encoded in some way
+ *
+ * @access public
+ * @param bool Whether to store response body in object property, set
+ * this to false if downloading a LARGE file and using a Listener.
+ * This is assumed to be true if body is gzip-encoded.
+ * @param bool Whether the response can actually have a message-body.
+ * Will be set to false for HEAD requests.
+ * @throws PEAR_Error
+ * @return mixed true on success, PEAR_Error in case of malformed response
+ */
+ function process($saveBody = true, $canHaveBody = true)
+ {
+ do {
+ $line = $this->_sock->readLine();
+ if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) {
+ return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
+ } else {
+ $this->_protocol = $s[1];
+ $this->_code = intval($s[2]);
+ $this->_reason = empty($s[3])? null: $s[3];
+ }
+ while ('' !== ($header = $this->_sock->readLine())) {
+ $this->_processHeader($header);
+ }
+ } while (100 == $this->_code);
+
+ $this->_notify('gotHeaders', $this->_headers);
+
+ // RFC 2616, section 4.4:
+ // 1. Any response message which "MUST NOT" include a message-body ...
+ // is always terminated by the first empty line after the header fields
+ // 3. ... If a message is received with both a
+ // Transfer-Encoding header field and a Content-Length header field,
+ // the latter MUST be ignored.
+ $canHaveBody = $canHaveBody && $this->_code >= 200 &&
+ $this->_code != 204 && $this->_code != 304;
+
+ // If response body is present, read it and decode
+ $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
+ $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
+ $hasBody = false;
+ if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
+ 0 != $this->_headers['content-length']))
+ {
+ if ($chunked || !isset($this->_headers['content-length'])) {
+ $this->_toRead = null;
+ } else {
+ $this->_toRead = $this->_headers['content-length'];
+ }
+ while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
+ if ($chunked) {
+ $data = $this->_readChunked();
+ } elseif (is_null($this->_toRead)) {
+ $data = $this->_sock->read(4096);
+ } else {
+ $data = $this->_sock->read(min(4096, $this->_toRead));
+ $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+ }
+ if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
+ break;
+ } else {
+ $hasBody = true;
+ if ($saveBody || $gzipped) {
+ $this->_body .= $data;
+ }
+ $this->_notify($gzipped? 'gzTick': 'tick', $data);
+ }
+ }
+ }
+
+ if ($hasBody) {
+ // Uncompress the body if needed
+ if ($gzipped) {
+ $body = $this->_decodeGzip($this->_body);
+ if (PEAR::isError($body)) {
+ return $body;
+ }
+ $this->_body = $body;
+ $this->_notify('gotBody', $this->_body);
+ } else {
+ $this->_notify('gotBody');
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Processes the response header
+ *
+ * @access private
+ * @param string HTTP header
+ */
+ function _processHeader($header)
+ {
+ if (false === strpos($header, ':')) {
+ return;
+ }
+ list($headername, $headervalue) = explode(':', $header, 2);
+ $headername = strtolower($headername);
+ $headervalue = ltrim($headervalue);
+
+ if ('set-cookie' != $headername) {
+ if (isset($this->_headers[$headername])) {
+ $this->_headers[$headername] .= ',' . $headervalue;
+ } else {
+ $this->_headers[$headername] = $headervalue;
+ }
+ } else {
+ $this->_parseCookie($headervalue);
+ }
+ }
+
+
+ /**
+ * Parse a Set-Cookie header to fill $_cookies array
+ *
+ * @access private
+ * @param string value of Set-Cookie header
+ */
+ function _parseCookie($headervalue)
+ {
+ $cookie = array(
+ 'expires' => null,
+ 'domain' => null,
+ 'path' => null,
+ 'secure' => false
+ );
+
+ // Only a name=value pair
+ if (!strpos($headervalue, ';')) {
+ $pos = strpos($headervalue, '=');
+ $cookie['name'] = trim(substr($headervalue, 0, $pos));
+ $cookie['value'] = trim(substr($headervalue, $pos + 1));
+
+ // Some optional parameters are supplied
+ } else {
+ $elements = explode(';', $headervalue);
+ $pos = strpos($elements[0], '=');
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+ for ($i = 1; $i < count($elements); $i++) {
+ if (false === strpos($elements[$i], '=')) {
+ $elName = trim($elements[$i]);
+ $elValue = null;
+ } else {
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+ }
+ $elName = strtolower($elName);
+ if ('secure' == $elName) {
+ $cookie['secure'] = true;
+ } elseif ('expires' == $elName) {
+ $cookie['expires'] = str_replace('"', '', $elValue);
+ } elseif ('path' == $elName || 'domain' == $elName) {
+ $cookie[$elName] = urldecode($elValue);
+ } else {
+ $cookie[$elName] = $elValue;
+ }
+ }
+ }
+ $this->_cookies[] = $cookie;
+ }
+
+
+ /**
+ * Read a part of response body encoded with chunked Transfer-Encoding
+ *
+ * @access private
+ * @return string
+ */
+ function _readChunked()
+ {
+ // at start of the next chunk?
+ if (0 == $this->_chunkLength) {
+ $line = $this->_sock->readLine();
+ if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+ $this->_chunkLength = hexdec($matches[1]);
+ // Chunk with zero length indicates the end
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // make this an eof()
+ return '';
+ }
+ } else {
+ return '';
+ }
+ }
+ $data = $this->_sock->read($this->_chunkLength);
+ $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // Trailing CRLF
+ }
+ return $data;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::_notify()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+
+
+ /**
+ * Decodes the message-body encoded by gzip
+ *
+ * The real decoding work is done by gzinflate() built-in function, this
+ * method only parses the header and checks data for compliance with
+ * RFC 1952
+ *
+ * @access private
+ * @param string gzip-encoded data
+ * @return string decoded data
+ */
+ function _decodeGzip($data)
+ {
+ if (HTTP_REQUEST_MBSTRING) {
+ $oldEncoding = mb_internal_encoding();
+ mb_internal_encoding('iso-8859-1');
+ }
+ $length = strlen($data);
+ // If it doesn't look like gzip-encoded data, don't bother
+ if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
+ return $data;
+ }
+ $method = ord(substr($data, 2, 1));
+ if (8 != $method) {
+ return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
+ }
+ $flags = ord(substr($data, 3, 1));
+ if ($flags & 224) {
+ return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+
+ // header is 10 bytes minimum. may be longer, though.
+ $headerLength = 10;
+ // extra fields, need to skip 'em
+ if ($flags & 4) {
+ if ($length - $headerLength - 2 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $extraLength = unpack('v', substr($data, 10, 2));
+ if ($length - $headerLength - 2 - $extraLength[1] < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $extraLength[1] + 2;
+ }
+ // file name, need to skip that
+ if ($flags & 8) {
+ if ($length - $headerLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $filenameLength = strpos(substr($data, $headerLength), chr(0));
+ if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $filenameLength + 1;
+ }
+ // comment, need to skip that also
+ if ($flags & 16) {
+ if ($length - $headerLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $commentLength = strpos(substr($data, $headerLength), chr(0));
+ if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $commentLength + 1;
+ }
+ // have a CRC for header. let's check
+ if ($flags & 1) {
+ if ($length - $headerLength - 2 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
+ $crcStored = unpack('v', substr($data, $headerLength, 2));
+ if ($crcReal != $crcStored[1]) {
+ return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+ }
+ $headerLength += 2;
+ }
+ // unpacked data CRC and size at the end of encoded data
+ $tmp = unpack('V2', substr($data, -8));
+ $dataCrc = $tmp[1];
+ $dataSize = $tmp[2];
+
+ // finally, call the gzinflate() function
+ // don't pass $dataSize to gzinflate, see bugs #13135, #14370
+ $unpacked = gzinflate(substr($data, $headerLength, -8));
+ if (false === $unpacked) {
+ return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
+ } elseif ($dataSize != strlen($unpacked)) {
+ return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
+ } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
+ return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+ }
+ if (HTTP_REQUEST_MBSTRING) {
+ mb_internal_encoding($oldEncoding);
+ }
+ return $unpacked;
+ }
+} // End class HTTP_Response
+?>
diff --git a/extlib/HTTP/Request/Listener.php b/extlib/HTTP/Request/Listener.php
new file mode 100644
index 000000000..b4fe444b3
--- /dev/null
+++ b/extlib/HTTP/Request/Listener.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Listener for HTTP_Request and HTTP_Response objects
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Alexey Borzov <avb@php.net>
+ * @copyright 2002-2007 Richard Heyes
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Listener.php,v 1.3 2007/05/18 10:33:31 avb Exp $
+ * @link http://pear.php.net/package/HTTP_Request/
+ */
+
+/**
+ * Listener for HTTP_Request and HTTP_Response objects
+ *
+ * This class implements the Observer part of a Subject-Observer
+ * design pattern.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.4
+ */
+class HTTP_Request_Listener
+{
+ /**
+ * A listener's identifier
+ * @var string
+ */
+ var $_id;
+
+ /**
+ * Constructor, sets the object's identifier
+ *
+ * @access public
+ */
+ function HTTP_Request_Listener()
+ {
+ $this->_id = md5(uniqid('http_request_', 1));
+ }
+
+
+ /**
+ * Returns the listener's identifier
+ *
+ * @access public
+ * @return string
+ */
+ function getId()
+ {
+ return $this->_id;
+ }
+
+
+ /**
+ * This method is called when Listener is notified of an event
+ *
+ * @access public
+ * @param object an object the listener is attached to
+ * @param string Event name
+ * @param mixed Additional data
+ * @abstract
+ */
+ function update(&$subject, $event, $data = null)
+ {
+ echo "Notified of event: '$event'\n";
+ if (null !== $data) {
+ echo "Additional data: ";
+ var_dump($data);
+ }
+ }
+}
+?>
diff --git a/extlib/MIME/Type.php b/extlib/MIME/Type.php
new file mode 100644
index 000000000..c335f8d92
--- /dev/null
+++ b/extlib/MIME/Type.php
@@ -0,0 +1,523 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002, 2008 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Type.php,v 1.6 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+$_fileCmd = 'file';
+
+/**
+ * Class for working with MIME types
+ *
+ * @category MIME
+ * @package MIME_Type
+ * @license PHP License 3.0
+ * @version 1.2.0
+ * @link http://pear.php.net/package/MIME_Type
+ * @author Ian Eure <ieure@php.net>
+ */
+class MIME_Type
+{
+ /**
+ * The MIME media type
+ *
+ * @var string
+ */
+ var $media = '';
+
+ /**
+ * The MIME media sub-type
+ *
+ * @var string
+ */
+ var $subType = '';
+
+ /**
+ * Optional MIME parameters
+ *
+ * @var array
+ */
+ var $parameters = array();
+
+ /**
+ * List of valid media types.
+ * A media type is the string in front of the slash.
+ * The media type of "text/xml" would be "text".
+ *
+ * @var array
+ */
+ var $validMediaTypes = array(
+ 'text',
+ 'image',
+ 'audio',
+ 'video',
+ 'application',
+ 'multipart',
+ 'message'
+ );
+
+
+ /**
+ * Constructor.
+ *
+ * If $type is set, if will be parsed and the appropriate class vars set.
+ * If not, you get an empty class.
+ * This is useful, but not quite as useful as parsing a type.
+ *
+ * @param string $type MIME type
+ *
+ * @return void
+ */
+ function MIME_Type($type = false)
+ {
+ if ($type) {
+ $this->parse($type);
+ }
+ }
+
+
+ /**
+ * Parse a mime-type and set the class variables.
+ *
+ * @param string $type MIME type to parse
+ *
+ * @return void
+ */
+ function parse($type)
+ {
+ $this->media = $this->getMedia($type);
+ $this->subType = $this->getSubType($type);
+ $this->parameters = array();
+ if (MIME_Type::hasParameters($type)) {
+ require_once 'MIME/Type/Parameter.php';
+ foreach (MIME_Type::getParameters($type) as $param) {
+ $param = new MIME_Type_Parameter($param);
+ $this->parameters[$param->name] = $param;
+ }
+ }
+ }
+
+
+ /**
+ * Does this type have any parameters?
+ *
+ * @param string $type MIME type to check
+ *
+ * @return boolean true if $type has parameters, false otherwise
+ * @static
+ */
+ function hasParameters($type)
+ {
+ if (strstr($type, ';')) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Get a MIME type's parameters
+ *
+ * @param string $type MIME type to get parameters of
+ *
+ * @return array $type's parameters
+ * @static
+ */
+ function getParameters($type)
+ {
+ $params = array();
+ $tmp = explode(';', $type);
+ for ($i = 1; $i < count($tmp); $i++) {
+ $params[] = trim($tmp[$i]);
+ }
+ return $params;
+ }
+
+
+ /**
+ * Strip parameters from a MIME type string.
+ *
+ * @param string $type MIME type string
+ *
+ * @return string MIME type with parameters removed
+ * @static
+ */
+ function stripParameters($type)
+ {
+ if (strstr($type, ';')) {
+ return substr($type, 0, strpos($type, ';'));
+ }
+ return $type;
+ }
+
+
+ /**
+ * Removes comments from a media type, subtype or parameter.
+ *
+ * @param string $string String to strip comments from
+ * @param string &$comment Comment is stored in there.
+ *
+ * @return string String without comments
+ * @static
+ */
+ function stripComments($string, &$comment)
+ {
+ if (strpos($string, '(') === false) {
+ return $string;
+ }
+
+ $inquote = false;
+ $quoting = false;
+ $incomment = 0;
+ $newstring = '';
+
+ for ($n = 0; $n < strlen($string); $n++) {
+ if ($quoting) {
+ if ($incomment == 0) {
+ $newstring .= $string[$n];
+ } else if ($comment !== null) {
+ $comment .= $string[$n];
+ }
+ $quoting = false;
+ } else if ($string[$n] == '\\') {
+ $quoting = true;
+ } else if (!$inquote && $incomment > 0 && $string[$n] == ')') {
+ $incomment--;
+ if ($incomment == 0 && $comment !== null) {
+ $comment .= ' ';
+ }
+ } else if (!$inquote && $string[$n] == '(') {
+ $incomment++;
+ } else if ($string[$n] == '"') {
+ if ($inquote) {
+ $inquote = false;
+ } else {
+ $inquote = true;
+ }
+ } else if ($incomment == 0) {
+ $newstring .= $string[$n];
+ } else if ($comment !== null) {
+ $comment .= $string[$n];
+ }
+ }
+
+ if ($comment !== null) {
+ $comment = trim($comment);
+ }
+
+ return $newstring;
+ }
+
+
+ /**
+ * Get a MIME type's media
+ *
+ * @note 'media' refers to the portion before the first slash
+ *
+ * @param string $type MIME type to get media of
+ *
+ * @return string $type's media
+ * @static
+ */
+ function getMedia($type)
+ {
+ $tmp = explode('/', $type);
+ return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+ }
+
+
+ /**
+ * Get a MIME type's subtype
+ *
+ * @param string $type MIME type to get subtype of
+ *
+ * @return string $type's subtype, null if invalid mime type
+ * @static
+ */
+ function getSubType($type)
+ {
+ $tmp = explode('/', $type);
+ if (!isset($tmp[1])) {
+ return null;
+ }
+ $tmp = explode(';', $tmp[1]);
+ return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+ }
+
+
+ /**
+ * Create a textual MIME type from object values
+ *
+ * This function performs the opposite function of parse().
+ *
+ * @return string MIME type string
+ */
+ function get()
+ {
+ $type = strtolower($this->media . '/' . $this->subType);
+ if (count($this->parameters)) {
+ foreach ($this->parameters as $key => $null) {
+ $type .= '; ' . $this->parameters[$key]->get();
+ }
+ }
+ return $type;
+ }
+
+
+ /**
+ * Is this type experimental?
+ *
+ * @note Experimental types are denoted by a leading 'x-' in the media or
+ * subtype, e.g. text/x-vcard or x-world/x-vrml.
+ *
+ * @param string $type MIME type to check
+ *
+ * @return boolean true if $type is experimental, false otherwise
+ * @static
+ */
+ function isExperimental($type)
+ {
+ if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' ||
+ substr(MIME_Type::getSubType($type), 0, 2) == 'x-') {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Is this a vendor MIME type?
+ *
+ * @note Vendor types are denoted with a leading 'vnd. in the subtype.
+ *
+ * @param string $type MIME type to check
+ *
+ * @return boolean true if $type is a vendor type, false otherwise
+ * @static
+ */
+ function isVendor($type)
+ {
+ if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Is this a wildcard type?
+ *
+ * @param string $type MIME type to check
+ *
+ * @return boolean true if $type is a wildcard, false otherwise
+ * @static
+ */
+ function isWildcard($type)
+ {
+ if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Perform a wildcard match on a MIME type
+ *
+ * Example:
+ * MIME_Type::wildcardMatch('image/*', 'image/png')
+ *
+ * @param string $card Wildcard to check against
+ * @param string $type MIME type to check
+ *
+ * @return boolean true if there was a match, false otherwise
+ * @static
+ */
+ function wildcardMatch($card, $type)
+ {
+ if (!MIME_Type::isWildcard($card)) {
+ return false;
+ }
+
+ if ($card == '*/*') {
+ return true;
+ }
+
+ if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Add a parameter to this type
+ *
+ * @param string $name Attribute name
+ * @param string $value Attribute value
+ * @param string $comment Comment for this parameter
+ *
+ * @return void
+ */
+ function addParameter($name, $value, $comment = false)
+ {
+ $tmp = new MIME_Type_Parameter();
+
+ $tmp->name = $name;
+ $tmp->value = $value;
+ $tmp->comment = $comment;
+ $this->parameters[$name] = $tmp;
+ }
+
+
+ /**
+ * Remove a parameter from this type
+ *
+ * @param string $name Parameter name
+ *
+ * @return void
+ */
+ function removeParameter($name)
+ {
+ unset($this->parameters[$name]);
+ }
+
+
+ /**
+ * Autodetect a file's MIME-type
+ *
+ * This function may be called staticly.
+ *
+ * @internal Tries to use fileinfo extension at first. If that
+ * does not work, mime_magic is used. If this is also not available
+ * or does not succeed, "file" command is tried to be executed with
+ * System_Command. When that fails, too, then we use our in-built
+ * extension-to-mimetype-mapping list.
+ *
+ * @param string $file Path to the file to get the type of
+ * @param bool $params Append MIME parameters if true
+ *
+ * @return string $file's MIME-type on success, PEAR_Error otherwise
+ *
+ * @since 1.0.0beta1
+ * @static
+ */
+ function autoDetect($file, $params = false)
+ {
+ // Sanity checks
+ if (!file_exists($file)) {
+ return PEAR::raiseError("File \"$file\" doesn't exist");
+ }
+
+ if (!is_readable($file)) {
+ return PEAR::raiseError("File \"$file\" is not readable");
+ }
+
+ if (function_exists('finfo_file')) {
+ $finfo = finfo_open(FILEINFO_MIME);
+ $type = finfo_file($finfo, $file);
+ finfo_close($finfo);
+ if ($type !== false && $type !== '') {
+ return MIME_Type::_handleDetection($type, $params);
+ }
+ }
+
+ if (function_exists('mime_content_type')) {
+ $type = mime_content_type($file);
+ if ($type !== false && $type !== '') {
+ return MIME_Type::_handleDetection($type, $params);
+ }
+ }
+
+ @include_once 'System/Command.php';
+ if (class_exists('System_Command')) {
+ return MIME_Type::_handleDetection(
+ MIME_Type::_fileAutoDetect($file),
+ $params
+ );
+ }
+
+ require_once 'MIME/Type/Extension.php';
+ $mte = new MIME_Type_Extension();
+ return $mte->getMIMEType($file);
+ }
+
+
+ /**
+ * Handles a detected MIME type and modifies it if necessary.
+ *
+ * @param string $type MIME Type of a file
+ * @param bool $params Append MIME parameters if true
+ *
+ * @return string $file's MIME-type on success, PEAR_Error otherwise
+ */
+ function _handleDetection($type, $params)
+ {
+ // _fileAutoDetect() may have returned an error.
+ if (PEAR::isError($type)) {
+ return $type;
+ }
+
+ // Don't return an empty string
+ if (!$type || !strlen($type)) {
+ return PEAR::raiseError("Sorry, couldn't determine file type.");
+ }
+
+ // Strip parameters if present & requested
+ if (MIME_Type::hasParameters($type) && !$params) {
+ $type = MIME_Type::stripParameters($type);
+ }
+
+ return $type;
+ }
+
+
+ /**
+ * Autodetect a file's MIME-type with 'file' and System_Command
+ *
+ * This function may be called staticly.
+ *
+ * @param string $file Path to the file to get the type of
+ *
+ * @return string $file's MIME-type
+ *
+ * @since 1.0.0beta1
+ * @static
+ */
+ function _fileAutoDetect($file)
+ {
+ $cmd = new System_Command();
+
+ // Make sure we have the 'file' command.
+ $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+ if (!$cmd->which($fileCmd)) {
+ unset($cmd);
+ return PEAR::raiseError("Can't find file command \"{$fileCmd}\"");
+ }
+
+ $cmd->pushCommand($fileCmd, "-bi " . escapeshellarg($file));
+ $res = $cmd->execute();
+ unset($cmd);
+
+ return $res;
+ }
+}
+
diff --git a/extlib/MIME/Type/Extension.php b/extlib/MIME/Type/Extension.php
new file mode 100644
index 000000000..1987e2a10
--- /dev/null
+++ b/extlib/MIME/Type/Extension.php
@@ -0,0 +1,298 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2009 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Christian Schmidt <schmidt@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Extension.php,v 1.1 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Class for mapping file extensions to MIME types.
+ *
+ * @category MIME
+ * @package MIME_Type
+ * @author Christian Schmidt <schmidt@php.net>
+ * @license PHP License 3.0
+ * @version 1.2.0
+ * @link http://pear.php.net/package/MIME_Type
+ */
+class MIME_Type_Extension
+{
+ /**
+ * Mapping between file extension and MIME type.
+ *
+ * @internal The array is sorted alphabetically by value and with primary
+ * extension first. Be careful about not adding duplicate keys - PHP
+ * silently ignores duplicates. The following command can be used for
+ * checking for duplicates:
+ * grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d
+ * application/octet-stream is generally used as fallback when no other
+ * MIME-type can be found, but the array does not contain a lot of such
+ * unknown extension. One entry exists, though, to allow detection of
+ * file extension for this MIME-type.
+ *
+ * @var array
+ */
+ var $extensionToType = array (
+ 'ez' => 'application/andrew-inset',
+ 'atom' => 'application/atom+xml',
+ 'jar' => 'application/java-archive',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'mathml' => 'application/mathml+xml',
+ 'doc' => 'application/msword',
+ 'dat' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'ogg' => 'application/ogg',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'rdf' => 'application/rdf+xml',
+ 'rss' => 'application/rss+xml',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'gram' => 'application/srgs',
+ 'grxml' => 'application/srgs+xml',
+ 'kml' => 'application/vnd.google-earth.kml+xml',
+ 'kmz' => 'application/vnd.google-earth.kmz',
+ 'mif' => 'application/vnd.mif',
+ 'xul' => 'application/vnd.mozilla.xul+xml',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlb' => 'application/vnd.ms-excel',
+ 'xlt' => 'application/vnd.ms-excel',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+ 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+ 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
+ 'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
+ 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
+ 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+ 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+ 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+ 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'pps' => 'application/vnd.ms-powerpoint',
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
+ 'odb' => 'application/vnd.oasis.opendocument.database',
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+ 'odi' => 'application/vnd.oasis.opendocument.image',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
+ 'oth' => 'application/vnd.oasis.opendocument.text-web',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'vsd' => 'application/vnd.visio',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'wmlsc' => 'application/vnd.wap.wmlscriptc',
+ 'vxml' => 'application/voicexml+xml',
+ 'bcpio' => 'application/x-bcpio',
+ 'vcd' => 'application/x-cdlink',
+ 'pgn' => 'application/x-chess-pgn',
+ 'cpio' => 'application/x-cpio',
+ 'csh' => 'application/x-csh',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'spl' => 'application/x-futuresplash',
+ 'tgz' => 'application/x-gtar',
+ 'gtar' => 'application/x-gtar',
+ 'hdf' => 'application/x-hdf',
+ 'js' => 'application/x-javascript',
+ 'skp' => 'application/x-koan',
+ 'skd' => 'application/x-koan',
+ 'skt' => 'application/x-koan',
+ 'skm' => 'application/x-koan',
+ 'latex' => 'application/x-latex',
+ 'nc' => 'application/x-netcdf',
+ 'cdf' => 'application/x-netcdf',
+ 'sh' => 'application/x-sh',
+ 'shar' => 'application/x-shar',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'sv4cpio' => 'application/x-sv4cpio',
+ 'sv4crc' => 'application/x-sv4crc',
+ 'tar' => 'application/x-tar',
+ 'tcl' => 'application/x-tcl',
+ 'tex' => 'application/x-tex',
+ 'texinfo' => 'application/x-texinfo',
+ 'texi' => 'application/x-texinfo',
+ 't' => 'application/x-troff',
+ 'tr' => 'application/x-troff',
+ 'roff' => 'application/x-troff',
+ 'man' => 'application/x-troff-man',
+ 'me' => 'application/x-troff-me',
+ 'ms' => 'application/x-troff-ms',
+ 'ustar' => 'application/x-ustar',
+ 'src' => 'application/x-wais-source',
+ 'xhtml' => 'application/xhtml+xml',
+ 'xht' => 'application/xhtml+xml',
+ 'xslt' => 'application/xslt+xml',
+ 'xml' => 'application/xml',
+ 'xsl' => 'application/xml',
+ 'dtd' => 'application/xml-dtd',
+ 'zip' => 'application/zip',
+ 'au' => 'audio/basic',
+ 'snd' => 'audio/basic',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'kar' => 'audio/midi',
+ 'mpga' => 'audio/mpeg',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'm3u' => 'audio/x-mpegurl',
+ 'wma' => 'audio/x-ms-wma',
+ 'wax' => 'audio/x-ms-wax',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'ra' => 'audio/x-pn-realaudio',
+ 'rm' => 'application/vnd.rn-realmedia',
+ 'wav' => 'audio/x-wav',
+ 'pdb' => 'chemical/x-pdb',
+ 'xyz' => 'chemical/x-xyz',
+ 'bmp' => 'image/bmp',
+ 'cgm' => 'image/cgm',
+ 'gif' => 'image/gif',
+ 'ief' => 'image/ief',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'svg' => 'image/svg+xml',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'djvu' => 'image/vnd.djvu',
+ 'djv' => 'image/vnd.djvu',
+ 'wbmp' => 'image/vnd.wap.wbmp',
+ 'ras' => 'image/x-cmu-raster',
+ 'ico' => 'image/x-icon',
+ 'pnm' => 'image/x-portable-anymap',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pgm' => 'image/x-portable-graymap',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'rgb' => 'image/x-rgb',
+ 'xbm' => 'image/x-xbitmap',
+ 'psd' => 'image/x-photoshop',
+ 'xpm' => 'image/x-xpixmap',
+ 'xwd' => 'image/x-xwindowdump',
+ 'eml' => 'message/rfc822',
+ 'igs' => 'model/iges',
+ 'iges' => 'model/iges',
+ 'msh' => 'model/mesh',
+ 'mesh' => 'model/mesh',
+ 'silo' => 'model/mesh',
+ 'wrl' => 'model/vrml',
+ 'vrml' => 'model/vrml',
+ 'ics' => 'text/calendar',
+ 'ifb' => 'text/calendar',
+ 'css' => 'text/css',
+ 'csv' => 'text/csv',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
+ 'txt' => 'text/plain',
+ 'asc' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'sgml' => 'text/sgml',
+ 'sgm' => 'text/sgml',
+ 'tsv' => 'text/tab-separated-values',
+ 'wml' => 'text/vnd.wap.wml',
+ 'wmls' => 'text/vnd.wap.wmlscript',
+ 'etx' => 'text/x-setext',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'qt' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+ 'mxu' => 'video/vnd.mpegurl',
+ 'm4u' => 'video/vnd.mpegurl',
+ 'flv' => 'video/x-flv',
+ 'asf' => 'video/x-ms-asf',
+ 'asx' => 'video/x-ms-asf',
+ 'wmv' => 'video/x-ms-wmv',
+ 'wm' => 'video/x-ms-wm',
+ 'wmx' => 'video/x-ms-wmx',
+ 'avi' => 'video/x-msvideo',
+ 'ogv' => 'video/ogg',
+ 'movie' => 'video/x-sgi-movie',
+ 'ice' => 'x-conference/x-cooltalk',
+ );
+
+
+
+ /**
+ * Autodetect a file's MIME-type.
+ *
+ * @param string $file Path to the file to get the type of
+ *
+ * @return string $file's MIME-type on success, PEAR_Error otherwise
+ */
+ function getMIMEType($file)
+ {
+ $extension = substr(strrchr($file, '.'), 1);
+ if ($extension === false) {
+ return PEAR::raiseError("File has no extension.");
+ }
+
+ if (!isset($this->extensionToType[$extension])) {
+ return PEAR::raiseError("Sorry, couldn't determine file type.");
+ }
+
+ return $this->extensionToType[$extension];
+ }
+
+
+
+ /**
+ * Return default MIME-type for the specified extension.
+ *
+ * @param string $type MIME-type
+ *
+ * @return string A file extension without leading period.
+ */
+ function getExtension($type)
+ {
+ require_once 'MIME/Type.php';
+ // Strip parameters and comments.
+ $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type);
+
+ $extension = array_search($type, $this->extensionToType);
+ if ($extension === false) {
+ return PEAR::raiseError("Sorry, couldn't determine extension.");
+ }
+ return $extension;
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/extlib/MIME/Type/Parameter.php b/extlib/MIME/Type/Parameter.php
new file mode 100644
index 000000000..399d3dd7a
--- /dev/null
+++ b/extlib/MIME/Type/Parameter.php
@@ -0,0 +1,163 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parameter.php,v 1.1 2007/03/25 10:10:21 cweiske Exp $
+
+/**
+ * Class for working with MIME type parameters
+ *
+ * @version 1.2.0
+ * @package MIME_Type
+ * @author Ian Eure <ieure@php.net>
+ */
+class MIME_Type_Parameter {
+ /**
+ * Parameter name
+ *
+ * @var string
+ */
+ var $name;
+
+ /**
+ * Parameter value
+ *
+ * @var string
+ */
+ var $value;
+
+ /**
+ * Parameter comment
+ *
+ * @var string
+ */
+ var $comment;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $param MIME parameter to parse, if set.
+ * @return void
+ */
+ function MIME_Type_Parameter($param = false)
+ {
+ if ($param) {
+ $this->parse($param);
+ }
+ }
+
+
+ /**
+ * Parse a MIME type parameter and set object fields
+ *
+ * @param string $param MIME type parameter to parse
+ * @return void
+ */
+ function parse($param)
+ {
+ $comment = '';
+ $param = MIME_Type::stripComments($param, $comment);
+ $this->name = $this->getAttribute($param);
+ $this->value = $this->getValue($param);
+ $this->comment = $comment;
+ }
+
+
+ /**
+ * Get a parameter attribute (e.g. name)
+ *
+ * @param string MIME type parameter
+ * @return string Attribute name
+ * @static
+ */
+ function getAttribute($param)
+ {
+ $tmp = explode('=', $param);
+ return trim($tmp[0]);
+ }
+
+
+ /**
+ * Get a parameter value
+ *
+ * @param string $param MIME type parameter
+ * @return string Value
+ * @static
+ */
+ function getValue($param)
+ {
+ $tmp = explode('=', $param, 2);
+ $value = $tmp[1];
+ $value = trim($value);
+ if ($value[0] == '"' && $value[strlen($value)-1] == '"') {
+ $value = substr($value, 1, -1);
+ }
+ $value = str_replace('\\"', '"', $value);
+ return $value;
+ }
+
+
+ /**
+ * Get a parameter comment
+ *
+ * @param string $param MIME type parameter
+ * @return string Parameter comment
+ * @see getComment()
+ * @static
+ */
+ function getComment($param)
+ {
+ $cs = strpos($param, '(');
+ $comment = substr($param, $cs);
+ return trim($comment, '() ');
+ }
+
+
+ /**
+ * Does this parameter have a comment?
+ *
+ * @param string $param MIME type parameter
+ * @return boolean true if $param has a comment, false otherwise
+ * @static
+ */
+ function hasComment($param)
+ {
+ if (strstr($param, '(')) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Get a string representation of this parameter
+ *
+ * This function performs the oppsite of parse()
+ *
+ * @return string String representation of parameter
+ */
+ function get()
+ {
+ $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"';
+ if ($this->comment) {
+ $val .= ' (' . $this->comment . ')';
+ }
+ return $val;
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Net/URL2.php b/extlib/Net/URL2.php
new file mode 100644
index 000000000..7a654aed8
--- /dev/null
+++ b/extlib/Net/URL2.php
@@ -0,0 +1,813 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2007-2008, Christian Schmidt, Peytz & Co. A/S |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Christian Schmidt <schmidt at php dot net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: URL2.php,v 1.10 2008/04/26 21:57:08 schmidt Exp $
+//
+// Net_URL2 Class (PHP5 Only)
+
+// This code is released under the BSD License - http://www.opensource.org/licenses/bsd-license.php
+/**
+ * @license BSD License
+ */
+class Net_URL2
+{
+ /**
+ * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default
+ * is true.
+ */
+ const OPTION_STRICT = 'strict';
+
+ /**
+ * Represent arrays in query using PHP's [] notation. Default is true.
+ */
+ const OPTION_USE_BRACKETS = 'use_brackets';
+
+ /**
+ * URL-encode query variable keys. Default is true.
+ */
+ const OPTION_ENCODE_KEYS = 'encode_keys';
+
+ /**
+ * Query variable separators when parsing the query string. Every character
+ * is considered a separator. Default is specified by the
+ * arg_separator.input php.ini setting (this defaults to "&").
+ */
+ const OPTION_SEPARATOR_INPUT = 'input_separator';
+
+ /**
+ * Query variable separator used when generating the query string. Default
+ * is specified by the arg_separator.output php.ini setting (this defaults
+ * to "&").
+ */
+ const OPTION_SEPARATOR_OUTPUT = 'output_separator';
+
+ /**
+ * Default options corresponds to how PHP handles $_GET.
+ */
+ private $options = array(
+ self::OPTION_STRICT => true,
+ self::OPTION_USE_BRACKETS => true,
+ self::OPTION_ENCODE_KEYS => true,
+ self::OPTION_SEPARATOR_INPUT => 'x&',
+ self::OPTION_SEPARATOR_OUTPUT => 'x&',
+ );
+
+ /**
+ * @var string|bool
+ */
+ private $scheme = false;
+
+ /**
+ * @var string|bool
+ */
+ private $userinfo = false;
+
+ /**
+ * @var string|bool
+ */
+ private $host = false;
+
+ /**
+ * @var int|bool
+ */
+ private $port = false;
+
+ /**
+ * @var string
+ */
+ private $path = '';
+
+ /**
+ * @var string|bool
+ */
+ private $query = false;
+
+ /**
+ * @var string|bool
+ */
+ private $fragment = false;
+
+ /**
+ * @param string $url an absolute or relative URL
+ * @param array $options
+ */
+ public function __construct($url, $options = null)
+ {
+ $this->setOption(self::OPTION_SEPARATOR_INPUT,
+ ini_get('arg_separator.input'));
+ $this->setOption(self::OPTION_SEPARATOR_OUTPUT,
+ ini_get('arg_separator.output'));
+ if (is_array($options)) {
+ foreach ($options as $optionName => $value) {
+ $this->setOption($optionName);
+ }
+ }
+
+ if (preg_match('@^([a-z][a-z0-9.+-]*):@i', $url, $reg)) {
+ $this->scheme = $reg[1];
+ $url = substr($url, strlen($reg[0]));
+ }
+
+ if (preg_match('@^//([^/#?]+)@', $url, $reg)) {
+ $this->setAuthority($reg[1]);
+ $url = substr($url, strlen($reg[0]));
+ }
+
+ $i = strcspn($url, '?#');
+ $this->path = substr($url, 0, $i);
+ $url = substr($url, $i);
+
+ if (preg_match('@^\?([^#]*)@', $url, $reg)) {
+ $this->query = $reg[1];
+ $url = substr($url, strlen($reg[0]));
+ }
+
+ if ($url) {
+ $this->fragment = substr($url, 1);
+ }
+ }
+
+ /**
+ * Returns the scheme, e.g. "http" or "urn", or false if there is no
+ * scheme specified, i.e. if this is a relative URL.
+ *
+ * @return string|bool
+ */
+ public function getScheme()
+ {
+ return $this->scheme;
+ }
+
+ /**
+ * @param string|bool $scheme
+ *
+ * @return void
+ * @see getScheme()
+ */
+ public function setScheme($scheme)
+ {
+ $this->scheme = $scheme;
+ }
+
+ /**
+ * Returns the user part of the userinfo part (the part preceding the first
+ * ":"), or false if there is no userinfo part.
+ *
+ * @return string|bool
+ */
+ public function getUser()
+ {
+ return $this->userinfo !== false ? preg_replace('@:.*$@', '', $this->userinfo) : false;
+ }
+
+ /**
+ * Returns the password part of the userinfo part (the part after the first
+ * ":"), or false if there is no userinfo part (i.e. the URL does not
+ * contain "@" in front of the hostname) or the userinfo part does not
+ * contain ":".
+ *
+ * @return string|bool
+ */
+ public function getPassword()
+ {
+ return $this->userinfo !== false ? substr(strstr($this->userinfo, ':'), 1) : false;
+ }
+
+ /**
+ * Returns the userinfo part, or false if there is none, i.e. if the
+ * authority part does not contain "@".
+ *
+ * @return string|bool
+ */
+ public function getUserinfo()
+ {
+ return $this->userinfo;
+ }
+
+ /**
+ * Sets the userinfo part. If two arguments are passed, they are combined
+ * in the userinfo part as username ":" password.
+ *
+ * @param string|bool $userinfo userinfo or username
+ * @param string|bool $password
+ *
+ * @return void
+ */
+ public function setUserinfo($userinfo, $password = false)
+ {
+ $this->userinfo = $userinfo;
+ if ($password !== false) {
+ $this->userinfo .= ':' . $password;
+ }
+ }
+
+ /**
+ * Returns the host part, or false if there is no authority part, e.g.
+ * relative URLs.
+ *
+ * @return string|bool
+ */
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ /**
+ * @param string|bool $host
+ *
+ * @return void
+ */
+ public function setHost($host)
+ {
+ $this->host = $host;
+ }
+
+ /**
+ * Returns the port number, or false if there is no port number specified,
+ * i.e. if the default port is to be used.
+ *
+ * @return int|bool
+ */
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ /**
+ * @param int|bool $port
+ *
+ * @return void
+ */
+ public function setPort($port)
+ {
+ $this->port = intval($port);
+ }
+
+ /**
+ * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
+ * false if there is no authority none.
+ *
+ * @return string|bool
+ */
+ public function getAuthority()
+ {
+ if (!$this->host) {
+ return false;
+ }
+
+ $authority = '';
+
+ if ($this->userinfo !== false) {
+ $authority .= $this->userinfo . '@';
+ }
+
+ $authority .= $this->host;
+
+ if ($this->port !== false) {
+ $authority .= ':' . $this->port;
+ }
+
+ return $authority;
+ }
+
+ /**
+ * @param string|false $authority
+ *
+ * @return void
+ */
+ public function setAuthority($authority)
+ {
+ $this->user = false;
+ $this->pass = false;
+ $this->host = false;
+ $this->port = false;
+ if (preg_match('@^(([^\@]+)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
+ if ($reg[1]) {
+ $this->userinfo = $reg[2];
+ }
+
+ $this->host = $reg[3];
+ if (isset($reg[5])) {
+ $this->port = intval($reg[5]);
+ }
+ }
+ }
+
+ /**
+ * Returns the path part (possibly an empty string).
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * @param string $path
+ *
+ * @return void
+ */
+ public function setPath($path)
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * Returns the query string (excluding the leading "?"), or false if "?"
+ * isn't present in the URL.
+ *
+ * @return string|bool
+ * @see self::getQueryVariables()
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
+ * @param string|bool $query
+ *
+ * @return void
+ * @see self::setQueryVariables()
+ */
+ public function setQuery($query)
+ {
+ $this->query = $query;
+ }
+
+ /**
+ * Returns the fragment name, or false if "#" isn't present in the URL.
+ *
+ * @return string|bool
+ */
+ public function getFragment()
+ {
+ return $this->fragment;
+ }
+
+ /**
+ * @param string|bool $fragment
+ *
+ * @return void
+ */
+ public function setFragment($fragment)
+ {
+ $this->fragment = $fragment;
+ }
+
+ /**
+ * Returns the query string like an array as the variables would appear in
+ * $_GET in a PHP script.
+ *
+ * @return array
+ */
+ public function getQueryVariables()
+ {
+ $pattern = '/[' .
+ preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') .
+ ']/';
+ $parts = preg_split($pattern, $this->query, -1, PREG_SPLIT_NO_EMPTY);
+ $return = array();
+
+ foreach ($parts as $part) {
+ if (strpos($part, '=') !== false) {
+ list($key, $value) = explode('=', $part, 2);
+ } else {
+ $key = $part;
+ $value = null;
+ }
+
+ if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
+ $key = rawurldecode($key);
+ }
+ $value = rawurldecode($value);
+
+ if ($this->getOption(self::OPTION_USE_BRACKETS) &&
+ preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
+
+ $key = $matches[1];
+ $idx = $matches[2];
+
+ // Ensure is an array
+ if (empty($return[$key]) || !is_array($return[$key])) {
+ $return[$key] = array();
+ }
+
+ // Add data
+ if ($idx === '') {
+ $return[$key][] = $value;
+ } else {
+ $return[$key][$idx] = $value;
+ }
+ } elseif (!$this->getOption(self::OPTION_USE_BRACKETS)
+ && !empty($return[$key])
+ ) {
+ $return[$key] = (array) $return[$key];
+ $return[$key][] = $value;
+ } else {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * @param array $array (name => value) array
+ *
+ * @return void
+ */
+ public function setQueryVariables(array $array)
+ {
+ if (!$array) {
+ $this->query = false;
+ } else {
+ foreach ($array as $name => $value) {
+ if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
+ $name = rawurlencode($name);
+ }
+
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ $parts[] = $this->getOption(self::OPTION_USE_BRACKETS)
+ ? sprintf('%s[%s]=%s', $name, $k, $v)
+ : ($name . '=' . $v);
+ }
+ } elseif (!is_null($value)) {
+ $parts[] = $name . '=' . $value;
+ } else {
+ $parts[] = $name;
+ }
+ }
+ $this->query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT),
+ $parts);
+ }
+ }
+
+ /**
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return array
+ */
+ public function setQueryVariable($name, $value)
+ {
+ $array = $this->getQueryVariables();
+ $array[$name] = $value;
+ $this->setQueryVariables($array);
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return void
+ */
+ public function unsetQueryVariable($name)
+ {
+ $array = $this->getQueryVariables();
+ unset($array[$name]);
+ $this->setQueryVariables($array);
+ }
+
+ /**
+ * Returns a string representation of this URL.
+ *
+ * @return string
+ */
+ public function getURL()
+ {
+ // See RFC 3986, section 5.3
+ $url = "";
+
+ if ($this->scheme !== false) {
+ $url .= $this->scheme . ':';
+ }
+
+ $authority = $this->getAuthority();
+ if ($authority !== false) {
+ $url .= '//' . $authority;
+ }
+ $url .= $this->path;
+
+ if ($this->query !== false) {
+ $url .= '?' . $this->query;
+ }
+
+ if ($this->fragment !== false) {
+ $url .= '#' . $this->fragment;
+ }
+
+ return $url;
+ }
+
+ /**
+ * Returns a normalized string representation of this URL. This is useful
+ * for comparison of URLs.
+ *
+ * @return string
+ */
+ public function getNormalizedURL()
+ {
+ $url = clone $this;
+ $url->normalize();
+ return $url->getUrl();
+ }
+
+ /**
+ * Returns a normalized Net_URL2 instance.
+ *
+ * @return Net_URL2
+ */
+ public function normalize()
+ {
+ // See RFC 3886, section 6
+
+ // Schemes are case-insensitive
+ if ($this->scheme) {
+ $this->scheme = strtolower($this->scheme);
+ }
+
+ // Hostnames are case-insensitive
+ if ($this->host) {
+ $this->host = strtolower($this->host);
+ }
+
+ // Remove default port number for known schemes (RFC 3986, section 6.2.3)
+ if ($this->port &&
+ $this->scheme &&
+ $this->port == getservbyname($this->scheme, 'tcp')) {
+
+ $this->port = false;
+ }
+
+ // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
+ foreach (array('userinfo', 'host', 'path') as $part) {
+ if ($this->$part) {
+ $this->$part = preg_replace('/%[0-9a-f]{2}/ie', 'strtoupper("\0")', $this->$part);
+ }
+ }
+
+ // Path segment normalization (RFC 3986, section 6.2.2.3)
+ $this->path = self::removeDotSegments($this->path);
+
+ // Scheme based normalization (RFC 3986, section 6.2.3)
+ if ($this->host && !$this->path) {
+ $this->path = '/';
+ }
+ }
+
+ /**
+ * Returns whether this instance represents an absolute URL.
+ *
+ * @return bool
+ */
+ public function isAbsolute()
+ {
+ return (bool) $this->scheme;
+ }
+
+ /**
+ * Returns an Net_URL2 instance representing an absolute URL relative to
+ * this URL.
+ *
+ * @param Net_URL2|string $reference relative URL
+ *
+ * @return Net_URL2
+ */
+ public function resolve($reference)
+ {
+ if (is_string($reference)) {
+ $reference = new self($reference);
+ }
+ if (!$this->isAbsolute()) {
+ throw new Exception('Base-URL must be absolute');
+ }
+
+ // A non-strict parser may ignore a scheme in the reference if it is
+ // identical to the base URI's scheme.
+ if (!$this->getOption(self::OPTION_STRICT) && $reference->scheme == $this->scheme) {
+ $reference->scheme = false;
+ }
+
+ $target = new self('');
+ if ($reference->scheme !== false) {
+ $target->scheme = $reference->scheme;
+ $target->setAuthority($reference->getAuthority());
+ $target->path = self::removeDotSegments($reference->path);
+ $target->query = $reference->query;
+ } else {
+ $authority = $reference->getAuthority();
+ if ($authority !== false) {
+ $target->setAuthority($authority);
+ $target->path = self::removeDotSegments($reference->path);
+ $target->query = $reference->query;
+ } else {
+ if ($reference->path == '') {
+ $target->path = $this->path;
+ if ($reference->query !== false) {
+ $target->query = $reference->query;
+ } else {
+ $target->query = $this->query;
+ }
+ } else {
+ if (substr($reference->path, 0, 1) == '/') {
+ $target->path = self::removeDotSegments($reference->path);
+ } else {
+ // Merge paths (RFC 3986, section 5.2.3)
+ if ($this->host !== false && $this->path == '') {
+ $target->path = '/' . $this->path;
+ } else {
+ $i = strrpos($this->path, '/');
+ if ($i !== false) {
+ $target->path = substr($this->path, 0, $i + 1);
+ }
+ $target->path .= $reference->path;
+ }
+ $target->path = self::removeDotSegments($target->path);
+ }
+ $target->query = $reference->query;
+ }
+ $target->setAuthority($this->getAuthority());
+ }
+ $target->scheme = $this->scheme;
+ }
+
+ $target->fragment = $reference->fragment;
+
+ return $target;
+ }
+
+ /**
+ * Removes dots as described in RFC 3986, section 5.2.4, e.g.
+ * "/foo/../bar/baz" => "/bar/baz"
+ *
+ * @param string $path a path
+ *
+ * @return string a path
+ */
+ private static function removeDotSegments($path)
+ {
+ $output = '';
+
+ // Make sure not to be trapped in an infinite loop due to a bug in this
+ // method
+ $j = 0;
+ while ($path && $j++ < 100) {
+ // Step A
+ if (substr($path, 0, 2) == './') {
+ $path = substr($path, 2);
+ } elseif (substr($path, 0, 3) == '../') {
+ $path = substr($path, 3);
+
+ // Step B
+ } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
+ $path = '/' . substr($path, 3);
+
+ // Step C
+ } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
+ $path = '/' . substr($path, 4);
+ $i = strrpos($output, '/');
+ $output = $i === false ? '' : substr($output, 0, $i);
+
+ // Step D
+ } elseif ($path == '.' || $path == '..') {
+ $path = '';
+
+ // Step E
+ } else {
+ $i = strpos($path, '/');
+ if ($i === 0) {
+ $i = strpos($path, '/', 1);
+ }
+ if ($i === false) {
+ $i = strlen($path);
+ }
+ $output .= substr($path, 0, $i);
+ $path = substr($path, $i);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns a Net_URL2 instance representing the canonical URL of the
+ * currently executing PHP script.
+ *
+ * @return string
+ */
+ public static function getCanonical()
+ {
+ if (!isset($_SERVER['REQUEST_METHOD'])) {
+ // ALERT - no current URL
+ throw new Exception('Script was not called through a webserver');
+ }
+
+ // Begin with a relative URL
+ $url = new self($_SERVER['PHP_SELF']);
+ $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
+ $url->host = $_SERVER['SERVER_NAME'];
+ $port = intval($_SERVER['SERVER_PORT']);
+ if ($url->scheme == 'http' && $port != 80 ||
+ $url->scheme == 'https' && $port != 443) {
+
+ $url->port = $port;
+ }
+ return $url;
+ }
+
+ /**
+ * Returns the URL used to retrieve the current request.
+ *
+ * @return string
+ */
+ public static function getRequestedURL()
+ {
+ return self::getRequested()->getUrl();
+ }
+
+ /**
+ * Returns a Net_URL2 instance representing the URL used to retrieve the
+ * current request.
+ *
+ * @return Net_URL2
+ */
+ public static function getRequested()
+ {
+ if (!isset($_SERVER['REQUEST_METHOD'])) {
+ // ALERT - no current URL
+ throw new Exception('Script was not called through a webserver');
+ }
+
+ // Begin with a relative URL
+ $url = new self($_SERVER['REQUEST_URI']);
+ $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
+ // Set host and possibly port
+ $url->setAuthority($_SERVER['HTTP_HOST']);
+ return $url;
+ }
+
+ /**
+ * Sets the specified option.
+ *
+ * @param string $optionName a self::OPTION_ constant
+ * @param mixed $value option value
+ *
+ * @return void
+ * @see self::OPTION_STRICT
+ * @see self::OPTION_USE_BRACKETS
+ * @see self::OPTION_ENCODE_KEYS
+ */
+ function setOption($optionName, $value)
+ {
+ if (!array_key_exists($optionName, $this->options)) {
+ return false;
+ }
+ $this->options[$optionName] = $value;
+ }
+
+ /**
+ * Returns the value of the specified option.
+ *
+ * @param string $optionName The name of the option to retrieve
+ *
+ * @return mixed
+ */
+ function getOption($optionName)
+ {
+ return isset($this->options[$optionName])
+ ? $this->options[$optionName] : false;
+ }
+}
diff --git a/extlib/Services/oEmbed.php b/extlib/Services/oEmbed.php
new file mode 100644
index 000000000..5d38ed883
--- /dev/null
+++ b/extlib/Services/oEmbed.php
@@ -0,0 +1,357 @@
+<?php
+
+/**
+ * An interface for oEmbed consumption
+ *
+ * PHP version 5.1.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Validate.php';
+require_once 'Net/URL2.php';
+require_once 'HTTP/Request.php';
+require_once 'Services/oEmbed/Exception.php';
+require_once 'Services/oEmbed/Exception/NoSupport.php';
+require_once 'Services/oEmbed/Object.php';
+
+/**
+ * Base class for consuming oEmbed objects
+ *
+ * <code>
+ * <?php
+ *
+ * require_once 'Services/oEmbed.php';
+ *
+ * // The URL that we'd like to find out more information about.
+ * $url = 'http://flickr.com/photos/joestump/2848795611/';
+ *
+ * // The oEmbed API URI. Not all providers support discovery yet so we're
+ * // explicitly providing one here. If one is not provided Services_oEmbed
+ * // attempts to discover it. If none is found an exception is thrown.
+ * $oEmbed = new Services_oEmbed($url, array(
+ * Services_oEmbed::OPTION_API => 'http://www.flickr.com/services/oembed/'
+ * ));
+ * $object = $oEmbed->getObject();
+ *
+ * // All of the objects have somewhat sane __toString() methods that allow
+ * // you to output them directly.
+ * echo (string)$object;
+ *
+ * ?>
+ * </code>
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed
+{
+ /**
+ * HTTP timeout in seconds
+ *
+ * All HTTP requests made by Services_oEmbed will respect this timeout.
+ * This can be passed to {@link Services_oEmbed::setOption()} or to the
+ * options parameter in {@link Services_oEmbed::__construct()}.
+ *
+ * @var string OPTION_TIMEOUT Timeout in seconds
+ */
+ const OPTION_TIMEOUT = 'http_timeout';
+
+ /**
+ * HTTP User-Agent
+ *
+ * All HTTP requests made by Services_oEmbed will be sent with the
+ * string set by this option.
+ *
+ * @var string OPTION_USER_AGENT The HTTP User-Agent string
+ */
+ const OPTION_USER_AGENT = 'http_user_agent';
+
+ /**
+ * The API's URI
+ *
+ * If the API is known ahead of time this option can be used to explicitly
+ * set it. If not present then the API is attempted to be discovered
+ * through the auto-discovery mechanism.
+ *
+ * @var string OPTION_API
+ */
+ const OPTION_API = 'oembed_api';
+
+ /**
+ * Options for oEmbed requests
+ *
+ * @var array $options The options for making requests
+ */
+ protected $options = array(
+ self::OPTION_TIMEOUT => 3,
+ self::OPTION_API => null,
+ self::OPTION_USER_AGENT => 'Services_oEmbed 0.1.0'
+ );
+
+ /**
+ * URL of object to get embed information for
+ *
+ * @var object $url {@link Net_URL2} instance of URL of object
+ */
+ protected $url = null;
+
+ /**
+ * Constructor
+ *
+ * @param string $url The URL to fetch an oEmbed for
+ * @param array $options A list of options for the oEmbed lookup
+ *
+ * @throws {@link Services_oEmbed_Exception} if the $url is invalid
+ * @throws {@link Services_oEmbed_Exception} when no valid API is found
+ * @return void
+ */
+ public function __construct($url, array $options = array())
+ {
+ if (Validate::uri($url)) {
+ $this->url = new Net_URL2($url);
+ } else {
+ throw new Services_oEmbed_Exception('URL is invalid');
+ }
+
+ if (count($options)) {
+ foreach ($options as $key => $val) {
+ $this->setOption($key, $val);
+ }
+ }
+
+ if ($this->options[self::OPTION_API] === null) {
+ $this->options[self::OPTION_API] = $this->discover();
+ }
+ }
+
+ /**
+ * Set an option for the oEmbed request
+ *
+ * @param mixed $option The option name
+ * @param mixed $value The option value
+ *
+ * @see Services_oEmbed::OPTION_API, Services_oEmbed::OPTION_TIMEOUT
+ * @throws {@link Services_oEmbed_Exception} on invalid option
+ * @access public
+ * @return void
+ */
+ public function setOption($option, $value)
+ {
+ switch ($option) {
+ case self::OPTION_API:
+ case self::OPTION_TIMEOUT:
+ break;
+ default:
+ throw new Services_oEmbed_Exception(
+ 'Invalid option "' . $option . '"'
+ );
+ }
+
+ $func = '_set_' . $option;
+ if (method_exists($this, $func)) {
+ $this->options[$option] = $this->$func($value);
+ } else {
+ $this->options[$option] = $value;
+ }
+ }
+
+ /**
+ * Set the API option
+ *
+ * @param string $value The API's URI
+ *
+ * @throws {@link Services_oEmbed_Exception} on invalid API URI
+ * @see Validate::uri()
+ * @return string
+ */
+ protected function _set_oembed_api($value)
+ {
+ if (!Validate::uri($value)) {
+ throw new Services_oEmbed_Exception(
+ 'API URI provided is invalid'
+ );
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get the oEmbed response
+ *
+ * @param array $params Optional parameters for
+ *
+ * @throws {@link Services_oEmbed_Exception} on cURL errors
+ * @throws {@link Services_oEmbed_Exception} on HTTP errors
+ * @throws {@link Services_oEmbed_Exception} when result is not parsable
+ * @return object The oEmbed response as an object
+ */
+ public function getObject(array $params = array())
+ {
+ $params['url'] = $this->url->getURL();
+ if (!isset($params['format'])) {
+ $params['format'] = 'json';
+ }
+
+ $sets = array();
+ foreach ($params as $var => $val) {
+ $sets[] = $var . '=' . urlencode($val);
+ }
+
+ $url = $this->options[self::OPTION_API] . '?' . implode('&', $sets);
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->options[self::OPTION_TIMEOUT]);
+ $result = curl_exec($ch);
+
+ if (curl_errno($ch)) {
+ throw new Services_oEmbed_Exception(
+ curl_error($ch), curl_errno($ch)
+ );
+ }
+
+ $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if (substr($code, 0, 1) != '2') {
+ throw new Services_oEmbed_Exception('Non-200 code returned');
+ }
+
+ curl_close($ch);
+
+ switch ($params['format']) {
+ case 'json':
+ $res = json_decode($result);
+ if (!is_object($res)) {
+ throw new Services_oEmbed_Exception(
+ 'Could not parse JSON response'
+ );
+ }
+ break;
+ case 'xml':
+ libxml_use_internal_errors(true);
+ $res = simplexml_load_string($result);
+ if (!$res instanceof SimpleXMLElement) {
+ $errors = libxml_get_errors();
+ $err = array_shift($errors);
+ libxml_clear_errors();
+ libxml_use_internal_errors(false);
+ throw new Services_oEmbed_Exception(
+ $err->message, $error->code
+ );
+ }
+ break;
+ }
+
+ return Services_oEmbed_Object::factory($res);
+ }
+
+ /**
+ * Discover an oEmbed API
+ *
+ * @param string $url The URL to attempt to discover oEmbed for
+ *
+ * @throws {@link Services_oEmbed_Exception} if the $url is invalid
+ * @return string The oEmbed API endpoint discovered
+ */
+ protected function discover($url)
+ {
+ $body = $this->sendRequest($url);
+
+ // Find all <link /> tags that have a valid oembed type set. We then
+ // extract the href attribute for each type.
+ $regexp = '#<link([^>]*)type="' .
+ '(application/json|text/xml)\+oembed"([^>]*)>#i';
+
+ $m = $ret = array();
+ if (!preg_match_all($regexp, $body, $m)) {
+ throw new Services_oEmbed_Exception_NoSupport(
+ 'No valid oEmbed links found on page'
+ );
+ }
+
+ foreach ($m[0] as $i => $link) {
+ $h = array();
+ if (preg_match('/href="([^"]+)"/i', $link, $h)) {
+ $ret[$m[2][$i]] = $h[1];
+ }
+ }
+
+ return (isset($ret['json']) ? $ret['json'] : array_pop($ret));
+ }
+
+ /**
+ * Send a GET request to the provider
+ *
+ * @param mixed $url The URL to send the request to
+ *
+ * @throws {@link Services_oEmbed_Exception} on HTTP errors
+ * @return string The contents of the response
+ */
+ private function sendRequest($url)
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->options[self::OPTION_TIMEOUT]);
+ curl_setopt($ch, CURLOPT_USERAGENT, $this->options[self::OPTION_USER_AGENT]);
+ $result = curl_exec($ch);
+ if (curl_errno($ch)) {
+ throw new Services_oEmbed_Exception(
+ curl_error($ch), curl_errno($ch)
+ );
+ }
+
+ $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if (substr($code, 0, 1) != '2') {
+ throw new Services_oEmbed_Exception('Non-200 code returned');
+ }
+
+ return $result;
+ }
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Exception.php b/extlib/Services/oEmbed/Exception.php
new file mode 100644
index 000000000..446ac2a70
--- /dev/null
+++ b/extlib/Services/oEmbed/Exception.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Base exception class for {@link Services_oEmbed}
+ *
+ * PHP version 5.1.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'PEAR/Exception.php';
+
+/**
+ * Base exception class for {@link Services_oEmbed}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Exception extends PEAR_Exception
+{
+
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Exception/NoSupport.php b/extlib/Services/oEmbed/Exception/NoSupport.php
new file mode 100644
index 000000000..384c7191f
--- /dev/null
+++ b/extlib/Services/oEmbed/Exception/NoSupport.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Exception class when no oEmbed support is discovered
+ *
+ * PHP version 5.2.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+/**
+ * Exception class when no oEmbed support is discovered
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Exception_NoSupport extends Services_oEmbed_Exception
+{
+
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object.php b/extlib/Services/oEmbed/Object.php
new file mode 100644
index 000000000..9eedd7efb
--- /dev/null
+++ b/extlib/Services/oEmbed/Object.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * An interface for oEmbed consumption
+ *
+ * PHP version 5.1.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Object/Exception.php';
+
+/**
+ * Base class for consuming oEmbed objects
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+abstract class Services_oEmbed_Object
+{
+
+ /**
+ * Valid oEmbed object types
+ *
+ * @var array $types Array of valid object types
+ * @see Services_oEmbed_Object::factory()
+ */
+ static protected $types = array(
+ 'photo' => 'Photo',
+ 'video' => 'Video',
+ 'link' => 'Link',
+ 'rich' => 'Rich'
+ );
+
+ /**
+ * Create an oEmbed object from result
+ *
+ * @param object $object Raw object returned from API
+ *
+ * @throws {@link Services_oEmbed_Object_Exception} on object error
+ * @return object Instance of object driver
+ * @see Services_oEmbed_Object_Link, Services_oEmbed_Object_Photo
+ * @see Services_oEmbed_Object_Rich, Services_oEmbed_Object_Video
+ */
+ static public function factory($object)
+ {
+ if (!isset($object->type)) {
+ throw new Services_oEmbed_Object_Exception(
+ 'Object has no type'
+ );
+ }
+
+ $type = (string)$object->type;
+ if (!isset(self::$types[$type])) {
+ throw new Services_oEmbed_Object_Exception(
+ 'Object type is unknown or invalid: ' . $type
+ );
+ }
+
+ $file = 'Services/oEmbed/Object/' . self::$types[$type] . '.php';
+ include_once $file;
+
+ $class = 'Services_oEmbed_Object_' . self::$types[$type];
+ if (!class_exists($class)) {
+ throw new Services_oEmbed_Object_Exception(
+ 'Object class is invalid or not present'
+ );
+ }
+
+ $instance = new $class($object);
+ return $instance;
+ }
+
+ /**
+ * Instantiation is not allowed
+ *
+ * @return void
+ */
+ private function __construct()
+ {
+
+ }
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Common.php b/extlib/Services/oEmbed/Object/Common.php
new file mode 100644
index 000000000..f568ec89f
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Common.php
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * Base class for oEmbed objects
+ *
+ * PHP version 5.1.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+/**
+ * Base class for oEmbed objects
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+abstract class Services_oEmbed_Object_Common
+{
+ /**
+ * Raw object returned from API
+ *
+ * @var object $object The raw object from the API
+ */
+ protected $object = null;
+
+ /**
+ * Required fields per the specification
+ *
+ * @var array $required Array of required fields
+ * @link http://oembed.com
+ */
+ protected $required = array();
+
+ /**
+ * Constructor
+ *
+ * @param object $object Raw object returned from the API
+ *
+ * @throws {@link Services_oEmbed_Object_Exception} on missing fields
+ * @return void
+ */
+ public function __construct($object)
+ {
+ $this->object = $object;
+
+ $this->required[] = 'version';
+ foreach ($this->required as $field) {
+ if (!isset($this->$field)) {
+ throw new Services_oEmbed_Object_Exception(
+ 'Object is missing required ' . $field . ' attribute'
+ );
+ }
+ }
+ }
+
+ /**
+ * Get object variable
+ *
+ * @param string $var Variable to get
+ *
+ * @see Services_oEmbed_Object_Common::$object
+ * @return mixed Attribute's value or null if it's not set/exists
+ */
+ public function __get($var)
+ {
+ if (property_exists($this->object, $var)) {
+ return $this->object->$var;
+ }
+
+ return null;
+ }
+
+ /**
+ * Is variable set?
+ *
+ * @param string $var Variable name to check
+ *
+ * @return boolean True if set, false if not
+ * @see Services_oEmbed_Object_Common::$object
+ */
+ public function __isset($var)
+ {
+ if (property_exists($this->object, $var)) {
+ return (isset($this->object->$var));
+ }
+
+ return false;
+ }
+
+ /**
+ * Require a sane __toString for all objects
+ *
+ * @return string
+ */
+ abstract public function __toString();
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Exception.php b/extlib/Services/oEmbed/Object/Exception.php
new file mode 100644
index 000000000..6025ffd49
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Exception.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Exception for {@link Services_oEmbed_Object}
+ *
+ * PHP version 5.1.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Exception.php';
+
+/**
+ * Exception for {@link Services_oEmbed_Object}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Object_Exception extends Services_oEmbed_Exception
+{
+
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Link.php b/extlib/Services/oEmbed/Object/Link.php
new file mode 100644
index 000000000..9b627a89a
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Link.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Link object for {@link Services_oEmbed}
+ *
+ * PHP version 5.2.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Object/Common.php';
+
+/**
+ * Link object for {@link Services_oEmbed}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Object_Link extends Services_oEmbed_Object_Common
+{
+ /**
+ * Output a sane link
+ *
+ * @return string An HTML link of the object
+ */
+ public function __toString()
+ {
+ return '<a href="' . $this->url . '">' . $this->title . '</a>';
+ }
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Photo.php b/extlib/Services/oEmbed/Object/Photo.php
new file mode 100644
index 000000000..5fbf4292f
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Photo.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * PHP version 5.2.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Object/Common.php';
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Object_Photo extends Services_oEmbed_Object_Common
+{
+ /**
+ * Required fields for photo objects
+ *
+ * @var array $required Required fields
+ */
+ protected $required = array(
+ 'url', 'width', 'height'
+ );
+
+ /**
+ * Output a valid HTML tag for image
+ *
+ * @return string HTML <img /> tag for Photo
+ */
+ public function __toString()
+ {
+ $img = '<img src="' . $this->url . '" width="' . $this->width . '" ' .
+ 'height="' . $this->height . '"';
+
+ if (isset($this->title)) {
+ $img .= ' alt="' . $this->title . '"';
+ }
+
+ return $img . ' />';
+ }
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Rich.php b/extlib/Services/oEmbed/Object/Rich.php
new file mode 100644
index 000000000..dbf6933ac
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Rich.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * PHP version 5.2.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Object/Common.php';
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Object_Rich extends Services_oEmbed_Object_Common
+{
+ /**
+ * Required fields for rich objects
+ *
+ * @var array $required Required fields
+ */
+ protected $required = array(
+ 'html', 'width', 'height'
+ );
+
+ /**
+ * Output a the HTML tag for rich object
+ *
+ * @return string HTML for rich object
+ */
+ public function __toString()
+ {
+ return $this->html;
+ }
+}
+
+?>
diff --git a/extlib/Services/oEmbed/Object/Video.php b/extlib/Services/oEmbed/Object/Video.php
new file mode 100644
index 000000000..746108115
--- /dev/null
+++ b/extlib/Services/oEmbed/Object/Video.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * PHP version 5.2.0+
+ *
+ * Copyright (c) 2008, Digg.com, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Digg.com, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version SVN: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+
+require_once 'Services/oEmbed/Object/Common.php';
+
+/**
+ * Photo object for {@link Services_oEmbed}
+ *
+ * @category Services
+ * @package Services_oEmbed
+ * @author Joe Stump <joe@joestump.net>
+ * @copyright 2008 Digg.com, Inc.
+ * @license http://tinyurl.com/42zef New BSD License
+ * @version Release: @version@
+ * @link http://code.google.com/p/digg
+ * @link http://oembed.com
+ */
+class Services_oEmbed_Object_Video extends Services_oEmbed_Object_Common
+{
+ /**
+ * Required fields for video objects
+ *
+ * @var array $required Required fields
+ */
+ protected $required = array(
+ 'html', 'width', 'height'
+ );
+
+ /**
+ * Output a valid embed tag for video
+ *
+ * @return string HTML for video
+ */
+ public function __toString()
+ {
+ return $this->html;
+ }
+}
+
+?>
diff --git a/extlib/Stomp.php b/extlib/Stomp.php
new file mode 100644
index 000000000..9e1c97b3b
--- /dev/null
+++ b/extlib/Stomp.php
@@ -0,0 +1,594 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+require_once 'Stomp/Frame.php';
+
+/**
+ * A Stomp Connection
+ *
+ *
+ * @package Stomp
+ * @author Hiram Chirino <hiram@hiramchirino.com>
+ * @author Dejan Bosanac <dejan@nighttale.net>
+ * @author Michael Caplan <mcaplan@labnet.net>
+ * @version $Revision: 43 $
+ */
+class Stomp
+{
+ /**
+ * Perform request synchronously
+ *
+ * @var boolean
+ */
+ public $sync = false;
+
+ /**
+ * Default prefetch size
+ *
+ * @var int
+ */
+ public $prefetchSize = 1;
+
+ /**
+ * Client id used for durable subscriptions
+ *
+ * @var string
+ */
+ public $clientId = null;
+
+ protected $_brokerUri = null;
+ protected $_socket = null;
+ protected $_hosts = array();
+ protected $_params = array();
+ protected $_subscriptions = array();
+ protected $_defaultPort = 61613;
+ protected $_currentHost = - 1;
+ protected $_attempts = 10;
+ protected $_username = '';
+ protected $_password = '';
+ protected $_sessionId;
+ protected $_read_timeout_seconds = 60;
+ protected $_read_timeout_milliseconds = 0;
+
+ /**
+ * Constructor
+ *
+ * @param string $brokerUri Broker URL
+ * @throws Stomp_Exception
+ */
+ public function __construct ($brokerUri)
+ {
+ $this->_brokerUri = $brokerUri;
+ $this->_init();
+ }
+ /**
+ * Initialize connection
+ *
+ * @throws Stomp_Exception
+ */
+ protected function _init ()
+ {
+ $pattern = "|^(([a-zA-Z]+)://)+\(*([a-zA-Z0-9\.:/i,-]+)\)*\??([a-zA-Z0-9=]*)$|i";
+ if (preg_match($pattern, $this->_brokerUri, $regs)) {
+ $scheme = $regs[2];
+ $hosts = $regs[3];
+ $params = $regs[4];
+ if ($scheme != "failover") {
+ $this->_processUrl($this->_brokerUri);
+ } else {
+ $urls = explode(",", $hosts);
+ foreach ($urls as $url) {
+ $this->_processUrl($url);
+ }
+ }
+ if ($params != null) {
+ parse_str($params, $this->_params);
+ }
+ } else {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("Bad Broker URL {$this->_brokerUri}");
+ }
+ }
+ /**
+ * Process broker URL
+ *
+ * @param string $url Broker URL
+ * @throws Stomp_Exception
+ * @return boolean
+ */
+ protected function _processUrl ($url)
+ {
+ $parsed = parse_url($url);
+ if ($parsed) {
+ array_push($this->_hosts, array($parsed['host'] , $parsed['port'] , $parsed['scheme']));
+ } else {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("Bad Broker URL $url");
+ }
+ }
+ /**
+ * Make socket connection to the server
+ *
+ * @throws Stomp_Exception
+ */
+ protected function _makeConnection ()
+ {
+ if (count($this->_hosts) == 0) {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("No broker defined");
+ }
+
+ // force disconnect, if previous established connection exists
+ $this->disconnect();
+
+ $i = $this->_currentHost;
+ $att = 0;
+ $connected = false;
+ while (! $connected && $att ++ < $this->_attempts) {
+ if (isset($this->_params['randomize']) && $this->_params['randomize'] == 'true') {
+ $i = rand(0, count($this->_hosts) - 1);
+ } else {
+ $i = ($i + 1) % count($this->_hosts);
+ }
+ $broker = $this->_hosts[$i];
+ $host = $broker[0];
+ $port = $broker[1];
+ $scheme = $broker[2];
+ if ($port == null) {
+ $port = $this->_defaultPort;
+ }
+ if ($this->_socket != null) {
+ fclose($this->_socket);
+ $this->_socket = null;
+ }
+ $this->_socket = @fsockopen($scheme . '://' . $host, $port);
+ if (!is_resource($this->_socket) && $att >= $this->_attempts && !array_key_exists($i + 1, $this->_hosts)) {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("Could not connect to $host:$port ($att/{$this->_attempts})");
+ } else if (is_resource($this->_socket)) {
+ $connected = true;
+ $this->_currentHost = $i;
+ break;
+ }
+ }
+ if (! $connected) {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("Could not connect to a broker");
+ }
+ }
+ /**
+ * Connect to server
+ *
+ * @param string $username
+ * @param string $password
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function connect ($username = '', $password = '')
+ {
+ $this->_makeConnection();
+ if ($username != '') {
+ $this->_username = $username;
+ }
+ if ($password != '') {
+ $this->_password = $password;
+ }
+ $headers = array('login' => $this->_username , 'passcode' => $this->_password);
+ if ($this->clientId != null) {
+ $headers["client-id"] = $this->clientId;
+ }
+ $frame = new Stomp_Frame("CONNECT", $headers);
+ $this->_writeFrame($frame);
+ $frame = $this->readFrame();
+ if ($frame instanceof Stomp_Frame && $frame->command == 'CONNECTED') {
+ $this->_sessionId = $frame->headers["session"];
+ return true;
+ } else {
+ require_once 'Stomp/Exception.php';
+ if ($frame instanceof Stomp_Frame) {
+ throw new Stomp_Exception("Unexpected command: {$frame->command}", 0, $frame->body);
+ } else {
+ throw new Stomp_Exception("Connection not acknowledged");
+ }
+ }
+ }
+
+ /**
+ * Check if client session has ben established
+ *
+ * @return boolean
+ */
+ public function isConnected ()
+ {
+ return !empty($this->_sessionId) && is_resource($this->_socket);
+ }
+ /**
+ * Current stomp session ID
+ *
+ * @return string
+ */
+ public function getSessionId()
+ {
+ return $this->_sessionId;
+ }
+ /**
+ * Send a message to a destination in the messaging system
+ *
+ * @param string $destination Destination queue
+ * @param string|Stomp_Frame $msg Message
+ * @param array $properties
+ * @param boolean $sync Perform request synchronously
+ * @return boolean
+ */
+ public function send ($destination, $msg, $properties = null, $sync = null)
+ {
+ if ($msg instanceof Stomp_Frame) {
+ $msg->headers['destination'] = $destination;
+ $msg->headers = array_merge($msg->headers, $properties);
+ $frame = $msg;
+ } else {
+ $headers = $properties;
+ $headers['destination'] = $destination;
+ $frame = new Stomp_Frame('SEND', $headers, $msg);
+ }
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ return $this->_waitForReceipt($frame, $sync);
+ }
+ /**
+ * Prepair frame receipt
+ *
+ * @param Stomp_Frame $frame
+ * @param boolean $sync
+ */
+ protected function _prepareReceipt (Stomp_Frame $frame, $sync)
+ {
+ $receive = $this->sync;
+ if ($sync !== null) {
+ $receive = $sync;
+ }
+ if ($receive == true) {
+ $frame->headers['receipt'] = md5(microtime());
+ }
+ }
+ /**
+ * Wait for receipt
+ *
+ * @param Stomp_Frame $frame
+ * @param boolean $sync
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ protected function _waitForReceipt (Stomp_Frame $frame, $sync)
+ {
+
+ $receive = $this->sync;
+ if ($sync !== null) {
+ $receive = $sync;
+ }
+ if ($receive == true) {
+ $id = (isset($frame->headers['receipt'])) ? $frame->headers['receipt'] : null;
+ if ($id == null) {
+ return true;
+ }
+ $frame = $this->readFrame();
+ if ($frame instanceof Stomp_Frame && $frame->command == 'RECEIPT') {
+ if ($frame->headers['receipt-id'] == $id) {
+ return true;
+ } else {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception("Unexpected receipt id {$frame->headers['receipt-id']}", 0, $frame->body);
+ }
+ } else {
+ require_once 'Stomp/Exception.php';
+ if ($frame instanceof Stomp_Frame) {
+ throw new Stomp_Exception("Unexpected command {$frame->command}", 0, $frame->body);
+ } else {
+ throw new Stomp_Exception("Receipt not received");
+ }
+ }
+ }
+ return true;
+ }
+ /**
+ * Register to listen to a given destination
+ *
+ * @param string $destination Destination queue
+ * @param array $properties
+ * @param boolean $sync Perform request synchronously
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function subscribe ($destination, $properties = null, $sync = null)
+ {
+ $headers = array('ack' => 'client');
+ $headers['activemq.prefetchSize'] = $this->prefetchSize;
+ if ($this->clientId != null) {
+ $headers["activemq.subcriptionName"] = $this->clientId;
+ }
+ if (isset($properties)) {
+ foreach ($properties as $name => $value) {
+ $headers[$name] = $value;
+ }
+ }
+ $headers['destination'] = $destination;
+ $frame = new Stomp_Frame('SUBSCRIBE', $headers);
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ if ($this->_waitForReceipt($frame, $sync) == true) {
+ $this->_subscriptions[$destination] = $properties;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ /**
+ * Remove an existing subscription
+ *
+ * @param string $destination
+ * @param array $properties
+ * @param boolean $sync Perform request synchronously
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function unsubscribe ($destination, $properties = null, $sync = null)
+ {
+ $headers = array();
+ if (isset($properties)) {
+ foreach ($properties as $name => $value) {
+ $headers[$name] = $value;
+ }
+ }
+ $headers['destination'] = $destination;
+ $frame = new Stomp_Frame('UNSUBSCRIBE', $headers);
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ if ($this->_waitForReceipt($frame, $sync) == true) {
+ unset($this->_subscriptions[$destination]);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ /**
+ * Start a transaction
+ *
+ * @param string $transactionId
+ * @param boolean $sync Perform request synchronously
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function begin ($transactionId = null, $sync = null)
+ {
+ $headers = array();
+ if (isset($transactionId)) {
+ $headers['transaction'] = $transactionId;
+ }
+ $frame = new Stomp_Frame('BEGIN', $headers);
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ return $this->_waitForReceipt($frame, $sync);
+ }
+ /**
+ * Commit a transaction in progress
+ *
+ * @param string $transactionId
+ * @param boolean $sync Perform request synchronously
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function commit ($transactionId = null, $sync = null)
+ {
+ $headers = array();
+ if (isset($transactionId)) {
+ $headers['transaction'] = $transactionId;
+ }
+ $frame = new Stomp_Frame('COMMIT', $headers);
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ return $this->_waitForReceipt($frame, $sync);
+ }
+ /**
+ * Roll back a transaction in progress
+ *
+ * @param string $transactionId
+ * @param boolean $sync Perform request synchronously
+ */
+ public function abort ($transactionId = null, $sync = null)
+ {
+ $headers = array();
+ if (isset($transactionId)) {
+ $headers['transaction'] = $transactionId;
+ }
+ $frame = new Stomp_Frame('ABORT', $headers);
+ $this->_prepareReceipt($frame, $sync);
+ $this->_writeFrame($frame);
+ return $this->_waitForReceipt($frame, $sync);
+ }
+ /**
+ * Acknowledge consumption of a message from a subscription
+ * Note: This operation is always asynchronous
+ *
+ * @param string|Stomp_Frame $messageMessage ID
+ * @param string $transactionId
+ * @return boolean
+ * @throws Stomp_Exception
+ */
+ public function ack ($message, $transactionId = null)
+ {
+ if ($message instanceof Stomp_Frame) {
+ $frame = new Stomp_Frame('ACK', $message->headers);
+ $this->_writeFrame($frame);
+ return true;
+ } else {
+ $headers = array();
+ if (isset($transactionId)) {
+ $headers['transaction'] = $transactionId;
+ }
+ $headers['message-id'] = $message;
+ $frame = new Stomp_Frame('ACK', $headers);
+ $this->_writeFrame($frame);
+ return true;
+ }
+ }
+ /**
+ * Graceful disconnect from the server
+ *
+ */
+ public function disconnect ()
+ {
+ $header = array();
+
+ if ($this->clientId != null) {
+ $headers["client-id"] = $this->clientId;
+ }
+
+ if (is_resource($this->_socket)) {
+ $this->_writeFrame(new Stomp_Frame('DISCONNECT', $headers));
+ fclose($this->_socket);
+ }
+ $this->_socket = null;
+ $this->_sessionId = null;
+ $this->_currentHost = -1;
+ $this->_subscriptions = array();
+ $this->_username = '';
+ $this->_password = '';
+ }
+ /**
+ * Write frame to server
+ *
+ * @param Stomp_Frame $stompFrame
+ */
+ protected function _writeFrame (Stomp_Frame $stompFrame)
+ {
+ if (!is_resource($this->_socket)) {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception('Socket connection hasn\'t been established');
+ }
+
+ $data = $stompFrame->__toString();
+ $r = fwrite($this->_socket, $data, strlen($data));
+ if ($r === false || $r == 0) {
+ $this->_reconnect();
+ $this->_writeFrame($stompFrame);
+ }
+ }
+
+ /**
+ * Set timeout to wait for content to read
+ *
+ * @param int $seconds_to_wait Seconds to wait for a frame
+ * @param int $milliseconds Milliseconds to wait for a frame
+ */
+ public function setReadTimeout($seconds, $milliseconds = 0)
+ {
+ $this->_read_timeout_seconds = $seconds;
+ $this->_read_timeout_milliseconds = $milliseconds;
+ }
+
+ /**
+ * Read responce frame from server
+ *
+ * @return Stomp_Frame|Stomp_Message_Map|boolean False when no frame to read
+ */
+ public function readFrame ()
+ {
+ if (!$this->hasFrameToRead()) {
+ return false;
+ }
+
+ $rb = 1024;
+ $data = '';
+ do {
+ $read = fgets($this->_socket, $rb);
+ if ($read === false) {
+ $this->_reconnect();
+ return $this->readFrame();
+ }
+ $data .= $read;
+ $len = strlen($data);
+ } while (($len < 2 || ! ($data[$len - 2] == "\x00" && $data[$len - 1] == "\n")));
+
+ list ($header, $body) = explode("\n\n", $data, 2);
+ $header = explode("\n", $header);
+ $headers = array();
+ $command = null;
+ foreach ($header as $v) {
+ if (isset($command)) {
+ list ($name, $value) = explode(':', $v, 2);
+ $headers[$name] = $value;
+ } else {
+ $command = $v;
+ }
+ }
+ $frame = new Stomp_Frame($command, $headers, trim($body));
+ if (isset($frame->headers['amq-msg-type']) && $frame->headers['amq-msg-type'] == 'MapMessage') {
+ require_once 'Stomp/Message/Map.php';
+ return new Stomp_Message_Map($frame);
+ } else {
+ return $frame;
+ }
+ }
+
+ /**
+ * Check if there is a frame to read
+ *
+ * @return boolean
+ */
+ public function hasFrameToRead()
+ {
+ $read = array($this->_socket);
+ $write = null;
+ $except = null;
+
+ $has_frame_to_read = stream_select($read, $write, $except, $this->_read_timeout_seconds, $this->_read_timeout_milliseconds);
+
+ if ($has_frame_to_read === false) {
+ throw new Stomp_Exception('Check failed to determin if the socket is readable');
+ } else if ($has_frame_to_read > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Reconnects and renews subscriptions (if there were any)
+ * Call this method when you detect connection problems
+ */
+ protected function _reconnect ()
+ {
+ $subscriptions = $this->_subscriptions;
+
+ $this->connect($this->_username, $this->_password);
+ foreach ($subscriptions as $dest => $properties) {
+ $this->subscribe($dest, $properties);
+ }
+ }
+ /**
+ * Graceful object desruction
+ *
+ */
+ public function __destruct()
+ {
+ $this->disconnect();
+ }
+}
+?>
diff --git a/extlib/Stomp/Exception.php b/extlib/Stomp/Exception.php
new file mode 100644
index 000000000..e6870bc15
--- /dev/null
+++ b/extlib/Stomp/Exception.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+/**
+ * A Stomp Connection
+ *
+ *
+ * @package Stomp
+ * @author Michael Caplan <mcaplan@labnet.net>
+ * @version $Revision: 23 $
+ */
+class Stomp_Exception extends Exception
+{
+ protected $_details;
+
+ /**
+ * Constructor
+ *
+ * @param string $message Error message
+ * @param int $code Error code
+ * @param string $details Stomp server error details
+ */
+ public function __construct($message = null, $code = 0, $details = '')
+ {
+ $this->_details = $details;
+
+ parent::__construct($message, $code);
+ }
+
+ /**
+ * Stomp server error details
+ *
+ * @return string
+ */
+ public function getDetails()
+ {
+ return $this->_details;
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Stomp/Frame.php b/extlib/Stomp/Frame.php
new file mode 100644
index 000000000..dc59c1cb7
--- /dev/null
+++ b/extlib/Stomp/Frame.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+/**
+ * Stomp Frames are messages that are sent and received on a StompConnection.
+ *
+ * @package Stomp
+ * @author Hiram Chirino <hiram@hiramchirino.com>
+ * @author Dejan Bosanac <dejan@nighttale.net>
+ * @author Michael Caplan <mcaplan@labnet.net>
+ * @version $Revision: 36 $
+ */
+class Stomp_Frame
+{
+ public $command;
+ public $headers = array();
+ public $body;
+
+ /**
+ * Constructor
+ *
+ * @param string $command
+ * @param array $headers
+ * @param string $body
+ */
+ public function __construct ($command = null, $headers = null, $body = null)
+ {
+ $this->_init($command, $headers, $body);
+ }
+
+ protected function _init ($command = null, $headers = null, $body = null)
+ {
+ $this->command = $command;
+ if ($headers != null) {
+ $this->headers = $headers;
+ }
+ $this->body = $body;
+
+ if ($this->command == 'ERROR') {
+ require_once 'Stomp/Exception.php';
+ throw new Stomp_Exception($this->headers['message'], 0, $this->body);
+ }
+ }
+
+ /**
+ * Convert frame to transportable string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $data = $this->command . "\n";
+
+ foreach ($this->headers as $name => $value) {
+ $data .= $name . ": " . $value . "\n";
+ }
+
+ $data .= "\n";
+ $data .= $this->body;
+ return $data .= "\x00\n";
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Stomp/Message.php b/extlib/Stomp/Message.php
new file mode 100644
index 000000000..6bcad3efd
--- /dev/null
+++ b/extlib/Stomp/Message.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+require_once 'Stomp/Frame.php';
+
+/**
+ * Basic text stomp message
+ *
+ * @package Stomp
+ * @author Dejan Bosanac <dejan@nighttale.net>
+ * @version $Revision: 23 $
+ */
+class Stomp_Message extends Stomp_Frame
+{
+ public function __construct ($body, $headers = null)
+ {
+ $this->_init("SEND", $headers, $body);
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Stomp/Message/Bytes.php b/extlib/Stomp/Message/Bytes.php
new file mode 100644
index 000000000..c75f23e43
--- /dev/null
+++ b/extlib/Stomp/Message/Bytes.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+require_once 'Stomp/Message.php';
+
+/**
+ * Message that contains a stream of uninterpreted bytes
+ *
+ * @package Stomp
+ * @author Dejan Bosanac <dejan@nighttale.net>
+ * @version $Revision: 23 $
+ */
+class Stomp_Message_Bytes extends Stomp_Message
+{
+ /**
+ * Constructor
+ *
+ * @param string $body
+ * @param array $headers
+ */
+ function __construct ($body, $headers = null)
+ {
+ $this->_init("SEND", $headers, $body);
+ if ($this->headers == null) {
+ $this->headers = array();
+ }
+ $this->headers['content-length'] = count($body);
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Stomp/Message/Map.php b/extlib/Stomp/Message/Map.php
new file mode 100644
index 000000000..288456a84
--- /dev/null
+++ b/extlib/Stomp/Message/Map.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* vim: set expandtab tabstop=3 shiftwidth=3: */
+
+require_once 'Stomp/Message.php';
+
+/**
+ * Message that contains a set of name-value pairs
+ *
+ * @package Stomp
+ * @author Dejan Bosanac <dejan@nighttale.net>
+ * @version $Revision: 23 $
+ */
+class Stomp_Message_Map extends Stomp_Message
+{
+ public $map;
+
+ /**
+ * Constructor
+ *
+ * @param Stomp_Frame|string $msg
+ * @param array $headers
+ */
+ function __construct ($msg, $headers = null)
+ {
+ if ($msg instanceof Stomp_Frame) {
+ $this->_init($msg->command, $msg->headers, $msg->body);
+ $this->map = json_decode($msg->body);
+ } else {
+ $this->_init("SEND", $headers, $msg);
+ if ($this->headers == null) {
+ $this->headers = array();
+ }
+ $this->headers['amq-msg-type'] = 'MapMessage';
+ $this->body = json_encode($msg);
+ }
+ }
+}
+?> \ No newline at end of file
diff --git a/extlib/Validate.php b/extlib/Validate.php
index 4c05506b3..3d8bc23f2 100644
--- a/extlib/Validate.php
+++ b/extlib/Validate.php
@@ -1,1051 +1,1116 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox, Amir Saied |
-// +----------------------------------------------------------------------+
-// | This source file is subject to the New BSD license, That is bundled |
-// | with this package in the file LICENSE, and is available through |
-// | the world-wide-web at |
-// | http://www.opensource.org/licenses/bsd-license.php |
-// | If you did not receive a copy of the new BSDlicense and are unable |
-// | to obtain it through the world-wide-web, please send a note to |
-// | pajoye@php.net so we can mail you a copy immediately. |
-// +----------------------------------------------------------------------+
-// | Author: Tomas V.V.Cox <cox@idecnet.com> |
-// | Pierre-Alain Joye <pajoye@php.net> |
-// | Amir Mohammad Saied <amir@php.net> |
-// +----------------------------------------------------------------------+
-//
-/**
- * Validation class
- *
- * Package to validate various datas. It includes :
- * - numbers (min/max, decimal or not)
- * - email (syntax, domain check)
- * - string (predifined type alpha upper and/or lowercase, numeric,...)
- * - date (min, max, rfc822 compliant)
- * - uri (RFC2396)
- * - possibility valid multiple data with a single method call (::multiple)
- *
- * @category Validate
- * @package Validate
- * @author Tomas V.V.Cox <cox@idecnet.com>
- * @author Pierre-Alain Joye <pajoye@php.net>
- * @author Amir Mohammad Saied <amir@php.net>
- * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
- * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
- * @version CVS: $Id: Validate.php,v 1.123 2007/12/12 16:45:51 davidc Exp $
- * @link http://pear.php.net/package/Validate
- */
-
-/**
- * Methods for common data validations
- */
-define('VALIDATE_NUM', '0-9');
-define('VALIDATE_SPACE', '\s');
-define('VALIDATE_ALPHA_LOWER', 'a-z');
-define('VALIDATE_ALPHA_UPPER', 'A-Z');
-define('VALIDATE_ALPHA', VALIDATE_ALPHA_LOWER . VALIDATE_ALPHA_UPPER);
-define('VALIDATE_EALPHA_LOWER', VALIDATE_ALPHA_LOWER . 'áéíóúýàèìòùäëïöüÿâêîôûãñõ¨åæç½ðøþß');
-define('VALIDATE_EALPHA_UPPER', VALIDATE_ALPHA_UPPER . 'ÁÉÍÓÚÝÀÈÌÒÙÄËÏÖܾÂÊÎÔÛÃÑÕ¦ÅÆǼÐØÞ');
-define('VALIDATE_EALPHA', VALIDATE_EALPHA_LOWER . VALIDATE_EALPHA_UPPER);
-define('VALIDATE_PUNCTUATION', VALIDATE_SPACE . '\.,;\:&"\'\?\!\(\)');
-define('VALIDATE_NAME', VALIDATE_EALPHA . VALIDATE_SPACE . "'" . "-");
-define('VALIDATE_STREET', VALIDATE_NUM . VALIDATE_NAME . "/\\ºª\.");
-
-define('VALIDATE_ITLD_EMAILS', 1);
-define('VALIDATE_GTLD_EMAILS', 2);
-define('VALIDATE_CCTLD_EMAILS', 4);
-define('VALIDATE_ALL_EMAILS', 8);
-
-/**
- * Validation class
- *
- * Package to validate various datas. It includes :
- * - numbers (min/max, decimal or not)
- * - email (syntax, domain check)
- * - string (predifined type alpha upper and/or lowercase, numeric,...)
- * - date (min, max)
- * - uri (RFC2396)
- * - possibility valid multiple data with a single method call (::multiple)
- *
- * @category Validate
- * @package Validate
- * @author Tomas V.V.Cox <cox@idecnet.com>
- * @author Pierre-Alain Joye <pajoye@php.net>
- * @author Amir Mohammad Saied <amir@php.net>
- * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
- * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
- * @version Release: @package_version@
- * @link http://pear.php.net/package/Validate
- */
-class Validate
-{
- /**
- * International Top-Level Domain
- *
- * This is an array of the known international
- * top-level domain names.
- *
- * @access protected
- * @var array $_iTld (International top-level domains)
- */
- var $_itld = array(
- 'arpa',
- 'root',
- );
-
- /**
- * Generic top-level domain
- *
- * This is an array of the official
- * generic top-level domains.
- *
- * @access protected
- * @var array $_gTld (Generic top-level domains)
- */
- var $_gtld = array(
- 'aero',
- 'biz',
- 'cat',
- 'com',
- 'coop',
- 'edu',
- 'gov',
- 'info',
- 'int',
- 'jobs',
- 'mil',
- 'mobi',
- 'museum',
- 'name',
- 'net',
- 'org',
- 'pro',
- 'travel',
- 'asia',
- 'post',
- 'tel',
- 'geo',
- );
-
- /**
- * Country code top-level domains
- *
- * This is an array of the official country
- * codes top-level domains
- *
- * @access protected
- * @var array $_ccTld (Country Code Top-Level Domain)
- */
- var $_cctld = array(
- 'ac',
- 'ad','ae','af','ag',
- 'ai','al','am','an',
- 'ao','aq','ar','as',
- 'at','au','aw','ax',
- 'az','ba','bb','bd',
- 'be','bf','bg','bh',
- 'bi','bj','bm','bn',
- 'bo','br','bs','bt',
- 'bu','bv','bw','by',
- 'bz','ca','cc','cd',
- 'cf','cg','ch','ci',
- 'ck','cl','cm','cn',
- 'co','cr','cs','cu',
- 'cv','cx','cy','cz',
- 'de','dj','dk','dm',
- 'do','dz','ec','ee',
- 'eg','eh','er','es',
- 'et','eu','fi','fj',
- 'fk','fm','fo','fr',
- 'ga','gb','gd','ge',
- 'gf','gg','gh','gi',
- 'gl','gm','gn','gp',
- 'gq','gr','gs','gt',
- 'gu','gw','gy','hk',
- 'hm','hn','hr','ht',
- 'hu','id','ie','il',
- 'im','in','io','iq',
- 'ir','is','it','je',
- 'jm','jo','jp','ke',
- 'kg','kh','ki','km',
- 'kn','kp','kr','kw',
- 'ky','kz','la','lb',
- 'lc','li','lk','lr',
- 'ls','lt','lu','lv',
- 'ly','ma','mc','md',
- 'me','mg','mh','mk',
- 'ml','mm','mn','mo',
- 'mp','mq','mr','ms',
- 'mt','mu','mv','mw',
- 'mx','my','mz','na',
- 'nc','ne','nf','ng',
- 'ni','nl','no','np',
- 'nr','nu','nz','om',
- 'pa','pe','pf','pg',
- 'ph','pk','pl','pm',
- 'pn','pr','ps','pt',
- 'pw','py','qa','re',
- 'ro','rs','ru','rw',
- 'sa','sb','sc','sd',
- 'se','sg','sh','si',
- 'sj','sk','sl','sm',
- 'sn','so','sr','st',
- 'su','sv','sy','sz',
- 'tc','td','tf','tg',
- 'th','tj','tk','tl',
- 'tm','tn','to','tp',
- 'tr','tt','tv','tw',
- 'tz','ua','ug','uk',
- 'us','uy','uz','va',
- 'vc','ve','vg','vi',
- 'vn','vu','wf','ws',
- 'ye','yt','yu','za',
- 'zm','zw',
- );
-
-
- /**
- * Validate a number
- *
- * @param string $number Number to validate
- * @param array $options array where:
- * 'decimal' is the decimal char or false when decimal not allowed
- * i.e. ',.' to allow both ',' and '.'
- * 'dec_prec' Number of allowed decimals
- * 'min' minimum value
- * 'max' maximum value
- *
- * @return boolean true if valid number, false if not
- *
- * @access public
- */
- function number($number, $options = array())
- {
- $decimal = $dec_prec = $min = $max = null;
- if (is_array($options)) {
- extract($options);
- }
-
- $dec_prec = $dec_prec ? "{1,$dec_prec}" : '+';
- $dec_regex = $decimal ? "[$decimal][0-9]$dec_prec" : '';
-
- if (!preg_match("|^[-+]?\s*[0-9]+($dec_regex)?\$|", $number)) {
- return false;
- }
-
- if ($decimal != '.') {
- $number = strtr($number, $decimal, '.');
- }
-
- $number = (float)str_replace(' ', '', $number);
- if ($min !== null && $min > $number) {
- return false;
- }
-
- if ($max !== null && $max < $number) {
- return false;
- }
- return true;
- }
-
- /**
- * Converting a string to UTF-7 (RFC 2152)
- *
- * @param $string string to be converted
- *
- * @return string converted string
- *
- * @access private
- */
- function __stringToUtf7($string) {
- $return = '';
- $utf7 = array(
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
- 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
- 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
- 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
- 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
- '3', '4', '5', '6', '7', '8', '9', '+', ','
- );
-
- $state = 0;
- if (!empty($string)) {
- $i = 0;
- while ($i <= strlen($string)) {
- $char = substr($string, $i, 1);
- if ($state == 0) {
- if ((ord($char) >= 0x7F) || (ord($char) <= 0x1F)) {
- if ($char) {
- $return .= '&';
- }
- $state = 1;
- } elseif ($char == '&') {
- $return .= '&-';
- } else {
- $return .= $char;
- }
- } elseif (($i == strlen($string) ||
- !((ord($char) >= 0x7F)) || (ord($char) <= 0x1F))) {
- if ($state != 1) {
- if (ord($char) > 64) {
- $return .= '';
- } else {
- $return .= $utf7[ord($char)];
- }
- }
- $return .= '-';
- $state = 0;
- } else {
- switch($state) {
- case 1:
- $return .= $utf7[ord($char) >> 2];
- $residue = (ord($char) & 0x03) << 4;
- $state = 2;
- break;
- case 2:
- $return .= $utf7[$residue | (ord($char) >> 4)];
- $residue = (ord($char) & 0x0F) << 2;
- $state = 3;
- break;
- case 3:
- $return .= $utf7[$residue | (ord($char) >> 6)];
- $return .= $utf7[ord($char) & 0x3F];
- $state = 1;
- break;
- }
- }
- $i++;
- }
- return $return;
- }
- return '';
- }
-
- /**
- * Validate an email according to full RFC822 (inclusive human readable part)
- *
- * @param string $email email to validate,
- * will return the address for optional dns validation
- * @param array $options email() options
- *
- * @return boolean true if valid email, false if not
- *
- * @access private
- */
- function __emailRFC822(&$email, &$options)
- {
- if (Validate::__stringToUtf7($email) != $email) {
- return false;
- }
- static $address = null;
- static $uncomment = null;
- if (!$address) {
- // atom = 1*<any CHAR except specials, SPACE and CTLs>
- $atom = '[^][()<>@,;:\\".\s\000-\037\177-\377]+\s*';
- // qtext = <any CHAR excepting <">, ; => may be folded
- // "\" & CR, and including linear-white-space>
- $qtext = '[^"\\\\\r]';
- // quoted-pair = "\" CHAR ; may quote any char
- $quoted_pair = '\\\\.';
- // quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
- // ; quoted chars.
- $quoted_string = '"(?:' . $qtext . '|' . $quoted_pair . ')*"\s*';
- // word = atom / quoted-string
- $word = '(?:' . $atom . '|' . $quoted_string . ')';
- // local-part = word *("." word) ; uninterpreted
- // ; case-preserved
- $local_part = $word . '(?:\.\s*' . $word . ')*';
- // dtext = <any CHAR excluding "[", ; => may be folded
- // "]", "\" & CR, & including linear-white-space>
- $dtext = '[^][\\\\\r]';
- // domain-literal = "[" *(dtext / quoted-pair) "]"
- $domain_literal = '\[(?:' . $dtext . '|' . $quoted_pair . ')*\]\s*';
- // sub-domain = domain-ref / domain-literal
- // domain-ref = atom ; symbolic reference
- $sub_domain = '(?:' . $atom . '|' . $domain_literal . ')';
- // domain = sub-domain *("." sub-domain)
- $domain = $sub_domain . '(?:\.\s*' . $sub_domain . ')*';
- // addr-spec = local-part "@" domain ; global address
- $addr_spec = $local_part . '@\s*' . $domain;
- // route = 1#("@" domain) ":" ; path-relative
- $route = '@' . $domain . '(?:,@\s*' . $domain . ')*:\s*';
- // route-addr = "<" [route] addr-spec ">"
- $route_addr = '<\s*(?:' . $route . ')?' . $addr_spec . '>\s*';
- // phrase = 1*word ; Sequence of words
- $phrase = $word . '+';
- // mailbox = addr-spec ; simple address
- // / phrase route-addr ; name & addr-spec
- $mailbox = '(?:' . $addr_spec . '|' . $phrase . $route_addr . ')';
- // group = phrase ":" [#mailbox] ";"
- $group = $phrase . ':\s*(?:' . $mailbox . '(?:,\s*' . $mailbox . ')*)?;\s*';
- // address = mailbox ; one addressee
- // / group ; named list
- $address = '/^\s*(?:' . $mailbox . '|' . $group . ')$/';
- $uncomment =
- '/((?:(?:\\\\"|[^("])*(?:' . $quoted_string .
- ')?)*)((?<!\\\\)\((?:(?2)|.)*?(?<!\\\\)\))/';
- }
- // strip comments
- $email = preg_replace($uncomment, '$1 ', $email);
- return preg_match($address, $email);
- }
-
- /**
- * Full TLD Validation function
- *
- * This function is used to make a much more proficient validation
- * against all types of official domain names.
- *
- * @access protected
- * @param string $email The email address to check.
- * @param array $options The options for validation
- * @return bool True if validating succeeds
- */
- function _fullTLDValidation($email, $options)
- {
- $validate = array();
-
- switch ($options) {
- /** 1 */
- case VALIDATE_ITLD_EMAILS:
- array_push($validate, 'itld');
- break;
-
- /** 2 */
- case VALIDATE_GTLD_EMAILS:
- array_push($validate, 'gtld');
- break;
-
- /** 3 */
- case VALIDATE_ITLD_EMAILS | VALIDATE_GTLD_EMAILS:
- array_push($validate, 'itld');
- array_push($validate, 'gtld');
- break;
-
- /** 4 */
- case VALIDATE_CCTLD_EMAILS:
- array_push($validate, 'cctld');
- break;
-
- /** 5 */
- case VALIDATE_CCTLD_EMAILS | VALIDATE_ITLD_EMAILS:
- array_push($validate, 'cctld');
- array_push($validate, 'itld');
- break;
-
- /** 6 */
- case VALIDATE_CCTLD_EMAILS ^ VALIDATE_ITLD_EMAILS:
- array_push($validate, 'cctld');
- array_push($validate, 'itld');
- break;
-
- /** 7 - 8 */
- case VALIDATE_CCTLD_EMAILS | VALIDATE_ITLD_EMAILS | VALIDATE_GTLD_EMAILS:
- case VALIDATE_ALL_EMAILS:
- array_push($validate, 'cctld');
- array_push($validate, 'itld');
- array_push($validate, 'gtld');
- break;
- }
-
- /**
- * Debugging still, not implemented but code is somewhat here.
- */
-
- $self = new Validate;
-
- $toValidate = array();
-
- foreach ($validate as $valid) {
- $tmpVar = '_' . (string)$valid;
- $toValidate[$valid] = $self->{$tmpVar};
- }
-
- $e = $self->executeFullEmailValidation($email, $toValidate);
-
- return $e;
- }
- // {{{ protected function executeFullEmailValidation
- /**
- * Execute the validation
- *
- * This function will execute the full email vs tld
- * validation using an array of tlds passed to it.
- *
- * @access public
- * @param string $email The email to validate.
- * @param array $arrayOfTLDs The array of the TLDs to validate
- * @return true or false (Depending on if it validates or if it does not)
- */
- function executeFullEmailValidation($email, $arrayOfTLDs)
- {
- $emailEnding = explode('.', $email);
- $emailEnding = $emailEnding[count($emailEnding)-1];
-
- foreach ($arrayOfTLDs as $validator => $keys) {
- if (in_array($emailEnding, $keys)) {
- return true;
- }
- }
- return false;
- }
- // }}}
-
- /**
- * Validate an email
- *
- * @param string $email email to validate
- * @param mixed boolean (BC) $check_domain Check or not if the domain exists
- * array $options associative array of options
- * 'check_domain' boolean Check or not if the domain exists
- * 'use_rfc822' boolean Apply the full RFC822 grammar
- *
- * @return boolean true if valid email, false if not
- *
- * @access public
- */
- function email($email, $options = null)
- {
- $check_domain = false;
- $use_rfc822 = false;
- if (is_bool($options)) {
- $check_domain = $options;
- } elseif (is_array($options)) {
- extract($options);
- }
-
- /**
- * @todo Fix bug here.. even if it passes this, it won't be passing
- * The regular expression below
- */
- if (isset($fullTLDValidation)) {
- $valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
-
- if (!$valid) {
- return false;
- }
- }
-
- // the base regexp for address
- $regex = '&^(?: # recipient:
- ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
- ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
- @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
- (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
- (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
- ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
- \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
- $&xi';
-
- if ($use_rfc822? Validate::__emailRFC822($email, $options) :
- preg_match($regex, $email)) {
- if ($check_domain && function_exists('checkdnsrr')) {
- list (, $domain) = explode('@', $email);
- if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
- return true;
- }
- return false;
- }
- return true;
- }
- return false;
- }
-
- /**
- * Validate a string using the given format 'format'
- *
- * @param string $string String to validate
- * @param array $options Options array where:
- * 'format' is the format of the string
- * Ex: VALIDATE_NUM . VALIDATE_ALPHA (see constants)
- * 'min_length' minimum length
- * 'max_length' maximum length
- *
- * @return boolean true if valid string, false if not
- *
- * @access public
- */
- function string($string, $options)
- {
- $format = null;
- $min_length = $max_length = 0;
- if (is_array($options)) {
- extract($options);
- }
- if ($format && !preg_match("|^[$format]*\$|s", $string)) {
- return false;
- }
- if ($min_length && strlen($string) < $min_length) {
- return false;
- }
- if ($max_length && strlen($string) > $max_length) {
- return false;
- }
- return true;
- }
-
- /**
- * Validate an URI (RFC2396)
- * This function will validate 'foobarstring' by default, to get it to validate
- * only http, https, ftp and such you have to pass it in the allowed_schemes
- * option, like this:
- * <code>
- * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
- * var_dump(Validate::uri('http://www.example.org', $options));
- * </code>
- *
- * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
- * e.g. http://example.co-m should be valid
- * However, as '-' is not used in any known TLD, it is invalid
- * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
- * including an authority can be valid, no relative URIs
- * the // are mandatory (optionally preceeded by the 'sheme:' )
- * NOTE 3: the full complience to rfc2396 is not achieved by default
- * the characters ';/?:@$,' will not be accepted in the query part
- * if not urlencoded, refer to the option "strict'"
- *
- * @param string $url URI to validate
- * @param array $options Options used by the validation method.
- * key => type
- * 'domain_check' => boolean
- * Whether to check the DNS entry or not
- * 'allowed_schemes' => array, list of protocols
- * List of allowed schemes ('http',
- * 'ssh+svn', 'mms')
- * 'strict' => string the refused chars
- * in query and fragment parts
- * default: ';/?:@$,'
- * empty: accept all rfc2396 foreseen chars
- *
- * @return boolean true if valid uri, false if not
- *
- * @access public
- */
- function uri($url, $options = null)
- {
- $strict = ';/?:@$,';
- $domain_check = false;
- $allowed_schemes = null;
- if (is_array($options)) {
- extract($options);
- }
- if (preg_match(
- '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
- (?:// # authority start
- (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
- (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
- |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
- (?::([0-9]*))?) # 5. authority-port
- ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
- (?:\?([^#]*))? # 7. query
- (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
- $&xi', $url, $matches)) {
- $scheme = isset($matches[1]) ? $matches[1] : '';
- $authority = isset($matches[3]) ? $matches[3] : '' ;
- if (is_array($allowed_schemes) &&
- !in_array($scheme,$allowed_schemes)
- ) {
- return false;
- }
- if (!empty($matches[4])) {
- $parts = explode('.', $matches[4]);
- foreach ($parts as $part) {
- if ($part > 255) {
- return false;
- }
- }
- } elseif ($domain_check && function_exists('checkdnsrr')) {
- if (!checkdnsrr($authority, 'A')) {
- return false;
- }
- }
- if ($strict) {
- $strict = '#[' . preg_quote($strict, '#') . ']#';
- if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
- || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * Validate date and times. Note that this method need the Date_Calc class
- *
- * @param string $date Date to validate
- * @param array $options array options where :
- * 'format' The format of the date (%d-%m-%Y)
- * or rfc822_compliant
- * 'min' The date has to be greater
- * than this array($day, $month, $year)
- * or PEAR::Date object
- * 'max' The date has to be smaller than
- * this array($day, $month, $year)
- * or PEAR::Date object
- *
- * @return boolean true if valid date/time, false if not
- *
- * @access public
- */
- function date($date, $options)
- {
- $max = $min = false;
- $format = '';
- if (is_array($options)) {
- extract($options);
- }
-
- if (strtolower($format) == 'rfc822_compliant') {
- $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
- (?:(\d{2})?) \s+
- (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
- (?:(\d{2}(\d{2})?)?) \s+
- (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
- (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
-
- if (!preg_match($preg, $date, $matches)) {
- return false;
- }
-
- $year = (int)$matches[4];
- $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
- $month = array_keys($months, $matches[3]);
- $month = (int)$month[0]+1;
- $day = (int)$matches[2];
- $weekday= $matches[1];
- $hour = (int)$matches[6];
- $minute = (int)$matches[7];
- isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
-
- if ((strlen($year) != 4) ||
- ($day > 31 || $day < 1)||
- ($hour > 23) ||
- ($minute > 59) ||
- ($second > 59)) {
- return false;
- }
- } else {
- $date_len = strlen($format);
- for ($i = 0; $i < $date_len; $i++) {
- $c = $format{$i};
- if ($c == '%') {
- $next = $format{$i + 1};
- switch ($next) {
- case 'j':
- case 'd':
- if ($next == 'j') {
- $day = (int)Validate::_substr($date, 1, 2);
- } else {
- $day = (int)Validate::_substr($date, 0, 2);
- }
- if ($day < 1 || $day > 31) {
- return false;
- }
- break;
- case 'm':
- case 'n':
- if ($next == 'm') {
- $month = (int)Validate::_substr($date, 0, 2);
- } else {
- $month = (int)Validate::_substr($date, 1, 2);
- }
- if ($month < 1 || $month > 12) {
- return false;
- }
- break;
- case 'Y':
- case 'y':
- if ($next == 'Y') {
- $year = Validate::_substr($date, 4);
- $year = (int)$year?$year:'';
- } else {
- $year = (int)(substr(date('Y'), 0, 2) .
- Validate::_substr($date, 2));
- }
- if (strlen($year) != 4 || $year < 0 || $year > 9999) {
- return false;
- }
- break;
- case 'g':
- case 'h':
- if ($next == 'g') {
- $hour = Validate::_substr($date, 1, 2);
- } else {
- $hour = Validate::_substr($date, 2);
- }
- if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
- return false;
- }
- break;
- case 'G':
- case 'H':
- if ($next == 'G') {
- $hour = Validate::_substr($date, 1, 2);
- } else {
- $hour = Validate::_substr($date, 2);
- }
- if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
- return false;
- }
- break;
- case 's':
- case 'i':
- $t = Validate::_substr($date, 2);
- if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
- return false;
- }
- break;
- default:
- trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
- }
- $i++;
- } else {
- //literal
- if (Validate::_substr($date, 1) != $c) {
- return false;
- }
- }
- }
- }
- // there is remaing data, we don't want it
- if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
- return false;
- }
-
- if (isset($day) && isset($month) && isset($year)) {
- if (!checkdate($month, $day, $year)) {
- return false;
- }
-
- if (strtolower($format) == 'rfc822_compliant') {
- if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
- return false;
- }
- }
-
- if ($min) {
- include_once 'Date/Calc.php';
- if (is_a($min, 'Date') &&
- (Date_Calc::compareDates($day, $month, $year,
- $min->getDay(), $min->getMonth(), $min->getYear()) < 0))
- {
- return false;
- } elseif (is_array($min) &&
- (Date_Calc::compareDates($day, $month, $year,
- $min[0], $min[1], $min[2]) < 0))
- {
- return false;
- }
- }
-
- if ($max) {
- include_once 'Date/Calc.php';
- if (is_a($max, 'Date') &&
- (Date_Calc::compareDates($day, $month, $year,
- $max->getDay(), $max->getMonth(), $max->getYear()) > 0))
- {
- return false;
- } elseif (is_array($max) &&
- (Date_Calc::compareDates($day, $month, $year,
- $max[0], $max[1], $max[2]) > 0))
- {
- return false;
- }
- }
- }
-
- return true;
- }
-
- function _substr(&$date, $num, $opt = false)
- {
- if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
- $ret = $m[0];
- } else {
- $ret = substr($date, 0, $num);
- }
- $date = substr($date, strlen($ret));
- return $ret;
- }
-
- function _modf($val, $div) {
- if (function_exists('bcmod')) {
- return bcmod($val, $div);
- } elseif (function_exists('fmod')) {
- return fmod($val, $div);
- }
- $r = $val / $div;
- $i = intval($r);
- return intval($val - $i * $div + .1);
- }
-
- /**
- * Calculates sum of product of number digits with weights
- *
- * @param string $number number string
- * @param array $weights reference to array of weights
- *
- * @returns int returns product of number digits with weights
- *
- * @access protected
- */
- function _multWeights($number, &$weights) {
- if (!is_array($weights)) {
- return -1;
- }
- $sum = 0;
-
- $count = min(count($weights), strlen($number));
- if ($count == 0) { // empty string or weights array
- return -1;
- }
- for ($i = 0; $i < $count; ++$i) {
- $sum += intval(substr($number, $i, 1)) * $weights[$i];
- }
-
- return $sum;
- }
-
- /**
- * Calculates control digit for a given number
- *
- * @param string $number number string
- * @param array $weights reference to array of weights
- * @param int $modulo (optionsl) number
- * @param int $subtract (optional) number
- * @param bool $allow_high (optional) true if function can return number higher than 10
- *
- * @returns int -1 calculated control number is returned
- *
- * @access protected
- */
- function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false) {
- // calc sum
- $sum = Validate::_multWeights($number, $weights);
- if ($sum == -1) {
- return -1;
- }
- $mod = Validate::_modf($sum, $modulo); // calculate control digit
-
- if ($subtract > $mod && $mod > 0) {
- $mod = $subtract - $mod;
- }
- if ($allow_high === false) {
- $mod %= 10; // change 10 to zero
- }
- return $mod;
- }
-
- /**
- * Validates a number
- *
- * @param string $number number to validate
- * @param array $weights reference to array of weights
- * @param int $modulo (optionsl) number
- * @param int $subtract (optional) numbier
- *
- * @returns bool true if valid, false if not
- *
- * @access protected
- */
- function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0) {
- if (strlen($number) < count($weights)) {
- return false;
- }
- $target_digit = substr($number, count($weights), 1);
- $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
-
- if ($control_digit == -1) {
- return false;
- }
- if ($target_digit === 'X' && $control_digit == 10) {
- return true;
- }
- if ($control_digit != $target_digit) {
- return false;
- }
- return true;
- }
-
- /**
- * Bulk data validation for data introduced in the form of an
- * assoc array in the form $var_name => $value.
- * Can be used on any of Validate subpackages
- *
- * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
- * @param array $val_type Contains the validation type and all parameters used in.
- * 'val_type' is not optional
- * others validations properties must have the same name as the function
- * parameters.
- * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
- * @param boolean $remove if set, the elements not listed in data will be removed
- *
- * @return array value name => true|false the value name comes from the data key
- *
- * @access public
- */
- function multiple(&$data, &$val_type, $remove = false)
- {
- $keys = array_keys($data);
- $valid = array();
- foreach ($keys as $var_name) {
- if (!isset($val_type[$var_name])) {
- if ($remove) {
- unset($data[$var_name]);
- }
- continue;
- }
- $opt = $val_type[$var_name];
- $methods = get_class_methods('Validate');
- $val2check = $data[$var_name];
- // core validation method
- if (in_array(strtolower($opt['type']), $methods)) {
- //$opt[$opt['type']] = $data[$var_name];
- $method = $opt['type'];
- unset($opt['type']);
-
- if (sizeof($opt) == 1 && is_array(reset($opt))) {
- $opt = array_pop($opt);
- }
- $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
-
- /**
- * external validation method in the form:
- * "<class name><underscore><method name>"
- * Ex: us_ssn will include class Validate/US.php and call method ssn()
- */
- } elseif (strpos($opt['type'], '_') !== false) {
- $validateType = explode('_', $opt['type']);
- $method = array_pop($validateType);
- $class = implode('_', $validateType);
- $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
- $class = 'Validate_' . $class;
- if (!@include_once "Validate/$classPath.php") {
- trigger_error("$class isn't installed or you may have some permissoin issues", E_USER_ERROR);
- }
-
- $ce = substr(phpversion(), 0, 1) > 4 ? class_exists($class, false) : class_exists($class);
- if (!$ce ||
- !in_array($method, get_class_methods($class)))
- {
- trigger_error("Invalid validation type $class::$method", E_USER_WARNING);
- continue;
- }
- unset($opt['type']);
- if (sizeof($opt) == 1) {
- $opt = array_pop($opt);
- }
- $valid[$var_name] = call_user_func(array($class, $method), $data[$var_name], $opt);
- } else {
- trigger_error("Invalid validation type {$opt['type']}", E_USER_WARNING);
- }
- }
- return $valid;
- }
-}
-
+<?php
+/**
+ * Validation class
+ *
+ * Copyright (c) 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox, Amir Saied
+ *
+ * This source file is subject to the New BSD license, That is bundled
+ * with this package in the file LICENSE, and is available through
+ * the world-wide-web at
+ * http://www.opensource.org/licenses/bsd-license.php
+ * If you did not receive a copy of the new BSDlicense and are unable
+ * to obtain it through the world-wide-web, please send a note to
+ * pajoye@php.net so we can mail you a copy immediately.
+ *
+ * Author: Tomas V.V.Cox <cox@idecnet.com>
+ * Pierre-Alain Joye <pajoye@php.net>
+ * Amir Mohammad Saied <amir@php.net>
+ *
+ *
+ * Package to validate various datas. It includes :
+ * - numbers (min/max, decimal or not)
+ * - email (syntax, domain check)
+ * - string (predifined type alpha upper and/or lowercase, numeric,...)
+ * - date (min, max, rfc822 compliant)
+ * - uri (RFC2396)
+ * - possibility valid multiple data with a single method call (::multiple)
+ *
+ * @category Validate
+ * @package Validate
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Pierre-Alain Joye <pajoye@php.net>
+ * @author Amir Mohammad Saied <amir@php.net>
+ * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Validate.php,v 1.134 2009/01/28 12:27:33 davidc Exp $
+ * @link http://pear.php.net/package/Validate
+ */
+
+/**
+ * Methods for common data validations
+ */
+define('VALIDATE_NUM', '0-9');
+define('VALIDATE_SPACE', '\s');
+define('VALIDATE_ALPHA_LOWER', 'a-z');
+define('VALIDATE_ALPHA_UPPER', 'A-Z');
+define('VALIDATE_ALPHA', VALIDATE_ALPHA_LOWER . VALIDATE_ALPHA_UPPER);
+define('VALIDATE_EALPHA_LOWER', VALIDATE_ALPHA_LOWER . 'áéíóúýàèìòùäëïöüÿâêîôûãñõ¨åæç½ðøþß');
+define('VALIDATE_EALPHA_UPPER', VALIDATE_ALPHA_UPPER . 'ÁÉÍÓÚÝÀÈÌÒÙÄËÏÖܾÂÊÎÔÛÃÑÕ¦ÅÆǼÐØÞ');
+define('VALIDATE_EALPHA', VALIDATE_EALPHA_LOWER . VALIDATE_EALPHA_UPPER);
+define('VALIDATE_PUNCTUATION', VALIDATE_SPACE . '\.,;\:&"\'\?\!\(\)');
+define('VALIDATE_NAME', VALIDATE_EALPHA . VALIDATE_SPACE . "'" . "-");
+define('VALIDATE_STREET', VALIDATE_NUM . VALIDATE_NAME . "/\\ºª\.");
+
+define('VALIDATE_ITLD_EMAILS', 1);
+define('VALIDATE_GTLD_EMAILS', 2);
+define('VALIDATE_CCTLD_EMAILS', 4);
+define('VALIDATE_ALL_EMAILS', 8);
+
+/**
+ * Validation class
+ *
+ * Package to validate various datas. It includes :
+ * - numbers (min/max, decimal or not)
+ * - email (syntax, domain check)
+ * - string (predifined type alpha upper and/or lowercase, numeric,...)
+ * - date (min, max)
+ * - uri (RFC2396)
+ * - possibility valid multiple data with a single method call (::multiple)
+ *
+ * @category Validate
+ * @package Validate
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Pierre-Alain Joye <pajoye@php.net>
+ * @author Amir Mohammad Saied <amir@php.net>
+ * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Validate
+ */
+class Validate
+{
+ /**
+ * International Top-Level Domain
+ *
+ * This is an array of the known international
+ * top-level domain names.
+ *
+ * @access protected
+ * @var array $_iTld (International top-level domains)
+ */
+ var $_itld = array(
+ 'arpa',
+ 'root',
+ );
+
+ /**
+ * Generic top-level domain
+ *
+ * This is an array of the official
+ * generic top-level domains.
+ *
+ * @access protected
+ * @var array $_gTld (Generic top-level domains)
+ */
+ var $_gtld = array(
+ 'aero',
+ 'biz',
+ 'cat',
+ 'com',
+ 'coop',
+ 'edu',
+ 'gov',
+ 'info',
+ 'int',
+ 'jobs',
+ 'mil',
+ 'mobi',
+ 'museum',
+ 'name',
+ 'net',
+ 'org',
+ 'pro',
+ 'travel',
+ 'asia',
+ 'post',
+ 'tel',
+ 'geo',
+ );
+
+ /**
+ * Country code top-level domains
+ *
+ * This is an array of the official country
+ * codes top-level domains
+ *
+ * @access protected
+ * @var array $_ccTld (Country Code Top-Level Domain)
+ */
+ var $_cctld = array(
+ 'ac',
+ 'ad','ae','af','ag',
+ 'ai','al','am','an',
+ 'ao','aq','ar','as',
+ 'at','au','aw','ax',
+ 'az','ba','bb','bd',
+ 'be','bf','bg','bh',
+ 'bi','bj','bm','bn',
+ 'bo','br','bs','bt',
+ 'bu','bv','bw','by',
+ 'bz','ca','cc','cd',
+ 'cf','cg','ch','ci',
+ 'ck','cl','cm','cn',
+ 'co','cr','cs','cu',
+ 'cv','cx','cy','cz',
+ 'de','dj','dk','dm',
+ 'do','dz','ec','ee',
+ 'eg','eh','er','es',
+ 'et','eu','fi','fj',
+ 'fk','fm','fo','fr',
+ 'ga','gb','gd','ge',
+ 'gf','gg','gh','gi',
+ 'gl','gm','gn','gp',
+ 'gq','gr','gs','gt',
+ 'gu','gw','gy','hk',
+ 'hm','hn','hr','ht',
+ 'hu','id','ie','il',
+ 'im','in','io','iq',
+ 'ir','is','it','je',
+ 'jm','jo','jp','ke',
+ 'kg','kh','ki','km',
+ 'kn','kp','kr','kw',
+ 'ky','kz','la','lb',
+ 'lc','li','lk','lr',
+ 'ls','lt','lu','lv',
+ 'ly','ma','mc','md',
+ 'me','mg','mh','mk',
+ 'ml','mm','mn','mo',
+ 'mp','mq','mr','ms',
+ 'mt','mu','mv','mw',
+ 'mx','my','mz','na',
+ 'nc','ne','nf','ng',
+ 'ni','nl','no','np',
+ 'nr','nu','nz','om',
+ 'pa','pe','pf','pg',
+ 'ph','pk','pl','pm',
+ 'pn','pr','ps','pt',
+ 'pw','py','qa','re',
+ 'ro','rs','ru','rw',
+ 'sa','sb','sc','sd',
+ 'se','sg','sh','si',
+ 'sj','sk','sl','sm',
+ 'sn','so','sr','st',
+ 'su','sv','sy','sz',
+ 'tc','td','tf','tg',
+ 'th','tj','tk','tl',
+ 'tm','tn','to','tp',
+ 'tr','tt','tv','tw',
+ 'tz','ua','ug','uk',
+ 'us','uy','uz','va',
+ 'vc','ve','vg','vi',
+ 'vn','vu','wf','ws',
+ 'ye','yt','yu','za',
+ 'zm','zw',
+ );
+
+ /**
+ * Validate a tag URI (RFC4151)
+ *
+ * @param string $uri tag URI to validate
+ *
+ * @return boolean true if valid tag URI, false if not
+ *
+ * @access private
+ */
+ function __uriRFC4151($uri)
+ {
+ $datevalid = false;
+ if (preg_match(
+ '/^tag:(?<name>.*),(?<date>\d{4}-?\d{0,2}-?\d{0,2}):(?<specific>.*)(.*:)*$/', $uri, $matches)) {
+ $date = $matches['date'];
+ $date6 = strtotime($date);
+ if ((strlen($date) == 4) && $date <= date('Y')) {
+ $datevalid = true;
+ } elseif ((strlen($date) == 7) && ($date6 < strtotime("now"))) {
+ $datevalid = true;
+ } elseif ((strlen($date) == 10) && ($date6 < strtotime("now"))) {
+ $datevalid = true;
+ }
+ if (self::email($matches['name'])) {
+ $namevalid = true;
+ } else {
+ $namevalid = self::email('info@' . $matches['name']);
+ }
+ return $datevalid && $namevalid;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Validate a number
+ *
+ * @param string $number Number to validate
+ * @param array $options array where:
+ * 'decimal' is the decimal char or false when decimal
+ * not allowed.
+ * i.e. ',.' to allow both ',' and '.'
+ * 'dec_prec' Number of allowed decimals
+ * 'min' minimum value
+ * 'max' maximum value
+ *
+ * @return boolean true if valid number, false if not
+ *
+ * @access public
+ */
+ function number($number, $options = array())
+ {
+ $decimal = $dec_prec = $min = $max = null;
+ if (is_array($options)) {
+ extract($options);
+ }
+
+ $dec_prec = $dec_prec ? "{1,$dec_prec}" : '+';
+ $dec_regex = $decimal ? "[$decimal][0-9]$dec_prec" : '';
+
+ if (!preg_match("|^[-+]?\s*[0-9]+($dec_regex)?\$|", $number)) {
+ return false;
+ }
+
+ if ($decimal != '.') {
+ $number = strtr($number, $decimal, '.');
+ }
+
+ $number = (float)str_replace(' ', '', $number);
+ if ($min !== null && $min > $number) {
+ return false;
+ }
+
+ if ($max !== null && $max < $number) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Converting a string to UTF-7 (RFC 2152)
+ *
+ * @param string $string string to be converted
+ *
+ * @return string converted string
+ *
+ * @access private
+ */
+ function __stringToUtf7($string)
+ {
+ $return = '';
+ $utf7 = array(
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
+ '3', '4', '5', '6', '7', '8', '9', '+', ','
+ );
+
+
+ $state = 0;
+
+ if (!empty($string)) {
+ $i = 0;
+ while ($i <= strlen($string)) {
+ $char = substr($string, $i, 1);
+ if ($state == 0) {
+ if ((ord($char) >= 0x7F) || (ord($char) <= 0x1F)) {
+ if ($char) {
+ $return .= '&';
+ }
+ $state = 1;
+ } elseif ($char == '&') {
+ $return .= '&-';
+ } else {
+ $return .= $char;
+ }
+ } elseif (($i == strlen($string) ||
+ !((ord($char) >= 0x7F)) || (ord($char) <= 0x1F))) {
+ if ($state != 1) {
+ if (ord($char) > 64) {
+ $return .= '';
+ } else {
+ $return .= $utf7[ord($char)];
+ }
+ }
+ $return .= '-';
+ $state = 0;
+ } else {
+ switch($state) {
+ case 1:
+ $return .= $utf7[ord($char) >> 2];
+ $residue = (ord($char) & 0x03) << 4;
+ $state = 2;
+ break;
+ case 2:
+ $return .= $utf7[$residue | (ord($char) >> 4)];
+ $residue = (ord($char) & 0x0F) << 2;
+ $state = 3;
+ break;
+ case 3:
+ $return .= $utf7[$residue | (ord($char) >> 6)];
+ $return .= $utf7[ord($char) & 0x3F];
+ $state = 1;
+ break;
+ }
+ }
+ $i++;
+ }
+ return $return;
+ }
+ return '';
+ }
+
+ /**
+ * Validate an email according to full RFC822 (inclusive human readable part)
+ *
+ * @param string $email email to validate,
+ * will return the address for optional dns validation
+ * @param array $options email() options
+ *
+ * @return boolean true if valid email, false if not
+ *
+ * @access private
+ */
+ function __emailRFC822(&$email, &$options)
+ {
+ static $address = null;
+ static $uncomment = null;
+ if (!$address) {
+ // atom = 1*<any CHAR except specials, SPACE and CTLs>
+ $atom = '[^][()<>@,;:\\".\s\000-\037\177-\377]+\s*';
+ // qtext = <any CHAR excepting <">, ; => may be folded
+ // "\" & CR, and including linear-white-space>
+ $qtext = '[^"\\\\\r]';
+ // quoted-pair = "\" CHAR ; may quote any char
+ $quoted_pair = '\\\\.';
+ // quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
+ // ; quoted chars.
+ $quoted_string = '"(?:' . $qtext . '|' . $quoted_pair . ')*"\s*';
+ // word = atom / quoted-string
+ $word = '(?:' . $atom . '|' . $quoted_string . ')';
+ // local-part = word *("." word) ; uninterpreted
+ // ; case-preserved
+ $local_part = $word . '(?:\.\s*' . $word . ')*';
+ // dtext = <any CHAR excluding "[", ; => may be folded
+ // "]", "\" & CR, & including linear-white-space>
+ $dtext = '[^][\\\\\r]';
+ // domain-literal = "[" *(dtext / quoted-pair) "]"
+ $domain_literal = '\[(?:' . $dtext . '|' . $quoted_pair . ')*\]\s*';
+ // sub-domain = domain-ref / domain-literal
+ // domain-ref = atom ; symbolic reference
+ $sub_domain = '(?:' . $atom . '|' . $domain_literal . ')';
+ // domain = sub-domain *("." sub-domain)
+ $domain = $sub_domain . '(?:\.\s*' . $sub_domain . ')*';
+ // addr-spec = local-part "@" domain ; global address
+ $addr_spec = $local_part . '@\s*' . $domain;
+ // route = 1#("@" domain) ":" ; path-relative
+ $route = '@' . $domain . '(?:,@\s*' . $domain . ')*:\s*';
+ // route-addr = "<" [route] addr-spec ">"
+ $route_addr = '<\s*(?:' . $route . ')?' . $addr_spec . '>\s*';
+ // phrase = 1*word ; Sequence of words
+ $phrase = $word . '+';
+ // mailbox = addr-spec ; simple address
+ // / phrase route-addr ; name & addr-spec
+ $mailbox = '(?:' . $addr_spec . '|' . $phrase . $route_addr . ')';
+ // group = phrase ":" [#mailbox] ";"
+ $group = $phrase . ':\s*(?:' . $mailbox . '(?:,\s*' . $mailbox . ')*)?;\s*';
+ // address = mailbox ; one addressee
+ // / group ; named list
+ $address = '/^\s*(?:' . $mailbox . '|' . $group . ')$/';
+
+ $uncomment =
+ '/((?:(?:\\\\"|[^("])*(?:' . $quoted_string .
+ ')?)*)((?<!\\\\)\((?:(?2)|.)*?(?<!\\\\)\))/';
+ }
+ // strip comments
+ $email = preg_replace($uncomment, '$1 ', $email);
+ return preg_match($address, $email);
+ }
+
+ /**
+ * Full TLD Validation function
+ *
+ * This function is used to make a much more proficient validation
+ * against all types of official domain names.
+ *
+ * @param string $email The email address to check.
+ * @param array $options The options for validation
+ *
+ * @access protected
+ *
+ * @return bool True if validating succeeds
+ */
+ function _fullTLDValidation($email, $options)
+ {
+ $validate = array();
+ if(!empty($options["VALIDATE_ITLD_EMAILS"])) array_push($validate, 'itld');
+ if(!empty($options["VALIDATE_GTLD_EMAILS"])) array_push($validate, 'gtld');
+ if(!empty($options["VALIDATE_CCTLD_EMAILS"])) array_push($validate, 'cctld');
+
+ $self = new Validate;
+
+ $toValidate = array();
+
+ foreach ($validate as $valid) {
+ $tmpVar = '_' . (string)$valid;
+
+ $toValidate[$valid] = $self->{$tmpVar};
+ }
+
+ $e = $self->executeFullEmailValidation($email, $toValidate);
+
+ return $e;
+ }
+
+ /**
+ * Execute the validation
+ *
+ * This function will execute the full email vs tld
+ * validation using an array of tlds passed to it.
+ *
+ * @param string $email The email to validate.
+ * @param array $arrayOfTLDs The array of the TLDs to validate
+ *
+ * @access public
+ *
+ * @return true or false (Depending on if it validates or if it does not)
+ */
+ function executeFullEmailValidation($email, $arrayOfTLDs)
+ {
+ $emailEnding = explode('.', $email);
+ $emailEnding = $emailEnding[count($emailEnding)-1];
+ foreach ($arrayOfTLDs as $validator => $keys) {
+ if (in_array($emailEnding, $keys)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Validate an email
+ *
+ * @param string $email email to validate
+ * @param mixed boolean (BC) $check_domain Check or not if the domain exists
+ * array $options associative array of options
+ * 'check_domain' boolean Check or not if the domain exists
+ * 'use_rfc822' boolean Apply the full RFC822 grammar
+ *
+ * Ex.
+ * $options = array(
+ * 'check_domain' => 'true',
+ * 'fullTLDValidation' => 'true',
+ * 'use_rfc822' => 'true',
+ * 'VALIDATE_GTLD_EMAILS' => 'true',
+ * 'VALIDATE_CCTLD_EMAILS' => 'true',
+ * 'VALIDATE_ITLD_EMAILS' => 'true',
+ * );
+ *
+ * @return boolean true if valid email, false if not
+ *
+ * @access public
+ */
+ function email($email, $options = null)
+ {
+ $check_domain = false;
+ $use_rfc822 = false;
+ if (is_bool($options)) {
+ $check_domain = $options;
+ } elseif (is_array($options)) {
+ extract($options);
+ }
+
+ /**
+ * Check for IDN usage so we can encode the domain as Punycode
+ * before continuing.
+ */
+ $hasIDNA = false;
+
+ if (@include_once('Net/IDNA.php')) {
+ $hasIDNA = true;
+ }
+
+ if ($hasIDNA === true) {
+ if (strpos($email, '@') !== false) {
+ list($name, $domain) = explode('@', $email, 2);
+
+ // Check if the domain contains characters > 127 which means
+ // it's an idn domain name.
+ $chars = count_chars($domain, 1);
+ if (!empty($chars) && max(array_keys($chars)) > 127) {
+ $idna =& Net_IDNA::singleton();
+ $domain = $idna->encode($domain);
+ }
+
+ $email = "$name@$domain";
+ }
+ }
+
+ /**
+ * @todo Fix bug here.. even if it passes this, it won't be passing
+ * The regular expression below
+ */
+ if (isset($fullTLDValidation)) {
+ //$valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
+ $valid = Validate::_fullTLDValidation($email, $options);
+
+ if (!$valid) {
+ return false;
+ }
+ }
+
+ // the base regexp for address
+ $regex = '&^(?: # recipient:
+ ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
+ ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
+ @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
+ (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
+ (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
+ ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
+ \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
+ $&xi';
+
+ //checks if exists the domain (MX or A)
+ if ($use_rfc822? Validate::__emailRFC822($email, $options) :
+ preg_match($regex, $email)) {
+ if ($check_domain && function_exists('checkdnsrr')) {
+ list ($account, $domain) = explode('@', $email);
+ if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Validate a string using the given format 'format'
+ *
+ * @param string $string String to validate
+ * @param array $options Options array where:
+ * 'format' is the format of the string
+ * Ex:VALIDATE_NUM . VALIDATE_ALPHA (see constants)
+ * 'min_length' minimum length
+ * 'max_length' maximum length
+ *
+ * @return boolean true if valid string, false if not
+ *
+ * @access public
+ */
+ function string($string, $options)
+ {
+ $format = null;
+ $min_length = 0;
+ $max_length = 0;
+
+ if (is_array($options)) {
+ extract($options);
+ }
+
+ if ($format && !preg_match("|^[$format]*\$|s", $string)) {
+ return false;
+ }
+
+ if ($min_length && strlen($string) < $min_length) {
+ return false;
+ }
+
+ if ($max_length && strlen($string) > $max_length) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate an URI (RFC2396)
+ * This function will validate 'foobarstring' by default, to get it to validate
+ * only http, https, ftp and such you have to pass it in the allowed_schemes
+ * option, like this:
+ * <code>
+ * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
+ * var_dump(Validate::uri('http://www.example.org', $options));
+ * </code>
+ *
+ * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
+ * e.g. http://example.co-m should be valid
+ * However, as '-' is not used in any known TLD, it is invalid
+ * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
+ * including an authority can be valid, no relative URIs
+ * the // are mandatory (optionally preceeded by the 'sheme:' )
+ * NOTE 3: the full complience to rfc2396 is not achieved by default
+ * the characters ';/?:@$,' will not be accepted in the query part
+ * if not urlencoded, refer to the option "strict'"
+ *
+ * @param string $url URI to validate
+ * @param array $options Options used by the validation method.
+ * key => type
+ * 'domain_check' => boolean
+ * Whether to check the DNS entry or not
+ * 'allowed_schemes' => array, list of protocols
+ * List of allowed schemes ('http',
+ * 'ssh+svn', 'mms')
+ * 'strict' => string the refused chars
+ * in query and fragment parts
+ * default: ';/?:@$,'
+ * empty: accept all rfc2396 foreseen chars
+ *
+ * @return boolean true if valid uri, false if not
+ *
+ * @access public
+ */
+ function uri($url, $options = null)
+ {
+ $strict = ';/?:@$,';
+ $domain_check = false;
+ $allowed_schemes = null;
+ if (is_array($options)) {
+ extract($options);
+ }
+ if (is_array($allowed_schemes) &&
+ in_array("tag", $allowed_schemes)
+ ) {
+ if (strpos($url, "tag:") === 0) {
+ return self::__uriRFC4151($url);
+ }
+ }
+
+ if (preg_match(
+ '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
+ (?:// # authority start
+ (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
+ (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
+ |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
+ (?::([0-9]*))?) # 5. authority-port
+ ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
+ (?:\?([^#]*))? # 7. query
+ (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
+ $&xi', $url, $matches)) {
+ $scheme = isset($matches[1]) ? $matches[1] : '';
+ $authority = isset($matches[3]) ? $matches[3] : '' ;
+ if (is_array($allowed_schemes) &&
+ !in_array($scheme, $allowed_schemes)
+ ) {
+ return false;
+ }
+ if (!empty($matches[4])) {
+ $parts = explode('.', $matches[4]);
+ foreach ($parts as $part) {
+ if ($part > 255) {
+ return false;
+ }
+ }
+ } elseif ($domain_check && function_exists('checkdnsrr')) {
+ if (!checkdnsrr($authority, 'A')) {
+ return false;
+ }
+ }
+ if ($strict) {
+ $strict = '#[' . preg_quote($strict, '#') . ']#';
+ if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
+ || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Validate date and times. Note that this method need the Date_Calc class
+ *
+ * @param string $date Date to validate
+ * @param array $options array options where :
+ * 'format' The format of the date (%d-%m-%Y)
+ * or rfc822_compliant
+ * 'min' The date has to be greater
+ * than this array($day, $month, $year)
+ * or PEAR::Date object
+ * 'max' The date has to be smaller than
+ * this array($day, $month, $year)
+ * or PEAR::Date object
+ *
+ * @return boolean true if valid date/time, false if not
+ *
+ * @access public
+ */
+ function date($date, $options)
+ {
+ $max = false;
+ $min = false;
+ $format = '';
+
+ if (is_array($options)) {
+ extract($options);
+ }
+
+ if (strtolower($format) == 'rfc822_compliant') {
+ $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
+ (?:(\d{2})?) \s+
+ (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
+ (?:(\d{2}(\d{2})?)?) \s+
+ (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
+ (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
+
+ if (!preg_match($preg, $date, $matches)) {
+ return false;
+ }
+
+ $year = (int)$matches[4];
+ $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
+ $month = array_keys($months, $matches[3]);
+ $month = (int)$month[0]+1;
+ $day = (int)$matches[2];
+ $weekday = $matches[1];
+ $hour = (int)$matches[6];
+ $minute = (int)$matches[7];
+ isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
+
+ if ((strlen($year) != 4) ||
+ ($day > 31 || $day < 1)||
+ ($hour > 23) ||
+ ($minute > 59) ||
+ ($second > 59)) {
+ return false;
+ }
+ } else {
+ $date_len = strlen($format);
+ for ($i = 0; $i < $date_len; $i++) {
+ $c = $format{$i};
+ if ($c == '%') {
+ $next = $format{$i + 1};
+ switch ($next) {
+ case 'j':
+ case 'd':
+ if ($next == 'j') {
+ $day = (int)Validate::_substr($date, 1, 2);
+ } else {
+ $day = (int)Validate::_substr($date, 0, 2);
+ }
+ if ($day < 1 || $day > 31) {
+ return false;
+ }
+ break;
+ case 'm':
+ case 'n':
+ if ($next == 'm') {
+ $month = (int)Validate::_substr($date, 0, 2);
+ } else {
+ $month = (int)Validate::_substr($date, 1, 2);
+ }
+ if ($month < 1 || $month > 12) {
+ return false;
+ }
+ break;
+ case 'Y':
+ case 'y':
+ if ($next == 'Y') {
+ $year = Validate::_substr($date, 4);
+ $year = (int)$year?$year:'';
+ } else {
+ $year = (int)(substr(date('Y'), 0, 2) .
+ Validate::_substr($date, 2));
+ }
+ if (strlen($year) != 4 || $year < 0 || $year > 9999) {
+ return false;
+ }
+ break;
+ case 'g':
+ case 'h':
+ if ($next == 'g') {
+ $hour = Validate::_substr($date, 1, 2);
+ } else {
+ $hour = Validate::_substr($date, 2);
+ }
+ if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
+ return false;
+ }
+ break;
+ case 'G':
+ case 'H':
+ if ($next == 'G') {
+ $hour = Validate::_substr($date, 1, 2);
+ } else {
+ $hour = Validate::_substr($date, 2);
+ }
+ if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
+ return false;
+ }
+ break;
+ case 's':
+ case 'i':
+ $t = Validate::_substr($date, 2);
+ if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
+ return false;
+ }
+ break;
+ default:
+ trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
+ }
+ $i++;
+ } else {
+ //literal
+ if (Validate::_substr($date, 1) != $c) {
+ return false;
+ }
+ }
+ }
+ }
+ // there is remaing data, we don't want it
+ if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
+ return false;
+ }
+
+ if (isset($day) && isset($month) && isset($year)) {
+ if (!checkdate($month, $day, $year)) {
+ return false;
+ }
+
+ if (strtolower($format) == 'rfc822_compliant') {
+ if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
+ return false;
+ }
+ }
+
+ if ($min) {
+ include_once 'Date/Calc.php';
+ if (is_a($min, 'Date') &&
+ (Date_Calc::compareDates($day, $month, $year,
+ $min->getDay(), $min->getMonth(), $min->getYear()) < 0)
+ ) {
+ return false;
+ } elseif (is_array($min) &&
+ (Date_Calc::compareDates($day, $month, $year,
+ $min[0], $min[1], $min[2]) < 0)
+ ) {
+ return false;
+ }
+ }
+
+ if ($max) {
+ include_once 'Date/Calc.php';
+ if (is_a($max, 'Date') &&
+ (Date_Calc::compareDates($day, $month, $year,
+ $max->getDay(), $max->getMonth(), $max->getYear()) > 0)
+ ) {
+ return false;
+ } elseif (is_array($max) &&
+ (Date_Calc::compareDates($day, $month, $year,
+ $max[0], $max[1], $max[2]) > 0)
+ ) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Substr
+ *
+ * @param string &$date Date
+ * @param string $num Length
+ * @param string $opt Unknown
+ *
+ * @access private
+ * @return string
+ */
+ function _substr(&$date, $num, $opt = false)
+ {
+ if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
+ $ret = $m[0];
+ } else {
+ $ret = substr($date, 0, $num);
+ }
+ $date = substr($date, strlen($ret));
+ return $ret;
+ }
+
+ function _modf($val, $div)
+ {
+ if (function_exists('bcmod')) {
+ return bcmod($val, $div);
+ } elseif (function_exists('fmod')) {
+ return fmod($val, $div);
+ }
+ $r = $val / $div;
+ $i = intval($r);
+ return intval($val - $i * $div + .1);
+ }
+
+ /**
+ * Calculates sum of product of number digits with weights
+ *
+ * @param string $number number string
+ * @param array $weights reference to array of weights
+ *
+ * @access protected
+ *
+ * @return int returns product of number digits with weights
+ */
+ function _multWeights($number, &$weights)
+ {
+ if (!is_array($weights)) {
+ return -1;
+ }
+ $sum = 0;
+
+ $count = min(count($weights), strlen($number));
+ if ($count == 0) { // empty string or weights array
+ return -1;
+ }
+ for ($i = 0; $i < $count; ++$i) {
+ $sum += intval(substr($number, $i, 1)) * $weights[$i];
+ }
+
+ return $sum;
+ }
+
+ /**
+ * Calculates control digit for a given number
+ *
+ * @param string $number number string
+ * @param array $weights reference to array of weights
+ * @param int $modulo (optionsl) number
+ * @param int $subtract (optional) number
+ * @param bool $allow_high (optional) true if function can return number higher than 10
+ *
+ * @access protected
+ *
+ * @return int -1 calculated control number is returned
+ */
+ function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false)
+ {
+ // calc sum
+ $sum = Validate::_multWeights($number, $weights);
+ if ($sum == -1) {
+ return -1;
+ }
+ $mod = Validate::_modf($sum, $modulo); // calculate control digit
+
+ if ($subtract > $mod && $mod > 0) {
+ $mod = $subtract - $mod;
+ }
+ if ($allow_high === false) {
+ $mod %= 10; // change 10 to zero
+ }
+ return $mod;
+ }
+
+ /**
+ * Validates a number
+ *
+ * @param string $number number to validate
+ * @param array $weights reference to array of weights
+ * @param int $modulo (optional) number
+ * @param int $subtract (optional) number
+ *
+ * @access protected
+ *
+ * @return bool true if valid, false if not
+ */
+ function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0)
+ {
+ if (strlen($number) < count($weights)) {
+ return false;
+ }
+ $target_digit = substr($number, count($weights), 1);
+ $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
+
+ if ($control_digit == -1) {
+ return false;
+ }
+ if ($target_digit === 'X' && $control_digit == 10) {
+ return true;
+ }
+ if ($control_digit != $target_digit) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Bulk data validation for data introduced in the form of an
+ * assoc array in the form $var_name => $value.
+ * Can be used on any of Validate subpackages
+ *
+ * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
+ * @param array $val_type Contains the validation type and all parameters used in.
+ * 'val_type' is not optional
+ * others validations properties must have the same name as the function
+ * parameters.
+ * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
+ * @param boolean $remove if set, the elements not listed in data will be removed
+ *
+ * @return array value name => true|false the value name comes from the data key
+ *
+ * @access public
+ */
+ function multiple(&$data, &$val_type, $remove = false)
+ {
+ $keys = array_keys($data);
+ $valid = array();
+
+ foreach ($keys as $var_name) {
+ if (!isset($val_type[$var_name])) {
+ if ($remove) {
+ unset($data[$var_name]);
+ }
+ continue;
+ }
+ $opt = $val_type[$var_name];
+ $methods = get_class_methods('Validate');
+ $val2check = $data[$var_name];
+ // core validation method
+ if (in_array(strtolower($opt['type']), $methods)) {
+ //$opt[$opt['type']] = $data[$var_name];
+ $method = $opt['type'];
+ unset($opt['type']);
+
+ if (sizeof($opt) == 1 && is_array(reset($opt))) {
+ $opt = array_pop($opt);
+ }
+ $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
+
+ /**
+ * external validation method in the form:
+ * "<class name><underscore><method name>"
+ * Ex: us_ssn will include class Validate/US.php and call method ssn()
+ */
+ } elseif (strpos($opt['type'], '_') !== false) {
+ $validateType = explode('_', $opt['type']);
+ $method = array_pop($validateType);
+ $class = implode('_', $validateType);
+ $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
+ $class = 'Validate_' . $class;
+ if (!@include_once "Validate/$classPath.php") {
+ trigger_error("$class isn't installed or you may have some permissoin issues", E_USER_ERROR);
+ }
+
+ $ce = substr(phpversion(), 0, 1) > 4 ?
+ class_exists($class, false) : class_exists($class);
+ if (!$ce ||
+ !in_array($method, get_class_methods($class))
+ ) {
+ trigger_error("Invalid validation type $class::$method",
+ E_USER_WARNING);
+ continue;
+ }
+ unset($opt['type']);
+ if (sizeof($opt) == 1) {
+ $opt = array_pop($opt);
+ }
+ $valid[$var_name] = call_user_func(array($class, $method),
+ $data[$var_name], $opt);
+ } else {
+ trigger_error("Invalid validation type {$opt['type']}",
+ E_USER_WARNING);
+ }
+ }
+ return $valid;
+ }
+}
+
diff --git a/extlib/facebook/facebook.php b/extlib/facebook/facebook.php
index 35de6be50..fee1dd086 100644
--- a/extlib/facebook/facebook.php
+++ b/extlib/facebook/facebook.php
@@ -1,5 +1,5 @@
<?php
-// Copyright 2004-2008 Facebook. All Rights Reserved.
+// Copyright 2004-2009 Facebook. All Rights Reserved.
//
// +---------------------------------------------------------------------------+
// | Facebook Platform PHP5 client |
@@ -30,13 +30,12 @@
// +---------------------------------------------------------------------------+
// | For help with this library, contact developers-help@facebook.com |
// +---------------------------------------------------------------------------+
-//
+
include_once 'facebookapi_php5_restlib.php';
define('FACEBOOK_API_VALIDATION_ERROR', 1);
class Facebook {
public $api_client;
-
public $api_key;
public $secret;
public $generate_session_secret;
@@ -213,28 +212,55 @@ class Facebook {
}
}
- // Invalidate the session currently being used, and clear any state associated with it
+ // Invalidate the session currently being used, and clear any state associated
+ // with it. Note that the user will still remain logged into Facebook.
public function expire_session() {
if ($this->api_client->auth_expireSession()) {
- if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) {
- $cookies = array('user', 'session_key', 'expires', 'ss');
- foreach ($cookies as $name) {
- setcookie($this->api_key . '_' . $name, false, time() - 3600);
- unset($_COOKIE[$this->api_key . '_' . $name]);
- }
- setcookie($this->api_key, false, time() - 3600);
- unset($_COOKIE[$this->api_key]);
- }
-
- // now, clear the rest of the stored state
- $this->user = 0;
- $this->api_client->session_key = 0;
+ $this->clear_cookie_state();
return true;
} else {
return false;
}
}
+ /** Logs the user out of all temporary application sessions as well as their
+ * Facebook session. Note this will only work if the user has a valid current
+ * session with the application.
+ *
+ * @param string $next URL to redirect to upon logging out
+ *
+ */
+ public function logout($next) {
+ $logout_url = $this->get_logout_url($next);
+
+ // Clear any stored state
+ $this->clear_cookie_state();
+
+ $this->redirect($logout_url);
+ }
+
+ /**
+ * Clears any persistent state stored about the user, including
+ * cookies and information related to the current session in the
+ * client.
+ *
+ */
+ public function clear_cookie_state() {
+ if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) {
+ $cookies = array('user', 'session_key', 'expires', 'ss');
+ foreach ($cookies as $name) {
+ setcookie($this->api_key . '_' . $name, false, time() - 3600);
+ unset($_COOKIE[$this->api_key . '_' . $name]);
+ }
+ setcookie($this->api_key, false, time() - 3600);
+ unset($_COOKIE[$this->api_key]);
+ }
+
+ // now, clear the rest of the stored state
+ $this->user = 0;
+ $this->api_client->session_key = 0;
+ }
+
public function redirect($url) {
if ($this->in_fb_canvas()) {
echo '<fb:redirect url="' . $url . '"/>';
@@ -249,7 +275,8 @@ class Facebook {
}
public function in_frame() {
- return isset($this->fb_params['in_canvas']) || isset($this->fb_params['in_iframe']);
+ return isset($this->fb_params['in_canvas'])
+ || isset($this->fb_params['in_iframe']);
}
public function in_fb_canvas() {
return isset($this->fb_params['in_canvas']);
@@ -296,14 +323,42 @@ class Facebook {
}
public function get_add_url($next=null) {
- return self::get_facebook_url().'/add.php?api_key='.$this->api_key .
- ($next ? '&next=' . urlencode($next) : '');
+ $page = self::get_facebook_url().'/add.php';
+ $params = array('api_key' => $this->api_key);
+
+ if ($next) {
+ $params['next'] = $next;
+ }
+
+ return $page . '?' . http_build_query($params);
}
public function get_login_url($next, $canvas) {
- return self::get_facebook_url().'/login.php?v=1.0&api_key=' . $this->api_key .
- ($next ? '&next=' . urlencode($next) : '') .
- ($canvas ? '&canvas' : '');
+ $page = self::get_facebook_url().'/login.php';
+ $params = array('api_key' => $this->api_key,
+ 'v' => '1.0');
+
+ if ($next) {
+ $params['next'] = $next;
+ }
+ if ($canvas) {
+ $params['canvas'] = '1';
+ }
+
+ return $page . '?' . http_build_query($params);
+ }
+
+ public function get_logout_url($next) {
+ $page = self::get_facebook_url().'/logout.php';
+ $params = array('app_key' => $this->api_key,
+ 'session_key' => $this->api_client->session_key);
+
+ if ($next) {
+ $params['connect_next'] = 1;
+ $params['next'] = $next;
+ }
+
+ return $page . '?' . http_build_query($params);
}
public function set_user($user, $session_key, $expires=null, $session_secret=null) {
@@ -410,7 +465,20 @@ class Facebook {
return $fb_params;
}
- /*
+ /**
+ * Validates the account that a user was trying to set up an
+ * independent account through Facebook Connect.
+ *
+ * @param user The user attempting to set up an independent account.
+ * @param hash The hash passed to the reclamation URL used.
+ * @return bool True if the user is the one that selected the
+ * reclamation link.
+ */
+ public function verify_account_reclamation($user, $hash) {
+ return $hash == md5($user . $this->secret);
+ }
+
+ /**
* Validates that a given set of parameters match their signature.
* Parameters all match a given input prefix, such as "fb_sig".
*
@@ -422,6 +490,37 @@ class Facebook {
return self::generate_sig($fb_params, $this->secret) == $expected_sig;
}
+ /**
+ * Validate the given signed public session data structure with
+ * public key of the app that
+ * the session proof belongs to.
+ *
+ * @param $signed_data the session info that is passed by another app
+ * @param string $public_key Optional public key of the app. If this
+ * is not passed, function will make an API call to get it.
+ * return true if the session proof passed verification.
+ */
+ public function verify_signed_public_session_data($signed_data,
+ $public_key = null) {
+
+ // If public key is not already provided, we need to get it through API
+ if (!$public_key) {
+ $public_key = $this->api_client->auth_getAppPublicKey(
+ $signed_data['api_key']);
+ }
+
+ // Create data to verify
+ $data_to_serialize = $signed_data;
+ unset($data_to_serialize['sig']);
+ $serialized_data = implode('_', $data_to_serialize);
+
+ // Decode signature
+ $signature = base64_decode($signed_data['sig']);
+ $result = openssl_verify($serialized_data, $signature, $public_key,
+ OPENSSL_ALGO_SHA1);
+ return $result == 1;
+ }
+
/*
* Generate a signature using the application secret key.
*
diff --git a/extlib/facebook/facebook_desktop.php b/extlib/facebook/facebook_desktop.php
index 90cdf66bd..e79a2ca34 100644
--- a/extlib/facebook/facebook_desktop.php
+++ b/extlib/facebook/facebook_desktop.php
@@ -1,5 +1,5 @@
<?php
-// Copyright 2004-2008 Facebook. All Rights Reserved.
+// Copyright 2004-2009 Facebook. All Rights Reserved.
//
// +---------------------------------------------------------------------------+
// | Facebook Platform PHP5 client |
diff --git a/extlib/facebook/facebookapi_php5_restlib.php b/extlib/facebook/facebookapi_php5_restlib.php
index 389f40a9d..3fec06e8a 100644..100755
--- a/extlib/facebook/facebookapi_php5_restlib.php
+++ b/extlib/facebook/facebookapi_php5_restlib.php
@@ -1,9 +1,10 @@
<?php
+// Copyright 2004-2009 Facebook. All Rights Reserved.
//
// +---------------------------------------------------------------------------+
// | Facebook Platform PHP5 client |
// +---------------------------------------------------------------------------+
-// | Copyright (c) 2007-2008 Facebook, Inc. |
+// | Copyright (c) 2007-2009 Facebook, Inc. |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
@@ -32,6 +33,7 @@
//
include_once 'jsonwrapper/jsonwrapper.php';
+
class FacebookRestClient {
public $secret;
public $session_key;
@@ -50,7 +52,9 @@ class FacebookRestClient {
public $canvas_user;
public $batch_mode;
private $batch_queue;
+ private $pending_batch;
private $call_as_apikey;
+ private $use_curl_if_available;
const BATCH_MODE_DEFAULT = 0;
const BATCH_MODE_SERVER_PARALLEL = 0;
@@ -70,7 +74,8 @@ class FacebookRestClient {
$this->batch_mode = FacebookRestClient::BATCH_MODE_DEFAULT;
$this->last_call_id = 0;
$this->call_as_apikey = '';
- $this->server_addr = Facebook::get_facebook_url('api') . '/restserver.php';
+ $this->use_curl_if_available = true;
+ $this->server_addr = Facebook::get_facebook_url('api') . '/restserver.php';
if (!empty($GLOBALS['facebook_config']['debug'])) {
$this->cur_id = 0;
@@ -123,39 +128,61 @@ function toggleDisplay(id, type) {
}
/**
+ * Normally, if the cURL library/PHP extension is available, it is used for
+ * HTTP transactions. This allows that behavior to be overridden, falling
+ * back to a vanilla-PHP implementation even if cURL is installed.
+ *
+ * @param $use_curl_if_available bool whether or not to use cURL if available
+ */
+ public function set_use_curl_if_available($use_curl_if_available) {
+ $this->use_curl_if_available = $use_curl_if_available;
+ }
+
+ /**
* Start a batch operation.
*/
public function begin_batch() {
- if($this->batch_queue !== null) {
+ if ($this->pending_batch()) {
$code = FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED;
- throw new FacebookRestClientException($code,
- FacebookAPIErrorCodes::$api_error_descriptions[$code]);
+ $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
+ throw new FacebookRestClientException($description, $code);
}
$this->batch_queue = array();
+ $this->pending_batch = true;
}
/*
* End current batch operation
*/
public function end_batch() {
- if($this->batch_queue === null) {
+ if (!$this->pending_batch()) {
$code = FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED;
- throw new FacebookRestClientException($code,
- FacebookAPIErrorCodes::$api_error_descriptions[$code]);
+ $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
+ throw new FacebookRestClientException($description, $code);
}
- $this->execute_server_side_batch();
+ $this->pending_batch = false;
+ $this->execute_server_side_batch();
$this->batch_queue = null;
}
+ /**
+ * are we currently queueing up calls for a batch?
+ */
+ public function pending_batch() {
+ return $this->pending_batch;
+ }
+
private function execute_server_side_batch() {
$item_count = count($this->batch_queue);
$method_feed = array();
foreach($this->batch_queue as $batch_item) {
- $method_feed[] = $this->create_post_string($batch_item['m'],
- $batch_item['p']);
+ $method = $batch_item['m'];
+ $params = $batch_item['p'];
+ $this->finalize_params($method, $params);
+ $method_feed[] = $this->create_post_string($method, $params);
}
$method_feed_json = json_encode($method_feed);
@@ -202,6 +229,18 @@ function toggleDisplay(id, type) {
$this->call_as_apikey = '';
}
+
+ /*
+ * If a page is loaded via HTTPS, then all images and static
+ * resources need to be printed with HTTPS urls to avoid
+ * mixed content warnings. If your page loads with an HTTPS
+ * url, then call set_use_ssl_resources to retrieve the correct
+ * urls.
+ */
+ public function set_use_ssl_resources($is_ssl = true) {
+ $this->use_ssl_resources = $is_ssl;
+ }
+
/**
* Returns public information for an application (as shown in the application
* directory) by either application ID, API key, or canvas page name.
@@ -231,7 +270,7 @@ function toggleDisplay(id, type) {
* @return string An authentication token.
*/
public function auth_createToken() {
- return $this->call_method('facebook.auth.createToken', array());
+ return $this->call_method('facebook.auth.createToken');
}
/**
@@ -246,8 +285,7 @@ function toggleDisplay(id, type) {
* @return array An assoc array containing session_key, uid
*/
public function auth_getSession($auth_token, $generate_session_secret=false) {
- //Check if we are in batch mode
- if($this->batch_queue === null) {
+ if (!$this->pending_batch()) {
$result = $this->call_method('facebook.auth.getSession',
array('auth_token' => $auth_token,
'generate_session_secret' => $generate_session_secret));
@@ -271,7 +309,7 @@ function toggleDisplay(id, type) {
* API_EC_PARAM_UNKNOWN
*/
public function auth_promoteSession() {
- return $this->call_method('facebook.auth.promoteSession', array());
+ return $this->call_method('facebook.auth.promoteSession');
}
/**
@@ -282,7 +320,20 @@ function toggleDisplay(id, type) {
* @return bool true if session expiration was successful, false otherwise
*/
public function auth_expireSession() {
- return $this->call_method('facebook.auth.expireSession', array());
+ return $this->call_method('facebook.auth.expireSession');
+ }
+
+ /**
+ * Revokes the given extended permission that the user granted at some
+ * prior time (for instance, offline_access or email). If no user is
+ * provided, it will be revoked for the user of the current session.
+ *
+ * @param string $perm The permission to revoke
+ * @param int $uid The user for whom to revoke the permission.
+ */
+ public function auth_revokeExtendedPermission($perm, $uid=null) {
+ return $this->call_method('facebook.auth.revokeExtendedPermission',
+ array('perm' => $perm, 'uid' => $uid));
}
/**
@@ -303,6 +354,30 @@ function toggleDisplay(id, type) {
}
/**
+ * Get public key that is needed to verify digital signature
+ * an app may pass to other apps. The public key is only used by
+ * other apps for verification purposes.
+ * @param string API key of an app
+ * @return string The public key for the app.
+ */
+ public function auth_getAppPublicKey($target_app_key) {
+ return $this->call_method('facebook.auth.getAppPublicKey',
+ array('target_app_key' => $target_app_key));
+ }
+
+ /**
+ * Get a structure that can be passed to another app
+ * as proof of session. The other app can verify it using public
+ * key of this app.
+ *
+ * @return signed public session data structure.
+ */
+ public function auth_getSignedPublicSessionData() {
+ return $this->call_method('facebook.auth.getSignedPublicSessionData',
+ array());
+ }
+
+ /**
* Returns the number of unconnected friends that exist in this application.
* This number is determined based on the accounts registered through
* connect.registerUsers() (see below).
@@ -363,8 +438,9 @@ function toggleDisplay(id, type) {
*
* @param int $uid (Optional) User associated with events. A null
* parameter will default to the session user.
- * @param array $eids (Optional) Filter by these event ids. A null
- * parameter will get all events for the user.
+ * @param array/string $eids (Optional) Filter by these event
+ * ids. A null parameter will get all events for
+ * the user. (A csv list will work but is deprecated)
* @param int $start_time (Optional) Filter with this unix time as lower
* bound. A null or zero parameter indicates no
* lower bound.
@@ -718,12 +794,15 @@ function toggleDisplay(id, type) {
* @param string $body_general (Optional) Additional markup that extends
* the body of a short story.
* @param int $story_size (Optional) A story size (see above)
+ * @param string $user_message (Optional) A user message for a short
+ * story.
*
* @return bool true on success
*/
public function &feed_publishUserAction(
$template_bundle_id, $template_data, $target_ids='', $body_general='',
- $story_size=FacebookRestClient::STORY_SIZE_ONE_LINE) {
+ $story_size=FacebookRestClient::STORY_SIZE_ONE_LINE,
+ $user_message='') {
if (is_array($template_data)) {
$template_data = json_encode($template_data);
@@ -739,7 +818,107 @@ function toggleDisplay(id, type) {
'template_data' => $template_data,
'target_ids' => $target_ids,
'body_general' => $body_general,
- 'story_size' => $story_size));
+ 'story_size' => $story_size,
+ 'user_message' => $user_message));
+ }
+
+
+ /**
+ * Publish a post to the user's stream.
+ *
+ * @param $message the user's message
+ * @param $attachment the post's attachment (optional)
+ * @param $action links the post's action links (optional)
+ * @param $target_id the user on whose wall the post will be posted
+ * (optional)
+ * @param $uid the actor (defaults to session user)
+ * @return string the post id
+ */
+ public function stream_publish(
+ $message, $attachment = null, $action_links = null, $target_id = null,
+ $uid = null) {
+
+ return $this->call_method(
+ 'facebook.stream.publish',
+ array('message' => $message,
+ 'attachment' => $attachment,
+ 'action_links' => $action_links,
+ 'target_id' => $target_id,
+ 'uid' => $this->get_uid($uid)));
+ }
+
+ /**
+ * Remove a post from the user's stream.
+ * Currently, you may only remove stories you application created.
+ *
+ * @param $post_id the post id
+ * @param $uid the actor (defaults to session user)
+ * @return bool
+ */
+ public function stream_remove($post_id, $uid = null) {
+ return $this->call_method(
+ 'facebook.stream.remove',
+ array('post_id' => $post_id,
+ 'uid' => $this->get_uid($uid)));
+ }
+
+ /**
+ * Add a comment to a stream post
+ *
+ * @param $post_id the post id
+ * @param $comment the comment text
+ * @param $uid the actor (defaults to session user)
+ * @return string the id of the created comment
+ */
+ public function stream_addComment($post_id, $comment, $uid = null) {
+ return $this->call_method(
+ 'facebook.stream.addComment',
+ array('post_id' => $post_id,
+ 'comment' => $comment,
+ 'uid' => $this->get_uid($uid)));
+ }
+
+
+ /**
+ * Remove a comment from a stream post
+ *
+ * @param $comment_id the comment id
+ * @param $uid the actor (defaults to session user)
+ * @return bool
+ */
+ public function stream_removeComment($comment_id, $uid = null) {
+ return $this->call_method(
+ 'facebook.stream.removeComment',
+ array('comment_id' => $comment_id,
+ 'uid' => $this->get_uid($uid)));
+ }
+
+ /**
+ * Add a like to a stream post
+ *
+ * @param $post_id the post id
+ * @param $uid the actor (defaults to session user)
+ * @return bool
+ */
+ public function stream_addLike($post_id, $uid = null) {
+ return $this->call_method(
+ 'facebook.stream.addLike',
+ array('post_id' => $post_id,
+ 'uid' => $this->get_uid($uid)));
+ }
+
+ /**
+ * Remove a like from a stream post
+ *
+ * @param $post_id the post id
+ * @param $uid the actor (defaults to session user)
+ * @return bool
+ */
+ public function stream_removeLike($post_id, $uid = null) {
+ return $this->call_method(
+ 'facebook.stream.removeLike',
+ array('post_id' => $post_id,
+ 'uid' => $this->get_uid($uid)));
}
/**
@@ -750,7 +929,7 @@ function toggleDisplay(id, type) {
* @return array An array of feed story objects.
*/
public function &feed_getAppFriendStories() {
- return $this->call_method('facebook.feed.getAppFriendStories', array());
+ return $this->call_method('facebook.feed.getAppFriendStories');
}
/**
@@ -771,33 +950,42 @@ function toggleDisplay(id, type) {
* Returns whether or not pairs of users are friends.
* Note that the Facebook friend relationship is symmetric.
*
- * @param array $uids1 array of ids (id_1, id_2,...) of some length X
- * @param array $uids2 array of ids (id_A, id_B,...) of SAME length X
+ * @param array/string $uids1 list of ids (id_1, id_2,...)
+ * of some length X (csv is deprecated)
+ * @param array/string $uids2 list of ids (id_A, id_B,...)
+ * of SAME length X (csv is deprecated)
*
* @return array An array with uid1, uid2, and bool if friends, e.g.:
* array(0 => array('uid1' => id_1, 'uid2' => id_A, 'are_friends' => 1),
* 1 => array('uid1' => id_2, 'uid2' => id_B, 'are_friends' => 0)
* ...)
+ * @error
+ * API_EC_PARAM_USER_ID_LIST
*/
public function &friends_areFriends($uids1, $uids2) {
return $this->call_method('facebook.friends.areFriends',
- array('uids1' => $uids1, 'uids2' => $uids2));
+ array('uids1' => $uids1,
+ 'uids2' => $uids2));
}
/**
* Returns the friends of the current session user.
*
* @param int $flid (Optional) Only return friends on this friend list.
+ * @param int $uid (Optional) Return friends for this user.
*
* @return array An array of friends
*/
- public function &friends_get($flid=null) {
+ public function &friends_get($flid=null, $uid = null) {
if (isset($this->friends_list)) {
return $this->friends_list;
}
$params = array();
- if (isset($this->canvas_user)) {
- $params['uid'] = $this->canvas_user;
+ if (!$uid && isset($this->canvas_user)) {
+ $uid = $this->canvas_user;
+ }
+ if ($uid) {
+ $params['uid'] = $uid;
}
if ($flid) {
$params['flid'] = $flid;
@@ -812,7 +1000,7 @@ function toggleDisplay(id, type) {
* @return array An array of friend list objects
*/
public function &friends_getLists() {
- return $this->call_method('facebook.friends.getLists', array());
+ return $this->call_method('facebook.friends.getLists');
}
/**
@@ -822,7 +1010,7 @@ function toggleDisplay(id, type) {
* @return array An array of friends also using the app
*/
public function &friends_getAppUsers() {
- return $this->call_method('facebook.friends.getAppUsers', array());
+ return $this->call_method('facebook.friends.getAppUsers');
}
/**
@@ -830,8 +1018,9 @@ function toggleDisplay(id, type) {
*
* @param int $uid (Optional) User associated with groups. A null
* parameter will default to the session user.
- * @param array $gids (Optional) Group ids to query. A null parameter will
- * get all groups for the user.
+ * @param array/string $gids (Optional) Array of group ids to query. A null
+ * parameter will get all groups for the user.
+ * (csv is deprecated)
*
* @return array An array of group objects
*/
@@ -890,6 +1079,40 @@ function toggleDisplay(id, type) {
}
/**
+ * Retrieves links posted by the given user.
+ *
+ * @param int $uid The user whose links you wish to retrieve
+ * @param int $limit The maximimum number of links to retrieve
+ * @param array $link_ids (Optional) Array of specific link
+ * IDs to retrieve by this user
+ *
+ * @return array An array of links.
+ */
+ public function &links_get($uid, $limit, $link_ids = null) {
+ return $this->call_method('links.get',
+ array('uid' => $uid,
+ 'limit' => $limit,
+ 'link_ids' => $link_ids));
+ }
+
+ /**
+ * Posts a link on Facebook.
+ *
+ * @param string $url URL/link you wish to post
+ * @param string $comment (Optional) A comment about this link
+ * @param int $uid (Optional) User ID that is posting this link;
+ * defaults to current session user
+ *
+ * @return bool
+ */
+ public function &links_post($url, $comment='', $uid = null) {
+ return $this->call_method('links.post',
+ array('uid' => $uid,
+ 'url' => $url,
+ 'comment' => $comment));
+ }
+
+ /**
* Permissions API
*/
@@ -946,6 +1169,78 @@ function toggleDisplay(id, type) {
}
/**
+ * Creates a note with the specified title and content.
+ *
+ * @param string $title Title of the note.
+ * @param string $content Content of the note.
+ * @param int $uid (Optional) The user for whom you are creating a
+ * note; defaults to current session user
+ *
+ * @return int The ID of the note that was just created.
+ */
+ public function &notes_create($title, $content, $uid = null) {
+ return $this->call_method('notes.create',
+ array('uid' => $uid,
+ 'title' => $title,
+ 'content' => $content));
+ }
+
+ /**
+ * Deletes the specified note.
+ *
+ * @param int $note_id ID of the note you wish to delete
+ * @param int $uid (Optional) Owner of the note you wish to delete;
+ * defaults to current session user
+ *
+ * @return bool
+ */
+ public function &notes_delete($note_id, $uid = null) {
+ return $this->call_method('notes.delete',
+ array('uid' => $uid,
+ 'note_id' => $note_id));
+ }
+
+ /**
+ * Edits a note, replacing its title and contents with the title
+ * and contents specified.
+ *
+ * @param int $note_id ID of the note you wish to edit
+ * @param string $title Replacement title for the note
+ * @param string $content Replacement content for the note
+ * @param int $uid (Optional) Owner of the note you wish to edit;
+ * defaults to current session user
+ *
+ * @return bool
+ */
+ public function &notes_edit($note_id, $title, $content, $uid = null) {
+ return $this->call_method('notes.edit',
+ array('uid' => $uid,
+ 'note_id' => $note_id,
+ 'title' => $title,
+ 'content' => $content));
+ }
+
+ /**
+ * Retrieves all notes by a user. If note_ids are specified,
+ * retrieves only those specific notes by that user.
+ *
+ * @param int $uid User whose notes you wish to retrieve
+ * @param array $note_ids (Optional) List of specific note
+ * IDs by this user to retrieve
+ *
+ * @return array A list of all of the given user's notes, or an empty list
+ * if the viewer lacks permissions or if there are no visible
+ * notes.
+ */
+ public function &notes_get($uid, $note_ids = null) {
+
+ return $this->call_method('notes.get',
+ array('uid' => $uid,
+ 'note_ids' => $note_ids));
+ }
+
+
+ /**
* Returns the outstanding notifications for the session user.
*
* @return array An assoc array of notification count objects for
@@ -954,13 +1249,15 @@ function toggleDisplay(id, type) {
* and an eid list of 'event_invites'
*/
public function &notifications_get() {
- return $this->call_method('facebook.notifications.get', array());
+ return $this->call_method('facebook.notifications.get');
}
/**
* Sends a notification to the specified users.
*
* @return A comma separated list of successful recipients
+ * @error
+ * API_EC_PARAM_USER_ID_LIST
*/
public function &notifications_send($to_ids, $notification, $type) {
return $this->call_method('facebook.notifications.send',
@@ -972,12 +1269,14 @@ function toggleDisplay(id, type) {
/**
* Sends an email to the specified user of the application.
*
- * @param array $recipients id of the recipients
+ * @param array/string $recipients array of ids of the recipients (csv is deprecated)
* @param string $subject subject of the email
* @param string $text (plain text) body of the email
* @param string $fbml fbml markup for an html version of the email
*
* @return string A comma separated list of successful recipients
+ * @error
+ * API_EC_PARAM_USER_ID_LIST
*/
public function &notifications_sendEmail($recipients,
$subject,
@@ -993,9 +1292,9 @@ function toggleDisplay(id, type) {
/**
* Returns the requested info fields for the requested set of pages.
*
- * @param array $page_ids an array of page ids
- * @param array $fields an array of strings describing the info fields
- * desired
+ * @param array/string $page_ids an array of page ids (csv is deprecated)
+ * @param array/string $fields an array of strings describing the
+ * info fields desired (csv is deprecated)
* @param int $uid (Optional) limit results to pages of which this
* user is a fan.
* @param string type limits results to a particular type of page.
@@ -1090,7 +1389,7 @@ function toggleDisplay(id, type) {
'tag_text' => $tag_text,
'x' => $x,
'y' => $y,
- 'tags' => json_encode($tags),
+ 'tags' => (is_array($tags)) ? json_encode($tags) : null,
'owner_uid' => $this->get_uid($owner_uid)));
}
@@ -1128,7 +1427,8 @@ function toggleDisplay(id, type) {
* @param int $subj_id (Optional) Filter by uid of user tagged in the photos.
* @param int $aid (Optional) Filter by an album, as returned by
* photos_getAlbums.
- * @param array $pids (Optional) Restrict to a list of pids
+ * @param array/string $pids (Optional) Restrict to an array of pids
+ * (csv is deprecated)
*
* Note that at least one of these parameters needs to be specified, or an
* error is returned.
@@ -1143,9 +1443,10 @@ function toggleDisplay(id, type) {
/**
* Returns the albums created by the given user.
*
- * @param int $uid (Optional) The uid of the user whose albums you want.
- * A null will return the albums of the session user.
- * @param array $aids (Optional) A list of aids to restrict the query.
+ * @param int $uid (Optional) The uid of the user whose albums you want.
+ * A null will return the albums of the session user.
+ * @param string $aids (Optional) An array of aids to restrict
+ * the query. (csv is deprecated)
*
* Note that at least one of the (uid, aids) parameters must be specified.
*
@@ -1172,16 +1473,66 @@ function toggleDisplay(id, type) {
}
/**
+ * Uploads a photo.
+ *
+ * @param string $file The location of the photo on the local filesystem.
+ * @param int $aid (Optional) The album into which to upload the
+ * photo.
+ * @param string $caption (Optional) A caption for the photo.
+ * @param int uid (Optional) The user ID of the user whose photo you
+ * are uploading
+ *
+ * @return array An array of user objects
+ */
+ public function photos_upload($file, $aid=null, $caption=null, $uid=null) {
+ return $this->call_upload_method('facebook.photos.upload',
+ array('aid' => $aid,
+ 'caption' => $caption,
+ 'uid' => $uid),
+ $file);
+ }
+
+
+ /**
+ * Uploads a video.
+ *
+ * @param string $file The location of the video on the local filesystem.
+ * @param string $title (Optional) A title for the video. Titles over 65 characters in length will be truncated.
+ * @param string $description (Optional) A description for the video.
+ *
+ * @return array An array with the video's ID, title, description, and a link to view it on Facebook.
+ */
+ public function video_upload($file, $title=null, $description=null) {
+ return $this->call_upload_method('facebook.video.upload',
+ array('title' => $title,
+ 'description' => $description),
+ $file,
+ Facebook::get_facebook_url('api-video') . '/restserver.php');
+ }
+
+ /**
+ * Returns an array with the video limitations imposed on the current session's
+ * associated user. Maximum length is measured in seconds; maximum size is
+ * measured in bytes.
+ *
+ * @return array Array with "length" and "size" keys
+ */
+ public function &video_getUploadLimits() {
+ return $this->call_method('facebook.video.getUploadLimits');
+ }
+
+ /**
* Returns the requested info fields for the requested set of users.
*
- * @param array $uids An array of user ids
- * @param array $fields An array of info field names desired
+ * @param array/string $uids An array of user ids (csv is deprecated)
+ * @param array/string $fields An array of info field names desired (csv is deprecated)
*
* @return array An array of user objects
*/
public function &users_getInfo($uids, $fields) {
return $this->call_method('facebook.users.getInfo',
- array('uids' => $uids, 'fields' => $fields));
+ array('uids' => $uids,
+ 'fields' => $fields));
}
/**
@@ -1194,14 +1545,15 @@ function toggleDisplay(id, type) {
* users, use users.getInfo instead, so that proper privacy rules will be
* applied.
*
- * @param array $uids An array of user ids
- * @param array $fields An array of info field names desired
+ * @param array/string $uids An array of user ids (csv is deprecated)
+ * @param array/string $fields An array of info field names desired (csv is deprecated)
*
* @return array An array of user objects
*/
public function &users_getStandardInfo($uids, $fields) {
return $this->call_method('facebook.users.getStandardInfo',
- array('uids' => $uids, 'fields' => $fields));
+ array('uids' => $uids,
+ 'fields' => $fields));
}
/**
@@ -1210,7 +1562,7 @@ function toggleDisplay(id, type) {
* @return integer User id
*/
public function &users_getLoggedInUser() {
- return $this->call_method('facebook.users.getLoggedInUser', array());
+ return $this->call_method('facebook.users.getLoggedInUser');
}
/**
@@ -1239,6 +1591,17 @@ function toggleDisplay(id, type) {
}
/**
+ * Returns whether or not the user corresponding to the current
+ * session object is verified by Facebook. See the documentation
+ * for Users.isVerified for details.
+ *
+ * @return boolean true if the user is verified
+ */
+ public function &users_isVerified() {
+ return $this->call_method('facebook.users.isVerified');
+ }
+
+ /**
* Sets the users' current status message. Message does NOT contain the
* word "is" , so make sure to include a verb.
*
@@ -1269,6 +1632,69 @@ function toggleDisplay(id, type) {
}
/**
+ * Gets the stream on behalf of a user using a set of users. This
+ * call will return the latest $limit queries between $start_time
+ * and $end_time.
+ *
+ * @param int $viewer_id user making the call (def: session)
+ * @param array $source_ids users/pages to look at (def: all connections)
+ * @param int $start_time start time to look for stories (def: 1 day ago)
+ * @param int $end_time end time to look for stories (def: now)
+ * @param int $limit number of stories to attempt to fetch (def: 30)
+ * @param string $filter_key key returned by stream.getFilters to fetch
+ *
+ * @return array(
+ * 'posts' => array of posts,
+ * 'profiles' => array of profile metadata of users/pages in posts
+ * 'albums' => array of album metadata in posts
+ * )
+ */
+ public function &stream_get($viewer_id = null,
+ $source_ids = null,
+ $start_time = 0,
+ $end_time = 0,
+ $limit = 30,
+ $filter_key = '') {
+ $args = array(
+ 'viewer_id' => $viewer_id,
+ 'source_ids' => $source_ids,
+ 'start_time' => $start_time,
+ 'end_time' => $end_time,
+ 'limit' => $limit,
+ 'filter_key' => $filter_key);
+ return $this->call_method('facebook.stream.get', $args);
+ }
+
+ /**
+ * Gets the filters (with relevant filter keys for stream.get) for a
+ * particular user. These filters are typical things like news feed,
+ * friend lists, networks. They can be used to filter the stream
+ * without complex queries to determine which ids belong in which groups.
+ *
+ * @param int $uid user to get filters for
+ *
+ * @return array of stream filter objects
+ */
+ public function &stream_getFilters($uid = null) {
+ $args = array('uid' => $uid);
+ return $this->call_method('facebook.stream.getFilters', $args);
+ }
+
+ /**
+ * Gets the full comments given a post_id from stream.get or the
+ * stream FQL table. Initially, only a set of preview comments are
+ * returned because some posts can have many comments.
+ *
+ * @param string $post_id id of the post to get comments for
+ *
+ * @return array of comment objects
+ */
+ public function &stream_getComments($post_id) {
+ $args = array('post_id' => $post_id);
+ return $this->call_method('facebook.stream.getComments', $args);
+ }
+
+ /**
* Sets the FBML for the profile of the user attached to this session.
*
* @param string $markup The FBML that describes the profile
@@ -1690,7 +2116,7 @@ function toggleDisplay(id, type) {
* API_EC_DATA_UNKNOWN_ERROR
*/
public function &data_getObjectTypes() {
- return $this->call_method('facebook.data.getObjectTypes', array());
+ return $this->call_method('facebook.data.getObjectTypes');
}
/**
@@ -2315,12 +2741,14 @@ function toggleDisplay(id, type) {
*
* @param string $integration_point_name Name of an integration point
* (see developer wiki for list).
+ * @param int $uid Specific user to check the limit.
*
* @return int Integration point allocation value
*/
- public function &admin_getAllocation($integration_point_name) {
+ public function &admin_getAllocation($integration_point_name, $uid=null) {
return $this->call_method('facebook.admin.getAllocation',
- array('integration_point_name' => $integration_point_name));
+ array('integration_point_name' => $integration_point_name,
+ 'uid' => $uid));
}
/**
@@ -2376,28 +2804,75 @@ function toggleDisplay(id, type) {
*/
public function admin_getRestrictionInfo() {
return json_decode(
- $this->call_method('admin.getRestrictionInfo', array()),
+ $this->call_method('admin.getRestrictionInfo'),
true);
}
+
+ /**
+ * Bans a list of users from the app. Banned users can't
+ * access the app's canvas page and forums.
+ *
+ * @param array $uids an array of user ids
+ * @return bool true on success
+ */
+ public function admin_banUsers($uids) {
+ return $this->call_method(
+ 'admin.banUsers', array('uids' => json_encode($uids)));
+ }
+
+ /**
+ * Unban users that have been previously banned with
+ * admin_banUsers().
+ *
+ * @param array $uids an array of user ids
+ * @return bool true on success
+ */
+ public function admin_unbanUsers($uids) {
+ return $this->call_method(
+ 'admin.unbanUsers', array('uids' => json_encode($uids)));
+ }
+
+ /**
+ * Gets the list of users that have been banned from the application.
+ * $uids is an optional parameter that filters the result with the list
+ * of provided user ids. If $uids is provided,
+ * only banned user ids that are contained in $uids are returned.
+ *
+ * @param array $uids an array of user ids to filter by
+ * @return bool true on success
+ */
+
+ public function admin_getBannedUsers($uids = null) {
+ return $this->call_method(
+ 'admin.getBannedUsers',
+ array('uids' => $uids ? json_encode($uids) : null));
+ }
+
/* UTILITY FUNCTIONS */
/**
- * Calls the specified method with the specified parameters.
+ * Calls the specified normal POST method with the specified parameters.
*
* @param string $method Name of the Facebook method to invoke
* @param array $params A map of param names => param values
*
- * @return mixed Result of method call
+ * @return mixed Result of method call; this returns a reference to support
+ * 'delayed returns' when in a batch context.
+ * See: http://wiki.developers.facebook.com/index.php/Using_batching_API
*/
- public function & call_method($method, $params) {
- //Check if we are in batch mode
- if($this->batch_queue === null) {
+ public function &call_method($method, $params = array()) {
+ if (!$this->pending_batch()) {
if ($this->call_as_apikey) {
$params['call_as_apikey'] = $this->call_as_apikey;
}
- $xml = $this->post_request($method, $params);
- $result = $this->convert_xml_to_result($xml, $method, $params);
+ $data = $this->post_request($method, $params);
+ if (empty($params['format']) || strtolower($params['format']) != 'json') {
+ $result = $this->convert_xml_to_result($data, $method, $params);
+ }
+ else {
+ $result = json_decode($data, true);
+ }
if (is_array($result) && isset($result['error_code'])) {
throw new FacebookRestClientException($result['error_msg'],
@@ -2413,11 +2888,46 @@ function toggleDisplay(id, type) {
return $result;
}
- private function convert_xml_to_result($xml, $method, $params) {
+ /**
+ * Calls the specified file-upload POST method with the specified parameters
+ *
+ * @param string $method Name of the Facebook method to invoke
+ * @param array $params A map of param names => param values
+ * @param string $file A path to the file to upload (required)
+ *
+ * @return array A dictionary representing the response.
+ */
+ public function call_upload_method($method, $params, $file, $server_addr = null) {
+ if (!$this->pending_batch()) {
+ if (!file_exists($file)) {
+ $code =
+ FacebookAPIErrorCodes::API_EC_PARAM;
+ $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
+ throw new FacebookRestClientException($description, $code);
+ }
+
+ $xml = $this->post_upload_request($method, $params, $file, $server_addr);
+ $result = $this->convert_xml_to_result($xml, $method, $params);
+
+ if (is_array($result) && isset($result['error_code'])) {
+ throw new FacebookRestClientException($result['error_msg'],
+ $result['error_code']);
+ }
+ }
+ else {
+ $code =
+ FacebookAPIErrorCodes::API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE;
+ $description = FacebookAPIErrorCodes::$api_error_descriptions[$code];
+ throw new FacebookRestClientException($description, $code);
+ }
+
+ return $result;
+ }
+
+ protected function convert_xml_to_result($xml, $method, $params) {
$sxml = simplexml_load_string($xml);
$result = self::convert_simplexml_to_array($sxml);
-
if (!empty($GLOBALS['facebook_config']['debug'])) {
// output the raw xml and its corresponding php object, for debugging:
print '<div style="margin: 10px 30px; padding: 5px; border: 2px solid black; background: gray; color: white; font-size: 12px; font-weight: bold;">';
@@ -2436,7 +2946,25 @@ function toggleDisplay(id, type) {
return $result;
}
- private function create_post_string($method, $params) {
+ private function finalize_params($method, &$params) {
+ $this->add_standard_params($method, $params);
+ // we need to do this before signing the params
+ $this->convert_array_values_to_json($params);
+ $params['sig'] = Facebook::generate_sig($params, $this->secret);
+ }
+
+ private function convert_array_values_to_json(&$params) {
+ foreach ($params as $key => &$val) {
+ if (is_array($val)) {
+ $val = json_encode($val);
+ }
+ }
+ }
+
+ private function add_standard_params($method, &$params) {
+ if ($this->call_as_apikey) {
+ $params['call_as_apikey'] = $this->call_as_apikey;
+ }
$params['method'] = $method;
$params['session_key'] = $this->session_key;
$params['api_key'] = $this->api_key;
@@ -2448,50 +2976,118 @@ function toggleDisplay(id, type) {
if (!isset($params['v'])) {
$params['v'] = '1.0';
}
+ if (isset($this->use_ssl_resources) &&
+ $this->use_ssl_resources) {
+ $params['return_ssl_resources'] = true;
+ }
+ }
+
+ private function create_post_string($method, $params) {
$post_params = array();
foreach ($params as $key => &$val) {
- if (is_array($val)) $val = implode(',', $val);
$post_params[] = $key.'='.urlencode($val);
}
- $secret = $this->secret;
- $post_params[] = 'sig='.Facebook::generate_sig($params, $secret);
return implode('&', $post_params);
}
- public function post_request($method, $params) {
+ private function run_multipart_http_transaction($method, $params, $file, $server_addr) {
- $post_string = $this->create_post_string($method, $params);
+ // the format of this message is specified in RFC1867/RFC1341.
+ // we add twenty pseudo-random digits to the end of the boundary string.
+ $boundary = '--------------------------FbMuLtIpArT' .
+ sprintf("%010d", mt_rand()) .
+ sprintf("%010d", mt_rand());
+ $content_type = 'multipart/form-data; boundary=' . $boundary;
+ // within the message, we prepend two extra hyphens.
+ $delimiter = '--' . $boundary;
+ $close_delimiter = $delimiter . '--';
+ $content_lines = array();
+ foreach ($params as $key => &$val) {
+ $content_lines[] = $delimiter;
+ $content_lines[] = 'Content-Disposition: form-data; name="' . $key . '"';
+ $content_lines[] = '';
+ $content_lines[] = $val;
+ }
+ // now add the file data
+ $content_lines[] = $delimiter;
+ $content_lines[] =
+ 'Content-Disposition: form-data; filename="' . $file . '"';
+ $content_lines[] = 'Content-Type: application/octet-stream';
+ $content_lines[] = '';
+ $content_lines[] = file_get_contents($file);
+ $content_lines[] = $close_delimiter;
+ $content_lines[] = '';
+ $content = implode("\r\n", $content_lines);
+ return $this->run_http_post_transaction($content_type, $content, $server_addr);
+ }
- if (function_exists('curl_init')) {
- // Use CURL if installed...
+ public function post_request($method, $params) {
+ $this->finalize_params($method, $params);
+ $post_string = $this->create_post_string($method, $params);
+ if ($this->use_curl_if_available && function_exists('curl_init')) {
$useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->server_addr);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
curl_close($ch);
} else {
- // Non-CURL based version...
$content_type = 'application/x-www-form-urlencoded';
- $user_agent = 'Facebook API PHP5 Client 1.1 (non-curl) '.phpversion();
- $context =
- array('http' =>
+ $content = $post_string;
+ $result = $this->run_http_post_transaction($content_type,
+ $content,
+ $this->server_addr);
+ }
+ return $result;
+ }
+
+ private function post_upload_request($method, $params, $file, $server_addr = null) {
+ $server_addr = $server_addr ? $server_addr : $this->server_addr;
+ $this->finalize_params($method, $params);
+ if ($this->use_curl_if_available && function_exists('curl_init')) {
+ // prepending '@' causes cURL to upload the file; the key is ignored.
+ $params['_file'] = '@' . $file;
+ $useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion();
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $server_addr);
+ // this has to come before the POSTFIELDS set!
+ curl_setopt($ch, CURLOPT_POST, 1 );
+ // passing an array gets curl to use the multipart/form-data content type
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
+ $result = curl_exec($ch);
+ curl_close($ch);
+ } else {
+ $result = $this->run_multipart_http_transaction($method, $params, $file, $server_addr);
+ }
+ return $result;
+ }
+
+ private function run_http_post_transaction($content_type, $content, $server_addr) {
+
+ $user_agent = 'Facebook API PHP5 Client 1.1 (non-curl) ' . phpversion();
+ $content_length = strlen($content);
+ $context =
+ array('http' =>
array('method' => 'POST',
- 'header' => 'Content-type: '.$content_type."\r\n".
- 'User-Agent: '.$user_agent."\r\n".
- 'Content-length: ' . strlen($post_string),
- 'content' => $post_string));
- $contextid=stream_context_create($context);
- $sock=fopen($this->server_addr, 'r', false, $contextid);
- if ($sock) {
- $result='';
- while (!feof($sock))
- $result.=fgets($sock, 4096);
-
- fclose($sock);
+ 'user_agent' => $user_agent,
+ 'header' => 'Content-Type: ' . $content_type . "\r\n" .
+ 'Content-Length: ' . $content_length,
+ 'content' => $content));
+ $context_id = stream_context_create($context);
+ $sock = fopen($server_addr, 'r', false, $context_id);
+
+ $result = '';
+ if ($sock) {
+ while (!feof($sock)) {
+ $result .= fgets($sock, 4096);
}
+ fclose($sock);
}
return $result;
}
@@ -2541,6 +3137,14 @@ class FacebookAPIErrorCodes {
const API_EC_METHOD = 3;
const API_EC_TOO_MANY_CALLS = 4;
const API_EC_BAD_IP = 5;
+ const API_EC_HOST_API = 6;
+ const API_EC_HOST_UP = 7;
+ const API_EC_SECURE = 8;
+ const API_EC_RATE = 9;
+ const API_EC_PERMISSION_DENIED = 10;
+ const API_EC_DEPRECATED = 11;
+ const API_EC_VERSION = 12;
+ const API_EC_INTERNAL_FQL_ERROR = 13;
/*
* PARAMETER ERRORS
@@ -2550,27 +3154,121 @@ class FacebookAPIErrorCodes {
const API_EC_PARAM_SESSION_KEY = 102;
const API_EC_PARAM_CALL_ID = 103;
const API_EC_PARAM_SIGNATURE = 104;
+ const API_EC_PARAM_TOO_MANY = 105;
const API_EC_PARAM_USER_ID = 110;
const API_EC_PARAM_USER_FIELD = 111;
const API_EC_PARAM_SOCIAL_FIELD = 112;
+ const API_EC_PARAM_EMAIL = 113;
+ const API_EC_PARAM_USER_ID_LIST = 114;
+ const API_EC_PARAM_FIELD_LIST = 115;
const API_EC_PARAM_ALBUM_ID = 120;
+ const API_EC_PARAM_PHOTO_ID = 121;
+ const API_EC_PARAM_FEED_PRIORITY = 130;
+ const API_EC_PARAM_CATEGORY = 140;
+ const API_EC_PARAM_SUBCATEGORY = 141;
+ const API_EC_PARAM_TITLE = 142;
+ const API_EC_PARAM_DESCRIPTION = 143;
+ const API_EC_PARAM_BAD_JSON = 144;
const API_EC_PARAM_BAD_EID = 150;
const API_EC_PARAM_UNKNOWN_CITY = 151;
+ const API_EC_PARAM_BAD_PAGE_TYPE = 152;
/*
* USER PERMISSIONS ERRORS
*/
const API_EC_PERMISSION = 200;
const API_EC_PERMISSION_USER = 210;
+ const API_EC_PERMISSION_NO_DEVELOPERS = 211;
const API_EC_PERMISSION_ALBUM = 220;
const API_EC_PERMISSION_PHOTO = 221;
+ const API_EC_PERMISSION_MESSAGE = 230;
+ const API_EC_PERMISSION_OTHER_USER = 240;
+ const API_EC_PERMISSION_STATUS_UPDATE = 250;
+ const API_EC_PERMISSION_PHOTO_UPLOAD = 260;
+ const API_EC_PERMISSION_VIDEO_UPLOAD = 261;
+ const API_EC_PERMISSION_SMS = 270;
+ const API_EC_PERMISSION_CREATE_LISTING = 280;
+ const API_EC_PERMISSION_CREATE_NOTE = 281;
+ const API_EC_PERMISSION_SHARE_ITEM = 282;
const API_EC_PERMISSION_EVENT = 290;
+ const API_EC_PERMISSION_LARGE_FBML_TEMPLATE = 291;
+ const API_EC_PERMISSION_LIVEMESSAGE = 292;
const API_EC_PERMISSION_RSVP_EVENT = 299;
- const FQL_EC_PARSER = 601;
+ /*
+ * DATA EDIT ERRORS
+ */
+ const API_EC_EDIT = 300;
+ const API_EC_EDIT_USER_DATA = 310;
+ const API_EC_EDIT_PHOTO = 320;
+ const API_EC_EDIT_ALBUM_SIZE = 321;
+ const API_EC_EDIT_PHOTO_TAG_SUBJECT = 322;
+ const API_EC_EDIT_PHOTO_TAG_PHOTO = 323;
+ const API_EC_EDIT_PHOTO_FILE = 324;
+ const API_EC_EDIT_PHOTO_PENDING_LIMIT = 325;
+ const API_EC_EDIT_PHOTO_TAG_LIMIT = 326;
+ const API_EC_EDIT_ALBUM_REORDER_PHOTO_NOT_IN_ALBUM = 327;
+ const API_EC_EDIT_ALBUM_REORDER_TOO_FEW_PHOTOS = 328;
+
+ const API_EC_MALFORMED_MARKUP = 329;
+ const API_EC_EDIT_MARKUP = 330;
+
+ const API_EC_EDIT_FEED_TOO_MANY_USER_CALLS = 340;
+ const API_EC_EDIT_FEED_TOO_MANY_USER_ACTION_CALLS = 341;
+ const API_EC_EDIT_FEED_TITLE_LINK = 342;
+ const API_EC_EDIT_FEED_TITLE_LENGTH = 343;
+ const API_EC_EDIT_FEED_TITLE_NAME = 344;
+ const API_EC_EDIT_FEED_TITLE_BLANK = 345;
+ const API_EC_EDIT_FEED_BODY_LENGTH = 346;
+ const API_EC_EDIT_FEED_PHOTO_SRC = 347;
+ const API_EC_EDIT_FEED_PHOTO_LINK = 348;
+
+ const API_EC_EDIT_VIDEO_SIZE = 350;
+ const API_EC_EDIT_VIDEO_INVALID_FILE = 351;
+ const API_EC_EDIT_VIDEO_INVALID_TYPE = 352;
+ const API_EC_EDIT_VIDEO_FILE = 353;
+
+ const API_EC_EDIT_FEED_TITLE_ARRAY = 360;
+ const API_EC_EDIT_FEED_TITLE_PARAMS = 361;
+ const API_EC_EDIT_FEED_BODY_ARRAY = 362;
+ const API_EC_EDIT_FEED_BODY_PARAMS = 363;
+ const API_EC_EDIT_FEED_PHOTO = 364;
+ const API_EC_EDIT_FEED_TEMPLATE = 365;
+ const API_EC_EDIT_FEED_TARGET = 366;
+ const API_EC_EDIT_FEED_MARKUP = 367;
+
+ /**
+ * SESSION ERRORS
+ */
+ const API_EC_SESSION_TIMED_OUT = 450;
+ const API_EC_SESSION_METHOD = 451;
+ const API_EC_SESSION_INVALID = 452;
+ const API_EC_SESSION_REQUIRED = 453;
+ const API_EC_SESSION_REQUIRED_FOR_SECRET = 454;
+ const API_EC_SESSION_CANNOT_USE_SESSION_SECRET = 455;
+
+
+ /**
+ * FQL ERRORS
+ */
+ const FQL_EC_UNKNOWN_ERROR = 600;
+ const FQL_EC_PARSER = 601; // backwards compatibility
+ const FQL_EC_PARSER_ERROR = 601;
const FQL_EC_UNKNOWN_FIELD = 602;
const FQL_EC_UNKNOWN_TABLE = 603;
- const FQL_EC_NOT_INDEXABLE = 604;
+ const FQL_EC_NOT_INDEXABLE = 604; // backwards compatibility
+ const FQL_EC_NO_INDEX = 604;
+ const FQL_EC_UNKNOWN_FUNCTION = 605;
+ const FQL_EC_INVALID_PARAM = 606;
+ const FQL_EC_INVALID_FIELD = 607;
+ const FQL_EC_INVALID_SESSION = 608;
+ const FQL_EC_UNSUPPORTED_APP_TYPE = 609;
+ const FQL_EC_SESSION_SECRET_NOT_ALLOWED = 610;
+ const FQL_EC_DEPRECATED_TABLE = 611;
+ const FQL_EC_EXTENDED_PERMISSION = 612;
+ const FQL_EC_RATE_LIMIT_EXCEEDED = 613;
+
+ const API_EC_REF_SET_FAILED = 700;
/**
* DATA STORE API ERRORS
@@ -2581,52 +3279,122 @@ class FacebookAPIErrorCodes {
const API_EC_DATA_OBJECT_NOT_FOUND = 803;
const API_EC_DATA_OBJECT_ALREADY_EXISTS = 804;
const API_EC_DATA_DATABASE_ERROR = 805;
+ const API_EC_DATA_CREATE_TEMPLATE_ERROR = 806;
+ const API_EC_DATA_TEMPLATE_EXISTS_ERROR = 807;
+ const API_EC_DATA_TEMPLATE_HANDLE_TOO_LONG = 808;
+ const API_EC_DATA_TEMPLATE_HANDLE_ALREADY_IN_USE = 809;
+ const API_EC_DATA_TOO_MANY_TEMPLATE_BUNDLES = 810;
+ const API_EC_DATA_MALFORMED_ACTION_LINK = 811;
+ const API_EC_DATA_TEMPLATE_USES_RESERVED_TOKEN = 812;
/*
- * Batch ERROR
+ * APPLICATION INFO ERRORS
*/
- const API_EC_BATCH_ALREADY_STARTED = 900;
- const API_EC_BATCH_NOT_STARTED = 901;
- const API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE = 902;
+ const API_EC_NO_SUCH_APP = 900;
+ /*
+ * BATCH ERRORS
+ */
+ const API_EC_BATCH_TOO_MANY_ITEMS = 950;
+ const API_EC_BATCH_ALREADY_STARTED = 951;
+ const API_EC_BATCH_NOT_STARTED = 952;
+ const API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE = 953;
+
+ /*
+ * EVENT API ERRORS
+ */
+ const API_EC_EVENT_INVALID_TIME = 1000;
+
+ /*
+ * INFO BOX ERRORS
+ */
+ const API_EC_INFO_NO_INFORMATION = 1050;
+ const API_EC_INFO_SET_FAILED = 1051;
+
+ /*
+ * LIVEMESSAGE API ERRORS
+ */
+ const API_EC_LIVEMESSAGE_SEND_FAILED = 1100;
+ const API_EC_LIVEMESSAGE_EVENT_NAME_TOO_LONG = 1101;
+ const API_EC_LIVEMESSAGE_MESSAGE_TOO_LONG = 1102;
+
+ /*
+ * CONNECT SESSION ERRORS
+ */
+ const API_EC_CONNECT_FEED_DISABLED = 1300;
+
+ /*
+ * Platform tag bundles errors
+ */
+ const API_EC_TAG_BUNDLE_QUOTA = 1400;
+
+ /*
+ * SHARE
+ */
+ const API_EC_SHARE_BAD_URL = 1500;
+
+ /*
+ * NOTES
+ */
+ const API_EC_NOTE_CANNOT_MODIFY = 1600;
+
+ /*
+ * COMMENTS
+ */
+ const API_EC_COMMENTS_UNKNOWN = 1700;
+ const API_EC_COMMENTS_POST_TOO_LONG = 1701;
+ const API_EC_COMMENTS_DB_DOWN = 1702;
+ const API_EC_COMMENTS_INVALID_XID = 1703;
+ const API_EC_COMMENTS_INVALID_UID = 1704;
+ const API_EC_COMMENTS_INVALID_POST = 1705;
+
+ /**
+ * This array is no longer maintained; to view the description of an error
+ * code, please look at the message element of the API response or visit
+ * the developer wiki at http://wiki.developers.facebook.com/.
+ */
public static $api_error_descriptions = array(
- API_EC_SUCCESS => 'Success',
- API_EC_UNKNOWN => 'An unknown error occurred',
- API_EC_SERVICE => 'Service temporarily unavailable',
- API_EC_METHOD => 'Unknown method',
- API_EC_TOO_MANY_CALLS => 'Application request limit reached',
- API_EC_BAD_IP => 'Unauthorized source IP address',
- API_EC_PARAM => 'Invalid parameter',
- API_EC_PARAM_API_KEY => 'Invalid API key',
- API_EC_PARAM_SESSION_KEY => 'Session key invalid or no longer valid',
- API_EC_PARAM_CALL_ID => 'Call_id must be greater than previous',
- API_EC_PARAM_SIGNATURE => 'Incorrect signature',
- API_EC_PARAM_USER_ID => 'Invalid user id',
- API_EC_PARAM_USER_FIELD => 'Invalid user info field',
- API_EC_PARAM_SOCIAL_FIELD => 'Invalid user field',
- API_EC_PARAM_ALBUM_ID => 'Invalid album id',
- API_EC_PARAM_BAD_EID => 'Invalid eid',
- API_EC_PARAM_UNKNOWN_CITY => 'Unknown city',
- API_EC_PERMISSION => 'Permissions error',
- API_EC_PERMISSION_USER => 'User not visible',
- API_EC_PERMISSION_ALBUM => 'Album not visible',
- API_EC_PERMISSION_PHOTO => 'Photo not visible',
- API_EC_PERMISSION_EVENT => 'Creating and modifying events required the extended permission create_event',
- API_EC_PERMISSION_RSVP_EVENT => 'RSVPing to events required the extended permission rsvp_event',
- FQL_EC_PARSER => 'FQL: Parser Error',
- FQL_EC_UNKNOWN_FIELD => 'FQL: Unknown Field',
- FQL_EC_UNKNOWN_TABLE => 'FQL: Unknown Table',
- FQL_EC_NOT_INDEXABLE => 'FQL: Statement not indexable',
- FQL_EC_UNKNOWN_FUNCTION => 'FQL: Attempted to call unknown function',
- FQL_EC_INVALID_PARAM => 'FQL: Invalid parameter passed in',
- API_EC_DATA_UNKNOWN_ERROR => 'Unknown data store API error',
- API_EC_DATA_INVALID_OPERATION => 'Invalid operation',
- API_EC_DATA_QUOTA_EXCEEDED => 'Data store allowable quota was exceeded',
- API_EC_DATA_OBJECT_NOT_FOUND => 'Specified object cannot be found',
- API_EC_DATA_OBJECT_ALREADY_EXISTS => 'Specified object already exists',
- API_EC_DATA_DATABASE_ERROR => 'A database error occurred. Please try again',
- API_EC_BATCH_ALREADY_STARTED => 'begin_batch already called, please make sure to call end_batch first',
- API_EC_BATCH_NOT_STARTED => 'end_batch called before start_batch',
- API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE => 'This method is not allowed in batch mode',
+ self::API_EC_SUCCESS => 'Success',
+ self::API_EC_UNKNOWN => 'An unknown error occurred',
+ self::API_EC_SERVICE => 'Service temporarily unavailable',
+ self::API_EC_METHOD => 'Unknown method',
+ self::API_EC_TOO_MANY_CALLS => 'Application request limit reached',
+ self::API_EC_BAD_IP => 'Unauthorized source IP address',
+ self::API_EC_PARAM => 'Invalid parameter',
+ self::API_EC_PARAM_API_KEY => 'Invalid API key',
+ self::API_EC_PARAM_SESSION_KEY => 'Session key invalid or no longer valid',
+ self::API_EC_PARAM_CALL_ID => 'Call_id must be greater than previous',
+ self::API_EC_PARAM_SIGNATURE => 'Incorrect signature',
+ self::API_EC_PARAM_USER_ID => 'Invalid user id',
+ self::API_EC_PARAM_USER_FIELD => 'Invalid user info field',
+ self::API_EC_PARAM_SOCIAL_FIELD => 'Invalid user field',
+ self::API_EC_PARAM_USER_ID_LIST => 'Invalid user id list',
+ self::API_EC_PARAM_FIELD_LIST => 'Invalid field list',
+ self::API_EC_PARAM_ALBUM_ID => 'Invalid album id',
+ self::API_EC_PARAM_BAD_EID => 'Invalid eid',
+ self::API_EC_PARAM_UNKNOWN_CITY => 'Unknown city',
+ self::API_EC_PERMISSION => 'Permissions error',
+ self::API_EC_PERMISSION_USER => 'User not visible',
+ self::API_EC_PERMISSION_NO_DEVELOPERS => 'Application has no developers',
+ self::API_EC_PERMISSION_ALBUM => 'Album not visible',
+ self::API_EC_PERMISSION_PHOTO => 'Photo not visible',
+ self::API_EC_PERMISSION_EVENT => 'Creating and modifying events required the extended permission create_event',
+ self::API_EC_PERMISSION_RSVP_EVENT => 'RSVPing to events required the extended permission rsvp_event',
+ self::API_EC_EDIT_ALBUM_SIZE => 'Album is full',
+ self::FQL_EC_PARSER => 'FQL: Parser Error',
+ self::FQL_EC_UNKNOWN_FIELD => 'FQL: Unknown Field',
+ self::FQL_EC_UNKNOWN_TABLE => 'FQL: Unknown Table',
+ self::FQL_EC_NOT_INDEXABLE => 'FQL: Statement not indexable',
+ self::FQL_EC_UNKNOWN_FUNCTION => 'FQL: Attempted to call unknown function',
+ self::FQL_EC_INVALID_PARAM => 'FQL: Invalid parameter passed in',
+ self::API_EC_DATA_UNKNOWN_ERROR => 'Unknown data store API error',
+ self::API_EC_DATA_INVALID_OPERATION => 'Invalid operation',
+ self::API_EC_DATA_QUOTA_EXCEEDED => 'Data store allowable quota was exceeded',
+ self::API_EC_DATA_OBJECT_NOT_FOUND => 'Specified object cannot be found',
+ self::API_EC_DATA_OBJECT_ALREADY_EXISTS => 'Specified object already exists',
+ self::API_EC_DATA_DATABASE_ERROR => 'A database error occurred. Please try again',
+ self::API_EC_BATCH_ALREADY_STARTED => 'begin_batch already called, please make sure to call end_batch first',
+ self::API_EC_BATCH_NOT_STARTED => 'end_batch called before begin_batch',
+ self::API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE => 'This method is not allowed in batch mode'
);
}