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