root/ext/soap/php_http.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. proxy_authentication
  2. basic_authentication
  3. http_context_headers
  4. http_connect
  5. in_domain
  6. make_http_soap_request
  7. get_http_header_value
  8. get_http_body
  9. get_http_headers

   1 /*
   2   +----------------------------------------------------------------------+
   3   | PHP Version 7                                                        |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 1997-2016 The PHP Group                                |
   6   +----------------------------------------------------------------------+
   7   | This source file is subject to version 3.01 of the PHP license,      |
   8   | that is bundled with this package in the file LICENSE, and is        |
   9   | available through the world-wide-web at the following url:           |
  10   | http://www.php.net/license/3_01.txt                                  |
  11   | If you did not receive a copy of the PHP license and are unable to   |
  12   | obtain it through the world-wide-web, please send a note to          |
  13   | license@php.net so we can mail you a copy immediately.               |
  14   +----------------------------------------------------------------------+
  15   | Authors: Brad Lafountain <rodif_bl@yahoo.com>                        |
  16   |          Shane Caraveo <shane@caraveo.com>                           |
  17   |          Dmitry Stogov <dmitry@zend.com>                             |
  18   +----------------------------------------------------------------------+
  19 */
  20 /* $Id$ */
  21 
  22 #include "php_soap.h"
  23 #include "ext/standard/base64.h"
  24 #include "ext/standard/md5.h"
  25 #include "ext/standard/php_rand.h"
  26 
  27 static char *get_http_header_value(char *headers, char *type);
  28 static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
  29 static zend_string *get_http_headers(php_stream *socketd);
  30 
  31 #define smart_str_append_const(str, const) \
  32         smart_str_appendl(str,const,sizeof(const)-1)
  33 
  34 /* Proxy HTTP Authentication */
  35 int proxy_authentication(zval* this_ptr, smart_str* soap_headers)
  36 {
  37         zval *login, *password;
  38 
  39         if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_login", sizeof("_proxy_login")-1)) != NULL &&
  40             Z_TYPE_P(login) == IS_STRING) {
  41                 zend_string *buf;
  42                 smart_str auth = {0};
  43 
  44                 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
  45                 smart_str_appendc(&auth, ':');
  46                 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_password", sizeof("_proxy_password")-1)) != NULL &&
  47                     Z_TYPE_P(password) == IS_STRING) {
  48                         smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
  49                 }
  50                 smart_str_0(&auth);
  51                 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
  52                 smart_str_append_const(soap_headers, "Proxy-Authorization: Basic ");
  53                 smart_str_appendl(soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
  54                 smart_str_append_const(soap_headers, "\r\n");
  55                 zend_string_release(buf);
  56                 smart_str_free(&auth);
  57                 return 1;
  58         }
  59         return 0;
  60 }
  61 
  62 /* HTTP Authentication */
  63 int basic_authentication(zval* this_ptr, smart_str* soap_headers)
  64 {
  65         zval *login, *password;
  66 
  67         if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
  68             Z_TYPE_P(login) == IS_STRING &&
  69             !zend_hash_str_exists(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) {
  70                 zend_string* buf;
  71                 smart_str auth = {0};
  72 
  73                 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
  74                 smart_str_appendc(&auth, ':');
  75                 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
  76                     Z_TYPE_P(password) == IS_STRING) {
  77                         smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
  78                 }
  79                 smart_str_0(&auth);
  80                 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
  81                 smart_str_append_const(soap_headers, "Authorization: Basic ");
  82                 smart_str_appendl(soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
  83                 smart_str_append_const(soap_headers, "\r\n");
  84                 zend_string_release(buf);
  85                 smart_str_free(&auth);
  86                 return 1;
  87         }
  88         return 0;
  89 }
  90 
  91 /* Additional HTTP headers */
  92 void http_context_headers(php_stream_context* context,
  93                           zend_bool has_authorization,
  94                           zend_bool has_proxy_authorization,
  95                           zend_bool has_cookies,
  96                           smart_str* soap_headers)
  97 {
  98         zval *tmp;
  99 
 100         if (context &&
 101                 (tmp = php_stream_context_get_option(context, "http", "header")) != NULL &&
 102                 Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
 103                 char *s = Z_STRVAL_P(tmp);
 104                 char *p;
 105                 int name_len;
 106 
 107                 while (*s) {
 108                         /* skip leading newlines and spaces */
 109                         while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
 110                                 s++;
 111                         }
 112                         /* extract header name */
 113                         p = s;
 114                         name_len = -1;
 115                         while (*p) {
 116                                 if (*p == ':') {
 117                                         if (name_len < 0) name_len = p - s;
 118                                         break;
 119                                 } else if (*p == ' ' || *p == '\t') {
 120                                         if (name_len < 0) name_len = p - s;
 121                                 } else if (*p == '\r' || *p == '\n') {
 122                                         break;
 123                                 }
 124                                 p++;
 125                         }
 126                         if (*p == ':') {
 127                                 /* extract header value */
 128                                 while (*p && *p != '\r' && *p != '\n') {
 129                                         p++;
 130                                 }
 131                                 /* skip some predefined headers */
 132                                 if ((name_len != sizeof("host")-1 ||
 133                                      strncasecmp(s, "host", sizeof("host")-1) != 0) &&
 134                                     (name_len != sizeof("connection")-1 ||
 135                                      strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
 136                                     (name_len != sizeof("user-agent")-1 ||
 137                                      strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
 138                                     (name_len != sizeof("content-length")-1 ||
 139                                      strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
 140                                     (name_len != sizeof("content-type")-1 ||
 141                                      strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
 142                                     (!has_cookies ||
 143                                      name_len != sizeof("cookie")-1 ||
 144                                      strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
 145                                     (!has_authorization ||
 146                                      name_len != sizeof("authorization")-1 ||
 147                                      strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
 148                                     (!has_proxy_authorization ||
 149                                      name_len != sizeof("proxy-authorization")-1 ||
 150                                      strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
 151                                     /* add header */
 152                                         smart_str_appendl(soap_headers, s, p-s);
 153                                         smart_str_append_const(soap_headers, "\r\n");
 154                                 }
 155                         }
 156                         s = (*p) ? (p + 1) : p;
 157                 }
 158         }
 159 }
 160 
 161 static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, php_stream_context *context, int *use_proxy)
 162 {
 163         php_stream *stream;
 164         zval *proxy_host, *proxy_port, *tmp;
 165         char *host;
 166         char *name;
 167         char *protocol;
 168         zend_long namelen;
 169         int port;
 170         int old_error_reporting;
 171         struct timeval tv;
 172         struct timeval *timeout = NULL;
 173 
 174         if ((proxy_host = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_host", sizeof("_proxy_host")-1)) != NULL &&
 175             Z_TYPE_P(proxy_host) == IS_STRING &&
 176             (proxy_port = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_port", sizeof("_proxy_port")-1)) != NULL &&
 177             Z_TYPE_P(proxy_port) == IS_LONG) {
 178                 host = Z_STRVAL_P(proxy_host);
 179                 port = Z_LVAL_P(proxy_port);
 180                 *use_proxy = 1;
 181         } else {
 182                 host = phpurl->host;
 183                 port = phpurl->port;
 184         }
 185         if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_connection_timeout", sizeof("_connection_timeout")-1)) != NULL &&
 186             Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) > 0) {
 187           tv.tv_sec = Z_LVAL_P(tmp);
 188           tv.tv_usec = 0;
 189                 timeout = &tv;
 190         }
 191 
 192         old_error_reporting = EG(error_reporting);
 193         EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);
 194 
 195         /* Changed ternary operator to an if/else so that additional comparisons can be done on the ssl_method property */
 196         if (use_ssl && !*use_proxy) {
 197                 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method")-1)) != NULL &&
 198                         Z_TYPE_P(tmp) == IS_LONG) {
 199                         /* uses constants declared in soap.c to determine ssl uri protocol */
 200                         switch (Z_LVAL_P(tmp)) {
 201                                 case SOAP_SSL_METHOD_TLS:
 202                                         protocol = "tls";
 203                                         break;
 204 
 205                                 case SOAP_SSL_METHOD_SSLv2:
 206                                         protocol = "sslv2";
 207                                         break;
 208 
 209                                 case SOAP_SSL_METHOD_SSLv3:
 210                                         protocol = "sslv3";
 211                                         break;
 212 
 213                                 case SOAP_SSL_METHOD_SSLv23:
 214                                         protocol = "ssl";
 215                                         break;
 216 
 217                                 default:
 218                                         protocol = "ssl";
 219                                         break;
 220 
 221                         }
 222                 } else {
 223                         protocol = "ssl";
 224                 }
 225         } else {
 226                 protocol = "tcp";
 227         }
 228 
 229         namelen = spprintf(&name, 0, "%s://%s:%d", protocol, host, port);
 230 
 231         stream = php_stream_xport_create(name, namelen,
 232                 REPORT_ERRORS,
 233                 STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
 234                 NULL /*persistent_id*/,
 235                 timeout,
 236                 context,
 237                 NULL, NULL);
 238         efree(name);
 239 
 240         /* SSL & proxy */
 241         if (stream && *use_proxy && use_ssl) {
 242                 smart_str soap_headers = {0};
 243 
 244                 smart_str_append_const(&soap_headers, "CONNECT ");
 245                 smart_str_appends(&soap_headers, phpurl->host);
 246                 smart_str_appendc(&soap_headers, ':');
 247                 smart_str_append_unsigned(&soap_headers, phpurl->port);
 248                 smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
 249                 smart_str_append_const(&soap_headers, "Host: ");
 250                 smart_str_appends(&soap_headers, phpurl->host);
 251                 if (phpurl->port != 80) {
 252                         smart_str_appendc(&soap_headers, ':');
 253                         smart_str_append_unsigned(&soap_headers, phpurl->port);
 254                 }
 255                 smart_str_append_const(&soap_headers, "\r\n");
 256                 proxy_authentication(this_ptr, &soap_headers);
 257                 smart_str_append_const(&soap_headers, "\r\n");
 258                 if (php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s)) != ZSTR_LEN(soap_headers.s)) {
 259                         php_stream_close(stream);
 260                         stream = NULL;
 261                 }
 262                 smart_str_free(&soap_headers);
 263 
 264                 if (stream) {
 265                         zend_string *http_headers = get_http_headers(stream);
 266                         if (http_headers) {
 267                                 zend_string_free(http_headers);
 268                         } else {
 269                                 php_stream_close(stream);
 270                                 stream = NULL;
 271                         }
 272                 }
 273                 /* enable SSL transport layer */
 274                 if (stream) {
 275                         /* if a stream is created without encryption, check to see if SSL method parameter is specified and use
 276                            proper encrypyion method based on constants defined in soap.c */
 277                         int crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
 278                         if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method")-1)) != NULL &&
 279                                 Z_TYPE_P(tmp) == IS_LONG) {
 280                                 switch (Z_LVAL_P(tmp)) {
 281                                         case SOAP_SSL_METHOD_TLS:
 282                                                 crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
 283                                                 break;
 284 
 285                                         case SOAP_SSL_METHOD_SSLv2:
 286                                                 crypto_method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
 287                                                 break;
 288 
 289                                         case SOAP_SSL_METHOD_SSLv3:
 290                                                 crypto_method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
 291                                                 break;
 292 
 293                                         case SOAP_SSL_METHOD_SSLv23:
 294                                                 crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
 295                                                 break;
 296 
 297                                         default:
 298                                                 crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
 299                                                 break;
 300                                 }
 301                         }
 302                         if (php_stream_xport_crypto_setup(stream, crypto_method, NULL) < 0 ||
 303                             php_stream_xport_crypto_enable(stream, 1) < 0) {
 304                                 php_stream_close(stream);
 305                                 stream = NULL;
 306                         }
 307                 }
 308         }
 309 
 310         EG(error_reporting) = old_error_reporting;
 311         return stream;
 312 }
 313 
 314 static int in_domain(const char *host, const char *domain)
 315 {
 316   if (domain[0] == '.') {
 317     int l1 = strlen(host);
 318     int l2 = strlen(domain);
 319     if (l1 > l2) {
 320         return strcmp(host+l1-l2,domain) == 0;
 321     } else {
 322       return 0;
 323     }
 324   } else {
 325     return strcmp(host,domain) == 0;
 326   }
 327 }
 328 
 329 int make_http_soap_request(zval        *this_ptr,
 330                            zend_string *buf,
 331                            char        *location,
 332                            char        *soapaction,
 333                            int          soap_version,
 334                            zval        *return_value)
 335 {
 336         zend_string *request;
 337         smart_str soap_headers = {0};
 338         smart_str soap_headers_z = {0};
 339         int err;
 340         php_url *phpurl = NULL;
 341         php_stream *stream;
 342         zval *trace, *tmp;
 343         int use_proxy = 0;
 344         int use_ssl;
 345         zend_string *http_body;
 346         char *content_type, *http_version, *cookie_itt;
 347         int http_close;
 348         zend_string *http_headers;
 349         char *connection;
 350         int http_1_1;
 351         int http_status;
 352         int content_type_xml = 0;
 353         zend_long redirect_max = 20;
 354         char *content_encoding;
 355         char *http_msg = NULL;
 356         zend_bool old_allow_url_fopen;
 357         php_stream_context *context = NULL;
 358         zend_bool has_authorization = 0;
 359         zend_bool has_proxy_authorization = 0;
 360         zend_bool has_cookies = 0;
 361 
 362         if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) {
 363                 return FALSE;
 364         }
 365 
 366         request = buf;
 367         /* Compress request */
 368         if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "compression", sizeof("compression")-1)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
 369                 int level = Z_LVAL_P(tmp) & 0x0f;
 370                 int kind  = Z_LVAL_P(tmp) & SOAP_COMPRESSION_DEFLATE;
 371 
 372                 if (level > 9) {level = 9;}
 373 
 374           if ((Z_LVAL_P(tmp) & SOAP_COMPRESSION_ACCEPT) != 0) {
 375                         smart_str_append_const(&soap_headers_z,"Accept-Encoding: gzip, deflate\r\n");
 376           }
 377           if (level > 0) {
 378                         zval func;
 379                         zval retval;
 380                         zval params[3];
 381                         int n;
 382 
 383                         ZVAL_STR_COPY(&params[0], buf);
 384                         ZVAL_LONG(&params[1], level);
 385                         if (kind == SOAP_COMPRESSION_DEFLATE) {
 386                                 n = 2;
 387                                 ZVAL_STRING(&func, "gzcompress");
 388                                 smart_str_append_const(&soap_headers_z,"Content-Encoding: deflate\r\n");
 389                         } else {
 390                                 n = 3;
 391                                 ZVAL_STRING(&func, "gzencode");
 392                                 smart_str_append_const(&soap_headers_z,"Content-Encoding: gzip\r\n");
 393                                 ZVAL_LONG(&params[2], 0x1f);
 394                         }
 395                         if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, n, params) == SUCCESS &&
 396                             Z_TYPE(retval) == IS_STRING) {
 397                                 zval_ptr_dtor(&params[0]);
 398                                 zval_ptr_dtor(&func);
 399                                 request = Z_STR(retval);
 400                         } else {
 401                                 zval_ptr_dtor(&params[0]);
 402                                 zval_ptr_dtor(&func);
 403                                 if (request != buf) {
 404                                         zend_string_release(request);
 405                                 }
 406                                 smart_str_free(&soap_headers_z);
 407                                 return FALSE;
 408                         }
 409           }
 410         }
 411 
 412         if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1)) != NULL) {
 413                 php_stream_from_zval_no_verify(stream,tmp);
 414                 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
 415                         use_proxy = Z_LVAL_P(tmp);
 416                 }
 417         } else {
 418                 stream = NULL;
 419         }
 420 
 421         if (location != NULL && location[0] != '\000') {
 422                 phpurl = php_url_parse(location);
 423         }
 424 
 425         if (NULL != (tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr),
 426                         "_stream_context", sizeof("_stream_context")-1))) {
 427                 context = php_stream_context_from_zval(tmp, 0);
 428         }
 429 
 430         if (context &&
 431                 (tmp = php_stream_context_get_option(context, "http", "max_redirects")) != NULL) {
 432                 if (Z_TYPE_P(tmp) != IS_STRING || !is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &redirect_max, NULL, 1)) {
 433                         if (Z_TYPE_P(tmp) == IS_LONG)
 434                                 redirect_max = Z_LVAL_P(tmp);
 435                 }
 436         }
 437 
 438 try_again:
 439         if (phpurl == NULL || phpurl->host == NULL) {
 440           if (phpurl != NULL) {php_url_free(phpurl);}
 441                 if (request != buf) {
 442                         zend_string_release(request);
 443                 }
 444                 add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL);
 445                 smart_str_free(&soap_headers_z);
 446                 return FALSE;
 447         }
 448 
 449         use_ssl = 0;
 450         if (phpurl->scheme != NULL && strcmp(phpurl->scheme, "https") == 0) {
 451                 use_ssl = 1;
 452         } else if (phpurl->scheme == NULL || strcmp(phpurl->scheme, "http") != 0) {
 453                 php_url_free(phpurl);
 454                 if (request != buf) {
 455                         zend_string_release(request);
 456                 }
 457                 add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL);
 458                 smart_str_free(&soap_headers_z);
 459                 return FALSE;
 460         }
 461 
 462         old_allow_url_fopen = PG(allow_url_fopen);
 463         PG(allow_url_fopen) = 1;
 464         if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY) == NULL) {
 465                 php_url_free(phpurl);
 466                 if (request != buf) {
 467                         zend_string_release(request);
 468                 }
 469                 add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL);
 470                 PG(allow_url_fopen) = old_allow_url_fopen;
 471                 smart_str_free(&soap_headers_z);
 472                 return FALSE;
 473         }
 474 
 475         if (phpurl->port == 0) {
 476                 phpurl->port = use_ssl ? 443 : 80;
 477         }
 478 
 479         /* Check if request to the same host */
 480         if (stream != NULL) {
 481           php_url *orig;
 482                 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1)) != NULL &&
 483                     (orig = (php_url *) zend_fetch_resource_ex(tmp, "httpurl", le_url)) != NULL &&
 484                     ((use_proxy && !use_ssl) ||
 485                      (((use_ssl && orig->scheme != NULL && strcmp(orig->scheme, "https") == 0) ||
 486                       (!use_ssl && orig->scheme == NULL) ||
 487                       (!use_ssl && strcmp(orig->scheme, "https") != 0)) &&
 488                      strcmp(orig->host, phpurl->host) == 0 &&
 489                      orig->port == phpurl->port))) {
 490     } else {
 491                         php_stream_close(stream);
 492                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
 493                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
 494                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
 495                         stream = NULL;
 496                         use_proxy = 0;
 497     }
 498         }
 499 
 500         /* Check if keep-alive connection is still opened */
 501         if (stream != NULL && php_stream_eof(stream)) {
 502                 php_stream_close(stream);
 503                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
 504                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
 505                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
 506                 stream = NULL;
 507                 use_proxy = 0;
 508         }
 509 
 510         if (!stream) {
 511                 stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy);
 512                 if (stream) {
 513                         php_stream_auto_cleanup(stream);
 514                         add_property_resource(this_ptr, "httpsocket", stream->res);
 515                         GC_REFCOUNT(stream->res)++;
 516                         add_property_long(this_ptr, "_use_proxy", use_proxy);
 517                 } else {
 518                         php_url_free(phpurl);
 519                         if (request != buf) {
 520                                 zend_string_release(request);
 521                         }
 522                         add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL);
 523                         PG(allow_url_fopen) = old_allow_url_fopen;
 524                         smart_str_free(&soap_headers_z);
 525                         return FALSE;
 526                 }
 527         }
 528         PG(allow_url_fopen) = old_allow_url_fopen;
 529 
 530         if (stream) {
 531                 zval *cookies, *login, *password;
 532                 zend_resource *ret = zend_register_resource(phpurl, le_url);
 533 
 534                 add_property_resource(this_ptr, "httpurl", ret);
 535                 GC_REFCOUNT(ret)++;
 536                 /*zend_list_addref(ret);*/
 537 
 538                 if (context &&
 539                     (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL &&
 540                     Z_TYPE_P(tmp) == IS_DOUBLE &&
 541                     Z_DVAL_P(tmp) == 1.0) {
 542                         http_1_1 = 0;
 543                 } else {
 544                         http_1_1 = 1;
 545                 }
 546 
 547                 smart_str_append_const(&soap_headers, "POST ");
 548                 if (use_proxy && !use_ssl) {
 549                         smart_str_appends(&soap_headers, phpurl->scheme);
 550                         smart_str_append_const(&soap_headers, "://");
 551                         smart_str_appends(&soap_headers, phpurl->host);
 552                         smart_str_appendc(&soap_headers, ':');
 553                         smart_str_append_unsigned(&soap_headers, phpurl->port);
 554                 }
 555                 if (phpurl->path) {
 556                         smart_str_appends(&soap_headers, phpurl->path);
 557                 } else {
 558                         smart_str_appendc(&soap_headers, '/');
 559                 }
 560                 if (phpurl->query) {
 561                         smart_str_appendc(&soap_headers, '?');
 562                         smart_str_appends(&soap_headers, phpurl->query);
 563                 }
 564                 if (phpurl->fragment) {
 565                         smart_str_appendc(&soap_headers, '#');
 566                         smart_str_appends(&soap_headers, phpurl->fragment);
 567                 }
 568                 if (http_1_1) {
 569                         smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
 570                 } else {
 571                         smart_str_append_const(&soap_headers, " HTTP/1.0\r\n");
 572                 }
 573                 smart_str_append_const(&soap_headers, "Host: ");
 574                 smart_str_appends(&soap_headers, phpurl->host);
 575                 if (phpurl->port != (use_ssl?443:80)) {
 576                         smart_str_appendc(&soap_headers, ':');
 577                         smart_str_append_unsigned(&soap_headers, phpurl->port);
 578                 }
 579                 if (!http_1_1 ||
 580                         ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_keep_alive", sizeof("_keep_alive")-1)) != NULL &&
 581                          (Z_TYPE_P(tmp) == IS_FALSE || (Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) == 0)))) {
 582                         smart_str_append_const(&soap_headers, "\r\n"
 583                                 "Connection: close\r\n");
 584                 } else {
 585                         smart_str_append_const(&soap_headers, "\r\n"
 586                                 "Connection: Keep-Alive\r\n");
 587                 }
 588                 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_user_agent", sizeof("_user_agent")-1)) != NULL &&
 589                     Z_TYPE_P(tmp) == IS_STRING) {
 590                         if (Z_STRLEN_P(tmp) > 0) {
 591                                 smart_str_append_const(&soap_headers, "User-Agent: ");
 592                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 593                                 smart_str_append_const(&soap_headers, "\r\n");
 594                         }
 595                 } else if (context &&
 596                            (tmp = php_stream_context_get_option(context, "http", "user_agent")) != NULL &&
 597                            Z_TYPE_P(tmp) == IS_STRING) {
 598                         if (Z_STRLEN_P(tmp) > 0) {
 599                                 smart_str_append_const(&soap_headers, "User-Agent: ");
 600                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 601                                 smart_str_append_const(&soap_headers, "\r\n");
 602                         }
 603                 } else if (FG(user_agent)) {
 604                         smart_str_append_const(&soap_headers, "User-Agent: ");
 605                         smart_str_appends(&soap_headers, FG(user_agent));
 606                         smart_str_append_const(&soap_headers, "\r\n");
 607                 } else {
 608                         smart_str_append_const(&soap_headers, "User-Agent: PHP-SOAP/"PHP_VERSION"\r\n");
 609                 }
 610 
 611                 smart_str_append_smart_str(&soap_headers, &soap_headers_z);
 612 
 613                 if (soap_version == SOAP_1_2) {
 614                         smart_str_append_const(&soap_headers,"Content-Type: application/soap+xml; charset=utf-8");
 615                         if (soapaction) {
 616                                 smart_str_append_const(&soap_headers,"; action=\"");
 617                                 smart_str_appends(&soap_headers, soapaction);
 618                                 smart_str_append_const(&soap_headers,"\"");
 619                         }
 620                         smart_str_append_const(&soap_headers,"\r\n");
 621                 } else {
 622                         smart_str_append_const(&soap_headers,"Content-Type: text/xml; charset=utf-8\r\n");
 623                         if (soapaction) {
 624                                 smart_str_append_const(&soap_headers, "SOAPAction: \"");
 625                                 smart_str_appends(&soap_headers, soapaction);
 626                                 smart_str_append_const(&soap_headers, "\"\r\n");
 627                         }
 628                 }
 629                 smart_str_append_const(&soap_headers,"Content-Length: ");
 630                 smart_str_append_long(&soap_headers, request->len);
 631                 smart_str_append_const(&soap_headers, "\r\n");
 632 
 633                 /* HTTP Authentication */
 634                 if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
 635                     Z_TYPE_P(login) == IS_STRING) {
 636                         zval *digest;
 637 
 638                         has_authorization = 1;
 639                         if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
 640                                 if (Z_TYPE_P(digest) == IS_ARRAY) {
 641                                         char          HA1[33], HA2[33], response[33], cnonce[33], nc[9];
 642                                         PHP_MD5_CTX   md5ctx;
 643                                         unsigned char hash[16];
 644 
 645                                         PHP_MD5Init(&md5ctx);
 646                                         snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, php_rand());
 647                                         PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
 648                                         PHP_MD5Final(hash, &md5ctx);
 649                                         make_digest(cnonce, hash);
 650 
 651                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
 652                                             Z_TYPE_P(tmp) == IS_LONG) {
 653                                                 Z_LVAL_P(tmp)++;
 654                                                 snprintf(nc, sizeof(nc), "%08ld", Z_LVAL_P(tmp));
 655                                         } else {
 656                                                 add_assoc_long(digest, "nc", 1);
 657                                                 strcpy(nc, "00000001");
 658                                         }
 659 
 660                                         PHP_MD5Init(&md5ctx);
 661                                         PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(login), Z_STRLEN_P(login));
 662                                         PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 663                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
 664                                             Z_TYPE_P(tmp) == IS_STRING) {
 665                                                 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 666                                         }
 667                                         PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 668                                         if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
 669                                             Z_TYPE_P(password) == IS_STRING) {
 670                                                 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(password), Z_STRLEN_P(password));
 671                                         }
 672                                         PHP_MD5Final(hash, &md5ctx);
 673                                         make_digest(HA1, hash);
 674                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
 675                                             Z_TYPE_P(tmp) == IS_STRING &&
 676                                             Z_STRLEN_P(tmp) == sizeof("md5-sess")-1 &&
 677                                             stricmp(Z_STRVAL_P(tmp), "md5-sess") == 0) {
 678                                                 PHP_MD5Init(&md5ctx);
 679                                                 PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
 680                                                 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 681                                                 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
 682                                                     Z_TYPE_P(tmp) == IS_STRING) {
 683                                                         PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 684                                                 }
 685                                                 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 686                                                 PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
 687                                                 PHP_MD5Final(hash, &md5ctx);
 688                                                 make_digest(HA1, hash);
 689                                         }
 690 
 691                                         PHP_MD5Init(&md5ctx);
 692                                         PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1);
 693                                         if (phpurl->path) {
 694                                                 PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->path, strlen(phpurl->path));
 695                                         } else {
 696                                                 PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1);
 697                                         }
 698                                         if (phpurl->query) {
 699                                                 PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1);
 700                                                 PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->query, strlen(phpurl->query));
 701                                         }
 702 
 703                                         PHP_MD5Final(hash, &md5ctx);
 704                                         make_digest(HA2, hash);
 705 
 706                                         PHP_MD5Init(&md5ctx);
 707                                         PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
 708                                         PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 709                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
 710                                             Z_TYPE_P(tmp) == IS_STRING) {
 711                                                 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 712                                         }
 713                                         PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 714                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
 715                                             Z_TYPE_P(tmp) == IS_STRING) {
 716                                                 PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8);
 717                                                 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 718                                                 PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
 719                                                 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 720                                                 /* TODO: Support for qop="auth-int" */
 721                                                 PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
 722                                                 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
 723                                         }
 724                                         PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32);
 725                                         PHP_MD5Final(hash, &md5ctx);
 726                                         make_digest(response, hash);
 727 
 728                                         smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
 729                                         smart_str_appendl(&soap_headers, Z_STRVAL_P(login), Z_STRLEN_P(login));
 730                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
 731                                             Z_TYPE_P(tmp) == IS_STRING) {
 732                                                 smart_str_append_const(&soap_headers, "\", realm=\"");
 733                                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 734                                         }
 735                                 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
 736                                             Z_TYPE_P(tmp) == IS_STRING) {
 737                                                 smart_str_append_const(&soap_headers, "\", nonce=\"");
 738                                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 739                                         }
 740                                         smart_str_append_const(&soap_headers, "\", uri=\"");
 741                                         if (phpurl->path) {
 742                                                 smart_str_appends(&soap_headers, phpurl->path);
 743                                         } else {
 744                                                 smart_str_appendc(&soap_headers, '/');
 745                                         }
 746                                         if (phpurl->query) {
 747                                                 smart_str_appendc(&soap_headers, '?');
 748                                                 smart_str_appends(&soap_headers, phpurl->query);
 749                                         }
 750                                         if (phpurl->fragment) {
 751                                                 smart_str_appendc(&soap_headers, '#');
 752                                                 smart_str_appends(&soap_headers, phpurl->fragment);
 753                                         }
 754                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
 755                                             Z_TYPE_P(tmp) == IS_STRING) {
 756                                         /* TODO: Support for qop="auth-int" */
 757                                                 smart_str_append_const(&soap_headers, "\", qop=\"auth");
 758                                                 smart_str_append_const(&soap_headers, "\", nc=\"");
 759                                                 smart_str_appendl(&soap_headers, nc, 8);
 760                                                 smart_str_append_const(&soap_headers, "\", cnonce=\"");
 761                                                 smart_str_appendl(&soap_headers, cnonce, 8);
 762                                         }
 763                                         smart_str_append_const(&soap_headers, "\", response=\"");
 764                                         smart_str_appendl(&soap_headers, response, 32);
 765                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "opaque", sizeof("opaque")-1)) != NULL &&
 766                                             Z_TYPE_P(tmp) == IS_STRING) {
 767                                                 smart_str_append_const(&soap_headers, "\", opaque=\"");
 768                                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 769                                         }
 770                                         if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
 771                                                 Z_TYPE_P(tmp) == IS_STRING) {
 772                                                 smart_str_append_const(&soap_headers, "\", algorithm=\"");
 773                                                 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
 774                                         }
 775                                         smart_str_append_const(&soap_headers, "\"\r\n");
 776                                 }
 777                         } else {
 778                                 zend_string *buf;
 779 
 780                                 smart_str auth = {0};
 781                                 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
 782                                 smart_str_appendc(&auth, ':');
 783                                 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
 784                                     Z_TYPE_P(password) == IS_STRING) {
 785                                         smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
 786                                 }
 787                                 smart_str_0(&auth);
 788                                 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
 789                                 smart_str_append_const(&soap_headers, "Authorization: Basic ");
 790                                 smart_str_appendl(&soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
 791                                 smart_str_append_const(&soap_headers, "\r\n");
 792                                 zend_string_release(buf);
 793                                 smart_str_free(&auth);
 794                         }
 795                 }
 796 
 797                 /* Proxy HTTP Authentication */
 798                 if (use_proxy && !use_ssl) {
 799                         has_proxy_authorization = proxy_authentication(this_ptr, &soap_headers);
 800                 }
 801 
 802                 /* Send cookies along with request */
 803                 if ((cookies = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1)) != NULL &&
 804                     Z_TYPE_P(cookies) == IS_ARRAY) {
 805                         zval *data;
 806                         zend_string *key;
 807                         int i, n;
 808 
 809                         has_cookies = 1;
 810                         n = zend_hash_num_elements(Z_ARRVAL_P(cookies));
 811                         if (n > 0) {
 812                                 zend_hash_internal_pointer_reset(Z_ARRVAL_P(cookies));
 813                                 smart_str_append_const(&soap_headers, "Cookie: ");
 814                                 for (i = 0; i < n; i++) {
 815                                         zend_ulong numindx;
 816                                         int res = zend_hash_get_current_key(Z_ARRVAL_P(cookies), &key, &numindx);
 817                                         data = zend_hash_get_current_data(Z_ARRVAL_P(cookies));
 818                                         
 819                                         if (res == HASH_KEY_IS_STRING && Z_TYPE_P(data) == IS_ARRAY) {
 820                                           zval *value;
 821 
 822                                                 if ((value = zend_hash_index_find(Z_ARRVAL_P(data), 0)) != NULL &&
 823                                                     Z_TYPE_P(value) == IS_STRING) {
 824                                                   zval *tmp;
 825                                                   if (((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 1)) == NULL ||
 826                                                            Z_TYPE_P(tmp) != IS_STRING ||
 827                                                        strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_P(tmp),Z_STRLEN_P(tmp)) == 0) &&
 828                                                       ((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 2)) == NULL ||
 829                                                            Z_TYPE_P(tmp) != IS_STRING ||
 830                                                        in_domain(phpurl->host,Z_STRVAL_P(tmp))) &&
 831                                                       (use_ssl || (tmp = zend_hash_index_find(Z_ARRVAL_P(data), 3)) == NULL)) {
 832                                                                 smart_str_append(&soap_headers, key);
 833                                                                 smart_str_appendc(&soap_headers, '=');
 834                                                                 smart_str_append(&soap_headers, Z_STR_P(value));
 835                                                                 smart_str_appendc(&soap_headers, ';');
 836                                                         }
 837                                                 }
 838                                         }
 839                                         zend_hash_move_forward(Z_ARRVAL_P(cookies));
 840                                 }
 841                                 smart_str_append_const(&soap_headers, "\r\n");
 842                         }
 843                 }
 844 
 845                 http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers);
 846 
 847                 smart_str_append_const(&soap_headers, "\r\n");
 848                 smart_str_0(&soap_headers);
 849                 if ((trace = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace")-1)) != NULL &&
 850                     (Z_TYPE_P(trace) == IS_TRUE || (Z_TYPE_P(trace) == IS_LONG && Z_LVAL_P(trace) != 0))) {
 851                         add_property_stringl(this_ptr, "__last_request_headers", ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
 852                 }
 853                 smart_str_appendl(&soap_headers, request->val, request->len);
 854                 smart_str_0(&soap_headers);
 855 
 856                 err = php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
 857                 if (err != ZSTR_LEN(soap_headers.s)) {
 858                         if (request != buf) {
 859                                 zend_string_release(request);
 860                         }
 861                         php_stream_close(stream);
 862                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
 863                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
 864                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
 865                         add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL);
 866                         smart_str_free(&soap_headers_z);
 867                         return FALSE;
 868                 }
 869                 smart_str_free(&soap_headers);
 870         } else {
 871                 add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL);
 872                 smart_str_free(&soap_headers_z);
 873                 return FALSE;
 874         }
 875 
 876         if (!return_value) {
 877                 php_stream_close(stream);
 878                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
 879                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
 880                 smart_str_free(&soap_headers_z);
 881                 return TRUE;
 882         }
 883 
 884         do {
 885                 http_headers = get_http_headers(stream);
 886                 if (!http_headers) {
 887                         if (request != buf) {
 888                                 zend_string_release(request);
 889                         }
 890                         php_stream_close(stream);
 891                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
 892                         zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
 893                         add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL);
 894                         smart_str_free(&soap_headers_z);
 895                         return FALSE;
 896                 }
 897 
 898                 if ((trace = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace")-1)) != NULL &&
 899                     (Z_TYPE_P(trace) == IS_TRUE || (Z_TYPE_P(trace) == IS_LONG && Z_LVAL_P(trace) != 0))) {
 900                         add_property_str(this_ptr, "__last_response_headers", zend_string_copy(http_headers));
 901                 }
 902 
 903                 /* Check to see what HTTP status was sent */
 904                 http_1_1 = 0;
 905                 http_status = 0;
 906                 http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/");
 907                 if (http_version) {
 908                         char *tmp;
 909 
 910                         if (!strncmp(http_version,"1.1", 3)) {
 911                                 http_1_1 = 1;
 912                         }
 913 
 914                         tmp = strstr(http_version," ");
 915                         if (tmp != NULL) {
 916                                 tmp++;
 917                                 http_status = atoi(tmp);
 918                         }
 919                         tmp = strstr(tmp," ");
 920                         if (tmp != NULL) {
 921                                 tmp++;
 922                                 if (http_msg) {
 923                                         efree(http_msg);
 924                                 }
 925                                 http_msg = estrdup(tmp);
 926                         }
 927                         efree(http_version);
 928 
 929                         /* Try and get headers again */
 930                         if (http_status == 100) {
 931                                 zend_string_release(http_headers);
 932                         }
 933                 }
 934         } while (http_status == 100);
 935 
 936         /* Grab and send back every cookie */
 937 
 938         /* Not going to worry about Path: because
 939            we shouldn't be changing urls so path dont
 940            matter too much
 941         */
 942         cookie_itt = strstr(ZSTR_VAL(http_headers), "Set-Cookie: ");
 943         while (cookie_itt) {
 944                 char *cookie;
 945                 char *eqpos, *sempos;
 946                 zval *cookies;
 947 
 948                 if ((cookies = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1)) == NULL ||
 949                     Z_TYPE_P(cookies) != IS_ARRAY) {
 950                         zval tmp_cookies;
 951                         array_init(&tmp_cookies);
 952                         cookies = zend_hash_str_update(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1, &tmp_cookies);
 953                 }
 954 
 955                 cookie = get_http_header_value(cookie_itt,"Set-Cookie: ");
 956 
 957                 eqpos = strstr(cookie, "=");
 958                 sempos = strstr(cookie, ";");
 959                 if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
 960                         smart_str name = {0};
 961                         int cookie_len;
 962                         zval zcookie;
 963 
 964                         if (sempos != NULL) {
 965                                 cookie_len = sempos-(eqpos+1);
 966                         } else {
 967                                 cookie_len = strlen(cookie)-(eqpos-cookie)-1;
 968                         }
 969 
 970                         smart_str_appendl(&name, cookie, eqpos - cookie);
 971                         smart_str_0(&name);
 972 
 973                         array_init(&zcookie);
 974                         add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len);
 975 
 976                         if (sempos != NULL) {
 977                                 char *options = cookie + cookie_len+1;
 978                                 while (*options) {
 979                                         while (*options == ' ') {options++;}
 980                                         sempos = strstr(options, ";");
 981                                         if (strstr(options,"path=") == options) {
 982                                                 eqpos = options + sizeof("path=")-1;
 983                                                 add_index_stringl(&zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
 984                                         } else if (strstr(options,"domain=") == options) {
 985                                                 eqpos = options + sizeof("domain=")-1;
 986                                                 add_index_stringl(&zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
 987                                         } else if (strstr(options,"secure") == options) {
 988                                                 add_index_bool(&zcookie, 3, 1);
 989                                         }
 990                                         if (sempos != NULL) {
 991                                                 options = sempos+1;
 992                                         } else {
 993                                           break;
 994                                         }
 995                                 }
 996                         }
 997                         if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 1)) {
 998                                 char *t = phpurl->path?phpurl->path:"/";
 999                                 char *c = strrchr(t, '/');
1000                                 if (c) {
1001                                         add_index_stringl(&zcookie, 1, t, c-t);
1002                                 }
1003                         }
1004                         if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 2)) {
1005                                 add_index_string(&zcookie, 2, phpurl->host);
1006                         }
1007 
1008                         zend_symtable_update(Z_ARRVAL_P(cookies), name.s, &zcookie);
1009                         smart_str_free(&name);
1010                 }
1011 
1012                 cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: ");
1013                 efree(cookie);
1014         }
1015 
1016         /* See if the server requested a close */
1017         if (http_1_1) {
1018                 http_close = FALSE;
1019                 if (use_proxy && !use_ssl) {
1020                         connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1021                         if (connection) {
1022                                 if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1023                                         http_close = TRUE;
1024                                 }
1025                                 efree(connection);
1026                         }
1027                 }
1028                 if (http_close == FALSE) {
1029                         connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1030                         if (connection) {
1031                                 if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1032                                         http_close = TRUE;
1033                                 }
1034                                 efree(connection);
1035                         }
1036                 }
1037         } else {
1038                 http_close = TRUE;
1039                 if (use_proxy && !use_ssl) {
1040                         connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1041                         if (connection) {
1042                                 if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1043                                         http_close = FALSE;
1044                                 }
1045                                 efree(connection);
1046                         }
1047                 }
1048                 if (http_close == TRUE) {
1049                         connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1050                         if (connection) {
1051                                 if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1052                                         http_close = FALSE;
1053                                 }
1054                                 efree(connection);
1055                         }
1056                 }
1057         }
1058 
1059 
1060         http_body = get_http_body(stream, http_close, ZSTR_VAL(http_headers));
1061         if (!http_body) {
1062                 if (request != buf) {
1063                         zend_string_release(request);
1064                 }
1065                 php_stream_close(stream);
1066                 zend_string_release(http_headers);
1067                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
1068                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
1069                 add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL);
1070                 if (http_msg) {
1071                         efree(http_msg);
1072                 }
1073                 smart_str_free(&soap_headers_z);
1074                 return FALSE;
1075         }
1076 
1077         if (request != buf) {
1078                 zend_string_release(request);
1079         }
1080 
1081         if (http_close) {
1082                 php_stream_close(stream);
1083                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
1084                 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
1085                 stream = NULL;
1086         }
1087 
1088         /* Process HTTP status codes */
1089         if (http_status >= 300 && http_status < 400) {
1090                 char *loc;
1091 
1092                 if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location: ")) != NULL) {
1093                         php_url *new_url  = php_url_parse(loc);
1094 
1095                         if (new_url != NULL) {
1096                                 zend_string_release(http_headers);
1097                                 zend_string_release(http_body);
1098                                 efree(loc);
1099                                 if (new_url->scheme == NULL && new_url->path != NULL) {
1100                                         new_url->scheme = phpurl->scheme ? estrdup(phpurl->scheme) : NULL;
1101                                         new_url->host = phpurl->host ? estrdup(phpurl->host) : NULL;
1102                                         new_url->port = phpurl->port;
1103                                         if (new_url->path && new_url->path[0] != '/') {
1104                                                 if (phpurl->path) {
1105                                                         char *t = phpurl->path;
1106                                                         char *p = strrchr(t, '/');
1107                                                         if (p) {
1108                                                                 char *s = emalloc((p - t) + strlen(new_url->path) + 2);
1109                                                                 strncpy(s, t, (p - t) + 1);
1110                                                                 s[(p - t) + 1] = 0;
1111                                                                 strcat(s, new_url->path);
1112                                                                 efree(new_url->path);
1113                                                                 new_url->path = s;
1114                                                         }
1115                                                 } else {
1116                                                         char *s = emalloc(strlen(new_url->path) + 2);
1117                                                         s[0] = '/'; s[1] = 0;
1118                                                         strcat(s, new_url->path);
1119                                                         efree(new_url->path);
1120                                                         new_url->path = s;
1121                                                 }
1122                                         }
1123                                 }
1124                                 phpurl = new_url;
1125 
1126                                 if (--redirect_max < 1) {
1127                                         add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL);
1128                                         smart_str_free(&soap_headers_z);
1129                                         return FALSE;
1130                                 }
1131 
1132                                 goto try_again;
1133                         }
1134                 }
1135         } else if (http_status == 401) {
1136                 /* Digest authentication */
1137                 zval *digest, *login, *password;
1138                 char *auth = get_http_header_value(ZSTR_VAL(http_headers), "WWW-Authenticate: ");
1139 
1140                 if (auth &&
1141                                 strstr(auth, "Digest") == auth &&
1142                     ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) == NULL ||
1143                      Z_TYPE_P(digest) != IS_ARRAY) &&
1144                     (login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
1145                     Z_TYPE_P(login) == IS_STRING &&
1146                     (password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
1147                     Z_TYPE_P(password) == IS_STRING) {
1148                         char *s;
1149                         zval digest;
1150 
1151                         ZVAL_UNDEF(&digest);
1152                         s = auth + sizeof("Digest")-1;
1153                         while (*s != '\0') {
1154                                 char *name, *val;
1155                                 while (*s == ' ') ++s;
1156                                 name = s;
1157                                 while (*s != '\0' && *s != '=') ++s;
1158                                 if (*s == '=') {
1159                                         *s = '\0';
1160                                         ++s;
1161                                         if (*s == '"') {
1162                                                 ++s;
1163                                                 val = s;
1164                                                 while (*s != '\0' && *s != '"') ++s;
1165                                         } else {
1166                                                 val = s;
1167                                                 while (*s != '\0' && *s != ' ' && *s != ',') ++s;
1168                                         }
1169                                         if (*s != '\0') {
1170                                                 if (*s != ',') {
1171                                                         *s = '\0';
1172                                                         ++s;
1173                                                         while (*s != '\0' && *s != ',') ++s;
1174                                                         if (*s != '\0') ++s;
1175                                                 } else {
1176                                                         *s = '\0';
1177                                                         ++s;
1178                                                 }
1179                                         }
1180                                         if (Z_TYPE(digest) == IS_UNDEF) {
1181                                                 array_init(&digest);
1182                                         }
1183                                         add_assoc_string(&digest, name, val);
1184                                 }
1185                         }
1186 
1187                         if (Z_TYPE(digest) != IS_UNDEF) {
1188                                 php_url *new_url  = emalloc(sizeof(php_url));
1189 
1190                                 Z_DELREF(digest);
1191                                 add_property_zval_ex(this_ptr, "_digest", sizeof("_digest")-1, &digest);
1192 
1193                                 *new_url = *phpurl;
1194                                 if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme);
1195                                 if (phpurl->user) phpurl->user = estrdup(phpurl->user);
1196                                 if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass);
1197                                 if (phpurl->host) phpurl->host = estrdup(phpurl->host);
1198                                 if (phpurl->path) phpurl->path = estrdup(phpurl->path);
1199                                 if (phpurl->query) phpurl->query = estrdup(phpurl->query);
1200                                 if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment);
1201                                 phpurl = new_url;
1202 
1203                                 efree(auth);
1204                                 zend_string_release(http_headers);
1205                                 zend_string_release(http_body);
1206 
1207                                 goto try_again;
1208                         }
1209                 }
1210                 if (auth) efree(auth);
1211         }
1212         smart_str_free(&soap_headers_z);
1213 
1214         /* Check and see if the server even sent a xml document */
1215         content_type = get_http_header_value(ZSTR_VAL(http_headers), "Content-Type: ");
1216         if (content_type) {
1217                 char *pos = NULL;
1218                 int cmplen;
1219                 pos = strstr(content_type,";");
1220                 if (pos != NULL) {
1221                         cmplen = pos - content_type;
1222                 } else {
1223                         cmplen = strlen(content_type);
1224                 }
1225                 if (strncmp(content_type, "text/xml", cmplen) == 0 ||
1226                     strncmp(content_type, "application/soap+xml", cmplen) == 0) {
1227                         content_type_xml = 1;
1228 /*
1229                         if (strncmp(http_body, "<?xml", 5)) {
1230                                 zval *err;
1231                                 MAKE_STD_ZVAL(err);
1232                                 ZVAL_STRINGL(err, http_body, http_body_size, 1);
1233                                 add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err);
1234                                 efree(content_type);
1235                                 zend_string_release(http_headers);
1236                                 efree(http_body);
1237                                 return FALSE;
1238                         }
1239 */
1240                 }
1241                 efree(content_type);
1242         }
1243 
1244         /* Decompress response */
1245         content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding: ");
1246         if (content_encoding) {
1247                 zval func;
1248                 zval retval;
1249                 zval params[1];
1250 
1251                 if ((strcmp(content_encoding,"gzip") == 0 ||
1252                      strcmp(content_encoding,"x-gzip") == 0) &&
1253                      zend_hash_str_exists(EG(function_table), "gzinflate", sizeof("gzinflate")-1)) {
1254                         ZVAL_STRING(&func, "gzinflate");
1255                         ZVAL_STRINGL(&params[0], http_body->val+10, http_body->len-10);
1256                 } else if (strcmp(content_encoding,"deflate") == 0 &&
1257                            zend_hash_str_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1)) {
1258                         ZVAL_STRING(&func, "gzuncompress");
1259                         ZVAL_STR_COPY(&params[0], http_body);
1260                 } else {
1261                         efree(content_encoding);
1262                         zend_string_release(http_headers);
1263                         zend_string_release(http_body);
1264                         if (http_msg) {
1265                                 efree(http_msg);
1266                         }
1267                         add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL);
1268                         return FALSE;
1269                 }
1270                 if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS &&
1271                     Z_TYPE(retval) == IS_STRING) {
1272                         zval_ptr_dtor(&params[0]);
1273                         zval_ptr_dtor(&func);
1274                         zend_string_release(http_body);
1275                         ZVAL_COPY_VALUE(return_value, &retval);
1276                 } else {
1277                         zval_ptr_dtor(&params[0]);
1278                         zval_ptr_dtor(&func);
1279                         efree(content_encoding);
1280                         zend_string_release(http_headers);
1281                         zend_string_release(http_body);
1282                         add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL);
1283                         if (http_msg) {
1284                                 efree(http_msg);
1285                         }
1286                         return FALSE;
1287                 }
1288                 efree(content_encoding);
1289         } else {
1290                 ZVAL_STR(return_value, http_body);
1291         }
1292 
1293         zend_string_release(http_headers);
1294 
1295         if (http_status >= 400) {
1296                 int error = 0;
1297 
1298                 if (Z_STRLEN_P(return_value) == 0) {
1299                         error = 1;
1300                 } else if (Z_STRLEN_P(return_value) > 0) {
1301                         if (!content_type_xml) {
1302                                 char *s = Z_STRVAL_P(return_value);
1303 
1304                                 while (*s != '\0' && *s < ' ') {
1305                                         s++;
1306                                 }
1307                                 if (strncmp(s, "<?xml", 5)) {
1308                                         error = 1;
1309                                 }
1310                         }
1311                 }
1312 
1313                 if (error) {
1314                         zval_ptr_dtor(return_value);
1315                         ZVAL_UNDEF(return_value);
1316                         add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL);
1317                         efree(http_msg);
1318                         return FALSE;
1319                 }
1320         }
1321 
1322         if (http_msg) {
1323                 efree(http_msg);
1324         }
1325 
1326         return TRUE;
1327 }
1328 
1329 static char *get_http_header_value(char *headers, char *type)
1330 {
1331         char *pos, *tmp = NULL;
1332         int typelen, headerslen;
1333 
1334         typelen = strlen(type);
1335         headerslen = strlen(headers);
1336 
1337         /* header `titles' can be lower case, or any case combination, according
1338          * to the various RFC's. */
1339         pos = headers;
1340         do {
1341                 /* start of buffer or start of line */
1342                 if (strncasecmp(pos, type, typelen) == 0) {
1343                         char *eol;
1344 
1345                         /* match */
1346                         tmp = pos + typelen;
1347                         eol = strchr(tmp, '\n');
1348                         if (eol == NULL) {
1349                                 eol = headers + headerslen;
1350                         } else if (eol > tmp && *(eol-1) == '\r') {
1351                                 eol--;
1352                         }
1353                         return estrndup(tmp, eol - tmp);
1354                 }
1355 
1356                 /* find next line */
1357                 pos = strchr(pos, '\n');
1358                 if (pos) {
1359                         pos++;
1360                 }
1361 
1362         } while (pos);
1363 
1364         return NULL;
1365 }
1366 
1367 static zend_string* get_http_body(php_stream *stream, int close, char *headers)
1368 {
1369         zend_string *http_buf = NULL;
1370         char *header;
1371         int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1372 
1373         if (!close) {
1374                 header = get_http_header_value(headers, "Connection: ");
1375                 if (header) {
1376                         if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1377                         efree(header);
1378                 }
1379         }
1380         header = get_http_header_value(headers, "Transfer-Encoding: ");
1381         if (header) {
1382                 if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1383                 efree(header);
1384         }
1385         header = get_http_header_value(headers, "Content-Length: ");
1386         if (header) {
1387                 header_length = atoi(header);
1388                 efree(header);
1389                 if (!header_length && !header_chunked) {
1390                         /* Empty response */
1391                         return ZSTR_EMPTY_ALLOC();
1392                 }
1393         }
1394 
1395         if (header_chunked) {
1396                 char ch, done, headerbuf[8192];
1397 
1398                 done = FALSE;
1399 
1400                 while (!done) {
1401                         int buf_size = 0;
1402 
1403                         php_stream_gets(stream, headerbuf, sizeof(headerbuf));
1404                         if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
1405                                 if (buf_size > 0) {
1406                                         int len_size = 0;
1407 
1408                                         if (http_buf_size + buf_size + 1 < 0) {
1409                                                 if (http_buf) {
1410                                                         zend_string_release(http_buf);
1411                                                 }
1412                                                 return NULL;
1413                                         }
1414 
1415                                         if (http_buf) {
1416                                                 http_buf = zend_string_realloc(http_buf, http_buf_size + buf_size, 0);
1417                                         } else {
1418                                                 http_buf = zend_string_alloc(buf_size, 0);
1419                                         }
1420 
1421                                         while (len_size < buf_size) {
1422                                                 int len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size);
1423                                                 if (len_read <= 0) {
1424                                                         /* Error or EOF */
1425                                                         done = TRUE;
1426                                                   break;
1427                                                 }
1428                                                 len_size += len_read;
1429                                                 http_buf_size += len_read;
1430                                         }
1431 
1432                                         /* Eat up '\r' '\n' */
1433                                         ch = php_stream_getc(stream);
1434                                         if (ch == '\r') {
1435                                                 ch = php_stream_getc(stream);
1436                                         }
1437                                         if (ch != '\n') {
1438                                                 /* Somthing wrong in chunked encoding */
1439                                                 if (http_buf) {
1440                                                         zend_string_release(http_buf);
1441                                                 }
1442                                                 return NULL;
1443                                         }
1444                                 }
1445                         } else {
1446                                 /* Somthing wrong in chunked encoding */
1447                                 if (http_buf) {
1448                                         zend_string_release(http_buf);
1449                                 }
1450                                 return NULL;
1451                         }
1452                         if (buf_size == 0) {
1453                                 done = TRUE;
1454                         }
1455                 }
1456 
1457                 /* Ignore trailer headers */
1458                 while (1) {
1459                         if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1460                                 break;
1461                         }
1462 
1463                         if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1464                             (headerbuf[0] == '\n')) {
1465                                 /* empty line marks end of headers */
1466                                 break;
1467                         }
1468                 }
1469 
1470                 if (http_buf == NULL) {
1471                         return ZSTR_EMPTY_ALLOC();
1472                 }
1473 
1474         } else if (header_length) {
1475                 if (header_length < 0 || header_length >= INT_MAX) {
1476                         return NULL;
1477                 }
1478                 http_buf = zend_string_alloc(header_length, 0);
1479                 while (http_buf_size < header_length) {
1480                         int len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size);
1481                         if (len_read <= 0) {
1482                                 break;
1483                         }
1484                         http_buf_size += len_read;
1485                 }
1486         } else if (header_close) {
1487                 do {
1488                         int len_read;
1489                         if (http_buf) {
1490                                 http_buf = zend_string_realloc(http_buf, http_buf_size + 4096, 0);
1491                         } else {
1492                                 http_buf = zend_string_alloc(4096, 0);
1493                         }
1494                         len_read = php_stream_read(stream, http_buf->val + http_buf_size, 4096);
1495                         if (len_read > 0) {
1496                                 http_buf_size += len_read;
1497                         }
1498                 } while(!php_stream_eof(stream));
1499         } else {
1500                 return NULL;
1501         }
1502 
1503         http_buf->val[http_buf_size] = '\0';
1504         http_buf->len = http_buf_size;
1505         return http_buf;
1506 }
1507 
1508 static zend_string *get_http_headers(php_stream *stream)
1509 {
1510         smart_str tmp_response = {0};
1511         char headerbuf[8192];
1512 
1513         while (php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1514                 if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1515                     (headerbuf[0] == '\n')) {
1516                         /* empty line marks end of headers */
1517                         smart_str_0(&tmp_response);
1518                         return tmp_response.s;
1519                 }
1520 
1521                 /* add header to collection */
1522                 smart_str_appends(&tmp_response, headerbuf);
1523         }
1524 
1525         smart_str_free(&tmp_response);
1526         return NULL;
1527 }
1528 /*
1529  * Local variables:
1530  * tab-width: 4
1531  * c-basic-offset: 4
1532  * End:
1533  * vim600: sw=4 ts=4 fdm=marker
1534  * vim<600: sw=4 ts=4
1535  */

/* [<][>][^][v][top][bottom][index][help] */