From 37644e66bb5e3b83e7740cdc4f994b94d9b92855 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 2 Oct 2020 15:29:20 +0200 Subject: [PATCH] Completed a previous commit, especially to web form library (WSF_FORM). Updated EiffelStudio wizard. --- examples/simple_ssl/simple.ini | 1 + .../http_client/src/http_client_constants.e | 1 + ...pplication_x_www_form_urlencoded_handler.e | 258 +++++++++++++++--- .../server/wsf_html/api/wsf_api_utilities.e | 17 +- .../wsf_html/form/input/wsf_form_date_input.e | 25 ++ .../form/input/wsf_form_selectable_input.e | 28 +- .../wsf_html/form/select/wsf_form_select.e | 8 +- .../form/select/wsf_form_select_option.e | 10 +- .../server/wsf_html/form/wsf_form_composite.e | 14 + library/server/wsf_html/form/wsf_form_data.e | 8 +- library/server/wsf_html/form/wsf_form_field.e | 5 +- .../wsf_form_field_with_numeric_attribute.e | 28 +- .../server/wsf_html/form/wsf_form_utility.e | 2 +- .../wsf_html/widget/wsf_widget_composite.e | 13 + .../wsf_html/widget/wsf_with_html_attribute.e | 4 +- library/server/wsf_html/wsf_html.ecf | 2 +- tools/estudio_wizard/console_wizard.ecf | 3 + .../console/console_wizard_application.e | 4 +- .../lib/wizard/console/console_wizard_page.e | 4 +- .../lib/wizard/core/wizard_application.e | 16 +- .../lib/wizard/core/wizard_generator.e | 18 +- .../wizard/gui/graphical_wizard_application.e | 11 +- .../lib/wizard/gui/graphical_wizard_page.e | 13 +- .../lib/wizard/gui/graphical_wizard_styler.e | 10 +- .../graphical_wizard_directory_question.e | 8 +- .../items/graphical_wizard_integer_question.e | 4 - .../gui/items/graphical_wizard_question.e | 3 - .../rootdir/resources/${APP_NAME}.ecf | 20 +- 28 files changed, 391 insertions(+), 147 deletions(-) diff --git a/examples/simple_ssl/simple.ini b/examples/simple_ssl/simple.ini index 5f3fda5c..1c9ccdb6 100644 --- a/examples/simple_ssl/simple.ini +++ b/examples/simple_ssl/simple.ini @@ -20,6 +20,7 @@ port=9090 ### Secure connection settings # enable SSL, with file certificate. is_secure=true +secure_port=9443 secure_certificate=ca.crt secure_certificate_key=ca.key diff --git a/library/network/http_client/src/http_client_constants.e b/library/network/http_client/src/http_client_constants.e index 990f9da4..f36744f6 100644 --- a/library/network/http_client/src/http_client_constants.e +++ b/library/network/http_client/src/http_client_constants.e @@ -30,6 +30,7 @@ feature -- Auth type note copyright: "2011-2020, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + revised_by: "Alexander Kogtenkov" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 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 1772aaf8..f0202c54 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,72 +1,264 @@ note - description: "Summary description for {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER}." + description: "Summary description for {WSF_MULTIPART_FORM_DATA_HANDLER}." date: "$Date$" revision: "$Revision$" class - WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER + WSF_MULTIPART_FORM_DATA_HANDLER inherit WSF_MIME_HANDLER WSF_MIME_HANDLER_HELPER - WSF_VALUE_UTILITIES - export - {NONE} all +create + make + +feature {NONE} -- Initialization + + make + -- Instantiate Current + do 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}.application_x_www_form_encoded) + Result := a_content_type.same_simple_type ({HTTP_MIME_TYPES}.multipart_form_data) 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 - l_content := full_input_data (req) + s := full_input_data (req) if a_raw_data /= Void then - a_raw_data.replace (l_content) + a_raw_data.replace (s) end - 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 + --| 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' from - p := 1 - until - p = 0 - loop - i := l_content.index_of ('&', p) - if i = 0 then - s := l_content.substring (p, n) - p := 0 - else - s := l_content.substring (p, i - 1) - p := i + 1 + i := 1 + l_boundary_len + 1 + if is_crlf then + i := i + 1 --| +1 = CR = %R 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) + next_b := i + until + i = 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 + else + if is_crlf then + i := i + 1 + 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-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 diff --git a/library/server/wsf_html/api/wsf_api_utilities.e b/library/server/wsf_html/api/wsf_api_utilities.e index a45b299b..b7affac9 100644 --- a/library/server/wsf_html/api/wsf_api_utilities.e +++ b/library/server/wsf_html/api/wsf_api_utilities.e @@ -18,7 +18,7 @@ feature -- Core deferred end - based_path (p: STRING): STRING + based_path (p: READABLE_STRING_8): STRING -- Path `p' in the context of the `base_url' do if attached base_url as l_base_url then @@ -32,7 +32,7 @@ feature -- Core end end else - Result := p + Result := p.to_string_8 end end @@ -106,7 +106,8 @@ feature -- Conversion url (a_path: READABLE_STRING_8; opts: detachable WSF_API_OPTIONS): STRING local - q,f: detachable STRING_8 + q: detachable STRING_8 + f: detachable READABLE_STRING_8 l_abs: BOOLEAN do l_abs := False @@ -115,7 +116,7 @@ feature -- Conversion l_abs := opts.boolean_item ("absolute", l_abs) if attached opts.item ("query") as l_query then if attached {READABLE_STRING_8} l_query as s_value then - q := s_value + q := s_value.to_string_8 elseif attached {ITERABLE [TUPLE [key, value: READABLE_STRING_GENERAL]]} l_query as lst then create q.make_empty across @@ -154,16 +155,18 @@ feature -- Conversion end end else - Result := a_path + Result := a_path.to_string_8 end else Result := based_path (a_path) end if q /= Void then - Result.append ("?" + q) + Result.append_character ('?') + Result.append (q) end if f /= Void then - Result.append ("#" + f) + Result.append_character ('#') + Result.append (f) end end diff --git a/library/server/wsf_html/form/input/wsf_form_date_input.e b/library/server/wsf_html/form/input/wsf_form_date_input.e index e825f842..297a67fe 100644 --- a/library/server/wsf_html/form/input/wsf_form_date_input.e +++ b/library/server/wsf_html/form/input/wsf_form_date_input.e @@ -26,6 +26,31 @@ feature -- Access input_type: STRING = "date" +feature -- Element change + + set_date_value (dt: DATE) + -- Set value using date `dt`. + local + y,m,d: INTEGER + s: STRING + do + y := dt.year + m := dt.month + d := dt.day + create s.make (10) + s.append_integer (y) + s.append_character ('-') + if m <= 9 then + s.append_character ('0') + end + s.append_integer (m) + s.append_character ('-') + if d <= 9 then + s.append_character ('0') + end + s.append_integer (d) + set_text_value (s) + end feature {NONE} -- Conversion diff --git a/library/server/wsf_html/form/input/wsf_form_selectable_input.e b/library/server/wsf_html/form/input/wsf_form_selectable_input.e index 19b00d7d..b52f743d 100644 --- a/library/server/wsf_html/form/input/wsf_form_selectable_input.e +++ b/library/server/wsf_html/form/input/wsf_form_selectable_input.e @@ -15,7 +15,7 @@ inherit redefine set_value, specific_input_attributes_string, - append_child_to_html + append_item_to_html end WSF_FORM_SELECTABLE_ITEM @@ -97,14 +97,30 @@ feature -- Change feature {NONE} -- Implementation - append_child_to_html (a_theme: WSF_THEME; a_html: STRING_8) - -- Specific child element if any. - --| To redefine if needed + append_item_to_html (a_theme: WSF_THEME; a_html: STRING_8) + do + Precursor (a_theme, a_html) + append_label_to_html (a_theme, a_html) + end + + append_label_to_html (a_theme: WSF_THEME; a_html: STRING_8) + -- Specific label element if any. + local + s: READABLE_STRING_8 do if attached raw_title as t then - a_html.append (t) + s := t elseif attached title as t then - a_html.append (a_theme.html_encoded (t)) + s := a_theme.html_encoded (t) + end + if s /= Void then + if attached css_id as l_id then + a_html.append ("