+static inline int eol_match(char **line, int *eol_len)
+{
+ char *ptr = *line;
+
+ while (0x20 == *ptr) ++ptr;
+
+ if (ptr == http_locate_eol(*line, eol_len)) {
+ *line = ptr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
+PHP_HTTP_API const char *_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
+{
+ int eol_len = 0;
+ char *n_ptr = NULL;
+ const char *e_ptr = encoded;
+
+ *decoded_len = 0;
+ *decoded = ecalloc(1, encoded_len);
+
+ while ((encoded + encoded_len - e_ptr) > 0) {
+ ulong chunk_len = 0, rest;
+
+ chunk_len = strtoul(e_ptr, &n_ptr, 16);
+
+ /* we could not read in chunk size */
+ if (n_ptr == e_ptr) {
+ /*
+ * if this is the first turn and there doesn't seem to be a chunk
+ * size at the begining of the body, do not fail on apparently
+ * not encoded data and return a copy
+ */
+ if (e_ptr == encoded) {
+ http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
+ memcpy(*decoded, encoded, encoded_len);
+ *decoded_len = encoded_len;
+ return encoded + encoded_len;
+ } else {
+ efree(*decoded);
+ http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
+ return NULL;
+ }
+ }
+
+ /* reached the end */
+ if (!chunk_len) {
+ /* move over '0' chunked encoding terminator */
+ while (*e_ptr == '0') ++e_ptr;
+ break;
+ }
+
+ /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
+ if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
+ if (eol_len == 2) {
+ http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1));
+ } else {
+ http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr);
+ }
+ }
+ n_ptr += eol_len;
+
+ /* chunk size pretends more data than we actually got, so it's probably a truncated message */
+ if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
+ http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len);
+ chunk_len = rest;
+ }
+
+ /* copy the chunk */
+ memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
+ *decoded_len += chunk_len;
+
+ if (chunk_len == rest) {
+ e_ptr = n_ptr + chunk_len;
+ break;
+ } else {
+ /* advance to next chunk */
+ e_ptr = n_ptr + chunk_len + eol_len;
+ }
+ }
+
+ return e_ptr;
+}
+/* }}} */