1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 | Author: Jim Winstead <jimw@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <sys/types.h>
23
24 #include "php.h"
25
26 #include "url.h"
27 #include "file.h"
28 #ifdef _OSD_POSIX
29 # ifndef CHARSET_EBCDIC
30 # define CHARSET_EBCDIC /* this machine uses EBCDIC, not ASCII! */
31 # endif
32 # include "ebcdic.h"
33 #endif /*_OSD_POSIX*/
34
35 /* {{{ free_url
36 */
php_url_free(php_url * theurl)37 PHPAPI void php_url_free(php_url *theurl)
38 {
39 if (theurl->scheme)
40 zend_string_release_ex(theurl->scheme, 0);
41 if (theurl->user)
42 zend_string_release_ex(theurl->user, 0);
43 if (theurl->pass)
44 zend_string_release_ex(theurl->pass, 0);
45 if (theurl->host)
46 zend_string_release_ex(theurl->host, 0);
47 if (theurl->path)
48 zend_string_release_ex(theurl->path, 0);
49 if (theurl->query)
50 zend_string_release_ex(theurl->query, 0);
51 if (theurl->fragment)
52 zend_string_release_ex(theurl->fragment, 0);
53 efree(theurl);
54 }
55 /* }}} */
56
57 /* {{{ php_replace_controlchars
58 */
php_replace_controlchars_ex(char * str,size_t len)59 PHPAPI char *php_replace_controlchars_ex(char *str, size_t len)
60 {
61 unsigned char *s = (unsigned char *)str;
62 unsigned char *e = (unsigned char *)str + len;
63
64 if (!str) {
65 return (NULL);
66 }
67
68 while (s < e) {
69
70 if (iscntrl(*s)) {
71 *s='_';
72 }
73 s++;
74 }
75
76 return (str);
77 }
78 /* }}} */
79
php_replace_controlchars(char * str)80 PHPAPI char *php_replace_controlchars(char *str)
81 {
82 return php_replace_controlchars_ex(str, strlen(str));
83 }
84
php_url_parse(char const * str)85 PHPAPI php_url *php_url_parse(char const *str)
86 {
87 return php_url_parse_ex(str, strlen(str));
88 }
89
binary_strcspn(const char * s,const char * e,const char * chars)90 static const char *binary_strcspn(const char *s, const char *e, const char *chars) {
91 while (*chars) {
92 const char *p = memchr(s, *chars, e - s);
93 if (p) {
94 e = p;
95 }
96 chars++;
97 }
98 return e;
99 }
100
101 /* {{{ php_url_parse
102 */
php_url_parse_ex(char const * str,size_t length)103 PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
104 {
105 zend_bool has_port;
106 return php_url_parse_ex2(str, length, &has_port);
107 }
108
109 /* {{{ php_url_parse_ex2
110 */
php_url_parse_ex2(char const * str,size_t length,zend_bool * has_port)111 PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, zend_bool *has_port)
112 {
113 char port_buf[6];
114 php_url *ret = ecalloc(1, sizeof(php_url));
115 char const *s, *e, *p, *pp, *ue;
116
117 *has_port = 0;
118 s = str;
119 ue = s + length;
120
121 /* parse scheme */
122 if ((e = memchr(s, ':', length)) && e != s) {
123 /* validate scheme */
124 p = s;
125 while (p < e) {
126 /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */
127 if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') {
128 if (e + 1 < ue && e < binary_strcspn(s, ue, "?#")) {
129 goto parse_port;
130 } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
131 s += 2;
132 e = 0;
133 goto parse_host;
134 } else {
135 goto just_path;
136 }
137 }
138 p++;
139 }
140
141 if (e + 1 == ue) { /* only scheme is available */
142 ret->scheme = zend_string_init(s, (e - s), 0);
143 php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
144 return ret;
145 }
146
147 /*
148 * certain schemas like mailto: and zlib: may not have any / after them
149 * this check ensures we support those.
150 */
151 if (*(e+1) != '/') {
152 /* check if the data we get is a port this allows us to
153 * correctly parse things like a.com:80
154 */
155 p = e + 1;
156 while (p < ue && isdigit(*p)) {
157 p++;
158 }
159
160 if ((p == ue || *p == '/') && (p - e) < 7) {
161 goto parse_port;
162 }
163
164 ret->scheme = zend_string_init(s, (e-s), 0);
165 php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
166
167 s = e + 1;
168 goto just_path;
169 } else {
170 ret->scheme = zend_string_init(s, (e-s), 0);
171 php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
172
173 if (e + 2 < ue && *(e + 2) == '/') {
174 s = e + 3;
175 if (zend_string_equals_literal_ci(ret->scheme, "file")) {
176 if (e + 3 < ue && *(e + 3) == '/') {
177 /* support windows drive letters as in:
178 file:///c:/somedir/file.txt
179 */
180 if (e + 5 < ue && *(e + 5) == ':') {
181 s = e + 4;
182 }
183 goto just_path;
184 }
185 }
186 } else {
187 s = e + 1;
188 goto just_path;
189 }
190 }
191 } else if (e) { /* no scheme; starts with colon: look for port */
192 parse_port:
193 p = e + 1;
194 pp = p;
195
196 while (pp < ue && pp - p < 6 && isdigit(*pp)) {
197 pp++;
198 }
199
200 if (pp - p > 0 && pp - p < 6 && (pp == ue || *pp == '/')) {
201 zend_long port;
202 char *end;
203 memcpy(port_buf, p, (pp - p));
204 port_buf[pp - p] = '\0';
205 port = ZEND_STRTOL(port_buf, &end, 10);
206 if (port >= 0 && port <= 65535 && end != port_buf) {
207 *has_port = 1;
208 ret->port = (unsigned short) port;
209 if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
210 s += 2;
211 }
212 } else {
213 php_url_free(ret);
214 return NULL;
215 }
216 } else if (p == pp && pp == ue) {
217 php_url_free(ret);
218 return NULL;
219 } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
220 s += 2;
221 } else {
222 goto just_path;
223 }
224 } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
225 s += 2;
226 } else {
227 goto just_path;
228 }
229
230 parse_host:
231 e = binary_strcspn(s, ue, "/?#");
232
233 /* check for login and password */
234 if ((p = zend_memrchr(s, '@', (e-s)))) {
235 if ((pp = memchr(s, ':', (p-s)))) {
236 ret->user = zend_string_init(s, (pp-s), 0);
237 php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
238
239 pp++;
240 ret->pass = zend_string_init(pp, (p-pp), 0);
241 php_replace_controlchars_ex(ZSTR_VAL(ret->pass), ZSTR_LEN(ret->pass));
242 } else {
243 ret->user = zend_string_init(s, (p-s), 0);
244 php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
245 }
246
247 s = p + 1;
248 }
249
250 /* check for port */
251 if (s < ue && *s == '[' && *(e-1) == ']') {
252 /* Short circuit portscan,
253 we're dealing with an
254 IPv6 embedded address */
255 p = NULL;
256 } else {
257 p = zend_memrchr(s, ':', (e-s));
258 }
259
260 if (p) {
261 if (!ret->port) {
262 p++;
263 if (e-p > 5) { /* port cannot be longer then 5 characters */
264 php_url_free(ret);
265 return NULL;
266 } else if (e - p > 0) {
267 zend_long port;
268 char *end;
269 memcpy(port_buf, p, (e - p));
270 port_buf[e - p] = '\0';
271 port = ZEND_STRTOL(port_buf, &end, 10);
272 if (port >= 0 && port <= 65535 && end != port_buf) {
273 *has_port = 1;
274 ret->port = (unsigned short)port;
275 } else {
276 php_url_free(ret);
277 return NULL;
278 }
279 }
280 p--;
281 }
282 } else {
283 p = e;
284 }
285
286 /* check if we have a valid host, if we don't reject the string as url */
287 if ((p-s) < 1) {
288 php_url_free(ret);
289 return NULL;
290 }
291
292 ret->host = zend_string_init(s, (p-s), 0);
293 php_replace_controlchars_ex(ZSTR_VAL(ret->host), ZSTR_LEN(ret->host));
294
295 if (e == ue) {
296 return ret;
297 }
298
299 s = e;
300
301 just_path:
302
303 e = ue;
304 p = memchr(s, '#', (e - s));
305 if (p) {
306 p++;
307 if (p < e) {
308 ret->fragment = zend_string_init(p, (e - p), 0);
309 php_replace_controlchars_ex(ZSTR_VAL(ret->fragment), ZSTR_LEN(ret->fragment));
310 }
311 e = p-1;
312 }
313
314 p = memchr(s, '?', (e - s));
315 if (p) {
316 p++;
317 if (p < e) {
318 ret->query = zend_string_init(p, (e - p), 0);
319 php_replace_controlchars_ex(ZSTR_VAL(ret->query), ZSTR_LEN(ret->query));
320 }
321 e = p-1;
322 }
323
324 if (s < e || s == ue) {
325 ret->path = zend_string_init(s, (e - s), 0);
326 php_replace_controlchars_ex(ZSTR_VAL(ret->path), ZSTR_LEN(ret->path));
327 }
328
329 return ret;
330 }
331 /* }}} */
332
333 /* {{{ proto mixed parse_url(string url, [int url_component])
334 Parse a URL and return its components */
PHP_FUNCTION(parse_url)335 PHP_FUNCTION(parse_url)
336 {
337 char *str;
338 size_t str_len;
339 php_url *resource;
340 zend_long key = -1;
341 zval tmp;
342 zend_bool has_port;
343
344 ZEND_PARSE_PARAMETERS_START(1, 2)
345 Z_PARAM_STRING(str, str_len)
346 Z_PARAM_OPTIONAL
347 Z_PARAM_LONG(key)
348 ZEND_PARSE_PARAMETERS_END();
349
350 resource = php_url_parse_ex2(str, str_len, &has_port);
351 if (resource == NULL) {
352 /* @todo Find a method to determine why php_url_parse_ex() failed */
353 RETURN_FALSE;
354 }
355
356 if (key > -1) {
357 switch (key) {
358 case PHP_URL_SCHEME:
359 if (resource->scheme != NULL) RETVAL_STR_COPY(resource->scheme);
360 break;
361 case PHP_URL_HOST:
362 if (resource->host != NULL) RETVAL_STR_COPY(resource->host);
363 break;
364 case PHP_URL_PORT:
365 if (has_port) RETVAL_LONG(resource->port);
366 break;
367 case PHP_URL_USER:
368 if (resource->user != NULL) RETVAL_STR_COPY(resource->user);
369 break;
370 case PHP_URL_PASS:
371 if (resource->pass != NULL) RETVAL_STR_COPY(resource->pass);
372 break;
373 case PHP_URL_PATH:
374 if (resource->path != NULL) RETVAL_STR_COPY(resource->path);
375 break;
376 case PHP_URL_QUERY:
377 if (resource->query != NULL) RETVAL_STR_COPY(resource->query);
378 break;
379 case PHP_URL_FRAGMENT:
380 if (resource->fragment != NULL) RETVAL_STR_COPY(resource->fragment);
381 break;
382 default:
383 php_error_docref(NULL, E_WARNING, "Invalid URL component identifier " ZEND_LONG_FMT, key);
384 RETVAL_FALSE;
385 }
386 goto done;
387 }
388
389 /* allocate an array for return */
390 array_init(return_value);
391
392 /* add the various elements to the array */
393 if (resource->scheme != NULL) {
394 ZVAL_STR_COPY(&tmp, resource->scheme);
395 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_SCHEME), &tmp);
396 }
397 if (resource->host != NULL) {
398 ZVAL_STR_COPY(&tmp, resource->host);
399 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_HOST), &tmp);
400 }
401 if (has_port) {
402 ZVAL_LONG(&tmp, resource->port);
403 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PORT), &tmp);
404 }
405 if (resource->user != NULL) {
406 ZVAL_STR_COPY(&tmp, resource->user);
407 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_USER), &tmp);
408 }
409 if (resource->pass != NULL) {
410 ZVAL_STR_COPY(&tmp, resource->pass);
411 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PASS), &tmp);
412 }
413 if (resource->path != NULL) {
414 ZVAL_STR_COPY(&tmp, resource->path);
415 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PATH), &tmp);
416 }
417 if (resource->query != NULL) {
418 ZVAL_STR_COPY(&tmp, resource->query);
419 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_QUERY), &tmp);
420 }
421 if (resource->fragment != NULL) {
422 ZVAL_STR_COPY(&tmp, resource->fragment);
423 zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_FRAGMENT), &tmp);
424 }
425 done:
426 php_url_free(resource);
427 }
428 /* }}} */
429
430 /* {{{ php_htoi
431 */
php_htoi(char * s)432 static int php_htoi(char *s)
433 {
434 int value;
435 int c;
436
437 c = ((unsigned char *)s)[0];
438 if (isupper(c))
439 c = tolower(c);
440 value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
441
442 c = ((unsigned char *)s)[1];
443 if (isupper(c))
444 c = tolower(c);
445 value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
446
447 return (value);
448 }
449 /* }}} */
450
451 /* rfc1738:
452
453 ...The characters ";",
454 "/", "?", ":", "@", "=" and "&" are the characters which may be
455 reserved for special meaning within a scheme...
456
457 ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
458 reserved characters used for their reserved purposes may be used
459 unencoded within a URL...
460
461 For added safety, we only leave -_. unencoded.
462 */
463
464 static unsigned char hexchars[] = "0123456789ABCDEF";
465
466 /* {{{ php_url_encode
467 */
php_url_encode(char const * s,size_t len)468 PHPAPI zend_string *php_url_encode(char const *s, size_t len)
469 {
470 register unsigned char c;
471 unsigned char *to;
472 unsigned char const *from, *end;
473 zend_string *start;
474
475 from = (unsigned char *)s;
476 end = (unsigned char *)s + len;
477 start = zend_string_safe_alloc(3, len, 0, 0);
478 to = (unsigned char*)ZSTR_VAL(start);
479
480 while (from < end) {
481 c = *from++;
482
483 if (c == ' ') {
484 *to++ = '+';
485 #ifndef CHARSET_EBCDIC
486 } else if ((c < '0' && c != '-' && c != '.') ||
487 (c < 'A' && c > '9') ||
488 (c > 'Z' && c < 'a' && c != '_') ||
489 (c > 'z')) {
490 to[0] = '%';
491 to[1] = hexchars[c >> 4];
492 to[2] = hexchars[c & 15];
493 to += 3;
494 #else /*CHARSET_EBCDIC*/
495 } else if (!isalnum(c) && strchr("_-.", c) == NULL) {
496 /* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
497 to[0] = '%';
498 to[1] = hexchars[os_toascii[c] >> 4];
499 to[2] = hexchars[os_toascii[c] & 15];
500 to += 3;
501 #endif /*CHARSET_EBCDIC*/
502 } else {
503 *to++ = c;
504 }
505 }
506 *to = '\0';
507
508 start = zend_string_truncate(start, to - (unsigned char*)ZSTR_VAL(start), 0);
509
510 return start;
511 }
512 /* }}} */
513
514 /* {{{ proto string urlencode(string str)
515 URL-encodes string */
PHP_FUNCTION(urlencode)516 PHP_FUNCTION(urlencode)
517 {
518 zend_string *in_str;
519
520 ZEND_PARSE_PARAMETERS_START(1, 1)
521 Z_PARAM_STR(in_str)
522 ZEND_PARSE_PARAMETERS_END();
523
524 RETURN_STR(php_url_encode(ZSTR_VAL(in_str), ZSTR_LEN(in_str)));
525 }
526 /* }}} */
527
528 /* {{{ proto string urldecode(string str)
529 Decodes URL-encoded string */
PHP_FUNCTION(urldecode)530 PHP_FUNCTION(urldecode)
531 {
532 zend_string *in_str, *out_str;
533
534 ZEND_PARSE_PARAMETERS_START(1, 1)
535 Z_PARAM_STR(in_str)
536 ZEND_PARSE_PARAMETERS_END();
537
538 out_str = zend_string_init(ZSTR_VAL(in_str), ZSTR_LEN(in_str), 0);
539 ZSTR_LEN(out_str) = php_url_decode(ZSTR_VAL(out_str), ZSTR_LEN(out_str));
540
541 RETURN_NEW_STR(out_str);
542 }
543 /* }}} */
544
545 /* {{{ php_url_decode
546 */
php_url_decode(char * str,size_t len)547 PHPAPI size_t php_url_decode(char *str, size_t len)
548 {
549 char *dest = str;
550 char *data = str;
551
552 while (len--) {
553 if (*data == '+') {
554 *dest = ' ';
555 }
556 else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
557 && isxdigit((int) *(data + 2))) {
558 #ifndef CHARSET_EBCDIC
559 *dest = (char) php_htoi(data + 1);
560 #else
561 *dest = os_toebcdic[(unsigned char) php_htoi(data + 1)];
562 #endif
563 data += 2;
564 len -= 2;
565 } else {
566 *dest = *data;
567 }
568 data++;
569 dest++;
570 }
571 *dest = '\0';
572 return dest - str;
573 }
574 /* }}} */
575
576 /* {{{ php_raw_url_encode
577 */
php_raw_url_encode(char const * s,size_t len)578 PHPAPI zend_string *php_raw_url_encode(char const *s, size_t len)
579 {
580 register size_t x, y;
581 zend_string *str;
582 char *ret;
583
584 str = zend_string_safe_alloc(3, len, 0, 0);
585 ret = ZSTR_VAL(str);
586 for (x = 0, y = 0; len--; x++, y++) {
587 char c = s[x];
588
589 ret[y] = c;
590 #ifndef CHARSET_EBCDIC
591 if ((c < '0' && c != '-' && c != '.') ||
592 (c < 'A' && c > '9') ||
593 (c > 'Z' && c < 'a' && c != '_') ||
594 (c > 'z' && c != '~')) {
595 ret[y++] = '%';
596 ret[y++] = hexchars[(unsigned char) c >> 4];
597 ret[y] = hexchars[(unsigned char) c & 15];
598 #else /*CHARSET_EBCDIC*/
599 if (!isalnum(c) && strchr("_-.~", c) != NULL) {
600 ret[y++] = '%';
601 ret[y++] = hexchars[os_toascii[(unsigned char) c] >> 4];
602 ret[y] = hexchars[os_toascii[(unsigned char) c] & 15];
603 #endif /*CHARSET_EBCDIC*/
604 }
605 }
606 ret[y] = '\0';
607 str = zend_string_truncate(str, y, 0);
608
609 return str;
610 }
611 /* }}} */
612
613 /* {{{ proto string rawurlencode(string str)
614 URL-encodes string */
615 PHP_FUNCTION(rawurlencode)
616 {
617 zend_string *in_str;
618
619 ZEND_PARSE_PARAMETERS_START(1, 1)
620 Z_PARAM_STR(in_str)
621 ZEND_PARSE_PARAMETERS_END();
622
623 RETURN_STR(php_raw_url_encode(ZSTR_VAL(in_str), ZSTR_LEN(in_str)));
624 }
625 /* }}} */
626
627 /* {{{ proto string rawurldecode(string str)
628 Decodes URL-encodes string */
629 PHP_FUNCTION(rawurldecode)
630 {
631 zend_string *in_str, *out_str;
632
633 ZEND_PARSE_PARAMETERS_START(1, 1)
634 Z_PARAM_STR(in_str)
635 ZEND_PARSE_PARAMETERS_END();
636
637 out_str = zend_string_init(ZSTR_VAL(in_str), ZSTR_LEN(in_str), 0);
638 ZSTR_LEN(out_str) = php_raw_url_decode(ZSTR_VAL(out_str), ZSTR_LEN(out_str));
639
640 RETURN_NEW_STR(out_str);
641 }
642 /* }}} */
643
644 /* {{{ php_raw_url_decode
645 */
646 PHPAPI size_t php_raw_url_decode(char *str, size_t len)
647 {
648 char *dest = str;
649 char *data = str;
650
651 while (len--) {
652 if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
653 && isxdigit((int) *(data + 2))) {
654 #ifndef CHARSET_EBCDIC
655 *dest = (char) php_htoi(data + 1);
656 #else
657 *dest = os_toebcdic[(unsigned char) php_htoi(data + 1)];
658 #endif
659 data += 2;
660 len -= 2;
661 } else {
662 *dest = *data;
663 }
664 data++;
665 dest++;
666 }
667 *dest = '\0';
668 return dest - str;
669 }
670 /* }}} */
671
672 /* {{{ proto array get_headers(string url[, int format[, resource context]])
673 fetches all the headers sent by the server in response to a HTTP request */
674 PHP_FUNCTION(get_headers)
675 {
676 char *url;
677 size_t url_len;
678 php_stream *stream;
679 zval *prev_val, *hdr = NULL;
680 zend_long format = 0;
681 zval *zcontext = NULL;
682 php_stream_context *context;
683
684 ZEND_PARSE_PARAMETERS_START(1, 3)
685 Z_PARAM_PATH(url, url_len)
686 Z_PARAM_OPTIONAL
687 Z_PARAM_LONG(format)
688 Z_PARAM_RESOURCE_EX(zcontext, 1, 0)
689 ZEND_PARSE_PARAMETERS_END();
690
691 context = php_stream_context_from_zval(zcontext, 0);
692
693 if (!(stream = php_stream_open_wrapper_ex(url, "r", REPORT_ERRORS | STREAM_USE_URL | STREAM_ONLY_GET_HEADERS, NULL, context))) {
694 RETURN_FALSE;
695 }
696
697 if (Z_TYPE(stream->wrapperdata) != IS_ARRAY) {
698 php_stream_close(stream);
699 RETURN_FALSE;
700 }
701
702 array_init(return_value);
703
704 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(&stream->wrapperdata), hdr) {
705 if (Z_TYPE_P(hdr) != IS_STRING) {
706 continue;
707 }
708 if (!format) {
709 no_name_header:
710 add_next_index_str(return_value, zend_string_copy(Z_STR_P(hdr)));
711 } else {
712 char c;
713 char *s, *p;
714
715 if ((p = strchr(Z_STRVAL_P(hdr), ':'))) {
716 c = *p;
717 *p = '\0';
718 s = p + 1;
719 while (isspace((int)*(unsigned char *)s)) {
720 s++;
721 }
722
723 if ((prev_val = zend_hash_str_find(Z_ARRVAL_P(return_value), Z_STRVAL_P(hdr), (p - Z_STRVAL_P(hdr)))) == NULL) {
724 add_assoc_stringl_ex(return_value, Z_STRVAL_P(hdr), (p - Z_STRVAL_P(hdr)), s, (Z_STRLEN_P(hdr) - (s - Z_STRVAL_P(hdr))));
725 } else { /* some headers may occur more than once, therefor we need to remake the string into an array */
726 convert_to_array(prev_val);
727 add_next_index_stringl(prev_val, s, (Z_STRLEN_P(hdr) - (s - Z_STRVAL_P(hdr))));
728 }
729
730 *p = c;
731 } else {
732 goto no_name_header;
733 }
734 }
735 } ZEND_HASH_FOREACH_END();
736
737 php_stream_close(stream);
738 }
739 /* }}} */
740