Fixed error in previous commits.
This commit is contained in:
@@ -1,262 +1,70 @@
|
|||||||
note
|
note
|
||||||
description: "Summary description for {WSF_MULTIPART_FORM_DATA_HANDLER}."
|
description: "Summary description for {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER}."
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
|
||||||
class
|
class
|
||||||
WSF_MULTIPART_FORM_DATA_HANDLER
|
WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
WSF_MIME_HANDLER
|
WSF_MIME_HANDLER
|
||||||
|
|
||||||
WSF_MIME_HANDLER_HELPER
|
WSF_MIME_HANDLER_HELPER
|
||||||
|
|
||||||
create
|
WSF_VALUE_UTILITIES
|
||||||
make
|
export
|
||||||
|
{NONE} all
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make
|
|
||||||
-- Instantiate Current
|
|
||||||
do
|
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
|
valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
|
||||||
do
|
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
|
end
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
handle (a_content_type: HTTP_CONTENT_TYPE; req: WSF_REQUEST;
|
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
|
local
|
||||||
|
l_content: READABLE_STRING_8
|
||||||
|
n, p, i, j: INTEGER
|
||||||
s: READABLE_STRING_8
|
s: READABLE_STRING_8
|
||||||
|
l_name, l_value: READABLE_STRING_8
|
||||||
do
|
do
|
||||||
s := full_input_data (req)
|
l_content := full_input_data (req)
|
||||||
if a_raw_data /= Void then
|
if a_raw_data /= Void then
|
||||||
a_raw_data.replace (s)
|
a_raw_data.replace (l_content)
|
||||||
end
|
end
|
||||||
--| FIXME: optimization ... fetch the input data progressively, otherwise we might run out of memory ...
|
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
|
||||||
analyze_multipart_form (req, a_content_type, s, a_vars)
|
n := l_content.count
|
||||||
end
|
if n > 0 then
|
||||||
|
|
||||||
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'
|
|
||||||
from
|
from
|
||||||
i := 1 + l_boundary_len + 1
|
p := 1
|
||||||
if is_crlf then
|
|
||||||
i := i + 1 --| +1 = CR = %R
|
|
||||||
end
|
|
||||||
next_b := i
|
|
||||||
until
|
until
|
||||||
i = 0
|
p = 0
|
||||||
loop
|
loop
|
||||||
next_b := s.substring_index (l_boundary, i)
|
i := l_content.index_of ('&', p)
|
||||||
if next_b > 0 then
|
if i = 0 then
|
||||||
if is_crlf then
|
s := l_content.substring (p, n)
|
||||||
m := s.substring (i, next_b - 1 - 2) --| 2 = CR LF = %R %N
|
p := 0
|
||||||
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
|
|
||||||
else
|
else
|
||||||
if is_crlf then
|
s := l_content.substring (p, i - 1)
|
||||||
i := 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
|
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
|
||||||
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
|
note
|
||||||
copyright: "2011-2020, 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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ feature {NONE} -- Implementation: Form analyzer
|
|||||||
vars_attached: vars /= Void
|
vars_attached: vars /= Void
|
||||||
local
|
local
|
||||||
p,i,next_b: INTEGER
|
p,i,next_b: INTEGER
|
||||||
l_boundary_prefix: STRING
|
l_boundary_prefix: READABLE_STRING_8
|
||||||
l_boundary_len: INTEGER
|
l_boundary_len: INTEGER
|
||||||
l_boundary: detachable READABLE_STRING_8
|
l_boundary: detachable READABLE_STRING_8
|
||||||
m: STRING
|
m: READABLE_STRING_8
|
||||||
|
tmp: STRING_8
|
||||||
is_crlf: BOOLEAN
|
is_crlf: BOOLEAN
|
||||||
do
|
do
|
||||||
l_boundary := a_content_type.parameter ("boundary")
|
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_prefix := s.substring (1, p - 1)
|
||||||
l_boundary := l_boundary_prefix + l_boundary
|
l_boundary := l_boundary_prefix + l_boundary
|
||||||
else
|
else
|
||||||
create l_boundary_prefix.make_empty
|
l_boundary_prefix := ""
|
||||||
end
|
end
|
||||||
l_boundary_len := l_boundary.count
|
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)
|
--| 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.
|
--| 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
|
from
|
||||||
i := 1 + l_boundary_len + 1
|
i := 1 + l_boundary_len + 1
|
||||||
if is_crlf then
|
if is_crlf then
|
||||||
@@ -120,9 +121,10 @@ feature {NONE} -- Implementation: Form analyzer
|
|||||||
if is_crlf then
|
if is_crlf then
|
||||||
i := i + 1
|
i := i + 1
|
||||||
end
|
end
|
||||||
m := s.substring (i - 1, s.count)
|
tmp := s.substring (i - 1, s.count).to_string_8
|
||||||
m.right_adjust
|
tmp.right_adjust
|
||||||
if i >= s.count and not l_boundary_prefix.same_string (m) then
|
-- 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")
|
req.error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input")
|
||||||
end
|
end
|
||||||
i := next_b
|
i := next_b
|
||||||
@@ -131,17 +133,17 @@ feature {NONE} -- Implementation: Form analyzer
|
|||||||
end
|
end
|
||||||
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
|
-- Analyze multipart entry
|
||||||
require
|
require
|
||||||
s_not_empty: s /= Void and then not s.is_empty
|
s_not_empty: s /= Void and then not s.is_empty
|
||||||
local
|
local
|
||||||
n, i,p, b,e: INTEGER
|
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_unicode_name: READABLE_STRING_32
|
||||||
l_header: detachable STRING_8
|
l_header: detachable READABLE_STRING_8
|
||||||
l_content: detachable STRING_8
|
l_content: detachable READABLE_STRING_8
|
||||||
l_line: detachable STRING_8
|
l_line: detachable READABLE_STRING_8
|
||||||
l_up_file: WSF_UPLOADED_FILE
|
l_up_file: WSF_UPLOADED_FILE
|
||||||
utf: UTF_CONVERTER
|
utf: UTF_CONVERTER
|
||||||
do
|
do
|
||||||
@@ -256,7 +258,7 @@ feature {NONE} -- Implementation: Form analyzer
|
|||||||
-- Default content type
|
-- Default content type
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
Reference in New Issue
Block a user