#define HTTP_HEADER_CONTENT_LENGTH 16
#define HTTP_HEADER_TYPE 32
+static inline char *php_http_detect_charset(char *http_header_line)
+{
+ char *s;
+
+ /* Note: This is a fairly remedial parser which could be easily confused by invalid data
+ The worst case scenario from such confusion should only result in the unicode filter not
+ being applied. While unfortunate, it's more an issue of the server sending a bad header */
+ for (s = strchr(http_header_line, ';'); s; s = strchr(s + 1, ';')) {
+ char *p = s;
+
+ while (*(++p) == ' ');
+ if (strncmp(p, "charset", sizeof("charset") - 1) != 0) {
+ continue;
+ }
+ p += sizeof("charset") - 1;
+
+ while (*p == ' ') p++;
+ if (*p != '=') {
+ continue;
+ }
+
+ while (*(++p) == ' ');
+ if (*p == '"') {
+ s = p + 1;
+ if (!(p = strchr(s, '"'))) {
+ /* Bad things, unmatched quote */
+ return NULL;
+ }
+ return estrndup(s, p - s);
+ break;
+ }
+
+ /* Unquoted value */
+ s = p;
+ while (*p && *p != ' ' && *p != ';') p++;
+ return estrndup(s, p - s);
+ }
+
+ return NULL;
+}
+
php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context, int redirect_max, int header_init STREAMS_DC TSRMLS_DC)
{
php_stream *stream = NULL;
int transport_len, have_header = 0, request_fulluri = 0;
char *protocol_version = NULL;
int protocol_version_len = 3; /* Default: "1.0" */
+ char *charset = NULL;
tmp_line[0] = '\0';
if (!strncasecmp(http_header_line, "Location: ", 10)) {
strlcpy(location, http_header_line + 10, sizeof(location));
} else if (!strncasecmp(http_header_line, "Content-Type: ", 14)) {
+
+ if (UG(unicode) && strchr(mode, 't')) {
+ charset = php_http_detect_charset(http_header_line + sizeof("Content-type: "));
+ }
+
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_line + 14, 0);
} else if (!strncasecmp(http_header_line, "Content-Length: ", 16)) {
file_size = atoi(http_header_line + 16);
php_stream_close(stream);
stream = NULL;
+ if (charset) {
+ efree(charset);
+ charset = NULL;
+ }
+
if (location[0] != '\0') {
char new_path[HTTP_HEADER_BLOCK_SIZE];
}
+ if (charset) {
+ if (stream && UG(unicode) && strchr(mode, 't')) {
+ php_stream_encoding_apply(stream, 0, charset, UG(to_error_mode), NULL);
+ }
+ efree(charset);
+ }
+
return stream;
}
return preferred ^ inverted;
}
+PHPAPI int _php_stream_filter_product(php_stream_filter_chain *chain, int type TSRMLS_DC)
+{
+ php_stream_filter *f;
+
+ for (f = chain->head; f; f = f->next) {
+ if ((type == IS_STRING && (f->fops->flags & PSFO_FLAG_ACCEPTS_STRING) == 0) ||
+ (type == IS_UNICODE && (f->fops->flags & PSFO_FLAG_ACCEPTS_UNICODE) == 0)) {
+ /* At some point, the type produced conflicts with the type accepted */
+ return 0;
+ }
+
+ if (f->fops->flags & PSFO_FLAG_OUTPUTS_OPPOSITE) {
+ type = (type == IS_STRING) ? IS_UNICODE : IS_STRING;
+ continue;
+ }
+ if ((f->fops->flags & PSFO_FLAG_OUTPUTS_SAME) ||
+ (f->fops->flags & PSFO_FLAG_OUTPUTS_ANY)) {
+ continue;
+ }
+ if (f->fops->flags & PSFO_FLAG_OUTPUTS_UNICODE) {
+ type = IS_UNICODE;
+ continue;
+ }
+ type = IS_STRING;
+ }
+
+ return type;
+}
+
PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
{
php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
PHPAPI int _php_stream_filter_check_chain(php_stream_filter_chain *chain TSRMLS_DC);
PHPAPI int _php_stream_filter_output_prefer_unicode(php_stream_filter *filter TSRMLS_DC);
+PHPAPI int _php_stream_filter_product(php_stream_filter_chain *chain, int type TSRMLS_DC);
PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC);
PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC);
PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC);
#define php_stream_filter_flush(filter, finish) _php_stream_filter_flush((filter), (finish) TSRMLS_CC)
#define php_stream_filter_check_chain(chain) _php_stream_filter_check_chain((chain) TSRMLS_CC)
#define php_stream_filter_output_prefer_unicode(filter) _php_stream_filter_output_prefer_unicode((filter) TSRMLS_CC)
+#define php_stream_filter_product(chain, type) _php_stream_filter_product((chain), (type) TSRMLS_CC)
#define php_stream_encoding_apply(stream, writechain, encoding, error_mode, subst) \
_php_stream_encoding_apply((stream), (writechain), (encoding), (error_mode), (subst) TSRMLS_CC)
/* Output encoding on text mode streams defaults to utf8 unless specified in context parameter */
if (stream && strchr(implicit_mode, 't') && UG(unicode)) {
- if (strchr(implicit_mode, 'w') || strchr(implicit_mode, 'a') || strchr(implicit_mode, '+')) {
+ /* Only apply implicit unicode.to. filter if the wrapper didn't do it for us */
+ if ((php_stream_filter_product(&stream->writefilters, IS_UNICODE) == IS_STRING) &&
+ (strchr(implicit_mode, 'w') || strchr(implicit_mode, 'a') || strchr(implicit_mode, '+'))) {
char *encoding = (context && context->output_encoding) ? context->output_encoding : "utf8";
/* UTODO: (Maybe?) Allow overriding the default error handlers on a per-stream basis via context params */
php_stream_encoding_apply(stream, 1, encoding, UG(from_error_mode), UG(from_subst_char));
}
- if (strchr(implicit_mode, 'r') || strchr(implicit_mode, '+')) {
+
+ /* Only apply implicit unicode.from. filter if the wrapper didn't do it for us */
+ if ((stream->readbuf_type == IS_STRING) && (strchr(implicit_mode, 'r') || strchr(implicit_mode, '+'))) {
char *encoding = (context && context->input_encoding) ? context->input_encoding : "utf8";
/* UTODO: (Maybe?) Allow overriding the default error handlers on a per-stream basis via context params */