class.soap_transport_http.php 43 KB

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