/* $Id$ */
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include <ctype.h>
+
#include "php.h"
#include "php_version.h"
#include "php_streams.h"
#include "SAPI.h"
-#if (PHP_MAJOR_VERSION >= 5)
+#ifdef ZEND_ENGINE_2
#include "ext/standard/php_http.h"
#else
-#include "php_http_build_query.h"
-#include "http_build_query.c"
+#include "php_http_build_query.c"
#endif
#include "php_http.h"
#include "php_http_api.h"
-#include <ctype.h>
-#if defined(HAVE_CURL) && HAVE_CURL
+#ifdef HTTP_HAVE_CURL
+
+#ifdef PHP_WIN32
+#include <winsock2.h>
+#include <sys/types.h>
+#endif
+
#include <curl/curl.h>
#include <curl/easy.h>
#endif
+
ZEND_DECLARE_MODULE_GLOBALS(http)
/* {{{ day/month names */
static int check_tzone(char *tzone);
/* {{{ HAVE_CURL */
-#if defined(HAVE_CURL) && HAVE_CURL
+#ifdef HTTP_HAVE_CURL
#define http_curl_initbuf(m) _http_curl_initbuf((m) TSRMLS_CC)
static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC);
#define http_curl_freebuf(m) _http_curl_freebuf((m) TSRMLS_CC)
/* }}} */
/* {{{ HAVE_CURL */
-#if defined(HAVE_CURL) && HAVE_CURL
+#ifdef HTTP_HAVE_CURL
/* {{{ static inline void http_curl_initbuf(http_curlbuf_member) */
static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC)
curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1);
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, http_curl_body_callback);
curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, http_curl_hdrs_callback);
-#if defined(ZTS)
+#ifdef ZTS
curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1);
#endif
-#if defined(PHP_DEBUG)
- curl_easy_setopt(ch, CURLOPT_VERBOSE, 1);
-#endif
if ((!options) || (1 > zend_hash_num_elements(options))) {
return;
}
}
smart_str_0(&qstr);
-
+
if (qstr.c) {
curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c);
}
char *header_key, header[1024] = {0};
zval **header_val;
struct curl_slist *headers = NULL;
-
+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption));
while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) {
if (key_type == HASH_KEY_IS_STRING) {
{
zval **var;
if (SUCCESS == zend_hash_find(
- Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]),
+ HTTP_SERVER_VARS,
(char *) key, strlen(key) + 1, (void **) &var)) {
return *var;
}
if (mode & PHP_OUTPUT_HANDLER_END) {
PHP_MD5Final(digest, &HTTP_G(etag_md5));
- make_digest(etag, digest);
- if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
- http_send_status(304);
- } else {
- http_send_etag(etag, 32);
+ /* just do that if desired */
+ if (HTTP_G(etag_started)) {
+ make_digest(etag, digest);
+
+ if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
+ http_send_status(304);
+ } else {
+ http_send_etag(etag, 32);
+ }
}
}
} else {
result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag));
}
-
efree(quoted_etag);
return result;
}
/* }}} */
/* {{{ STATUS http_send_last_modified(int) */
-PHP_HTTP_API STATUS _http_send_last_modified(const int t TSRMLS_DC)
+PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC)
{
char modified[96] = "Last-Modified: ", *date;
date = http_date(t);
strcat(modified, date);
efree(date);
+
+ /* remember */
+ HTTP_G(lmod) = t;
+
return http_send_header(modified);
}
/* }}} */
snprintf(etag_header, header_len, "ETag: \"%s\"", etag);
ret = http_send_header(etag_header);
efree(etag_header);
+
+ /* remember */
+ if (HTTP_G(etag)) {
+ efree(HTTP_G(etag));
+ }
+ HTTP_G(etag) = estrdup(etag);
+
return ret;
}
/* }}} */
if (NULL != (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';'))) {
qual = strtod(q_ptr + 3, NULL);
} else {
- qual = 1000.0 - 1;
+ qual = 1000.0 - i;
}
/* walk through the supported array */
PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges,
const size_t length TSRMLS_DC)
{
- zval *zrange, *zentry, **zentries;
+ zval *zrange;
char *range, c;
long begin = -1, end = -1, *ptr;
ptr = &end;
break;
+ case ' ':
+ /* IE - ignore for now */
+ break;
+
case 0:
case ',':
break;
}
}
-
- zentry = (zval *) zentries++;
- MAKE_STD_ZVAL(zentry);
- array_init(zentry);
- add_index_long(zentry, 0, begin);
- add_index_long(zentry, 1, end);
- add_next_index_zval(zranges, zentry);
-
- begin = -1;
- end = -1;
- ptr = &begin;
+ {
+ zval *zentry;
+ MAKE_STD_ZVAL(zentry);
+ array_init(zentry);
+ add_index_long(zentry, 0, begin);
+ add_index_long(zentry, 1, end);
+ add_next_index_zval(zranges, zentry);
+
+ begin = -1;
+ end = -1;
+ ptr = &begin;
+ }
break;
default:
PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC)
{
zval **zrange;
- long b, e, **begin, **end;
+ long **begin, **end;
char range_header[255], multi_header[68] = "Content-Type: multipart/byteranges; boundary=", bound[23], preface[1024];
int i, c;
zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin);
zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end);
- /* so zranges can be freed */
- b = **begin;
- e = **end;
-
- /* free zranges */
- zval_dtor(zranges);
- efree(zranges);
-
/* send content range header */
- snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", b, e, size);
+ 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, b, e + 1, mode);
+ return http_send_chunk(data, **begin, **end + 1, mode);
}
/* multi range */
**begin, **end, size);
php_body_write(preface, strlen(preface) TSRMLS_CC);
http_send_chunk(data, **begin, **end + 1, mode);
-
}
- /* free zranges */
- zval_dtor(zranges);
- efree(zranges);
-
/* write boundary once more */
- php_body_write("\n", 1 TSRMLS_CC);
+ php_body_write("\r\n", 1 TSRMLS_CC);
php_body_write(bound, strlen(bound) TSRMLS_CC);
return SUCCESS;
PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
const http_send_mode data_mode TSRMLS_DC)
{
- zval *zranges = NULL;
+ char *new_etag = NULL;
+ int is_range_request = http_is_range_request();
if (!data_ptr) {
return FAILURE;
}
- /* never ever use the output to compute the ETag if http_send() is used */
+ /* etag handling */
if (HTTP_G(etag_started)) {
- char *new_etag = (char *) emalloc(33);
+ new_etag = (char *) emalloc(33);
+ /* never ever use the output to compute the ETag if http_send() is used */
+ HTTP_G(etag_started) = 0;
php_end_ob_buffer(0, 0 TSRMLS_CC);
if (NULL == http_etag(&new_etag, data_ptr, data_size, data_mode)) {
efree(new_etag);
}
/* send 304 Not Modified if etag matches */
- if (http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) {
+ if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) {
efree(new_etag);
return http_send_status(304);
}
efree(new_etag);
}
- if (http_is_range_request()) {
+ /* 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);
+ }
- MAKE_STD_ZVAL(zranges);
- array_init(zranges);
+ if (is_range_request) {
- switch (http_get_request_ranges(zranges, data_size))
- {
- case RANGE_NO:
- zval_dtor(zranges);
- efree(zranges);
- /* go ahead and send all */
- break;
+ /* 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)))
+ ) {
- case RANGE_OK:
- return http_send_ranges(zranges, data_ptr, data_size, data_mode);
- break;
+ STATUS result = FAILURE;
+ zval *zranges = NULL;
+ MAKE_STD_ZVAL(zranges);
+ array_init(zranges);
- case RANGE_ERR:
- zval_dtor(zranges);
- efree(zranges);
- http_send_status(416);
- return FAILURE;
- break;
+ switch (http_get_request_ranges(zranges, data_size))
+ {
+ case RANGE_NO:
+ zval_dtor(zranges);
+ efree(zranges);
+ /* go ahead and send all */
+ break;
- default:
- return FAILURE;
- break;
+ case RANGE_OK:
+ result = http_send_ranges(zranges, data_ptr, data_size, data_mode);
+ zval_dtor(zranges);
+ efree(zranges);
+ return result;
+ break;
+
+ case RANGE_ERR:
+ zval_dtor(zranges);
+ efree(zranges);
+ http_send_status(416);
+ return FAILURE;
+ break;
+
+ default:
+ return FAILURE;
+ break;
+ }
}
}
/* send all */
return FAILURE;
}
- return http_send((void *) zdata, Z_STRLEN_P(zdata), SEND_DATA);
+ return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
}
/* }}} */
return FAILURE;
}
- return http_send((void *) file, HTTP_G(ssb).sb.st_size, SEND_RSRC);
+ return http_send(file, HTTP_G(ssb).sb.st_size, SEND_RSRC);
}
/* }}} */
/* }}} */
/* {{{ HAVE_CURL */
-#if defined(HAVE_CURL) && HAVE_CURL
+#ifdef HTTP_HAVE_CURL
/* {{{ STATUS http_get(char *, HashTable *, HashTable *, char **, size_t *) */
PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options,