url.c 18 KB

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