diff --git a/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e b/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e index f0202c54..6ae56b2c 100644 --- a/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e +++ b/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e @@ -1,262 +1,70 @@ note - description: "Summary description for {WSF_MULTIPART_FORM_DATA_HANDLER}." + description: "Summary description for {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER}." date: "$Date$" revision: "$Revision$" class - WSF_MULTIPART_FORM_DATA_HANDLER + WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER inherit WSF_MIME_HANDLER WSF_MIME_HANDLER_HELPER -create - make - -feature {NONE} -- Initialization - - make - -- Instantiate Current - do + WSF_VALUE_UTILITIES + export + {NONE} all end feature -- Status report valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN do - Result := a_content_type.same_simple_type ({HTTP_MIME_TYPES}.multipart_form_data) + Result := a_content_type.same_simple_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end feature -- Execution handle (a_content_type: HTTP_CONTENT_TYPE; req: WSF_REQUEST; - a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]; a_raw_data: detachable CELL [detachable READABLE_STRING_8]) + a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]; a_raw_data: detachable CELL [detachable READABLE_STRING_8]) local + l_content: READABLE_STRING_8 + n, p, i, j: INTEGER s: READABLE_STRING_8 + l_name, l_value: READABLE_STRING_8 do - s := full_input_data (req) + l_content := full_input_data (req) if a_raw_data /= Void then - a_raw_data.replace (s) + a_raw_data.replace (l_content) end - --| FIXME: optimization ... fetch the input data progressively, otherwise we might run out of memory ... - analyze_multipart_form (req, a_content_type, s, a_vars) - end - -feature {NONE} -- Implementation: Form analyzer - - analyze_multipart_form (req: WSF_REQUEST; a_content_type: HTTP_CONTENT_TYPE; s: READABLE_STRING_8; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) - -- Analyze multipart form content - --| FIXME[2011-06-21]: integrate eMIME parser library - require - a_content_type_valid: a_content_type /= Void and not a_content_type.has_error - s_attached: s /= Void - same_content_length: req.content_length_value > 0 implies req.content_length_value.as_integer_32 <= s.count - vars_attached: vars /= Void - local - p,i,next_b: INTEGER - l_boundary_prefix: READABLE_STRING_8 - l_boundary_len: INTEGER - l_boundary: detachable READABLE_STRING_8 - m: READABLE_STRING_8 - tmp: STRING_8 - is_crlf: BOOLEAN - do - l_boundary := a_content_type.parameter ("boundary") - if l_boundary /= Void and then not l_boundary.is_empty then - p := s.substring_index (l_boundary, 1) - if p > 1 then - l_boundary_prefix := s.substring (1, p - 1) - l_boundary := l_boundary_prefix + l_boundary - else - l_boundary_prefix := "" - end - l_boundary_len := l_boundary.count - --| Let's support either %R%N and %N ... - --| Since both cases might occurs (for instance, our implementation of CGI does not have %R%N) - --| then let's be as flexible as possible on this. - is_crlf := s [l_boundary_len + 1] = '%R' + check content_count_same_as_content_length_if_not_chunked: (not req.is_chunked_input) implies (l_content.count = req.content_length_value.to_integer_32) end --| FIXME: truncated value + n := l_content.count + if n > 0 then from - i := 1 + l_boundary_len + 1 - if is_crlf then - i := i + 1 --| +1 = CR = %R - end - next_b := i + p := 1 until - i = 0 + p = 0 loop - next_b := s.substring_index (l_boundary, i) - if next_b > 0 then - if is_crlf then - m := s.substring (i, next_b - 1 - 2) --| 2 = CR LF = %R %N - else - m := s.substring (i, next_b - 1 - 1) --| 1 = LF = %N - end - analyze_multipart_form_input (req, m, vars) - if s.valid_index (next_b + l_boundary_len + 1) then - if is_crlf then - if s[next_b + l_boundary_len] = '%R' and s[next_b + l_boundary_len + 1] = '%N' then - -- continue - else - i := 0 -- reached the end - end - else - if s[next_b + l_boundary_len + 1] = '%N' then - -- continue - else - i := 0 -- reached the end - end - end - else - i := 0 -- missing end ? - req.error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input") - end - if i > 0 then - i := next_b + l_boundary_len + 1 - if is_crlf then - i := i + 1 --| +1 = CR = %R - end - end + i := l_content.index_of ('&', p) + if i = 0 then + s := l_content.substring (p, n) + p := 0 else - if is_crlf then - i := i + 1 + s := l_content.substring (p, i - 1) + p := i + 1 + end + if not s.is_empty then + j := s.index_of ('=', 1) + if j > 0 then + l_name := s.substring (1, j - 1) + l_value := s.substring (j + 1, s.count) + add_percent_encoded_string_value_to_table (l_name, l_value, a_vars) end - tmp := s.substring (i - 1, s.count).to_string_8 - tmp.right_adjust - -- TODO: check if next condition should not use tmp instead of s - if i >= s.count and not l_boundary_prefix.same_string (tmp) then - req.error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input") - end - i := next_b end end end end - analyze_multipart_form_input (req: WSF_REQUEST; s: READABLE_STRING_8; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) - -- Analyze multipart entry - require - s_not_empty: s /= Void and then not s.is_empty - local - n, i,p, b,e: INTEGER - l_name, l_filename, l_content_type: detachable READABLE_STRING_8 - l_unicode_name: READABLE_STRING_32 - l_header: detachable READABLE_STRING_8 - l_content: detachable READABLE_STRING_8 - l_line: detachable READABLE_STRING_8 - l_up_file: WSF_UPLOADED_FILE - utf: UTF_CONVERTER - do - from - p := 1 - n := s.count - until - p > n or l_header /= Void - loop - inspect s[p] - when '%R' then -- CR - if - n >= p + 3 and then - s[p+1] = '%N' and then -- LF - s[p+2] = '%R' and then -- CR - s[p+3] = '%N' -- LF - then - l_header := s.substring (1, p + 1) - l_content := s.substring (p + 4, n) - end - when '%N' then - if - n >= p + 1 and then - s[p+1] = '%N' - then - l_header := s.substring (1, p) - l_content := s.substring (p + 2, n) - end - else - end - p := p + 1 - end - if l_header /= Void and l_content /= Void then - from - i := 1 - n := l_header.count - until - i = 0 or i > n - loop - l_line := Void - b := i - p := l_header.index_of ('%N', b) - if p > 0 then - if l_header[p - 1] = '%R' then - p := p - 1 - i := p + 2 - else - i := p + 1 - end - end - if p > 0 then - l_line := l_header.substring (b, p - 1) - if l_line.starts_with ("Content-Disposition: form-data") then - p := l_line.substring_index ("name=", 1) - if p > 0 then - p := p + 4 --| 4 = ("name=").count - 1 - if l_line.valid_index (p+1) and then l_line[p+1] = '%"' then - p := p + 1 - e := l_line.index_of ('"', p + 1) - else - e := l_line.index_of (';', p + 1) - if e = 0 then - e := l_line.count - end - end - l_name := l_header.substring (p + 1, e - 1) - end - - p := l_line.substring_index ("filename=", 1) - if p > 0 then - p := p + 8 --| 8 = ("filename=").count - 1 - if l_line.valid_index (p+1) and then l_line[p+1] = '%"' then - p := p + 1 - e := l_line.index_of ('"', p + 1) - else - e := l_line.index_of (';', p + 1) - if e = 0 then - e := l_line.count - end - end - l_filename := l_header.substring (p + 1, e - 1) - end - elseif l_line.starts_with ("Content-Type: ") then - l_content_type := l_line.substring (15, l_line.count) - end - else - i := 0 - end - end - if l_name /= Void then - if l_filename /= Void and then not l_filename.is_empty then - if l_content_type = Void then - l_content_type := default_content_type - end - l_unicode_name := utf.utf_8_string_8_to_string_32 (l_name) - create l_up_file.make (l_unicode_name, utf.utf_8_string_8_to_escaped_string_32 (l_filename), l_content_type, l_content.count) - add_value_to_table (l_unicode_name, l_up_file, vars) - --| `l_up_file' might have a new name - req.save_uploaded_file (l_up_file, l_content) - else - add_utf_8_string_value_to_table (l_name, l_content, vars) - end - else - req.error_handler.add_custom_error (0, "unamed multipart entry", Void) - end - else - req.error_handler.add_custom_error (0, "missformed multipart entry", Void) - end - end - - default_content_type: STRING = "text/plain" - -- Default content type - note copyright: "2011-2020, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e b/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e index c1885824..f0202c54 100644 --- a/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e +++ b/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e @@ -55,10 +55,11 @@ feature {NONE} -- Implementation: Form analyzer vars_attached: vars /= Void local p,i,next_b: INTEGER - l_boundary_prefix: STRING + l_boundary_prefix: READABLE_STRING_8 l_boundary_len: INTEGER l_boundary: detachable READABLE_STRING_8 - m: STRING + m: READABLE_STRING_8 + tmp: STRING_8 is_crlf: BOOLEAN do l_boundary := a_content_type.parameter ("boundary") @@ -68,13 +69,13 @@ feature {NONE} -- Implementation: Form analyzer l_boundary_prefix := s.substring (1, p - 1) l_boundary := l_boundary_prefix + l_boundary else - create l_boundary_prefix.make_empty + l_boundary_prefix := "" end l_boundary_len := l_boundary.count - --| Let's support either %R%N and %N ... + --| Let's support either %R%N and %N ... --| Since both cases might occurs (for instance, our implementation of CGI does not have %R%N) --| then let's be as flexible as possible on this. - is_crlf := s[l_boundary_len + 1] = '%R' + is_crlf := s [l_boundary_len + 1] = '%R' from i := 1 + l_boundary_len + 1 if is_crlf then @@ -120,9 +121,10 @@ feature {NONE} -- Implementation: Form analyzer if is_crlf then i := i + 1 end - m := s.substring (i - 1, s.count) - m.right_adjust - if i >= s.count and not l_boundary_prefix.same_string (m) then + tmp := s.substring (i - 1, s.count).to_string_8 + tmp.right_adjust + -- TODO: check if next condition should not use tmp instead of s + if i >= s.count and not l_boundary_prefix.same_string (tmp) then req.error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input") end i := next_b @@ -131,17 +133,17 @@ feature {NONE} -- Implementation: Form analyzer end end - analyze_multipart_form_input (req: WSF_REQUEST; s: STRING; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) + analyze_multipart_form_input (req: WSF_REQUEST; s: READABLE_STRING_8; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) -- Analyze multipart entry require s_not_empty: s /= Void and then not s.is_empty local n, i,p, b,e: INTEGER - l_name, l_filename, l_content_type: detachable STRING_8 + l_name, l_filename, l_content_type: detachable READABLE_STRING_8 l_unicode_name: READABLE_STRING_32 - l_header: detachable STRING_8 - l_content: detachable STRING_8 - l_line: detachable STRING_8 + l_header: detachable READABLE_STRING_8 + l_content: detachable READABLE_STRING_8 + l_line: detachable READABLE_STRING_8 l_up_file: WSF_UPLOADED_FILE utf: UTF_CONVERTER do @@ -256,7 +258,7 @@ feature {NONE} -- Implementation: Form analyzer -- Default content type note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + copyright: "2011-2020, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software