@@ -139,10 +139,9 @@ static zend_result php_stream_handle_proxy_authorization_header(const char *s, s
139139typedef struct _php_stream_http_response_header_info {
140140 php_stream_filter * transfer_encoding ;
141141 size_t file_size ;
142+ zend_string * location ;
142143 bool error ;
143144 bool follow_location ;
144- char * location ;
145- size_t location_len ;
146145} php_stream_http_response_header_info ;
147146
148147static void php_stream_http_response_header_info_init (
@@ -281,13 +280,11 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w
281280 zend_string_efree (last_header_line );
282281 return NULL ;
283282 }
284- if (header_info -> location_len == 0 ) {
285- header_info -> location = emalloc (last_header_value_len + 1 );
286- } else if (header_info -> location_len <= last_header_value_len ) {
287- header_info -> location = erealloc (header_info -> location , last_header_value_len + 1 );
283+ /* Previous location header encountered */
284+ if (UNEXPECTED (header_info -> location )) {
285+ zend_string_release_ex (header_info -> location , false);
288286 }
289- header_info -> location_len = last_header_value_len ;
290- memcpy (header_info -> location , last_header_value , last_header_value_len + 1 );
287+ header_info -> location = zend_string_init (last_header_value , last_header_value_len , false);
291288 } else if (zend_string_starts_with_literal_ci (last_header_line , "Content-Type:" )) {
292289 php_stream_notify_info (context , PHP_STREAM_NOTIFY_MIME_TYPE_IS , last_header_value , 0 );
293290 } else if (zend_string_starts_with_literal_ci (last_header_line , "Content-Length:" )) {
@@ -1037,7 +1034,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
10371034 }
10381035
10391036 if (header_info .location != NULL )
1040- php_stream_notify_info (context , PHP_STREAM_NOTIFY_REDIRECTED , header_info .location , 0 );
1037+ php_stream_notify_info (context , PHP_STREAM_NOTIFY_REDIRECTED , ZSTR_VAL ( header_info .location ) , 0 );
10411038
10421039 php_stream_close (stream );
10431040 stream = NULL ;
@@ -1048,63 +1045,66 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
10481045 }
10491046
10501047 if (header_info .location != NULL ) {
1048+ zend_string * new_path = NULL ;
1049+
1050+ /* Redirection location must be a URI with schema,
1051+ * if we don't have a schema, the location is within the existing schema and host */
1052+ if (ZSTR_LEN (header_info .location ) < 8 ||
1053+ (!zend_string_starts_with_literal_ci (header_info .location , "http://" ) &&
1054+ !zend_string_starts_with_literal_ci (header_info .location , "https://" ) &&
1055+ !zend_string_starts_with_literal_ci (header_info .location , "ftp://" ) &&
1056+ !zend_string_starts_with_literal_ci (header_info .location , "ftps://" ))
1057+ ) {
1058+ zend_string * loc_path = NULL ;
10511059
1052- char * new_path = NULL ;
1053-
1054- if (strlen (header_info .location ) < 8 ||
1055- (strncasecmp (header_info .location , "http://" , sizeof ("http://" )- 1 ) &&
1056- strncasecmp (header_info .location , "https://" , sizeof ("https://" )- 1 ) &&
1057- strncasecmp (header_info .location , "ftp://" , sizeof ("ftp://" )- 1 ) &&
1058- strncasecmp (header_info .location , "ftps://" , sizeof ("ftps://" )- 1 )))
1059- {
1060- char * loc_path = NULL ;
1061- if (* header_info .location != '/' ) {
1062- if (* (header_info .location + 1 ) != '\0' && resource -> path ) {
1060+ /* if we don't have an absolute location we need to determine it */
1061+ if (ZSTR_VAL (header_info .location )[0 ] != '/' ) {
1062+ if (ZSTR_VAL (header_info .location )[1 ] != '\0' && resource -> path ) {
1063+ /* find last '/' to determine relative path */
10631064 char * s = strrchr (ZSTR_VAL (resource -> path ), '/' );
10641065 if (!s ) {
1065- s = ZSTR_VAL (resource -> path );
1066- if (!ZSTR_LEN (resource -> path )) {
1067- zend_string_release_ex (resource -> path , 0 );
1068- resource -> path = ZSTR_INIT_LITERAL ("/" , 0 );
1069- s = ZSTR_VAL (resource -> path );
1070- } else {
1071- * s = '/' ;
1072- }
1073- }
1074- s [1 ] = '\0' ;
1075- if (resource -> path &&
1076- ZSTR_VAL (resource -> path )[0 ] == '/' &&
1077- ZSTR_VAL (resource -> path )[1 ] == '\0' ) {
1078- spprintf (& loc_path , 0 , "%s%s" , ZSTR_VAL (resource -> path ), header_info .location );
1066+ /* No path segment, just prefix relative location with '/' */
1067+ loc_path = zend_string_concat2 (
1068+ ZEND_STRL ("/" ),
1069+ ZSTR_VAL (header_info .location ), ZSTR_LEN (header_info .location )
1070+ );
10791071 } else {
1080- spprintf (& loc_path , 0 , "%s/%s" , ZSTR_VAL (resource -> path ), header_info .location );
1072+ size_t offset_to_last_slash = s - ZSTR_VAL (resource -> path );
1073+ ZEND_ASSERT (ZSTR_VAL (resource -> path )[offset_to_last_slash ] == '/' );
1074+ loc_path = zend_string_concat2 (
1075+ ZSTR_VAL (resource -> path ), offset_to_last_slash + 1 ,
1076+ ZSTR_VAL (header_info .location ), ZSTR_LEN (header_info .location )
1077+ );
10811078 }
10821079 } else {
1083- spprintf (& loc_path , 0 , "/%s" , header_info .location );
1080+ loc_path = zend_string_concat2 (
1081+ ZEND_STRL ("/" ),
1082+ ZSTR_VAL (header_info .location ), ZSTR_LEN (header_info .location )
1083+ );
10841084 }
10851085 } else {
10861086 loc_path = header_info .location ;
10871087 header_info .location = NULL ;
10881088 }
10891089 if ((use_ssl && resource -> port != 443 ) || (!use_ssl && resource -> port != 80 )) {
1090- spprintf ( & new_path , 0 , "%s://%s:" ZEND_LONG_FMT "%s" , ZSTR_VAL (resource -> scheme ),
1091- ZSTR_VAL (resource -> host ), resource -> port , loc_path );
1090+ new_path = zend_strpprintf ( 0 , "%s://%s:" ZEND_LONG_FMT "%s" , ZSTR_VAL (resource -> scheme ),
1091+ ZSTR_VAL (resource -> host ), resource -> port , ZSTR_VAL ( loc_path ) );
10921092 } else {
1093- spprintf ( & new_path , 0 , "%s://%s%s" , ZSTR_VAL (resource -> scheme ),
1094- ZSTR_VAL (resource -> host ), loc_path );
1093+ new_path = zend_strpprintf ( 0 , "%s://%s%s" , ZSTR_VAL (resource -> scheme ),
1094+ ZSTR_VAL (resource -> host ), ZSTR_VAL ( loc_path ) );
10951095 }
1096- efree (loc_path );
1096+ zend_string_release_ex (loc_path , false );
10971097 } else {
10981098 new_path = header_info .location ;
10991099 header_info .location = NULL ;
11001100 }
11011101
11021102 php_uri_struct_free (resource );
11031103 /* check for invalid redirection URLs */
1104- if ((resource = php_uri_parse_to_struct (uri_parser , new_path , strlen (new_path ), PHP_URI_COMPONENT_READ_MODE_RAW , true)) == NULL ) {
1104+ if ((resource = php_uri_parse_to_struct (uri_parser , ZSTR_VAL ( new_path ), ZSTR_LEN (new_path ), PHP_URI_COMPONENT_READ_MODE_RAW , true)) == NULL ) {
11051105 php_stream_wrapper_log_warn (wrapper , context , options , InvalidUrl ,
1106- "Invalid redirect URL! %s" , new_path );
1107- efree (new_path );
1106+ "Invalid redirect URL! %s" , ZSTR_VAL ( new_path ) );
1107+ zend_string_release_ex (new_path , false );
11081108 goto out ;
11091109 }
11101110
@@ -1116,7 +1116,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11161116 while (s < e) { \
11171117 if (iscntrl(*s)) { \
11181118 php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, \
1119- "Invalid redirect URL! %s", new_path); \
1119+ "Invalid redirect URL! %s", ZSTR_VAL( new_path) ); \
11201120 efree(new_path); \
11211121 goto out; \
11221122 } \
@@ -1125,7 +1125,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11251125 } \
11261126}
11271127 /* check for control characters in login, password & path */
1128- if (strncasecmp (new_path , "http://" , sizeof ( "http://" ) - 1 ) || strncasecmp (new_path , "https://" , sizeof ( "https://" ) - 1 )) {
1128+ if (zend_string_starts_with_literal_ci (new_path , "http://" ) || zend_string_starts_with_literal_ci (new_path , "https://" )) {
11291129 CHECK_FOR_CNTRL_CHARS (resource -> user );
11301130 CHECK_FOR_CNTRL_CHARS (resource -> password );
11311131 CHECK_FOR_CNTRL_CHARS (resource -> path );
@@ -1138,9 +1138,9 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11381138 new_flags |= HTTP_WRAPPER_KEEP_METHOD ;
11391139 }
11401140 stream = php_stream_url_wrap_http_ex (
1141- wrapper , new_path , mode , options , opened_path , context ,
1141+ wrapper , ZSTR_VAL ( new_path ) , mode , options , opened_path , context ,
11421142 -- redirect_max , new_flags , response_header STREAMS_CC );
1143- efree (new_path );
1143+ zend_string_release_ex (new_path , false );
11441144 } else {
11451145 php_stream_wrapper_log_warn (wrapper , context , options , ProtocolError ,
11461146 "HTTP request failed! %s" , tmp_line );
@@ -1155,7 +1155,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11551155 }
11561156
11571157 if (header_info .location != NULL ) {
1158- efree (header_info .location );
1158+ zend_string_release_ex (header_info .location , false );
11591159 }
11601160
11611161 if (resource ) {
0 commit comments