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