- add HttpResponse::guessContentType() through libmagic
authorMichael Wallner <mike@php.net>
Tue, 6 Sep 2005 11:49:17 +0000 (11:49 +0000)
committerMichael Wallner <mike@php.net>
Tue, 6 Sep 2005 11:49:17 +0000 (11:49 +0000)
KnownIssues.txt
config.m4
config.w32
http_api.c
http_response_object.c
php_http_api.h
php_http_response_object.h

index f789695..fb76544 100644 (file)
@@ -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.
 
index 90e3a3d..7b048bc 100644 (file)
--- 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 ----
index 80762b2..f6cf489 100644 (file)
@@ -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");
+       }
 }
index b36a32d..e2019ad 100644 (file)
 
 #include <ctype.h>
 
+#ifdef HTTP_HAVE_MAGIC
+#      include <magic.h>
+#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
index 85fef49..423b7b7 100644 (file)
@@ -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;
index 666c33b..42a99e0 100644 (file)
@@ -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)
 {
index 6472a31..60a5be0 100644 (file)
@@ -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);