/* $Id$ */
+#define _WINSOCKAPI_
#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
#ifdef HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
#endif
#include <ctype.h>
#include "SAPI.h"
#ifdef ZEND_ENGINE_2
-#include "ext/standard/php_http.h"
+# include "ext/standard/php_http.h"
#else
-#include "http_build_query.c"
+ #include "http_build_query.c"
#endif
#include "php_http.h"
#ifdef HTTP_HAVE_CURL
-#ifdef PHP_WIN32
-#include <winsock2.h>
-#include <sys/types.h>
-#endif
+# ifdef PHP_WIN32
+# include <winsock2.h>
+# include <sys/types.h>
+# endif
+
+# include <curl/curl.h>
+# include <curl/easy.h>
-#include <curl/curl.h>
-#include <curl/easy.h>
#endif
static int check_month(char *month);
static int check_tzone(char *tzone);
+static char *pretty_key(char *key, int key_len, int uctitle, int xhyphen);
+
+static int http_ob_stack_get(php_ob_buffer *, php_ob_buffer **);
+
/* {{{ HAVE_CURL */
#ifdef HTTP_HAVE_CURL
#define http_curl_initbuf(m) _http_curl_initbuf((m) TSRMLS_CC)
static inline char *_http_etag(char **new_etag, const void *data_ptr,
const size_t data_len, const http_send_mode data_mode TSRMLS_DC)
{
- char ssb_buf[127], digest[16];
+ char ssb_buf[127];
+ unsigned char digest[16];
PHP_MD5_CTX ctx;
PHP_MD5Init(&ctx);
break;
case SEND_DATA:
- return len == php_body_write(
- Z_STRVAL_P((zval *) data) + begin, len TSRMLS_CC)
+ return len == php_body_write(((char *)data) + begin, len TSRMLS_CC)
? SUCCESS : FAILURE;
break;
if (key_type == HASH_KEY_IS_STRING) {
zend_hash_get_current_key(Z_ARRVAL_P(zoption), &header_key, NULL, 0);
zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &header_val);
- snprintf(header, 1024, "%s: %s", header_key, Z_STRVAL_PP(header_val));
+ snprintf(header, 1023, "%s: %s", header_key, Z_STRVAL_PP(header_val));
headers = curl_slist_append(headers, header);
zend_hash_move_forward(Z_ARRVAL_P(zoption));
}
/* {{{ static inline char *http_curl_getinfoname(CURLINFO) */
static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC)
{
-#define CASE(I) case CURLINFO_ ##I : return #I
+#define CASE(I) case CURLINFO_ ##I : return pretty_key(estrdup( #I ), strlen(#I), 0, 0)
switch (i)
{
/* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */
{
double d;
if (CURLE_OK == curl_easy_getinfo(ch, i, &d)) {
- add_assoc_double(array, key, (double) d);
+ add_assoc_double(array, key, d);
}
}
break;
{
long l;
if (CURLE_OK == curl_easy_getinfo(ch, i, &l)) {
- add_assoc_long(array, key, (long) l);
+ add_assoc_long(array, key, l);
}
}
break;
/* {{{ static inline http_curl_getinfo(CURL, HashTable *) */
static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC)
{
- zval *array;
-
- MAKE_STD_ZVAL(array);
- Z_ARRVAL_P(array) = info;
+ zval array;
+ Z_ARRVAL(array) = info;
-#define INFO(I) http_curl_getinfo_ex(ch, CURLINFO_ ##I , array)
+#define INFO(I) http_curl_getinfo_ex(ch, CURLINFO_ ##I , &array)
/* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */
INFO(EFFECTIVE_URL);
/* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */
/* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */
INFO(PROXYAUTH_AVAIL);
#undef INFO
- efree(array);
}
/* }}} */
+#endif
+/* }}} HAVE_CURL */
+
/* {{{ Day/Month/TZ checks for http_parse_date()
Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. */
static int check_day(char *day, size_t len)
}
/* }}} */
-#endif
-/* }}} HAVE_CURL */
+/* static char *pretty_key(char *, int, int, int) */
+static char *pretty_key(char *key, int key_len, int uctitle, int xhyphen)
+{
+ if (key && key_len) {
+ int i, wasalpha;
+ if (wasalpha = isalpha(key[0])) {
+ key[0] = uctitle ? toupper(key[0]) : tolower(key[0]);
+ }
+ for (i = 1; i < key_len; i++) {
+ if (isalpha(key[i])) {
+ key[i] = ((!wasalpha) && uctitle) ? toupper(key[i]) : tolower(key[i]);
+ wasalpha = 1;
+ } else {
+ if (xhyphen && (key[i] == '_')) {
+ key[i] = '-';
+ }
+ wasalpha = 0;
+ }
+ }
+ }
+ return key;
+}
+/* }}} */
+
+/* {{{ static STATUS http_ob_stack_get(php_ob_buffer *, php_ob_buffer **) */
+static STATUS http_ob_stack_get(php_ob_buffer *o, php_ob_buffer **s)
+{
+ static int i = 0;
+ php_ob_buffer *b = emalloc(sizeof(php_ob_buffer));
+ b->handler_name = estrdup(o->handler_name);
+ b->buffer = estrndup(o->buffer, o->text_length);
+ b->chunk_size = o->chunk_size;
+ b->erase = o->erase;
+ s[i++] = b;
+ return SUCCESS;
+}
+/* }}} */
/* }}} internals */
PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC)
{
struct tm *gmtime, tmbuf;
- char *date = ecalloc(1, 30);
+ char *date = ecalloc(31, 1);
gmtime = php_gmtime_r(&t, &tmbuf);
snprintf(date, 30,
}
/* }}} */
+/* {{{ STATUS http_start_ob_handler(php_output_handler_func_t, char *, uint, zend_bool) */
+PHP_HTTP_API STATUS _http_start_ob_handler(php_output_handler_func_t handler_func,
+ char *handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
+{
+ php_ob_buffer **stack;
+ int count, i;
+ STATUS result;
+
+ count = OG(ob_nesting_level);
+ stack = emalloc(sizeof(php_ob_buffer *) * count);
+
+ if (count > 1) {
+ zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *elem, void *)) http_ob_stack_get, stack);
+ }
+
+ if (count > 0) {
+ http_ob_stack_get(&OG(active_ob_buffer), stack);
+ }
+
+ while (OG(ob_nesting_level)) {
+ php_end_ob_buffer(0, 0 TSRMLS_CC);
+ }
+
+ php_ob_set_internal_handler(handler_func, 0, handler_name, 0 TSRMLS_CC);
+ result = php_start_ob_buffer_named(handler_name, chunk_size, erase TSRMLS_CC);
+
+ for (i = 0; i < count; i++) {
+ php_ob_buffer *s = stack[i];
+ php_start_ob_buffer_named(s->handler_name, s->chunk_size, s->erase TSRMLS_CC);
+ php_body_write(s->buffer, s->text_length TSRMLS_CC);
+ }
+
+ return result;
+}
+/* }}} */
+
/* {{{ int http_modified_match(char *, int) */
PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_DC)
{
int header_len;
char *etag_header;
- header_len = strlen("ETag: \"\"") + etag_len + 1;
- etag_header = (char *) emalloc(header_len);
- snprintf(etag_header, header_len, "ETag: \"%s\"", etag);
+ header_len = sizeof("ETag: \"\"") + etag_len + 1;
+ etag_header = ecalloc(header_len, 1);
+ sprintf(etag_header, "ETag: \"%s\"", etag);
ret = http_send_header(etag_header);
efree(etag_header);
+ if (!etag_len){
+ php_error_docref(NULL TSRMLS_CC,E_ERROR,
+ "Sending empty Etag (previous: %s)\n", HTTP_G(etag));
+ return FAILURE;
+ }
/* remember */
if (HTTP_G(etag)) {
efree(HTTP_G(etag));
if (strncmp(range, "bytes=", strlen("bytes="))) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Range header misses bytes=");
- return RANGE_ERR;
+ return RANGE_NO;
}
ptr = &begin;
break;
default:
- return RANGE_ERR;
+ return RANGE_NO;
break;
}
} while (c != 0);
/* single range */
if ((c = zend_hash_num_elements(Z_ARRVAL_P(zranges))) == 1) {
char range_header[256] = {0};
-
+
zend_hash_index_find(Z_ARRVAL_P(zranges), 0, (void **) &zrange);
zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin);
zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end);
/* multi range */
else {
int i;
- char bound[23] = {0}, preface[1024] = {0},
+ char bound[23] = {0}, preface[1024] = {0},
multi_header[68] = "Content-Type: multipart/byteranges; boundary=";
-
+
snprintf(bound, 22, "--%d%0.9f", time(NULL), php_combined_lcg(TSRMLS_C));
strncat(multi_header, bound + 2, 21);
http_send_header(multi_header);
SUCCESS != zend_hash_index_find(
Z_ARRVAL_PP(zrange), 1, (void **) &end)) {
break;
- }
+ }
snprintf(preface, 1023,
- HTTP_CRLF "%s"
+ HTTP_CRLF "%s"
HTTP_CRLF "Content-Type: %s"
HTTP_CRLF "Content-Range: bytes %ld-%ld/%ld"
- HTTP_CRLF
+ HTTP_CRLF
HTTP_CRLF,
-
- bound,
- HTTP_G(ctype) ? HTTP_G(ctype) : "application/x-octetstream",
- **begin,
- **end,
+
+ 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);
}
PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
const http_send_mode data_mode TSRMLS_DC)
{
- char *new_etag = NULL;
int is_range_request = http_is_range_request();
if (!data_ptr) {
/* etag handling */
if (HTTP_G(etag_started)) {
- new_etag = (char *) emalloc(33);
-
- /* never ever use the output to compute the ETag if http_send() is used */
+ char *etag = ecalloc(33, 1);
+ /* 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 (NULL == http_etag(&new_etag, data_ptr, data_size, data_mode)) {
- efree(new_etag);
+ if (NULL == http_etag(&etag, data_ptr, data_size, data_mode)) {
+ efree(etag);
return FAILURE;
}
/* send 304 Not Modified if etag matches */
- if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) {
- efree(new_etag);
+ if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
+ efree(etag);
return http_send_status(304);
}
- http_send_etag(new_etag, 32);
- efree(new_etag);
+ http_send_etag(etag, 32);
+ efree(etag);
}
/* send 304 Not Modified if last-modified matches*/
return FAILURE;
}
- return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
+ return http_send(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata), SEND_DATA);
}
/* }}} */
{
char *colon = NULL, *line = NULL, *begin = header;
- if (header_len < 8) {
+ if (header_len < 2) {
return FAILURE;
}
line = header;
- while (header_len > (line - begin)) {
+ 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 != ' ') && (*line != '\t')) {
- char *key = estrndup(header, colon - header);
- add_assoc_stringl(array, key, colon + 2, line - colon - 4, 1);
- efree(key);
+ if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) {
+
+ /* skip empty key */
+ if (header != colon) {
+ char *key = estrndup(header, colon - header);
+ value_len += line - colon - 1;
+
+ /* skip leading ws */
+ while (isspace(*(++colon))) --value_len;
+ /* skip trailing ws */
+ while (isspace(colon[value_len - 1])) --value_len;
+
+ if (value_len < 1) {
+ /* hm, empty header? */
+ add_assoc_stringl(array, key, "", 0, 1);
+ } else {
+ add_assoc_stringl(array, key, colon, value_len, 1);
+ }
+ efree(key);
+ }
colon = NULL;
+ value_len = 0;
header += line - header;
}
break;
}
/* }}} */
+/* {{{ void http_get_request_headers(zval *) */
+PHP_HTTP_API void _http_get_request_headers(zval *array TSRMLS_DC)
+{
+ char *key;
+
+ for ( zend_hash_internal_pointer_reset(HTTP_SERVER_VARS);
+ zend_hash_get_current_key(HTTP_SERVER_VARS, &key, NULL, 0) != HASH_KEY_NON_EXISTANT;
+ zend_hash_move_forward(HTTP_SERVER_VARS)) {
+ if (!strncmp(key, "HTTP_", 5)) {
+ zval **header;
+ zend_hash_get_current_data(HTTP_SERVER_VARS, (void **) &header);
+ add_assoc_stringl(array, pretty_key(key + 5, strlen(key) - 5, 1, 1), Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
+ }
+ }
+}
+/* }}} */
+
/* {{{ HAVE_CURL */
#ifdef HTTP_HAVE_CURL
#endif
/* }}} HAVE_CURL */
-/* {{{ void http_auth_header(char *, char*) */
-PHP_HTTP_API void _http_auth_header(const char *type, const char *realm TSRMLS_DC)
+/* {{{ STATUS http_auth_header(char *, char*) */
+PHP_HTTP_API STATUS _http_auth_header(const char *type, const char *realm TSRMLS_DC)
{
- char realm_header[1024];
- snprintf(realm_header, 1024, "WWW-Authenticate: %s realm=\"%s\"", type, realm);
- http_send_status_header(401, realm_header);
+ char realm_header[1024] = {0};
+ snprintf(realm_header, 1023, "WWW-Authenticate: %s realm=\"%s\"", type, realm);
+ return http_send_status_header(401, realm_header);
}
/* }}} */