1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: url.c 290199 2009-11-04 13:44:10Z iliaa $ */
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 :
41 : /*
42 : * These macros are similar to ZVAL_U_STRING*() but they select between
43 : * Unicode/binary based on explicitly specified type, rather than the
44 : * UG(unicode) global. They may be useful for other functions, but implementing
45 : * them would probably require updating all of the corresponding add_*_assoc_*()
46 : * macros, so let's keep them here for now and revisit the issue if someone else
47 : * needs them in the future.
48 : */
49 : #define ZVAL_UT_STRING(conv, type, z, s, flags) { \
50 : if (type == IS_UNICODE) { \
51 : UErrorCode status = U_ZERO_ERROR; \
52 : char *__s = (s); \
53 : int __s_len = strlen(__s); \
54 : UChar *u_str; \
55 : int u_len; \
56 : zend_string_to_unicode_ex(conv, &u_str, &u_len, __s, __s_len, &status); \
57 : if ((flags) & ZSTR_AUTOFREE) { \
58 : efree(__s); \
59 : } \
60 : ZVAL_UNICODEL(z, u_str, u_len, 0); \
61 : } else { \
62 : char *__s=(s); \
63 : Z_STRLEN_P(z) = strlen(__s); \
64 : Z_STRVAL_P(z) = (((flags) & ZSTR_DUPLICATE) ? estrndup(__s, Z_STRLEN_P(z)) : __s); \
65 : Z_TYPE_P(z) = IS_STRING; \
66 : } \
67 : }
68 :
69 : #define ZVAL_UT_STRINGL(conv, type, z, s, l, flags) { \
70 : if (type == IS_UNICODE) { \
71 : UErrorCode status = U_ZERO_ERROR; \
72 : char *__s = (s); \
73 : int __s_len = (l); \
74 : UChar *u_str; \
75 : int u_len; \
76 : zend_string_to_unicode_ex(conv, &u_str, &u_len, __s, __s_len, &status); \
77 : if ((flags) & ZSTR_AUTOFREE) { \
78 : efree(__s); \
79 : } \
80 : ZVAL_UNICODEL(z, u_str, u_len, 0); \
81 : } else { \
82 : char *__s=(s); int __l=l; \
83 : Z_STRLEN_P(z) = __l; \
84 : Z_STRVAL_P(z) = (((flags) & ZSTR_DUPLICATE) ? estrndup(__s, __l) : __s); \
85 : Z_TYPE_P(z) = IS_STRING; \
86 : }\
87 : }
88 :
89 : #define RETVAL_UT_STRING(conv, type, s, flags) ZVAL_UT_STRING(conv, type, return_value, s, flags)
90 : #define RETVAL_UT_STRINGL(conv, type, s, l, flags) ZVAL_UT_STRINGL(conv, type, return_value, s, l, flags)
91 : #define RETURN_UT_STRING(conv, type, t, flags) { RETVAL_UT_STRING(conv, type, t, flags); return; }
92 : #define RETURN_UT_STRINGL(conv, type, t, l, flags) { RETVAL_UT_STRINGL(conv, type, t, l, flags); return; }
93 :
94 : /* {{{ free_url
95 : */
96 : PHPAPI void php_url_free(php_url *theurl)
97 2624 : {
98 2624 : if (theurl->scheme)
99 2266 : efree(theurl->scheme);
100 2624 : if (theurl->user)
101 122 : efree(theurl->user);
102 2624 : if (theurl->pass)
103 73 : efree(theurl->pass);
104 2624 : if (theurl->host)
105 2237 : efree(theurl->host);
106 2624 : if (theurl->path)
107 2389 : efree(theurl->path);
108 2624 : if (theurl->query)
109 235 : efree(theurl->query);
110 2624 : if (theurl->fragment)
111 123 : efree(theurl->fragment);
112 2624 : efree(theurl);
113 2624 : }
114 : /* }}} */
115 :
116 : /* {{{ php_replace_controlchars
117 : */
118 : PHPAPI char *php_replace_controlchars_ex(char *str, int len)
119 3343 : {
120 3343 : unsigned char *s = (unsigned char *)str;
121 3343 : unsigned char *e = (unsigned char *)str + len;
122 :
123 3343 : if (!str) {
124 0 : return (NULL);
125 : }
126 :
127 1058769 : while (s < e) {
128 :
129 1052083 : if (iscntrl(*s)) {
130 0 : *s='_';
131 : }
132 1052083 : s++;
133 : }
134 :
135 3343 : return (str);
136 : }
137 : /* }}} */
138 :
139 : PHPAPI char *php_replace_controlchars(char *str) /* {{{ */
140 0 : {
141 0 : return php_replace_controlchars_ex(str, strlen(str));
142 : }
143 : /* }}} */
144 :
145 : PHPAPI php_url *php_url_parse(char const *str) /* {{{ */
146 320 : {
147 320 : return php_url_parse_ex(str, strlen(str));
148 : }
149 : /* }}} */
150 :
151 : /* {{{ php_url_parse
152 : */
153 : PHPAPI php_url *php_url_parse_ex(char const *str, int length)
154 1489 : {
155 : char port_buf[6];
156 1489 : php_url *ret = ecalloc(1, sizeof(php_url));
157 : char const *s, *e, *p, *pp, *ue;
158 :
159 1489 : s = str;
160 1489 : ue = s + length;
161 :
162 : /* parse scheme */
163 2459 : if ((e = memchr(s, ':', length)) && (e - s)) {
164 : /* validate scheme */
165 1269 : p = s;
166 1032234 : while (p < e) {
167 : /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */
168 1029704 : if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') {
169 8 : if (e + 1 < ue) {
170 8 : goto parse_port;
171 : } else {
172 0 : goto just_path;
173 : }
174 : }
175 1029696 : p++;
176 : }
177 :
178 1261 : if (*(e + 1) == '\0') { /* only scheme is available */
179 11 : ret->scheme = estrndup(s, (e - s));
180 11 : php_replace_controlchars_ex(ret->scheme, (e - s));
181 11 : goto end;
182 : }
183 :
184 : /*
185 : * certain schemas like mailto: and zlib: may not have any / after them
186 : * this check ensures we support those.
187 : */
188 1250 : if (*(e+1) != '/') {
189 : /* check if the data we get is a port this allows us to
190 : * correctly parse things like a.com:80
191 : */
192 184 : p = e + 1;
193 961 : while (isdigit(*p)) {
194 593 : p++;
195 : }
196 :
197 184 : if ((*p == '\0' || *p == '/') && (p - e) < 7) {
198 130 : goto parse_port;
199 : }
200 :
201 54 : ret->scheme = estrndup(s, (e-s));
202 54 : php_replace_controlchars_ex(ret->scheme, (e - s));
203 :
204 54 : length -= ++e - s;
205 54 : s = e;
206 54 : goto just_path;
207 : } else {
208 1066 : ret->scheme = estrndup(s, (e-s));
209 1066 : php_replace_controlchars_ex(ret->scheme, (e - s));
210 :
211 1066 : if (*(e+2) == '/') {
212 1035 : s = e + 3;
213 1035 : if (!strncasecmp("file", ret->scheme, sizeof("file"))) {
214 76 : if (*(e + 3) == '/') {
215 : /* support windows drive letters as in:
216 : file:///c:/somedir/file.txt
217 : */
218 65 : if (*(e + 5) == ':') {
219 27 : s = e + 4;
220 : }
221 65 : goto nohost;
222 : }
223 : }
224 : } else {
225 31 : if (!strncasecmp("file", ret->scheme, sizeof("file"))) {
226 10 : s = e + 1;
227 10 : goto nohost;
228 : } else {
229 21 : length -= ++e - s;
230 21 : s = e;
231 21 : goto just_path;
232 : }
233 : }
234 : }
235 220 : } else if (e) { /* no scheme, look for port */
236 138 : parse_port:
237 138 : p = e + 1;
238 138 : pp = p;
239 :
240 807 : while (pp-p < 6 && isdigit(*pp)) {
241 531 : pp++;
242 : }
243 :
244 138 : if (pp-p < 6 && (*pp == '/' || *pp == '\0')) {
245 132 : memcpy(port_buf, p, (pp-p));
246 132 : port_buf[pp-p] = '\0';
247 132 : ret->port = atoi(port_buf);
248 : } else {
249 : goto just_path;
250 : }
251 : } else {
252 301 : just_path:
253 301 : ue = s + length;
254 301 : goto nohost;
255 : }
256 :
257 1102 : e = ue;
258 :
259 1102 : if (!(p = memchr(s, '/', (ue - s)))) {
260 : char *query, *fragment;
261 :
262 558 : query = memchr(s, '?', (ue - s));
263 558 : fragment = memchr(s, '#', (ue - s));
264 :
265 558 : if (query && fragment) {
266 0 : if (query > fragment) {
267 0 : p = e = fragment;
268 : } else {
269 0 : p = e = query;
270 : }
271 558 : } else if (query) {
272 55 : p = e = query;
273 503 : } else if (fragment) {
274 10 : p = e = fragment;
275 : }
276 : } else {
277 544 : e = p;
278 : }
279 :
280 : /* check for login and password */
281 1102 : if ((p = zend_memrchr(s, '@', (e-s)))) {
282 168 : if ((pp = memchr(s, ':', (p-s)))) {
283 92 : if ((pp-s) > 0) {
284 82 : ret->user = estrndup(s, (pp-s));
285 82 : php_replace_controlchars_ex(ret->user, (pp - s));
286 : }
287 :
288 92 : pp++;
289 92 : if (p-pp > 0) {
290 82 : ret->pass = estrndup(pp, (p-pp));
291 82 : php_replace_controlchars_ex(ret->pass, (p-pp));
292 : }
293 : } else {
294 76 : ret->user = estrndup(s, (p-s));
295 76 : php_replace_controlchars_ex(ret->user, (p-s));
296 : }
297 :
298 168 : s = p + 1;
299 : }
300 :
301 : /* check for port */
302 1111 : if (*s == '[' && *(e-1) == ']') {
303 : /* Short circuit portscan,
304 : we're dealing with an
305 : IPv6 embedded address */
306 9 : p = s;
307 : } else {
308 : /* memrchr is a GNU specific extension
309 : Emulate for wide compatability */
310 1093 : for(p = e; *p != ':' && p >= s; p--);
311 : }
312 :
313 1649 : if (p >= s && *p == ':') {
314 565 : if (!ret->port) {
315 433 : p++;
316 433 : if (e-p > 5) { /* port cannot be longer then 5 characters */
317 18 : STR_FREE(ret->scheme);
318 18 : STR_FREE(ret->user);
319 18 : STR_FREE(ret->pass);
320 18 : efree(ret);
321 18 : return NULL;
322 415 : } else if (e - p > 0) {
323 333 : memcpy(port_buf, p, (e-p));
324 333 : port_buf[e-p] = '\0';
325 333 : ret->port = atoi(port_buf);
326 : }
327 415 : p--;
328 : }
329 : } else {
330 537 : p = e;
331 : }
332 :
333 : /* check if we have a valid host, if we don't reject the string as url */
334 1084 : if ((p-s) < 1) {
335 353 : STR_FREE(ret->scheme);
336 353 : STR_FREE(ret->user);
337 353 : STR_FREE(ret->pass);
338 353 : efree(ret);
339 353 : return NULL;
340 : }
341 :
342 731 : ret->host = estrndup(s, (p-s));
343 731 : php_replace_controlchars_ex(ret->host, (p - s));
344 :
345 731 : if (e == ue) {
346 185 : return ret;
347 : }
348 :
349 546 : s = e;
350 :
351 922 : nohost:
352 :
353 922 : if ((p = memchr(s, '?', (ue - s)))) {
354 322 : pp = strchr(s, '#');
355 :
356 322 : if (pp && pp < p) {
357 0 : p = pp;
358 0 : goto label_parse;
359 : }
360 :
361 322 : if (p - s) {
362 293 : ret->path = estrndup(s, (p-s));
363 293 : php_replace_controlchars_ex(ret->path, (p - s));
364 : }
365 :
366 322 : if (pp) {
367 113 : if (pp - ++p) {
368 103 : ret->query = estrndup(p, (pp-p));
369 103 : php_replace_controlchars_ex(ret->query, (pp - p));
370 : }
371 113 : p = pp;
372 113 : goto label_parse;
373 209 : } else if (++p - ue) {
374 132 : ret->query = estrndup(p, (ue-p));
375 132 : php_replace_controlchars_ex(ret->query, (ue - p));
376 : }
377 600 : } else if ((p = memchr(s, '#', (ue - s)))) {
378 20 : if (p - s) {
379 10 : ret->path = estrndup(s, (p-s));
380 10 : php_replace_controlchars_ex(ret->path, (p - s));
381 : }
382 :
383 133 : label_parse:
384 133 : p++;
385 :
386 133 : if (ue - p) {
387 123 : ret->fragment = estrndup(p, (ue-p));
388 123 : php_replace_controlchars_ex(ret->fragment, (ue - p));
389 : }
390 : } else {
391 580 : ret->path = estrndup(s, (ue-s));
392 580 : php_replace_controlchars_ex(ret->path, (ue - s));
393 : }
394 933 : end:
395 933 : return ret;
396 : }
397 : /* }}} */
398 :
399 : #define add_ascii_assoc_u_ascii_string(arg, key, str, type, duplicate) do { \
400 : zval *___tmp; \
401 : MAKE_STD_ZVAL(___tmp); \
402 : ZVAL_UT_STRING(UG(ascii_conv), type, ___tmp, str, duplicate); \
403 : add_ascii_assoc_zval(arg, key, ___tmp); \
404 : } while (0)
405 :
406 :
407 : /* {{{ proto mixed parse_url(string url, [int url_component]) U
408 : Parse a URL and return its components */
409 : PHP_FUNCTION(parse_url)
410 1043 : {
411 : zstr str;
412 : int str_len;
413 : zend_uchar type;
414 : php_url *resource;
415 1043 : long key = -1;
416 :
417 1043 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t|l", &str, &str_len, &type, &key) == FAILURE) {
418 18 : return;
419 : }
420 :
421 1025 : if (type == IS_UNICODE) {
422 : char *temp;
423 :
424 1011 : if ((temp = zend_unicode_to_ascii(str.u, str_len TSRMLS_CC)) == NULL) {
425 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not convert URL parameter to ASCII");
426 0 : return;
427 : }
428 1011 : str.s = temp;
429 : }
430 :
431 1025 : resource = php_url_parse_ex(str.s, str_len);
432 1025 : if (resource == NULL) {
433 117 : php_error_docref1(NULL TSRMLS_CC, str.s, E_WARNING, "Unable to parse URL");
434 117 : if (type == IS_UNICODE) {
435 117 : efree(str.s);
436 : }
437 117 : RETURN_FALSE;
438 : }
439 :
440 908 : if (key > -1) {
441 716 : switch (key) {
442 : case PHP_URL_SCHEME:
443 96 : if (resource->scheme != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->scheme, 1);
444 96 : break;
445 : case PHP_URL_HOST:
446 90 : if (resource->host != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->host, 1);
447 90 : break;
448 : case PHP_URL_PORT:
449 88 : if (resource->port != 0) RETVAL_LONG(resource->port);
450 88 : break;
451 : case PHP_URL_USER:
452 88 : if (resource->user != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->user, 1);
453 88 : break;
454 : case PHP_URL_PASS:
455 88 : if (resource->pass != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->pass, 1);
456 88 : break;
457 : case PHP_URL_PATH:
458 88 : if (resource->path != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->path, 1);
459 88 : break;
460 : case PHP_URL_QUERY:
461 88 : if (resource->query != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->query, 1);
462 88 : break;
463 : case PHP_URL_FRAGMENT:
464 88 : if (resource->fragment != NULL) RETVAL_UT_STRING(UG(ascii_conv), type, resource->fragment, 1);
465 88 : break;
466 : default:
467 2 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid URL component identifier %ld", key);
468 2 : RETVAL_FALSE;
469 : }
470 716 : goto done;
471 : }
472 :
473 : /* allocate an array for return */
474 192 : array_init(return_value);
475 :
476 : /* add the various elements to the array */
477 192 : if (resource->scheme != NULL)
478 143 : add_ascii_assoc_u_ascii_string(return_value, "scheme", resource->scheme, type, 1);
479 192 : if (resource->host != NULL)
480 127 : add_ascii_assoc_u_ascii_string(return_value, "host", resource->host, type, 1);
481 192 : if (resource->port != 0)
482 64 : add_ascii_assoc_long(return_value, "port", resource->port);
483 192 : if (resource->user != NULL)
484 22 : add_ascii_assoc_u_ascii_string(return_value, "user", resource->user, type, 1);
485 192 : if (resource->pass != NULL)
486 13 : add_ascii_assoc_u_ascii_string(return_value, "pass", resource->pass, type, 1);
487 192 : if (resource->path != NULL)
488 165 : add_ascii_assoc_u_ascii_string(return_value, "path", resource->path, type, 1);
489 192 : if (resource->query != NULL)
490 45 : add_ascii_assoc_u_ascii_string(return_value, "query", resource->query, type, 1);
491 192 : if (resource->fragment != NULL)
492 23 : add_ascii_assoc_u_ascii_string(return_value, "fragment", resource->fragment, type, 1);
493 908 : done:
494 908 : php_url_free(resource);
495 908 : if (type == IS_UNICODE) {
496 894 : efree(str.s);
497 : }
498 : }
499 : /* }}} */
500 :
501 : /* {{{ php_htoi
502 : */
503 : static int php_htoi(char *s)
504 118 : {
505 : int value;
506 : int c;
507 :
508 118 : c = ((unsigned char *)s)[0];
509 118 : if (isupper(c))
510 1 : c = tolower(c);
511 118 : value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
512 :
513 118 : c = ((unsigned char *)s)[1];
514 118 : if (isupper(c))
515 47 : c = tolower(c);
516 118 : value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
517 :
518 118 : return (value);
519 : }
520 : /* }}} */
521 :
522 : /* rfc1738:
523 :
524 : ...The characters ";",
525 : "/", "?", ":", "@", "=" and "&" are the characters which may be
526 : reserved for special meaning within a scheme...
527 :
528 : ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
529 : reserved characters used for their reserved purposes may be used
530 : unencoded within a URL...
531 :
532 : For added safety, we only leave -_. unencoded.
533 : */
534 :
535 : static unsigned char hexchars[] = "0123456789ABCDEF";
536 :
537 : /* {{{ php_url_encode
538 : */
539 : PHPAPI char *php_url_encode(char const *s, int len, int *new_length)
540 529 : {
541 : register unsigned char c;
542 : unsigned char *to, *start;
543 : unsigned char const *from, *end;
544 :
545 529 : from = (unsigned char*)s;
546 529 : end = from + len;
547 529 : start = to = (unsigned char *) safe_emalloc(3, len, 1);
548 :
549 9905 : while (from < end) {
550 8847 : c = *from++;
551 :
552 8847 : if (c == ' ') {
553 10 : *to++ = '+';
554 : #ifndef CHARSET_EBCDIC
555 8962 : } else if ((c < '0' && c != '-' && c != '.') ||
556 : (c < 'A' && c > '9') ||
557 : (c > 'Z' && c < 'a' && c != '_') ||
558 : (c > 'z')) {
559 125 : to[0] = '%';
560 125 : to[1] = hexchars[c >> 4];
561 125 : to[2] = hexchars[c & 15];
562 125 : to += 3;
563 : #else /*CHARSET_EBCDIC*/
564 : } else if (!isalnum(c) && strchr("_-.", c) == NULL) {
565 : /* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
566 : to[0] = '%';
567 : to[1] = hexchars[os_toascii[c] >> 4];
568 : to[2] = hexchars[os_toascii[c] & 15];
569 : to += 3;
570 : #endif /*CHARSET_EBCDIC*/
571 : } else {
572 8712 : *to++ = c;
573 : }
574 : }
575 529 : *to = 0;
576 529 : if (new_length) {
577 121 : *new_length = to - start;
578 : }
579 529 : return (char *) start;
580 : }
581 : /* }}} */
582 :
583 : /* {{{ proto string urlencode(binary str) U
584 : URL-encodes string */
585 : PHP_FUNCTION(urlencode)
586 91 : {
587 : zstr in_str;
588 : char *out_str;
589 : int in_str_len, out_str_len;
590 : zend_uchar in_str_type;
591 :
592 91 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &in_str,
593 : &in_str_len, &in_str_type) == FAILURE) {
594 8 : return;
595 : }
596 :
597 83 : if (in_str_type == IS_UNICODE) {
598 21 : char *utf8_str = NULL;
599 : int utf8_str_len;
600 21 : UErrorCode status = U_ZERO_ERROR;
601 :
602 21 : zend_unicode_to_string_ex(UG(utf8_conv), &utf8_str, &utf8_str_len, in_str.u, in_str_len, &status);
603 21 : if (U_FAILURE(status)) {
604 0 : if (utf8_str) {
605 0 : efree(utf8_str);
606 : }
607 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not convert argument to UTF-8");
608 0 : RETURN_FALSE;
609 : }
610 :
611 21 : out_str = php_url_encode(utf8_str, utf8_str_len, &out_str_len);
612 21 : efree(utf8_str);
613 21 : php_error_docref(NULL TSRMLS_CC, E_STRICT, "expecting binary parameter, received Unicode parameter was converted to UTF-8");
614 : } else {
615 62 : out_str = php_url_encode(in_str.s, in_str_len, &out_str_len);
616 : }
617 83 : RETURN_STRINGL(out_str, out_str_len, 0);
618 : }
619 : /* }}} */
620 :
621 : /* {{{ proto binary urldecode(binary str) U
622 : Decodes URL-encoded string */
623 : PHP_FUNCTION(urldecode)
624 38 : {
625 : char *in_str, *out_str;
626 : int in_str_len, out_str_len;
627 :
628 38 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &in_str,
629 : &in_str_len) == FAILURE) {
630 14 : return;
631 : }
632 :
633 24 : out_str = estrndup(in_str, in_str_len);
634 24 : out_str_len = php_url_decode(out_str, in_str_len);
635 :
636 24 : RETURN_STRINGL(out_str, out_str_len, 0);
637 : }
638 : /* }}} */
639 :
640 : /* {{{ php_url_decode
641 : */
642 : PHPAPI int php_url_decode(char *str, int len)
643 514 : {
644 514 : char *dest = str;
645 514 : char *data = str;
646 :
647 3966 : while (len--) {
648 2938 : if (*data == '+') {
649 26 : *dest = ' ';
650 : }
651 3000 : else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
652 : && isxdigit((int) *(data + 2))) {
653 : #ifndef CHARSET_EBCDIC
654 88 : *dest = (char) php_htoi(data + 1);
655 : #else
656 : *dest = os_toebcdic[(char) php_htoi(data + 1)];
657 : #endif
658 88 : data += 2;
659 88 : len -= 2;
660 : } else {
661 2824 : *dest = *data;
662 : }
663 2938 : data++;
664 2938 : dest++;
665 : }
666 514 : *dest = '\0';
667 514 : return dest - str;
668 : }
669 : /* }}} */
670 :
671 : /* {{{ php_raw_url_encode
672 : */
673 : PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)
674 21 : {
675 : register int x, y;
676 : unsigned char *str;
677 :
678 21 : str = (unsigned char *) safe_emalloc(3, len, 1);
679 210 : for (x = 0, y = 0; len--; x++, y++) {
680 189 : str[y] = (unsigned char) s[x];
681 : #ifndef CHARSET_EBCDIC
682 189 : if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||
683 : (str[y] < 'A' && str[y] > '9') ||
684 : (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
685 : (str[y] > 'z' && str[y] != '~')) {
686 36 : str[y++] = '%';
687 36 : str[y++] = hexchars[(unsigned char) s[x] >> 4];
688 36 : str[y] = hexchars[(unsigned char) s[x] & 15];
689 : #else /*CHARSET_EBCDIC*/
690 : if (!isalnum(str[y]) && strchr("_-.", str[y]) != NULL) {
691 : str[y++] = '%';
692 : str[y++] = hexchars[os_toascii[(unsigned char) s[x]] >> 4];
693 : str[y] = hexchars[os_toascii[(unsigned char) s[x]] & 15];
694 : #endif /*CHARSET_EBCDIC*/
695 : }
696 : }
697 21 : str[y] = '\0';
698 21 : if (new_length) {
699 21 : *new_length = y;
700 : }
701 21 : return ((char *) str);
702 : }
703 : /* }}} */
704 :
705 : /* {{{ proto binary rawurlencode(binary str) U
706 : URL-encodes string */
707 : PHP_FUNCTION(rawurlencode)
708 29 : {
709 : zstr in_str;
710 : char *out_str;
711 : int in_str_len, out_str_len;
712 : zend_uchar in_str_type;
713 :
714 29 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &in_str,
715 : &in_str_len, &in_str_type) == FAILURE) {
716 8 : return;
717 : }
718 :
719 21 : if (in_str_type == IS_UNICODE) {
720 19 : char *utf8_str = NULL;
721 : int utf8_str_len;
722 19 : UErrorCode status = U_ZERO_ERROR;
723 :
724 19 : zend_unicode_to_string_ex(UG(utf8_conv), &utf8_str, &utf8_str_len, in_str.u, in_str_len, &status);
725 19 : if (U_FAILURE(status)) {
726 0 : if (utf8_str) {
727 0 : efree(utf8_str);
728 : }
729 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not convert argument to UTF-8");
730 0 : RETURN_FALSE;
731 : }
732 :
733 19 : out_str = php_raw_url_encode(utf8_str, utf8_str_len, &out_str_len);
734 19 : efree(utf8_str);
735 19 : php_error_docref(NULL TSRMLS_CC, E_STRICT, "expecting binary parameter, received Unicode parameter was converted to UTF-8");
736 : } else {
737 2 : out_str = php_raw_url_encode(in_str.s, in_str_len, &out_str_len);
738 : }
739 21 : RETURN_STRINGL(out_str, out_str_len, 0);
740 : }
741 : /* }}} */
742 :
743 : /* {{{ proto binary rawurldecode(binary str) U
744 : Decodes URL-encodes string */
745 : PHP_FUNCTION(rawurldecode)
746 28 : {
747 : char *in_str, *out_str;
748 : int in_str_len, out_str_len;
749 :
750 28 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &in_str,
751 : &in_str_len) == FAILURE) {
752 10 : return;
753 : }
754 :
755 18 : out_str = estrndup(in_str, in_str_len);
756 18 : out_str_len = php_raw_url_decode(out_str, in_str_len);
757 :
758 18 : RETURN_STRINGL(out_str, out_str_len, 0);
759 : }
760 : /* }}} */
761 :
762 : /* {{{ php_raw_url_decode
763 : */
764 : PHPAPI int php_raw_url_decode(char *str, int len)
765 18 : {
766 18 : char *dest = str;
767 18 : char *data = str;
768 :
769 183 : while (len--) {
770 177 : if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
771 : && isxdigit((int) *(data + 2))) {
772 : #ifndef CHARSET_EBCDIC
773 30 : *dest = (char) php_htoi(data + 1);
774 : #else
775 : *dest = os_toebcdic[(char) php_htoi(data + 1)];
776 : #endif
777 30 : data += 2;
778 30 : len -= 2;
779 : } else {
780 117 : *dest = *data;
781 : }
782 147 : data++;
783 147 : dest++;
784 : }
785 18 : *dest = '\0';
786 18 : return dest - str;
787 : }
788 : /* }}} */
789 :
790 : /* {{{ proto array get_headers(string url[, int format]) U
791 : fetches all the headers sent by the server in response to a HTTP request */
792 : PHP_FUNCTION(get_headers)
793 5 : {
794 : char *url;
795 : int url_len;
796 : php_stream_context *context;
797 : php_stream *stream;
798 5 : zval **prev_val, **hdr = NULL, **h;
799 : HashPosition pos;
800 : HashTable *hashT;
801 5 : long format = 0;
802 :
803 5 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &url, &url_len, &format) == FAILURE) {
804 5 : return;
805 : }
806 0 : context = FG(default_context) ? FG(default_context) : (FG(default_context) = php_stream_context_alloc());
807 :
808 0 : if (!(stream = php_stream_open_wrapper_ex(url, "r", REPORT_ERRORS | STREAM_USE_URL | STREAM_ONLY_GET_HEADERS, NULL, context))) {
809 0 : RETURN_FALSE;
810 : }
811 :
812 0 : if (!stream->wrapperdata || Z_TYPE_P(stream->wrapperdata) != IS_ARRAY) {
813 0 : php_stream_close(stream);
814 0 : RETURN_FALSE;
815 : }
816 :
817 0 : array_init(return_value);
818 :
819 : /* check for curl-wrappers that provide headers via a special "headers" element */
820 0 : if (zend_hash_find(HASH_OF(stream->wrapperdata), "headers", sizeof("headers"), (void **)&h) != FAILURE && Z_TYPE_PP(h) == IS_ARRAY) {
821 : /* curl-wrappers don't load data until the 1st read */
822 0 : if (!Z_ARRVAL_PP(h)->nNumOfElements) {
823 0 : php_stream_getc(stream);
824 : }
825 0 : zend_hash_find(HASH_OF(stream->wrapperdata), "headers", sizeof("headers"), (void **)&h);
826 0 : hashT = Z_ARRVAL_PP(h);
827 : } else {
828 0 : hashT = HASH_OF(stream->wrapperdata);
829 : }
830 :
831 0 : zend_hash_internal_pointer_reset_ex(hashT, &pos);
832 0 : while (zend_hash_get_current_data_ex(hashT, (void**)&hdr, &pos) != FAILURE) {
833 0 : if (!hdr || Z_TYPE_PP(hdr) != IS_STRING) {
834 0 : zend_hash_move_forward_ex(hashT, &pos);
835 0 : continue;
836 : }
837 :
838 0 : if (!format) {
839 0 : no_name_header:
840 0 : add_next_index_stringl(return_value, Z_STRVAL_PP(hdr), Z_STRLEN_PP(hdr), 1);
841 : } else {
842 : char c;
843 : char *s, *p;
844 :
845 0 : if ((p = strchr(Z_STRVAL_PP(hdr), ':'))) {
846 0 : c = *p;
847 0 : *p = '\0';
848 0 : s = p + 1;
849 0 : while (isspace((int)*(unsigned char *)s)) {
850 0 : s++;
851 : }
852 :
853 0 : if (zend_hash_find(HASH_OF(return_value), Z_STRVAL_PP(hdr), (p - Z_STRVAL_PP(hdr) + 1), (void **) &prev_val) == FAILURE) {
854 0 : 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);
855 : } else { /* some headers may occur more then once, therefor we need to remake the string into an array */
856 0 : convert_to_array(*prev_val);
857 0 : add_next_index_stringl(*prev_val, s, (Z_STRLEN_PP(hdr) - (s - Z_STRVAL_PP(hdr))), 1);
858 : }
859 :
860 0 : *p = c;
861 : } else {
862 0 : goto no_name_header;
863 : }
864 : }
865 0 : zend_hash_move_forward_ex(hashT, &pos);
866 : }
867 :
868 0 : php_stream_close(stream);
869 : }
870 : /* }}} */
871 :
872 : /*
873 : * Local variables:
874 : * tab-width: 4
875 : * c-basic-offset: 4
876 : * End:
877 : * vim600: sw=4 ts=4 fdm=marker
878 : * vim<600: sw=4 ts=4
879 : */
|