Commit bf7cd7aa168b033c306e2696da658b2ff561d2c7

Authored by Conrad Vermeulen
1 parent 1e9eebea

KTS-1694

"SOAP Webservice Implementation"
Updated. SOAP requires PEAR:HTTP libs.

Reviewed By: Kevin Fourie

git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@6494 c91229c3-7414-0410-bfa2-8a42b809f60b
thirdparty/pear/HTTP/Client.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4: */
  3 +// +----------------------------------------------------------------------+
  4 +// | PHP version 4 |
  5 +// +----------------------------------------------------------------------+
  6 +// | Copyright (c) 1997-2003 The PHP Group |
  7 +// +----------------------------------------------------------------------+
  8 +// | This source file is subject to version 3.0 of the PHP license, |
  9 +// | that is bundled with this package in the file LICENSE, and is |
  10 +// | available through the world-wide-web at |
  11 +// | http://www.php.net/license/3_0.txt. |
  12 +// | If you did not receive a copy of the PHP license and are unable to |
  13 +// | obtain it through the world-wide-web, please send a note to |
  14 +// | license@php.net so we can mail you a copy immediately. |
  15 +// +----------------------------------------------------------------------+
  16 +// | Author: Alexey Borzov <avb@php.net> |
  17 +// +----------------------------------------------------------------------+
  18 +//
  19 +// $Id: Client.php,v 1.4 2004/03/23 13:35:37 avb Exp $
  20 +
  21 +require_once 'HTTP/Request.php';
  22 +require_once 'HTTP/Client/CookieManager.php';
  23 +
  24 +/**
  25 + * A simple HTTP client class.
  26 + *
  27 + * The class wraps around HTTP_Request providing a higher-level
  28 + * API for performing multiple HTTP requests
  29 + *
  30 + * @package HTTP_Client
  31 + * @author Alexey Borzov <avb@php.net>
  32 + * @version $Revision: 1.4 $
  33 + */
  34 +class HTTP_Client
  35 +{
  36 + /**
  37 + * An HTTP_Client_CookieManager instance
  38 + * @var object
  39 + */
  40 + var $_cookieManager;
  41 +
  42 + /**
  43 + * Received HTTP responses
  44 + * @var array
  45 + */
  46 + var $_responses;
  47 +
  48 + /**
  49 + * Default headers to send on every request
  50 + * @var array
  51 + */
  52 + var $_defaultHeaders = array();
  53 +
  54 + /**
  55 + * Default parameters for HTTP_Request's constructor
  56 + * @var array
  57 + */
  58 + var $_defaultRequestParams = array();
  59 +
  60 + /**
  61 + * How many redirects were done
  62 + * @var integer
  63 + */
  64 + var $_redirectCount = 0;
  65 +
  66 + /**
  67 + * Maximum allowed redirects
  68 + * @var integer
  69 + */
  70 + var $_maxRedirects = 5;
  71 +
  72 + /**
  73 + * Listeners attached to the client
  74 + * @var array
  75 + */
  76 + var $_listeners = array();
  77 +
  78 + /**
  79 + * Whether the listener should be propagated to Request objects
  80 + * @var array
  81 + */
  82 + var $_propagate = array();
  83 +
  84 + /**
  85 + * Whether to keep all the responses or just the most recent one
  86 + * @var boolean
  87 + */
  88 + var $_isHistoryEnabled = true;
  89 +
  90 + /**
  91 + * Constructor
  92 + *
  93 + * @access public
  94 + * @param array Parameters to pass to HTTP_Request's constructor
  95 + * @param array Default headers to send on every request
  96 + */
  97 + function HTTP_Client($defaultRequestParams = null, $defaultHeaders = null)
  98 + {
  99 + $this->_cookieManager =& new HTTP_Client_CookieManager();
  100 + if (isset($defaultHeaders)) {
  101 + $this->setDefaultHeader($defaultHeaders);
  102 + }
  103 + if (isset($defaultRequestParams)) {
  104 + $this->setRequestParameter($defaultRequestParams);
  105 + }
  106 + }
  107 +
  108 +
  109 + /**
  110 + * Sets the maximum redirects that will be processed.
  111 + *
  112 + * Setting this to 0 disables redirect processing. If not 0 and the
  113 + * number of redirects in a request is bigger than this number, then an
  114 + * error will be raised.
  115 + *
  116 + * @access public
  117 + * @param int Max number of redirects to process
  118 + */
  119 + function setMaxRedirects($value)
  120 + {
  121 + $this->_maxRedirects = $value;
  122 + }
  123 +
  124 +
  125 + /**
  126 + * Sets whether to keep all the responses or just the most recent one
  127 + *
  128 + * @access public
  129 + * @param bool Whether to enable history
  130 + */
  131 + function enableHistory($enable)
  132 + {
  133 + $this->_isHistoryEnabled = (bool)$enable;
  134 + }
  135 +
  136 + /**
  137 + * Creates a HTTP_Request objects, applying all the necessary defaults
  138 + *
  139 + * @param string URL
  140 + * @param integer Method, constants are defined in HTTP_Request
  141 + * @access private
  142 + * @return object HTTP_Request object with all defaults applied
  143 + */
  144 + function &_createRequest($url, $method = HTTP_REQUEST_METHOD_GET)
  145 + {
  146 + $req =& new HTTP_Request($url, $this->_defaultRequestParams);
  147 + $req->setMethod($method);
  148 + foreach ($this->_defaultHeaders as $name => $value) {
  149 + $req->addHeader($name, $value);
  150 + }
  151 + $this->_cookieManager->passCookies($req);
  152 + foreach ($this->_propagate as $id => $propagate) {
  153 + if ($propagate) {
  154 + $req->attach($this->_listeners[$id]);
  155 + }
  156 + }
  157 + return $req;
  158 + }
  159 +
  160 +
  161 + /**
  162 + * Sends a 'HEAD' HTTP request
  163 + *
  164 + * @param string URL
  165 + * @access public
  166 + * @return integer HTTP response code
  167 + * @throws PEAR_Error
  168 + */
  169 + function head($url)
  170 + {
  171 + $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_HEAD);
  172 + return $this->_performRequest($request);
  173 + }
  174 +
  175 +
  176 + /**
  177 + * Sends a 'GET' HTTP request
  178 + *
  179 + * @param string URL
  180 + * @param mixed additional data to send
  181 + * @param boolean Whether the data is already urlencoded
  182 + * @access public
  183 + * @return integer HTTP response code
  184 + * @throws PEAR_Error
  185 + */
  186 + function get($url, $data = null, $preEncoded = false)
  187 + {
  188 + $request =& $this->_createRequest($url);
  189 + if (is_array($data)) {
  190 + foreach ($data as $name => $value) {
  191 + $request->addQueryString($name, $value, $preEncoded);
  192 + }
  193 + } elseif (isset($data)) {
  194 + $request->addRawQueryString($data, $preEncoded);
  195 + }
  196 + return $this->_performRequest($request);
  197 + }
  198 +
  199 +
  200 + /**
  201 + * Sends a 'POST' HTTP request
  202 + *
  203 + * @param string URL
  204 + * @param mixed Data to send
  205 + * @param boolean Whether the data is already urlencoded
  206 + * @param array Files to upload. Elements of the array should have the form:
  207 + * array(name, filename(s)[, content type]), see HTTP_Request::addFile()
  208 + * @access public
  209 + * @return integer HTTP response code
  210 + * @throws PEAR_Error
  211 + */
  212 + function post($url, $data, $preEncoded = false, $files = array())
  213 + {
  214 + $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_POST);
  215 + if (is_array($data)) {
  216 + foreach ($data as $name => $value) {
  217 + $request->addPostData($name, $value, $preEncoded);
  218 + }
  219 + } else {
  220 + $request->addRawPostData($data, $preEncoded);
  221 + }
  222 + foreach ($files as $fileData) {
  223 + $res = call_user_func_array(array(&$request, 'addFile'), $fileData);
  224 + if (PEAR::isError($res)) {
  225 + return $res;
  226 + }
  227 + }
  228 + return $this->_performRequest($request);
  229 + }
  230 +
  231 +
  232 + /**
  233 + * Sets default header(s) for HTTP requests
  234 + *
  235 + * @param mixed header name or array ('header name' => 'header value')
  236 + * @param string header value if $name is not an array
  237 + * @access public
  238 + */
  239 + function setDefaultHeader($name, $value = null)
  240 + {
  241 + if (is_array($name)) {
  242 + $this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
  243 + } else {
  244 + $this->_defaultHeaders[$name] = $value;
  245 + }
  246 + }
  247 +
  248 +
  249 + /**
  250 + * Sets parameter(s) for HTTP requests
  251 + *
  252 + * @param mixed parameter name or array ('parameter name' => 'parameter value')
  253 + * @param string parameter value if $name is not an array
  254 + * @access public
  255 + */
  256 + function setRequestParameter($name, $value = null)
  257 + {
  258 + if (is_array($name)) {
  259 + $this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
  260 + } else {
  261 + $this->_defaultRequestParams[$name] = $value;
  262 + }
  263 + }
  264 +
  265 +
  266 + /**
  267 + * Performs a request, processes redirects
  268 + *
  269 + * @param object HTTP_Request object
  270 + * @access private
  271 + * @return integer HTTP response code
  272 + * @throws PEAR_Error
  273 + */
  274 + function _performRequest(&$request)
  275 + {
  276 + // If this is not a redirect, notify the listeners of new request
  277 + if (0 == $this->_redirectCount) {
  278 + $this->_notify('request', $request->_url->getUrl());
  279 + }
  280 + if (PEAR::isError($err = $request->sendRequest())) {
  281 + return $err;
  282 + }
  283 + $this->_pushResponse($request);
  284 +
  285 + $code = $request->getResponseCode();
  286 + if ($this->_maxRedirects > 0 && in_array($code, array(300, 301, 302, 303, 307))) {
  287 + if (++$this->_redirectCount > $this->_maxRedirects) {
  288 + return PEAR::raiseError('Too many redirects');
  289 + }
  290 + $location = $request->getResponseHeader('Location');
  291 + if ('' == $location) {
  292 + return PEAR::raiseError("No 'Location' field on redirect");
  293 + }
  294 + $url = $this->_redirectUrl($request->_url, $location);
  295 + // Notify of redirection
  296 + $this->_notify('httpRedirect', $url);
  297 + // we access the private properties directly, as there are no accessors for them
  298 + switch ($request->_method) {
  299 + case HTTP_REQUEST_METHOD_POST:
  300 + if (302 == $code || 303 == $code) {
  301 + return $this->get($url);
  302 + } else {
  303 + $postFiles = array();
  304 + foreach ($request->_postFiles as $name => $data) {
  305 + $postFiles[] = array($name, $data['name'], $data['type']);
  306 + }
  307 + return $this->post($url, $request->_postData, true, $postFiles);
  308 + }
  309 + case HTTP_REQUEST_METHOD_HEAD:
  310 + return (303 == $code? $this->get($url): $this->head($url));
  311 + case HTTP_REQUEST_METHOD_GET:
  312 + default:
  313 + return $this->get($url);
  314 + } // switch
  315 +
  316 + } else {
  317 + $this->_redirectCount = 0;
  318 + if (400 >= $code) {
  319 + $this->_notify('httpSuccess');
  320 + $this->setDefaultHeader('Referer', $request->_url->getUrl());
  321 + // some result processing should go here
  322 + } else {
  323 + $this->_notify('httpError');
  324 + }
  325 + }
  326 + return $code;
  327 + }
  328 +
  329 +
  330 + /**
  331 + * Returns the most recent HTTP response
  332 + *
  333 + * @access public
  334 + * @return array
  335 + */
  336 + function &currentResponse()
  337 + {
  338 + return $this->_responses[count($this->_responses) - 1];
  339 + }
  340 +
  341 +
  342 + /**
  343 + * Saves the server's response to responses list
  344 + *
  345 + * @param object HTTP_Request object, with request already sent
  346 + * @access private
  347 + */
  348 + function _pushResponse(&$request)
  349 + {
  350 + $this->_cookieManager->updateCookies($request);
  351 + $idx = $this->_isHistoryEnabled? count($this->_responses): 0;
  352 + $this->_responses[$idx] = array(
  353 + 'code' => $request->getResponseCode(),
  354 + 'headers' => $request->getResponseHeader(),
  355 + 'body' => $request->getResponseBody()
  356 + );
  357 + }
  358 +
  359 +
  360 + /**
  361 + * Clears object's internal properties
  362 + *
  363 + * @access public
  364 + */
  365 + function reset()
  366 + {
  367 + $this->_cookieManager->reset();
  368 + $this->_responses = array();
  369 + $this->_defaultHeaders = array();
  370 + $this->_defaultRequestParams = array();
  371 + }
  372 +
  373 +
  374 + /**
  375 + * Adds a Listener to the list of listeners that are notified of
  376 + * the object's events
  377 + *
  378 + * @param object HTTP_Request_Listener instance to attach
  379 + * @param boolean Whether the listener should be attached to the
  380 + * created HTTP_Request objects
  381 + * @return boolean whether the listener was successfully attached
  382 + * @access public
  383 + */
  384 + function attach(&$listener, $propagate = false)
  385 + {
  386 + if (!is_a($listener, 'HTTP_Request_Listener')) {
  387 + return false;
  388 + }
  389 + $this->_listeners[$listener->getId()] =& $listener;
  390 + $this->_propagate[$listener->getId()] = $propagate;
  391 + return true;
  392 + }
  393 +
  394 +
  395 + /**
  396 + * Removes a Listener from the list of listeners
  397 + *
  398 + * @param object HTTP_Request_Listener instance to detach
  399 + * @return boolean whether the listener was successfully detached
  400 + * @access public
  401 + */
  402 + function detach(&$listener)
  403 + {
  404 + if (!is_a($listener, 'HTTP_Request_Listener') ||
  405 + !isset($this->_listeners[$listener->getId()])) {
  406 + return false;
  407 + }
  408 + unset($this->_listeners[$listener->getId()], $this->_propagate[$listener->getId()]);
  409 + return true;
  410 + }
  411 +
  412 +
  413 + /**
  414 + * Notifies all registered listeners of an event.
  415 + *
  416 + * Currently available events are:
  417 + * 'request': sent on HTTP request that is not a redirect
  418 + * 'httpSuccess': sent when we receive a successfull 2xx response
  419 + * 'httpRedirect': sent when we receive a redirection response
  420 + * 'httpError': sent on 4xx, 5xx response
  421 + *
  422 + * @param string Event name
  423 + * @param mixed Additional data
  424 + * @access private
  425 + */
  426 + function _notify($event, $data = null)
  427 + {
  428 + foreach (array_keys($this->_listeners) as $id) {
  429 + $this->_listeners[$id]->update($this, $event, $data);
  430 + }
  431 + }
  432 +
  433 +
  434 + /**
  435 + * Calculates the absolute URL of a redirect
  436 + *
  437 + * @param object Net_Url object containing the request URL
  438 + * @param string Value of the 'Location' response header
  439 + * @return string Absolute URL we are being redirected to
  440 + * @access private
  441 + */
  442 + function _redirectUrl($url, $location)
  443 + {
  444 + if (preg_match('!^https?://!i', $location)) {
  445 + return $location;
  446 + } else {
  447 + if ('/' == $location{0}) {
  448 + $url->path = Net_URL::resolvePath($location);
  449 + } elseif('/' == substr($url->path, -1)) {
  450 + $url->path = Net_URL::resolvePath($url->path . $location);
  451 + } else {
  452 + $dirname = (DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path));
  453 + $url->path = Net_URL::resolvePath($dirname . '/' . $location);
  454 + }
  455 + $url->querystring = array();
  456 + $url->anchor = '';
  457 + return $url->getUrl();
  458 + }
  459 + }
  460 +}
  461 +?>
... ...
thirdparty/pear/HTTP/Client/CookieManager.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4: */
  3 +// +----------------------------------------------------------------------+
  4 +// | PHP version 4 |
  5 +// +----------------------------------------------------------------------+
  6 +// | Copyright (c) 1997-2003 The PHP Group |
  7 +// +----------------------------------------------------------------------+
  8 +// | This source file is subject to version 3.0 of the PHP license, |
  9 +// | that is bundled with this package in the file LICENSE, and is |
  10 +// | available through the world-wide-web at |
  11 +// | http://www.php.net/license/3_0.txt. |
  12 +// | If you did not receive a copy of the PHP license and are unable to |
  13 +// | obtain it through the world-wide-web, please send a note to |
  14 +// | license@php.net so we can mail you a copy immediately. |
  15 +// +----------------------------------------------------------------------+
  16 +// | Author: Alexey Borzov <avb@php.net> |
  17 +// +----------------------------------------------------------------------+
  18 +//
  19 +// $Id: CookieManager.php,v 1.3 2004/04/10 10:04:52 avb Exp $
  20 +
  21 +/**
  22 + * This class is used to store cookies and pass them between HTTP requests.
  23 + *
  24 + * @package HTTP_Client
  25 + * @author Alexey Borzov <avb@php.net>
  26 + * @version $Revision: 1.3 $
  27 + */
  28 +class HTTP_Client_CookieManager
  29 +{
  30 + /**
  31 + * An array containing cookie values
  32 + * @var array
  33 + */
  34 + var $_cookies = array();
  35 +
  36 +
  37 + /**
  38 + * Constructor
  39 + *
  40 + * @access public
  41 + */
  42 + function HTTP_Client_CookieManager()
  43 + {
  44 + // abstract
  45 + }
  46 +
  47 +
  48 + /**
  49 + * Adds cookies to the request
  50 + *
  51 + * @access public
  52 + * @param object An HTTP_Request object
  53 + */
  54 + function passCookies(&$request)
  55 + {
  56 + if (!empty($this->_cookies)) {
  57 + $url =& $request->_url;
  58 + // We do not check cookie's "expires" field, as we do not store deleted
  59 + // cookies in the array and our client does not work long enough for other
  60 + // cookies to expire. If some kind of persistence is added to this object,
  61 + // then expiration should be checked upon loading and session cookies should
  62 + // be cleared on saving.
  63 + $cookies = array();
  64 + foreach ($this->_cookies as $cookie) {
  65 + if ($this->_domainMatch($url->host, $cookie['domain']) && (0 === strpos($url->path, $cookie['path']))
  66 + && (empty($cookie['secure']) || $url->protocol == 'https')) {
  67 + $cookies[$cookie['name']][strlen($cookie['path'])] = $cookie['value'];
  68 + }
  69 + }
  70 + // cookies with longer paths go first
  71 + foreach ($cookies as $name => $values) {
  72 + krsort($values);
  73 + foreach ($values as $value) {
  74 + $request->addCookie($name, $value);
  75 + }
  76 + }
  77 + }
  78 + return true;
  79 + }
  80 +
  81 +
  82 + /**
  83 + * Explicitly adds cookie to the list
  84 + *
  85 + * @param array An array representing cookie, this function expects all of the array's
  86 + * fields to be set
  87 + * @access public
  88 + */
  89 + function addCookie($cookie)
  90 + {
  91 + $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
  92 + $this->_cookies[$hash] = $cookie;
  93 + }
  94 +
  95 +
  96 + /**
  97 + * Updates cookie list from HTTP server response
  98 + *
  99 + * @access public
  100 + * @param object An HTTP_Request object with sendRequest() already done
  101 + */
  102 + function updateCookies(&$request)
  103 + {
  104 + if (false !== ($cookies = $request->getResponseCookies())) {
  105 + $url =& $request->_url;
  106 + foreach ($cookies as $cookie) {
  107 + // use the current domain by default
  108 + if (!isset($cookie['domain'])) {
  109 + $cookie['domain'] = $url->host;
  110 + }
  111 + // use the path to the current page by default
  112 + if (!isset($cookie['path'])) {
  113 + $cookie['path'] = DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path);
  114 + }
  115 + // check if the domains match
  116 + if ($this->_domainMatch($url->host, $cookie['domain'])) {
  117 + $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
  118 + // if value is empty or the time is in the past the cookie is deleted, else added
  119 + if (strlen($cookie['value'])
  120 + && (!isset($cookie['expires']) || (strtotime($cookie['expires']) > time()))) {
  121 + $this->_cookies[$hash] = $cookie;
  122 + } elseif (isset($this->_cookies[$hash])) {
  123 + unset($this->_cookies[$hash]);
  124 + }
  125 + }
  126 + }
  127 + }
  128 + }
  129 +
  130 +
  131 + /**
  132 + * Generates a key for the $_cookies array.
  133 + *
  134 + * The cookies is uniquely identified by its name, domain and path.
  135 + * Thus we cannot make f.e. an associative array with name as a key, we should
  136 + * generate a key from these 3 values.
  137 + *
  138 + * @access private
  139 + * @param string Cookie name
  140 + * @param string Cookie domain
  141 + * @param string Cookie path
  142 + * @return string a key
  143 + */
  144 + function _makeHash($name, $domain, $path)
  145 + {
  146 + return md5($name . "\r\n" . $domain . "\r\n" . $path);
  147 + }
  148 +
  149 +
  150 + /**
  151 + * Checks whether a cookie domain matches a request host.
  152 + *
  153 + * Cookie domain can begin with a dot, it also must contain at least
  154 + * two dots.
  155 + *
  156 + * @access private
  157 + * @param string request host
  158 + * @param string cookie domain
  159 + * @return bool match success
  160 + */
  161 + function _domainMatch($requestHost, $cookieDomain)
  162 + {
  163 + if ('.' != $cookieDomain{0}) {
  164 + return $requestHost == $cookieDomain;
  165 + } elseif (substr_count($cookieDomain, '.') < 2) {
  166 + return false;
  167 + } else {
  168 + return substr('.'. $requestHost, - strlen($cookieDomain)) == $cookieDomain;
  169 + }
  170 + }
  171 +
  172 +
  173 + /**
  174 + * Clears the $_cookies array
  175 + *
  176 + * @access public
  177 + */
  178 + function reset()
  179 + {
  180 + $this->_cookies = array();
  181 + }
  182 +}
  183 +?>
... ...
thirdparty/pear/HTTP/Download.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revised
  14 + * @version CVS: $Id: Download.php,v 1.75 2005/11/13 19:18:53 mike Exp $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +// {{{ includes
  19 +/**
  20 + * Requires PEAR
  21 + */
  22 +require_once 'PEAR.php';
  23 +
  24 +/**
  25 + * Requires HTTP_Header
  26 + */
  27 +require_once 'HTTP/Header.php';
  28 +// }}}
  29 +
  30 +// {{{ constants
  31 +/**#@+ Use with HTTP_Download::setContentDisposition() **/
  32 +/**
  33 + * Send data as attachment
  34 + */
  35 +define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
  36 +/**
  37 + * Send data inline
  38 + */
  39 +define('HTTP_DOWNLOAD_INLINE', 'inline');
  40 +/**#@-**/
  41 +
  42 +/**#@+ Use with HTTP_Download::sendArchive() **/
  43 +/**
  44 + * Send as uncompressed tar archive
  45 + */
  46 +define('HTTP_DOWNLOAD_TAR', 'TAR');
  47 +/**
  48 + * Send as gzipped tar archive
  49 + */
  50 +define('HTTP_DOWNLOAD_TGZ', 'TGZ');
  51 +/**
  52 + * Send as bzip2 compressed tar archive
  53 + */
  54 +define('HTTP_DOWNLOAD_BZ2', 'BZ2');
  55 +/**
  56 + * Send as zip archive
  57 + */
  58 +define('HTTP_DOWNLOAD_ZIP', 'ZIP');
  59 +/**#@-**/
  60 +
  61 +/**#@+
  62 + * Error constants
  63 + */
  64 +define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
  65 +define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
  66 +define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
  67 +define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
  68 +define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
  69 +define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
  70 +define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
  71 +define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
  72 +define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
  73 +/**#@-**/
  74 +// }}}
  75 +
  76 +/**
  77 + * Send HTTP Downloads/Responses.
  78 + *
  79 + * With this package you can handle (hidden) downloads.
  80 + * It supports partial downloads, resuming and sending
  81 + * raw data ie. from database BLOBs.
  82 + *
  83 + * <i>ATTENTION:</i>
  84 + * You shouldn't use this package together with ob_gzhandler or
  85 + * zlib.output_compression enabled in your php.ini, especially
  86 + * if you want to send already gzipped data!
  87 + *
  88 + * @access public
  89 + * @version $Revision: 1.75 $
  90 + */
  91 +class HTTP_Download
  92 +{
  93 + // {{{ protected member variables
  94 + /**
  95 + * Path to file for download
  96 + *
  97 + * @see HTTP_Download::setFile()
  98 + * @access protected
  99 + * @var string
  100 + */
  101 + var $file = '';
  102 +
  103 + /**
  104 + * Data for download
  105 + *
  106 + * @see HTTP_Download::setData()
  107 + * @access protected
  108 + * @var string
  109 + */
  110 + var $data = null;
  111 +
  112 + /**
  113 + * Resource handle for download
  114 + *
  115 + * @see HTTP_Download::setResource()
  116 + * @access protected
  117 + * @var int
  118 + */
  119 + var $handle = null;
  120 +
  121 + /**
  122 + * Whether to gzip the download
  123 + *
  124 + * @access protected
  125 + * @var bool
  126 + */
  127 + var $gzip = false;
  128 +
  129 + /**
  130 + * Whether to allow caching of the download on the clients side
  131 + *
  132 + * @access protected
  133 + * @var bool
  134 + */
  135 + var $cache = true;
  136 +
  137 + /**
  138 + * Size of download
  139 + *
  140 + * @access protected
  141 + * @var int
  142 + */
  143 + var $size = 0;
  144 +
  145 + /**
  146 + * Last modified
  147 + *
  148 + * @access protected
  149 + * @var int
  150 + */
  151 + var $lastModified = 0;
  152 +
  153 + /**
  154 + * HTTP headers
  155 + *
  156 + * @access protected
  157 + * @var array
  158 + */
  159 + var $headers = array(
  160 + 'Content-Type' => 'application/x-octetstream',
  161 + 'Pragma' => 'cache',
  162 + 'Cache-Control' => 'public, must-revalidate, max-age=0',
  163 + 'Accept-Ranges' => 'bytes',
  164 + 'X-Sent-By' => 'PEAR::HTTP::Download'
  165 + );
  166 +
  167 + /**
  168 + * HTTP_Header
  169 + *
  170 + * @access protected
  171 + * @var object
  172 + */
  173 + var $HTTP = null;
  174 +
  175 + /**
  176 + * ETag
  177 + *
  178 + * @access protected
  179 + * @var string
  180 + */
  181 + var $etag = '';
  182 +
  183 + /**
  184 + * Buffer Size
  185 + *
  186 + * @access protected
  187 + * @var int
  188 + */
  189 + var $bufferSize = 2097152;
  190 +
  191 + /**
  192 + * Throttle Delay
  193 + *
  194 + * @access protected
  195 + * @var float
  196 + */
  197 + var $throttleDelay = 0;
  198 +
  199 + /**
  200 + * Sent Bytes
  201 + *
  202 + * @access public
  203 + * @var int
  204 + */
  205 + var $sentBytes = 0;
  206 + // }}}
  207 +
  208 + // {{{ constructor
  209 + /**
  210 + * Constructor
  211 + *
  212 + * Set supplied parameters.
  213 + *
  214 + * @access public
  215 + * @param array $params associative array of parameters
  216 + *
  217 + * <b>one of:</b>
  218 + * o 'file' => path to file for download
  219 + * o 'data' => raw data for download
  220 + * o 'resource' => resource handle for download
  221 + * <br/>
  222 + * <b>and any of:</b>
  223 + * o 'cache' => whether to allow cs caching
  224 + * o 'gzip' => whether to gzip the download
  225 + * o 'lastmodified' => unix timestamp
  226 + * o 'contenttype' => content type of download
  227 + * o 'contentdisposition' => content disposition
  228 + * o 'buffersize' => amount of bytes to buffer
  229 + * o 'throttledelay' => amount of secs to sleep
  230 + * o 'cachecontrol' => cache privacy and validity
  231 + *
  232 + * <br />
  233 + * 'Content-Disposition' is not HTTP compliant, but most browsers
  234 + * follow this header, so it was borrowed from MIME standard.
  235 + *
  236 + * It looks like this: <br />
  237 + * "Content-Disposition: attachment; filename=example.tgz".
  238 + *
  239 + * @see HTTP_Download::setContentDisposition()
  240 + */
  241 + function HTTP_Download($params = array())
  242 + {
  243 + $this->HTTP = &new HTTP_Header;
  244 + $this->setParams($params);
  245 + }
  246 + // }}}
  247 +
  248 + // {{{ public methods
  249 + /**
  250 + * Set parameters
  251 + *
  252 + * Set supplied parameters through its accessor methods.
  253 + *
  254 + * @access public
  255 + * @return mixed Returns true on success or PEAR_Error on failure.
  256 + * @param array $params associative array of parameters
  257 + *
  258 + * @see HTTP_Download::HTTP_Download()
  259 + */
  260 + function setParams($params)
  261 + {
  262 + foreach((array) $params as $param => $value){
  263 + $method = 'set'. $param;
  264 +
  265 + if (!method_exists($this, $method)) {
  266 + return PEAR::raiseError(
  267 + "Method '$method' doesn't exist.",
  268 + HTTP_DOWNLOAD_E_INVALID_PARAM
  269 + );
  270 + }
  271 +
  272 + $e = call_user_func_array(array(&$this, $method), (array) $value);
  273 +
  274 + if (PEAR::isError($e)) {
  275 + return $e;
  276 + }
  277 + }
  278 + return true;
  279 + }
  280 +
  281 + /**
  282 + * Set path to file for download
  283 + *
  284 + * The Last-Modified header will be set to files filemtime(), actually.
  285 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
  286 + * Sends HTTP 404 status if $send_404 is set to true.
  287 + *
  288 + * @access public
  289 + * @return mixed Returns true on success or PEAR_Error on failure.
  290 + * @param string $file path to file for download
  291 + * @param bool $send_404 whether to send HTTP/404 if
  292 + * the file wasn't found
  293 + */
  294 + function setFile($file, $send_404 = true)
  295 + {
  296 + $file = realpath($file);
  297 + if (!is_file($file)) {
  298 + if ($send_404) {
  299 + $this->HTTP->sendStatusCode(404);
  300 + }
  301 + return PEAR::raiseError(
  302 + "File '$file' not found.",
  303 + HTTP_DOWNLOAD_E_INVALID_FILE
  304 + );
  305 + }
  306 + $this->setLastModified(filemtime($file));
  307 + $this->file = $file;
  308 + $this->size = filesize($file);
  309 + return true;
  310 + }
  311 +
  312 + /**
  313 + * Set data for download
  314 + *
  315 + * Set $data to null if you want to unset this.
  316 + *
  317 + * @access public
  318 + * @return void
  319 + * @param $data raw data to send
  320 + */
  321 + function setData($data = null)
  322 + {
  323 + $this->data = $data;
  324 + $this->size = strlen($data);
  325 + }
  326 +
  327 + /**
  328 + * Set resource for download
  329 + *
  330 + * The resource handle supplied will be closed after sending the download.
  331 + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
  332 + * is no valid resource. Set $handle to null if you want to unset this.
  333 + *
  334 + * @access public
  335 + * @return mixed Returns true on success or PEAR_Error on failure.
  336 + * @param int $handle resource handle
  337 + */
  338 + function setResource($handle = null)
  339 + {
  340 + if (!isset($handle)) {
  341 + $this->handle = null;
  342 + $this->size = 0;
  343 + return true;
  344 + }
  345 +
  346 + if (is_resource($handle)) {
  347 + $this->handle = $handle;
  348 + $filestats = fstat($handle);
  349 + $this->size = $filestats['size'];
  350 + return true;
  351 + }
  352 +
  353 + return PEAR::raiseError(
  354 + "Handle '$handle' is no valid resource.",
  355 + HTTP_DOWNLOAD_E_INVALID_RESOURCE
  356 + );
  357 + }
  358 +
  359 + /**
  360 + * Whether to gzip the download
  361 + *
  362 + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
  363 + * if ext/zlib is not available/loadable.
  364 + *
  365 + * @access public
  366 + * @return mixed Returns true on success or PEAR_Error on failure.
  367 + * @param bool $gzip whether to gzip the download
  368 + */
  369 + function setGzip($gzip = false)
  370 + {
  371 + if ($gzip && !PEAR::loadExtension('zlib')){
  372 + return PEAR::raiseError(
  373 + 'GZIP compression (ext/zlib) not available.',
  374 + HTTP_DOWNLOAD_E_NO_EXT_ZLIB
  375 + );
  376 + }
  377 + $this->gzip = (bool) $gzip;
  378 + return true;
  379 + }
  380 +
  381 + /**
  382 + * Whether to allow caching
  383 + *
  384 + * If set to true (default) we'll send some headers that are commonly
  385 + * used for caching purposes like ETag, Cache-Control and Last-Modified.
  386 + *
  387 + * If caching is disabled, we'll send the download no matter if it
  388 + * would actually be cached at the client side.
  389 + *
  390 + * @access public
  391 + * @return void
  392 + * @param bool $cache whether to allow caching
  393 + */
  394 + function setCache($cache = true)
  395 + {
  396 + $this->cache = (bool) $cache;
  397 + }
  398 +
  399 + /**
  400 + * Whether to allow proxies to cache
  401 + *
  402 + * If set to 'private' proxies shouldn't cache the response.
  403 + * This setting defaults to 'public' and affects only cached responses.
  404 + *
  405 + * @access public
  406 + * @return bool
  407 + * @param string $cache private or public
  408 + * @param int $maxage maximum age of the client cache entry
  409 + */
  410 + function setCacheControl($cache = 'public', $maxage = 0)
  411 + {
  412 + switch ($cache = strToLower($cache))
  413 + {
  414 + case 'private':
  415 + case 'public':
  416 + $this->headers['Cache-Control'] =
  417 + $cache .', must-revalidate, max-age='. abs($maxage);
  418 + return true;
  419 + break;
  420 + }
  421 + return false;
  422 + }
  423 +
  424 + /**
  425 + * Set ETag
  426 + *
  427 + * Sets a user-defined ETag for cache-validation. The ETag is usually
  428 + * generated by HTTP_Download through its payload information.
  429 + *
  430 + * @access public
  431 + * @return void
  432 + * @param string $etag Entity tag used for strong cache validation.
  433 + */
  434 + function setETag($etag = null)
  435 + {
  436 + $this->etag = (string) $etag;
  437 + }
  438 +
  439 + /**
  440 + * Set Size of Buffer
  441 + *
  442 + * The amount of bytes specified as buffer size is the maximum amount
  443 + * of data read at once from resources or files. The default size is 2M
  444 + * (2097152 bytes). Be aware that if you enable gzip compression and
  445 + * you set a very low buffer size that the actual file size may grow
  446 + * due to added gzip headers for each sent chunk of the specified size.
  447 + *
  448 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
  449 + * greater than 0 bytes.
  450 + *
  451 + * @access public
  452 + * @return mixed Returns true on success or PEAR_Error on failure.
  453 + * @param int $bytes Amount of bytes to use as buffer.
  454 + */
  455 + function setBufferSize($bytes = 2097152)
  456 + {
  457 + if (0 >= $bytes) {
  458 + return PEAR::raiseError(
  459 + 'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
  460 + HTTP_DOWNLOAD_E_INVALID_PARAM);
  461 + }
  462 + $this->bufferSize = abs($bytes);
  463 + return true;
  464 + }
  465 +
  466 + /**
  467 + * Set Throttle Delay
  468 + *
  469 + * Set the amount of seconds to sleep after each chunck that has been
  470 + * sent. One can implement some sort of throttle through adjusting the
  471 + * buffer size and the throttle delay. With the following settings
  472 + * HTTP_Download will sleep a second after each 25 K of data sent.
  473 + *
  474 + * <code>
  475 + * Array(
  476 + * 'throttledelay' => 1,
  477 + * 'buffersize' => 1024 * 25,
  478 + * )
  479 + * </code>
  480 + *
  481 + * Just be aware that if gzipp'ing is enabled, decreasing the chunk size
  482 + * too much leads to proportionally increased network traffic due to added
  483 + * gzip header and bottom bytes around each chunk.
  484 + *
  485 + * @access public
  486 + * @return void
  487 + * @param float $seconds Amount of seconds to sleep after each
  488 + * chunk that has been sent.
  489 + */
  490 + function setThrottleDelay($seconds = 0)
  491 + {
  492 + $this->throttleDelay = abs($seconds) * 1000;
  493 + }
  494 +
  495 + /**
  496 + * Set "Last-Modified"
  497 + *
  498 + * This is usually determined by filemtime() in HTTP_Download::setFile()
  499 + * If you set raw data for download with HTTP_Download::setData() and you
  500 + * want do send an appropiate "Last-Modified" header, you should call this
  501 + * method.
  502 + *
  503 + * @access public
  504 + * @return void
  505 + * @param int unix timestamp
  506 + */
  507 + function setLastModified($last_modified)
  508 + {
  509 + $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
  510 + }
  511 +
  512 + /**
  513 + * Set Content-Disposition header
  514 + *
  515 + * @see HTTP_Download::HTTP_Download
  516 + *
  517 + * @access public
  518 + * @return void
  519 + * @param string $disposition whether to send the download
  520 + * inline or as attachment
  521 + * @param string $file_name the filename to display in
  522 + * the browser's download window
  523 + *
  524 + * <b>Example:</b>
  525 + * <code>
  526 + * $HTTP_Download->setContentDisposition(
  527 + * HTTP_DOWNLOAD_ATTACHMENT,
  528 + * 'download.tgz'
  529 + * );
  530 + * </code>
  531 + */
  532 + function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
  533 + $file_name = null)
  534 + {
  535 + $cd = $disposition;
  536 + if (isset($file_name)) {
  537 + $cd .= '; filename="' . $file_name . '"';
  538 + } elseif ($this->file) {
  539 + $cd .= '; filename="' . basename($this->file) . '"';
  540 + }
  541 + $this->headers['Content-Disposition'] = $cd;
  542 + }
  543 +
  544 + /**
  545 + * Set content type of the download
  546 + *
  547 + * Default content type of the download will be 'application/x-octetstream'.
  548 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
  549 + * $content_type doesn't seem to be valid.
  550 + *
  551 + * @access public
  552 + * @return mixed Returns true on success or PEAR_Error on failure.
  553 + * @param string $content_type content type of file for download
  554 + */
  555 + function setContentType($content_type = 'application/x-octetstream')
  556 + {
  557 + if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
  558 + return PEAR::raiseError(
  559 + "Invalid content type '$content_type' supplied.",
  560 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
  561 + );
  562 + }
  563 + $this->headers['Content-Type'] = $content_type;
  564 + return true;
  565 + }
  566 +
  567 + /**
  568 + * Guess content type of file
  569 + *
  570 + * First we try to use PEAR::MIME_Type, if installed, to detect the content
  571 + * type, else we check if ext/mime_magic is loaded and properly configured.
  572 + *
  573 + * Returns PEAR_Error if:
  574 + * o if PEAR::MIME_Type failed to detect a proper content type
  575 + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
  576 + * o ext/magic.mime is not installed, or not properly configured
  577 + * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
  578 + * o mime_content_type() couldn't guess content type or returned
  579 + * a content type considered to be bogus by setContentType()
  580 + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
  581 + *
  582 + * @access public
  583 + * @return mixed Returns true on success or PEAR_Error on failure.
  584 + */
  585 + function guessContentType()
  586 + {
  587 + if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
  588 + if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
  589 + return PEAR::raiseError($mime_type->getMessage(),
  590 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
  591 + }
  592 + return $this->setContentType($mime_type);
  593 + }
  594 + if (!function_exists('mime_content_type')) {
  595 + return PEAR::raiseError(
  596 + 'This feature requires ext/mime_magic!',
  597 + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
  598 + );
  599 + }
  600 + if (!is_file(ini_get('mime_magic.magicfile'))) {
  601 + return PEAR::raiseError(
  602 + 'ext/mime_magic is loaded but not properly configured!',
  603 + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
  604 + );
  605 + }
  606 + if (!$content_type = @mime_content_type($this->file)) {
  607 + return PEAR::raiseError(
  608 + 'Couldn\'t guess content type with mime_content_type().',
  609 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
  610 + );
  611 + }
  612 + return $this->setContentType($content_type);
  613 + }
  614 +
  615 + /**
  616 + * Send
  617 + *
  618 + * Returns PEAR_Error if:
  619 + * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
  620 + * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
  621 + *
  622 + * @access public
  623 + * @return mixed Returns true on success or PEAR_Error on failure.
  624 + * @param bool $autoSetContentDisposition Whether to set the
  625 + * Content-Disposition header if it isn't already.
  626 + */
  627 + function send($autoSetContentDisposition = true)
  628 + {
  629 + if (headers_sent()) {
  630 + return PEAR::raiseError(
  631 + 'Headers already sent.',
  632 + HTTP_DOWNLOAD_E_HEADERS_SENT
  633 + );
  634 + }
  635 +
  636 + if (!ini_get('safe_mode')) {
  637 + @set_time_limit(0);
  638 + }
  639 +
  640 + if ($autoSetContentDisposition &&
  641 + !isset($this->headers['Content-Disposition'])) {
  642 + $this->setContentDisposition();
  643 + }
  644 +
  645 + if ($this->cache) {
  646 + $this->headers['ETag'] = $this->generateETag();
  647 + if ($this->isCached()) {
  648 + $this->HTTP->sendStatusCode(304);
  649 + $this->sendHeaders();
  650 + return true;
  651 + }
  652 + } else {
  653 + unset($this->headers['Last-Modified']);
  654 + }
  655 +
  656 + while (@ob_end_clean());
  657 +
  658 + if ($this->gzip) {
  659 + @ob_start('ob_gzhandler');
  660 + } else {
  661 + ob_start();
  662 + }
  663 +
  664 + $this->sentBytes = 0;
  665 +
  666 + if ($this->isRangeRequest()) {
  667 + $this->HTTP->sendStatusCode(206);
  668 + $chunks = $this->getChunks();
  669 + } else {
  670 + $this->HTTP->sendStatusCode(200);
  671 + $chunks = array(array(0, $this->size));
  672 + if (!$this->gzip && count(ob_list_handlers()) < 2) {
  673 + $this->headers['Content-Length'] = $this->size;
  674 + }
  675 + }
  676 +
  677 + if (PEAR::isError($e = $this->sendChunks($chunks))) {
  678 + ob_end_clean();
  679 + $this->HTTP->sendStatusCode(416);
  680 + return $e;
  681 + }
  682 +
  683 + ob_end_flush();
  684 + flush();
  685 + return true;
  686 + }
  687 +
  688 + /**
  689 + * Static send
  690 + *
  691 + * @see HTTP_Download::HTTP_Download()
  692 + * @see HTTP_Download::send()
  693 + *
  694 + * @static
  695 + * @access public
  696 + * @return mixed Returns true on success or PEAR_Error on failure.
  697 + * @param array $params associative array of parameters
  698 + * @param bool $guess whether HTTP_Download::guessContentType()
  699 + * should be called
  700 + */
  701 + function staticSend($params, $guess = false)
  702 + {
  703 + $d = &new HTTP_Download();
  704 + $e = $d->setParams($params);
  705 + if (PEAR::isError($e)) {
  706 + return $e;
  707 + }
  708 + if ($guess) {
  709 + $e = $d->guessContentType();
  710 + if (PEAR::isError($e)) {
  711 + return $e;
  712 + }
  713 + }
  714 + return $d->send();
  715 + }
  716 +
  717 + /**
  718 + * Send a bunch of files or directories as an archive
  719 + *
  720 + * Example:
  721 + * <code>
  722 + * require_once 'HTTP/Download.php';
  723 + * HTTP_Download::sendArchive(
  724 + * 'myArchive.tgz',
  725 + * '/var/ftp/pub/mike',
  726 + * HTTP_DOWNLOAD_TGZ,
  727 + * '',
  728 + * '/var/ftp/pub'
  729 + * );
  730 + * </code>
  731 + *
  732 + * @see Archive_Tar::createModify()
  733 + * @deprecated use HTTP_Download_Archive::send()
  734 + * @static
  735 + * @access public
  736 + * @return mixed Returns true on success or PEAR_Error on failure.
  737 + * @param string $name name the sent archive should have
  738 + * @param mixed $files files/directories
  739 + * @param string $type archive type
  740 + * @param string $add_path path that should be prepended to the files
  741 + * @param string $strip_path path that should be stripped from the files
  742 + */
  743 + function sendArchive( $name,
  744 + $files,
  745 + $type = HTTP_DOWNLOAD_TGZ,
  746 + $add_path = '',
  747 + $strip_path = '')
  748 + {
  749 + require_once 'HTTP/Download/Archive.php';
  750 + return HTTP_Download_Archive::send($name, $files, $type,
  751 + $add_path, $strip_path);
  752 + }
  753 + // }}}
  754 +
  755 + // {{{ protected methods
  756 + /**
  757 + * Generate ETag
  758 + *
  759 + * @access protected
  760 + * @return string
  761 + */
  762 + function generateETag()
  763 + {
  764 + if (!$this->etag) {
  765 + if ($this->data) {
  766 + $md5 = md5($this->data);
  767 + } else {
  768 + $fst = is_resource($this->handle) ?
  769 + fstat($this->handle) : stat($this->file);
  770 + $md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
  771 + }
  772 + $this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
  773 + }
  774 + return $this->etag;
  775 + }
  776 +
  777 + /**
  778 + * Send multiple chunks
  779 + *
  780 + * @access protected
  781 + * @return mixed Returns true on success or PEAR_Error on failure.
  782 + * @param array $chunks
  783 + */
  784 + function sendChunks($chunks)
  785 + {
  786 + if (count($chunks) == 1) {
  787 + return $this->sendChunk(array_shift($chunks));
  788 + }
  789 +
  790 + $bound = uniqid('HTTP_DOWNLOAD-', true);
  791 + $cType = $this->headers['Content-Type'];
  792 + $this->headers['Content-Type'] =
  793 + 'multipart/byteranges; boundary=' . $bound;
  794 + $this->sendHeaders();
  795 + foreach ($chunks as $chunk){
  796 + if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
  797 + return $e;
  798 + }
  799 + }
  800 + #echo "\r\n--$bound--\r\n";
  801 + return true;
  802 + }
  803 +
  804 + /**
  805 + * Send chunk of data
  806 + *
  807 + * @access protected
  808 + * @return mixed Returns true on success or PEAR_Error on failure.
  809 + * @param array $chunk start and end offset of the chunk to send
  810 + * @param string $cType actual content type
  811 + * @param string $bound boundary for multipart/byteranges
  812 + */
  813 + function sendChunk($chunk, $cType = null, $bound = null)
  814 + {
  815 + list($offset, $lastbyte) = $chunk;
  816 + $length = ($lastbyte - $offset) + 1;
  817 +
  818 + if ($length < 1) {
  819 + return PEAR::raiseError(
  820 + "Error processing range request: $offset-$lastbyte/$length",
  821 + HTTP_DOWNLOAD_E_INVALID_REQUEST
  822 + );
  823 + }
  824 +
  825 + $range = $offset . '-' . $lastbyte . '/' . $this->size;
  826 +
  827 + if (isset($cType, $bound)) {
  828 + echo "\r\n--$bound\r\n",
  829 + "Content-Type: $cType\r\n",
  830 + "Content-Range: bytes $range\r\n\r\n";
  831 + } else {
  832 + if ($this->isRangeRequest()) {
  833 + $this->headers['Content-Range'] = 'bytes '. $range;
  834 + }
  835 + $this->sendHeaders();
  836 + }
  837 +
  838 + if ($this->data) {
  839 + while (($length -= $this->bufferSize) > 0) {
  840 + $this->flush(substr($this->data, $offset, $this->bufferSize));
  841 + $this->throttleDelay and $this->sleep();
  842 + $offset += $this->bufferSize;
  843 + }
  844 + if ($length) {
  845 + $this->flush(substr($this->data, $offset, $this->bufferSize + $length));
  846 + }
  847 + } else {
  848 + if (!is_resource($this->handle)) {
  849 + $this->handle = fopen($this->file, 'rb');
  850 + }
  851 + fseek($this->handle, $offset);
  852 + while (($length -= $this->bufferSize) > 0) {
  853 + $this->flush(fread($this->handle, $this->bufferSize));
  854 + $this->throttleDelay and $this->sleep();
  855 + }
  856 + if ($length) {
  857 + $this->flush(fread($this->handle, $this->bufferSize + $length));
  858 + }
  859 + }
  860 + return true;
  861 + }
  862 +
  863 + /**
  864 + * Get chunks to send
  865 + *
  866 + * @access protected
  867 + * @return array
  868 + */
  869 + function getChunks()
  870 + {
  871 + $parts = array();
  872 + foreach (explode(',', $this->getRanges()) as $chunk){
  873 + list($o, $e) = explode('-', $chunk);
  874 + if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
  875 + $e = $this->size - 1;
  876 + }
  877 + if (empty($o) && $o !== 0 && $o !== '0') {
  878 + $o = $this->size - $e;
  879 + $e = $this->size - 1;
  880 + }
  881 + $parts[] = array($o, $e);
  882 + }
  883 + return $parts;
  884 + }
  885 +
  886 + /**
  887 + * Check if range is requested
  888 + *
  889 + * @access protected
  890 + * @return bool
  891 + */
  892 + function isRangeRequest()
  893 + {
  894 + if (!isset($_SERVER['HTTP_RANGE'])) {
  895 + return false;
  896 + }
  897 + return $this->isValidRange();
  898 + }
  899 +
  900 + /**
  901 + * Get range request
  902 + *
  903 + * @access protected
  904 + * @return array
  905 + */
  906 + function getRanges()
  907 + {
  908 + return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
  909 + @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
  910 + }
  911 +
  912 + /**
  913 + * Check if entity is cached
  914 + *
  915 + * @access protected
  916 + * @return bool
  917 + */
  918 + function isCached()
  919 + {
  920 + return (
  921 + (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
  922 + $this->lastModified == strtotime(current($a = explode(
  923 + ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
  924 + (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
  925 + $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
  926 + );
  927 + }
  928 +
  929 + /**
  930 + * Check if entity hasn't changed
  931 + *
  932 + * @access protected
  933 + * @return bool
  934 + */
  935 + function isValidRange()
  936 + {
  937 + if (isset($_SERVER['HTTP_IF_MATCH']) &&
  938 + !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
  939 + return false;
  940 + }
  941 + if (isset($_SERVER['HTTP_IF_RANGE']) &&
  942 + $_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
  943 + strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
  944 + return false;
  945 + }
  946 + if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
  947 + $lm = array_shift(explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
  948 + if (strtotime($lm) !== $this->lastModified) {
  949 + return false;
  950 + }
  951 + }
  952 + if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
  953 + $lm = array_shift(explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
  954 + if (strtotime($lm) !== $this->lastModified) {
  955 + return false;
  956 + }
  957 + }
  958 + return true;
  959 + }
  960 +
  961 + /**
  962 + * Compare against an asterisk or check for equality
  963 + *
  964 + * @access protected
  965 + * @return bool
  966 + * @param string key for the $_SERVER array
  967 + * @param string string to compare
  968 + */
  969 + function compareAsterisk($svar, $compare)
  970 + {
  971 + foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
  972 + if ($request === '*' || $request === $compare) {
  973 + return true;
  974 + }
  975 + }
  976 + return false;
  977 + }
  978 +
  979 + /**
  980 + * Send HTTP headers
  981 + *
  982 + * @access protected
  983 + * @return void
  984 + */
  985 + function sendHeaders()
  986 + {
  987 + foreach ($this->headers as $header => $value) {
  988 + $this->HTTP->setHeader($header, $value);
  989 + }
  990 + $this->HTTP->sendHeaders();
  991 + /* NSAPI won't output anything if we did this */
  992 + if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
  993 + ob_flush();
  994 + flush();
  995 + }
  996 + }
  997 +
  998 + /**
  999 + * Flush
  1000 + *
  1001 + * @access protected
  1002 + * @return void
  1003 + * @param string $data
  1004 + */
  1005 + function flush($data = '')
  1006 + {
  1007 + if ($dlen = strlen($data)) {
  1008 + $this->sentBytes += $dlen;
  1009 + echo $data;
  1010 + }
  1011 + ob_flush();
  1012 + flush();
  1013 + }
  1014 +
  1015 + /**
  1016 + * Sleep
  1017 + *
  1018 + * @access protected
  1019 + * @return void
  1020 + */
  1021 + function sleep()
  1022 + {
  1023 + if (OS_WINDOWS) {
  1024 + com_message_pump($this->throttleDelay);
  1025 + } else {
  1026 + usleep($this->throttleDelay * 1000);
  1027 + }
  1028 + }
  1029 + // }}}
  1030 +}
  1031 +?>
... ...
thirdparty/pear/HTTP/Download/Archive.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download::Archive
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revisewd
  14 + * @version CVS: $Id: Archive.php,v 1.4 2005/11/13 19:18:55 mike Exp $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +/**
  19 + * Requires HTTP_Download
  20 + */
  21 +require_once 'HTTP/Download.php';
  22 +
  23 +/**
  24 + * Requires System
  25 + */
  26 +require_once 'System.php';
  27 +
  28 +/**
  29 + * HTTP_Download_Archive
  30 + *
  31 + * Helper class for sending Archives.
  32 + *
  33 + * @access public
  34 + * @version $Revision: 1.4 $
  35 + */
  36 +class HTTP_Download_Archive
  37 +{
  38 + /**
  39 + * Send a bunch of files or directories as an archive
  40 + *
  41 + * Example:
  42 + * <code>
  43 + * require_once 'HTTP/Download/Archive.php';
  44 + * HTTP_Download_Archive::send(
  45 + * 'myArchive.tgz',
  46 + * '/var/ftp/pub/mike',
  47 + * HTTP_DOWNLOAD_BZ2,
  48 + * '',
  49 + * '/var/ftp/pub'
  50 + * );
  51 + * </code>
  52 + *
  53 + * @see Archive_Tar::createModify()
  54 + * @static
  55 + * @access public
  56 + * @return mixed Returns true on success or PEAR_Error on failure.
  57 + * @param string $name name the sent archive should have
  58 + * @param mixed $files files/directories
  59 + * @param string $type archive type
  60 + * @param string $add_path path that should be prepended to the files
  61 + * @param string $strip_path path that should be stripped from the files
  62 + */
  63 + function send($name, $files, $type = HTTP_DOWNLOAD_TGZ, $add_path = '', $strip_path = '')
  64 + {
  65 + $tmp = System::mktemp();
  66 +
  67 + switch ($type = strToUpper($type))
  68 + {
  69 + case HTTP_DOWNLOAD_TAR:
  70 + include_once 'Archive/Tar.php';
  71 + $arc = &new Archive_Tar($tmp);
  72 + $content_type = 'x-tar';
  73 + break;
  74 +
  75 + case HTTP_DOWNLOAD_TGZ:
  76 + include_once 'Archive/Tar.php';
  77 + $arc = &new Archive_Tar($tmp, 'gz');
  78 + $content_type = 'x-gzip';
  79 + break;
  80 +
  81 + case HTTP_DOWNLOAD_BZ2:
  82 + include_once 'Archive/Tar.php';
  83 + $arc = &new Archive_Tar($tmp, 'bz2');
  84 + $content_type = 'x-bzip2';
  85 + break;
  86 +
  87 + case HTTP_DOWNLOAD_ZIP:
  88 + include_once 'Archive/Zip.php';
  89 + $arc = &new Archive_Zip($tmp);
  90 + $content_type = 'x-zip';
  91 + break;
  92 +
  93 + default:
  94 + return PEAR::raiseError(
  95 + 'Archive type not supported: ' . $type,
  96 + HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE
  97 + );
  98 + }
  99 +
  100 + if ($type == HTTP_DOWNLOAD_ZIP) {
  101 + $options = array( 'add_path' => $add_path,
  102 + 'remove_path' => $strip_path);
  103 + if (!$arc->create($files, $options)) {
  104 + return PEAR::raiseError('Archive creation failed.');
  105 + }
  106 + } else {
  107 + if (!$e = $arc->createModify($files, $add_path, $strip_path)) {
  108 + return PEAR::raiseError('Archive creation failed.');
  109 + }
  110 + if (PEAR::isError($e)) {
  111 + return $e;
  112 + }
  113 + }
  114 + unset($arc);
  115 +
  116 + $dl = &new HTTP_Download(array('file' => $tmp));
  117 + $dl->setContentType('application/' . $content_type);
  118 + $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, $name);
  119 + return $dl->send();
  120 + }
  121 +}
  122 +?>
... ...
thirdparty/pear/HTTP/Download/PgLOB.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download::PgLOB
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revised
  14 + * @version CVS: $Id: PgLOB.php,v 1.14 2005/11/13 19:18:55 mike Exp $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +$GLOBALS['_HTTP_Download_PgLOB_Connection'] = null;
  19 +stream_register_wrapper('pglob', 'HTTP_Download_PgLOB');
  20 +
  21 +/**
  22 + * PgSQL large object stream interface for HTTP_Download
  23 + *
  24 + * Usage:
  25 + * <code>
  26 + * require_once 'HTTP/Download.php';
  27 + * require_once 'HTTP/Download/PgLOB.php';
  28 + * $db = &DB::connect('pgsql://user:pass@host/db');
  29 + * // or $db = pg_connect(...);
  30 + * $lo = HTTP_Download_PgLOB::open($db, 12345);
  31 + * $dl = &new HTTP_Download;
  32 + * $dl->setResource($lo);
  33 + * $dl->send()
  34 + * </code>
  35 + *
  36 + * @access public
  37 + * @version $Revision: 1.14 $
  38 + */
  39 +class HTTP_Download_PgLOB
  40 +{
  41 + /**
  42 + * Set Connection
  43 + *
  44 + * @static
  45 + * @access public
  46 + * @return bool
  47 + * @param mixed $conn
  48 + */
  49 + function setConnection($conn)
  50 + {
  51 + if (is_a($conn, 'DB_Common')) {
  52 + $conn = $conn->dbh;
  53 + } elseif ( is_a($conn, 'MDB_Common') ||
  54 + is_a($conn, 'MDB2_Driver_Common')) {
  55 + $conn = $conn->connection;
  56 + }
  57 + if ($isResource = is_resource($conn)) {
  58 + $GLOBALS['_HTTP_Download_PgLOB_Connection'] = $conn;
  59 + }
  60 + return $isResource;
  61 + }
  62 +
  63 + /**
  64 + * Get Connection
  65 + *
  66 + * @static
  67 + * @access public
  68 + * @return resource
  69 + */
  70 + function getConnection()
  71 + {
  72 + if (is_resource($GLOBALS['_HTTP_Download_PgLOB_Connection'])) {
  73 + return $GLOBALS['_HTTP_Download_PgLOB_Connection'];
  74 + }
  75 + return null;
  76 + }
  77 +
  78 + /**
  79 + * Open
  80 + *
  81 + * @static
  82 + * @access public
  83 + * @return resource
  84 + * @param mixed $conn
  85 + * @param int $loid
  86 + * @param string $mode
  87 + */
  88 + function open($conn, $loid, $mode = 'rb')
  89 + {
  90 + HTTP_Download_PgLOB::setConnection($conn);
  91 + return fopen('pglob:///'. $loid, $mode);
  92 + }
  93 +
  94 + /**#@+
  95 + * Stream Interface Implementation
  96 + * @internal
  97 + */
  98 + var $ID = 0;
  99 + var $size = 0;
  100 + var $conn = null;
  101 + var $handle = null;
  102 +
  103 + function stream_open($path, $mode)
  104 + {
  105 + if (!$this->conn = HTTP_Download_PgLOB::getConnection()) {
  106 + return false;
  107 + }
  108 + if (!preg_match('/(\d+)/', $path, $matches)) {
  109 + return false;
  110 + }
  111 + $this->ID = $matches[1];
  112 +
  113 + if (!pg_query($this->conn, 'BEGIN')) {
  114 + return false;
  115 + }
  116 +
  117 + $this->handle = pg_lo_open($this->conn, $this->ID, $mode);
  118 + if (!is_resource($this->handle)) {
  119 + return false;
  120 + }
  121 +
  122 + // fetch size of lob
  123 + pg_lo_seek($this->handle, 0, PGSQL_SEEK_END);
  124 + $this->size = (int) pg_lo_tell($this->handle);
  125 + pg_lo_seek($this->handle, 0, PGSQL_SEEK_SET);
  126 +
  127 + return true;
  128 + }
  129 +
  130 + function stream_read($length)
  131 + {
  132 + return pg_lo_read($this->handle, $length);
  133 + }
  134 +
  135 + function stream_seek($offset, $whence = SEEK_SET)
  136 + {
  137 + return pg_lo_seek($this->handle, $offset, $whence);
  138 + }
  139 +
  140 + function stream_tell()
  141 + {
  142 + return pg_lo_tell($this->handle);
  143 + }
  144 +
  145 + function stream_eof()
  146 + {
  147 + return pg_lo_tell($this->handle) >= $this->size;
  148 + }
  149 +
  150 + function stream_flush()
  151 + {
  152 + return true;
  153 + }
  154 +
  155 + function stream_stat()
  156 + {
  157 + return array('size' => $this->size, 'ino' => $this->ID);
  158 + }
  159 +
  160 + function stream_write($data)
  161 + {
  162 + return pg_lo_write($this->handle, $data);
  163 + }
  164 +
  165 + function stream_close()
  166 + {
  167 + if (pg_lo_close($this->handle)) {
  168 + return pg_query($this->conn, 'COMMIT');
  169 + } else {
  170 + pg_query($this->conn ,'ROLLBACK');
  171 + return false;
  172 + }
  173 + }
  174 + /**#@-*/
  175 +}
  176 +
  177 +?>
... ...
thirdparty/pear/HTTP/Header.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Header
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Header
  11 + * @author Wolfram Kriesing <wk@visionp.de>
  12 + * @author Davey Shafik <davey@php.net>
  13 + * @author Michael Wallner <mike@php.net>
  14 + * @copyright 2003-2005 The Authors
  15 + * @license BSD, revised
  16 + * @version CVS: $Id: Header.php,v 1.32 2005/11/08 19:06:10 mike Exp $
  17 + * @link http://pear.php.net/package/HTTP_Header
  18 + */
  19 +
  20 +/**
  21 + * Requires HTTP
  22 + */
  23 +require_once 'HTTP.php';
  24 +
  25 +/**#@+
  26 + * Information Codes
  27 + */
  28 +define('HTTP_HEADER_STATUS_100', '100 Continue');
  29 +define('HTTP_HEADER_STATUS_101', '101 Switching Protocols');
  30 +define('HTTP_HEADER_STATUS_102', '102 Processing');
  31 +define('HTTP_HEADER_STATUS_INFORMATIONAL',1);
  32 +/**#@-*/
  33 +
  34 +/**#+
  35 + * Success Codes
  36 + */
  37 +define('HTTP_HEADER_STATUS_200', '200 OK');
  38 +define('HTTP_HEADER_STATUS_201', '201 Created');
  39 +define('HTTP_HEADER_STATUS_202', '202 Accepted');
  40 +define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information');
  41 +define('HTTP_HEADER_STATUS_204', '204 No Content');
  42 +define('HTTP_HEADER_STATUS_205', '205 Reset Content');
  43 +define('HTTP_HEADER_STATUS_206', '206 Partial Content');
  44 +define('HTTP_HEADER_STATUS_207', '207 Multi-Status');
  45 +define('HTTP_HEADER_STATUS_SUCCESSFUL',2);
  46 +/**#@-*/
  47 +
  48 +/**#@+
  49 + * Redirection Codes
  50 + */
  51 +define('HTTP_HEADER_STATUS_300', '300 Multiple Choices');
  52 +define('HTTP_HEADER_STATUS_301', '301 Moved Permanently');
  53 +define('HTTP_HEADER_STATUS_302', '302 Found');
  54 +define('HTTP_HEADER_STATUS_303', '303 See Other');
  55 +define('HTTP_HEADER_STATUS_304', '304 Not Modified');
  56 +define('HTTP_HEADER_STATUS_305', '305 Use Proxy');
  57 +define('HTTP_HEADER_STATUS_306', '306 (Unused)');
  58 +define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect');
  59 +define('HTTP_HEADER_STATUS_REDIRECT',3);
  60 +/**#@-*/
  61 +
  62 +/**#@+
  63 + * Error Codes
  64 + */
  65 +define('HTTP_HEADER_STATUS_400', '400 Bad Request');
  66 +define('HTTP_HEADER_STATUS_401', '401 Unauthorized');
  67 +define('HTTP_HEADER_STATUS_402', '402 Payment Granted');
  68 +define('HTTP_HEADER_STATUS_403', '403 Forbidden');
  69 +define('HTTP_HEADER_STATUS_404', '404 File Not Found');
  70 +define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed');
  71 +define('HTTP_HEADER_STATUS_406', '406 Not Acceptable');
  72 +define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required');
  73 +define('HTTP_HEADER_STATUS_408', '408 Request Time-out');
  74 +define('HTTP_HEADER_STATUS_409', '409 Conflict');
  75 +define('HTTP_HEADER_STATUS_410', '410 Gone');
  76 +define('HTTP_HEADER_STATUS_411', '411 Length Required');
  77 +define('HTTP_HEADER_STATUS_412', '412 Precondition Failed');
  78 +define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large');
  79 +define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large');
  80 +define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type');
  81 +define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable');
  82 +define('HTTP_HEADER_STATUS_417', '417 Expectation Failed');
  83 +define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity');
  84 +define('HTTP_HEADER_STATUS_423', '423 Locked');
  85 +define('HTTP_HEADER_STATUS_424', '424 Failed Dependency');
  86 +define('HTTP_HEADER_STATUS_CLIENT_ERROR',4);
  87 +/**#@-*/
  88 +
  89 +/**#@+
  90 + * Server Errors
  91 + */
  92 +define('HTTP_HEADER_STATUS_500', '500 Internal Server Error');
  93 +define('HTTP_HEADER_STATUS_501', '501 Not Implemented');
  94 +define('HTTP_HEADER_STATUS_502', '502 Bad Gateway');
  95 +define('HTTP_HEADER_STATUS_503', '503 Service Unavailable');
  96 +define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out');
  97 +define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported');
  98 +define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage');
  99 +define('HTTP_HEADER_STATUS_SERVER_ERROR',5);
  100 +/**#@-*/
  101 +
  102 +/**
  103 + * HTTP_Header
  104 + *
  105 + * @package HTTP_Header
  106 + * @category HTTP
  107 + * @access public
  108 + * @version $Revision: 1.32 $
  109 + */
  110 +class HTTP_Header extends HTTP
  111 +{
  112 + /**
  113 + * Default Headers
  114 + *
  115 + * The values that are set as default, are the same as PHP sends by default.
  116 + *
  117 + * @var array
  118 + * @access private
  119 + */
  120 + var $_headers = array(
  121 + 'content-type' => 'text/html',
  122 + 'pragma' => 'no-cache',
  123 + 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
  124 + );
  125 +
  126 + /**
  127 + * HTTP version
  128 + *
  129 + * @var string
  130 + * @access private
  131 + */
  132 + var $_httpVersion = '1.0';
  133 +
  134 + /**
  135 + * Constructor
  136 + *
  137 + * Sets HTTP version.
  138 + *
  139 + * @access public
  140 + * @return object HTTP_Header
  141 + */
  142 + function HTTP_Header()
  143 + {
  144 + if (isset($_SERVER['SERVER_PROTOCOL'])) {
  145 + $this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3));
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * Set HTTP version
  151 + *
  152 + * @access public
  153 + * @return bool Returns true on success or false if version doesn't
  154 + * match 1.0 or 1.1 (note: 1 will result in 1.0)
  155 + * @param mixed $version HTTP version, either 1.0 or 1.1
  156 + */
  157 + function setHttpVersion($version)
  158 + {
  159 + $version = round((float) $version, 1);
  160 + if ($version < 1.0 || $version > 1.1) {
  161 + return false;
  162 + }
  163 + $this->_httpVersion = sprintf('%0.1f', $version);
  164 + return true;
  165 + }
  166 +
  167 + /**
  168 + * Get HTTP version
  169 + *
  170 + * @access public
  171 + * @return string
  172 + */
  173 + function getHttpVersion()
  174 + {
  175 + return $this->_httpVersion;
  176 + }
  177 +
  178 + /**
  179 + * Set Header
  180 + *
  181 + * The default value for the Last-Modified header will be current
  182 + * date and atime if $value is omitted.
  183 + *
  184 + * @access public
  185 + * @return bool Returns true on success or false if $key was empty or
  186 + * $value was not of an scalar type.
  187 + * @param string $key The name of the header.
  188 + * @param string $value The value of the header. (NULL to unset header)
  189 + */
  190 + function setHeader($key, $value = null)
  191 + {
  192 + if (empty($key) || (isset($value) && !is_scalar($value))) {
  193 + return false;
  194 + }
  195 +
  196 + $key = strToLower($key);
  197 + if ($key == 'last-modified') {
  198 + if (!isset($value)) {
  199 + $value = HTTP::Date(time());
  200 + } elseif (is_numeric($value)) {
  201 + $value = HTTP::Date($value);
  202 + }
  203 + }
  204 +
  205 + if (isset($value)) {
  206 + $this->_headers[$key] = $value;
  207 + } else {
  208 + unset($this->_headers[$key]);
  209 + }
  210 +
  211 + return true;
  212 + }
  213 +
  214 + /**
  215 + * Get Header
  216 + *
  217 + * If $key is omitted, all stored headers will be returned.
  218 + *
  219 + * @access public
  220 + * @return mixed Returns string value of the requested header,
  221 + * array values of all headers or false if header $key
  222 + * is not set.
  223 + * @param string $key The name of the header to fetch.
  224 + */
  225 + function getHeader($key = null)
  226 + {
  227 + if (!isset($key)) {
  228 + return $this->_headers;
  229 + }
  230 +
  231 + $key = strToLower($key);
  232 +
  233 + if (!isset($this->_headers[$key])) {
  234 + return false;
  235 + }
  236 +
  237 + return $this->_headers[$key];
  238 + }
  239 +
  240 + /**
  241 + * Send Headers
  242 + *
  243 + * Send out the header that you set via setHeader().
  244 + *
  245 + * @access public
  246 + * @return bool Returns true on success or false if headers are already
  247 + * sent.
  248 + * @param array $keys Headers to (not) send, see $include.
  249 + * @param array $include If true only $keys matching headers will be
  250 + * sent, if false only header not matching $keys will be
  251 + * sent.
  252 + */
  253 + function sendHeaders($keys = array(), $include = true)
  254 + {
  255 + if (headers_sent()) {
  256 + return false;
  257 + }
  258 +
  259 + if (count($keys)) {
  260 + array_change_key_case($keys, CASE_LOWER);
  261 + foreach ($this->_headers as $key => $value) {
  262 + if ($include ? in_array($key, $keys) : !in_array($key, $keys)) {
  263 + header($key .': '. $value);
  264 + }
  265 + }
  266 + } else {
  267 + foreach ($this->_headers as $header => $value) {
  268 + header($header .': '. $value);
  269 + }
  270 + }
  271 + return true;
  272 + }
  273 +
  274 + /**
  275 + * Send Satus Code
  276 + *
  277 + * Send out the given HTTP-Status code. Use this for example when you
  278 + * want to tell the client this page is cached, then you would call
  279 + * sendStatusCode(304).
  280 + *
  281 + * @see HTTP_Header_Cache::exitIfCached()
  282 + *
  283 + * @access public
  284 + * @return bool Returns true on success or false if headers are already
  285 + * sent.
  286 + * @param int $code The status code to send, i.e. 404, 304, 200, etc.
  287 + */
  288 + function sendStatusCode($code)
  289 + {
  290 + if (headers_sent()) {
  291 + return false;
  292 + }
  293 +
  294 + if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) {
  295 + $code = constant('HTTP_HEADER_STATUS_'. $code);
  296 + }
  297 +
  298 + if (strncasecmp(PHP_SAPI, 'cgi', 3)) {
  299 + header('HTTP/'. $this->_httpVersion .' '. $code);
  300 + } else {
  301 + header('Status: '. $code);
  302 + }
  303 + return true;
  304 + }
  305 +
  306 + /**
  307 + * Date to Timestamp
  308 + *
  309 + * Converts dates like
  310 + * Mon, 31 Mar 2003 15:26:34 GMT
  311 + * Tue, 15 Nov 1994 12:45:26 GMT
  312 + * into a timestamp, strtotime() didn't do it in older versions.
  313 + *
  314 + * @deprecated Use PHPs strtotime() instead.
  315 + * @access public
  316 + * @return mixed Returns int unix timestamp or false if the date doesn't
  317 + * seem to be a valid GMT date.
  318 + * @param string $date The GMT date.
  319 + */
  320 + function dateToTimestamp($date)
  321 + {
  322 + static $months = array(
  323 + null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
  324 + 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9,
  325 + 'Oct' => 10, 'Nov' => 11, 'Dec' => 12
  326 + );
  327 +
  328 + if (-1 < $timestamp = strToTime($date)) {
  329 + return $timestamp;
  330 + }
  331 +
  332 + if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~',
  333 + $date, $m)) {
  334 + return false;
  335 + }
  336 +
  337 + // [0] => Mon, 31 Mar 2003 15:42:55 GMT
  338 + // [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55
  339 + return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]);
  340 + }
  341 +
  342 + /**
  343 + * Redirect
  344 + *
  345 + * This function redirects the client. This is done by issuing a Location
  346 + * header and exiting. Additionally to HTTP::redirect() you can also add
  347 + * parameters to the url.
  348 + *
  349 + * If you dont need parameters to be added, simply use HTTP::redirect()
  350 + * otherwise use HTTP_Header::redirect().
  351 + *
  352 + * @see HTTP::redirect()
  353 + * @author Wolfram Kriesing <wk@visionp.de>
  354 + * @access public
  355 + * @return void
  356 + * @param string $url The URL to redirect to, if none is given it
  357 + * redirects to the current page.
  358 + * @param array $param Array of query string parameters to add; usually
  359 + * a set of key => value pairs; if an array entry consists
  360 + * only of an value it is used as key and the respective
  361 + * value is fetched from $GLOBALS[$value]
  362 + * @param bool $session Whether the session name/id should be added
  363 + */
  364 + function redirect($url = null, $param = array(), $session = false)
  365 + {
  366 + if (!isset($url)) {
  367 + $url = $_SERVER['PHP_SELF'];
  368 + }
  369 +
  370 + $qs = array();
  371 +
  372 + if ($session) {
  373 + $qs[] = session_name() .'='. session_id();
  374 + }
  375 +
  376 + if (is_array($param) && count($param)) {
  377 + if (count($param)) {
  378 + foreach ($param as $key => $val) {
  379 + if (is_string($key)) {
  380 + $qs[] = urlencode($key) .'='. urlencode($val);
  381 + } else {
  382 + $qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]);
  383 + }
  384 + }
  385 + }
  386 + }
  387 +
  388 + if ($qstr = implode('&', $qs)) {
  389 + $purl = parse_url($url);
  390 + $url .= (isset($purl['query']) ? '&' : '?') . $qstr;
  391 + }
  392 +
  393 + parent::redirect($url);
  394 + }
  395 +
  396 + /**#@+
  397 + * @author Davey Shafik <davey@php.net>
  398 + * @param int $http_code HTTP Code to check
  399 + * @access public
  400 + */
  401 +
  402 + /**
  403 + * Return HTTP Status Code Type
  404 + *
  405 + * @return int|false
  406 + */
  407 + function getStatusType($http_code)
  408 + {
  409 + if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) {
  410 + $type = substr($http_code,0,1);
  411 + switch ($type) {
  412 + case HTTP_HEADER_STATUS_INFORMATIONAL:
  413 + case HTTP_HEADER_STATUS_SUCCESSFUL:
  414 + case HTTP_HEADER_STATUS_REDIRECT:
  415 + case HTTP_HEADER_STATUS_CLIENT_ERROR:
  416 + case HTTP_HEADER_STATUS_SERVER_ERROR:
  417 + return $type;
  418 + break;
  419 + default:
  420 + return false;
  421 + break;
  422 + }
  423 + } else {
  424 + return false;
  425 + }
  426 + }
  427 +
  428 + /**
  429 + * Return Status Code Message
  430 + *
  431 + * @return string|false
  432 + */
  433 + function getStatusText($http_code)
  434 + {
  435 + if ($this->getStatusType($http_code)) {
  436 + if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) {
  437 + return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4);
  438 + } else {
  439 + return substr($http_code,4);
  440 + }
  441 + } else {
  442 + return false;
  443 + }
  444 + }
  445 +
  446 + /**
  447 + * Checks if HTTP Status code is Information (1xx)
  448 + *
  449 + * @return boolean
  450 + */
  451 + function isInformational($http_code)
  452 + {
  453 + if ($status_type = $this->getStatusType($http_code)) {
  454 + return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL;
  455 + } else {
  456 + return false;
  457 + }
  458 + }
  459 +
  460 + /**
  461 + * Checks if HTTP Status code is Successful (2xx)
  462 + *
  463 + * @return boolean
  464 + */
  465 + function isSuccessful($http_code)
  466 + {
  467 + if ($status_type = $this->getStatusType($http_code)) {
  468 + return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL;
  469 + } else {
  470 + return false;
  471 + }
  472 + }
  473 +
  474 + /**
  475 + * Checks if HTTP Status code is a Redirect (3xx)
  476 + *
  477 + * @return boolean
  478 + */
  479 + function isRedirect($http_code)
  480 + {
  481 + if ($status_type = $this->getStatusType($http_code)) {
  482 + return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT;
  483 + } else {
  484 + return false;
  485 + }
  486 + }
  487 +
  488 + /**
  489 + * Checks if HTTP Status code is a Client Error (4xx)
  490 + *
  491 + * @return boolean
  492 + */
  493 + function isClientError($http_code)
  494 + {
  495 + if ($status_type = $this->getStatusType($http_code)) {
  496 + return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR;
  497 + } else {
  498 + return false;
  499 + }
  500 + }
  501 +
  502 + /**
  503 + * Checks if HTTP Status code is Server Error (5xx)
  504 + *
  505 + * @return boolean
  506 + */
  507 + function isServerError($http_code)
  508 + {
  509 + if ($status_type = $this->getStatusType($http_code)) {
  510 + return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR;
  511 + } else {
  512 + return false;
  513 + }
  514 + }
  515 +
  516 + /**
  517 + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
  518 + *
  519 + * @return boolean
  520 + */
  521 + function isError($http_code)
  522 + {
  523 + if ($status_type = $this->getStatusType($http_code)) {
  524 + return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false;
  525 + } else {
  526 + return false;
  527 + }
  528 + }
  529 + /**#@-*/
  530 +}
  531 +?>
... ...
thirdparty/pear/HTTP/Header/Cache.php 0 → 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Header::Cache
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Header
  11 + * @author Wolfram Kriesing <wk@visionp.de>
  12 + * @author Michael Wallner <mike@php.net>
  13 + * @copyright 2003-2005 The Authors
  14 + * @license BSD, revised
  15 + * @version CVS: $Id: Cache.php,v 1.24 2005/11/08 19:06:14 mike Exp $
  16 + * @link http://pear.php.net/package/HTTP_Header
  17 + */
  18 +
  19 +/**
  20 + * Requires HTTP_Header
  21 + */
  22 +require_once 'HTTP/Header.php';
  23 +
  24 +/**
  25 + * HTTP_Header_Cache
  26 + *
  27 + * This package provides methods to easier handle caching of HTTP pages. That
  28 + * means that the pages can be cached at the client (user agent or browser) and
  29 + * your application only needs to send "hey client you already have the pages".
  30 + *
  31 + * Which is done by sending the HTTP-Status "304 Not Modified", so that your
  32 + * application load and the network traffic can be reduced, since you only need
  33 + * to send the complete page once. This is really an advantage e.g. for
  34 + * generated style sheets, or simply pages that do only change rarely.
  35 + *
  36 + * Usage:
  37 + * <code>
  38 + * require_once 'HTTP/Header/Cache.php';
  39 + * $httpCache = new HTTP_Header_Cache(4, 'weeks');
  40 + * $httpCache->sendHeaders();
  41 + * // your code goes here
  42 + * </code>
  43 + *
  44 + * @package HTTP_Header
  45 + * @category HTTP
  46 + * @access public
  47 + * @version $Revision: 1.24 $
  48 + */
  49 +class HTTP_Header_Cache extends HTTP_Header
  50 +{
  51 + /**
  52 + * Constructor
  53 + *
  54 + * Set the amount of time to cache.
  55 + *
  56 + * @access public
  57 + * @return object HTTP_Header_Cache
  58 + * @param int $expires
  59 + * @param string $unit
  60 + */
  61 + function HTTP_Header_Cache($expires = 0, $unit = 'seconds')
  62 + {
  63 + parent::HTTP_Header();
  64 + $this->setHeader('Pragma', 'cache');
  65 + $this->setHeader('Last-Modified', $this->getCacheStart());
  66 + $this->setHeader('Cache-Control', 'private, must-revalidate, max-age=0');
  67 +
  68 + if ($expires) {
  69 + if (!$this->isOlderThan($expires, $unit)) {
  70 + $this->exitCached();
  71 + }
  72 + $this->setHeader('Last-Modified', time());
  73 + }
  74 + }
  75 +
  76 + /**
  77 + * Get Cache Start
  78 + *
  79 + * Returns the unix timestamp of the If-Modified-Since HTTP header or the
  80 + * current time if the header was not sent by the client.
  81 + *
  82 + * @access public
  83 + * @return int unix timestamp
  84 + */
  85 + function getCacheStart()
  86 + {
  87 + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$this->isPost()) {
  88 + return strtotime(current($array = explode(';',
  89 + $_SERVER['HTTP_IF_MODIFIED_SINCE'])));
  90 + }
  91 + return time();
  92 + }
  93 +
  94 + /**
  95 + * Is Older Than
  96 + *
  97 + * You can call it like this:
  98 + * <code>
  99 + * $httpCache->isOlderThan(1, 'day');
  100 + * $httpCache->isOlderThan(47, 'days');
  101 + *
  102 + * $httpCache->isOlderThan(1, 'week');
  103 + * $httpCache->isOlderThan(3, 'weeks');
  104 + *
  105 + * $httpCache->isOlderThan(1, 'hour');
  106 + * $httpCache->isOlderThan(5, 'hours');
  107 + *
  108 + * $httpCache->isOlderThan(1, 'minute');
  109 + * $httpCache->isOlderThan(15, 'minutes');
  110 + *
  111 + * $httpCache->isOlderThan(1, 'second');
  112 + * $httpCache->isOlderThan(15);
  113 + * </code>
  114 + *
  115 + * If you specify something greater than "weeks" as time untit, it just
  116 + * works approximatly, because a month is taken to consist of 4.3 weeks.
  117 + *
  118 + * @access public
  119 + * @return bool Returns true if requested page is older than specified.
  120 + * @param int $time The amount of time.
  121 + * @param string $unit The unit of the time amount - (year[s], month[s],
  122 + * week[s], day[s], hour[s], minute[s], second[s]).
  123 + */
  124 + function isOlderThan($time = 0, $unit = 'seconds')
  125 + {
  126 + if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || $this->isPost()) {
  127 + return true;
  128 + }
  129 + if (!$time) {
  130 + return false;
  131 + }
  132 +
  133 + switch (strtolower($unit))
  134 + {
  135 + case 'year':
  136 + case 'years':
  137 + $time *= 12;
  138 + case 'month':
  139 + case 'months':
  140 + $time *= 4.3;
  141 + case 'week':
  142 + case 'weeks':
  143 + $time *= 7;
  144 + case 'day':
  145 + case 'days':
  146 + $time *= 24;
  147 + case 'hour':
  148 + case 'hours':
  149 + $time *= 60;
  150 + case 'minute':
  151 + case 'minutes':
  152 + $time *= 60;
  153 + }
  154 +
  155 + return (time() - $this->getCacheStart()) > $time;
  156 + }
  157 +
  158 + /**
  159 + * Is Cached
  160 + *
  161 + * Check whether we can consider to be cached on the client side.
  162 + *
  163 + * @access public
  164 + * @return bool Whether the page/resource is considered to be cached.
  165 + * @param int $lastModified Unix timestamp of last modification.
  166 + */
  167 + function isCached($lastModified = 0)
  168 + {
  169 + if ($this->isPost()) {
  170 + return false;
  171 + }
  172 + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$lastModified) {
  173 + return true;
  174 + }
  175 + if (!$seconds = time() - $lastModified) {
  176 + return false;
  177 + }
  178 + return !$this->isOlderThan($seconds);
  179 + }
  180 +
  181 + /**
  182 + * Is Post
  183 + *
  184 + * Check if request method is "POST".
  185 + *
  186 + * @access public
  187 + * @return bool
  188 + */
  189 + function isPost()
  190 + {
  191 + return isset($_SERVER['REQUEST_METHOD']) and
  192 + 'POST' == $_SERVER['REQUEST_METHOD'];
  193 + }
  194 +
  195 + /**
  196 + * Exit If Cached
  197 + *
  198 + * Exit with "HTTP 304 Not Modified" if we consider to be cached.
  199 + *
  200 + * @access public
  201 + * @return void
  202 + * @param int $lastModified Unix timestamp of last modification.
  203 + */
  204 + function exitIfCached($lastModified = 0)
  205 + {
  206 + if ($this->isCached($lastModified)) {
  207 + $this->exitCached();
  208 + }
  209 + }
  210 +
  211 + /**
  212 + * Exit Cached
  213 + *
  214 + * Exit with "HTTP 304 Not Modified".
  215 + *
  216 + * @access public
  217 + * @return void
  218 + */
  219 + function exitCached()
  220 + {
  221 + $this->sendHeaders();
  222 + $this->sendStatusCode(304);
  223 + exit;
  224 + }
  225 +
  226 + /**
  227 + * Set Last Modified
  228 + *
  229 + * @access public
  230 + * @return void
  231 + * @param int $lastModified The unix timestamp of last modification.
  232 + */
  233 + function setLastModified($lastModified = null)
  234 + {
  235 + $this->setHeader('Last-Modified', $lastModified);
  236 + }
  237 +}
  238 +?>
... ...
thirdparty/pear/HTTP/Request.php 0 → 100644
  1 +<?php
  2 +// +-----------------------------------------------------------------------+
  3 +// | Copyright (c) 2002-2003, Richard Heyes |
  4 +// | All rights reserved. |
  5 +// | |
  6 +// | Redistribution and use in source and binary forms, with or without |
  7 +// | modification, are permitted provided that the following conditions |
  8 +// | are met: |
  9 +// | |
  10 +// | o Redistributions of source code must retain the above copyright |
  11 +// | notice, this list of conditions and the following disclaimer. |
  12 +// | o Redistributions in binary form must reproduce the above copyright |
  13 +// | notice, this list of conditions and the following disclaimer in the |
  14 +// | documentation and/or other materials provided with the distribution.|
  15 +// | o The names of the authors may not be used to endorse or promote |
  16 +// | products derived from this software without specific prior written |
  17 +// | permission. |
  18 +// | |
  19 +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  20 +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  21 +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22 +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
  23 +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24 +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
  25 +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26 +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27 +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
  28 +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29 +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
  30 +// | |
  31 +// +-----------------------------------------------------------------------+
  32 +// | Author: Richard Heyes <richard@phpguru.org> |
  33 +// +-----------------------------------------------------------------------+
  34 +//
  35 +// $Id: Request.php,v 1.43 2005/11/06 18:29:14 avb Exp $
  36 +//
  37 +// HTTP_Request Class
  38 +//
  39 +// Simple example, (Fetches yahoo.com and displays it):
  40 +//
  41 +// $a = &new HTTP_Request('http://www.yahoo.com/');
  42 +// $a->sendRequest();
  43 +// echo $a->getResponseBody();
  44 +//
  45 +
  46 +require_once 'PEAR.php';
  47 +require_once 'Net/Socket.php';
  48 +require_once 'Net/URL.php';
  49 +
  50 +define('HTTP_REQUEST_METHOD_GET', 'GET', true);
  51 +define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
  52 +define('HTTP_REQUEST_METHOD_POST', 'POST', true);
  53 +define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
  54 +define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
  55 +define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
  56 +define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
  57 +
  58 +define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
  59 +define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
  60 +
  61 +class HTTP_Request {
  62 +
  63 + /**
  64 + * Instance of Net_URL
  65 + * @var object Net_URL
  66 + */
  67 + var $_url;
  68 +
  69 + /**
  70 + * Type of request
  71 + * @var string
  72 + */
  73 + var $_method;
  74 +
  75 + /**
  76 + * HTTP Version
  77 + * @var string
  78 + */
  79 + var $_http;
  80 +
  81 + /**
  82 + * Request headers
  83 + * @var array
  84 + */
  85 + var $_requestHeaders;
  86 +
  87 + /**
  88 + * Basic Auth Username
  89 + * @var string
  90 + */
  91 + var $_user;
  92 +
  93 + /**
  94 + * Basic Auth Password
  95 + * @var string
  96 + */
  97 + var $_pass;
  98 +
  99 + /**
  100 + * Socket object
  101 + * @var object Net_Socket
  102 + */
  103 + var $_sock;
  104 +
  105 + /**
  106 + * Proxy server
  107 + * @var string
  108 + */
  109 + var $_proxy_host;
  110 +
  111 + /**
  112 + * Proxy port
  113 + * @var integer
  114 + */
  115 + var $_proxy_port;
  116 +
  117 + /**
  118 + * Proxy username
  119 + * @var string
  120 + */
  121 + var $_proxy_user;
  122 +
  123 + /**
  124 + * Proxy password
  125 + * @var string
  126 + */
  127 + var $_proxy_pass;
  128 +
  129 + /**
  130 + * Post data
  131 + * @var array
  132 + */
  133 + var $_postData;
  134 +
  135 + /**
  136 + * Request body
  137 + * @var string
  138 + */
  139 + var $_body;
  140 +
  141 + /**
  142 + * A list of methods that MUST NOT have a request body, per RFC 2616
  143 + * @var array
  144 + */
  145 + var $_bodyDisallowed = array('TRACE');
  146 +
  147 + /**
  148 + * Files to post
  149 + * @var array
  150 + */
  151 + var $_postFiles = array();
  152 +
  153 + /**
  154 + * Connection timeout.
  155 + * @var float
  156 + */
  157 + var $_timeout;
  158 +
  159 + /**
  160 + * HTTP_Response object
  161 + * @var object HTTP_Response
  162 + */
  163 + var $_response;
  164 +
  165 + /**
  166 + * Whether to allow redirects
  167 + * @var boolean
  168 + */
  169 + var $_allowRedirects;
  170 +
  171 + /**
  172 + * Maximum redirects allowed
  173 + * @var integer
  174 + */
  175 + var $_maxRedirects;
  176 +
  177 + /**
  178 + * Current number of redirects
  179 + * @var integer
  180 + */
  181 + var $_redirects;
  182 +
  183 + /**
  184 + * Whether to append brackets [] to array variables
  185 + * @var bool
  186 + */
  187 + var $_useBrackets = true;
  188 +
  189 + /**
  190 + * Attached listeners
  191 + * @var array
  192 + */
  193 + var $_listeners = array();
  194 +
  195 + /**
  196 + * Whether to save response body in response object property
  197 + * @var bool
  198 + */
  199 + var $_saveBody = true;
  200 +
  201 + /**
  202 + * Timeout for reading from socket (array(seconds, microseconds))
  203 + * @var array
  204 + */
  205 + var $_readTimeout = null;
  206 +
  207 + /**
  208 + * Options to pass to Net_Socket::connect. See stream_context_create
  209 + * @var array
  210 + */
  211 + var $_socketOptions = null;
  212 +
  213 + /**
  214 + * Constructor
  215 + *
  216 + * Sets up the object
  217 + * @param string The url to fetch/access
  218 + * @param array Associative array of parameters which can have the following keys:
  219 + * <ul>
  220 + * <li>method - Method to use, GET, POST etc (string)</li>
  221 + * <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
  222 + * <li>user - Basic Auth username (string)</li>
  223 + * <li>pass - Basic Auth password (string)</li>
  224 + * <li>proxy_host - Proxy server host (string)</li>
  225 + * <li>proxy_port - Proxy server port (integer)</li>
  226 + * <li>proxy_user - Proxy auth username (string)</li>
  227 + * <li>proxy_pass - Proxy auth password (string)</li>
  228 + * <li>timeout - Connection timeout in seconds (float)</li>
  229 + * <li>allowRedirects - Whether to follow redirects or not (bool)</li>
  230 + * <li>maxRedirects - Max number of redirects to follow (integer)</li>
  231 + * <li>useBrackets - Whether to append [] to array variable names (bool)</li>
  232 + * <li>saveBody - Whether to save response body in response object property (bool)</li>
  233 + * <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
  234 + * <li>socketOptions - Options to pass to Net_Socket object (array)</li>
  235 + * </ul>
  236 + * @access public
  237 + */
  238 + function HTTP_Request($url = '', $params = array())
  239 + {
  240 + $this->_sock = &new Net_Socket();
  241 + $this->_method = HTTP_REQUEST_METHOD_GET;
  242 + $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
  243 + $this->_requestHeaders = array();
  244 + $this->_postData = array();
  245 + $this->_body = null;
  246 +
  247 + $this->_user = null;
  248 + $this->_pass = null;
  249 +
  250 + $this->_proxy_host = null;
  251 + $this->_proxy_port = null;
  252 + $this->_proxy_user = null;
  253 + $this->_proxy_pass = null;
  254 +
  255 + $this->_allowRedirects = false;
  256 + $this->_maxRedirects = 3;
  257 + $this->_redirects = 0;
  258 +
  259 + $this->_timeout = null;
  260 + $this->_response = null;
  261 +
  262 + foreach ($params as $key => $value) {
  263 + $this->{'_' . $key} = $value;
  264 + }
  265 +
  266 + if (!empty($url)) {
  267 + $this->setURL($url);
  268 + }
  269 +
  270 + // Default useragent
  271 + $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
  272 +
  273 + // Make sure keepalives dont knobble us
  274 + $this->addHeader('Connection', 'close');
  275 +
  276 + // Basic authentication
  277 + if (!empty($this->_user)) {
  278 + $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
  279 + }
  280 +
  281 + // Use gzip encoding if possible
  282 + // Avoid gzip encoding if using multibyte functions (see #1781)
  283 + if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib') &&
  284 + 0 == (2 & ini_get('mbstring.func_overload'))) {
  285 +
  286 + $this->addHeader('Accept-Encoding', 'gzip');
  287 + }
  288 + }
  289 +
  290 + /**
  291 + * Generates a Host header for HTTP/1.1 requests
  292 + *
  293 + * @access private
  294 + * @return string
  295 + */
  296 + function _generateHostHeader()
  297 + {
  298 + if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
  299 + $host = $this->_url->host . ':' . $this->_url->port;
  300 +
  301 + } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
  302 + $host = $this->_url->host . ':' . $this->_url->port;
  303 +
  304 + } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
  305 + $host = $this->_url->host . ':' . $this->_url->port;
  306 +
  307 + } else {
  308 + $host = $this->_url->host;
  309 + }
  310 +
  311 + return $host;
  312 + }
  313 +
  314 + /**
  315 + * Resets the object to its initial state (DEPRECATED).
  316 + * Takes the same parameters as the constructor.
  317 + *
  318 + * @param string $url The url to be requested
  319 + * @param array $params Associative array of parameters
  320 + * (see constructor for details)
  321 + * @access public
  322 + * @deprecated deprecated since 1.2, call the constructor if this is necessary
  323 + */
  324 + function reset($url, $params = array())
  325 + {
  326 + $this->HTTP_Request($url, $params);
  327 + }
  328 +
  329 + /**
  330 + * Sets the URL to be requested
  331 + *
  332 + * @param string The url to be requested
  333 + * @access public
  334 + */
  335 + function setURL($url)
  336 + {
  337 + $this->_url = &new Net_URL($url, $this->_useBrackets);
  338 +
  339 + if (!empty($this->_url->user) || !empty($this->_url->pass)) {
  340 + $this->setBasicAuth($this->_url->user, $this->_url->pass);
  341 + }
  342 +
  343 + if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
  344 + $this->addHeader('Host', $this->_generateHostHeader());
  345 + }
  346 + }
  347 +
  348 + /**
  349 + * Sets a proxy to be used
  350 + *
  351 + * @param string Proxy host
  352 + * @param int Proxy port
  353 + * @param string Proxy username
  354 + * @param string Proxy password
  355 + * @access public
  356 + */
  357 + function setProxy($host, $port = 8080, $user = null, $pass = null)
  358 + {
  359 + $this->_proxy_host = $host;
  360 + $this->_proxy_port = $port;
  361 + $this->_proxy_user = $user;
  362 + $this->_proxy_pass = $pass;
  363 +
  364 + if (!empty($user)) {
  365 + $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
  366 + }
  367 + }
  368 +
  369 + /**
  370 + * Sets basic authentication parameters
  371 + *
  372 + * @param string Username
  373 + * @param string Password
  374 + */
  375 + function setBasicAuth($user, $pass)
  376 + {
  377 + $this->_user = $user;
  378 + $this->_pass = $pass;
  379 +
  380 + $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
  381 + }
  382 +
  383 + /**
  384 + * Sets the method to be used, GET, POST etc.
  385 + *
  386 + * @param string Method to use. Use the defined constants for this
  387 + * @access public
  388 + */
  389 + function setMethod($method)
  390 + {
  391 + $this->_method = $method;
  392 + }
  393 +
  394 + /**
  395 + * Sets the HTTP version to use, 1.0 or 1.1
  396 + *
  397 + * @param string Version to use. Use the defined constants for this
  398 + * @access public
  399 + */
  400 + function setHttpVer($http)
  401 + {
  402 + $this->_http = $http;
  403 + }
  404 +
  405 + /**
  406 + * Adds a request header
  407 + *
  408 + * @param string Header name
  409 + * @param string Header value
  410 + * @access public
  411 + */
  412 + function addHeader($name, $value)
  413 + {
  414 + $this->_requestHeaders[strtolower($name)] = $value;
  415 + }
  416 +
  417 + /**
  418 + * Removes a request header
  419 + *
  420 + * @param string Header name to remove
  421 + * @access public
  422 + */
  423 + function removeHeader($name)
  424 + {
  425 + if (isset($this->_requestHeaders[strtolower($name)])) {
  426 + unset($this->_requestHeaders[strtolower($name)]);
  427 + }
  428 + }
  429 +
  430 + /**
  431 + * Adds a querystring parameter
  432 + *
  433 + * @param string Querystring parameter name
  434 + * @param string Querystring parameter value
  435 + * @param bool Whether the value is already urlencoded or not, default = not
  436 + * @access public
  437 + */
  438 + function addQueryString($name, $value, $preencoded = false)
  439 + {
  440 + $this->_url->addQueryString($name, $value, $preencoded);
  441 + }
  442 +
  443 + /**
  444 + * Sets the querystring to literally what you supply
  445 + *
  446 + * @param string The querystring data. Should be of the format foo=bar&x=y etc
  447 + * @param bool Whether data is already urlencoded or not, default = already encoded
  448 + * @access public
  449 + */
  450 + function addRawQueryString($querystring, $preencoded = true)
  451 + {
  452 + $this->_url->addRawQueryString($querystring, $preencoded);
  453 + }
  454 +
  455 + /**
  456 + * Adds postdata items
  457 + *
  458 + * @param string Post data name
  459 + * @param string Post data value
  460 + * @param bool Whether data is already urlencoded or not, default = not
  461 + * @access public
  462 + */
  463 + function addPostData($name, $value, $preencoded = false)
  464 + {
  465 + if ($preencoded) {
  466 + $this->_postData[$name] = $value;
  467 + } else {
  468 + $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
  469 + }
  470 + }
  471 +
  472 + /**
  473 + * Recursively applies the callback function to the value
  474 + *
  475 + * @param mixed Callback function
  476 + * @param mixed Value to process
  477 + * @access private
  478 + * @return mixed Processed value
  479 + */
  480 + function _arrayMapRecursive($callback, $value)
  481 + {
  482 + if (!is_array($value)) {
  483 + return call_user_func($callback, $value);
  484 + } else {
  485 + $map = array();
  486 + foreach ($value as $k => $v) {
  487 + $map[$k] = $this->_arrayMapRecursive($callback, $v);
  488 + }
  489 + return $map;
  490 + }
  491 + }
  492 +
  493 + /**
  494 + * Adds a file to upload
  495 + *
  496 + * This also changes content-type to 'multipart/form-data' for proper upload
  497 + *
  498 + * @access public
  499 + * @param string name of file-upload field
  500 + * @param mixed file name(s)
  501 + * @param mixed content-type(s) of file(s) being uploaded
  502 + * @return bool true on success
  503 + * @throws PEAR_Error
  504 + */
  505 + function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
  506 + {
  507 + if (!is_array($fileName) && !is_readable($fileName)) {
  508 + return PEAR::raiseError("File '{$fileName}' is not readable");
  509 + } elseif (is_array($fileName)) {
  510 + foreach ($fileName as $name) {
  511 + if (!is_readable($name)) {
  512 + return PEAR::raiseError("File '{$name}' is not readable");
  513 + }
  514 + }
  515 + }
  516 + $this->addHeader('Content-Type', 'multipart/form-data');
  517 + $this->_postFiles[$inputName] = array(
  518 + 'name' => $fileName,
  519 + 'type' => $contentType
  520 + );
  521 + return true;
  522 + }
  523 +
  524 + /**
  525 + * Adds raw postdata (DEPRECATED)
  526 + *
  527 + * @param string The data
  528 + * @param bool Whether data is preencoded or not, default = already encoded
  529 + * @access public
  530 + * @deprecated deprecated since 1.3.0, method addBody() should be used instead
  531 + */
  532 + function addRawPostData($postdata, $preencoded = true)
  533 + {
  534 + $this->_body = $preencoded ? $postdata : urlencode($postdata);
  535 + }
  536 +
  537 + /**
  538 + * Sets the request body (for POST, PUT and similar requests)
  539 + *
  540 + * @param string Request body
  541 + * @access public
  542 + */
  543 + function setBody($body)
  544 + {
  545 + $this->_body = $body;
  546 + }
  547 +
  548 + /**
  549 + * Clears any postdata that has been added (DEPRECATED).
  550 + *
  551 + * Useful for multiple request scenarios.
  552 + *
  553 + * @access public
  554 + * @deprecated deprecated since 1.2
  555 + */
  556 + function clearPostData()
  557 + {
  558 + $this->_postData = null;
  559 + }
  560 +
  561 + /**
  562 + * Appends a cookie to "Cookie:" header
  563 + *
  564 + * @param string $name cookie name
  565 + * @param string $value cookie value
  566 + * @access public
  567 + */
  568 + function addCookie($name, $value)
  569 + {
  570 + $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
  571 + $this->addHeader('Cookie', $cookies . $name . '=' . $value);
  572 + }
  573 +
  574 + /**
  575 + * Clears any cookies that have been added (DEPRECATED).
  576 + *
  577 + * Useful for multiple request scenarios
  578 + *
  579 + * @access public
  580 + * @deprecated deprecated since 1.2
  581 + */
  582 + function clearCookies()
  583 + {
  584 + $this->removeHeader('Cookie');
  585 + }
  586 +
  587 + /**
  588 + * Sends the request
  589 + *
  590 + * @access public
  591 + * @param bool Whether to store response body in Response object property,
  592 + * set this to false if downloading a LARGE file and using a Listener
  593 + * @return mixed PEAR error on error, true otherwise
  594 + */
  595 + function sendRequest($saveBody = true)
  596 + {
  597 + if (!is_a($this->_url, 'Net_URL')) {
  598 + return PEAR::raiseError('No URL given.');
  599 + }
  600 +
  601 + $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
  602 + $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
  603 +
  604 + // 4.3.0 supports SSL connections using OpenSSL. The function test determines
  605 + // we running on at least 4.3.0
  606 + if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) {
  607 + if (isset($this->_proxy_host)) {
  608 + return PEAR::raiseError('HTTPS proxies are not supported.');
  609 + }
  610 + $host = 'ssl://' . $host;
  611 + }
  612 +
  613 + // magic quotes may fuck up file uploads and chunked response processing
  614 + $magicQuotes = ini_get('magic_quotes_runtime');
  615 + ini_set('magic_quotes_runtime', false);
  616 +
  617 + // If this is a second request, we may get away without
  618 + // re-connecting if they're on the same server
  619 + $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
  620 + PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
  621 +
  622 + if (!PEAR::isError($err)) {
  623 + if (!empty($this->_readTimeout)) {
  624 + $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
  625 + }
  626 +
  627 + $this->_notify('sentRequest');
  628 +
  629 + // Read the response
  630 + $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
  631 + $err = $this->_response->process($this->_saveBody && $saveBody);
  632 + }
  633 +
  634 + ini_set('magic_quotes_runtime', $magicQuotes);
  635 +
  636 + if (PEAR::isError($err)) {
  637 + return $err;
  638 + }
  639 +
  640 +
  641 + // Check for redirection
  642 + if ( $this->_allowRedirects
  643 + AND $this->_redirects <= $this->_maxRedirects
  644 + AND $this->getResponseCode() > 300
  645 + AND $this->getResponseCode() < 399
  646 + AND !empty($this->_response->_headers['location'])) {
  647 +
  648 +
  649 + $redirect = $this->_response->_headers['location'];
  650 +
  651 + // Absolute URL
  652 + if (preg_match('/^https?:\/\//i', $redirect)) {
  653 + $this->_url = &new Net_URL($redirect);
  654 + $this->addHeader('Host', $this->_generateHostHeader());
  655 + // Absolute path
  656 + } elseif ($redirect{0} == '/') {
  657 + $this->_url->path = $redirect;
  658 +
  659 + // Relative path
  660 + } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
  661 + if (substr($this->_url->path, -1) == '/') {
  662 + $redirect = $this->_url->path . $redirect;
  663 + } else {
  664 + $redirect = dirname($this->_url->path) . '/' . $redirect;
  665 + }
  666 + $redirect = Net_URL::resolvePath($redirect);
  667 + $this->_url->path = $redirect;
  668 +
  669 + // Filename, no path
  670 + } else {
  671 + if (substr($this->_url->path, -1) == '/') {
  672 + $redirect = $this->_url->path . $redirect;
  673 + } else {
  674 + $redirect = dirname($this->_url->path) . '/' . $redirect;
  675 + }
  676 + $this->_url->path = $redirect;
  677 + }
  678 +
  679 + $this->_redirects++;
  680 + return $this->sendRequest($saveBody);
  681 +
  682 + // Too many redirects
  683 + } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
  684 + return PEAR::raiseError('Too many redirects');
  685 + }
  686 +
  687 + $this->_sock->disconnect();
  688 +
  689 + return true;
  690 + }
  691 +
  692 + /**
  693 + * Returns the response code
  694 + *
  695 + * @access public
  696 + * @return mixed Response code, false if not set
  697 + */
  698 + function getResponseCode()
  699 + {
  700 + return isset($this->_response->_code) ? $this->_response->_code : false;
  701 + }
  702 +
  703 + /**
  704 + * Returns either the named header or all if no name given
  705 + *
  706 + * @access public
  707 + * @param string The header name to return, do not set to get all headers
  708 + * @return mixed either the value of $headername (false if header is not present)
  709 + * or an array of all headers
  710 + */
  711 + function getResponseHeader($headername = null)
  712 + {
  713 + if (!isset($headername)) {
  714 + return isset($this->_response->_headers)? $this->_response->_headers: array();
  715 + } else {
  716 + $headername = strtolower($headername);
  717 + return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
  718 + }
  719 + }
  720 +
  721 + /**
  722 + * Returns the body of the response
  723 + *
  724 + * @access public
  725 + * @return mixed response body, false if not set
  726 + */
  727 + function getResponseBody()
  728 + {
  729 + return isset($this->_response->_body) ? $this->_response->_body : false;
  730 + }
  731 +
  732 + /**
  733 + * Returns cookies set in response
  734 + *
  735 + * @access public
  736 + * @return mixed array of response cookies, false if none are present
  737 + */
  738 + function getResponseCookies()
  739 + {
  740 + return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
  741 + }
  742 +
  743 + /**
  744 + * Builds the request string
  745 + *
  746 + * @access private
  747 + * @return string The request string
  748 + */
  749 + function _buildRequest()
  750 + {
  751 + $separator = ini_get('arg_separator.output');
  752 + ini_set('arg_separator.output', '&');
  753 + $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
  754 + ini_set('arg_separator.output', $separator);
  755 +
  756 + $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
  757 + $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
  758 + $path = (empty($this->_url->path)? '/': $this->_url->path) . $querystring;
  759 + $url = $host . $port . $path;
  760 +
  761 + $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
  762 +
  763 + if (in_array($this->_method, $this->_bodyDisallowed) ||
  764 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body)) ||
  765 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_postData) && empty($this->_postFiles))) {
  766 +
  767 + $this->removeHeader('Content-Type');
  768 + } else {
  769 + if (empty($this->_requestHeaders['content-type'])) {
  770 + // Add default content-type
  771 + $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
  772 + } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
  773 + $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
  774 + $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
  775 + }
  776 + }
  777 +
  778 + // Request Headers
  779 + if (!empty($this->_requestHeaders)) {
  780 + foreach ($this->_requestHeaders as $name => $value) {
  781 + $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
  782 + $request .= $canonicalName . ': ' . $value . "\r\n";
  783 + }
  784 + }
  785 +
  786 + // No post data or wrong method, so simply add a final CRLF
  787 + if (in_array($this->_method, $this->_bodyDisallowed) ||
  788 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) {
  789 +
  790 + $request .= "\r\n";
  791 +
  792 + // Post data if it's an array
  793 + } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
  794 + (!empty($this->_postData) || !empty($this->_postFiles))) {
  795 +
  796 + // "normal" POST request
  797 + if (!isset($boundary)) {
  798 + $postdata = implode('&', array_map(
  799 + create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
  800 + $this->_flattenArray('', $this->_postData)
  801 + ));
  802 +
  803 + // multipart request, probably with file uploads
  804 + } else {
  805 + $postdata = '';
  806 + if (!empty($this->_postData)) {
  807 + $flatData = $this->_flattenArray('', $this->_postData);
  808 + foreach ($flatData as $item) {
  809 + $postdata .= '--' . $boundary . "\r\n";
  810 + $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
  811 + $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
  812 + }
  813 + }
  814 + foreach ($this->_postFiles as $name => $value) {
  815 + if (is_array($value['name'])) {
  816 + $varname = $name . ($this->_useBrackets? '[]': '');
  817 + } else {
  818 + $varname = $name;
  819 + $value['name'] = array($value['name']);
  820 + }
  821 + foreach ($value['name'] as $key => $filename) {
  822 + $fp = fopen($filename, 'r');
  823 + $data = fread($fp, filesize($filename));
  824 + fclose($fp);
  825 + $basename = basename($filename);
  826 + $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
  827 +
  828 + $postdata .= '--' . $boundary . "\r\n";
  829 + $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
  830 + $postdata .= "\r\nContent-Type: " . $type;
  831 + $postdata .= "\r\n\r\n" . $data . "\r\n";
  832 + }
  833 + }
  834 + $postdata .= '--' . $boundary . "--\r\n";
  835 + }
  836 + $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n";
  837 + $request .= $postdata;
  838 +
  839 + // Explicitly set request body
  840 + } elseif (!empty($this->_body)) {
  841 +
  842 + $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n";
  843 + $request .= $this->_body;
  844 + }
  845 +
  846 + return $request;
  847 + }
  848 +
  849 + /**
  850 + * Helper function to change the (probably multidimensional) associative array
  851 + * into the simple one.
  852 + *
  853 + * @param string name for item
  854 + * @param mixed item's values
  855 + * @return array array with the following items: array('item name', 'item value');
  856 + */
  857 + function _flattenArray($name, $values)
  858 + {
  859 + if (!is_array($values)) {
  860 + return array(array($name, $values));
  861 + } else {
  862 + $ret = array();
  863 + foreach ($values as $k => $v) {
  864 + if (empty($name)) {
  865 + $newName = $k;
  866 + } elseif ($this->_useBrackets) {
  867 + $newName = $name . '[' . $k . ']';
  868 + } else {
  869 + $newName = $name;
  870 + }
  871 + $ret = array_merge($ret, $this->_flattenArray($newName, $v));
  872 + }
  873 + return $ret;
  874 + }
  875 + }
  876 +
  877 +
  878 + /**
  879 + * Adds a Listener to the list of listeners that are notified of
  880 + * the object's events
  881 + *
  882 + * @param object HTTP_Request_Listener instance to attach
  883 + * @return boolean whether the listener was successfully attached
  884 + * @access public
  885 + */
  886 + function attach(&$listener)
  887 + {
  888 + if (!is_a($listener, 'HTTP_Request_Listener')) {
  889 + return false;
  890 + }
  891 + $this->_listeners[$listener->getId()] =& $listener;
  892 + return true;
  893 + }
  894 +
  895 +
  896 + /**
  897 + * Removes a Listener from the list of listeners
  898 + *
  899 + * @param object HTTP_Request_Listener instance to detach
  900 + * @return boolean whether the listener was successfully detached
  901 + * @access public
  902 + */
  903 + function detach(&$listener)
  904 + {
  905 + if (!is_a($listener, 'HTTP_Request_Listener') ||
  906 + !isset($this->_listeners[$listener->getId()])) {
  907 + return false;
  908 + }
  909 + unset($this->_listeners[$listener->getId()]);
  910 + return true;
  911 + }
  912 +
  913 +
  914 + /**
  915 + * Notifies all registered listeners of an event.
  916 + *
  917 + * Events sent by HTTP_Request object
  918 + * 'sentRequest': after the request was sent
  919 + * Events sent by HTTP_Response object
  920 + * 'gotHeaders': after receiving response headers (headers are passed in $data)
  921 + * 'tick': on receiving a part of response body (the part is passed in $data)
  922 + * 'gzTick': on receiving a gzip-encoded part of response body (ditto)
  923 + * 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
  924 + *
  925 + * @param string Event name
  926 + * @param mixed Additional data
  927 + * @access private
  928 + */
  929 + function _notify($event, $data = null)
  930 + {
  931 + foreach (array_keys($this->_listeners) as $id) {
  932 + $this->_listeners[$id]->update($this, $event, $data);
  933 + }
  934 + }
  935 +}
  936 +
  937 +
  938 +/**
  939 +* Response class to complement the Request class
  940 +*/
  941 +class HTTP_Response
  942 +{
  943 + /**
  944 + * Socket object
  945 + * @var object
  946 + */
  947 + var $_sock;
  948 +
  949 + /**
  950 + * Protocol
  951 + * @var string
  952 + */
  953 + var $_protocol;
  954 +
  955 + /**
  956 + * Return code
  957 + * @var string
  958 + */
  959 + var $_code;
  960 +
  961 + /**
  962 + * Response headers
  963 + * @var array
  964 + */
  965 + var $_headers;
  966 +
  967 + /**
  968 + * Cookies set in response
  969 + * @var array
  970 + */
  971 + var $_cookies;
  972 +
  973 + /**
  974 + * Response body
  975 + * @var string
  976 + */
  977 + var $_body = '';
  978 +
  979 + /**
  980 + * Used by _readChunked(): remaining length of the current chunk
  981 + * @var string
  982 + */
  983 + var $_chunkLength = 0;
  984 +
  985 + /**
  986 + * Attached listeners
  987 + * @var array
  988 + */
  989 + var $_listeners = array();
  990 +
  991 + /**
  992 + * Constructor
  993 + *
  994 + * @param object Net_Socket socket to read the response from
  995 + * @param array listeners attached to request
  996 + * @return mixed PEAR Error on error, true otherwise
  997 + */
  998 + function HTTP_Response(&$sock, &$listeners)
  999 + {
  1000 + $this->_sock =& $sock;
  1001 + $this->_listeners =& $listeners;
  1002 + }
  1003 +
  1004 +
  1005 + /**
  1006 + * Processes a HTTP response
  1007 + *
  1008 + * This extracts response code, headers, cookies and decodes body if it
  1009 + * was encoded in some way
  1010 + *
  1011 + * @access public
  1012 + * @param bool Whether to store response body in object property, set
  1013 + * this to false if downloading a LARGE file and using a Listener.
  1014 + * This is assumed to be true if body is gzip-encoded.
  1015 + * @throws PEAR_Error
  1016 + * @return mixed true on success, PEAR_Error in case of malformed response
  1017 + */
  1018 + function process($saveBody = true)
  1019 + {
  1020 + do {
  1021 + $line = $this->_sock->readLine();
  1022 + if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
  1023 + return PEAR::raiseError('Malformed response.');
  1024 + } else {
  1025 + $this->_protocol = 'HTTP/' . $http_version;
  1026 + $this->_code = intval($returncode);
  1027 + }
  1028 + while ('' !== ($header = $this->_sock->readLine())) {
  1029 + $this->_processHeader($header);
  1030 + }
  1031 + } while (100 == $this->_code);
  1032 +
  1033 + $this->_notify('gotHeaders', $this->_headers);
  1034 +
  1035 + // If response body is present, read it and decode
  1036 + $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
  1037 + $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
  1038 + $hasBody = false;
  1039 + if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) {
  1040 + while (!$this->_sock->eof()) {
  1041 + if ($chunked) {
  1042 + $data = $this->_readChunked();
  1043 + } else {
  1044 + $data = $this->_sock->read(4096);
  1045 + }
  1046 + if ('' == $data) {
  1047 + break;
  1048 + } else {
  1049 + $hasBody = true;
  1050 + if ($saveBody || $gzipped) {
  1051 + $this->_body .= $data;
  1052 + }
  1053 + $this->_notify($gzipped? 'gzTick': 'tick', $data);
  1054 + }
  1055 + }
  1056 + }
  1057 + if ($hasBody) {
  1058 + // Uncompress the body if needed
  1059 + if ($gzipped) {
  1060 + $this->_body = gzinflate(substr($this->_body, 10));
  1061 + $this->_notify('gotBody', $this->_body);
  1062 + } else {
  1063 + $this->_notify('gotBody');
  1064 + }
  1065 + }
  1066 + return true;
  1067 + }
  1068 +
  1069 +
  1070 + /**
  1071 + * Processes the response header
  1072 + *
  1073 + * @access private
  1074 + * @param string HTTP header
  1075 + */
  1076 + function _processHeader($header)
  1077 + {
  1078 + list($headername, $headervalue) = explode(':', $header, 2);
  1079 + $headername = strtolower($headername);
  1080 + $headervalue = ltrim($headervalue);
  1081 +
  1082 + if ('set-cookie' != $headername) {
  1083 + if (isset($this->_headers[$headername])) {
  1084 + $this->_headers[$headername] .= ',' . $headervalue;
  1085 + } else {
  1086 + $this->_headers[$headername] = $headervalue;
  1087 + }
  1088 + } else {
  1089 + $this->_parseCookie($headervalue);
  1090 + }
  1091 + }
  1092 +
  1093 +
  1094 + /**
  1095 + * Parse a Set-Cookie header to fill $_cookies array
  1096 + *
  1097 + * @access private
  1098 + * @param string value of Set-Cookie header
  1099 + */
  1100 + function _parseCookie($headervalue)
  1101 + {
  1102 + $cookie = array(
  1103 + 'expires' => null,
  1104 + 'domain' => null,
  1105 + 'path' => null,
  1106 + 'secure' => false
  1107 + );
  1108 +
  1109 + // Only a name=value pair
  1110 + if (!strpos($headervalue, ';')) {
  1111 + $pos = strpos($headervalue, '=');
  1112 + $cookie['name'] = trim(substr($headervalue, 0, $pos));
  1113 + $cookie['value'] = trim(substr($headervalue, $pos + 1));
  1114 +
  1115 + // Some optional parameters are supplied
  1116 + } else {
  1117 + $elements = explode(';', $headervalue);
  1118 + $pos = strpos($elements[0], '=');
  1119 + $cookie['name'] = trim(substr($elements[0], 0, $pos));
  1120 + $cookie['value'] = trim(substr($elements[0], $pos + 1));
  1121 +
  1122 + for ($i = 1; $i < count($elements); $i++) {
  1123 + if (false === strpos($elements[$i], '=')) {
  1124 + $elName = trim($elements[$i]);
  1125 + $elValue = null;
  1126 + } else {
  1127 + list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
  1128 + }
  1129 + $elName = strtolower($elName);
  1130 + if ('secure' == $elName) {
  1131 + $cookie['secure'] = true;
  1132 + } elseif ('expires' == $elName) {
  1133 + $cookie['expires'] = str_replace('"', '', $elValue);
  1134 + } elseif ('path' == $elName || 'domain' == $elName) {
  1135 + $cookie[$elName] = urldecode($elValue);
  1136 + } else {
  1137 + $cookie[$elName] = $elValue;
  1138 + }
  1139 + }
  1140 + }
  1141 + $this->_cookies[] = $cookie;
  1142 + }
  1143 +
  1144 +
  1145 + /**
  1146 + * Read a part of response body encoded with chunked Transfer-Encoding
  1147 + *
  1148 + * @access private
  1149 + * @return string
  1150 + */
  1151 + function _readChunked()
  1152 + {
  1153 + // at start of the next chunk?
  1154 + if (0 == $this->_chunkLength) {
  1155 + $line = $this->_sock->readLine();
  1156 + if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
  1157 + $this->_chunkLength = hexdec($matches[1]);
  1158 + // Chunk with zero length indicates the end
  1159 + if (0 == $this->_chunkLength) {
  1160 + $this->_sock->readLine(); // make this an eof()
  1161 + return '';
  1162 + }
  1163 + } else {
  1164 + return '';
  1165 + }
  1166 + }
  1167 + $data = $this->_sock->read($this->_chunkLength);
  1168 + $this->_chunkLength -= strlen($data);
  1169 + if (0 == $this->_chunkLength) {
  1170 + $this->_sock->readLine(); // Trailing CRLF
  1171 + }
  1172 + return $data;
  1173 + }
  1174 +
  1175 +
  1176 + /**
  1177 + * Notifies all registered listeners of an event.
  1178 + *
  1179 + * @param string Event name
  1180 + * @param mixed Additional data
  1181 + * @access private
  1182 + * @see HTTP_Request::_notify()
  1183 + */
  1184 + function _notify($event, $data = null)
  1185 + {
  1186 + foreach (array_keys($this->_listeners) as $id) {
  1187 + $this->_listeners[$id]->update($this, $event, $data);
  1188 + }
  1189 + }
  1190 +} // End class HTTP_Response
  1191 +?>
... ...
thirdparty/pear/HTTP/Request/Listener.php 0 → 100644
  1 +<?php
  2 +// +-----------------------------------------------------------------------+
  3 +// | Copyright (c) 2002-2003, Richard Heyes |
  4 +// | All rights reserved. |
  5 +// | |
  6 +// | Redistribution and use in source and binary forms, with or without |
  7 +// | modification, are permitted provided that the following conditions |
  8 +// | are met: |
  9 +// | |
  10 +// | o Redistributions of source code must retain the above copyright |
  11 +// | notice, this list of conditions and the following disclaimer. |
  12 +// | o Redistributions in binary form must reproduce the above copyright |
  13 +// | notice, this list of conditions and the following disclaimer in the |
  14 +// | documentation and/or other materials provided with the distribution.|
  15 +// | o The names of the authors may not be used to endorse or promote |
  16 +// | products derived from this software without specific prior written |
  17 +// | permission. |
  18 +// | |
  19 +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  20 +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  21 +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22 +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
  23 +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24 +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
  25 +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26 +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27 +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
  28 +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29 +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
  30 +// | |
  31 +// +-----------------------------------------------------------------------+
  32 +// | Author: Alexey Borzov <avb@php.net> |
  33 +// +-----------------------------------------------------------------------+
  34 +//
  35 +// $Id: Listener.php,v 1.2 2003/10/26 10:28:29 avb Exp $
  36 +//
  37 +
  38 +/**
  39 + * This class implements the Observer part of a Subject-Observer
  40 + * design pattern. It listens to the events sent by a
  41 + * HTTP_Request or HTTP_Response instance.
  42 + *
  43 + * @package HTTP_Request
  44 + * @author Alexey Borzov <avb@php.net>
  45 + * @version $Revision: 1.2 $
  46 + */
  47 +class HTTP_Request_Listener
  48 +{
  49 + /**
  50 + * A listener's identifier
  51 + * @var string
  52 + */
  53 + var $_id;
  54 +
  55 + /**
  56 + * Constructor, sets the object's identifier
  57 + *
  58 + * @access public
  59 + */
  60 + function HTTP_Request_Listener()
  61 + {
  62 + $this->_id = md5(uniqid('http_request_', 1));
  63 + }
  64 +
  65 +
  66 + /**
  67 + * Returns the listener's identifier
  68 + *
  69 + * @access public
  70 + * @return string
  71 + */
  72 + function getId()
  73 + {
  74 + return $this->_id;
  75 + }
  76 +
  77 +
  78 + /**
  79 + * This method is called when Listener is notified of an event
  80 + *
  81 + * @access public
  82 + * @param object an object the listener is attached to
  83 + * @param string Event name
  84 + * @param mixed Additional data
  85 + * @abstract
  86 + */
  87 + function update(&$subject, $event, $data = null)
  88 + {
  89 + echo "Notified of event: '$event'\n";
  90 + if (null !== $data) {
  91 + echo "Additional data: ";
  92 + var_dump($data);
  93 + }
  94 + }
  95 +}
  96 +?>
... ...
thirdparty/pear/HTTP/Upload.php 0 → 100644
  1 +<?php
  2 +// **********************************************
  3 +//
  4 +// This software is licensed by the LGPL
  5 +// -> http://www.gnu.org/copyleft/lesser.txt
  6 +// (c) 2001-2004 by Tomas Von Veschler Cox
  7 +//
  8 +// **********************************************
  9 +//
  10 +// $Id: Upload.php,v 1.42 2004/08/08 09:37:50 wenz Exp $
  11 +
  12 +/*
  13 + * Pear File Uploader class. Easy and secure managment of files
  14 + * submitted via HTML Forms.
  15 + *
  16 + * Leyend:
  17 + * - you can add error msgs in your language in the HTTP_Upload_Error class
  18 + *
  19 + * TODO:
  20 + * - try to think a way of having all the Error system in other
  21 + * file and only include it when an error ocurrs
  22 + *
  23 + * -- Notes for users HTTP_Upload >= 0.9.0 --
  24 + *
  25 + * Error detection was enhanced, so you no longer need to
  26 + * check for PEAR::isError() in $upload->getFiles() or call
  27 + * $upload->isMissing(). Instead you'll
  28 + * get the error when do a check for $file->isError().
  29 + *
  30 + * Example:
  31 + *
  32 + * $upload = new HTTP_Upload('en');
  33 + * $file = $upload->getFiles('i_dont_exist_in_form_definition');
  34 + * if ($file->isError()) {
  35 + * die($file->getMessage());
  36 + * }
  37 + *
  38 + * --
  39 + *
  40 + */
  41 +
  42 +require_once 'PEAR.php';
  43 +
  44 +/**
  45 + * defines default chmod
  46 + */
  47 +define('HTTP_UPLOAD_DEFAULT_CHMOD', 0660);
  48 +
  49 +/**
  50 + * Error Class for HTTP_Upload
  51 + *
  52 + * @author Tomas V.V.Cox <cox@idecnet.com>
  53 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  54 + * @package HTTP_Upload
  55 + * @category HTTP
  56 + * @access public
  57 + */
  58 +class HTTP_Upload_Error extends PEAR
  59 +{
  60 + /**
  61 + * Selected language for error messages
  62 + * @var string
  63 + */
  64 + var $lang = 'en';
  65 +
  66 + /**
  67 + * Whether HTML entities shall be encoded automatically
  68 + * @var boolean
  69 + */
  70 + var $html = false;
  71 +
  72 + /**
  73 + * Constructor
  74 + *
  75 + * Creates a new PEAR_Error
  76 + *
  77 + * @param string $lang The language selected for error code messages
  78 + * @access public
  79 + */
  80 + function HTTP_Upload_Error($lang = null, $html = false)
  81 + {
  82 + $this->lang = ($lang !== null) ? $lang : $this->lang;
  83 + $this->html = ($html !== false) ? $html : $this->html;
  84 + $ini_size = preg_replace('/m/i', '000000', ini_get('upload_max_filesize'));
  85 +
  86 + if (function_exists('version_compare') &&
  87 + version_compare(phpversion(), '4.1', 'ge')) {
  88 + $maxsize = (isset($_POST['MAX_FILE_SIZE'])) ?
  89 + $_POST['MAX_FILE_SIZE'] : null;
  90 + } else {
  91 + global $HTTP_POST_VARS;
  92 + $maxsize = (isset($HTTP_POST_VARS['MAX_FILE_SIZE'])) ?
  93 + $HTTP_POST_VARS['MAX_FILE_SIZE'] : null;
  94 + }
  95 +
  96 + if (empty($maxsize) || ($maxsize > $ini_size)) {
  97 + $maxsize = $ini_size;
  98 + }
  99 + // XXXXX Add here error messages in your language
  100 + $this->error_codes = array(
  101 + 'TOO_LARGE' => array(
  102 + 'es' => "Fichero demasiado largo. El maximo permitido es: $maxsize bytes.",
  103 + 'en' => "File size too large. The maximum permitted size is: $maxsize bytes.",
  104 + 'de' => "Datei zu gro&szlig;. Die zul&auml;ssige Maximalgr&ouml;&szlig;e ist: $maxsize Bytes.",
  105 + 'nl' => "Het bestand is te groot, de maximale grootte is: $maxsize bytes.",
  106 + 'fr' => "Le fichier est trop gros. La taille maximum autoris&eacute;e est: $maxsize bytes.",
  107 + 'it' => "Il file &eacute; troppo grande. Il massimo permesso &eacute: $maxsize bytes.",
  108 + 'pt_BR' => "Arquivo muito grande. O tamanho m&aacute;ximo permitido &eacute; $maxsize bytes."
  109 + ),
  110 + 'MISSING_DIR' => array(
  111 + 'es' => 'Falta directorio destino.',
  112 + 'en' => 'Missing destination directory.',
  113 + 'de' => 'Kein Zielverzeichnis definiert.',
  114 + 'nl' => 'Geen bestemmings directory.',
  115 + 'fr' => 'Le r&eacute;pertoire de destination n\'est pas d&eacute;fini.',
  116 + 'it' => 'Manca la directory di destinazione.',
  117 + 'pt_BR' => 'Aus&ecirc;ncia de diret&oacute;rio de destino.'
  118 + ),
  119 + 'IS_NOT_DIR' => array(
  120 + 'es' => 'El directorio destino no existe o es un fichero regular.',
  121 + 'en' => 'The destination directory doesn\'t exist or is a regular file.',
  122 + 'de' => 'Das angebene Zielverzeichnis existiert nicht oder ist eine Datei.',
  123 + 'nl' => 'De doeldirectory bestaat niet, of is een gewoon bestand.',
  124 + 'fr' => 'Le r&eacute;pertoire de destination n\'existe pas ou il s\'agit d\'un fichier r&eacute;gulier.',
  125 + 'it' => 'La directory di destinazione non esiste o &eacute; un file.',
  126 + 'pt_BR' => 'O diret&oacute;rio de destino n&atilde;o existe ou &eacute; um arquivo.'
  127 + ),
  128 + 'NO_WRITE_PERMS' => array(
  129 + 'es' => 'El directorio destino no tiene permisos de escritura.',
  130 + 'en' => 'The destination directory doesn\'t have write perms.',
  131 + 'de' => 'Fehlende Schreibrechte f&uuml;r das Zielverzeichnis.',
  132 + 'nl' => 'Geen toestemming om te schrijven in de doeldirectory.',
  133 + 'fr' => 'Le r&eacute;pertoire de destination n\'a pas les droits en &eacute;criture.',
  134 + 'it' => 'Non si hanno i permessi di scrittura sulla directory di destinazione.',
  135 + 'pt_BR' => 'O diret&oacute;rio de destino n&atilde;o possui permiss&atilde;o para escrita.'
  136 + ),
  137 + 'NO_USER_FILE' => array(
  138 + 'es' => 'No se ha escogido fichero para el upload.',
  139 + 'en' => 'You haven\'t selected any file for uploading.',
  140 + 'de' => 'Es wurde keine Datei f&uuml;r den Upload ausgew&auml;hlt.',
  141 + 'nl' => 'Er is geen bestand opgegeven om te uploaden.',
  142 + 'fr' => 'Vous n\'avez pas s&eacute;lectionn&eacute; de fichier &agrave; envoyer.',
  143 + 'it' => 'Nessun file selezionato per l\'upload.',
  144 + 'pt_BR' => 'Nenhum arquivo selecionado para upload.'
  145 + ),
  146 + 'BAD_FORM' => array(
  147 + 'es' => 'El formulario no contiene method="post" enctype="multipart/form-data" requerido.',
  148 + 'en' => 'The html form doesn\'t contain the required method="post" enctype="multipart/form-data".',
  149 + 'de' => 'Das HTML-Formular enth&auml;lt nicht die Angabe method="post" enctype="multipart/form-data" '.
  150 + 'im &gt;form&lt;-Tag.',
  151 + 'nl' => 'Het HTML-formulier bevat niet de volgende benodigde '.
  152 + 'eigenschappen: method="post" enctype="multipart/form-data".',
  153 + 'fr' => 'Le formulaire HTML ne contient pas les attributs requis : '.
  154 + ' method="post" enctype="multipart/form-data".',
  155 + 'it' => 'Il modulo HTML non contiene gli attributi richiesti: "'.
  156 + ' method="post" enctype="multipart/form-data".',
  157 + 'pt_BR' => 'O formul&aacute;rio HTML n&atilde;o possui o method="post" enctype="multipart/form-data" requerido.'
  158 + ),
  159 + 'E_FAIL_COPY' => array(
  160 + 'es' => 'Fallo al copiar el fichero temporal.',
  161 + 'en' => 'Failed to copy the temporary file.',
  162 + 'de' => 'Tempor&auml;re Datei konnte nicht kopiert werden.',
  163 + 'nl' => 'Het tijdelijke bestand kon niet gekopieerd worden.',
  164 + 'fr' => 'L\'enregistrement du fichier temporaire a &eacute;chou&eacute;.',
  165 + 'it' => 'Copia del file temporaneo fallita.',
  166 + 'pt_BR' => 'Falha ao copiar o arquivo tempor&aacute;rio.'
  167 + ),
  168 + 'E_FAIL_MOVE' => array(
  169 + 'es' => 'No puedo mover el fichero.',
  170 + 'en' => 'Impossible to move the file.',
  171 + 'de' => 'Datei kann nicht verschoben werden.',
  172 + 'nl' => 'Het bestand kon niet verplaatst worden.',
  173 + 'fr' => 'Impossible de d&eacute;placer le fichier.',
  174 + 'pt_BR' => 'N&atilde;o foi poss&iacute;vel mover o arquivo.'
  175 + ),
  176 + 'FILE_EXISTS' => array(
  177 + 'es' => 'El fichero destino ya existe.',
  178 + 'en' => 'The destination file already exists.',
  179 + 'de' => 'Die zu erzeugende Datei existiert bereits.',
  180 + 'nl' => 'Het doelbestand bestaat al.',
  181 + 'fr' => 'Le fichier de destination existe d&eacute;j&agrave;.',
  182 + 'it' => 'File destinazione gi&agrave; esistente.',
  183 + 'pt_BR' => 'O arquivo de destino j&aacute; existe.'
  184 + ),
  185 + 'CANNOT_OVERWRITE' => array(
  186 + 'es' => 'El fichero destino ya existe y no se puede sobreescribir.',
  187 + 'en' => 'The destination file already exists and could not be overwritten.',
  188 + 'de' => 'Die zu erzeugende Datei existiert bereits und konnte nicht &uuml;berschrieben werden.',
  189 + 'nl' => 'Het doelbestand bestaat al, en kon niet worden overschreven.',
  190 + 'fr' => 'Le fichier de destination existe d&eacute;j&agrave; et ne peux pas &ecirc;tre remplac&eacute;.',
  191 + 'it' => 'File destinazione gi&agrave; esistente e non si pu&ograve; sovrascrivere.',
  192 + 'pt_BR' => 'O arquivo de destino j&aacute; existe e n&atilde;o p&ocirc;de ser sobrescrito.'
  193 + ),
  194 + 'NOT_ALLOWED_EXTENSION' => array(
  195 + 'es' => 'Extension de fichero no permitida.',
  196 + 'en' => 'File extension not permitted.',
  197 + 'de' => 'Unerlaubte Dateiendung.',
  198 + 'nl' => 'Niet toegestane bestands-extensie.',
  199 + 'fr' => 'Le fichier a une extension non autoris&eacute;e.',
  200 + 'it' => 'Estensione del File non permessa.',
  201 + 'pt_BR' => 'Extens&atilde;o de arquivo n&atilde;o permitida.'
  202 + ),
  203 + 'PARTIAL' => array(
  204 + 'es' => 'El fichero fue parcialmente subido',
  205 + 'en' => 'The file was only partially uploaded.',
  206 + 'de' => 'Die Datei wurde unvollst&auml;ndig &uuml;bertragen.',
  207 + 'nl' => 'Het bestand is slechts gedeeltelijk geupload.',
  208 + 'pt_BR' => 'O arquivo não foi enviado por completo.'
  209 + ),
  210 + 'ERROR' => array(
  211 + 'es' => 'Error en subida:',
  212 + 'en' => 'Upload error:',
  213 + 'de' => 'Fehler beim Upload:',
  214 + 'nl' => 'Upload fout:',
  215 + 'pt_BR' => 'Erro de upload:'
  216 + ),
  217 + 'DEV_NO_DEF_FILE' => array(
  218 + 'es' => 'No está definido en el formulario este nombre de fichero como &lt;input type="file" name=?&gt;.',
  219 + 'en' => 'This filename is not defined in the form as &lt;input type="file" name=?&gt;.',
  220 + 'de' => 'Dieser Dateiname ist im Formular nicht als &lt;input type="file" name=?&gt; definiert.',
  221 + 'nl' => 'Deze bestandsnaam is niett gedefineerd in het formulier als &lt;input type="file" name=?&gt;.'
  222 + )
  223 + );
  224 + }
  225 +
  226 + /**
  227 + * returns the error code
  228 + *
  229 + * @param string $e_code type of error
  230 + * @return string Error message
  231 + */
  232 + function errorCode($e_code)
  233 + {
  234 + if (!empty($this->error_codes[$e_code][$this->lang])) {
  235 + $msg = $this->html ?
  236 + html_entity_decode($this->error_codes[$e_code][$this->lang]) :
  237 + $this->error_codes[$e_code][$this->lang];
  238 + } else {
  239 + $msg = $e_code;
  240 + }
  241 +
  242 + if (!empty($this->error_codes['ERROR'][$this->lang])) {
  243 + $error = $this->error_codes['ERROR'][$this->lang];
  244 + } else {
  245 + $error = $this->error_codes['ERROR']['en'];
  246 + }
  247 + return $error.' '.$msg;
  248 + }
  249 +
  250 + /**
  251 + * Overwrites the PEAR::raiseError method
  252 + *
  253 + * @param string $e_code type of error
  254 + * @return object PEAR_Error a PEAR-Error object
  255 + * @access public
  256 + */
  257 + function raiseError($e_code)
  258 + {
  259 + return PEAR::raiseError($this->errorCode($e_code), $e_code);
  260 + }
  261 +}
  262 +
  263 +/**
  264 + * This class provides an advanced file uploader system
  265 + * for file uploads made from html forms
  266 +
  267 + *
  268 + * @author Tomas V.V.Cox <cox@idecnet.com>
  269 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  270 + * @package HTTP_Upload
  271 + * @category HTTP
  272 + * @access public
  273 + */
  274 +class HTTP_Upload extends HTTP_Upload_Error
  275 +{
  276 + /**
  277 + * Contains an array of "uploaded files" objects
  278 + * @var array
  279 + */
  280 + var $files = array();
  281 +
  282 + /**
  283 + * Contains the desired chmod for uploaded files
  284 + * @var int
  285 + * @access private
  286 + */
  287 + var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
  288 +
  289 + /**
  290 + * Constructor
  291 + *
  292 + * @param string $lang Language to use for reporting errors
  293 + * @see Upload_Error::error_codes
  294 + * @access public
  295 + */
  296 + function HTTP_Upload($lang = null)
  297 + {
  298 + $this->HTTP_Upload_Error($lang);
  299 + if (function_exists('version_compare') &&
  300 + version_compare(phpversion(), '4.1', 'ge'))
  301 + {
  302 + $this->post_files = $_FILES;
  303 + if (isset($_SERVER['CONTENT_TYPE'])) {
  304 + $this->content_type = $_SERVER['CONTENT_TYPE'];
  305 + }
  306 + } else {
  307 + global $HTTP_POST_FILES, $HTTP_SERVER_VARS;
  308 + $this->post_files = $HTTP_POST_FILES;
  309 + if (isset($HTTP_SERVER_VARS['CONTENT_TYPE'])) {
  310 + $this->content_type = $HTTP_SERVER_VARS['CONTENT_TYPE'];
  311 + }
  312 + }
  313 + }
  314 +
  315 + /**
  316 + * Get files
  317 + *
  318 + * @param mixed $file If:
  319 + * - not given, function will return array of upload_file objects
  320 + * - is int, will return the $file position in upload_file objects array
  321 + * - is string, will return the upload_file object corresponding
  322 + * to $file name of the form. For ex:
  323 + * if form is <input type="file" name="userfile">
  324 + * to get this file use: $upload->getFiles('userfile')
  325 + *
  326 + * @return mixed array or object (see @param $file above) or Pear_Error
  327 + * @access public
  328 + */
  329 + function &getFiles($file = null)
  330 + {
  331 + static $is_built = false;
  332 + //build only once for multiple calls
  333 + if (!$is_built) {
  334 + $files = &$this->_buildFiles();
  335 + if (PEAR::isError($files)) {
  336 + // there was an error with the form.
  337 + // Create a faked upload embedding the error
  338 + $this->files['_error'] = &new HTTP_Upload_File(
  339 + '_error', null,
  340 + null, null,
  341 + null, $files->getCode(),
  342 + $this->lang, $this->_chmod);
  343 + } else {
  344 + $this->files = $files;
  345 + }
  346 + $is_built = true;
  347 + }
  348 + if ($file !== null) {
  349 + if (is_int($file)) {
  350 + $pos = 0;
  351 + foreach ($this->files as $obj) {
  352 + if ($pos == $file) {
  353 + return $obj;
  354 + }
  355 + $pos++;
  356 + }
  357 + } elseif (is_string($file) && isset($this->files[$file])) {
  358 + return $this->files[$file];
  359 + }
  360 + if (isset($this->files['_error'])) {
  361 + return $this->files['_error'];
  362 + } else {
  363 + // developer didn't specify this name in the form
  364 + // warn him about it with a faked upload
  365 + return new HTTP_Upload_File(
  366 + '_error', null,
  367 + null, null,
  368 + null, 'DEV_NO_DEF_FILE',
  369 + $this->lang);
  370 + }
  371 + }
  372 + return $this->files;
  373 + }
  374 +
  375 + /**
  376 + * Creates the list of the uploaded file
  377 + *
  378 + * @return array of HTTP_Upload_File objects for every file
  379 + */
  380 + function &_buildFiles()
  381 + {
  382 + // Form method check
  383 + if (!isset($this->content_type) ||
  384 + strpos($this->content_type, 'multipart/form-data') !== 0)
  385 + {
  386 + return $this->raiseError('BAD_FORM');
  387 + }
  388 + // In 4.1 $_FILES isn't initialized when no uploads
  389 + // XXX (cox) afaik, in >= 4.1 and <= 4.3 only
  390 + if (function_exists('version_compare') &&
  391 + version_compare(phpversion(), '4.1', 'ge'))
  392 + {
  393 + $error = $this->isMissing();
  394 + if (PEAR::isError($error)) {
  395 + return $error;
  396 + }
  397 + }
  398 +
  399 + // map error codes from 4.2.0 $_FILES['userfile']['error']
  400 + if (function_exists('version_compare') &&
  401 + version_compare(phpversion(), '4.2.0', 'ge')) {
  402 + $uploadError = array(
  403 + 1 => 'TOO_LARGE',
  404 + 2 => 'TOO_LARGE',
  405 + 3 => 'PARTIAL',
  406 + 4 => 'NO_USER_FILE'
  407 + );
  408 + }
  409 +
  410 +
  411 + // Parse $_FILES (or $HTTP_POST_FILES)
  412 + $files = array();
  413 + foreach ($this->post_files as $userfile => $value) {
  414 + if (is_array($value['name'])) {
  415 + foreach ($value['name'] as $key => $val) {
  416 + $err = $value['error'][$key];
  417 + if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
  418 + $error = $uploadError[$err];
  419 + } else {
  420 + $error = null;
  421 + }
  422 + $name = basename($value['name'][$key]);
  423 + $tmp_name = $value['tmp_name'][$key];
  424 + $size = $value['size'][$key];
  425 + $type = $value['type'][$key];
  426 + $formname = $userfile . "[$key]";
  427 + $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
  428 + $formname, $type, $size, $error, $this->lang, $this->_chmod);
  429 + }
  430 + // One file
  431 + } else {
  432 + $err = $value['error'];
  433 + if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
  434 + $error = $uploadError[$err];
  435 + } else {
  436 + $error = null;
  437 + }
  438 + $name = basename($value['name']);
  439 + $tmp_name = $value['tmp_name'];
  440 + $size = $value['size'];
  441 + $type = $value['type'];
  442 + $formname = $userfile;
  443 + $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
  444 + $formname, $type, $size, $error, $this->lang, $this->_chmod);
  445 + }
  446 + }
  447 + return $files;
  448 + }
  449 +
  450 + /**
  451 + * Checks if the user submited or not some file
  452 + *
  453 + * @return mixed False when are files or PEAR_Error when no files
  454 + * @access public
  455 + * @see Read the note in the source code about this function
  456 + */
  457 + function isMissing()
  458 + {
  459 + if (count($this->post_files) < 1) {
  460 + return $this->raiseError('NO_USER_FILE');
  461 + }
  462 + //we also check if at least one file has more than 0 bytes :)
  463 + $files = array();
  464 + $size = 0;
  465 + foreach ($this->post_files as $userfile => $value) {
  466 + if (is_array($value['name'])) {
  467 + foreach ($value['name'] as $key => $val) {
  468 + $size += $value['size'][$key];
  469 + }
  470 + } else { //one file
  471 + $size = $value['size'];
  472 + }
  473 + }
  474 + if ($size == 0) {
  475 + $this->raiseError('NO_USER_FILE');
  476 + }
  477 + return false;
  478 + }
  479 +
  480 + /**
  481 + * Sets the chmod to be used for uploaded files
  482 + *
  483 + * @param int Desired mode
  484 + */
  485 + function setChmod($mode)
  486 + {
  487 + $this->_chmod = $mode;
  488 + }
  489 +}
  490 +
  491 +/**
  492 + * This class provides functions to work with the uploaded file
  493 + *
  494 + * @author Tomas V.V.Cox <cox@idecnet.com>
  495 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  496 + * @package HTTP_Upload
  497 + * @category HTTP
  498 + * @access public
  499 + */
  500 +class HTTP_Upload_File extends HTTP_Upload_Error
  501 +{
  502 + /**
  503 + * If the random seed was initialized before or not
  504 + * @var boolean;
  505 + */
  506 + var $_seeded = 0;
  507 +
  508 + /**
  509 + * Assoc array with file properties
  510 + * @var array
  511 + */
  512 + var $upload = array();
  513 +
  514 + /**
  515 + * If user haven't selected a mode, by default 'safe' will be used
  516 + * @var boolean
  517 + */
  518 + var $mode_name_selected = false;
  519 +
  520 + /**
  521 + * It's a common security risk in pages who has the upload dir
  522 + * under the document root (remember the hack of the Apache web?)
  523 + *
  524 + * @var array
  525 + * @access private
  526 + * @see HTTP_Upload_File::setValidExtensions()
  527 + */
  528 + var $_extensions_check = array('php', 'phtm', 'phtml', 'php3', 'inc');
  529 +
  530 + /**
  531 + * @see HTTP_Upload_File::setValidExtensions()
  532 + * @var string
  533 + * @access private
  534 + */
  535 + var $_extensions_mode = 'deny';
  536 +
  537 + /**
  538 + * Contains the desired chmod for uploaded files
  539 + * @var int
  540 + * @access private
  541 + */
  542 + var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
  543 +
  544 + /**
  545 + * Constructor
  546 + *
  547 + * @param string $name destination file name
  548 + * @param string $tmp temp file name
  549 + * @param string $formname name of the form
  550 + * @param string $type Mime type of the file
  551 + * @param string $size size of the file
  552 + * @param string $error error on upload
  553 + * @param string $lang used language for errormessages
  554 + * @access public
  555 + */
  556 + function HTTP_Upload_File($name = null, $tmp = null, $formname = null,
  557 + $type = null, $size = null, $error = null,
  558 + $lang = null, $chmod = HTTP_UPLOAD_DEFAULT_CHMOD)
  559 + {
  560 + $this->HTTP_Upload_Error($lang);
  561 + $ext = null;
  562 +
  563 + if (empty($name) || $size == 0) {
  564 + $error = 'NO_USER_FILE';
  565 + } elseif ($tmp == 'none') {
  566 + $error = 'TOO_LARGE';
  567 + } else {
  568 + // strpos needed to detect files without extension
  569 + if (($pos = strrpos($name, '.')) !== false) {
  570 + $ext = substr($name, $pos + 1);
  571 + }
  572 + }
  573 +
  574 + if (function_exists('version_compare') &&
  575 + version_compare(phpversion(), '4.1', 'ge')) {
  576 + if (isset($_POST['MAX_FILE_SIZE']) &&
  577 + $size > $_POST['MAX_FILE_SIZE']) {
  578 + $error = 'TOO_LARGE';
  579 + }
  580 + } else {
  581 + global $HTTP_POST_VARS;
  582 + if (isset($HTTP_POST_VARS['MAX_FILE_SIZE']) &&
  583 + $size > $HTTP_POST_VARS['MAX_FILE_SIZE']) {
  584 + $error = 'TOO_LARGE';
  585 + }
  586 + }
  587 +
  588 + $this->upload = array(
  589 + 'real' => $name,
  590 + 'name' => $name,
  591 + 'form_name' => $formname,
  592 + 'ext' => $ext,
  593 + 'tmp_name' => $tmp,
  594 + 'size' => $size,
  595 + 'type' => $type,
  596 + 'error' => $error
  597 + );
  598 +
  599 + $this->_chmod = $chmod;
  600 + }
  601 +
  602 + /**
  603 + * Sets the name of the destination file
  604 + *
  605 + * @param string $mode A valid mode: 'uniq', 'safe' or 'real' or a file name
  606 + * @param string $prepend A string to prepend to the name
  607 + * @param string $append A string to append to the name
  608 + *
  609 + * @return string The modified name of the destination file
  610 + * @access public
  611 + */
  612 + function setName($mode, $prepend = null, $append = null)
  613 + {
  614 + switch ($mode) {
  615 + case 'uniq':
  616 + $name = $this->nameToUniq();
  617 + $this->upload['ext'] = $this->nameToSafe($this->upload['ext'], 10);
  618 + $name .= '.' . $this->upload['ext'];
  619 + break;
  620 + case 'safe':
  621 + $name = $this->nameToSafe($this->upload['real']);
  622 + if (($pos = strrpos($name, '.')) !== false) {
  623 + $this->upload['ext'] = substr($name, $pos + 1);
  624 + } else {
  625 + $this->upload['ext'] = '';
  626 + }
  627 + break;
  628 + case 'real':
  629 + $name = $this->upload['real'];
  630 + break;
  631 + default:
  632 + $name = $mode;
  633 + }
  634 + $this->upload['name'] = $prepend . $name . $append;
  635 + $this->mode_name_selected = true;
  636 + return $this->upload['name'];
  637 + }
  638 +
  639 + /**
  640 + * Unique file names in the form: 9022210413b75410c28bef.html
  641 + * @see HTTP_Upload_File::setName()
  642 + */
  643 + function nameToUniq()
  644 + {
  645 + if (! $this->_seeded) {
  646 + srand((double) microtime() * 1000000);
  647 + $this->_seeded = 1;
  648 + }
  649 + $uniq = uniqid(rand());
  650 + return $uniq;
  651 + }
  652 +
  653 + /**
  654 + * Format a file name to be safe
  655 + *
  656 + * @param string $file The string file name
  657 + * @param int $maxlen Maximun permited string lenght
  658 + * @return string Formatted file name
  659 + * @see HTTP_Upload_File::setName()
  660 + */
  661 + function nameToSafe($name, $maxlen=250)
  662 + {
  663 + $noalpha = 'ÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÀÈÌÒÙàèìòùÄËÏÖÜäëïöüÿÃãÕõÅåÑñÇç@°ºª';
  664 + $alpha = 'AEIOUYaeiouyAEIOUaeiouAEIOUaeiouAEIOUaeiouyAaOoAaNnCcaooa';
  665 +
  666 + $name = substr($name, 0, $maxlen);
  667 + $name = strtr($name, $noalpha, $alpha);
  668 + // not permitted chars are replaced with "_"
  669 + return preg_replace('/[^a-zA-Z0-9,._\+\()\-]/', '_', $name);
  670 + }
  671 +
  672 + /**
  673 + * The upload was valid
  674 + *
  675 + * @return bool If the file was submitted correctly
  676 + * @access public
  677 + */
  678 + function isValid()
  679 + {
  680 + if ($this->upload['error'] === null) {
  681 + return true;
  682 + }
  683 + return false;
  684 + }
  685 +
  686 + /**
  687 + * User haven't submit a file
  688 + *
  689 + * @return bool If the user submitted a file or not
  690 + * @access public
  691 + */
  692 + function isMissing()
  693 + {
  694 + if ($this->upload['error'] == 'NO_USER_FILE') {
  695 + return true;
  696 + }
  697 + return false;
  698 + }
  699 +
  700 + /**
  701 + * Some error occured during upload (most common due a file size problem,
  702 + * like max size exceeded or 0 bytes long).
  703 + * @return bool If there were errors submitting the file (probably
  704 + * because the file excess the max permitted file size)
  705 + * @access public
  706 + */
  707 + function isError()
  708 + {
  709 + if (in_array($this->upload['error'], array('TOO_LARGE', 'BAD_FORM','DEV_NO_DEF_FILE'))) {
  710 + return true;
  711 + }
  712 + return false;
  713 + }
  714 +
  715 + /**
  716 + * Moves the uploaded file to its destination directory.
  717 + *
  718 + * @param string $dir_dest Destination directory
  719 + * @param bool $overwrite Overwrite if destination file exists?
  720 + * @return mixed True on success or Pear_Error object on error
  721 + * @access public
  722 + */
  723 + function moveTo($dir_dest, $overwrite = true)
  724 + {
  725 + if (!$this->isValid()) {
  726 + return $this->raiseError($this->upload['error']);
  727 + }
  728 +
  729 + //Valid extensions check
  730 + if (!$this->_evalValidExtensions()) {
  731 + return $this->raiseError('NOT_ALLOWED_EXTENSION');
  732 + }
  733 +
  734 + $err_code = $this->_chk_dir_dest($dir_dest);
  735 + if ($err_code !== false) {
  736 + return $this->raiseError($err_code);
  737 + }
  738 + // Use 'safe' mode by default if no other was selected
  739 + if (!$this->mode_name_selected) {
  740 + $this->setName('safe');
  741 + }
  742 +
  743 + $name_dest = $dir_dest . DIRECTORY_SEPARATOR . $this->upload['name'];
  744 +
  745 + if (@is_file($name_dest)) {
  746 + if ($overwrite !== true) {
  747 + return $this->raiseError('FILE_EXISTS');
  748 + } elseif (!is_writable($name_dest)) {
  749 + return $this->raiseError('CANNOT_OVERWRITE');
  750 + }
  751 + }
  752 +
  753 + // copy the file and let php clean the tmp
  754 + if (!@move_uploaded_file($this->upload['tmp_name'], $name_dest)) {
  755 + return $this->raiseError('E_FAIL_MOVE');
  756 + }
  757 + @chmod($name_dest, $this->_chmod);
  758 + return $this->getProp('name');
  759 + }
  760 +
  761 + /**
  762 + * Check for a valid destination dir
  763 + *
  764 + * @param string $dir_dest Destination dir
  765 + * @return mixed False on no errors or error code on error
  766 + */
  767 + function _chk_dir_dest($dir_dest)
  768 + {
  769 + if (!$dir_dest) {
  770 + return 'MISSING_DIR';
  771 + }
  772 + if (!@is_dir ($dir_dest)) {
  773 + return 'IS_NOT_DIR';
  774 + }
  775 + if (!is_writeable ($dir_dest)) {
  776 + return 'NO_WRITE_PERMS';
  777 + }
  778 + return false;
  779 + }
  780 + /**
  781 + * Retrive properties of the uploaded file
  782 + * @param string $name The property name. When null an assoc array with
  783 + * all the properties will be returned
  784 + * @return mixed A string or array
  785 + * @see HTTP_Upload_File::HTTP_Upload_File()
  786 + * @access public
  787 + */
  788 + function getProp($name = null)
  789 + {
  790 + if ($name === null) {
  791 + return $this->upload;
  792 + }
  793 + return $this->upload[$name];
  794 + }
  795 +
  796 + /**
  797 + * Returns a error message, if a error occured
  798 + * (deprecated) Use getMessage() instead
  799 + * @return string a Error message
  800 + * @access public
  801 + */
  802 + function errorMsg()
  803 + {
  804 + return $this->errorCode($this->upload['error']);
  805 + }
  806 +
  807 + /**
  808 + * Returns a error message, if a error occured
  809 + * @return string a Error message
  810 + * @access public
  811 + */
  812 + function getMessage()
  813 + {
  814 + return $this->errorCode($this->upload['error']);
  815 + }
  816 +
  817 + /**
  818 + * Function to restrict the valid extensions on file uploads
  819 + *
  820 + * @param array $exts File extensions to validate
  821 + * @param string $mode The type of validation:
  822 + * 1) 'deny' Will deny only the supplied extensions
  823 + * 2) 'accept' Will accept only the supplied extensions
  824 + * as valid
  825 + * @access public
  826 + */
  827 + function setValidExtensions($exts, $mode = 'deny')
  828 + {
  829 + $this->_extensions_check = $exts;
  830 + $this->_extensions_mode = $mode;
  831 + }
  832 +
  833 + /**
  834 + * Evaluates the validity of the extensions set by setValidExtensions
  835 + *
  836 + * @return bool False on non valid extension, true if they are valid
  837 + * @access private
  838 + */
  839 + function _evalValidExtensions()
  840 + {
  841 + $exts = $this->_extensions_check;
  842 + settype($exts, 'array');
  843 + if ($this->_extensions_mode == 'deny') {
  844 + if (in_array($this->getProp('ext'), $exts)) {
  845 + return false;
  846 + }
  847 + // mode == 'accept'
  848 + } else {
  849 + if (!in_array($this->getProp('ext'), $exts)) {
  850 + return false;
  851 + }
  852 + }
  853 + return true;
  854 + }
  855 +}
  856 +?>
0 857 \ No newline at end of file
... ...