* attempt to make http_build_query available for PHP4
[m6w6/ext-http] / http_api.c
index 32dc183077ac2639b83bcf2efbb24da08cca61d8..613d2597446b2cd2c02a121090990d1c4634d437 100644 (file)
 
 /* $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 */
@@ -135,7 +144,7 @@ static int check_month(char *month);
 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)
@@ -290,7 +299,7 @@ static STATUS _http_send_chunk(const void *data, const size_t begin,
 /* }}} */
 
 /* {{{ 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)
@@ -466,12 +475,9 @@ static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *opti
        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;
@@ -560,7 +566,7 @@ static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *opti
                        }
                }
                smart_str_0(&qstr);
-               
+
                if (qstr.c) {
                        curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c);
                }
@@ -578,7 +584,7 @@ static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *opti
                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) {
@@ -1023,7 +1029,7 @@ PHP_HTTP_API inline zval *_http_get_server_var(const char *key TSRMLS_DC)
 {
        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;
        }
@@ -1046,12 +1052,16 @@ PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len,
 
        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);
+                       }
                }
        }
 
@@ -1100,19 +1110,22 @@ PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC)
        } 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);
 }
 /* }}} */
@@ -1130,6 +1143,13 @@ PHP_HTTP_API STATUS _http_send_etag(const char *etag,
        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;
 }
 /* }}} */
@@ -1233,7 +1253,7 @@ PHP_HTTP_API char *_http_negotiate_q(const char *entry, const zval *supported,
                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 */
@@ -1271,7 +1291,7 @@ PHP_HTTP_API char *_http_negotiate_q(const char *entry, const zval *supported,
 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;
 
@@ -1319,6 +1339,10 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges,
                                ptr = &end;
                        break;
 
+                       case ' ':
+                               /* IE - ignore for now */
+                       break;
+
                        case 0:
                        case ',':
 
@@ -1367,17 +1391,18 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges,
                                                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:
@@ -1394,7 +1419,7 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges,
 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;
 
@@ -1407,20 +1432,12 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz
                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 */
@@ -1448,15 +1465,10 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz
                        **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;
@@ -1467,16 +1479,19 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz
 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);
@@ -1484,7 +1499,7 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
                }
 
                /* 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);
                }
@@ -1493,33 +1508,53 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size,
                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 */
@@ -1537,7 +1572,7 @@ PHP_HTTP_API STATUS _http_send_data(const zval *zdata TSRMLS_DC)
                return FAILURE;
        }
 
-       return http_send((void *) zdata, Z_STRLEN_P(zdata), SEND_DATA);
+       return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
 }
 /* }}} */
 
@@ -1548,7 +1583,7 @@ PHP_HTTP_API STATUS _http_send_stream(const php_stream *file TSRMLS_DC)
                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);
 }
 /* }}} */
 
@@ -1698,7 +1733,7 @@ PHP_HTTP_API void _http_split_response(const zval *zresponse, zval *zheaders,
 /* }}} */
 
 /* {{{ 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,