home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / SOAP / Transport / HTTP.php next >
Encoding:
PHP Script  |  2008-07-02  |  20.1 KB  |  621 lines

  1. <?php
  2. /**
  3.  * This file contains the code for a HTTP transport layer.
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 2.02 of the PHP license,
  8.  * that is bundled with this package in the file LICENSE, and is available at
  9.  * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
  10.  * did not receive a copy of the PHP license and are unable to obtain it
  11.  * through the world-wide-web, please send a note to license@php.net so we can
  12.  * mail you a copy immediately.
  13.  *
  14.  * @category   Web Services
  15.  * @package    SOAP
  16.  * @author     Shane Caraveo <Shane@Caraveo.com>
  17.  * @author     Jan Schneider <jan@horde.org>
  18.  * @copyright  2003-2006 The PHP Group
  19.  * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
  20.  * @link       http://pear.php.net/package/SOAP
  21.  */
  22.  
  23. /**
  24.  * HTTP Transport class
  25.  *
  26.  * @package  SOAP
  27.  * @category Web_Services
  28.  */
  29.  
  30. /**
  31.  * Needed Classes
  32.  */
  33. require_once 'SOAP/Transport.php';
  34.  
  35. /**
  36.  *  HTTP Transport for SOAP
  37.  *
  38.  * @access public
  39.  * @package SOAP
  40.  * @author Shane Caraveo <shane@php.net>
  41.  * @author Jan Schneider <jan@horde.org>
  42.  */
  43. class SOAP_Transport_HTTP extends SOAP_Transport
  44. {
  45.     /**
  46.      * Basic Auth string.
  47.      *
  48.      * @var array
  49.      */
  50.     var $headers = array();
  51.  
  52.     /**
  53.      * Cookies.
  54.      *
  55.      * @var array
  56.      */
  57.     var $cookies;
  58.  
  59.     /**
  60.      * Connection timeout in seconds. 0 = none.
  61.      *
  62.      * @var integer
  63.      */
  64.     var $timeout = 4;
  65.  
  66.     /**
  67.      * HTTP-Response Content-Type.
  68.      */
  69.     var $result_content_type;
  70.  
  71.     var $result_headers = array();
  72.  
  73.     var $result_cookies = array();
  74.  
  75.     /**
  76.      * SOAP_Transport_HTTP Constructor
  77.      *
  78.      * @access public
  79.      *
  80.      * @param string $url       HTTP url to SOAP endpoint.
  81.      * @param string $encoding  Encoding to use.
  82.      */
  83.     function SOAP_Transport_HTTP($url, $encoding = SOAP_DEFAULT_ENCODING)
  84.     {
  85.         parent::SOAP_Base('HTTP');
  86.         $this->urlparts = @parse_url($url);
  87.         $this->url = $url;
  88.         $this->encoding = $encoding;
  89.     }
  90.  
  91.     /**
  92.      * Sends and receives SOAP data.
  93.      *
  94.      * @access public
  95.      *
  96.      * @param string  Outgoing SOAP data.
  97.      * @param array   Options.
  98.      *
  99.      * @return string|SOAP_Fault
  100.      */
  101.     function send($msg, $options = array())
  102.     {
  103.         $this->fault = null;
  104.  
  105.         if (!$this->_validateUrl()) {
  106.             return $this->fault;
  107.         }
  108.  
  109.         if (isset($options['timeout'])) {
  110.             $this->timeout = (int)$options['timeout'];
  111.         }
  112.  
  113.         if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
  114.             return $this->_sendHTTP($msg, $options);
  115.         } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
  116.             return $this->_sendHTTPS($msg, $options);
  117.         }
  118.  
  119.         return $this->_raiseSoapFault('Invalid url scheme ' . $this->url);
  120.     }
  121.  
  122.     /**
  123.      * Sets data for HTTP authentication, creates authorization header.
  124.      *
  125.      * @param string $username   Username.
  126.      * @param string $password   Response data, minus HTTP headers.
  127.      *
  128.      * @access public
  129.      */
  130.     function setCredentials($username, $password)
  131.     {
  132.         $this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
  133.     }
  134.  
  135.     /**
  136.      * Adds a cookie.
  137.      *
  138.      * @access public
  139.      * @param string $name  Cookie name.
  140.      * @param mixed $value  Cookie value.
  141.      */
  142.     function addCookie($name, $value)
  143.     {
  144.         $this->cookies[$name] = $value;
  145.     }
  146.  
  147.     /**
  148.      * Generates the correct headers for the cookies.
  149.      *
  150.      * @access private
  151.      *
  152.      * @param array $options  Cookie options. If 'nocookies' is set and true
  153.      *                        the cookies from the last response are added
  154.      *                        automatically. 'cookies' is name-value-hash with
  155.      *                        a list of cookies to add.
  156.      *
  157.      * @return string  The cookie header value.
  158.      */
  159.     function _generateCookieHeader($options)
  160.     {
  161.         $this->cookies = array();
  162.  
  163.         if (empty($options['nocookies']) &&
  164.             isset($this->result_cookies)) {
  165.             // Add the cookies we got from the last request.
  166.             foreach ($this->result_cookies as $cookie) {
  167.                 if ($cookie['domain'] == $this->urlparts['host']) {
  168.                     $this->cookies[$cookie['name']] = $cookie['value'];
  169.                 }
  170.             }
  171.         }
  172.  
  173.         // Add cookies the user wants to set.
  174.         if (isset($options['cookies'])) {
  175.             foreach ($options['cookies'] as $cookie) {
  176.                 if ($cookie['domain'] == $this->urlparts['host']) {
  177.                     $this->cookies[$cookie['name']] = $cookie['value'];
  178.                 }
  179.             }
  180.         }
  181.  
  182.         $cookies = '';
  183.         foreach ($this->cookies as $name => $value) {
  184.             if (!empty($cookies)) {
  185.                 $cookies .= '; ';
  186.             }
  187.             $cookies .= urlencode($name) . '=' . urlencode($value);
  188.         }
  189.  
  190.         return $cookies;
  191.     }
  192.  
  193.     /**
  194.      * Validate url data passed to constructor.
  195.      *
  196.      * @access private
  197.      * @return boolean
  198.      */
  199.     function _validateUrl()
  200.     {
  201.         if (!is_array($this->urlparts) ) {
  202.             $this->_raiseSoapFault('Unable to parse URL ' . $this->url);
  203.             return false;
  204.         }
  205.         if (!isset($this->urlparts['host'])) {
  206.             $this->_raiseSoapFault('No host in URL ' . $this->url);
  207.             return false;
  208.         }
  209.         if (!isset($this->urlparts['port'])) {
  210.             if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
  211.                 $this->urlparts['port'] = 80;
  212.             } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
  213.                 $this->urlparts['port'] = 443;
  214.             }
  215.  
  216.         }
  217.         if (isset($this->urlparts['user'])) {
  218.             $this->setCredentials(urldecode($this->urlparts['user']),
  219.                                   urldecode($this->urlparts['pass']));
  220.         }
  221.         if (!isset($this->urlparts['path']) || !$this->urlparts['path']) {
  222.             $this->urlparts['path'] = '/';
  223.         }
  224.  
  225.         return true;
  226.     }
  227.  
  228.     /**
  229.      * Finds out what the encoding is.
  230.      * Sets the object property accordingly.
  231.      *
  232.      * @access private
  233.      * @param array $headers  Headers.
  234.      */
  235.     function _parseEncoding($headers)
  236.     {
  237.         $h = stristr($headers, 'Content-Type');
  238.         preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER);
  239.         $n = count($ct);
  240.         $ct = $ct[$n - 1];
  241.  
  242.         // Strip the string of \r.
  243.         $this->result_content_type = str_replace("\r", '', $ct[1]);
  244.  
  245.         if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i',
  246.                        $this->result_content_type,
  247.                        $m)) {
  248.             $this->result_content_type = $m[1];
  249.             if (count($m) > 2) {
  250.                 $enc = strtoupper(str_replace('"', '', $m[2]));
  251.                 if (in_array($enc, $this->_encodings)) {
  252.                     $this->result_encoding = $enc;
  253.                 }
  254.             }
  255.         }
  256.  
  257.         // Deal with broken servers that don't set content type on faults.
  258.         if (!$this->result_content_type) {
  259.             $this->result_content_type = 'text/xml';
  260.         }
  261.     }
  262.  
  263.     /**
  264.      * Parses the headers.
  265.      *
  266.      * @param array $headers  The headers.
  267.      */
  268.     function _parseHeaders($headers)
  269.     {
  270.         /* Largely borrowed from HTTP_Request. */
  271.         $this->result_headers = array();
  272.         $headers = split("\r?\n", $headers);
  273.         foreach ($headers as $value) {
  274.             if (strpos($value,':') === false) {
  275.                 $this->result_headers[0] = $value;
  276.                 continue;
  277.             }
  278.             list($name, $value) = split(':', $value);
  279.             $headername = strtolower($name);
  280.             $headervalue = trim($value);
  281.             $this->result_headers[$headername] = $headervalue;
  282.  
  283.             if ($headername == 'set-cookie') {
  284.                 // Parse a SetCookie header to fill _cookies array.
  285.                 $cookie = array('expires' => null,
  286.                                 'domain'  => $this->urlparts['host'],
  287.                                 'path'    => null,
  288.                                 'secure'  => false);
  289.  
  290.                 if (!strpos($headervalue, ';')) {
  291.                     // Only a name=value pair.
  292.                     list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue));
  293.                     $cookie['name']  = urldecode($cookie['name']);
  294.                     $cookie['value'] = urldecode($cookie['value']);
  295.  
  296.                 } else {
  297.                     // Some optional parameters are supplied.
  298.                     $elements = explode(';', $headervalue);
  299.                     list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0]));
  300.                     $cookie['name']  = urldecode($cookie['name']);
  301.                     $cookie['value'] = urldecode($cookie['value']);
  302.  
  303.                     for ($i = 1; $i < count($elements);$i++) {
  304.                         list($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
  305.                         if ('secure' == $elName) {
  306.                             $cookie['secure'] = true;
  307.                         } elseif ('expires' == $elName) {
  308.                             $cookie['expires'] = str_replace('"', '', $elValue);
  309.                         } elseif ('path' == $elName OR 'domain' == $elName) {
  310.                             $cookie[$elName] = urldecode($elValue);
  311.                         } else {
  312.                             $cookie[$elName] = $elValue;
  313.                         }
  314.                     }
  315.                 }
  316.                 $this->result_cookies[] = $cookie;
  317.             }
  318.         }
  319.     }
  320.  
  321.     /**
  322.      * Removes HTTP headers from response.
  323.      *
  324.      * @return boolean
  325.      * @access private
  326.      */
  327.     function _parseResponse()
  328.     {
  329.         if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
  330.                        $this->incoming_payload,
  331.                        $match)) {
  332.             $this->response = $match[2];
  333.             // Find the response error, some servers response with 500 for
  334.             // SOAP faults.
  335.             $this->_parseHeaders($match[1]);
  336.  
  337.             list(, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s');
  338.             unset($this->result_headers[0]);
  339.  
  340.             switch($code) {
  341.                 case 100: // Continue
  342.                     $this->incoming_payload = $match[2];
  343.                     return $this->_parseResponse();
  344.                 case 400:
  345.                     $this->_raiseSoapFault("HTTP Response $code Bad Request");
  346.                     return false;
  347.                     break;
  348.                 case 401:
  349.                     $this->_raiseSoapFault("HTTP Response $code Authentication Failed");
  350.                     return false;
  351.                     break;
  352.                 case 403:
  353.                     $this->_raiseSoapFault("HTTP Response $code Forbidden");
  354.                     return false;
  355.                     break;
  356.                 case 404:
  357.                     $this->_raiseSoapFault("HTTP Response $code Not Found");
  358.                     return false;
  359.                     break;
  360.                 case 407:
  361.                     $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required");
  362.                     return false;
  363.                     break;
  364.                 case 408:
  365.                     $this->_raiseSoapFault("HTTP Response $code Request Timeout");
  366.                     return false;
  367.                     break;
  368.                 case 410:
  369.                     $this->_raiseSoapFault("HTTP Response $code Gone");
  370.                     return false;
  371.                     break;
  372.                 default:
  373.                     if ($code >= 400 && $code < 500) {
  374.                         $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg");
  375.                         return false;
  376.                     }
  377.             }
  378.  
  379.             $this->_parseEncoding($match[1]);
  380.  
  381.             if ($this->result_content_type == 'application/dime') {
  382.                 // XXX quick hack insertion of DIME
  383.                 if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) {
  384.                     // _decodeDIMEMessage already raised $this->fault
  385.                     return false;
  386.                 }
  387.                 $this->result_content_type = $this->headers['content-type'];
  388.             } elseif (stristr($this->result_content_type, 'multipart/related')) {
  389.                 $this->response = $this->incoming_payload;
  390.                 if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) {
  391.                     // _decodeMimeMessage already raised $this->fault
  392.                     return false;
  393.                 }
  394.             } elseif ($this->result_content_type != 'text/xml') {
  395.                 $this->_raiseSoapFault($this->response);
  396.                 return false;
  397.             }
  398.             // if no content, return false
  399.             return strlen($this->response) > 0;
  400.         }
  401.         $this->_raiseSoapFault('Invalid HTTP Response');
  402.         return false;
  403.     }
  404.  
  405.     /**
  406.      * Creates an HTTP request, including headers, for the outgoing request.
  407.      *
  408.      * @access private
  409.      *
  410.      * @param string $msg     Outgoing SOAP package.
  411.      * @param array $options  Options.
  412.      *
  413.      * @return string  Outgoing payload.
  414.      */
  415.     function _getRequest($msg, $options)
  416.     {
  417.         $this->headers = array();
  418.  
  419.         $action = isset($options['soapaction']) ? $options['soapaction'] : '';
  420.         $fullpath = $this->urlparts['path'];
  421.         if (isset($this->urlparts['query'])) {
  422.             $fullpath .= '?' . $this->urlparts['query'];
  423.         }
  424.         if (isset($this->urlparts['fragment'])) {
  425.             $fullpath .= '#' . $this->urlparts['fragment'];
  426.         }
  427.  
  428.         if (isset($options['proxy_host'])) {
  429.             $fullpath = 'http://' . $this->urlparts['host'] . ':' .
  430.                 $this->urlparts['port'] . $fullpath;
  431.         }
  432.  
  433.         if (isset($options['proxy_user'])) {
  434.             $this->headers['Proxy-Authorization'] = 'Basic ' .
  435.                 base64_encode($options['proxy_user'] . ':' .
  436.                               $options['proxy_pass']);
  437.         }
  438.  
  439.         if (isset($options['user'])) {
  440.             $this->setCredentials($options['user'], $options['pass']);
  441.         }
  442.  
  443.         $this->headers['User-Agent'] = $this->_userAgent;
  444.         $this->headers['Host'] = $this->urlparts['host'];
  445.         $this->headers['Content-Type'] = "text/xml; charset=$this->encoding";
  446.         $this->headers['Content-Length'] = strlen($msg);
  447.         $this->headers['SOAPAction'] = '"' . $action . '"';
  448.         $this->headers['Connection'] = 'close';
  449.  
  450.         if (isset($options['headers'])) {
  451.             $this->headers = array_merge($this->headers, $options['headers']);
  452.         }
  453.  
  454.         $cookies = $this->_generateCookieHeader($options);
  455.         if ($cookies) {
  456.             $this->headers['Cookie'] = $cookies;
  457.         }
  458.  
  459.         $headers = '';
  460.         foreach ($this->headers as $k => $v) {
  461.             $headers .= "$k: $v\r\n";
  462.         }
  463.         $this->outgoing_payload = "POST $fullpath HTTP/1.0\r\n" . $headers .
  464.             "\r\n" . $msg;
  465.  
  466.         return $this->outgoing_payload;
  467.     }
  468.  
  469.     /**
  470.      * Sends the outgoing HTTP request and reads and parses the response.
  471.      *
  472.      * @access private
  473.      *
  474.      * @param string $msg     Outgoing SOAP package.
  475.      * @param array $options  Options.
  476.      *
  477.      * @return string  Response data without HTTP headers.
  478.      */
  479.     function _sendHTTP($msg, $options)
  480.     {
  481.         $this->incoming_payload = '';
  482.         $this->_getRequest($msg, $options);
  483.         $host = $this->urlparts['host'];
  484.         $port = $this->urlparts['port'];
  485.         if (isset($options['proxy_host'])) {
  486.             $host = $options['proxy_host'];
  487.             $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
  488.         }
  489.         // Send.
  490.         if ($this->timeout > 0) {
  491.             $fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
  492.         } else {
  493.             $fp = @fsockopen($host, $port, $this->errno, $this->errmsg);
  494.         }
  495.         if (!$fp) {
  496.             return $this->_raiseSoapFault("Connect Error to $host:$port");
  497.         }
  498.         if ($this->timeout > 0) {
  499.             // some builds of PHP do not support this, silence the warning
  500.             @socket_set_timeout($fp, $this->timeout);
  501.         }
  502.         if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
  503.             return $this->_raiseSoapFault("Error POSTing Data to $host");
  504.         }
  505.  
  506.         // get reponse
  507.         // XXX time consumer
  508.         do {
  509.             $data = fread($fp, 4096);
  510.             $_tmp_status = socket_get_status($fp);
  511.             if ($_tmp_status['timed_out']) {
  512.                 return $this->_raiseSoapFault("Timed out read from $host");
  513.             } else {
  514.                 $this->incoming_payload .= $data;
  515.             }
  516.         } while (!$_tmp_status['eof']);
  517.  
  518.         fclose($fp);
  519.  
  520.         if (!$this->_parseResponse()) {
  521.             return $this->fault;
  522.         }
  523.         return $this->response;
  524.     }
  525.  
  526.     /**
  527.      * Sends the outgoing HTTPS request and reads and parses the response.
  528.      *
  529.      * @access private
  530.      *
  531.      * @param string $msg     Outgoing SOAP package.
  532.      * @param array $options  Options.
  533.      *
  534.      * @return string  Response data without HTTP headers.
  535.      */
  536.     function _sendHTTPS($msg, $options)
  537.     {
  538.         /* Check if the required curl extension is installed. */
  539.         if (!extension_loaded('curl')) {
  540.             return $this->_raiseSoapFault('CURL Extension is required for HTTPS');
  541.         }
  542.  
  543.         $ch = curl_init();
  544.  
  545.         if (isset($options['proxy_host'])) {
  546.             $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
  547.             curl_setopt($ch, CURLOPT_PROXY,
  548.                         $options['proxy_host'] . ':' . $port);
  549.         }
  550.         if (isset($options['proxy_user'])) {
  551.             curl_setopt($ch, CURLOPT_PROXYUSERPWD,
  552.                         $options['proxy_user'] . ':' . $options['proxy_pass']);
  553.         }
  554.  
  555.         if (isset($options['user'])) {
  556.             curl_setopt($ch, CURLOPT_USERPWD,
  557.                         $options['user'] . ':' . $options['pass']);
  558.         }
  559.  
  560.         if (!isset($options['soapaction'])) {
  561.             $options['soapaction'] = '';
  562.         }
  563.         if (!isset($options['headers']['Content-Type'])) {
  564.            $options['headers']['Content-Type'] = 'text/xml';
  565.         }
  566.         curl_setopt($ch, CURLOPT_HTTPHEADER,
  567.                     array('Content-Type: ' . $options['headers']['Content-Type']
  568.                          . ';charset=' . $this->encoding,
  569.                           'SOAPAction: "' . $options['soapaction'] . '"'));
  570.         curl_setopt($ch, CURLOPT_USERAGENT,
  571.                     $this->_userAgent);
  572.  
  573.         if ($this->timeout) {
  574.             curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
  575.         }
  576.  
  577.         curl_setopt($ch, CURLOPT_POSTFIELDS, $msg);
  578.         curl_setopt($ch, CURLOPT_URL, $this->url);
  579.         curl_setopt($ch, CURLOPT_POST, 1);
  580.         curl_setopt($ch, CURLOPT_FAILONERROR, 0);
  581.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  582.         curl_setopt($ch, CURLOPT_HEADER, 1);
  583.         if (defined('CURLOPT_HTTP_VERSION')) {
  584.             curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
  585.         }
  586.         if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
  587.             curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  588.         }
  589.         $cookies = $this->_generateCookieHeader($options);
  590.         if ($cookies) {
  591.             curl_setopt($ch, CURLOPT_COOKIE, $cookies);
  592.         }
  593.  
  594.         if (isset($options['curl'])) {
  595.             foreach ($options['curl'] as $key => $val) {
  596.                 curl_setopt($ch, $key, $val);
  597.             }
  598.         }
  599.  
  600.         // Save the outgoing XML. This doesn't quite match _sendHTTP as CURL
  601.         // generates the headers, but having the XML is usually the most
  602.         // important part for tracing/debugging.
  603.         $this->outgoing_payload = $msg;
  604.  
  605.         $this->incoming_payload = curl_exec($ch);
  606.         if (!$this->incoming_payload) {
  607.             $m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch);
  608.             curl_close($ch);
  609.             return $this->_raiseSoapFault($m);
  610.         }
  611.         curl_close($ch);
  612.  
  613.         if (!$this->_parseResponse()) {
  614.             return $this->fault;
  615.         }
  616.  
  617.         return $this->response;
  618.     }
  619.  
  620. }
  621.