From: Michael Wallner Date: Tue, 6 Sep 2005 11:49:17 +0000 (+0000) Subject: - add HttpResponse::guessContentType() through libmagic X-Git-Tag: RELEASE_0_13_0~22 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=a673ef718611fe1a7c8a4c61b2f4c97fc06efc2d - add HttpResponse::guessContentType() through libmagic --- diff --git a/KnownIssues.txt b/KnownIssues.txt index f789695..fb76544 100644 --- a/KnownIssues.txt +++ b/KnownIssues.txt @@ -4,4 +4,8 @@ $Id$ HttpResponse class is only available for PHP >= 5.1 +HttpResponse::guessContentType() is not available on Windows at the time this +was written. On other platforms you'll need to fix the magic.mime file that's +shipped with PHP by executing `sed -e "s/\t!/\\\!/" magic.mime > magic.good` +and using the new magic file. diff --git a/config.m4 b/config.m4 index 90e3a3d..7b048bc 100644 --- a/config.m4 +++ b/config.m4 @@ -9,6 +9,9 @@ PHP_ARG_WITH([http-curl-requests], [wheter to enable cURL HTTP requests], PHP_ARG_WITH([http-mhash-etags], [whether to enable mhash ETag generator], [ --with-http-mhash-etags[=MHASHDIR] With mhash ETag generator support]) +PHP_ARG_WITH([http-magic-mime], [whether to enable response content type guessing], +[ --with-http-magic-mime[=MAGICDIR] + With magic mime response content type guessing]) if test "$PHP_HTTP" != "no"; then @@ -100,6 +103,31 @@ dnl ---- AC_DEFINE([HTTP_HAVE_MHASH], [1], [Have mhash support]) fi +dnl ---- +dnl MAGIC +dnl ---- + if test "$PHP_HTTP_MAGIC_MIME" != "no"; then + + AC_MSG_CHECKING([for magic.h]) + MAGIC_DIR= + for i in "$PHP_HTTP_MAGIC_MIME" /usr/local /usr /opt; do + if test -f "$i/include/magic.h"; then + MAGIC_DIR=$i + break + fi + done + if test -z "$MAGIC_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([could not find magic.h]) + else + AC_MSG_RESULT([found in $MAGIC_DIR]) + fi + + PHP_ADD_INCLUDE($MAGIC_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(magic, $MAGIC_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) + AC_DEFINE([HTTP_HAVE_MAGIC], [1], [Have magic mime support]) + fi + dnl ---- dnl DONE dnl ---- diff --git a/config.w32 b/config.w32 index 80762b2..f6cf489 100644 --- a/config.w32 +++ b/config.w32 @@ -37,4 +37,11 @@ if (PHP_HTTP != "no") { } else { WARNING("curl convenience functions not enabled; libraries and headers not found"); } + + if (CHECK_HEADER_ADD_INCLUDE("magic.h", "CFLAGS_HTTP") && + CHECK_LIB("magic.lib", "http", PHP_HTTP)) { + AC_DEFINE("HTTP_HAVE_MAGIC", 1, "Have magic library"); + } else { + WARNING("content type guessing not enabled; libraries and headers not found"); + } } diff --git a/http_api.c b/http_api.c index b36a32d..e2019ad 100644 --- a/http_api.c +++ b/http_api.c @@ -36,6 +36,10 @@ #include +#ifdef HTTP_HAVE_MAGIC +# include +#endif + ZEND_EXTERN_MODULE_GLOBALS(http); /* char *pretty_key(char *, size_t, zend_bool, zend_bool) */ @@ -328,7 +332,6 @@ PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encode http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Invalid chunk size: '%s' at pos %d", error, n_ptr - encoded); efree(error); } - return NULL; } } else { @@ -350,6 +353,60 @@ PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encode } /* }}} */ +/* {{{ char *http_guess_content_type(char *magic_file, long magic_mode, void *data, size_t size, http_send_mode mode) */ +PHP_HTTP_API char *_http_guess_content_type(const char *magicfile, long magicmode, void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC) +{ + char *ct = NULL; + +#ifdef HTTP_HAVE_MAGIC + struct magic_set *magic = magic_open(magicmode); + + if (!magic) { + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid magic mode: %ld", magicmode); + } else if (-1 == magic_load(magic, magicfile)) { + http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Failed to load magic database '%s'", magicfile); + } else { + const char *ctype = NULL; + + switch (data_mode) + { + case SEND_RSRC: + { + char *buffer; + size_t b_len; + + b_len = php_stream_copy_to_mem(data_ptr, &buffer, 65536, 0); + ctype = magic_buffer(magic, buffer, b_len); + efree(buffer); + } + break; + + case SEND_DATA: + ctype = magic_buffer(magic, data_ptr, data_len); + break; + + default: + ctype = magic_file(magic, data_ptr); + break; + } + + if (ctype) { + ct = estrdup(ctype); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "Failed to guess Content-Type"); + } + + if (magic) { + magic_close(magic); + } + } +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available"); +#endif + + return ct; +} +/* }}} */ /* * Local variables: * tab-width: 4 diff --git a/http_response_object.c b/http_response_object.c index 85fef49..423b7b7 100644 --- a/http_response_object.c +++ b/http_response_object.c @@ -89,6 +89,11 @@ HTTP_BEGIN_ARGS(setContentType, 1) HTTP_ARG_VAL(content_type, 0) HTTP_END_ARGS; +HTTP_BEGIN_ARGS(guessContentType, 1) + HTTP_ARG_VAL(magic_file, 0) + HTTP_ARG_VAL(magic_mode, 0) +HTTP_END_ARGS; + HTTP_EMPTY_ARGS(getContentDisposition, 0); HTTP_BEGIN_ARGS(setContentDisposition, 1) HTTP_ARG_VAL(filename, 0) @@ -160,6 +165,8 @@ zend_function_entry http_response_object_fe[] = { HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC) HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC) + + HTTP_RESPONSE_ME(guessContentType, ZEND_ACC_PUBLIC) HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC) HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC) @@ -440,6 +447,52 @@ PHP_METHOD(HttpResponse, getContentType) } /* }}} */ +/* {{{ proto static string HttpResponse::guessContentType(string magic_file[, long magic_mode]) + * + * Attempts to guess the content type of supplied payload through libmagic. + * See docs/KnownIssues.txt! + */ +PHP_METHOD(HttpResponse, guessContentType) +{ + char *magic_file, *ct = NULL; + int magic_file_len; + long magic_mode = 0; + + RETVAL_NULL(); + + SET_EH_THROW_HTTP(); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) { + switch (Z_LVAL_P(GET_STATIC_PROP(mode))) { + case SEND_DATA: + { + zval *data = GET_STATIC_PROP(data); + ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(data), Z_STRLEN_P(data), SEND_DATA); + } + break; + + case SEND_RSRC: + { + php_stream *s; + zval *z = GET_STATIC_PROP(stream); + z->type = IS_RESOURCE; + php_stream_from_zval(s, &z); + ct = http_guess_content_type(magic_file, magic_mode, s, 0, SEND_RSRC); + } + break; + + default: + ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(GET_STATIC_PROP(file)), 0, -1); + break; + } + if (ct) { + UPD_STATIC_PROP(string, contentType, ct); + RETVAL_STRING(ct, 0); + } + } + SET_EH_NORMAL(); +} +/* }}} */ + /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false]) * * Set the Content-Disposition of the sent entity. This setting aims to suggest @@ -649,12 +702,17 @@ PHP_METHOD(HttpResponse, setStream) { zval *the_stream; php_stream *the_real_stream; + php_stream_statbuf ssb; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) { RETURN_FALSE; } zend_list_addref(Z_LVAL_P(the_stream)); php_stream_from_zval(the_real_stream, &the_stream); + + if (php_stream_stat(the_real_stream, &ssb)) { + RETURN_FALSE; + } if ( (SUCCESS != UPD_STATIC_PROP(long, stream, Z_LVAL_P(the_stream))) || (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_RSRC))) { @@ -696,11 +754,16 @@ PHP_METHOD(HttpResponse, setFile) { char *the_file; int file_len; + php_stream_statbuf ssb; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) { RETURN_FALSE; } + if (php_stream_stat_path(the_file, &ssb)) { + RETURN_FALSE; + } + if ( (SUCCESS != UPD_STATIC_PROP(string, file, the_file)) || (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) { RETURN_FALSE; diff --git a/php_http_api.h b/php_http_api.h index 666c33b..42a99e0 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -19,6 +19,7 @@ #define PHP_HTTP_API_H #include "php_http_std_defs.h" +#include "php_http_send_api.h" #define pretty_key(key, key_len, uctitle, xhyphen) _http_pretty_key(key, key_len, uctitle, xhyphen) extern char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen); @@ -63,6 +64,10 @@ PHP_HTTP_API STATUS _http_get_request_body_ex(char **body, size_t *length, zend_ #define http_chunked_decode(e, el, d, dl) _http_chunked_decode((e), (el), (d), (dl) TSRMLS_CC) PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC); +#define http_guess_content_type(mf, mm, d, l, m) _http_guess_content_type((mf), (mm), (d), (l), (m) TSRMLS_CC) +PHP_HTTP_API char *_http_guess_content_type(const char *magic_file, long magic_mode, void *data_ptr, size_t data_len, http_send_mode mode TSRMLS_DC); + + #define http_locate_body _http_locate_body static inline const char *_http_locate_body(const char *message) { diff --git a/php_http_response_object.h b/php_http_response_object.h index 6472a31..60a5be0 100644 --- a/php_http_response_object.h +++ b/php_http_response_object.h @@ -39,6 +39,7 @@ PHP_METHOD(HttpResponse, setContentDisposition); PHP_METHOD(HttpResponse, getContentDisposition); PHP_METHOD(HttpResponse, setContentType); PHP_METHOD(HttpResponse, getContentType); +PHP_METHOD(HttpResponse, guessContentType); PHP_METHOD(HttpResponse, setCache); PHP_METHOD(HttpResponse, getCache); PHP_METHOD(HttpResponse, setCacheControl);