Stan Vassilev | FM wrote:
I suggest header_remove('*') or simply header_remove() /no param/ removes all headers (including the one PHP sets by default), so we can start with a clear state.
I added header_remove('Foo'). header_remove() without arguments removes all headers (though Apache still adds some headers that you cannot remove).
I have tested with apache2handler and cgi. I had to change the signature of SAPI header_handler function and sapi_header_struct, so the other SAPIs should be updated for this. I am not sure how to test all these? Creating a testing environment for all those webservers seems like a huge task.
I am not comfortable with the size of this patch, given my understanding of the PHP source code and my general C skills, so I am posting this patch hoping that somebody will pick it up or help me get it into shape.
Christian
Index: ext/standard/basic_functions.c =================================================================== RCS file: /repository/php-src/ext/standard/basic_functions.c,v retrieving revision 1.725.2.31.2.64.2.69 diff -u -8 -p -r1.725.2.31.2.64.2.69 basic_functions.c --- ext/standard/basic_functions.c 5 Nov 2008 21:35:02 -0000 1.725.2.31.2.64.2.69 +++ ext/standard/basic_functions.c 9 Nov 2008 18:37:09 -0000 @@ -1687,16 +1687,21 @@ ZEND_END_ARG_INFO() static ZEND_BEGIN_ARG_INFO_EX(arginfo_header, 0, 0, 1) ZEND_ARG_INFO(0, header) ZEND_ARG_INFO(0, replace) ZEND_ARG_INFO(0, http_response_code) ZEND_END_ARG_INFO() static +ZEND_BEGIN_ARG_INFO_EX(arginfo_header_remove, 0, 0, 0) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +static ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expires) ZEND_ARG_INFO(0, path) ZEND_ARG_INFO(0, domain) ZEND_ARG_INFO(0, secure) ZEND_END_ARG_INFO() @@ -3445,16 +3450,17 @@ const zend_function_entry basic_function PHP_FE(ini_restore, arginfo_ini_restore) PHP_FE(get_include_path, arginfo_get_include_path) PHP_FE(set_include_path, arginfo_set_include_path) PHP_FE(restore_include_path, arginfo_restore_include_path) PHP_FE(setcookie, arginfo_setcookie) PHP_FE(setrawcookie, arginfo_setrawcookie) PHP_FE(header, arginfo_header) + PHP_FE(header_remove, arginfo_header_remove) PHP_FE(headers_sent, arginfo_headers_sent) PHP_FE(headers_list, arginfo_headers_list) PHP_FE(connection_aborted, arginfo_connection_aborted) PHP_FE(connection_status, arginfo_connection_status) PHP_FE(ignore_user_abort, arginfo_ignore_user_abort) PHP_FE(parse_ini_file, arginfo_parse_ini_file) PHP_FE(parse_ini_string, arginfo_parse_ini_string) Index: ext/standard/head.c =================================================================== RCS file: /repository/php-src/ext/standard/head.c,v retrieving revision 1.84.2.1.2.7.2.5 diff -u -8 -p -r1.84.2.1.2.7.2.5 head.c --- ext/standard/head.c 21 Oct 2008 22:08:37 -0000 1.84.2.1.2.7.2.5 +++ ext/standard/head.c 9 Nov 2008 18:37:09 -0000 @@ -45,16 +45,30 @@ PHP_FUNCTION(header) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &ctr.line, &ctr.line_len, &rep, &ctr.response_code) == FAILURE) return; sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr TSRMLS_CC); } /* }}} */ +/* {{{ proto void header_remove([string name]) + Removes an HTTP header previously set using header() */ +PHP_FUNCTION(header_remove) +{ + sapi_header_line ctr = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ctr.line, + &ctr.line_len) == FAILURE) + return; + + sapi_header_op(ZEND_NUM_ARGS() == 0 ? SAPI_HEADER_DELETE_ALL : SAPI_HEADER_DELETE, &ctr TSRMLS_CC); +} +/* }}} */ + PHPAPI int php_header(TSRMLS_D) { if (sapi_send_headers(TSRMLS_C)==FAILURE || SG(request_info).headers_only) { return 0; /* don't allow output */ } else { return 1; /* allow output */ } } Index: ext/standard/head.h =================================================================== RCS file: /repository/php-src/ext/standard/head.h,v retrieving revision 1.28.2.1.2.2.2.1 diff -u -8 -p -r1.28.2.1.2.2.2.1 head.h --- ext/standard/head.h 31 Dec 2007 07:17:15 -0000 1.28.2.1.2.2.2.1 +++ ext/standard/head.h 9 Nov 2008 18:37:09 -0000 @@ -18,16 +18,17 @@ /* $Id: head.h,v 1.28.2.1.2.2.2.1 2007/12/31 07:17:15 sebastian Exp $ */ #ifndef HEAD_H #define HEAD_H extern PHP_RINIT_FUNCTION(head); PHP_FUNCTION(header); +PHP_FUNCTION(header_remove); PHP_FUNCTION(setcookie); PHP_FUNCTION(setrawcookie); PHP_FUNCTION(headers_sent); PHP_FUNCTION(headers_list); PHPAPI int php_header(TSRMLS_D); PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, time_t expires, char *path, int path_len, char *domain, int domain_len, int secure, int url_encode, int httponly TSRMLS_DC); Index: main/SAPI.c =================================================================== RCS file: /repository/php-src/main/SAPI.c,v retrieving revision 1.202.2.7.2.15.2.4 diff -u -8 -p -r1.202.2.7.2.15.2.4 SAPI.c --- main/SAPI.c 18 Mar 2008 21:42:50 -0000 1.202.2.7.2.15.2.4 +++ main/SAPI.c 9 Nov 2008 18:37:09 -0000 @@ -492,17 +492,18 @@ static void sapi_update_response_code(in efree(SG(sapi_headers).http_status_line); SG(sapi_headers).http_status_line = NULL; } SG(sapi_headers).http_response_code = ncode; } static int sapi_find_matching_header(void *element1, void *element2) { - return strncasecmp(((sapi_header_struct*)element1)->header, (char*)element2, strlen((char*)element2)) == 0; + int len = strlen((char*)element2); + return strncasecmp(((sapi_header_struct*)element1)->header, (char*)element2, len) == 0 && ((sapi_header_struct*)element1)->header[len] == ':'; } SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC) { sapi_header_line ctr = {0}; int r; ctr.line = header_line; @@ -520,17 +521,16 @@ SAPI_API int sapi_add_header_ex(char *he SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) { int retval; sapi_header_struct sapi_header; char *colon_offset; long myuid = 0L; char *header_line; uint header_line_len; - zend_bool replace; int http_response_code; if (SG(headers_sent) && !SG(request_info).no_headers) { char *output_start_filename = php_get_output_start_filename(TSRMLS_C); int output_start_lineno = php_get_output_start_lineno(TSRMLS_C); if (output_start_filename) { sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent by (output started at %s:%d)", @@ -541,57 +541,78 @@ SAPI_API int sapi_header_op(sapi_header_ return FAILURE; } switch (op) { case SAPI_HEADER_SET_STATUS: sapi_update_response_code((int)(zend_intptr_t) arg TSRMLS_CC); return SUCCESS; + case SAPI_HEADER_ADD: case SAPI_HEADER_REPLACE: - case SAPI_HEADER_ADD: { + case SAPI_HEADER_DELETE: { sapi_header_line *p = arg; if (!p->line || !p->line_len) { return FAILURE; } header_line = p->line; header_line_len = p->line_len; http_response_code = p->response_code; - replace = (op == SAPI_HEADER_REPLACE); break; } + case SAPI_HEADER_DELETE_ALL: + if (sapi_module.header_handler) { + sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); + } + zend_llist_clean(&SG(sapi_headers).headers); + return SUCCESS; + default: return FAILURE; } header_line = estrndup(header_line, header_line_len); /* cut of trailing spaces, linefeeds and carriage-returns */ while(header_line_len && isspace(header_line[header_line_len-1])) header_line[--header_line_len]='\0'; - /* new line safety check */ - { + if (op == SAPI_HEADER_DELETE) { + if (strchr(header_line, ':')) { + efree(header_line); + sapi_module.sapi_error(E_WARNING, "Header to delete may not contain colon."); + return FAILURE; + } + } else { + /* new line safety check */ char *s = header_line, *e = header_line + header_line_len, *p; while (s < e && (p = memchr(s, '\n', (e - s)))) { if (*(p + 1) == ' ' || *(p + 1) == '\t') { s = p + 1; continue; } efree(header_line); sapi_module.sapi_error(E_WARNING, "Header may not contain more than a single header, new line detected."); return FAILURE; } } sapi_header.header = header_line; sapi_header.header_len = header_line_len; - sapi_header.replace = replace; + + if (op == SAPI_HEADER_DELETE) { + if (sapi_module.header_handler) { + sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); + } + zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header); + sapi_free_header(&sapi_header); + return SUCCESS; + } /* Check the header for a few cases that we have special support for in SAPI */ if (header_line_len>=5 && !strncasecmp(header_line, "HTTP/", 5)) { /* filter out the response code */ sapi_update_response_code(sapi_extract_response_code(header_line) TSRMLS_CC); /* sapi_update_response_code doesn't free the status line if the code didn't change */ if (SG(sapi_headers).http_status_line) { @@ -723,30 +744,26 @@ SAPI_API int sapi_header_op(sapi_header_ *colon_offset = ':'; } } } if (http_response_code) { sapi_update_response_code(http_response_code TSRMLS_CC); } if (sapi_module.header_handler) { - retval = sapi_module.header_handler(&sapi_header, &SG(sapi_headers) TSRMLS_CC); + retval = sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); } else { retval = SAPI_HEADER_ADD; } - if (retval & SAPI_HEADER_DELETE_ALL) { - zend_llist_clean(&SG(sapi_headers).headers); - } if (retval & SAPI_HEADER_ADD) { /* in replace mode first remove the header if it already exists in the headers llist */ - if (replace) { + if (op == SAPI_HEADER_REPLACE) { colon_offset = strchr(sapi_header.header, ':'); if (colon_offset) { char sav; - colon_offset++; sav = *colon_offset; *colon_offset = 0; zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header); *colon_offset = sav; } } zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); Index: main/SAPI.h =================================================================== RCS file: /repository/php-src/main/SAPI.h,v retrieving revision 1.114.2.1.2.3.2.3 diff -u -8 -p -r1.114.2.1.2.3.2.3 SAPI.h --- main/SAPI.h 18 Mar 2008 21:42:50 -0000 1.114.2.1.2.3.2.3 +++ main/SAPI.h 9 Nov 2008 18:37:09 -0000 @@ -45,17 +45,16 @@ # define SAPI_API #endif #undef shutdown typedef struct { char *header; uint header_len; - zend_bool replace; } sapi_header_struct; typedef struct { zend_llist headers; int http_response_code; unsigned char send_default_content_type; char *mimetype; @@ -165,16 +164,18 @@ typedef struct { char *line; /* If you allocated this, you need to free it yourself */ uint line_len; long response_code; /* long due to zend_parse_parameters compatibility */ } sapi_header_line; typedef enum { /* Parameter: */ SAPI_HEADER_REPLACE, /* sapi_header_line* */ SAPI_HEADER_ADD, /* sapi_header_line* */ + SAPI_HEADER_DELETE, /* sapi_header_line* */ + SAPI_HEADER_DELETE_ALL, /* void */ SAPI_HEADER_SET_STATUS /* int */ } sapi_header_op_enum; BEGIN_EXTERN_C() SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC); /* Deprecated functions. Use sapi_header_op instead. */ SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC); @@ -222,17 +223,17 @@ struct _sapi_module_struct { int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC); void (*flush)(void *server_context); struct stat *(*get_stat)(TSRMLS_D); char *(*getenv)(char *name, size_t name_len TSRMLS_DC); void (*sapi_error)(int type, const char *error_msg, ...); - int (*header_handler)(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC); + int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC); int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC); void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC); int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC); char *(*read_cookies)(TSRMLS_D); void (*register_server_variables)(zval *track_vars_array TSRMLS_DC); void (*log_message)(char *message); @@ -270,18 +271,16 @@ struct _sapi_post_entry { char *content_type; uint content_type_len; void (*post_reader)(TSRMLS_D); void (*post_handler)(char *content_type_dup, void *arg TSRMLS_DC); }; /* header_handler() constants */ #define SAPI_HEADER_ADD (1<<0) -#define SAPI_HEADER_DELETE_ALL (1<<1) -#define SAPI_HEADER_SEND_NOW (1<<2) #define SAPI_HEADER_SENT_SUCCESSFULLY 1 #define SAPI_HEADER_DO_SEND 2 #define SAPI_HEADER_SEND_FAILED 3 #define SAPI_DEFAULT_MIMETYPE "text/html" #define SAPI_DEFAULT_CHARSET "" Index: sapi/apache2handler/sapi_apache2.c =================================================================== RCS file: /repository/php-src/sapi/apache2handler/sapi_apache2.c,v retrieving revision 1.57.2.10.2.15.2.3 diff -u -8 -p -r1.57.2.10.2.15.2.3 sapi_apache2.c --- sapi/apache2handler/sapi_apache2.c 18 Mar 2008 22:23:20 -0000 1.57.2.10.2.15.2.3 +++ sapi/apache2handler/sapi_apache2.c 9 Nov 2008 18:37:09 -0000 @@ -78,50 +78,60 @@ php_apache_sapi_ub_write(const char *str if (ap_rwrite(str, str_length, r) < 0) { php_handle_aborted_connection(); } return str_length; /* we always consume all the data passed to us. */ } static int -php_apache_sapi_header_handler(sapi_header_struct *sapi_header,sapi_headers_struct *sapi_headers TSRMLS_DC) +php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC) { php_struct *ctx; char *val, *ptr; ctx = SG(server_context); - val = strchr(sapi_header->header, ':'); - - if (!val) { - sapi_free_header(sapi_header); - return 0; - } - ptr = val; + switch (op) { + case SAPI_HEADER_DELETE: + apr_table_unset(ctx->r->headers_out, sapi_header->header); + return 0; + + case SAPI_HEADER_DELETE_ALL: + apr_table_clear(ctx->r->headers_out); + return 0; + + case SAPI_HEADER_ADD: + case SAPI_HEADER_REPLACE: + val = strchr(sapi_header->header, ':'); + + if (!val) { + sapi_free_header(sapi_header); + return 0; + } + ptr = val; - *val = '\0'; + *val = '\0'; - do { - val++; - } while (*val == ' '); - - if (!strcasecmp(sapi_header->header, "content-type")) { - if (ctx->content_type) { - efree(ctx->content_type); - } - ctx->content_type = estrdup(val); - } else if (sapi_header->replace) { - apr_table_set(ctx->r->headers_out, sapi_header->header, val); - } else { - apr_table_add(ctx->r->headers_out, sapi_header->header, val); + do { + val++; + } while (*val == ' '); + + if (!strcasecmp(sapi_header->header, "content-type")) { + if (ctx->content_type) { + efree(ctx->content_type); + } + ctx->content_type = estrdup(val); + } else if (op == SAPI_HEADER_REPLACE) { + apr_table_set(ctx->r->headers_out, sapi_header->header, val); + } else { + apr_table_add(ctx->r->headers_out, sapi_header->header, val); + } + return SAPI_HEADER_ADD; } - *ptr = ':'; - - return SAPI_HEADER_ADD; } static int php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) { php_struct *ctx = SG(server_context); const char *sline = SG(sapi_headers).http_status_line;
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php