class.soap_transport_http.php 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304
  1. <?php
  2. /**
  3. * transport class for sending/receiving data via HTTP and HTTPS
  4. * NOTE: PHP must be compiled with the CURL extension for HTTPS support
  5. *
  6. * @author Dietrich Ayala <dietrich@ganx4.com>
  7. * @author Scott Nichol <snichol@users.sourceforge.net>
  8. * @version $Id: class.soap_transport_http.php,v 1.68 2010/04/26 20:15:08 snichol Exp $
  9. * @access public
  10. */
  11. class soap_transport_http extends nusoap_base
  12. {
  13. var $url = '';
  14. var $uri = '';
  15. var $digest_uri = '';
  16. var $scheme = '';
  17. var $host = '';
  18. var $port = '';
  19. var $path = '';
  20. var $request_method = 'POST';
  21. var $protocol_version = '1.0';
  22. var $encoding = '';
  23. var $outgoing_headers = array();
  24. var $incoming_headers = array();
  25. var $incoming_cookies = array();
  26. var $outgoing_payload = '';
  27. var $incoming_payload = '';
  28. var $response_status_line; // HTTP response status line
  29. var $useSOAPAction = true;
  30. var $persistentConnection = false;
  31. var $ch = false; // cURL handle
  32. var $ch_options = array(); // cURL custom options
  33. var $use_curl = false; // force cURL use
  34. var $proxy = null; // proxy information (associative array)
  35. var $username = '';
  36. var $password = '';
  37. var $authtype = '';
  38. var $digestRequest = array();
  39. var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
  40. // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
  41. // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
  42. // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
  43. // passphrase: SSL key password/passphrase
  44. // certpassword: SSL certificate password
  45. // verifypeer: default is 1
  46. // verifyhost: default is 1
  47. /**
  48. * constructor
  49. *
  50. * @param string $url The URL to which to connect
  51. * @param array $curl_options User-specified cURL options
  52. * @param boolean $use_curl Whether to try to force cURL use
  53. * @access public
  54. */
  55. function __construct($url, $curl_options = NULL, $use_curl = false){
  56. parent::__construct();
  57. $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
  58. $this->appendDebug($this->varDump($curl_options));
  59. $this->setURL($url);
  60. if (is_array($curl_options)) {
  61. $this->ch_options = $curl_options;
  62. }
  63. $this->use_curl = $use_curl;
  64. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
  65. $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
  66. }
  67. /**
  68. * sets a cURL option
  69. *
  70. * @param mixed $option The cURL option (always integer?)
  71. * @param mixed $value The cURL option value
  72. * @access private
  73. */
  74. function setCurlOption($option, $value) {
  75. $this->debug("setCurlOption option=$option, value=");
  76. $this->appendDebug($this->varDump($value));
  77. curl_setopt($this->ch, $option, $value);
  78. }
  79. /**
  80. * sets an HTTP header
  81. *
  82. * @param string $name The name of the header
  83. * @param string $value The value of the header
  84. * @access private
  85. */
  86. function setHeader($name, $value) {
  87. $this->outgoing_headers[$name] = $value;
  88. $this->debug("set header $name: $value");
  89. }
  90. /**
  91. * unsets an HTTP header
  92. *
  93. * @param string $name The name of the header
  94. * @access private
  95. */
  96. function unsetHeader($name) {
  97. if (isset($this->outgoing_headers[$name])) {
  98. $this->debug("unset header $name");
  99. unset($this->outgoing_headers[$name]);
  100. }
  101. }
  102. /**
  103. * sets the URL to which to connect
  104. *
  105. * @param string $url The URL to which to connect
  106. * @access private
  107. */
  108. function setURL($url) {
  109. $this->url = $url;
  110. $u = parse_url($url);
  111. foreach($u as $k => $v){
  112. $this->debug("parsed URL $k = $v");
  113. $this->$k = $v;
  114. }
  115. // add any GET params to path
  116. if(isset($u['query']) && $u['query'] != ''){
  117. $this->path .= '?' . $u['query'];
  118. }
  119. // set default port
  120. if(!isset($u['port'])){
  121. if($u['scheme'] == 'https'){
  122. $this->port = 443;
  123. } else {
  124. $this->port = 80;
  125. }
  126. }
  127. $this->uri = $this->path;
  128. $this->digest_uri = $this->uri;
  129. // build headers
  130. if (!isset($u['port'])) {
  131. $this->setHeader('Host', $this->host);
  132. } else {
  133. $this->setHeader('Host', $this->host.':'.$this->port);
  134. }
  135. if (isset($u['user']) && $u['user'] != '') {
  136. $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
  137. }
  138. }
  139. /**
  140. * gets the I/O method to use
  141. *
  142. * @return string I/O method to use (socket|curl|unknown)
  143. * @access private
  144. */
  145. function io_method() {
  146. if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
  147. return 'curl';
  148. if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
  149. return 'socket';
  150. return 'unknown';
  151. }
  152. /**
  153. * establish an HTTP connection
  154. *
  155. * @param integer $timeout set connection timeout in seconds
  156. * @param integer $response_timeout set response timeout in seconds
  157. * @return boolean true if connected, false if not
  158. * @access private
  159. */
  160. function connect($connection_timeout=0,$response_timeout=30){
  161. // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
  162. // "regular" socket.
  163. // TODO: disabled for now because OpenSSL must be *compiled* in (not just
  164. // loaded), and until PHP5 stream_get_wrappers is not available.
  165. // if ($this->scheme == 'https') {
  166. // if (version_compare(phpversion(), '4.3.0') >= 0) {
  167. // if (extension_loaded('openssl')) {
  168. // $this->scheme = 'ssl';
  169. // $this->debug('Using SSL over OpenSSL');
  170. // }
  171. // }
  172. // }
  173. $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
  174. if ($this->io_method() == 'socket') {
  175. if (!is_array($this->proxy)) {
  176. $host = $this->host;
  177. $port = $this->port;
  178. } else {
  179. $host = $this->proxy['host'];
  180. $port = $this->proxy['port'];
  181. }
  182. // use persistent connection
  183. if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
  184. if (!feof($this->fp)) {
  185. $this->debug('Re-use persistent connection');
  186. return true;
  187. }
  188. fclose($this->fp);
  189. $this->debug('Closed persistent connection at EOF');
  190. }
  191. // munge host if using OpenSSL
  192. if ($this->scheme == 'ssl') {
  193. $host = 'ssl://' . $host;
  194. }
  195. $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
  196. // open socket
  197. if($connection_timeout > 0){
  198. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
  199. } else {
  200. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
  201. }
  202. // test pointer
  203. if(!$this->fp) {
  204. $msg = 'Couldn\'t open socket connection to server ' . $this->url;
  205. if ($this->errno) {
  206. $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
  207. } else {
  208. $msg .= ' prior to connect(). This is often a problem looking up the host name.';
  209. }
  210. $this->debug($msg);
  211. $this->setError($msg);
  212. return false;
  213. }
  214. // set response timeout
  215. $this->debug('set response timeout to ' . $response_timeout);
  216. socket_set_timeout( $this->fp, $response_timeout);
  217. $this->debug('socket connected');
  218. return true;
  219. } else if ($this->io_method() == 'curl') {
  220. if (!extension_loaded('curl')) {
  221. // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
  222. $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
  223. return false;
  224. }
  225. // Avoid warnings when PHP does not have these options
  226. if (defined('CURLOPT_CONNECTIONTIMEOUT'))
  227. $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
  228. else
  229. $CURLOPT_CONNECTIONTIMEOUT = 78;
  230. if (defined('CURLOPT_HTTPAUTH'))
  231. $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
  232. else
  233. $CURLOPT_HTTPAUTH = 107;
  234. if (defined('CURLOPT_PROXYAUTH'))
  235. $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
  236. else
  237. $CURLOPT_PROXYAUTH = 111;
  238. if (defined('CURLAUTH_BASIC'))
  239. $CURLAUTH_BASIC = CURLAUTH_BASIC;
  240. else
  241. $CURLAUTH_BASIC = 1;
  242. if (defined('CURLAUTH_DIGEST'))
  243. $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
  244. else
  245. $CURLAUTH_DIGEST = 2;
  246. if (defined('CURLAUTH_NTLM'))
  247. $CURLAUTH_NTLM = CURLAUTH_NTLM;
  248. else
  249. $CURLAUTH_NTLM = 8;
  250. $this->debug('connect using cURL');
  251. // init CURL
  252. $this->ch = curl_init();
  253. // set url
  254. $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
  255. // add path
  256. $hostURL .= $this->path;
  257. $this->setCurlOption(CURLOPT_URL, $hostURL);
  258. // follow location headers (re-directs)
  259. if (ini_get('safe_mode') || ini_get('open_basedir')) {
  260. $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
  261. $this->debug('safe_mode = ');
  262. $this->appendDebug($this->varDump(ini_get('safe_mode')));
  263. $this->debug('open_basedir = ');
  264. $this->appendDebug($this->varDump(ini_get('open_basedir')));
  265. } else {
  266. $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
  267. }
  268. // ask for headers in the response output
  269. $this->setCurlOption(CURLOPT_HEADER, 1);
  270. // ask for the response output as the return value
  271. $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
  272. // encode
  273. // We manage this ourselves through headers and encoding
  274. // if(function_exists('gzuncompress')){
  275. // $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
  276. // }
  277. // persistent connection
  278. if ($this->persistentConnection) {
  279. // I believe the following comment is now bogus, having applied to
  280. // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
  281. // The way we send data, we cannot use persistent connections, since
  282. // there will be some "junk" at the end of our request.
  283. //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
  284. $this->persistentConnection = false;
  285. $this->setHeader('Connection', 'close');
  286. }
  287. // set timeouts
  288. if ($connection_timeout != 0) {
  289. $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
  290. }
  291. if ($response_timeout != 0) {
  292. $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
  293. }
  294. if ($this->scheme == 'https') {
  295. $this->debug('set cURL SSL verify options');
  296. // recent versions of cURL turn on peer/host checking by default,
  297. // while PHP binaries are not compiled with a default location for the
  298. // CA cert bundle, so disable peer/host checking.
  299. //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
  300. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
  301. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
  302. // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
  303. if ($this->authtype == 'certificate') {
  304. $this->debug('set cURL certificate options');
  305. if (isset($this->certRequest['cainfofile'])) {
  306. $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
  307. }
  308. if (isset($this->certRequest['verifypeer'])) {
  309. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
  310. } else {
  311. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
  312. }
  313. if (isset($this->certRequest['verifyhost'])) {
  314. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
  315. } else {
  316. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
  317. }
  318. if (isset($this->certRequest['sslcertfile'])) {
  319. $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
  320. }
  321. if (isset($this->certRequest['sslkeyfile'])) {
  322. $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
  323. }
  324. if (isset($this->certRequest['passphrase'])) {
  325. $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
  326. }
  327. if (isset($this->certRequest['certpassword'])) {
  328. $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
  329. }
  330. }
  331. }
  332. if ($this->authtype && ($this->authtype != 'certificate')) {
  333. if ($this->username) {
  334. $this->debug('set cURL username/password');
  335. $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
  336. }
  337. if ($this->authtype == 'basic') {
  338. $this->debug('set cURL for Basic authentication');
  339. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
  340. }
  341. if ($this->authtype == 'digest') {
  342. $this->debug('set cURL for digest authentication');
  343. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
  344. }
  345. if ($this->authtype == 'ntlm') {
  346. $this->debug('set cURL for NTLM authentication');
  347. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
  348. }
  349. }
  350. if (is_array($this->proxy)) {
  351. $this->debug('set cURL proxy options');
  352. if ($this->proxy['port'] != '') {
  353. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
  354. } else {
  355. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
  356. }
  357. if ($this->proxy['username'] || $this->proxy['password']) {
  358. $this->debug('set cURL proxy authentication options');
  359. $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
  360. if ($this->proxy['authtype'] == 'basic') {
  361. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
  362. }
  363. if ($this->proxy['authtype'] == 'ntlm') {
  364. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
  365. }
  366. }
  367. }
  368. $this->debug('cURL connection set up');
  369. return true;
  370. } else {
  371. $this->setError('Unknown scheme ' . $this->scheme);
  372. $this->debug('Unknown scheme ' . $this->scheme);
  373. return false;
  374. }
  375. }
  376. /**
  377. * sends the SOAP request and gets the SOAP response via HTTP[S]
  378. *
  379. * @param string $data message data
  380. * @param integer $timeout set connection timeout in seconds
  381. * @param integer $response_timeout set response timeout in seconds
  382. * @param array $cookies cookies to send
  383. * @return string data
  384. * @access public
  385. */
  386. function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
  387. $this->debug('entered send() with data of length: '.strlen($data));
  388. $this->tryagain = true;
  389. $tries = 0;
  390. while ($this->tryagain) {
  391. $this->tryagain = false;
  392. if ($tries++ < 2) {
  393. // make connnection
  394. if (!$this->connect($timeout, $response_timeout)){
  395. return false;
  396. }
  397. // send request
  398. if (!$this->sendRequest($data, $cookies)){
  399. return false;
  400. }
  401. // get response
  402. $respdata = $this->getResponse();
  403. } else {
  404. $this->setError("Too many tries to get an OK response ($this->response_status_line)");
  405. }
  406. }
  407. $this->debug('end of send()');
  408. return $respdata;
  409. }
  410. /**
  411. * sends the SOAP request and gets the SOAP response via HTTPS using CURL
  412. *
  413. * @param string $data message data
  414. * @param integer $timeout set connection timeout in seconds
  415. * @param integer $response_timeout set response timeout in seconds
  416. * @param array $cookies cookies to send
  417. * @return string data
  418. * @access public
  419. * @deprecated
  420. */
  421. function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
  422. return $this->send($data, $timeout, $response_timeout, $cookies);
  423. }
  424. /**
  425. * if authenticating, set user credentials here
  426. *
  427. * @param string $username
  428. * @param string $password
  429. * @param string $authtype (basic|digest|certificate|ntlm)
  430. * @param array $digestRequest (keys must be nonce, nc, realm, qop)
  431. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
  432. * @access public
  433. */
  434. function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
  435. $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
  436. $this->appendDebug($this->varDump($digestRequest));
  437. $this->debug("certRequest=");
  438. $this->appendDebug($this->varDump($certRequest));
  439. // cf. RFC 2617
  440. if ($authtype == 'basic') {
  441. $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
  442. } elseif ($authtype == 'digest') {
  443. if (isset($digestRequest['nonce'])) {
  444. $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
  445. // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
  446. // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  447. $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
  448. // H(A1) = MD5(A1)
  449. $HA1 = md5($A1);
  450. // A2 = Method ":" digest-uri-value
  451. $A2 = $this->request_method . ':' . $this->digest_uri;
  452. // H(A2)
  453. $HA2 = md5($A2);
  454. // KD(secret, data) = H(concat(secret, ":", data))
  455. // if qop == auth:
  456. // request-digest = <"> < KD ( H(A1), unq(nonce-value)
  457. // ":" nc-value
  458. // ":" unq(cnonce-value)
  459. // ":" unq(qop-value)
  460. // ":" H(A2)
  461. // ) <">
  462. // if qop is missing,
  463. // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
  464. $unhashedDigest = '';
  465. $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
  466. $cnonce = $nonce;
  467. if ($digestRequest['qop'] != '') {
  468. $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
  469. } else {
  470. $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
  471. }
  472. $hashedDigest = md5($unhashedDigest);
  473. $opaque = '';
  474. if (isset($digestRequest['opaque'])) {
  475. $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
  476. }
  477. $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
  478. }
  479. } elseif ($authtype == 'certificate') {
  480. $this->certRequest = $certRequest;
  481. $this->debug('Authorization header not set for certificate');
  482. } elseif ($authtype == 'ntlm') {
  483. // do nothing
  484. $this->debug('Authorization header not set for ntlm');
  485. }
  486. $this->username = $username;
  487. $this->password = $password;
  488. $this->authtype = $authtype;
  489. $this->digestRequest = $digestRequest;
  490. }
  491. /**
  492. * set the soapaction value
  493. *
  494. * @param string $soapaction
  495. * @access public
  496. */
  497. function setSOAPAction($soapaction) {
  498. $this->setHeader('SOAPAction', '"' . $soapaction . '"');
  499. }
  500. /**
  501. * use http encoding
  502. *
  503. * @param string $enc encoding style. supported values: gzip, deflate, or both
  504. * @access public
  505. */
  506. function setEncoding($enc='gzip, deflate') {
  507. if (function_exists('gzdeflate')) {
  508. $this->protocol_version = '1.1';
  509. $this->setHeader('Accept-Encoding', $enc);
  510. if (!isset($this->outgoing_headers['Connection'])) {
  511. $this->setHeader('Connection', 'close');
  512. $this->persistentConnection = false;
  513. }
  514. // deprecated as of PHP 5.3.0
  515. //set_magic_quotes_runtime(0);
  516. $this->encoding = $enc;
  517. }
  518. }
  519. /**
  520. * set proxy info here
  521. *
  522. * @param string $proxyhost use an empty string to remove proxy
  523. * @param string $proxyport
  524. * @param string $proxyusername
  525. * @param string $proxypassword
  526. * @param string $proxyauthtype (basic|ntlm)
  527. * @access public
  528. */
  529. function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
  530. if ($proxyhost) {
  531. $this->proxy = array(
  532. 'host' => $proxyhost,
  533. 'port' => $proxyport,
  534. 'username' => $proxyusername,
  535. 'password' => $proxypassword,
  536. 'authtype' => $proxyauthtype
  537. );
  538. if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
  539. $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
  540. }
  541. } else {
  542. $this->debug('remove proxy');
  543. $proxy = null;
  544. unsetHeader('Proxy-Authorization');
  545. }
  546. }
  547. /**
  548. * Test if the given string starts with a header that is to be skipped.
  549. * Skippable headers result from chunked transfer and proxy requests.
  550. *
  551. * @param string $data The string to check.
  552. * @returns boolean Whether a skippable header was found.
  553. * @access private
  554. */
  555. function isSkippableCurlHeader(&$data) {
  556. $skipHeaders = array( 'HTTP/1.1 100',
  557. 'HTTP/1.0 301',
  558. 'HTTP/1.1 301',
  559. 'HTTP/1.0 302',
  560. 'HTTP/1.1 302',
  561. 'HTTP/1.0 401',
  562. 'HTTP/1.1 401',
  563. 'HTTP/1.0 200 Connection established');
  564. foreach ($skipHeaders as $hd) {
  565. $prefix = substr($data, 0, strlen($hd));
  566. if ($prefix == $hd) return true;
  567. }
  568. return false;
  569. }
  570. /**
  571. * decode a string that is encoded w/ "chunked' transfer encoding
  572. * as defined in RFC2068 19.4.6
  573. *
  574. * @param string $buffer
  575. * @param string $lb
  576. * @returns string
  577. * @access public
  578. * @deprecated
  579. */
  580. function decodeChunked($buffer, $lb){
  581. // length := 0
  582. $length = 0;
  583. $new = '';
  584. // read chunk-size, chunk-extension (if any) and CRLF
  585. // get the position of the linebreak
  586. $chunkend = strpos($buffer, $lb);
  587. if ($chunkend == FALSE) {
  588. $this->debug('no linebreak found in decodeChunked');
  589. return $new;
  590. }
  591. $temp = substr($buffer,0,$chunkend);
  592. $chunk_size = hexdec( trim($temp) );
  593. $chunkstart = $chunkend + strlen($lb);
  594. // while (chunk-size > 0) {
  595. while ($chunk_size > 0) {
  596. $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
  597. $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
  598. // Just in case we got a broken connection
  599. if ($chunkend == FALSE) {
  600. $chunk = substr($buffer,$chunkstart);
  601. // append chunk-data to entity-body
  602. $new .= $chunk;
  603. $length += strlen($chunk);
  604. break;
  605. }
  606. // read chunk-data and CRLF
  607. $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
  608. // append chunk-data to entity-body
  609. $new .= $chunk;
  610. // length := length + chunk-size
  611. $length += strlen($chunk);
  612. // read chunk-size and CRLF
  613. $chunkstart = $chunkend + strlen($lb);
  614. $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
  615. if ($chunkend == FALSE) {
  616. break; //Just in case we got a broken connection
  617. }
  618. $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
  619. $chunk_size = hexdec( trim($temp) );
  620. $chunkstart = $chunkend;
  621. }
  622. return $new;
  623. }
  624. /**
  625. * Writes the payload, including HTTP headers, to $this->outgoing_payload.
  626. *
  627. * @param string $data HTTP body
  628. * @param string $cookie_str data for HTTP Cookie header
  629. * @return void
  630. * @access private
  631. */
  632. function buildPayload($data, $cookie_str = '') {
  633. // Note: for cURL connections, $this->outgoing_payload is ignored,
  634. // as is the Content-Length header, but these are still created as
  635. // debugging guides.
  636. // add content-length header
  637. if ($this->request_method != 'GET') {
  638. $this->setHeader('Content-Length', strlen($data));
  639. }
  640. // start building outgoing payload:
  641. if ($this->proxy) {
  642. $uri = $this->url;
  643. } else {
  644. $uri = $this->uri;
  645. }
  646. $req = "$this->request_method $uri HTTP/$this->protocol_version";
  647. $this->debug("HTTP request: $req");
  648. $this->outgoing_payload = "$req\r\n";
  649. // loop thru headers, serializing
  650. foreach($this->outgoing_headers as $k => $v){
  651. $hdr = $k.': '.$v;
  652. $this->debug("HTTP header: $hdr");
  653. $this->outgoing_payload .= "$hdr\r\n";
  654. }
  655. // add any cookies
  656. if ($cookie_str != '') {
  657. $hdr = 'Cookie: '.$cookie_str;
  658. $this->debug("HTTP header: $hdr");
  659. $this->outgoing_payload .= "$hdr\r\n";
  660. }
  661. // header/body separator
  662. $this->outgoing_payload .= "\r\n";
  663. // add data
  664. $this->outgoing_payload .= $data;
  665. }
  666. /**
  667. * sends the SOAP request via HTTP[S]
  668. *
  669. * @param string $data message data
  670. * @param array $cookies cookies to send
  671. * @return boolean true if OK, false if problem
  672. * @access private
  673. */
  674. function sendRequest($data, $cookies = NULL) {
  675. // build cookie string
  676. $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
  677. // build payload
  678. $this->buildPayload($data, $cookie_str);
  679. if ($this->io_method() == 'socket') {
  680. // send payload
  681. if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
  682. $this->setError('couldn\'t write message data to socket');
  683. $this->debug('couldn\'t write message data to socket');
  684. return false;
  685. }
  686. $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
  687. return true;
  688. } else if ($this->io_method() == 'curl') {
  689. // set payload
  690. // cURL does say this should only be the verb, and in fact it
  691. // turns out that the URI and HTTP version are appended to this, which
  692. // some servers refuse to work with (so we no longer use this method!)
  693. //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
  694. $curl_headers = array();
  695. foreach($this->outgoing_headers as $k => $v){
  696. if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
  697. $this->debug("Skip cURL header $k: $v");
  698. } else {
  699. $curl_headers[] = "$k: $v";
  700. }
  701. }
  702. if ($cookie_str != '') {
  703. $curl_headers[] = 'Cookie: ' . $cookie_str;
  704. }
  705. $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
  706. $this->debug('set cURL HTTP headers');
  707. if ($this->request_method == "POST") {
  708. $this->setCurlOption(CURLOPT_POST, 1);
  709. $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
  710. $this->debug('set cURL POST data');
  711. } else {
  712. }
  713. // insert custom user-set cURL options
  714. foreach ($this->ch_options as $key => $val) {
  715. $this->setCurlOption($key, $val);
  716. }
  717. $this->debug('set cURL payload');
  718. return true;
  719. }
  720. }
  721. /**
  722. * gets the SOAP response via HTTP[S]
  723. *
  724. * @return string the response (also sets member variables like incoming_payload)
  725. * @access private
  726. */
  727. function getResponse(){
  728. $this->incoming_payload = '';
  729. if ($this->io_method() == 'socket') {
  730. // loop until headers have been retrieved
  731. $data = '';
  732. while (!isset($lb)){
  733. // We might EOF during header read.
  734. if(feof($this->fp)) {
  735. $this->incoming_payload = $data;
  736. $this->debug('found no headers before EOF after length ' . strlen($data));
  737. $this->debug("received before EOF:\n" . $data);
  738. $this->setError('server failed to send headers');
  739. return false;
  740. }
  741. $tmp = fgets($this->fp, 256);
  742. $tmplen = strlen($tmp);
  743. $this->debug("read line of $tmplen bytes: " . trim($tmp));
  744. if ($tmplen == 0) {
  745. $this->incoming_payload = $data;
  746. $this->debug('socket read of headers timed out after length ' . strlen($data));
  747. $this->debug("read before timeout: " . $data);
  748. $this->setError('socket read of headers timed out');
  749. return false;
  750. }
  751. $data .= $tmp;
  752. $pos = strpos($data,"\r\n\r\n");
  753. if($pos > 1){
  754. $lb = "\r\n";
  755. } else {
  756. $pos = strpos($data,"\n\n");
  757. if($pos > 1){
  758. $lb = "\n";
  759. }
  760. }
  761. // remove 100 headers
  762. if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
  763. unset($lb);
  764. $data = '';
  765. }//
  766. }
  767. // store header data
  768. $this->incoming_payload .= $data;
  769. $this->debug('found end of headers after length ' . strlen($data));
  770. // process headers
  771. $header_data = trim(substr($data,0,$pos));
  772. $header_array = explode($lb,$header_data);
  773. $this->incoming_headers = array();
  774. $this->incoming_cookies = array();
  775. foreach($header_array as $header_line){
  776. $arr = explode(':',$header_line, 2);
  777. if(count($arr) > 1){
  778. $header_name = strtolower(trim($arr[0]));
  779. $this->incoming_headers[$header_name] = trim($arr[1]);
  780. if ($header_name == 'set-cookie') {
  781. // TODO: allow multiple cookies from parseCookie
  782. $cookie = $this->parseCookie(trim($arr[1]));
  783. if ($cookie) {
  784. $this->incoming_cookies[] = $cookie;
  785. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
  786. } else {
  787. $this->debug('did not find cookie in ' . trim($arr[1]));
  788. }
  789. }
  790. } else if (isset($header_name)) {
  791. // append continuation line to previous header
  792. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
  793. }
  794. }
  795. // loop until msg has been received
  796. if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
  797. $content_length = 2147483647; // ignore any content-length header
  798. $chunked = true;
  799. $this->debug("want to read chunked content");
  800. } elseif (isset($this->incoming_headers['content-length'])) {
  801. $content_length = $this->incoming_headers['content-length'];
  802. $chunked = false;
  803. $this->debug("want to read content of length $content_length");
  804. } else {
  805. $content_length = 2147483647;
  806. $chunked = false;
  807. $this->debug("want to read content to EOF");
  808. }
  809. $data = '';
  810. do {
  811. if ($chunked) {
  812. $tmp = fgets($this->fp, 256);
  813. $tmplen = strlen($tmp);
  814. $this->debug("read chunk line of $tmplen bytes");
  815. if ($tmplen == 0) {
  816. $this->incoming_payload = $data;
  817. $this->debug('socket read of chunk length timed out after length ' . strlen($data));
  818. $this->debug("read before timeout:\n" . $data);
  819. $this->setError('socket read of chunk length timed out');
  820. return false;
  821. }
  822. $content_length = hexdec(trim($tmp));
  823. $this->debug("chunk length $content_length");
  824. }
  825. $strlen = 0;
  826. while (($strlen < $content_length) && (!feof($this->fp))) {
  827. $readlen = min(8192, $content_length - $strlen);
  828. $tmp = fread($this->fp, $readlen);
  829. $tmplen = strlen($tmp);
  830. $this->debug("read buffer of $tmplen bytes");
  831. if (($tmplen == 0) && (!feof($this->fp))) {
  832. $this->incoming_payload = $data;
  833. $this->debug('socket read of body timed out after length ' . strlen($data));
  834. $this->debug("read before timeout:\n" . $data);
  835. $this->setError('socket read of body timed out');
  836. return false;
  837. }
  838. $strlen += $tmplen;
  839. $data .= $tmp;
  840. }
  841. if ($chunked && ($content_length > 0)) {
  842. $tmp = fgets($this->fp, 256);
  843. $tmplen = strlen($tmp);
  844. $this->debug("read chunk terminator of $tmplen bytes");
  845. if ($tmplen == 0) {
  846. $this->incoming_payload = $data;
  847. $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
  848. $this->debug("read before timeout:\n" . $data);
  849. $this->setError('socket read of chunk terminator timed out');
  850. return false;
  851. }
  852. }
  853. } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
  854. if (feof($this->fp)) {
  855. $this->debug('read to EOF');
  856. }
  857. $this->debug('read body of length ' . strlen($data));
  858. $this->incoming_payload .= $data;
  859. $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
  860. // close filepointer
  861. if(
  862. (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
  863. (! $this->persistentConnection) || feof($this->fp)){
  864. fclose($this->fp);
  865. $this->fp = false;
  866. $this->debug('closed socket');
  867. }
  868. // connection was closed unexpectedly
  869. if($this->incoming_payload == ''){
  870. $this->setError('no response from server');
  871. return false;
  872. }
  873. // decode transfer-encoding
  874. // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
  875. // if(!$data = $this->decodeChunked($data, $lb)){
  876. // $this->setError('Decoding of chunked data failed');
  877. // return false;
  878. // }
  879. //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
  880. // set decoded payload
  881. // $this->incoming_payload = $header_data.$lb.$lb.$data;
  882. // }
  883. } else if ($this->io_method() == 'curl') {
  884. // send and receive
  885. $this->debug('send and receive with cURL');
  886. $this->incoming_payload = curl_exec($this->ch);
  887. $data = $this->incoming_payload;
  888. $cErr = curl_error($this->ch);
  889. if ($cErr != '') {
  890. $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
  891. // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
  892. foreach(curl_getinfo($this->ch) as $k => $v){
  893. $err .= "$k: $v<br>";
  894. }
  895. $this->debug($err);
  896. $this->setError($err);
  897. curl_close($this->ch);
  898. return false;
  899. } else {
  900. //echo '<pre>';
  901. //var_dump(curl_getinfo($this->ch));
  902. //echo '</pre>';
  903. }
  904. // close curl
  905. $this->debug('No cURL error, closing cURL');
  906. curl_close($this->ch);
  907. // try removing skippable headers
  908. $savedata = $data;
  909. while ($this->isSkippableCurlHeader($data)) {
  910. $this->debug("Found HTTP header to skip");
  911. if ($pos = strpos($data,"\r\n\r\n")) {
  912. $data = ltrim(substr($data,$pos));
  913. } elseif($pos = strpos($data,"\n\n") ) {
  914. $data = ltrim(substr($data,$pos));
  915. }
  916. }
  917. if ($data == '') {
  918. // have nothing left; just remove 100 header(s)
  919. $data = $savedata;
  920. while (preg_match('/^HTTP\/1.1 100/',$data)) {
  921. if ($pos = strpos($data,"\r\n\r\n")) {
  922. $data = ltrim(substr($data,$pos));
  923. } elseif($pos = strpos($data,"\n\n") ) {
  924. $data = ltrim(substr($data,$pos));
  925. }
  926. }
  927. }
  928. // separate content from HTTP headers
  929. if ($pos = strpos($data,"\r\n\r\n")) {
  930. $lb = "\r\n";
  931. } elseif( $pos = strpos($data,"\n\n")) {
  932. $lb = "\n";
  933. } else {
  934. $this->debug('no proper separation of headers and document');
  935. $this->setError('no proper separation of headers and document');
  936. return false;
  937. }
  938. $header_data = trim(substr($data,0,$pos));
  939. $header_array = explode($lb,$header_data);
  940. $data = ltrim(substr($data,$pos));
  941. $this->debug('found proper separation of headers and document');
  942. $this->debug('cleaned data, stringlen: '.strlen($data));
  943. // clean headers
  944. foreach ($header_array as $header_line) {
  945. $arr = explode(':',$header_line,2);
  946. if(count($arr) > 1){
  947. $header_name = strtolower(trim($arr[0]));
  948. $this->incoming_headers[$header_name] = trim($arr[1]);
  949. if ($header_name == 'set-cookie') {
  950. // TODO: allow multiple cookies from parseCookie
  951. $cookie = $this->parseCookie(trim($arr[1]));
  952. if ($cookie) {
  953. $this->incoming_cookies[] = $cookie;
  954. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
  955. } else {
  956. $this->debug('did not find cookie in ' . trim($arr[1]));
  957. }
  958. }
  959. } else if (isset($header_name)) {
  960. // append continuation line to previous header
  961. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
  962. }
  963. }
  964. }
  965. $this->response_status_line = $header_array[0];
  966. $arr = explode(' ', $this->response_status_line, 3);
  967. $http_version = $arr[0];
  968. $http_status = intval($arr[1]);
  969. $http_reason = count($arr) > 2 ? $arr[2] : '';
  970. // see if we need to resend the request with http digest authentication
  971. if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
  972. $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
  973. $this->setURL($this->incoming_headers['location']);
  974. $this->tryagain = true;
  975. return false;
  976. }
  977. // see if we need to resend the request with http digest authentication
  978. if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
  979. $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
  980. if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
  981. $this->debug('Server wants digest authentication');
  982. // remove "Digest " from our elements
  983. $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
  984. // parse elements into array
  985. $digestElements = explode(',', $digestString);
  986. foreach ($digestElements as $val) {
  987. $tempElement = explode('=', trim($val), 2);
  988. $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
  989. }
  990. // should have (at least) qop, realm, nonce
  991. if (isset($digestRequest['nonce'])) {
  992. $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
  993. $this->tryagain = true;
  994. return false;
  995. }
  996. }
  997. $this->debug('HTTP authentication failed');
  998. $this->setError('HTTP authentication failed');
  999. return false;
  1000. }
  1001. if (
  1002. ($http_status >= 300 && $http_status <= 307) ||
  1003. ($http_status >= 400 && $http_status <= 417) ||
  1004. ($http_status >= 501 && $http_status <= 505)
  1005. ) {
  1006. $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
  1007. return false;
  1008. }
  1009. // decode content-encoding
  1010. if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
  1011. if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
  1012. // if decoding works, use it. else assume data wasn't gzencoded
  1013. if(function_exists('gzinflate')){
  1014. //$timer->setMarker('starting decoding of gzip/deflated content');
  1015. // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
  1016. // this means there are no Zlib headers, although there should be
  1017. $this->debug('The gzinflate function exists');
  1018. $datalen = strlen($data);
  1019. if ($this->incoming_headers['content-encoding'] == 'deflate') {
  1020. if ($degzdata = @gzinflate($data)) {
  1021. $data = $degzdata;
  1022. $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
  1023. if (strlen($data) < $datalen) {
  1024. // test for the case that the payload has been compressed twice
  1025. $this->debug('The inflated payload is smaller than the gzipped one; try again');
  1026. if ($degzdata = @gzinflate($data)) {
  1027. $data = $degzdata;
  1028. $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
  1029. }
  1030. }
  1031. } else {
  1032. $this->debug('Error using gzinflate to inflate the payload');
  1033. $this->setError('Error using gzinflate to inflate the payload');
  1034. }
  1035. } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
  1036. if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
  1037. $data = $degzdata;
  1038. $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
  1039. if (strlen($data) < $datalen) {
  1040. // test for the case that the payload has been compressed twice
  1041. $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
  1042. if ($degzdata = @gzinflate(substr($data, 10))) {
  1043. $data = $degzdata;
  1044. $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
  1045. }
  1046. }
  1047. } else {
  1048. $this->debug('Error using gzinflate to un-gzip the payload');
  1049. $this->setError('Error using gzinflate to un-gzip the payload');
  1050. }
  1051. }
  1052. //$timer->setMarker('finished decoding of gzip/deflated content');
  1053. //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
  1054. // set decoded payload
  1055. $this->incoming_payload = $header_data.$lb.$lb.$data;
  1056. } else {
  1057. $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
  1058. $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
  1059. }
  1060. } else {
  1061. $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
  1062. $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
  1063. }
  1064. } else {
  1065. $this->debug('No Content-Encoding header');
  1066. }
  1067. if(strlen($data) == 0){
  1068. $this->debug('no data after headers!');
  1069. $this->setError('no data present after HTTP headers');
  1070. return false;
  1071. }
  1072. return $data;
  1073. }
  1074. /**
  1075. * sets the content-type for the SOAP message to be sent
  1076. *
  1077. * @param string $type the content type, MIME style
  1078. * @param mixed $charset character set used for encoding (or false)
  1079. * @access public
  1080. */
  1081. function setContentType($type, $charset = false) {
  1082. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
  1083. }
  1084. /**
  1085. * specifies that an HTTP persistent connection should be used
  1086. *
  1087. * @return boolean whether the request was honored by this method.
  1088. * @access public
  1089. */
  1090. function usePersistentConnection(){
  1091. if (isset($this->outgoing_headers['Accept-Encoding'])) {
  1092. return false;
  1093. }
  1094. $this->protocol_version = '1.1';
  1095. $this->persistentConnection = true;
  1096. $this->setHeader('Connection', 'Keep-Alive');
  1097. return true;
  1098. }
  1099. /**
  1100. * parse an incoming Cookie into it's parts
  1101. *
  1102. * @param string $cookie_str content of cookie
  1103. * @return array with data of that cookie
  1104. * @access private
  1105. */
  1106. /*
  1107. * TODO: allow a Set-Cookie string to be parsed into multiple cookies
  1108. */
  1109. function parseCookie($cookie_str) {
  1110. $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
  1111. $data = preg_split('/;/', $cookie_str);
  1112. $value_str = $data[0];
  1113. $cookie_param = 'domain=';
  1114. $start = strpos($cookie_str, $cookie_param);
  1115. if ($start > 0) {
  1116. $domain = substr($cookie_str, $start + strlen($cookie_param));
  1117. $domain = substr($domain, 0, strpos($domain, ';'));
  1118. } else {
  1119. $domain = '';
  1120. }
  1121. $cookie_param = 'expires=';
  1122. $start = strpos($cookie_str, $cookie_param);
  1123. if ($start > 0) {
  1124. $expires = substr($cookie_str, $start + strlen($cookie_param));
  1125. $expires = substr($expires, 0, strpos($expires, ';'));
  1126. } else {
  1127. $expires = '';
  1128. }
  1129. $cookie_param = 'path=';
  1130. $start = strpos($cookie_str, $cookie_param);
  1131. if ( $start > 0 ) {
  1132. $path = substr($cookie_str, $start + strlen($cookie_param));
  1133. $path = substr($path, 0, strpos($path, ';'));
  1134. } else {
  1135. $path = '/';
  1136. }
  1137. $cookie_param = ';secure;';
  1138. if (strpos($cookie_str, $cookie_param) !== FALSE) {
  1139. $secure = true;
  1140. } else {
  1141. $secure = false;
  1142. }
  1143. $sep_pos = strpos($value_str, '=');
  1144. if ($sep_pos) {
  1145. $name = substr($value_str, 0, $sep_pos);
  1146. $value = substr($value_str, $sep_pos + 1);
  1147. $cookie= array( 'name' => $name,
  1148. 'value' => $value,
  1149. 'domain' => $domain,
  1150. 'path' => $path,
  1151. 'expires' => $expires,
  1152. 'secure' => $secure
  1153. );
  1154. return $cookie;
  1155. }
  1156. return false;
  1157. }
  1158. /**
  1159. * sort out cookies for the current request
  1160. *
  1161. * @param array $cookies array with all cookies
  1162. * @param boolean $secure is the send-content secure or not?
  1163. * @return string for Cookie-HTTP-Header
  1164. * @access private
  1165. */
  1166. function getCookiesForRequest($cookies, $secure=false) {
  1167. $cookie_str = '';
  1168. if ((! is_null($cookies)) && (is_array($cookies))) {
  1169. foreach ($cookies as $cookie) {
  1170. if (! is_array($cookie)) {
  1171. continue;
  1172. }
  1173. $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
  1174. if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
  1175. if (strtotime($cookie['expires']) <= time()) {
  1176. $this->debug('cookie has expired');
  1177. continue;
  1178. }
  1179. }
  1180. if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
  1181. $domain = preg_quote($cookie['domain']);
  1182. if (! preg_match("'.*$domain$'i", $this->host)) {
  1183. $this->debug('cookie has different domain');
  1184. continue;
  1185. }
  1186. }
  1187. if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
  1188. $path = preg_quote($cookie['path']);
  1189. if (! preg_match("'^$path.*'i", $this->path)) {
  1190. $this->debug('cookie is for a different path');
  1191. continue;
  1192. }
  1193. }
  1194. if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
  1195. $this->debug('cookie is secure, transport is not');
  1196. continue;
  1197. }
  1198. $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
  1199. $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
  1200. }
  1201. }
  1202. return $cookie_str;
  1203. }
  1204. }
  1205. ?>