-
- default:
- return RANGE_NO;
- break;
- }
- } while (c != 0);
-
- return RANGE_OK;
-}
-/* }}} */
-
-/* {{{ STATUS http_send_ranges(HashTable *, void *, size_t, http_send_mode) */
-PHP_HTTP_API STATUS _http_send_ranges(HashTable *ranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC)
-{
- long **begin, **end;
- zval **zrange;
-
- /* single range */
- if (zend_hash_num_elements(ranges) == 1) {
- char range_header[256] = {0};
-
- if (SUCCESS != zend_hash_index_find(ranges, 0, (void **) &zrange) ||
- SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin) ||
- SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end)) {
- return FAILURE;
- }
-
- /* Send HTTP 206 Partial Content */
- http_send_status(206);
-
- /* send content range header */
- snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", **begin, **end, size);
- http_send_header(range_header);
-
- /* send requested chunk */
- return http_send_chunk(data, **begin, **end + 1, mode);
- }
-
- /* multi range */
- else {
- char bound[23] = {0}, preface[1024] = {0},
- multi_header[68] = "Content-Type: multipart/byteranges; boundary=";
-
- /* Send HTTP 206 Partial Content */
- http_send_status(206);
-
- /* send multipart/byteranges header */
- snprintf(bound, 22, "--%d%0.9f", time(NULL), php_combined_lcg(TSRMLS_C));
- strncat(multi_header, bound + 2, 21);
- http_send_header(multi_header);
-
- /* send each requested chunk */
- FOREACH_HASH_VAL(ranges, zrange) {
- if (SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin) ||
- SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end)) {
- break;
- }
-
- snprintf(preface, 1023,
- HTTP_CRLF "%s"
- HTTP_CRLF "Content-Type: %s"
- HTTP_CRLF "Content-Range: bytes %ld-%ld/%ld"
- HTTP_CRLF
- HTTP_CRLF,
-
- bound,
- HTTP_G(ctype) ? HTTP_G(ctype) : "application/x-octetstream",
- **begin,
- **end,
- size
- );
-
- php_body_write(preface, strlen(preface) TSRMLS_CC);
- http_send_chunk(data, **begin, **end + 1, mode);
- }
-
- /* write boundary once more */
- php_body_write(HTTP_CRLF, sizeof(HTTP_CRLF) - 1 TSRMLS_CC);
- php_body_write(bound, strlen(bound) TSRMLS_CC);
- php_body_write("--", 2 TSRMLS_CC);
-
- return SUCCESS;
- }
-}
-/* }}} */
-
-/* {{{ STATUS http_send(void *, size_t, http_send_mode) */
-PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
- const http_send_mode data_mode TSRMLS_DC)
-{
- int is_range_request = http_is_range_request();
-
- if (!data_ptr) {
- return FAILURE;
- }
- if (!data_size) {
- return SUCCESS;
- }
-
- /* etag handling */
- if (HTTP_G(etag_started)) {
- char *etag;
- /* interrupt */
- HTTP_G(etag_started) = 0;
- /* never ever use the output to compute the ETag if http_send() is used */
- php_end_ob_buffer(0, 0 TSRMLS_CC);
- if (!(etag = http_etag(data_ptr, data_size, data_mode))) {
- return FAILURE;
- }
-
- /* send 304 Not Modified if etag matches */
- if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
- efree(etag);
- return http_send_status(304);
- }
-
- http_send_etag(etag, 32);
- efree(etag);
- }
-
- /* send 304 Not Modified if last-modified matches*/
- if ((!is_range_request) && http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) {
- return http_send_status(304);
- }
-
- if (is_range_request) {
-
- /* only send ranges if entity hasn't changed */
- if (
- ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_MATCH", 13)) ||
- http_etag_match("HTTP_IF_MATCH", HTTP_G(etag)))
- &&
- ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_UNMODIFIED_SINCE", 25)) ||
- http_modified_match("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod)))
- ) {
-
- STATUS result = FAILURE;
- HashTable ranges;
- zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
-
- switch (http_get_request_ranges(&ranges, data_size))
- {
- case RANGE_NO:
- zend_hash_destroy(&ranges);
- /* go ahead and send all */
- break;
-
- case RANGE_OK:
- result = http_send_ranges(&ranges, data_ptr, data_size, data_mode);
- zend_hash_destroy(&ranges);
- return result;
- break;
-
- case RANGE_ERR:
- zend_hash_destroy(&ranges);
- http_send_status(416);
- return FAILURE;
- break;
-
- default:
- return FAILURE;
- break;
- }
- }
- }
- /* send all */
- return http_send_chunk(data_ptr, 0, data_size, data_mode);
-}
-/* }}} */
-
-/* {{{ STATUS http_send_stream(php_stream *) */
-PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file,
- zend_bool close_stream TSRMLS_DC)
-{
- STATUS status;
-
- if ((!file) || php_stream_stat(file, &HTTP_G(ssb))) {
- return FAILURE;
- }
-
- status = http_send(file, HTTP_G(ssb).sb.st_size, SEND_RSRC);
-
- if (close_stream) {
- php_stream_close(file);
- }
-
- return status;
-}
-/* }}} */
-
-/* {{{ STATUS http_chunked_decode(char *, size_t, char **, size_t *) */
-PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded,
- const size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
-{
- const char *e_ptr;
- char *d_ptr;
-
- *decoded_len = 0;
- *decoded = ecalloc(1, encoded_len);
- d_ptr = *decoded;
- e_ptr = encoded;
-
- while (((e_ptr - encoded) - encoded_len) > 0) {
- char hex_len[9] = {0};
- size_t chunk_len = 0;
- int i = 0;
-
- /* read in chunk size */
- while (isxdigit(*e_ptr)) {
- if (i == 9) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Chunk size is too long: 0x%s...", hex_len);
- efree(*decoded);
- return FAILURE;
- }
- hex_len[i++] = *e_ptr++;
- }
-
- /* reached the end */
- if (!strcmp(hex_len, "0")) {
- break;
- }
-
- /* new line */
- if (strncmp(e_ptr, HTTP_CRLF, 2)) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Invalid character (expected 0x0D 0x0A; got: %x %x)",
- *e_ptr, *(e_ptr + 1));
- efree(*decoded);
- return FAILURE;
- }
-
- /* hex to long */
- {
- char *error = NULL;
- chunk_len = strtol(hex_len, &error, 16);
- if (error == hex_len) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Invalid chunk size string: '%s'", hex_len);
- efree(*decoded);
- return FAILURE;
- }
- }
-
- memcpy(d_ptr, e_ptr += 2, chunk_len);
- d_ptr += chunk_len;
- e_ptr += chunk_len + 2;
- *decoded_len += chunk_len;
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ STATUS http_split_response(zval *, zval *, zval *) */
-PHP_HTTP_API STATUS _http_split_response(zval *response, zval *headers, zval *body TSRMLS_DC)
-{
- char *b = NULL;
- size_t l = 0;
- STATUS status = http_split_response_ex(Z_STRVAL_P(response), Z_STRLEN_P(response), Z_ARRVAL_P(headers), &b, &l);
- ZVAL_STRINGL(body, b, l, 0);
- return status;
-}
-/* }}} */
-
-/* {{{ STATUS http_split_response(char *, size_t, HashTable *, char **, size_t *) */
-PHP_HTTP_API STATUS _http_split_response_ex(char *response,
- size_t response_len, HashTable *headers, char **body, size_t *body_len TSRMLS_DC)
-{
- char *header = response, *real_body = NULL;
-
- while (0 < (response_len - (response - header + 4))) {
- if ( (*response++ == '\r') &&
- (*response++ == '\n') &&
- (*response++ == '\r') &&
- (*response++ == '\n')) {
- real_body = response;
- break;
- }
- }
-
- if (real_body && (*body_len = (response_len - (real_body - header)))) {
- *body = ecalloc(1, *body_len + 1);
- memcpy(*body, real_body, *body_len);
- }
-
- return http_parse_headers_ex(header, real_body ? response_len - *body_len : response_len, headers, 1);
-}
-/* }}} */
-
-/* {{{ STATUS http_parse_headers(char *, long, zval *) */
-PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, int header_len,
- HashTable *headers, zend_bool prettify TSRMLS_DC)
-{
- char *colon = NULL, *line = NULL, *begin = header;
- zval array;
-
- Z_ARRVAL(array) = headers;
-
- if (header_len < 2) {
- return FAILURE;
- }
-
- /* status code */
- if (!strncmp(header, "HTTP/1.", 7)) {
- char *end = strstr(header, HTTP_CRLF);
- add_assoc_stringl(&array, "Status",
- header + sizeof("HTTP/1.x ") - 1,
- end - (header + sizeof("HTTP/1.x ") - 1), 1);
- header = end + 2;
- }
-
- line = header;
-
- while (header_len >= (line - begin)) {
- int value_len = 0;
-
- switch (*line++)
- {
- case 0:
- --value_len; /* we don't have CR so value length is one char less */
- case '\n':
- if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) {
-
- /* skip empty key */
- if (header != colon) {
- char *key = estrndup(header, colon - header);
-
- if (prettify) {
- key = pretty_key(key, colon - header, 1, 1);