class.soap_transport_http.php 45 KB

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