- return SUCCESS;
-}
-/* }}} */
-
-/* {{{ char *http_absolute_uri(char *) */
-PHP_HTTP_API char *_http_absolute_uri_ex(
- const char *url, size_t url_len,
- const char *proto, size_t proto_len,
- const char *host, size_t host_len,
- unsigned port TSRMLS_DC)
-{
-#if defined(PHP_WIN32) || defined(HAVE_NETDB_H)
- struct servent *se;
-#endif
- php_url *purl, furl = {NULL};
- size_t full_len = 0;
- zval *zhost = NULL;
- char *scheme = NULL, *URL = ecalloc(1, HTTP_URI_MAXLEN + 1);
-
- if ((!url || !url_len) && (
- (!(url = SG(request_info).request_uri)) ||
- (!(url_len = strlen(SG(request_info).request_uri))))) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING,
- "Cannot build an absolute URI if supplied URL and REQUEST_URI is empty");
- return NULL;
- }
-
- if (!(purl = php_url_parse((char *) url))) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse supplied URL");
- return NULL;
- }
-
- furl.user = purl->user;
- furl.pass = purl->pass;
- furl.path = purl->path;
- furl.query = purl->query;
- furl.fragment = purl->fragment;
-
- if (proto && proto_len) {
- furl.scheme = scheme = estrdup(proto);
- } else if (purl->scheme) {
- furl.scheme = purl->scheme;
-#if defined(PHP_WIN32) || defined(HAVE_NETDB_H)
- } else if (port && (se = getservbyport(port, "tcp"))) {
- furl.scheme = (scheme = estrdup(se->s_name));
-#endif
- } else {
- furl.scheme = "http";
- }
-
- if (port) {
- furl.port = port;
- } else if (purl->port) {
- furl.port = purl->port;
- } else if (strncmp(furl.scheme, "http", 4)) {
-#if defined(PHP_WIN32) || defined(HAVE_NETDB_H)
- if (se = getservbyname(furl.scheme, "tcp")) {
- furl.port = se->s_port;
- }
-#endif
- } else {
- furl.port = (furl.scheme[4] == 's') ? 443 : 80;
- }
-
- if (host) {
- furl.host = (char *) host;
- } else if (purl->host) {
- furl.host = purl->host;
- } else if ( (zhost = http_get_server_var("HTTP_HOST")) ||
- (zhost = http_get_server_var("SERVER_NAME"))) {
- furl.host = Z_STRVAL_P(zhost);
- } else {
- furl.host = "localhost";
- }
-
-#define HTTP_URI_STRLCATS(URL, full_len, add_string) HTTP_URI_STRLCAT(URL, full_len, add_string, sizeof(add_string)-1)
-#define HTTP_URI_STRLCATL(URL, full_len, add_string) HTTP_URI_STRLCAT(URL, full_len, add_string, strlen(add_string))
-#define HTTP_URI_STRLCAT(URL, full_len, add_string, add_len) \
- if ((full_len += add_len) > HTTP_URI_MAXLEN) { \
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, \
- "Absolute URI would have exceeded max URI length (%d bytes) - " \
- "tried to add %d bytes ('%s')", \
- HTTP_URI_MAXLEN, add_len, add_string); \
- if (scheme) { \
- efree(scheme); \
- } \
- php_url_free(purl); \
- return URL; \
- } else { \
- strcat(URL, add_string); \
- }
-
- HTTP_URI_STRLCATL(URL, full_len, furl.scheme);
- HTTP_URI_STRLCATS(URL, full_len, "://");
-
- if (furl.user) {
- HTTP_URI_STRLCATL(URL, full_len, furl.user);
- if (furl.pass) {
- HTTP_URI_STRLCATS(URL, full_len, ":");
- HTTP_URI_STRLCATL(URL, full_len, furl.pass);
- }
- HTTP_URI_STRLCATS(URL, full_len, "@");
- }
-
- HTTP_URI_STRLCATL(URL, full_len, furl.host);
-
- if ( (!strcmp(furl.scheme, "http") && (furl.port != 80)) ||
- (!strcmp(furl.scheme, "https") && (furl.port != 443))) {
- char port_string[8] = {0};
- snprintf(port_string, 7, ":%u", furl.port);
- HTTP_URI_STRLCATL(URL, full_len, port_string);
- }
-
- if (furl.path) {
- if (furl.path[0] != '/') {
- HTTP_URI_STRLCATS(URL, full_len, "/");
- }
- HTTP_URI_STRLCATL(URL, full_len, furl.path);
- } else {
- HTTP_URI_STRLCATS(URL, full_len, "/");
- }
-
- if (furl.query) {
- HTTP_URI_STRLCATS(URL, full_len, "?");
- HTTP_URI_STRLCATL(URL, full_len, furl.query);
- }
-
- if (furl.fragment) {
- HTTP_URI_STRLCATS(URL, full_len, "#");
- HTTP_URI_STRLCATL(URL, full_len, furl.fragment);
- }
-
- if (scheme) {
- efree(scheme);
- }
- php_url_free(purl);
-
- return URL;
-}
-/* }}} */
-
-/* {{{ char *http_negotiate_q(char *, HashTable *, char *) */
-PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC)
-{
- zval *zaccept, zdelim, zarray, zentries, **zentry, **zsupp;
- char *q_ptr = NULL, *key = NULL;
- int i = 0, idx = 0;
- double qual;
-
- HTTP_GSC(zaccept, entry, estrdup(def));
-
- array_init(&zarray);
- array_init(&zentries);
-
- Z_STRVAL(zdelim) = ",";
- Z_STRLEN(zdelim) = 1;
-
- php_explode(&zdelim, zaccept, &zarray, -1);
-
- FOREACH_HASH_VAL(Z_ARRVAL(zarray), zentry) {
- if (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';')) {
- qual = strtod(q_ptr + 3, NULL);
- *q_ptr = 0;
- q_ptr = NULL;
- } else {
- qual = 1000.0 - i++;
- }
- FOREACH_HASH_VAL((HashTable *)supported, zsupp) {
- if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) {
- add_assoc_double(&zentries, Z_STRVAL_PP(zsupp), qual);
- break;
- }
- }
- }
- zval_dtor(&zarray);
-
- zend_hash_sort(Z_ARRVAL(zentries), zend_qsort, http_sort_q, 0 TSRMLS_CC);
-
- FOREACH_HASH_KEY(Z_ARRVAL(zentries), key, idx) {
- if (key) {
- key = estrdup(key);
- zval_dtor(&zentries);
- return key;
- }
- }
- zval_dtor(&zentries);
-
- return estrdup(def);
-}
-/* }}} */
-
-/* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */
-PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC)
-{
- zval *zrange;
- char *range, c;
- long begin = -1, end = -1, *ptr;
-
- HTTP_GSC(zrange, "HTTP_RANGE", RANGE_NO);
- range = Z_STRVAL_P(zrange);
-
- if (strncmp(range, "bytes=", sizeof("bytes=") - 1)) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Range header misses bytes=");
- return RANGE_NO;
- }
-
- ptr = &begin;
- range += sizeof("bytes=") - 1;
-
- do {
- switch (c = *(range++))
- {
- case '0':
- *ptr *= 10;
- break;
-
- case '1': case '2': case '3':
- case '4': case '5': case '6':
- case '7': case '8': case '9':
- /*
- * If the value of the pointer is already set (non-negative)
- * then multiply its value by ten and add the current value,
- * else initialise the pointers value with the current value
- * --
- * This let us recognize empty fields when validating the
- * ranges, i.e. a "-10" for begin and "12345" for the end
- * was the following range request: "Range: bytes=0-12345";
- * While a "-1" for begin and "12345" for the end would
- * have been: "Range: bytes=-12345".
- */
- if (*ptr > 0) {
- *ptr *= 10;
- *ptr += c - '0';
- } else {
- *ptr = c - '0';
- }
- break;
-
- case '-':
- ptr = &end;
- break;
-
- case ' ':
- /* IE - ignore for now */
- break;
-
- case 0:
- case ',':
-
- if (length) {
- /* validate ranges */
- switch (begin)
- {
- /* "0-12345" */
- case -10:
- /* "0-" */
- if (end == -1) {
- return RANGE_NO;
- }
- /* "0-0" or overflow */
- if (end == -10 || length <= end) {
- return RANGE_ERR;
- }
- begin = 0;
- break;
-
- /* "-12345" */
- case -1:
- /* "-", "-0" or overflow */
- if (end == -1 || end == -10 || length <= end) {
- return RANGE_ERR;
- }
- begin = length - end;
- end = length - 1;
- break;
-
- /* "12345-(xxx)" */
- default:
- switch (end)
- {
- /* "12345-0" */
- case -10:
- return RANGE_ERR;
- break;
-
- /* "12345-" */
- case -1:
- if (length <= begin) {
- return RANGE_ERR;
- }
- end = length - 1;
- break;
-
- /* "12345-67890" */
- default:
- if ( (length <= begin) ||
- (length <= end) ||
- (end < begin)) {
- return RANGE_ERR;
- }
- break;
- }
- break;
- }
- }
- {
- zval *zentry;
- MAKE_STD_ZVAL(zentry);
- array_init(zentry);
- add_index_long(zentry, 0, begin);
- add_index_long(zentry, 1, end);
- zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
-
- begin = -1;
- end = -1;
- ptr = &begin;
- }
- break;
-
- 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, size_t size, 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, size_t data_size, http_send_mode data_mode TSRMLS_DC)
-{
- HashTable ranges;
- http_range_status range_status;
- int cache_etag = 0;
-
- if (!data_ptr) {
- return FAILURE;
- }
- if (!data_size) {
- return SUCCESS;
- }
-
- /* stop on-the-fly etag generation */
- if (cache_etag = HTTP_G(etag_started)) {
- /* interrupt */
- HTTP_G(etag_started) = 0;
- /* never ever use the output to compute the ETag if http_send() is used */
- php_end_ob_buffers(0 TSRMLS_CC);
- }
-
- zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
- range_status = http_get_request_ranges(&ranges, data_size);
-
- if (range_status == RANGE_ERR) {
- zend_hash_destroy(&ranges);
- http_send_status(416);
- return FAILURE;
- }
-
- /* Range Request - only send ranges if entity hasn't changed */
- if ( range_status == RANGE_OK &&
- http_etag_match_ex("HTTP_IF_MATCH", HTTP_G(etag), 0) &&
- http_modified_match_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod), 0)) {
- STATUS result = http_send_ranges(&ranges, data_ptr, data_size, data_mode);
- zend_hash_destroy(&ranges);
- return result;
- }
-
- zend_hash_destroy(&ranges);
-
- /* send 304 Not Modified if etag matches */
- if (cache_etag) {
- char *etag = NULL;
- int etag_match = 0;
-
- if (!(etag = http_etag(data_ptr, data_size, data_mode))) {
- return FAILURE;
- }
-
- http_send_etag(etag, 32);
- etag_match = http_etag_match("HTTP_IF_NONE_MATCH", etag);
- efree(etag);
-
- if (etag_match) {
- return http_send_status(304);
- }
- }
-
- /* send 304 Not Modified if last modified matches */
- if (http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) {
- return http_send_status(304);
- }
-
- /* send full entity */
- 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);