X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;ds=sidebyside;f=http_functions.c;h=364e81fe13c948eb9b6331afd1272a2f8212cb48;hb=a6b4fe496b44ab45fbc84d0b491ce7322e7532f7;hp=4b611e037d964ee7122aaa970de514073b5418c9;hpb=781c90c0447166dd52ef881ae15751fa466c32fb;p=m6w6%2Fext-http diff --git a/http_functions.c b/http_functions.c index 4b611e0..364e81f 100644 --- a/http_functions.c +++ b/http_functions.c @@ -18,37 +18,44 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif - #include "php.h" + +#include "zend_operators.h" + +#include "SAPI.h" #include "php_ini.h" #include "ext/standard/info.h" -#include "ext/session/php_session.h" #include "ext/standard/php_string.h" - -#include "SAPI.h" - -#include "phpstr/phpstr.h" +#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) +# include "ext/session/php_session.h" +#endif #include "php_http.h" #include "php_http_std_defs.h" #include "php_http_api.h" -#include "php_http_auth_api.h" #include "php_http_request_api.h" #include "php_http_cache_api.h" +#include "php_http_request_method_api.h" #include "php_http_request_api.h" #include "php_http_date_api.h" #include "php_http_headers_api.h" #include "php_http_message_api.h" #include "php_http_send_api.h" #include "php_http_url_api.h" +#include "php_http_encoding_api.h" + +#include "phpstr/phpstr.h" ZEND_EXTERN_MODULE_GLOBALS(http) /* {{{ proto string http_date([int timestamp]) * - * This function returns a valid HTTP date regarding RFC 822/1123 + * Compose a valid HTTP date regarding RFC 822/1123 * looking like: "Wed, 22 Dec 2004 11:34:47 GMT" * + * Takes an optional unix timestamp as parameter. + * + * Returns the HTTP date as string. */ PHP_FUNCTION(http_date) { @@ -66,24 +73,28 @@ PHP_FUNCTION(http_date) } /* }}} */ -/* {{{ proto string http_absolute_uri(string url[, string proto[, string host[, int port]]]) +/* {{{ proto string http_build_uri(string url[, string proto[, string host[, int port]]]) * - * This function returns an absolute URI constructed from url. + * Build a complete URI according to the supplied parameters. + * * If the url is already abolute but a different proto was supplied, * only the proto part of the URI will be updated. If url has no * path specified, the path of the current REQUEST_URI will be taken. * The host will be taken either from the Host HTTP header of the client * the SERVER_NAME or just localhost if prior are not available. - * - * Some examples: + * If a port is pecified in either the url or as sperate parameter, + * it will be added if it differs from te default port for HTTP(S). + * + * Returns the absolute URI as string. + * + * Examples: *
- * url = "page.php" => http://www.example.com/current/path/page.php - * url = "/page.php" => http://www.example.com/page.php - * url = "/page.php", proto = "https" => https://www.example.com/page.php + * *- * */ -PHP_FUNCTION(http_absolute_uri) +PHP_FUNCTION(http_build_uri) { char *url = NULL, *proto = NULL, *host = NULL; int url_len = 0, proto_len = 0, host_len = 0; @@ -97,17 +108,63 @@ PHP_FUNCTION(http_absolute_uri) } /* }}} */ -/* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US']) +#define HTTP_DO_NEGOTIATE(type, supported, rs_array) \ +{ \ + HashTable *result; \ + if (result = http_negotiate_ ##type(supported)) { \ + char *key; \ + uint key_len; \ + ulong idx; \ + \ + if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \ + RETVAL_STRINGL(key, key_len-1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + \ + if (rs_array) { \ + zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \ + } \ + \ + zend_hash_destroy(result); \ + FREE_HASHTABLE(result); \ + \ + } else { \ + zval **value; \ + \ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported)); \ + if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void **) &value)) { \ + RETVAL_ZVAL(*value, 1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + \ + if (rs_array) { \ + zval **value; \ + \ + FOREACH_VAL(supported, value) { \ + convert_to_string_ex(value); \ + add_assoc_double(rs_array, Z_STRVAL_PP(value), 1.0); \ + } \ + } \ + } \ +} + + +/* {{{ proto string http_negotiate_language(array supported[, array &result]) * * This function negotiates the clients preferred language based on its - * Accept-Language HTTP header. It returns the negotiated language or - * the default language if none match. - * - * The qualifier is recognized and languages without qualifier are rated highest. - * - * The supported parameter is expected to be an array having - * the supported languages as array values. - * + * Accept-Language HTTP header. The qualifier is recognized and languages + * without qualifier are rated highest. The qualifier will be decreased by + * 10% for partial matches (i.e. matching primary language). + * + * Expects an array as parameter cotaining the supported languages as values. + * If the optional second parameter is supplied, it will be filled with an + * array containing the negotiation results. + * + * Returns the negotiated language or the default language (i.e. first array entry) + * if none match. + * * Example: *
* *- * */ PHP_FUNCTION(http_negotiate_language) { - zval *supported; - char *def = NULL; - int def_len = 0; + zval *supported, *rs_array = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) { RETURN_FALSE; } - - if (!def) { - def = "en-US"; + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); } - - RETURN_STRING(http_negotiate_language(supported, def), 0); + + HTTP_DO_NEGOTIATE(language, supported, rs_array); } /* }}} */ -/* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1']) +/* {{{ proto string http_negotiate_charset(array supported[, array &result]) * * This function negotiates the clients preferred charset based on its - * Accept-Charset HTTP header. It returns the negotiated charset or - * the default charset if none match. - * - * The qualifier is recognized and charset without qualifier are rated highest. - * - * The supported parameter is expected to be an array having - * the supported charsets as array values. - * + * Accept-Charset HTTP header. The qualifier is recognized and charsets + * without qualifier are rated highest. + * + * Expects an array as parameter cotaining the supported charsets as values. + * If the optional second parameter is supplied, it will be filled with an + * array containing the negotiation results. + * + * Returns the negotiated charset or the default charset (i.e. first array entry) + * if none match. + * * Example: *
* **/ PHP_FUNCTION(http_negotiate_charset) { - zval *supported; - char *def = NULL; - int def_len = 0; + zval *supported, *rs_array = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &supported, &rs_array) != SUCCESS) { RETURN_FALSE; } - - if (!def) { - def = "iso-8859-1"; + + if (rs_array) { + zval_dtor(rs_array); + array_init(rs_array); } - RETURN_STRING(http_negotiate_charset(supported, def), 0); + HTTP_DO_NEGOTIATE(charset, supported, rs_array); } /* }}} */ @@ -194,6 +257,9 @@ PHP_FUNCTION(http_negotiate_charset) * * Send HTTP status code. * + * Expects an HTTP status code as parameter. + * + * Returns TRUE on success or FALSE on failure. */ PHP_FUNCTION(http_send_status) { @@ -203,7 +269,7 @@ PHP_FUNCTION(http_send_status) RETURN_FALSE; } if (status < 100 || status > 510) { - http_error_ex(E_WARNING, HTTP_E_HEADER, "Invalid HTTP status code (100-510): %d", status); + http_error_ex(HE_WARNING, HTTP_E_HEADER, "Invalid HTTP status code (100-510): %d", status); RETURN_FALSE; } @@ -213,10 +279,13 @@ PHP_FUNCTION(http_send_status) /* {{{ proto bool http_send_last_modified([int timestamp]) * - * This converts the given timestamp to a valid HTTP date and + * Send a "Last-Modified" header with a valid HTTP date. + * + * Accepts a unix timestamp, converts it to a valid HTTP date and * sends it as "Last-Modified" HTTP header. If timestamp is - * omitted, current time is sent. + * omitted, the current time will be sent. * + * Returns TRUE on success or FALSE on failure. */ PHP_FUNCTION(http_send_last_modified) { @@ -236,31 +305,39 @@ PHP_FUNCTION(http_send_last_modified) /* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream']) * - * Sets the content type. + * Send the Content-Type of the sent entity. This is particularly important + * if you use the http_send() API. + * + * Accepts an optional string parameter containing the desired content type + * (primary/secondary). * + * Returns TRUE on success or FALSE on failure. */ PHP_FUNCTION(http_send_content_type) { - char *ct; - int ct_len = 0; + char *ct = "application/x-octetstream"; + int ct_len = lenof("application/x-octetstream"); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) { RETURN_FALSE; } - if (!ct_len) { - RETURN_SUCCESS(http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"))); - } RETURN_SUCCESS(http_send_content_type(ct, ct_len)); } /* }}} */ /* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false]) * - * Set the Content Disposition. The Content-Disposition header is very useful + * Send the Content-Disposition. The Content-Disposition header is very useful * if the data actually sent came from a file or something similar, that should * be "saved" by the client/user (i.e. by browsers "Save as..." popup window). * + * Expects a string parameter specifying the file name the "Save as..." dialogue + * should display. Optionally accepts a bool parameter, which, if set to true + * and the user agent knows how to handle the content type, will probably not + * cause the popup window to be shown. + * + * Returns TRUE on success or FALSE on failure. */ PHP_FUNCTION(http_send_content_disposition) { @@ -275,11 +352,18 @@ PHP_FUNCTION(http_send_content_disposition) } /* }}} */ -/* {{{ proto bool http_match_modified([int timestamp[, for_range = false]]) +/* {{{ proto bool http_match_modified([int timestamp[, bool for_range = false]]) * - * Matches the given timestamp against the clients "If-Modified-Since" resp. - * "If-Unmodified-Since" HTTP headers. + * Matches the given unix timestamp against the clients "If-Modified-Since" + * resp. "If-Unmodified-Since" HTTP headers. * + * Accepts a unix timestamp which should be matched. Optionally accepts an + * additional bool parameter, which if set to true will check the header + * usually used to validate HTTP ranges. If timestamp is omitted, the + * current time will be used. + * + * Returns TRUE if timestamp represents an earlier date than the header, + * else FALSE. */ PHP_FUNCTION(http_match_modified) { @@ -302,11 +386,17 @@ PHP_FUNCTION(http_match_modified) } /* }}} */ -/* {{{ proto bool http_match_etag(string etag[, for_range = false]) +/* {{{ proto bool http_match_etag(string etag[, bool for_range = false]) * - * This matches the given ETag against the clients - * "If-Match" resp. "If-None-Match" HTTP headers. + * Matches the given ETag against the clients "If-Match" resp. + * "If-None-Match" HTTP headers. * + * Expects a string parameter containing the ETag to compare. Optionally + * accepts a bool parameter, which, if set to true, will check the header + * usually used to validate HTTP ranges. + * + * Retuns TRUE if ETag matches or the header contained the asterisk ("*"), + * else FALSE. */ PHP_FUNCTION(http_match_etag) { @@ -327,6 +417,10 @@ PHP_FUNCTION(http_match_etag) /* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]]) * + * Attempts to cache the sent entity by its last modification date. + * + * Accepts a unix timestamp as parameter which is handled as follows: + * * If timestamp_or_expires is greater than 0, it is handled as timestamp * and will be sent as date of last modification. If it is 0 or omitted, * the current time will be sent as Last-Modified date. If it's negative, @@ -334,6 +428,10 @@ PHP_FUNCTION(http_match_etag) * requested last modification date is not between the calculated timespan, * the Last-Modified header is updated and the actual body will be sent. * + * Returns FALSE on failure, or *exits* with "304 Not Modified" if the entity is cached. + * + * A log entry will be written to the cache log if the INI entry + * http.cache_log is set and the cache attempt was successful. */ PHP_FUNCTION(http_cache_last_modified) { @@ -370,14 +468,17 @@ PHP_FUNCTION(http_cache_last_modified) /* {{{ proto bool http_cache_etag([string etag]) * - * This function attempts to cache the HTTP body based on an ETag, - * either supplied or generated through calculation of the MD5 - * checksum of the output (uses output buffering). + * Attempts to cache the sent entity by its ETag, either supplied or generated + * by the hash algorythm specified by the INI setting "http.etag_mode". * - * If clients "If-None-Match" header matches the supplied/calculated + * If the clients "If-None-Match" header matches the supplied/calculated * ETag, the body is considered cached on the clients side and * a "304 Not Modified" status code is issued. * + * Returns FALSE on failure, or *exits* with "304 Not Modified" if the entity is cached. + * + * A log entry is written to the cache log if the INI entry + * "http.cache_log" is set and the cache attempt was successful. */ PHP_FUNCTION(http_cache_etag) { @@ -394,7 +495,8 @@ PHP_FUNCTION(http_cache_etag) /* {{{ proto string ob_etaghandler(string data, int mode) * - * For use with ob_start(). + * For use with ob_start(). Output buffer handler generating an ETag with + * the hash algorythm specified with the INI setting "http.etag_mode". */ PHP_FUNCTION(ob_etaghandler) { @@ -407,16 +509,24 @@ PHP_FUNCTION(ob_etaghandler) } Z_TYPE_P(return_value) = IS_STRING; - http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), mode); + http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), (uint *) &Z_STRLEN_P(return_value), mode); } /* }}} */ -/* {{{ proto void http_throttle(double sec[, long bytes = 2097152]) +/* {{{ proto void http_throttle(double sec[, int bytes = 2097152]) * - * Use with http_send() API. + * Sets the throttle delay and send buffer size for use with http_send() API. + * Provides a basic throttling mechanism, which will yield the current process + * resp. thread until the entity has been completely sent, though. + * + * Note: This doesn't really work with the FastCGI SAPI. * + * Expects a double parameter specifying the seconds too sleep() after + * each chunk sent. Additionally accepts an optional int parameter + * representing the chunk size in bytes. + * * Example: - *
+ *
*
- *
+ *
*/
PHP_FUNCTION(http_throttle)
{
- long chunk_size;
+ long chunk_size = HTTP_SENDBUF_SIZE;
double interval;
- if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dl", &interval, &chunk_size)) {
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l", &interval, &chunk_size)) {
return;
}
@@ -440,48 +550,97 @@ PHP_FUNCTION(http_throttle)
}
/* }}} */
-/* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]])
+/* {{{ proto void http_redirect([string url[, array params[, bool session = false[, int status = 302]]]])
*
- * Redirect to a given url.
- * The supplied url will be expanded with http_absolute_uri(), the params array will
+ * Redirect to the given url.
+ *
+ * The supplied url will be expanded with http_build_uri(), the params array will
* be treated with http_build_query() and the session identification will be appended
* if session is true.
*
- * Depending on permanent the redirection will be issued with a permanent
- * ("301 Moved Permanently") or a temporary ("302 Found") redirection
- * status code.
+ * The HTTP response code will be set according to status.
+ * You can use one of the following constants for convenience:
+ * - HTTP_REDIRECT 302 Found
+ * - HTTP_REDIRECT_PERM 301 Moved Permanently
+ * - HTTP_REDIRECT_POST 303 See Other
+ * - HTTP_REDIRECT_TEMP 307 Temporary Redirect
+ *
+ * Please see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
+ * for which redirect response code to use in which situation.
*
* To be RFC compliant, "Redirecting to URI." will be displayed,
- * if the client doesn't redirect immediatly.
+ * if the client doesn't redirect immediatly, and the request method was
+ * another one than HEAD.
+ *
+ * Returns FALSE on failure, or *exits* on success.
+ *
+ * A log entry will be written to the redirect log, if the INI entry
+ * "http.redirect_log" is set and the redirect attempt was successful.
*/
PHP_FUNCTION(http_redirect)
{
int url_len;
size_t query_len = 0;
- zend_bool session = 0, permanent = 0;
+ zend_bool session = 0, free_params = 0;
zval *params = NULL;
- char *query, *url, *URI,
- LOC[HTTP_URI_MAXLEN + sizeof("Location: ")],
- RED[HTTP_URI_MAXLEN * 2 + sizeof("Redirecting to %s?%s.\n")];
+ long status = 302;
+ char *query = NULL, *url = NULL, *URI, *LOC, *RED = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bl", &url, &url_len, ¶ms, &session, &status) != SUCCESS) {
RETURN_FALSE;
}
/* append session info */
- if (session && (PS(session_status) == php_session_active)) {
+ if (session) {
if (!params) {
+ free_params = 1;
MAKE_STD_ZVAL(params);
array_init(params);
}
- if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
- http_error(E_WARNING, HTTP_E_ENCODE, "Could not append session information");
+#ifdef HAVE_PHP_SESSION
+# ifdef COMPILE_DL_SESSION
+ if (SUCCESS == zend_get_module_started("session")) {
+ zval nm_retval, id_retval, func;
+
+ INIT_PZVAL(&func);
+ INIT_PZVAL(&nm_retval);
+ INIT_PZVAL(&id_retval);
+ ZVAL_NULL(&nm_retval);
+ ZVAL_NULL(&id_retval);
+
+ ZVAL_STRINGL(&func, "session_id", lenof("session_id"), 0);
+ call_user_function(EG(function_table), NULL, &func, &id_retval, 0, NULL TSRMLS_CC);
+ ZVAL_STRINGL(&func, "session_name", lenof("session_name"), 0);
+ call_user_function(EG(function_table), NULL, &func, &nm_retval, 0, NULL TSRMLS_CC);
+
+ if ( Z_TYPE(nm_retval) == IS_STRING && Z_STRLEN(nm_retval) &&
+ Z_TYPE(id_retval) == IS_STRING && Z_STRLEN(id_retval)) {
+ if (add_assoc_stringl_ex(params, Z_STRVAL(nm_retval), Z_STRLEN(nm_retval)+1,
+ Z_STRVAL(id_retval), Z_STRLEN(id_retval), 0) != SUCCESS) {
+ http_error(HE_WARNING, HTTP_E_RUNTIME, "Could not append session information");
+ }
+ }
+ }
+# else
+ if (PS(session_status) == php_session_active) {
+ if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
+ http_error(HE_WARNING, HTTP_E_RUNTIME, "Could not append session information");
+ }
}
+# endif
+#endif
}
/* treat params array with http_build_query() */
if (params) {
if (SUCCESS != http_urlencode_hash_ex(Z_ARRVAL_P(params), 0, NULL, 0, &query, &query_len)) {
+ if (free_params) {
+ zval_dtor(params);
+ FREE_ZVAL(params);
+ }
+ if (query) {
+ efree(query);
+ }
RETURN_FALSE;
}
}
@@ -489,20 +648,27 @@ PHP_FUNCTION(http_redirect)
URI = http_absolute_uri(url);
if (query_len) {
- snprintf(LOC, HTTP_URI_MAXLEN + sizeof("Location: "), "Location: %s?%s", URI, query);
- sprintf(RED, "Redirecting to %s?%s.\n", URI, query, URI, query);
- efree(query);
+ spprintf(&LOC, 0, "Location: %s?%s", URI, query);
+ if (SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD")) {
+ spprintf(&RED, 0, "Redirecting to %s?%s.\n", URI, query, URI, query);
+ }
} else {
- snprintf(LOC, HTTP_URI_MAXLEN + sizeof("Location: "), "Location: %s", URI);
- sprintf(RED, "Redirecting to %s.\n", URI, URI);
+ spprintf(&LOC, 0, "Location: %s", URI);
+ if (SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD")) {
+ spprintf(&RED, 0, "Redirecting to %s.\n", URI, URI);
+ }
}
+
efree(URI);
-
- if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) {
- php_body_write(RED, strlen(RED) TSRMLS_CC);
- RETURN_TRUE;
+ if (query) {
+ efree(query);
+ }
+ if (free_params) {
+ zval_dtor(params);
+ FREE_ZVAL(params);
}
- RETURN_FALSE;
+
+ RETURN_SUCCESS(http_exit_ex(status, LOC, RED, 1));
}
/* }}} */
@@ -510,6 +676,7 @@ PHP_FUNCTION(http_redirect)
*
* Sends raw data with support for (multiple) range requests.
*
+ * Retursn TRUE on success, or FALSE on failure.
*/
PHP_FUNCTION(http_send_data)
{
@@ -528,6 +695,9 @@ PHP_FUNCTION(http_send_data)
*
* Sends a file with support for (multiple) range requests.
*
+ * Expects a string parameter referencing the file to send.
+ *
+ * Returns TRUE on success, or FALSE on failure.
*/
PHP_FUNCTION(http_send_file)
{
@@ -549,6 +719,9 @@ PHP_FUNCTION(http_send_file)
*
* Sends an already opened stream with support for (multiple) range requests.
*
+ * Expects a resource parameter referncing the stream to read from.
+ *
+ * Returns TRUE on success, or FALSE on failure.
*/
PHP_FUNCTION(http_send_stream)
{
@@ -566,71 +739,123 @@ PHP_FUNCTION(http_send_stream)
/* {{{ proto string http_chunked_decode(string encoded)
*
- * This function decodes a string that was HTTP-chunked encoded.
- * Returns false on failure.
+ * Decodes a string that was HTTP-chunked encoded.
+ *
+ * Expects a chunked encoded string as parameter.
+ *
+ * Returns the decoded string on success or FALSE on failure.
*/
PHP_FUNCTION(http_chunked_decode)
{
char *encoded = NULL, *decoded = NULL;
- int encoded_len = 0, decoded_len = 0;
+ size_t decoded_len = 0;
+ int encoded_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) {
RETURN_FALSE;
}
- if (NULL != http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
- RETURN_STRINGL(decoded, decoded_len, 0);
+ if (NULL != http_encoding_dechunk(encoded, encoded_len, &decoded, &decoded_len)) {
+ RETURN_STRINGL(decoded, (int) decoded_len, 0);
} else {
RETURN_FALSE;
}
}
/* }}} */
-/* {{{ proto array http_split_response(string http_response)
+/* {{{ proto object http_parse_message(string message)
*
- * This function splits an HTTP response into an array with headers and the
- * content body. The returned array may look simliar to the following example:
+ * Parses (a) http_message(s) into a simple recursive object structure.
+ *
+ * Expects a string parameter containing a single HTTP message or
+ * several consecutive HTTP messages.
+ *
+ * Returns an hierachical object structure of the parsed messages.
*
+ * Example:
* * array( - * 'Response Status' => '200 Ok', - * 'Content-Type' => 'text/plain', - * 'Content-Language' => 'en-US' - * ), - * 1 => "Hello World!" - * ); + * print_r(http_parse_message(http_get(URL, array('redirect' => 3))); + * + * stdClass object + * ( + * [type] => 2 + * [httpVersion] => 1.1 + * [responseCode] => 200 + * [headers] => Array + * ( + * [Content-Length] => 3 + * [Server] => Apache + * ) + * [body] => Hi! + * [parentMessage] => stdClass object + * ( + * [type] => 2 + * [httpVersion] => 1.1 + * [responseCode] => 302 + * [headers] => Array + * ( + * [Content-Length] => 0 + * [Location] => ... + * ) + * [body] => + * [parentMessage] => ... + * ) + * ) * ?> **/ -PHP_FUNCTION(http_split_response) +PHP_FUNCTION(http_parse_message) { - zval *zresponse, *zbody, *zheaders; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) { - RETURN_FALSE; + char *message; + int message_len; + http_message *msg = NULL; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &message, &message_len)) { + RETURN_NULL(); } - - convert_to_string(zresponse); - - MAKE_STD_ZVAL(zbody); - MAKE_STD_ZVAL(zheaders); - array_init(zheaders); - - if (SUCCESS != http_split_response(zresponse, zheaders, zbody)) { - http_error(E_WARNING, HTTP_E_PARSE, "Could not parse HTTP response"); - RETURN_FALSE; + + if (msg = http_message_parse(message, message_len)) { + object_init(return_value); + http_message_tostruct_recursive(msg, return_value); + http_message_free(&msg); + } else { + RETURN_NULL(); } - - array_init(return_value); - add_index_zval(return_value, 0, zheaders); - add_index_zval(return_value, 1, zbody); } /* }}} */ /* {{{ proto array http_parse_headers(string header) * + * Parses HTTP headers into an associative array. + * + * Expects a string parameter containing HTTP headers. + * + * Returns an array on success, or FALSE on failure. + * + * Example: + *
+ * text/html; chatset=UTF-8 + * [Server] => Funky/1.0 + * [Set-Cookie] => Array + * ( + * [0] => foo=bar + * [1] => baz=quux + * ) + * [Folded] => works + * too + * ?> + **/ PHP_FUNCTION(http_parse_headers) { @@ -643,7 +868,6 @@ PHP_FUNCTION(http_parse_headers) array_init(return_value); if (SUCCESS != http_parse_headers(header, return_value)) { - http_error(E_WARNING, HTTP_E_PARSE, "Could not parse HTTP headers"); zval_dtor(return_value); RETURN_FALSE; } @@ -652,6 +876,9 @@ PHP_FUNCTION(http_parse_headers) /* {{{ proto array http_get_request_headers(void) * + * Get a list of incoming HTTP headers. + * + * Returns an associative array of incoming request headers. */ PHP_FUNCTION(http_get_request_headers) { @@ -662,6 +889,51 @@ PHP_FUNCTION(http_get_request_headers) } /* }}} */ +/* {{{ proto string http_get_request_body(void) + * + * Get the raw request body (e.g. POST or PUT data). + * + * Returns NULL when using the CLI SAPI. + */ +PHP_FUNCTION(http_get_request_body) +{ + char *body; + size_t length; + + NO_ARGS; + + if (SUCCESS == http_get_request_body(&body, &length)) { + RETURN_STRINGL(body, (int) length, 0); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto bool http_match_request_header(string header, string value[, bool match_case = false]) + * + * Match an incoming HTTP header. + * + * Expects two string parameters representing the header name (case-insensitive) + * and the header value that should be compared. The case sensitivity of the + * header value depends on the additional optional bool parameter accepted. + * + * Returns TRUE if header value matches, else FALSE. + */ +PHP_FUNCTION(http_match_request_header) +{ + char *header, *value; + int header_len, value_len; + zend_bool match_case = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &header, &header_len, &value, &value_len, &match_case)) { + RETURN_FALSE; + } + + RETURN_BOOL(http_match_request_header_ex(header, value, match_case)); +} +/* }}} */ + /* {{{ HAVE_CURL */ #ifdef HTTP_HAVE_CURL @@ -669,7 +941,7 @@ PHP_FUNCTION(http_get_request_headers) * * Performs an HTTP GET request on the supplied url. * - * The second parameter is expected to be an associative + * The second parameter, if set, is expected to be an associative * array where the following keys will be recognized: *
* - redirect: int, whether and how many redirects to follow @@ -700,7 +972,6 @@ PHP_FUNCTION(http_get_request_headers) * - timeout: int, seconds the request may take * - connecttimeout: int, seconds the connect may take * - onprogress: mixed, progress callback - * - ondebug: mixed, debug callback ** * The optional third parameter will be filled with some additional information @@ -728,13 +999,14 @@ PHP_FUNCTION(http_get_request_headers) * 'content_type' => 'text/html; charset=iso-8859-1', * 'redirect_time' => 0, * 'redirect_count' => 0, - * 'private' => '', * 'http_connectcode' => 0, * 'httpauth_avail' => 0, * 'proxyauth_avail' => 0, * ) * ?> * + * + * Returns the HTTP response(s) as string on success, or FALSE on failure. */ PHP_FUNCTION(http_get) { @@ -754,7 +1026,7 @@ PHP_FUNCTION(http_get) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_get(URL, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_FALSE; } @@ -763,9 +1035,11 @@ PHP_FUNCTION(http_get) /* {{{ proto string http_head(string url[, array options[, array &info]]) * - * Performs an HTTP HEAD request on the suppied url. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. + * Performs an HTTP HEAD request on the supplied url. + * + * See http_get() for a full list of available parameters and options. + * + * Returns the HTTP response as string on success, or FALSE on failure. */ PHP_FUNCTION(http_head) { @@ -785,18 +1059,21 @@ PHP_FUNCTION(http_head) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_head(URL, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_FALSE; } } /* }}} */ -/* {{{ proto string http_post_data(string url, string data[, array options[, &info]]) +/* {{{ proto string http_post_data(string url, string data[, array options[, array &info]]) * - * Performs an HTTP POST request, posting data. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. + * Performs an HTTP POST requeston the supplied url. + * + * Expects a string as second parameter containing the pre-encoded post data. + * See http_get() for a full list of available parameters and options. + * + * Returns the HTTP response(s) as string on success, or FALSE on failure. */ PHP_FUNCTION(http_post_data) { @@ -821,19 +1098,21 @@ PHP_FUNCTION(http_post_data) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_post(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } - http_request_body_dtor(&body); } /* }}} */ /* {{{ proto string http_post_fields(string url, array data[, array files[, array options[, array &info]]]) * - * Performs an HTTP POST request, posting www-form-urlencoded array data. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. + * Performs an HTTP POST request on the supplied url. + * + * Expecrs an associative array as second parameter, which will be + * www-form-urlencoded. See http_get() for a full list of available options. + * + * Returns the HTTP response(s) as string on success, or FALSE on failure. */ PHP_FUNCTION(http_post_fields) { @@ -858,7 +1137,7 @@ PHP_FUNCTION(http_post_fields) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_post(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } @@ -868,6 +1147,12 @@ PHP_FUNCTION(http_post_fields) /* {{{ proto string http_put_file(string url, string file[, array options[, array &info]]) * + * Performs an HTTP PUT request on the supplied url. + * + * Expects the second parameter to be a string referncing the file to upload. + * See http_get() for a full list of available options. + * + * Returns the HTTP response(s) as string on success, or FALSE on failure. */ PHP_FUNCTION(http_put_file) { @@ -902,7 +1187,7 @@ PHP_FUNCTION(http_put_file) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_put(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } @@ -912,6 +1197,13 @@ PHP_FUNCTION(http_put_file) /* {{{ proto string http_put_stream(string url, resource stream[, array options[, array &info]]) * + * Performs an HTTP PUT request on the supplied url. + * + * Expects the second parameter to be a resource referencing an already + * opened stream, from which the data to upload should be read. + * See http_get() for a full list of available options. + * + * Returns the HTTP response(s) as string on success. or FALSE on failure. */ PHP_FUNCTION(http_put_stream) { @@ -943,24 +1235,27 @@ PHP_FUNCTION(http_put_stream) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_put(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_NULL(); } } /* }}} */ +#endif /* HTTP_HAVE_CURL */ +/* }}} HAVE_CURL */ -/* {{{ proto bool http_request() - */ -/* }}} */ - -/* {{{ proto long http_request_method_register(string method) +/* {{{ proto int http_request_method_register(string method) * + * Register a custom request method. + * + * Expects a string parameter containing the request method name to register. + * + * Returns the ID of the request method on success, or FALSE on failure. */ PHP_FUNCTION(http_request_method_register) { char *method; - int *method_len; + int method_len; unsigned long existing; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) { @@ -970,18 +1265,21 @@ PHP_FUNCTION(http_request_method_register) RETURN_LONG((long) existing); } - RETVAL_LONG((long) http_request_method_register(method)); + RETVAL_LONG((long) http_request_method_register(method, method_len)); } /* }}} */ /* {{{ proto bool http_request_method_unregister(mixed method) * + * Unregister a previously registered custom request method. + * + * Expects either the request method name or ID. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_FUNCTION(http_request_method_unregister) { zval *method; - zend_bool numeric; - unsigned long existing; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &method)) { RETURN_FALSE; @@ -992,7 +1290,6 @@ PHP_FUNCTION(http_request_method_unregister) case IS_OBJECT: convert_to_string(method); case IS_STRING: -#include "zend_operators.h" if (is_numeric_string(Z_STRVAL_P(method), Z_STRLEN_P(method), NULL, NULL, 1)) { convert_to_long(method); } else { @@ -1011,8 +1308,13 @@ PHP_FUNCTION(http_request_method_unregister) } /* }}} */ -/* {{{ proto long http_request_method_exists(mixed method) +/* {{{ proto int http_request_method_exists(mixed method) * + * Check if a request method is registered (or available by default). + * + * Expects either the request method name or ID as parameter. + * + * Returns TRUE if the request method is known, else FALSE. */ PHP_FUNCTION(http_request_method_exists) { @@ -1042,8 +1344,13 @@ PHP_FUNCTION(http_request_method_exists) } /* }}} */ -/* {{{ proto string http_request_method_name(long method) +/* {{{ proto string http_request_method_name(int method) * + * Get the literal string representation of a standard or registered request method. + * + * Expects the request method ID as parameter. + * + * Returns the request method name as string on success, or FALSE on failure. */ PHP_FUNCTION(http_request_method_name) { @@ -1058,118 +1365,6 @@ PHP_FUNCTION(http_request_method_name) } } /* }}} */ -#endif -/* }}} HAVE_CURL */ - - -/* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"]) - * - * Example: - *
- * Authorization failed!'); - * } - * ?> - *- */ -PHP_FUNCTION(http_auth_basic) -{ - char *realm = NULL, *user, *pass, *suser, *spass; - int r_len, u_len, p_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!realm) { - realm = "Restricted"; - } - - if (SUCCESS != http_auth_credentials(&suser, &spass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - if (strcasecmp(suser, user)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - if (strcmp(spass, pass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"]) - * - * Example: - *
- * quoteSmart($user); - * if (strlen($realpass = $db->getOne($query)) { - * return $pass === $realpass; - * } - * return false; - * } - * if (!http_auth_basic_cb('auth_cb')) { - * die('- */ -PHP_FUNCTION(http_auth_basic_cb) -{ - zval *cb; - char *realm = NULL, *user, *pass; - int r_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!realm) { - realm = "Restricted"; - } - - if (SUCCESS != http_auth_credentials(&user, &pass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - { - zval *zparams[2] = {NULL, NULL}, retval; - int result = 0; - - MAKE_STD_ZVAL(zparams[0]); - MAKE_STD_ZVAL(zparams[1]); - ZVAL_STRING(zparams[0], user, 0); - ZVAL_STRING(zparams[1], pass, 0); - - if (SUCCESS == call_user_function(EG(function_table), NULL, cb, - &retval, 2, zparams TSRMLS_CC)) { - result = Z_LVAL(retval); - } - - efree(user); - efree(pass); - efree(zparams[0]); - efree(zparams[1]); - - if (!result) { - http_auth_header("Basic", realm); - } - - RETURN_BOOL(result); - } -} -/* }}}*/ /* {{{ Sara Golemons http_build_query() */ #ifndef ZEND_ENGINE_2 @@ -1188,7 +1383,7 @@ PHP_FUNCTION(http_build_query) } if (Z_TYPE_P(formdata) != IS_ARRAY && Z_TYPE_P(formdata) != IS_OBJECT) { - http_error(E_WARNING, HTTP_E_PARAM, "Parameter 1 expected to be Array or Object. Incorrect value given."); + http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Parameter 1 expected to be Array or Object. Incorrect value given."); RETURN_FALSE; } @@ -1198,12 +1393,12 @@ PHP_FUNCTION(http_build_query) formstr = phpstr_new(); if (SUCCESS != http_urlencode_hash_implementation_ex(HASH_OF(formdata), formstr, arg_sep, prefix, prefix_len, NULL, 0, NULL, 0, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL))) { - phpstr_free(formstr); + phpstr_free(&formstr); RETURN_FALSE; } if (!formstr->used) { - phpstr_free(formstr); + phpstr_free(&formstr); RETURN_NULL(); } @@ -1215,7 +1410,6 @@ PHP_FUNCTION(http_build_query) PHP_FUNCTION(http_test) { - RETURN_NULL(); } /* @@ -1226,3 +1420,4 @@ PHP_FUNCTION(http_test) * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ +Authorization failed
'); - * } - * ?> - *