diff --git a/draft/application/cms/cms-safe.ecf b/draft/application/cms/cms-safe.ecf index e7df2d67..6766f70c 100644 --- a/draft/application/cms/cms-safe.ecf +++ b/draft/application/cms/cms-safe.ecf @@ -23,6 +23,7 @@ + diff --git a/draft/application/cms/cms.ecf b/draft/application/cms/cms.ecf new file mode 100644 index 00000000..164e5649 --- /dev/null +++ b/draft/application/cms/cms.ecf @@ -0,0 +1,31 @@ + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + + + + diff --git a/draft/application/cms/example/src/web_cms.e b/draft/application/cms/example/src/web_cms.e new file mode 100644 index 00000000..716da98f --- /dev/null +++ b/draft/application/cms/example/src/web_cms.e @@ -0,0 +1,135 @@ +note + description: "[ + This class implements the Demo of WEB CMS service + + ]" + +class + WEB_CMS + +inherit + WSF_DEFAULT_SERVICE + redefine + initialize + end + +create + make_and_launch + +feature {NONE} -- Initialization + + initialize + local + args: ARGUMENTS_32 + cfg: detachable READABLE_STRING_32 + i,n: INTEGER + do + --| Arguments + create args + from + i := 1 + n := args.argument_count + until + i > n or cfg /= Void + loop + if attached args.argument (i) as s then + if s.same_string_general ("--config") or s.same_string_general ("-c") then + if i < n then + cfg := args.argument (i + 1) + end + end + end + i := i + 1 + end + if cfg = Void then + if file_exists ("cms.ini") then + cfg := {STRING_32} "cms.ini" + end + end + + --| EWF settings + service_options := create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("ewf.ini") + Precursor + + --| CMS initialization + launch_cms (cms_setup (cfg)) + end + + cms_setup (a_cfg_fn: detachable READABLE_STRING_GENERAL): CMS_CUSTOM_SETUP + do + if a_cfg_fn /= Void then + create Result.make_from_file (a_cfg_fn) + else + create Result -- Default + end + setup_modules (Result) + setup_storage (Result) + end + + launch_cms (a_setup: CMS_SETUP) + local + cms: CMS_SERVICE + do + create cms.make (a_setup) + on_launched (cms) + cms_service := cms + end + +feature -- Execution + + cms_service: CMS_SERVICE + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + do + cms_service.execute (req, res) + end + +feature -- Access + + setup_modules (a_setup: CMS_SETUP) + local + m: CMS_MODULE + do + create {DEMO_MODULE} m.make + m.enable + a_setup.add_module (m) + + create {SHUTDOWN_MODULE} m.make + m.enable + a_setup.add_module (m) + + create {DEBUG_MODULE} m.make + m.enable + a_setup.add_module (m) + + create {OPENID_MODULE} m.make + m.enable + a_setup.add_module (m) + end + + setup_storage (a_setup: CMS_SETUP) + do + + end + +feature -- Event + + on_launched (cms: CMS_SERVICE) + local + e: CMS_EMAIL + do + create e.make (cms.site_email, cms.site_email, "[" + cms.site_name + "] launched...", "The site [" + cms.site_name + "] was launched at " + (create {DATE_TIME}.make_now_utc).out + " UTC.") + cms.mailer.safe_process_email (e) + end + +feature -- Helper + + file_exists (fn: READABLE_STRING_GENERAL): BOOLEAN + local + f: RAW_FILE + do + create f.make_with_name (fn) + Result := f.exists and then f.is_readable + end + +end diff --git a/draft/application/cms/src/cms_configuration.e b/draft/application/cms/src/cms_configuration.e new file mode 100644 index 00000000..937629e3 --- /dev/null +++ b/draft/application/cms/src/cms_configuration.e @@ -0,0 +1,311 @@ +note + description: "Summary description for {CMS_CONFIGURATION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_CONFIGURATION + +inherit + ANY + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + +create + make, + make_from_file + +feature {NONE} -- Initialization + + make + do + create options.make_equal (10) + analyze + end + + make_from_file (a_filename: READABLE_STRING_GENERAL) + -- Initialize `Current'. + local + p: PATH + do + make + create p.make_from_string (a_filename) + configuration_location := p + import_from_path (p) + analyze + end + + analyze + do + get_root_location + get_var_location + get_themes_location + get_files_location + end + +feature -- Access + + configuration_location: detachable PATH + + option (a_name: READABLE_STRING_GENERAL): detachable ANY + do + Result := options.item (a_name) + end + + options: STRING_TABLE [STRING_32] + +feature -- Conversion + + append_to_string (s: STRING) + local + utf: UTF_CONVERTER + do + s.append ("Options:%N") + across + options as c + loop + s.append (c.key.to_string_8) + s.append_character ('=') + utf.string_32_into_utf_8_string_8 (c.item, s) + s.append_character ('%N') + end + + s.append ("Specific:%N") + s.append ("root_location=" + root_location.utf_8_name + "%N") + s.append ("var_location=" + var_location.utf_8_name + "%N") + s.append ("files_location=" + files_location.utf_8_name + "%N") + s.append ("themes_location=" + themes_location.utf_8_name + "%N") + end + +feature -- Element change + + set_option (a_name: READABLE_STRING_GENERAL; a_value: STRING_32) + do + options.force (a_value, a_name.as_string_8) + end + +feature -- Access + + var_location: PATH + + root_location: PATH + + files_location: PATH + + themes_location: PATH + + theme_name (dft: detachable like theme_name): READABLE_STRING_8 + do + if attached options.item ("theme") as s then + Result := s + elseif dft /= Void then + Result := dft + else + Result := "default" + end + end + + site_id: READABLE_STRING_8 + do + if attached options.item ("site.id") as s then + Result := s + else + Result := "_EWF_CMS_NO_ID_" + end + end + + site_name (dft: like site_name): READABLE_STRING_8 + do + if attached options.item ("site.name") as s then + Result := s + else + Result := dft + end + end + + site_url (dft: like site_url): READABLE_STRING_8 + do + if attached options.item ("site.url") as s then + Result := s + else + Result := dft + end + if Result /= Void then + if Result.is_empty then + -- ok + elseif not Result.ends_with ("/") then + Result := Result + "/" + end + end + end + + site_script_url (dft: like site_script_url): detachable READABLE_STRING_8 + do + if attached options.item ("site.script_url") as s then + Result := s + else + Result := dft + end + if Result /= Void then + if Result.is_empty then + elseif not Result.ends_with ("/") then + Result := Result + "/" + end + end + end + + site_email (dft: like site_email): READABLE_STRING_8 + do + if attached options.item ("site.email") as s then + Result := s + else + Result := dft + end + end + +feature -- Change + + get_var_location + local + utf: UTF_CONVERTER + do + if attached options.item ("var-dir") as s then + create var_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + var_location := execution_environment.current_working_path + end + end + + get_root_location + local + utf: UTF_CONVERTER + do + if attached options.item ("root-dir") as s then + create root_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + root_location := execution_environment.current_working_path + end + end + + get_files_location + local + utf: UTF_CONVERTER + do + if attached options.item ("files-dir") as s then + create files_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + create files_location.make_from_string ("files") + end + end + + get_themes_location + local + utf: UTF_CONVERTER + do + if attached options.item ("themes-dir") as s then + create themes_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + themes_location := root_location.extended ("themes") + end + end + +feature {NONE} -- Implementation + + import_from_file (fn: READABLE_STRING_GENERAL) + do + import_from_path (create {PATH}.make_from_string (fn)) + end + + import_from_path (a_filename: PATH) + -- Import ini file content + local + f: PLAIN_TEXT_FILE + l,v: STRING_8 + p: INTEGER + do + create f.make_with_path (a_filename) + if f.exists and f.is_readable then + f.open_read + from + f.read_line + until + f.exhausted + loop + l := f.last_string + l.left_adjust + if not l.is_empty then + if l[1] = '#' then + -- commented line + else + p := l.index_of ('=', 1) + if p > 1 then + v := l.substring (p + 1, l.count) + l.keep_head (p - 1) + v.left_adjust + v.right_adjust + l.right_adjust + + if l.is_case_insensitive_equal ("@include") then + import_from_file (resolved_string (v)) + else + set_option (l.as_lower, resolved_string (v)) + end + end + end + end + f.read_line + end + f.close + end + end + +feature {NONE} -- Environment + + resolved_string (s: READABLE_STRING_8): STRING_32 + -- Resolved `s' using `options' or else environment variables. + local + i,n,b,e: INTEGER + k: detachable READABLE_STRING_8 + do + from + i := 1 + n := s.count + create Result.make (s.count) + until + i > n + loop + if i + 1 < n and then s[i] = '$' and then s[i+1] = '{' then + b := i + 2 + e := s.index_of ('}', b) - 1 + if e > 0 then + k := s.substring (b, e) + if attached option (k) as v then + if attached {READABLE_STRING_32} v as s32 then + Result.append (s32) + else + Result.append (v.out) + end + i := e + 1 + elseif attached execution_environment.item (k) as v then + Result.append (v) + i := e + 1 + else + Result.extend (s[i]) + end + else + Result.extend (s[i]) + end + else + Result.extend (s[i]) + end + i := i + 1 + end + end + + + +end diff --git a/draft/application/cms/src/cms_default_setup.e b/draft/application/cms/src/cms_default_setup.e new file mode 100644 index 00000000..96002a52 --- /dev/null +++ b/draft/application/cms/src/cms_default_setup.e @@ -0,0 +1,132 @@ +note + description: "Summary description for {CMS_DEFAULT_SETUP}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_DEFAULT_SETUP + +inherit + CMS_SETUP + redefine + default_create + end + +create + default_create, + make, + make_from_file + +feature {NONE} -- Initialization + + make (a_cfg: CMS_CONFIGURATION) + do + configuration := a_cfg + default_create + end + + make_from_file (fn: READABLE_STRING_GENERAL) + local + cfg: CMS_CONFIGURATION + do + create cfg.make_from_file (fn) + make (cfg) + end + + default_create + do + Precursor + build_modules + build_storage + build_session_manager + build_auth_engine + build_mailer + end + +feature -- Access + + modules: ARRAYED_LIST [CMS_MODULE] + + storage: CMS_STORAGE + -- CMS persistent layer + + session_manager: WSF_SESSION_MANAGER + -- CMS Session manager + + auth_engine: CMS_AUTH_ENGINE + -- CMS Authentication engine + + mailer: NOTIFICATION_MAILER + +feature {NONE} -- Initialization + + build_modules + local + m: CMS_MODULE + do + create modules.make (3) + + -- Core + create {USER_MODULE} m.make + m.enable + modules.extend (m) + + create {ADMIN_MODULE} m.make + m.enable + modules.extend (m) + + create {NODE_MODULE} m.make + m.enable + modules.extend (m) + end + + build_storage + local + dn: PATH + do + if attached configuration as cfg and then attached cfg.var_location as l_site_var_dir then + dn := l_site_var_dir + else + create dn.make_current + end + create {CMS_SED_STORAGE} storage.make (dn.extended ("_storage_").name) + end + + build_session_manager + local + dn: PATH + do + if attached configuration as cfg and then attached cfg.var_location as l_site_var_dir then + dn := l_site_var_dir + else + create dn.make_empty + end + dn := dn.extended ("_storage_").extended ("_sessions_") + create {WSF_FS_SESSION_MANAGER} session_manager.make_with_folder (dn.name) + end + + build_auth_engine + do + create {CMS_STORAGE_AUTH_ENGINE} auth_engine.make (storage) + end + + build_mailer + local + ch_mailer: NOTIFICATION_CHAIN_MAILER + st_mailer: CMS_STORAGE_MAILER + do + create st_mailer.make (storage) + create ch_mailer.make (st_mailer) + ch_mailer.set_next (create {NOTIFICATION_SENDMAIL_MAILER}) + mailer := ch_mailer + end + +feature -- Change + + add_module (m: CMS_MODULE) + do + modules.force (m) + end + +end diff --git a/draft/application/cms/src/cms_service.e b/draft/application/cms/src/cms_service.e new file mode 100644 index 00000000..c0bab037 --- /dev/null +++ b/draft/application/cms/src/cms_service.e @@ -0,0 +1,442 @@ +note + description: "[ + This class implements the CMS service + + It could be used to implement the main EWF service, or + even for a specific handler. + ]" + +class + CMS_SERVICE + +inherit + WSF_SERVICE + +create + make + +feature {NONE} -- Initialization + + make (a_setup: CMS_SETUP) + local + cfg: detachable CMS_CONFIGURATION + do + cfg := a_setup.configuration + if cfg = Void then + create cfg.make + end + + configuration := cfg + base_url := a_setup.base_url + + site_id := cfg.site_id + site_url := cfg.site_url ("") + site_name := cfg.site_name ("EWF::CMS") + site_email := cfg.site_email ("webmaster") + site_dir := cfg.root_location + site_var_dir := cfg.var_location + files_location := cfg.files_location + themes_location := cfg.themes_location + theme_name := cfg.theme_name ("default") + + set_script_url (cfg.site_script_url (Void)) -- Temporary value + + compute_theme_resource_location + + create content_types.make (3) + + modules := a_setup.modules + storage := a_setup.storage + session_manager := a_setup.session_manager + auth_engine := a_setup.auth_engine + mailer := a_setup.mailer + + initialize_storage + initialize_auth_engine + initialize_session_manager + initialize_mailer + initialize_router + initialize_modules + end + + initialize_session_manager +-- local +-- dn: DIRECTORY_NAME + do +-- create dn.make_from_string (site_var_dir) +-- dn.extend ("_storage_") +-- dn.extend ("_sessions_") +-- create {WSF_FS_SESSION_MANAGER} session_manager.make_with_folder (dn.string) + end + + initialize_storage + do + if not storage.has_user then + initialize_users + end + end + + initialize_users + require + has_no_user: not storage.has_user + local + u: CMS_USER + ur: CMS_USER_ROLE + do + create u.make_new ("admin") + u.set_password ("istrator") + storage.save_user (u) + + create ur.make_with_id (1, "anonymous") + storage.save_user_role (ur) + create ur.make_with_id (2, "authenticated") + ur.add_permission ("create page") + ur.add_permission ("edit page") + storage.save_user_role (ur) + end + + initialize_mailer + local +-- ch_mailer: CMS_CHAIN_MAILER +-- st_mailer: CMS_STORAGE_MAILER + do +-- create st_mailer.make (storage) +-- create ch_mailer.make (st_mailer) +-- ch_mailer.set_next (create {CMS_SENDMAIL_MAILER}) +-- mailer := ch_mailer + end + + initialize_router + local +-- h: CMS_HANDLER + file_hdl: CMS_FILE_SYSTEM_HANDLER + do + create router.make (10) + router.set_base_url (base_url) + + router.map (create {WSF_URI_MAPPING}.make ("/", create {CMS_HANDLER}.make (agent handle_home))) + router.map (create {WSF_URI_MAPPING}.make ("/favicon.ico", create {CMS_HANDLER}.make (agent handle_favicon))) + + create file_hdl.make_with_path (files_location) + file_hdl.disable_index + file_hdl.set_max_age (8*60*60) + router.map (create {WSF_STARTS_WITH_MAPPING}.make ("/files/", file_hdl)) + + create file_hdl.make_with_path (theme_resource_location) + file_hdl.set_max_age (8*60*60) + router.map (create {WSF_STARTS_WITH_MAPPING}.make ("/theme/", file_hdl)) + end + + initialize_modules + do + across + modules as m + loop + if m.item.is_enabled then + m.item.register (Current) + if attached {CMS_HOOK_AUTO_REGISTER} m.item as h_auto then + h_auto.hook_auto_register (Current) + end + end + end + end + + initialize_auth_engine + do +-- create {CMS_STORAGE_AUTH_ENGINE} auth_engine.make (storage) + end + +feature -- Access + + configuration: CMS_CONFIGURATION + + auth_engine: CMS_AUTH_ENGINE + + modules: LIST [CMS_MODULE] + +feature -- Hook: menu_alter + + add_menu_alter_hook (h: like menu_alter_hooks.item) + local + lst: like menu_alter_hooks + do + lst := menu_alter_hooks + if lst = Void then + create lst.make (1) + menu_alter_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + menu_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_MENU_ALTER] + + call_menu_alter_hooks (m: CMS_MENU_SYSTEM; a_execution: CMS_EXECUTION) + do + if attached menu_alter_hooks as lst then + across + lst as c + loop + c.item.menu_alter (m, a_execution) + end + end + end + +feature -- Hook: form_alter + + add_form_alter_hook (h: like form_alter_hooks.item) + local + lst: like form_alter_hooks + do + lst := form_alter_hooks + if lst = Void then + create lst.make (1) + form_alter_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + form_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_FORM_ALTER] + + call_form_alter_hooks (f: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_execution: CMS_EXECUTION) + do + if attached form_alter_hooks as lst then + across + lst as c + loop + c.item.form_alter (f, a_form_data, a_execution) + end + end + end + +feature -- Hook: block + + add_block_hook (h: like block_hooks.item) + local + lst: like block_hooks + do + lst := block_hooks + if lst = Void then + create lst.make (1) + block_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + block_hooks: detachable ARRAYED_LIST [CMS_HOOK_BLOCK] + + hook_block_view (a_execution: CMS_EXECUTION) + do + if attached block_hooks as lst then + across + lst as c + loop + across + c.item.block_list as blst + loop + c.item.get_block_view (blst.item, a_execution) + end + end + end + end + +feature -- Router + + site_id: READABLE_STRING_8 + + site_name: READABLE_STRING_32 + + site_email: READABLE_STRING_8 + + site_url: READABLE_STRING_8 + + site_dir: PATH + + site_var_dir: PATH + + files_location: PATH + + themes_location: PATH + + compute_theme_resource_location + do + theme_resource_location := themes_location.extended (theme_name).extended ("res") + end + + theme_resource_location: PATH + + theme_name: READABLE_STRING_32 + + router: WSF_ROUTER + + map_uri_template (tpl: STRING; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + do + router.map (create {WSF_URI_TEMPLATE_MAPPING}.make_from_template (tpl, create {CMS_HANDLER}.make (proc))) + end + + map_uri (a_uri: STRING; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + do + router.map (create {WSF_URI_MAPPING}.make (a_uri, create {CMS_HANDLER}.make (proc))) + end + +feature -- URL related + + front_path: STRING + do + if attached base_url as l_base_url then + Result := l_base_url + "/" + else + Result := "/" + end + end + + urls_set: BOOLEAN + + initialize_urls (req: WSF_REQUEST) + local + u: like base_url + do + if not urls_set then + u := base_url + if u = Void then + u := "" + end + urls_set := True + if site_url.is_empty then + site_url := req.absolute_script_url (u) + end + set_script_url (req.script_url (u)) + end + end + + base_url: detachable READABLE_STRING_8 + -- Base url (related to the script path). + + script_url: detachable READABLE_STRING_8 + + set_script_url (a_url: like script_url) + local + s: STRING_8 + do + if a_url = Void then + script_url := Void + elseif not a_url.is_empty then + if a_url.ends_with ("/") then + create s.make_from_string (a_url) + else + create s.make (a_url.count + 1) + s.append (a_url) + s.append_character ('/') + end + script_url := s + end + ensure + attached script_url as l_url implies l_url.ends_with ("/") + end + +feature -- Report + + is_front_page (req: WSF_REQUEST): BOOLEAN + do + Result := req.path_info.same_string (front_path) + end + +feature {CMS_EXECUTION, CMS_MODULE} -- Security report + + user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN + -- Anonymous or user `u' has permission for `s' ? + --| `s' could be "create page", + do + Result := storage.user_has_permission (u, s) + end + +feature -- Storage + + session_controller (req: WSF_REQUEST): CMS_SESSION_CONTROLER + -- New session controller for request `req' + do + create Result.make (req, session_manager, site_id) + end + + session_manager: WSF_SESSION_MANAGER + -- CMS Session manager + + storage: CMS_STORAGE + +feature -- Logging + + log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK) + local + l_log: CMS_LOG + do + create l_log.make (a_category, a_message, a_level, Void) + if a_link /= Void then + l_log.set_link (a_link) + end + storage.save_log (l_log) + end + +feature -- Content type + + content_types: ARRAYED_LIST [CMS_CONTENT_TYPE] + -- Available content types + + add_content_type (a_type: CMS_CONTENT_TYPE) + do + content_types.force (a_type) + end + + content_type (a_name: READABLE_STRING_8): detachable CMS_CONTENT_TYPE + do + across + content_types as t + until + Result /= Void + loop + if t.item.name.same_string (a_name) then + Result := t.item + end + end + end + +feature -- Notification + + mailer: NOTIFICATION_MAILER + +feature -- Core Execution + + handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE) + local + fres: WSF_FILE_RESPONSE + do + create fres.make_with_path (theme_resource_location.extended ("favicon.ico")) + fres.set_expires_in_seconds (7 * 24 * 60 * 60) -- 7 jours + res.send (fres) + end + + handle_home (req: WSF_REQUEST; res: WSF_RESPONSE) + do + (create {HOME_CMS_EXECUTION}.make (req, res, Current)).execute + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Default request handler if no other are relevant + local + e: CMS_EXECUTION + sess: WSF_ROUTER_SESSION + do + initialize_urls (req) + create sess + router.dispatch (req, res, sess) + if not sess.dispatched then + create {NOT_FOUND_CMS_EXECUTION} e.make (req, res, Current) + e.execute + end + end + +end diff --git a/draft/application/cms/src/cms_setup.e b/draft/application/cms/src/cms_setup.e new file mode 100644 index 00000000..2f2902ba --- /dev/null +++ b/draft/application/cms/src/cms_setup.e @@ -0,0 +1,55 @@ +note + description: "Summary description for {CMS_SETUP}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_SETUP + +feature -- Access + + configuration: detachable CMS_CONFIGURATION + + base_url: detachable READABLE_STRING_8 + + modules: LIST [CMS_MODULE] + deferred + end + + storage: CMS_STORAGE + -- CMS persistent layer + deferred + end + + session_manager: WSF_SESSION_MANAGER + -- CMS Session manager + deferred + end + + auth_engine: CMS_AUTH_ENGINE + -- CMS Authentication engine + deferred + end + + mailer: NOTIFICATION_MAILER + -- CMS email engine + deferred + end + +feature -- Change + + set_base_url (a_base_url: like base_url) + do + if a_base_url /= Void and then not a_base_url.is_empty then + base_url := a_base_url + else + base_url := Void + end + end + + add_module (m: CMS_MODULE) + deferred + end + +end diff --git a/draft/application/cms/src/handler/cms_file_system_handler.e b/draft/application/cms/src/handler/cms_file_system_handler.e new file mode 100644 index 00000000..6e36d089 --- /dev/null +++ b/draft/application/cms/src/handler/cms_file_system_handler.e @@ -0,0 +1,17 @@ +note + description: "Summary description for {CMS_FILE_SYSTEM_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_FILE_SYSTEM_HANDLER + +inherit + WSF_FILE_SYSTEM_HANDLER + +create + make, + make_with_path + +end diff --git a/draft/application/cms/src/kernel/content/format/filters/cms_html_filter.e b/draft/application/cms/src/kernel/content/format/filters/cms_html_filter.e new file mode 100644 index 00000000..55c509e6 --- /dev/null +++ b/draft/application/cms/src/kernel/content/format/filters/cms_html_filter.e @@ -0,0 +1,128 @@ +note + description: "Summary description for {CMS_HTML_FILTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_HTML_FILTER + +inherit + CMS_FILTER + redefine + default_create + end + +feature {NONE} -- Initialization + + default_create + do + Precursor + allowed_html_tags := <<"a", "em", "strong", "cite", "blockquote", "code", "ul", "ol", "li", "dl">> + description := "Allowed HTML tags: " + across + allowed_html_tags as c + loop + description.append ("<" + c.item + "> ") + end + end + +feature -- Access + + name: STRING_8 = "html_filter" + + title: STRING_8 = "HTML filter" + + description: STRING_8 + + allowed_html_tags: ITERABLE [READABLE_STRING_8] + +feature -- Conversion + + filter (a_text: STRING_8) + local + l_new: STRING_8 + i: INTEGER + n: INTEGER + in_tag: BOOLEAN + p1, p2: INTEGER + do + create l_new.make (a_text.count) + from + p1 := 1 + i := a_text.index_of ('<', 1) + if i > 0 then + l_new.append (a_text.substring (1, i - 1)) + end + n := a_text.count + until + i = 0 or i > n + loop + if a_text[i] = '<' then + in_tag := True + p1 := i + p2 := a_text.index_of ('>', i + 1) + if p2 = 0 then + -- next '<' + i := a_text.index_of ('<', i + 1) + if i > 0 then + l_new.append (a_text.substring (p1, i - 1)) + end + else + if is_authorized (a_text.substring (p1, p2)) then + l_new.append (a_text.substring (p1, p2)) + i := a_text.index_of ('<', p2 + 1) + else + i := a_text.index_of ('<', p2 + 1) + end + if i = 0 then + p1 := p2 + 1 + else + l_new.append (a_text.substring (p2 + 1, i - 1)) + end + end + else + i := i + 1 + end + end + l_new.append (a_text.substring (p1, n)) + a_text.wipe_out + a_text.append (l_new) + end + + is_authorized (s: READABLE_STRING_8): BOOLEAN + -- Is `s' authorized? + --| `s' has either "<....>" or "<..../>" or "" + local + l_tagname: detachable STRING + i,n,p1: INTEGER + do +-- create l_tagname.make_empty + from + i := 2 -- skip first '<' + n := s.count + until + i > n or l_tagname /= Void + loop + if p1 > 0 then + if s[i].is_space or s[i] = '/' or s[i] = '>' then + l_tagname := s.substring (p1, i - 1) + end + else + if s[i].is_space or s[i] = '/' then + else + p1 := i + end + end + i := i + 1 + end + if l_tagname /= Void then + l_tagname.to_lower + Result := across allowed_html_tags as c some c.item.same_string (l_tagname) end + else + Result := True + end + end + + +end diff --git a/draft/application/cms/src/modules/debug/debug_module.e b/draft/application/cms/src/modules/debug/debug_module.e new file mode 100644 index 00000000..6444fc4c --- /dev/null +++ b/draft/application/cms/src/modules/debug/debug_module.e @@ -0,0 +1,128 @@ +note + description: "Summary description for {DEBUG_MODULE}." + date: "$Date$" + revision: "$Revision$" + +class + DEBUG_MODULE + +inherit + CMS_MODULE + +-- CMS_HOOK_BLOCK + + CMS_HOOK_AUTO_REGISTER + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + +create + make + +feature {NONE} -- Initialization + + make + do + name := "debug" + version := "1.0" + description := "Debug" + package := "cms" + end + +feature {CMS_SERVICE} -- Registration + + service: detachable CMS_SERVICE + + register (a_service: CMS_SERVICE) + do + service := a_service + a_service.map_uri_template ("/debug/", agent handle_debug (a_service, ?, ?)) + end + +feature -- Hooks + +-- block_list: ITERABLE [like {CMS_BLOCK}.name] +-- do +-- Result := <<"debug-info">> +-- end + +-- get_block_view (a_block_id: detachable READABLE_STRING_8; a_execution: CMS_EXECUTION) +-- local +-- b: CMS_CONTENT_BLOCK +-- do +-- create b.make ("debug-info", "Debug", "... ", a_execution.formats.plain_text) +-- a_execution.add_block (b, Void) +-- end + +feature -- Handler + + handle_debug (cms: CMS_SERVICE; req: WSF_REQUEST; res: WSF_RESPONSE) + local + e: CMS_EXECUTION + s: STRING + do + if req.is_get_request_method then + create {ANY_CMS_EXECUTION} e.make (req, res, cms) + e.set_title ("DEBUG") + + create s.make_empty + append_info_to ("Name", cms.site_name, e, s) + append_info_to ("Url", cms.site_url, e, s) + + if attached cms.configuration as cfg and then attached cfg.configuration_location as l_loc then + s.append ("
") + append_info_to ("Configuration file", l_loc.name, e, s) + end + + s.append ("
") + + append_info_to ("Current dir", execution_environment.current_working_path.utf_8_name, e, s) + append_info_to ("Base url", cms.base_url, e, s) + append_info_to ("Script url", cms.script_url, e, s) + s.append ("
") + append_info_to ("Dir", cms.site_dir.utf_8_name, e, s) + append_info_to ("Var dir", cms.site_var_dir.utf_8_name, e, s) + s.append ("
") + append_info_to ("Theme", cms.theme_name, e, s) + append_info_to ("Theme location", cms.theme_resource_location.utf_8_name, e, s) + s.append ("
") + append_info_to ("Files location", cms.files_location.utf_8_name, e, s) + s.append ("
") + + append_info_to ("Url", e.url ("/", Void), e, s) + if attached e.user as u then + append_info_to ("User", u.name, e, s) + append_info_to ("User url", e.user_url (u), e, s) + + end + + e.set_main_content (s) + else + create {NOT_FOUND_CMS_EXECUTION} e.make (req, res, cms) + end + e.execute + end + + append_info_to (n: READABLE_STRING_8; v: detachable READABLE_STRING_GENERAL; e: CMS_EXECUTION; t: STRING) + do + t.append ("
  • ") + t.append ("" + n + ": ") + if v /= Void then + t.append (e.html_encoded (v)) + end + t.append ("
  • ") + end + +note + copyright: "Copyright (c) 1984-2013, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/draft/application/cms/src/notification/cms_email.e b/draft/application/cms/src/notification/cms_email.e new file mode 100644 index 00000000..3826a4fc --- /dev/null +++ b/draft/application/cms/src/notification/cms_email.e @@ -0,0 +1,18 @@ +note + description : "[ + Component representing an email + ]" + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + CMS_EMAIL + +inherit + NOTIFICATION_EMAIL + +create + make + +end diff --git a/draft/application/cms/src/notification/cms_storage_mailer.e b/draft/application/cms/src/notification/cms_storage_mailer.e new file mode 100644 index 00000000..c5a4c2d7 --- /dev/null +++ b/draft/application/cms/src/notification/cms_storage_mailer.e @@ -0,0 +1,38 @@ +note + description: "Summary description for {CMS_CHAIN_MAILER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_STORAGE_MAILER + +inherit + NOTIFICATION_MAILER + +create + make + +feature {NONE} -- Initialization + + make (a_storage: like storage) + do + storage := a_storage + end + +feature -- Access + + storage: CMS_STORAGE + +feature -- Status + + is_available: BOOLEAN = True + +feature -- Basic operation + + process_email (a_email: NOTIFICATION_EMAIL) + do + storage.save_email (a_email) + end + +end diff --git a/draft/application/cms/src/storage/cms_sed_storage.e b/draft/application/cms/src/storage/cms_sed_storage.e new file mode 100644 index 00000000..20e340fd --- /dev/null +++ b/draft/application/cms/src/storage/cms_sed_storage.e @@ -0,0 +1,615 @@ +note + description : "[ + CMS Storage implemented using SED + ]" + date : "$Date$" + revision : "$Revision$" + +class + CMS_SED_STORAGE + +inherit + CMS_STORAGE + +create + make + +feature {NONE} -- Initialization + + make (dn: READABLE_STRING_GENERAL) + -- Initialize `Current'. + do + create directory_name.make_from_string (dn) + ensure_directory_exists (directory_name) + create sed + initialize + end + + directory_name: PATH + + sed: SED_STORABLE_FACILITIES + + sed_file_retrieved (f: FILE): detachable ANY + local + r: SED_MEDIUM_READER_WRITER + do + create r.make (f) + r.set_for_reading + Result := sed.retrieved (r, True) + end + + sed_file_store (obj: ANY; f: FILE) + local + w: SED_MEDIUM_READER_WRITER + do + create w.make (f) + w.set_for_writing + sed.store (obj, w) + end + + save_object_with_id (obj: ANY; a_id: INTEGER; a_type: STRING) + local + fn: PATH + f: RAW_FILE + do + fn := directory_name.extended (a_type) + ensure_directory_exists (fn) + fn := fn.extended (a_id.out) +-- .appended_with_extension ("txt") + create f.make_with_path (fn) +-- check not f.exists end + f.create_read_write + sed_file_store (obj, f) + f.close + end + + object_with_id (a_id: INTEGER; a_type: STRING): detachable ANY + local + fn: PATH + f: RAW_FILE + do + fn := directory_name.extended (a_type) + ensure_directory_exists (fn) + fn := fn.extended (a_id.out) +-- .append_with_extension ("txt") + create f.make_with_path (fn) + if f.exists and f.is_readable then + f.open_read + Result := sed_file_retrieved (f) + f.close + end + end + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + do + Result := users_count > 0 + end + + users_count: INTEGER + do + Result := last_sequence ("user") + end + + fill_user_profile (a_user: CMS_USER) + do + if a_user.profile = Void then + if attached user_profile (a_user) as p then + a_user.set_profile (p) + end + end + end + + all_users: LIST [CMS_USER] + local + res: ARRAYED_LIST [like all_users.item] + i, n: like last_sequence + do + n := last_sequence ("user") + create res.make (n) + from + i := 1 + until + i > n + loop + if attached user_by_id (i) as u then + res.force (u) + end + i := i + 1 + end + Result := res + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + do + if attached {like user_by_id} object_with_id (a_id, "user") as u then + Result := u + end + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + local + uid: INTEGER + do + if attached users_index as t then + uid := t.by_name.item (a_name) + if uid > 0 then + Result := user_by_id (uid) + end + end + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + local + uid: INTEGER + do + if attached users_index as t then + uid := t.by_email.item (a_email) + if uid > 0 then + Result := user_by_id (uid) + end + end + end + + is_valid_credential (u, p: READABLE_STRING_32): BOOLEAN + do + if attached user_by_name (u) as l_user then + Result := attached l_user.encoded_password as l_pass and then l_pass.same_string (encoded_password (p)) + end + end + + encoded_password (a_raw_password: STRING_32): attached like {CMS_USER}.encoded_password + do + Result := a_raw_password.as_string_8 + "!123!" + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + local + uid: INTEGER + prof: like {CMS_USER}.profile + l_has_new_name: BOOLEAN + l_has_new_email: BOOLEAN + l_stored_user: like user_by_id + do + if a_user.has_id then + uid := a_user.id + l_stored_user := user_by_id (uid) + if l_stored_user /= Void then + l_has_new_name := not l_stored_user.name.same_string (a_user.name) + l_has_new_email := not (l_stored_user.email ~ a_user.email) + end + else + l_has_new_name := True + l_has_new_email := True + uid := next_sequence ("user") + a_user.set_id (uid) + end + if attached a_user.password as p then + a_user.set_encoded_password (encoded_password (p)) + a_user.set_password (Void) + end + + prof := a_user.profile + a_user.set_profile (Void) + if prof /= Void then + save_user_profile (a_user, prof) + end + save_object_with_id (a_user, uid, "user") + if l_has_new_name or l_has_new_email then + if attached users_index as l_index then + l_index.by_name.force (uid, a_user.name) + l_index.by_email.force (uid, a_user.email) + store_users_index (l_index) + end + end + a_user.set_profile (prof) + end + +feature -- Access: user_role + + user_role_by_id (a_id: INTEGER): detachable CMS_USER_ROLE + do + if attached {like user_role_by_id} object_with_id (a_id, "user_roles") as ur then + Result := ur + end + end + + user_roles: LIST [CMS_USER_ROLE] + local + i: INTEGER + n: like last_sequence + do + n := last_sequence ("user_roles") + create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (n) + if n > 0 then + from + i := 1 + until + i > n + loop + if attached user_role_by_id (i) as ur then + Result.force (ur) + end + i := i + 1 + end + end + end + +feature -- Change: user_role + + save_user_role (a_role: CMS_USER_ROLE) + do + if not a_role.has_id then + a_role.set_id (next_sequence ("user_roles")) + end + save_object_with_id (a_role, a_role.id, "user_roles") + end + +feature -- Email + + save_email (a_email: NOTIFICATION_EMAIL) + local + dn: PATH + fn: PATH + f: RAW_FILE + ts: INTEGER_64 + i: INTEGER + do + dn := directory_name.extended ("emails") + ensure_directory_exists (dn) + ts := (create {HTTP_DATE_TIME_UTILITIES}).unix_time_stamp (a_email.date) + from + fn := dn.extended (ts.out).appended_with_extension ("txt") + create f.make_with_path (fn) + until + not f.exists + loop + i := i + 1 + fn := dn.extended (ts.out + "-" + i.out).appended_with_extension ("txt") + f.make_with_path (fn) + end + f.create_read_write + f.put_string (a_email.message) + f.close + end + +feature -- Log + + log (a_id: like {CMS_LOG}.id): detachable CMS_LOG + do + if attached {CMS_LOG} object_with_id (a_id, "log") as l then + Result := l + end + end + + recent_logs (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] + local + n: Like last_sequence + i, p1, nb: INTEGER + do + n := last_sequence ("log") + p1 := n - a_lower + 1 + + if p1 > 0 then + create {ARRAYED_LIST [CMS_LOG]} Result.make (a_count) + from + i := p1 + until + i < 1 or nb = a_count + loop + if attached log (i) as obj then + Result.force (obj) + nb := nb + 1 + end + i := i - 1 + end + else + create {ARRAYED_LIST [CMS_LOG]} Result.make (0) + end + end + + + save_log (a_log: CMS_LOG) + do + if not a_log.has_id then + a_log.set_id (next_sequence ("log")) + end + save_object_with_id (a_log, a_log.id, "log") + end + +feature -- Node + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + local + n: Like last_sequence + i, p1, nb: INTEGER + do + n := last_sequence ("node") + p1 := n - a_lower + 1 + + if p1 > 0 then + create {ARRAYED_LIST [CMS_NODE]} Result.make (a_count) + from + i := p1 + until + i < 1 or nb = a_count + loop + if attached node (i) as l_node then + Result.force (l_node) + nb := nb + 1 + end + i := i - 1 + end + else + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + end + end + + node (a_id: INTEGER): detachable CMS_NODE + do + if attached {like node} object_with_id (a_id, "node") as obj then + Result := obj + end + end + + save_node (a_node: CMS_NODE) + local + nid: INTEGER + do + if a_node.has_id then + nid := a_node.id + else + nid := next_sequence ("node") + a_node.set_id (nid) + end + + save_object_with_id (a_node, nid, "node") + end + +feature {NONE} -- Implementation + + last_sequence (a_type: STRING): INTEGER + local + fn: PATH + f: RAW_FILE + do + fn := directory_name.extended (a_type).appended_with_extension ("last_id") + create f.make_with_path (fn) + if f.exists and then f.is_readable then + f.open_read + f.read_line + if f.last_string.is_integer then + Result := f.last_string.to_integer + else + check is_integer: False end + end + f.close + end + end + + next_sequence (a_type: STRING): INTEGER + local + fn: PATH + f: RAW_FILE + do + fn := directory_name.extended (a_type).appended_with_extension ("last_id") + create f.make_with_path (fn) + if f.exists and then f.is_readable then + f.open_read + f.read_line + if f.last_string.is_integer then + Result := f.last_string.to_integer + else + check is_integer: False end + end + f.close + end + Result := Result + 1 + f.open_write + f.put_string (Result.out) + f.put_new_line + f.close + end + + users_index: TUPLE [ + by_name: HASH_TABLE [like {CMS_USER}.id, like {CMS_USER}.name]; + by_email: HASH_TABLE [like {CMS_USER}.id, like {CMS_USER}.email] + ] + local + f: RAW_FILE + fn: PATH + res: detachable like users_index + retried: INTEGER + do + fn := directory_name.extended ("users.db") + create f.make_with_path (fn) + if retried = 0 then + if f.exists and then f.is_readable then + f.open_read + if attached {like users_index} sed_file_retrieved (f) as r then + res := r + end + f.close + else + end + end + if res = Void then + res := [ create {HASH_TABLE [like {CMS_USER}.id, like {CMS_USER}.name]}.make (1), + create {HASH_TABLE [like {CMS_USER}.id, like {CMS_USER}.email]}.make (1) ] + end + Result := res + rescue + retried := retried + 1 + retry + end + + store_users_index (a_users_index: like users_index) + local + f: RAW_FILE + fn: PATH + do + fn := directory_name.extended ("users.db") + create f.make_with_path (fn) + if not f.exists or else f.is_writable then + f.open_write + sed_file_store (a_users_index, f) + f.close + end + end + + user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE + do + if attached {like user_profile} object_with_id (a_user.id, "user_profile") as obj then + Result := obj + end + end + + save_user_profile (a_user: CMS_USER; a_prof: CMS_USER_PROFILE) + local + l_id: INTEGER + do + if a_user.has_id then + l_id := a_user.id + end + + save_object_with_id (a_prof, l_id, "user_profile") + end + +feature -- Misc + + custom_type (a_type: READABLE_STRING_8): STRING + do + Result := "custom__" + a_type + end + + custom_value_id (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): INTEGER + -- Storage `id' for custom value named `a_name' if any. + -- If no such data exists, return 0 + local + i, + l_id, l_last_id: INTEGER + t: STRING + do + t := custom_type (a_type) + l_last_id := last_sequence (t) + from + i := 1 + until + i > l_last_id or l_id > 0 + loop + if + attached {TUPLE [name: READABLE_STRING_8; value: attached like custom_value]} object_with_id (i, t) as obj and then + obj.name.same_string (a_name) + then + l_id := i + end + i := i + 1 + end + end + + set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value ; a_type: READABLE_STRING_8) + -- Save data `a_name:a_value' for type `a_type' + local + t: STRING + l_id: INTEGER + do + t := custom_type (a_type) + l_id := custom_value_id (a_name, a_type) + if l_id = 0 then + l_id := next_sequence (t) + end + save_object_with_id ([a_name, a_value], l_id, t) + end + + custom_value (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable TABLE_ITERABLE [READABLE_STRING_8, STRING_8] + -- Data for name `a_name' and type `a_type'. + local + i, + l_id, l_last_id: INTEGER + t: STRING + do + t := custom_type (a_type) + l_last_id := last_sequence (t) + from + i := 1 + until + i > l_last_id or l_id > 0 + loop + if + attached {TUPLE [name: READABLE_STRING_8; value: attached like custom_value]} object_with_id (i, t) as obj and then + obj.name.same_string (a_name) + then + l_id := i + Result := obj.value + end + i := i + 1 + end + end + + custom_value_names_where (a_where_key, a_where_value: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable LIST [READABLE_STRING_8] + -- Name where custom value has item `a_where_key' same as `a_where_value' for type `a_type'. + local + i, l_last_id: INTEGER + t: STRING + l_key_found: BOOLEAN + res: ARRAYED_LIST [READABLE_STRING_8] + do + create res.make (0) + t := custom_type (a_type) + l_last_id := last_sequence (t) + from + i := 1 + until + i > l_last_id + loop + if + attached {TUPLE [name: READABLE_STRING_8; value: attached like custom_value]} object_with_id (i, t) as d + then + l_key_found := False + across + d.value as c + until + l_key_found or Result /= Void + loop + if c.key.same_string (a_where_key) then + l_key_found := True + if c.item.same_string (a_where_value) then + res.force (d.name) + end + end + end + end + i := i + 1 + end + if not res.is_empty then + Result := res + end + end + +feature {NONE} -- Implementation + + ensure_directory_exists (dn: PATH) + local + d: DIRECTORY + do + d := tmp_dir + d.make_with_path (dn) + if not d.exists then + d.recursive_create_dir + end + end + +feature {NONE} -- Implementation + + tmp_dir: DIRECTORY + once + create Result.make_with_path (directory_name) + end + +invariant + +end diff --git a/draft/application/cms/src/storage/cms_storage.e b/draft/application/cms/src/storage/cms_storage.e new file mode 100644 index 00000000..4bf8818c --- /dev/null +++ b/draft/application/cms/src/storage/cms_storage.e @@ -0,0 +1,186 @@ +note + description : "[ + CMS interface to storage + ]" + date : "$Date$" + revision : "$Revision$" + +deferred class + CMS_STORAGE + +feature {NONE} -- Initialization + + initialize + do + end + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + deferred + end + + fill_user_profile (a_user: CMS_USER) + deferred + end + + all_users: LIST [CMS_USER] + deferred + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + require + a_id > 0 + deferred + ensure + same_id: Result /= Void implies Result.id = a_id + no_password: Result /= Void implies Result.password = Void + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + require + a_name /= Void and then not a_name.is_empty + deferred + ensure + no_password: Result /= Void implies Result.password = Void + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + deferred + ensure + no_password: Result /= Void implies Result.password = Void + end + + is_valid_credential (u, p: READABLE_STRING_32): BOOLEAN + deferred + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + deferred + ensure + a_user_password_is_encoded: a_user.password = Void + a_user.has_id + end + +feature -- Access: roles and permissions + + user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN + -- Anonymous or user `u' has permission for `s' ? + --| `s' could be "create page", + do + if s = Void then + Result := True + elseif u = Void then + Result := user_role_has_permission (anonymous_user_role, s) + else + Result := user_role_has_permission (authenticated_user_role, s) + if not Result and attached u.roles as l_roles then + across + l_roles as r + until + Result + loop + if attached user_role_by_id (r.item) as ur then + Result := user_role_has_permission (ur, s) + end + end + end + end + end + + anonymous_user_role: CMS_USER_ROLE + do + if attached user_role_by_id (1) as l_anonymous then + Result := l_anonymous + else + create Result.make ("anonymous") + end + end + + authenticated_user_role: CMS_USER_ROLE + do + if attached user_role_by_id (2) as l_authenticated then + Result := l_authenticated + else + create Result.make ("authenticated") + end + end + + user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN + do + Result := a_role.has_permission (s) + end + + user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + deferred + end + + user_roles: LIST [CMS_USER_ROLE] + deferred + end + +feature -- Change: roles and permissions + + save_user_role (a_user_role: CMS_USER_ROLE) + deferred + end + +feature -- Email + + save_email (a_email: NOTIFICATION_EMAIL) + deferred + end + +feature -- Log + + recent_logs (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] + deferred + end + + log (a_id: like {CMS_LOG}.id): detachable CMS_LOG + require + a_id > 0 + deferred + end + + save_log (a_log: CMS_LOG) + deferred + end + +feature -- Node + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + deferred + end + + node (a_id: INTEGER): detachable CMS_NODE + require + a_id > 0 + deferred + end + + save_node (a_node: CMS_NODE) + deferred + end + +feature -- Misc + + set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: READABLE_STRING_8) + -- Save data `a_name:a_value' for type `a_type' + deferred + end + + custom_value (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable TABLE_ITERABLE [READABLE_STRING_8, STRING_8] + -- Data for name `a_name' and type `a_type'. + deferred + end + + custom_value_names_where (a_where_key, a_where_value: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable LIST [READABLE_STRING_8] + -- Names where custom value has item `a_where_key' same as `a_where_value' for type `a_type'. + deferred + end + +end diff --git a/examples/tutorial/step_4/hello/hello.ecf b/examples/tutorial/step_4/hello/hello.ecf index 2a1a12d3..37cfd5d0 100644 --- a/examples/tutorial/step_4/hello/hello.ecf +++ b/examples/tutorial/step_4/hello/hello.ecf @@ -11,7 +11,6 @@ - diff --git a/examples/tutorial/step_4/hello/src/user_message_handler.e b/examples/tutorial/step_4/hello/src/user_message_handler.e index 12a6a0e8..5e1b3f5e 100644 --- a/examples/tutorial/step_4/hello/src/user_message_handler.e +++ b/examples/tutorial/step_4/hello/src/user_message_handler.e @@ -10,6 +10,10 @@ class inherit WSF_URI_TEMPLATE_RESPONSE_HANDLER + SHARED_WSF_PERCENT_ENCODER + rename + percent_encoder as url_encoder + end feature -- Access @@ -75,7 +79,8 @@ feature -- Access url_encoded_string (s: READABLE_STRING_32): STRING_8 do - Result := (create {UTF8_URL_ENCODER}).encoded_string (s) + create Result.make (s.count) + url_encoder.append_percent_encoded_string_to (s, Result) end html_decoded_string (v: READABLE_STRING_32): READABLE_STRING_32 diff --git a/library/network/protocol/http/src/http_date.e b/library/network/protocol/http/src/http_date.e index 7a404017..70e489a4 100644 --- a/library/network/protocol/http/src/http_date.e +++ b/library/network/protocol/http/src/http_date.e @@ -158,60 +158,128 @@ feature -- Conversion to string append_date_time_to_ansi_c_string (date_time, Result) end +feature -- Conversion into string + + append_to_yyyy_mmm_dd_string (s: STRING_GENERAL) + local + dt: DATE_TIME + do + dt := date_time + append_integer_to (dt.year, s) -- yyyy + s.append_code (32) -- 32 ' ' -- SPace + append_month_mmm_to (dt.month, s) -- mmm + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_integer_to (dt.day, s) -- dd + end + + append_to_rfc1123_string (s: STRING_GENERAL) + local + dt: DATE_TIME + do + dt := date_time + append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd + s.append_code (44) -- 44 ',' -- ',' + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_integer_to (dt.day, s) -- dd + s.append_code (32) -- 32 ' ' -- SPace + append_month_mmm_to (dt.month, s) -- mmm + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.year, s) -- YYYY + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append (" GMT") -- SPace + GMT + end + + append_rfc850_string (s: STRING_GENERAL) + local + dt: DATE_TIME + do + dt := date_time + append_day_name_to (dt.date.day_of_the_week, s) -- mmm + s.append_code (44) -- 44 ',' -- ',' + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_integer_to (dt.day, s) -- dd + s.append_code (45) -- 45 '-' -- '-' + append_month_mmm_to (dt.month, s) -- mmm + s.append_code (45) -- 45 '-' -- '-' + append_integer_to (dt.year \\ 100, s) -- yy + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append (" GMT") -- SPace + GMT + end + + append_to_ansi_c_string (s: STRING_GENERAL) + --| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + local + dt: DATE_TIME + do + dt := date_time + append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd + s.append_code (32) -- 32 ' ' -- SPace + append_month_mmm_to (dt.month, s) -- mmm + s.append_code (32) -- 32 ' ' -- SPace + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.day, s) -- d + s.append_code (32) -- 32 ' ' -- SPace + append_2_digits_time_to (dt.time, s) -- hh:mi:ss + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.year, s) -- yyyy + end + feature -- Conversion into string - append_date_time_to_yyyy_mmm_dd_string (dt: DATE_TIME; s: STRING) + append_date_time_to_yyyy_mmm_dd_string (dt: DATE_TIME; s: STRING_GENERAL) do - s.append_integer (dt.year) -- yyyy - s.append_character (' ') -- ' ' + append_integer_to (dt.year, s) -- yyyy + s.append_code (32) -- 32 ' ' -- SPace append_month_mmm_to (dt.month, s) -- mmm - s.append_character (' ') -- ' ' + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_integer_to (dt.day, s) -- dd end - append_date_time_to_rfc1123_string (dt: DATE_TIME; s: STRING) + append_date_time_to_rfc1123_string (dt: DATE_TIME; s: STRING_GENERAL) do append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd - s.append_character (',') -- ',' - s.append_character (' ') -- SPace + s.append_code (44) -- 44 ',' -- ',' + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_integer_to (dt.day, s) -- dd - s.append_character (' ') -- SPace + s.append_code (32) -- 32 ' ' -- SPace append_month_mmm_to (dt.month, s) -- mmm - s.append_character (' ') -- SPace - s.append_integer (dt.year) -- yyyy - s.append_character (' ') -- SPace + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.year, s) -- yyyy + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_time_to (dt.time, s) -- hh:mi:ss s.append (" GMT") -- SPace + GMT end - append_date_time_to_rfc850_string (dt: DATE_TIME; s: STRING) + append_date_time_to_rfc850_string (dt: DATE_TIME; s: STRING_GENERAL) do append_day_name_to (dt.date.day_of_the_week, s) -- mmm - s.append_character (',') -- , - s.append_character (' ') -- SPace + s.append_code (44) -- 44 ',' -- ',' + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_integer_to (dt.day, s) -- dd - s.append_character ('-') -- '-' + s.append_code (45) -- 45 '-' -- '-' append_month_mmm_to (dt.month, s) -- mmm - s.append_character ('-') -- '-' - s.append_integer (dt.year \\ 100) -- yy - s.append_character (' ') -- SPace + s.append_code (45) -- 45 '-' -- '-' + append_integer_to (dt.year \\ 100, s) -- yy + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_time_to (dt.time, s) -- hh:mi:ss s.append (" GMT") -- SPace + GMT end - append_date_time_to_ansi_c_string (dt: DATE_TIME; s: STRING) + append_date_time_to_ansi_c_string (dt: DATE_TIME; s: STRING_GENERAL) --| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format do append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd - s.append_character (' ') -- SP + s.append_code (32) -- 32 ' ' -- SPace append_month_mmm_to (dt.month, s) -- mmm - s.append_character (' ') -- SPace - s.append_character (' ') -- SPace - s.append_integer (dt.day) -- d - s.append_character (' ') -- SPace + s.append_code (32) -- 32 ' ' -- SPace + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.day, s) -- d + s.append_code (32) -- 32 ' ' -- SPace append_2_digits_time_to (dt.time, s) -- hh:mi:ss - s.append_character (' ') -- SPace - s.append_integer (dt.year) -- yyyy + s.append_code (32) -- 32 ' ' -- SPace + append_integer_to (dt.year, s) -- yyyy end feature -- Status report @@ -228,26 +296,26 @@ feature -- Status report feature {NONE} -- Implementation - append_2_digits_integer_to (i: INTEGER; s: STRING) + append_2_digits_integer_to (i: INTEGER; s: STRING_GENERAL) require is_not_negative: i >= 0 do if i <= 9 then - s.append_character ('0') + s.append_code (48) -- 48 '0' end - s.append_integer (i) + append_integer_to (i, s) end - append_2_digits_time_to (t: TIME; s: STRING) + append_2_digits_time_to (t: TIME; s: STRING_GENERAL) do append_2_digits_integer_to (t.hour, s) -- hh - s.append_character (':') -- : + s.append_code (58) -- 58 ':' -- : append_2_digits_integer_to (t.minute, s) -- mi - s.append_character (':') -- : + s.append_code (58) -- 58 ':' -- : append_2_digits_integer_to (t.second, s) -- ss end - append_day_ddd_to (d: INTEGER; s: STRING) + append_day_ddd_to (d: INTEGER; s: STRING_GENERAL) require 1 <= d and d <= 7 do @@ -264,7 +332,7 @@ feature {NONE} -- Implementation end end - append_day_name_to (d: INTEGER; s: STRING) + append_day_name_to (d: INTEGER; s: STRING_GENERAL) require 1 <= d and d <= 7 do @@ -281,7 +349,7 @@ feature {NONE} -- Implementation end end - append_month_mmm_to (m: INTEGER; s: STRING) + append_month_mmm_to (m: INTEGER; s: STRING_GENERAL) require 1 <= m and m <= 12 do @@ -303,6 +371,17 @@ feature {NONE} -- Implementation end end + append_integer_to (i: INTEGER; s: STRING_GENERAL) + do + if attached {STRING_32} s as s32 then + s32.append_integer (i) + elseif attached {STRING_8} s as s8 then + s8.append_integer (i) + else + s.append (i.out) + end + end + feature {NONE} -- Implementation string_to_date_time (s: READABLE_STRING_8): detachable DATE_TIME diff --git a/library/network/protocol/http/src/http_file_extension_mime_mapping.e b/library/network/protocol/http/src/http_file_extension_mime_mapping.e index 387e8ed4..043e0fc1 100644 --- a/library/network/protocol/http/src/http_file_extension_mime_mapping.e +++ b/library/network/protocol/http/src/http_file_extension_mime_mapping.e @@ -33,8 +33,7 @@ feature {NONE} -- Initialization -- Create with no mapping -- but one can use `map' to add new mapping do - create mapping.make (n) - mapping.compare_objects + create mapping.make_caseless (n) end make_default @@ -43,9 +42,8 @@ feature {NONE} -- Initialization local m: like mapping do - create m.make (40) + create m.make_caseless (40) mapping := m - m.compare_objects m.force (text_css, "css") m.force (text_html, "html") m.force (text_xml, "xml") @@ -74,13 +72,13 @@ feature {NONE} -- Initialization m.force (text_plain, "txt") end - make_from_file (fn: READABLE_STRING_8) + make_from_file (fn: READABLE_STRING_GENERAL) -- Create with mime.types file -- One can use `map' to add new mapping local f: RAW_FILE do - create f.make (fn) + create f.make_with_name (fn) if f.exists and then f.is_readable then make_empty (50) f.open_read @@ -128,7 +126,7 @@ feature {NONE} -- Initialization feature -- Access - mime_type (ext: READABLE_STRING_8): detachable READABLE_STRING_8 + mime_type (ext: READABLE_STRING_GENERAL): detachable READABLE_STRING_8 -- Mime type for extension `ext' do Result := mapping.item (ext.as_lower) @@ -136,7 +134,7 @@ feature -- Access feature -- Element change - map (e: READABLE_STRING_8; t: READABLE_STRING_8) + map (e: READABLE_STRING_GENERAL; t: READABLE_STRING_8) -- Add mapping extension `e' to mime type `t' do mapping.force (t, e.as_lower) @@ -220,13 +218,13 @@ feature {NONE} -- Implementation feature {NONE} -- Extension MIME mapping - mapping: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + mapping: STRING_TABLE [READABLE_STRING_8] invariant mapping_keys_are_lowercase: across mapping as c all c.key.same_string (c.key.as_lower) end note - copyright: "2011-2011, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/tests/tests.ecf b/library/network/protocol/http/tests/tests.ecf index d3f421e2..24346a73 100644 --- a/library/network/protocol/http/tests/tests.ecf +++ b/library/network/protocol/http/tests/tests.ecf @@ -1,7 +1,7 @@ - + /.git$ /EIFGENs$ diff --git a/library/runtime/process/notification_email/license.lic b/library/runtime/process/notification_email/license.lic new file mode 100644 index 00000000..27384d13 --- /dev/null +++ b/library/runtime/process/notification_email/license.lic @@ -0,0 +1,10 @@ +${NOTE_KEYWORD} + copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" diff --git a/library/runtime/process/notification_email/notification_chain_mailer.e b/library/runtime/process/notification_email/notification_chain_mailer.e new file mode 100644 index 00000000..7c400683 --- /dev/null +++ b/library/runtime/process/notification_email/notification_chain_mailer.e @@ -0,0 +1,68 @@ +note + description: "Summary description for {NOTIFICATION_CHAIN_MAILER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + NOTIFICATION_CHAIN_MAILER + +inherit + NOTIFICATION_MAILER + +create + make + +feature {NONE} -- Initialization + + make (a_mailer: like active) + do + active := a_mailer + end + +feature -- Access + + active: NOTIFICATION_MAILER + + next: detachable NOTIFICATION_MAILER + +feature -- Status + + is_available: BOOLEAN + do + Result := active.is_available + if not Result and attached next as l_next then + Result := l_next.is_available + end + end + +feature -- Change + + set_next (m: like next) + do + next := m + end + +feature -- Basic operation + + process_email (a_email: NOTIFICATION_EMAIL) + do + if active.is_available then + active.process_email (a_email) + end + if attached next as l_next and then l_next.is_available then + l_next.process_email (a_email) + end + end + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/runtime/process/notification_email/notification_email-safe.ecf b/library/runtime/process/notification_email/notification_email-safe.ecf new file mode 100644 index 00000000..8bf4ed88 --- /dev/null +++ b/library/runtime/process/notification_email/notification_email-safe.ecf @@ -0,0 +1,18 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + diff --git a/library/runtime/process/notification_email/notification_email.e b/library/runtime/process/notification_email/notification_email.e new file mode 100644 index 00000000..4ca33864 --- /dev/null +++ b/library/runtime/process/notification_email/notification_email.e @@ -0,0 +1,104 @@ +note + description : "[ + Component representing an email + ]" + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + NOTIFICATION_EMAIL + +create + make + +feature {NONE} -- Initialization + + make (a_from: like from_address; a_to_address: READABLE_STRING_8; a_subject: like subject; a_body: like body) + -- Initialize `Current'. + do + initialize + from_address := a_from + subject := a_subject + body := a_body + to_addresses.extend (a_to_address) + + end + + initialize + do + create date.make_now_utc + create to_addresses.make (1) + end + +feature -- Access + + date: DATE_TIME + + from_address: READABLE_STRING_8 + + to_addresses: ARRAYED_LIST [READABLE_STRING_8] + + subject: READABLE_STRING_8 + + body: READABLE_STRING_8 + +feature -- Change + + set_date (d: like date) + do + date := d + end + +feature -- Conversion + + message: STRING_8 + do + Result := header + Result.append_character ('%N') + Result.append (body) + Result.append_character ('%N') + Result.append_character ('%N') + end + + header: STRING_8 + local + hdate: HTTP_DATE + do + create Result.make (20) + Result.append ("From: ") + Result.append (from_address) + Result.append_character ('%N') + Result.append ("Date: ") + create hdate.make_from_date_time (date) + hdate.append_to_rfc1123_string (Result) + Result.append (" GMT%N") + Result.append ("To: ") + across + to_addresses as c + loop + Result.append (c.item) + Result.append_character (';') + end + Result.append_character ('%N') + Result.append ("Subject: ") + Result.append (subject) + Result.append_character ('%N') + ensure + Result.ends_with ("%N") + end + +invariant +-- invariant_clause: True + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/runtime/process/notification_email/notification_email.ecf b/library/runtime/process/notification_email/notification_email.ecf new file mode 100644 index 00000000..80db0f22 --- /dev/null +++ b/library/runtime/process/notification_email/notification_email.ecf @@ -0,0 +1,18 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + diff --git a/library/runtime/process/notification_email/notification_external_mailer.e b/library/runtime/process/notification_email/notification_external_mailer.e new file mode 100644 index 00000000..1e88e856 --- /dev/null +++ b/library/runtime/process/notification_email/notification_external_mailer.e @@ -0,0 +1,208 @@ +note + description: "[ + Component responsible to send email using an external mailer + i.e: an external tool such as sendmail or a script, ... + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +class + NOTIFICATION_EXTERNAL_MAILER + +inherit + NOTIFICATION_MAILER + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + +create + make + +feature {NONE} -- Initialization + + make (a_exe: READABLE_STRING_GENERAL; args: detachable ITERABLE [READABLE_STRING_GENERAL]) + -- Initialize `Current'. + do + set_parameters (a_exe, args) + end + + executable_path: PATH + + arguments: detachable ARRAYED_LIST [READABLE_STRING_GENERAL] + + stdin_mode_set: BOOLEAN + -- Use `stdin' to pass email message, rather than using local file? + + stdin_termination_sequence: detachable READABLE_STRING_8 + -- Termination sequence for the stdin mode + --| If any, this tells the executable all the data has been provided + --| For instance, using sendmail, you should have "%N.%N%N" + +feature -- Status + + is_available: BOOLEAN + local + f: RAW_FILE + do + create f.make_with_path (executable_path) + Result := f.exists + end + +feature -- Change + + set_parameters (cmd: READABLE_STRING_GENERAL; args: detachable ITERABLE [READABLE_STRING_GENERAL]) + -- Set parameters `executable_path' and associated `arguments' + local + l_args: like arguments + do + create executable_path.make_from_string (cmd) + if args = Void then + arguments := Void + else + create l_args.make (5) + across + args as c + loop + l_args.force (c.item) + end + arguments := l_args + end + end + + set_stdin_mode (b: BOOLEAN; v: like stdin_termination_sequence) + -- Set the `stdin_mode_set' value + -- and provide optional termination sequence when stdin mode is selected. + do + stdin_mode_set := b + stdin_termination_sequence := v + end + +feature -- Basic operation + + process_email (a_email: NOTIFICATION_EMAIL) + local + l_factory: PROCESS_FACTORY + args: like arguments + p: detachable PROCESS + retried: INTEGER + do + if retried = 0 then + create l_factory + if stdin_mode_set then + p := l_factory.process_launcher (executable_path.name, arguments, Void) + p.set_hidden (True) + p.set_separate_console (False) + + p.redirect_input_to_stream + p.launch + if p.launched then + p.put_string (a_email.message) + if attached stdin_termination_sequence as v then + p.put_string (v) + end + end + else + if attached arguments as l_args then + args := l_args.twin + else + if attached {RAW_FILE} new_temporary_file (generator) as f then + f.create_read_write + f.put_string (a_email.message) + f.close + create args.make (1) + args.force (f.name) + end + end + p := l_factory.process_launcher (executable_path.name, args, Void) + p.set_hidden (True) + p.set_separate_console (False) + + p.launch + end + if p.launched and not p.has_exited then + p.wait_for_exit_with_timeout (1_000_000) + if not p.has_exited then + p.terminate + if not p.has_exited then + p.wait_for_exit_with_timeout (1_000_000) + end + end + end + elseif retried = 1 then + if p /= Void and then p.launched and then not p.has_exited then + p.terminate + if not p.has_exited then + p.wait_for_exit_with_timeout (1_000_000) + end + end + end + rescue + retried := retried + 1 + retry + end + +feature {NONE} -- Implementation + + new_temporary_file (a_extension: detachable READABLE_STRING_8): RAW_FILE + -- Create file with temporary name. + -- With concurrent execution, noting ensures that {FILE_NAME}.make_temporary_name is unique + -- So using `a_extension' may help + local + bn: STRING_32 + fn: PATH + s: STRING_32 + f: detachable like new_temporary_file + i: INTEGER + do + -- With concurrent execution, nothing ensures that {FILE_NAME}.make_temporary_name is unique + -- So let's try to find + from + create bn.make_from_string_general ((create {FILE_NAME}.make_temporary_name).string) + create s.make_empty + until + f /= Void or i > 1000 + loop + create fn.make_from_string (bn) + s.make_empty + if i > 0 then + s.append_character ('-') + s.append_integer (i) + fn := fn.appended (s) + end + if a_extension /= Void then + fn := fn.appended_with_extension (a_extension) + end + create f.make_with_path (fn) + if f.exists then + i := i + 1 + f := Void + end + end + if f = Void then + Result := new_temporary_file (Void) + else + Result := f + check not_temporary_file_exists: not Result.exists end + check temporary_creatable: Result.is_creatable end + end + ensure + not_result_exists: not Result.exists + result_creatable: Result.is_creatable + end + +invariant + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/runtime/process/notification_email/notification_mailer.e b/library/runtime/process/notification_email/notification_mailer.e new file mode 100644 index 00000000..01a16b2e --- /dev/null +++ b/library/runtime/process/notification_email/notification_mailer.e @@ -0,0 +1,58 @@ +note + description: "[ + Component responsible to send email + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +deferred class + NOTIFICATION_MAILER + +feature -- Status + + is_available: BOOLEAN + -- Is mailer available to use? + deferred + end + +feature -- Basic operation + + process_emails (lst: ITERABLE [NOTIFICATION_EMAIL]) + -- Process set of emails `lst' + require + is_available + do + across + lst as c + loop + process_email (c.item) + end + end + + safe_process_email (a_email: NOTIFICATION_EMAIL) + -- Same as `process_email', but include the check of `is_available' + do + if is_available then + process_email (a_email) + end + end + + process_email (a_email: NOTIFICATION_EMAIL) + -- Process the sending of `a_email' + require + is_available + deferred + end + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/runtime/process/notification_email/notification_sendmail_mailer.e b/library/runtime/process/notification_email/notification_sendmail_mailer.e new file mode 100644 index 00000000..2ea09397 --- /dev/null +++ b/library/runtime/process/notification_email/notification_sendmail_mailer.e @@ -0,0 +1,44 @@ +note + description : "[ + NOTIFICATION_MAILER using sendmail as mailtool + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +class + NOTIFICATION_SENDMAIL_MAILER + +inherit + NOTIFICATION_EXTERNAL_MAILER + redefine + default_create + end + +create + default_create + +feature {NONE} -- Initialization + + default_create + do + Precursor + make ("/usr/sbin/sendmail", <<"-t">>) + if not is_available then + make ("/usr/bin/sendmail", <<"-t">>) + end + set_stdin_mode (True, "%N.%N%N") + end + + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/security/openid/consumer/demo/application.e b/library/security/openid/consumer/demo/application.e index c92a7146..b74ef494 100644 --- a/library/security/openid/consumer/demo/application.e +++ b/library/security/openid/consumer/demo/application.e @@ -12,13 +12,18 @@ inherit undefine requires_proxy end - + WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE WSF_SERVICE WSF_NO_PROXY_POLICY + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + create make_and_launch @@ -42,14 +47,20 @@ feature {NONE} -- Initialization on_launched (conn: WGI_CONNECTOR) local e: EXECUTION_ENVIRONMENT + cmd: STRING_32 do if attached {WGI_NINO_CONNECTOR} conn as nino then - create e - if attached e.get ("COMSPEC") as l_comspec then - e.launch (l_comspec + " /C start " + "http://localhost:" + nino.port.out + "/") - else - e.launch ("http://localhost:" + nino.port.out + "/") + e := execution_environment + create cmd.make (32) + if attached e.item ("COMSPEC") as l_comspec then + cmd.append (l_comspec) + cmd.append ({STRING_32} " /C start ") end + cmd.append ("http://localhost:") + cmd.append_integer (nino.port) + cmd.append_character ({CHARACTER_32} '/') + + e.launch (cmd) end end diff --git a/library/security/openid/consumer/src/openid_consumer.e b/library/security/openid/consumer/src/openid_consumer.e index 2dcc090c..68b73a5c 100644 --- a/library/security/openid/consumer/src/openid_consumer.e +++ b/library/security/openid/consumer/src/openid_consumer.e @@ -137,7 +137,7 @@ feature {OPENID_CONSUMER_VALIDATION} -- Implementation sess: HTTP_CLIENT_SESSION ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT xrds_location: detachable READABLE_STRING_8 - xml: XML_LITE_PARSER + xml: XML_STANDARD_PARSER tree: XML_CALLBACKS_DOCUMENT xelt: detachable XML_ELEMENT s: READABLE_STRING_32 diff --git a/library/server/ewsgi/connectors/cgi/src/wgi_cgi_connector.e b/library/server/ewsgi/connectors/cgi/src/wgi_cgi_connector.e index 56718745..809e4da1 100644 --- a/library/server/ewsgi/connectors/cgi/src/wgi_cgi_connector.e +++ b/library/server/ewsgi/connectors/cgi/src/wgi_cgi_connector.e @@ -52,19 +52,23 @@ feature -- Execution res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void) end if res.message_writable then - res.put_string ("
    " + l_trace + "
    ") + res.put_string ("
    ")
    +							res.put_string (l_trace)
    +							res.put_string ("
    ") end res.push end end end rescue - rescued := True - retry + if not rescued then + rescued := True + retry + end end note - copyright: "2011-2012, Eiffel Software and others" + copyright: "2011-2013, 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/ewsgi/connectors/libfcgi/src/wgi_libfcgi_connector.e b/library/server/ewsgi/connectors/libfcgi/src/wgi_libfcgi_connector.e index 32f54566..b223538a 100644 --- a/library/server/ewsgi/connectors/libfcgi/src/wgi_libfcgi_connector.e +++ b/library/server/ewsgi/connectors/libfcgi/src/wgi_libfcgi_connector.e @@ -55,7 +55,7 @@ feature -- Server feature -- Execution - process_fcgi_request (vars: HASH_TABLE [STRING, STRING]; a_input: like input; a_output: like output) + process_fcgi_request (vars: STRING_TABLE [READABLE_STRING_8]; a_input: like input; a_output: like output) local req: WGI_REQUEST_FROM_TABLE res: detachable WGI_RESPONSE_STREAM @@ -73,15 +73,19 @@ feature -- Execution res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void) end if res.message_writable then - res.put_string ("
    " + l_trace + "
    ") + res.put_string ("
    ")
    +							res.put_string (l_trace)
    +							res.put_string ("
    ") end res.push end end end rescue - rescued := True - retry + if not rescued then + rescued := True + retry + end end feature -- Input/Output @@ -100,7 +104,7 @@ invariant fcgi_attached: fcgi /= Void note - copyright: "2011-2011, Eiffel Software and others" + copyright: "2011-2013, 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/ewsgi/connectors/nino/src/wgi_nino_connector.e b/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e index 45f0a401..431001af 100644 --- a/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e +++ b/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e @@ -127,20 +127,28 @@ feature -- Server server.setup (l_http_handler) end - process_request (env: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_socket: TCP_STREAM_SOCKET) + process_request (env: STRING_TABLE [READABLE_STRING_8]; a_headers_text: STRING; a_socket: TCP_STREAM_SOCKET) local req: WGI_REQUEST_FROM_TABLE res: detachable WGI_NINO_RESPONSE_STREAM + retried: BOOLEAN do - create req.make (env, create {WGI_NINO_INPUT_STREAM}.make (a_socket), Current) - create res.make (create {WGI_NINO_OUTPUT_STREAM}.make (a_socket), create {WGI_NINO_ERROR_STREAM}.make_stderr (a_socket.descriptor.out)) - req.set_meta_string_variable ("RAW_HEADER_DATA", a_headers_text) - service.execute (req, res) - res.push + if not retried then + create req.make (env, create {WGI_NINO_INPUT_STREAM}.make (a_socket), Current) + create res.make (create {WGI_NINO_OUTPUT_STREAM}.make (a_socket), create {WGI_NINO_ERROR_STREAM}.make_stderr (a_socket.descriptor.out)) + req.set_meta_string_variable ("RAW_HEADER_DATA", a_headers_text) + service.execute (req, res) + res.push + end + rescue + if not retried then + retried := True + retry + end end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, 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/ewsgi/connectors/nino/src/wgi_nino_handler.e b/library/server/ewsgi/connectors/nino/src/wgi_nino_handler.e index b1602527..c9890a27 100644 --- a/library/server/ewsgi/connectors/nino/src/wgi_nino_handler.e +++ b/library/server/ewsgi/connectors/nino/src/wgi_nino_handler.e @@ -61,24 +61,24 @@ feature -- Request processing process_request (a_handler: HTTP_CONNECTION_HANDLER; a_socket: TCP_STREAM_SOCKET) -- Process request ... local - env: HASH_TABLE [STRING, STRING] + env: STRING_TABLE [READABLE_STRING_8] p: INTEGER l_request_uri, l_script_name, l_query_string, l_path_info: STRING l_server_name, l_server_port: detachable STRING - a_headers_map: HASH_TABLE [STRING, STRING] + l_headers_map: HASH_TABLE [STRING, STRING] vn: STRING e: EXECUTION_ENVIRONMENT do l_request_uri := a_handler.uri - a_headers_map := a_handler.request_header_map + l_headers_map := a_handler.request_header_map create e if attached e.starting_environment_variables as vars then - create env.make (vars.count) + create env.make_equal (vars.count) across vars as c loop - env.force (c.item.to_string_8, c.key.to_string_8) + env.force (c.item.to_string_8, c.key) end else create env.make (0) @@ -86,11 +86,11 @@ feature -- Request processing --| for Any Abc-Def-Ghi add (or replace) the HTTP_ABC_DEF_GHI variable to `env' from - a_headers_map.start + l_headers_map.start until - a_headers_map.after + l_headers_map.after loop - create vn.make_from_string (a_headers_map.key_for_iteration.as_upper) + create vn.make_from_string (l_headers_map.key_for_iteration.as_upper) vn.replace_substring_all ("-", "_") if vn.starts_with ("CONTENT_") and then @@ -100,8 +100,8 @@ feature -- Request processing else vn.prepend ("HTTP_") end - add_environment_variable (a_headers_map.item_for_iteration, vn, env) - a_headers_map.forth + add_environment_variable (l_headers_map.item_for_iteration, vn, env) + l_headers_map.forth end --| Specific cases @@ -114,7 +114,7 @@ feature -- Request processing l_script_name := l_request_uri.string l_query_string := "" end - if attached a_headers_map.item ("Host") as l_host then + if attached l_headers_map.item ("Host") as l_host then check has_host: env.has ("HTTP_HOST") end -- set_environment_variable (l_host, "HTTP_HOST", env) p := l_host.index_of (':', 1) @@ -129,7 +129,7 @@ feature -- Request processing check host_available: False end end - if attached a_headers_map.item ("Authorization") as l_authorization then + if attached l_headers_map.item ("Authorization") as l_authorization then check has_authorization: env.has ("HTTP_AUTHORIZATION") end -- set_environment_variable (l_authorization, "HTTP_AUTHORIZATION", env) p := l_authorization.index_of (' ', 1) @@ -174,7 +174,7 @@ feature -- Request processing callback.process_request (env, a_handler.request_header, a_socket) end - add_environment_variable (a_value: detachable STRING; a_var_name: STRING; env: HASH_TABLE [STRING, STRING]) + add_environment_variable (a_value: detachable STRING; a_var_name: READABLE_STRING_GENERAL; env: STRING_TABLE [READABLE_STRING_8]) -- Add variable `a_var_name => a_value' to `env' do if a_value /= Void then @@ -188,7 +188,7 @@ feature -- Request processing end end - set_environment_variable (a_value: detachable STRING; a_var_name: STRING; env: HASH_TABLE [STRING, STRING]) + set_environment_variable (a_value: detachable STRING; a_var_name: READABLE_STRING_GENERAL; env: STRING_TABLE [READABLE_STRING_8]) -- Add variable `a_var_name => a_value' to `env' do if a_value /= Void then @@ -197,7 +197,7 @@ feature -- Request processing end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, 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/ewsgi/specification/request/wgi_request.e b/library/server/ewsgi/specification/request/wgi_request.e index 9420a766..fb1f5ef0 100644 --- a/library/server/ewsgi/specification/request/wgi_request.e +++ b/library/server/ewsgi/specification/request/wgi_request.e @@ -88,14 +88,14 @@ feature -- Access: Input feature -- Access: CGI meta variables - meta_variable (a_name: READABLE_STRING_8): detachable READABLE_STRING_8 + meta_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8 -- Environment variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - meta_string_variable (a_name: READABLE_STRING_8): detachable READABLE_STRING_8 + meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8 -- Environment variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty @@ -105,7 +105,7 @@ feature -- Access: CGI meta variables end end - meta_variables: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + meta_variables: STRING_TABLE [READABLE_STRING_8] -- These variables are specific to requests made with HTTP. -- Interpretation of these variables may depend on the value of -- SERVER_PROTOCOL. @@ -635,7 +635,7 @@ invariant path_info_identical: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, 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/ewsgi/specification/stream/wgi_input_stream.e b/library/server/ewsgi/specification/stream/wgi_input_stream.e index f37898fa..63f1cb66 100644 --- a/library/server/ewsgi/specification/stream/wgi_input_stream.e +++ b/library/server/ewsgi/specification/stream/wgi_input_stream.e @@ -138,8 +138,6 @@ feature -- Input nb_large_enough: nb > 0 local s: like last_string - i, end_pos: INTEGER - l_count: INTEGER n: INTEGER l_remaining: INTEGER do diff --git a/library/server/ewsgi/src/implementation/wgi_request_from_table.e b/library/server/ewsgi/src/implementation/wgi_request_from_table.e index 636f5495..a7688281 100644 --- a/library/server/ewsgi/src/implementation/wgi_request_from_table.e +++ b/library/server/ewsgi/src/implementation/wgi_request_from_table.e @@ -61,16 +61,16 @@ feature -- EWSGI access feature -- Access: CGI meta parameters - meta_variables: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + meta_variables: STRING_TABLE [READABLE_STRING_8] -- CGI Environment parameters - meta_variable (a_name: READABLE_STRING_8): detachable READABLE_STRING_8 + meta_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8 -- CGI meta variable related to `a_name' do Result := meta_variables.item (a_name) end - meta_string_variable_or_default (a_name: READABLE_STRING_8; a_default: READABLE_STRING_8; use_default_when_empty: BOOLEAN): READABLE_STRING_8 + meta_string_variable_or_default (a_name: READABLE_STRING_GENERAL; a_default: READABLE_STRING_8; use_default_when_empty: BOOLEAN): READABLE_STRING_8 -- Value for meta parameter `a_name' -- If not found, return `a_default' require @@ -86,14 +86,14 @@ feature -- Access: CGI meta parameters end end - set_meta_string_variable (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8) + set_meta_string_variable (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_8) do meta_variables.force (a_value, a_name) ensure param_set: attached meta_variable (a_name) as val and then val ~ a_value end - unset_meta_variable (a_name: READABLE_STRING_8) + unset_meta_variable (a_name: READABLE_STRING_GENERAL) do meta_variables.remove (a_name) ensure @@ -268,7 +268,7 @@ feature {NONE} -- Element change: CGI meta parameter related to PATH_INFO -- Fill with variable from `a_vars' local s: like meta_string_variable - table: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + table: STRING_TABLE [READABLE_STRING_8] l_query_string: like query_string l_request_uri: detachable STRING_32 l_empty_string: like empty_string @@ -276,15 +276,14 @@ feature {NONE} -- Element change: CGI meta parameter related to PATH_INFO create {STRING_8} l_empty_string.make_empty empty_string := l_empty_string - create table.make (a_vars.count) - table.compare_objects + create table.make_equal (a_vars.count) meta_variables := table from a_vars.start until a_vars.after loop - table.force (a_vars.item_for_iteration.to_string_8, a_vars.key_for_iteration.to_string_8) + table.force (a_vars.item_for_iteration.to_string_8, a_vars.key_for_iteration) a_vars.forth end @@ -446,7 +445,7 @@ invariant empty_string_unchanged: empty_string.is_empty note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, 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/libfcgi/interface/fcgi_i.e b/library/server/libfcgi/interface/fcgi_i.e index b568f197..08ec4242 100644 --- a/library/server/libfcgi/interface/fcgi_i.e +++ b/library/server/libfcgi/interface/fcgi_i.e @@ -19,21 +19,21 @@ feature {NONE} -- Initialization feature -- Access - updated_environ_variables: HASH_TABLE [STRING, STRING] + updated_environ_variables: STRING_TABLE [READABLE_STRING_8] local i: INTEGER p, v, null: POINTER do p := fcgi_environ - create Result.make (50) + create Result.make_equal (50) if p /= null then from i := 0 - v := fcgi_i_th_environ (i,p) + v := fcgi_i_th_environ (i, p) until v = null loop - if attached separated_variables (create {STRING}.make_from_c (v)) as t then + if attached separated_variables (create {STRING_8}.make_from_c (v)) as t then Result.force (t.value, t.key) end i := i + 1 @@ -196,7 +196,7 @@ feature {NONE} -- Implementation: Environment "return ((char **)$p)[$i];" end - separated_variables (a_var: STRING): detachable TUPLE [value: STRING; key: STRING] + separated_variables (a_var: READABLE_STRING_8): detachable TUPLE [value: READABLE_STRING_8; key: READABLE_STRING_8] -- Given an environment variable `a_var' in form of "key=value", -- return separated key and value. -- Return Void if `a_var' is in incorrect format. @@ -224,7 +224,7 @@ feature {NONE} -- Implementation: Environment end note - copyright: "Copyright (c) 1984-2011, Eiffel Software and others" + copyright: "Copyright (c) 1984-2013, 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/connector/openshift/wsf_openshift_service_launcher.e b/library/server/wsf/connector/openshift/wsf_openshift_service_launcher.e index cd5ffbc4..5e262ae9 100644 --- a/library/server/wsf/connector/openshift/wsf_openshift_service_launcher.e +++ b/library/server/wsf/connector/openshift/wsf_openshift_service_launcher.e @@ -24,6 +24,11 @@ inherit initialize end + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + create make, make_and_launch, @@ -37,15 +42,19 @@ feature {NONE} -- Initialization l_env: EXECUTION_ENVIRONMENT do Precursor - create l_env + l_env := execution_environment - if attached l_env.get (Openshift_ip) as l_ip then - server_name := l_ip.to_string_8 + if attached l_env.item (Openshift_ip) as l_ip then + if l_ip.is_valid_as_string_8 then + server_name := l_ip.to_string_8 + else + die ("could not parse " + Openshift_ip) + end else die (Openshift_ip + " is not defined") end - if attached l_env.get (Openshift_port) as l_port then + if attached l_env.item (Openshift_port) as l_port then if l_port.is_integer then port_number := l_port.to_integer else @@ -77,7 +86,7 @@ feature {NONE} -- Implementation ;note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, 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/router/documentation/wsf_router_self_documentation_message.e b/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e index 21d4bfc4..acd21662 100644 --- a/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e +++ b/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e @@ -137,7 +137,7 @@ feature {WSF_RESPONSE} -- Output debug l_description.append ("

    Meta Information

      ") - l_description.append ("
    • PATH_INFO=" + request.path_info + "
    • ") + l_description.append ("
    • PATH_INFO=" + request.percent_encoded_path_info + "
    • ") l_description.append ("
    • QUERY_STRING=" + request.query_string + "
    • ") l_description.append ("
    • REQUEST_URI=" + request.request_uri + "
    • ") l_description.append ("
    • SCRIPT_NAME=" + request.script_name + "
    • ") diff --git a/library/server/wsf/router/support/starts_with/wsf_starts_with_handler.e b/library/server/wsf/router/support/starts_with/wsf_starts_with_handler.e index b1d6d422..b3863683 100644 --- a/library/server/wsf/router/support/starts_with/wsf_starts_with_handler.e +++ b/library/server/wsf/router/support/starts_with/wsf_starts_with_handler.e @@ -20,7 +20,7 @@ feature -- Execution a_start_path_attached: a_start_path /= Void req_attached: req /= Void res_attached: res /= Void - path_start_with_a_start_path: req.path_info.starts_with (a_start_path) + path_start_with_a_start_path: req.percent_encoded_path_info.starts_with (a_start_path) deferred end @@ -33,7 +33,7 @@ feature {WSF_ROUTER} -- Mapping end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/router/support/starts_with/wsf_starts_with_mapping_i.e b/library/server/wsf/router/support/starts_with/wsf_starts_with_mapping_i.e index ac09d22c..1a1a8435 100644 --- a/library/server/wsf/router/support/starts_with/wsf_starts_with_mapping_i.e +++ b/library/server/wsf/router/support/starts_with/wsf_starts_with_mapping_i.e @@ -83,7 +83,7 @@ feature {NONE} -- Execution a_start_path_attached: a_start_path /= Void req_attached: req /= Void res_attached: res /= Void - path_start_with_a_start_path: req.path_info.starts_with (a_start_path) + path_start_with_a_start_path: req.percent_encoded_path_info.starts_with (a_start_path) deferred end diff --git a/library/server/wsf/router/support/uri_template/wsf_uri_template_mapping_i.e b/library/server/wsf/router/support/uri_template/wsf_uri_template_mapping_i.e index f1430fa0..786d2af6 100644 --- a/library/server/wsf/router/support/uri_template/wsf_uri_template_mapping_i.e +++ b/library/server/wsf/router/support/uri_template/wsf_uri_template_mapping_i.e @@ -61,7 +61,7 @@ feature -- Status -- local tpl: URI_TEMPLATE - p: READABLE_STRING_32 + p: READABLE_STRING_8 do p := path_from_request (req) tpl := based_uri_template (template, a_router) @@ -72,7 +72,7 @@ feature -- Status -- local tpl: URI_TEMPLATE - p: READABLE_STRING_32 + p: READABLE_STRING_8 new_src: detachable WSF_REQUEST_PATH_PARAMETERS_PROVIDER do p := path_from_request (req) diff --git a/library/server/wsf/router/wsf_file_system_handler.e b/library/server/wsf/router/wsf_file_system_handler.e index 9e54a43e..f494b5b4 100644 --- a/library/server/wsf/router/wsf_file_system_handler.e +++ b/library/server/wsf/router/wsf_file_system_handler.e @@ -15,31 +15,53 @@ inherit WSF_SELF_DOCUMENTED_HANDLER + SHARED_HTML_ENCODER + + SHARED_WSF_PERCENT_ENCODER + rename + percent_encoder as url_encoder + export + {NONE} all + end + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + create + make_with_path, + make_hidden_with_path, make, make_hidden feature {NONE} -- Initialization - make (d: like document_root) - require - valid_d: (d /= Void and then not d.is_empty) implies not d.ends_with (operating_environment.directory_separator.out) - local - e: EXECUTION_ENVIRONMENT + make_with_path (d: like document_root) do if d.is_empty then - create e - document_root := e.current_working_directory + document_root := execution_environment.current_working_path else document_root := d end ensure - not document_root.is_empty and then not document_root.ends_with (operating_environment.directory_separator.out) + not document_root.is_empty end - make_hidden (d: like document_root) - require - valid_d: (d /= Void and then not d.is_empty) implies not d.ends_with (operating_environment.directory_separator.out) + make_hidden_with_path (d: like document_root) + do + make_with_path (d) + is_hidden := True + ensure + hidden: is_hidden + end + + make (d: READABLE_STRING_GENERAL) + do + make_with_path (create {PATH}.make_from_string (d)) + end + + make_hidden (d: READABLE_STRING_GENERAL) do make (d) is_hidden := True @@ -60,9 +82,10 @@ feature -- Documentation Result.add_description ("File service") end -feature -- Access +feature -- Access + + document_root: PATH - document_root: STRING max_age: INTEGER index_disabled: BOOLEAN @@ -118,10 +141,10 @@ feature -- Execution execute (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) local - p: STRING + p: STRING_32 do - p := req.path_info - if p.starts_with (a_start_path) then + create p.make_from_string (req.path_info) + if p.starts_with_general (a_start_path) then p.remove_head (a_start_path.count) else check starts_with_base: False end @@ -131,16 +154,16 @@ feature -- Execution execute_starts_with (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) do - execute (a_start_path,req, res) + execute (a_start_path, req, res) end - process_uri (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) + process_uri (uri: READABLE_STRING_32; req: WSF_REQUEST; res: WSF_RESPONSE) local f: RAW_FILE - fn: READABLE_STRING_8 + fn: like resource_filename do fn := resource_filename (uri) - create f.make (fn) + create f.make_with_path (fn) if f.exists then if f.is_readable then if f.is_directory then @@ -160,14 +183,15 @@ feature -- Execution end end - process_index (a_uri: READABLE_STRING_8; dn: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) + process_index (a_uri: READABLE_STRING_8; dn: PATH; req: WSF_REQUEST; res: WSF_RESPONSE) local h: HTTP_HEADER uri, s: STRING_8 d: DIRECTORY - l_files: LIST [STRING_8] + l_files: LIST [PATH] do - create d.make_open_read (dn) + create d.make_with_path (dn) + d.open_read if attached directory_index_file (d) as f then process_file (f, req, res) else @@ -187,12 +211,16 @@ feature -- Execution s.replace_substring_all ("$URI", uri) from - l_files := d.linear_representation + l_files := d.entries l_files.start until l_files.after loop - s.append ("
    • " + l_files.item_for_iteration + "
    • %N") + s.append ("
    • ") + s.append (html_encoder.encoded_string (l_files.item.name)) + s.append ("
    • %N") l_files.forth end s.append ("[ @@ -217,12 +245,12 @@ feature -- Execution process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE) local - ext: READABLE_STRING_8 + ext: READABLE_STRING_32 ct: detachable READABLE_STRING_8 fres: WSF_FILE_RESPONSE dt: DATE_TIME do - ext := extension (f.name) + ext := extension (f.path.name) ct := extension_mime_mapping.mime_type (ext) if ct = Void then ct := {HTTP_MIME_TYPES}.application_force_download @@ -235,7 +263,7 @@ feature -- Execution then process_not_modified (f_date, req, res) else - create fres.make_with_content_type (ct, f.name) + create fres.make_with_content_type (ct, f.path.name) fres.set_status_code ({HTTP_STATUS_CODE}.ok) -- cache control @@ -341,7 +369,7 @@ feature {NONE} -- Implementation directory_index_file (d: DIRECTORY): detachable FILE local f: detachable RAW_FILE - fn: FILE_NAME + fn: PATH do if attached directory_index as default_index then across @@ -350,12 +378,11 @@ feature {NONE} -- Implementation Result /= Void loop if d.has_entry (c.item) then - create fn.make_from_string (d.name) - fn.set_file_name (c.item) + fn := d.path.extended (c.item) if f = Void then - create f.make (fn.string) + create f.make_with_path (fn) else - f.make (fn.string) + f.make_with_path (fn) end if f.exists and then f.is_readable then Result := f @@ -365,28 +392,34 @@ feature {NONE} -- Implementation end end - resource_filename (uri: READABLE_STRING_8): READABLE_STRING_8 - do - Result := real_filename (document_root + operating_environment.directory_separator.out + real_filename (uri)) - end - - dirname (uri: READABLE_STRING_8): READABLE_STRING_8 + resource_filename (uri: READABLE_STRING_32): PATH local - p: INTEGER + s: like uri_path_to_filename do - p := uri.last_index_of ('/', uri.count) - if p > 0 then - Result := uri.substring (1, p - 1) - else - create {STRING_8} Result.make_empty + Result := document_root + s := uri_path_to_filename (uri) + if not s.is_empty then + Result := Result.extended (s) end end - filename (uri: READABLE_STRING_8): READABLE_STRING_8 + dirname (uri: READABLE_STRING_32): READABLE_STRING_32 local p: INTEGER do - p := uri.last_index_of ('/', uri.count) + p := uri.last_index_of ({CHARACTER_32} '/', uri.count) + if p > 0 then + Result := uri.substring (1, p - 1) + else + create {STRING_32} Result.make_empty + end + end + + filename (uri: READABLE_STRING_32): READABLE_STRING_32 + local + p: INTEGER + do + p := uri.last_index_of ({CHARACTER_32} '/', uri.count) if p > 0 then Result := uri.substring (p + 1, uri.count) else @@ -394,58 +427,52 @@ feature {NONE} -- Implementation end end - extension (uri: READABLE_STRING_8): READABLE_STRING_8 + extension (uri: READABLE_STRING_32): READABLE_STRING_32 local p: INTEGER do - p := uri.last_index_of ('.', uri.count) + p := uri.last_index_of ({CHARACTER_32} '.', uri.count) if p > 0 then Result := uri.substring (p + 1, uri.count) else - create {STRING_8} Result.make_empty + create {STRING_32} Result.make_empty end end - real_filename (fn: STRING): STRING + uri_path_to_filename (fn: READABLE_STRING_32): STRING_32 -- Real filename from url-path `fn' --| Find a better design for this piece of code --| Eventually in a spec/$ISE_PLATFORM/ specific cluster + local + n: INTEGER do - if fn.is_empty then - Result := fn - else + n := fn.count + create Result.make_from_string (fn) + if n > 0 and then Result.item (Result.count) = {CHARACTER_32} '/' then + Result.remove_tail (1) + n := n - 1 + end + if n > 0 and then Result.item (1) = {CHARACTER_32} '/' then + Result.remove_head (1) + n := n - 1 + end + + if n > 0 then if {PLATFORM}.is_windows then - create Result.make_from_string (fn) - Result.replace_substring_all ("/", "\") - if Result [Result.count] = '\' then - Result.remove_tail (1) - end - else - Result := fn - if Result [Result.count] = '/' then - Result.remove_tail (1) - end + Result.replace_substring_all ({STRING_32} "/", {STRING_32} "\") end end end feature {NONE} -- Implementation - node_exists (p: READABLE_STRING_8): BOOLEAN - local - f: RAW_FILE - do - create f.make (p) - Result := f.exists - end - extension_mime_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING local f: RAW_FILE once - create f.make ("mime.types") + create f.make_with_name ("mime.types") if f.exists and then f.is_readable then - create Result.make_from_file (f.name) + create Result.make_from_file (f.path.name) else create Result.make_default end diff --git a/library/server/wsf/router/wsf_router_mapping.e b/library/server/wsf/router/wsf_router_mapping.e index 475818ca..873a25cb 100644 --- a/library/server/wsf/router/wsf_router_mapping.e +++ b/library/server/wsf/router/wsf_router_mapping.e @@ -77,12 +77,12 @@ feature -- Status feature -- Helper - path_from_request (req: WSF_REQUEST): READABLE_STRING_32 + path_from_request (req: WSF_REQUEST): READABLE_STRING_8 -- Path used by `Current' to check that mapping matches request `req' require req_attached: req /= Void do - Result := req.path_info + Result := req.percent_encoded_path_info ensure path_from_request_attached: Result /= Void end diff --git a/library/server/wsf/session/wsf_cookie_session.e b/library/server/wsf/session/wsf_cookie_session.e index 7c8d1425..0e499f6d 100644 --- a/library/server/wsf/session/wsf_cookie_session.e +++ b/library/server/wsf/session/wsf_cookie_session.e @@ -10,6 +10,11 @@ class inherit WSF_SESSION + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + create make, make_new @@ -128,15 +133,6 @@ feature {NONE} -- Storage data.compare_objects end - sessions_folder_name: READABLE_STRING_8 - local - dn: DIRECTORY_NAME - once - create dn.make_from_string ((create {EXECUTION_ENVIRONMENT}).current_working_directory) - dn.extend ("_sessions_") - Result := dn.string - end - load do if manager.session_exists (uuid) then @@ -181,7 +177,7 @@ feature {NONE} -- Implementation end note - copyright: "Copyright (c) 1984-2012, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/session/wsf_fs_session_manager.e b/library/server/wsf/session/wsf_fs_session_manager.e index 260607a1..81187b6f 100644 --- a/library/server/wsf/session/wsf_fs_session_manager.e +++ b/library/server/wsf/session/wsf_fs_session_manager.e @@ -21,12 +21,12 @@ feature {NONE} -- Initialization make_with_folder ("_WSF_SESSIONS_") end - make_with_folder (a_folder: like sessions_folder_name) + make_with_folder (a_folder: READABLE_STRING_GENERAL) do - sessions_folder_name := a_folder + create sessions_folder_name.make_from_string (a_folder) end - sessions_folder_name: STRING_8 + sessions_folder_name: PATH feature -- Access @@ -34,7 +34,7 @@ feature -- Access local f: RAW_FILE do - create f.make (file_name (a_session_uuid)) + create f.make_with_path (file_name (a_session_uuid)) Result := f.exists and then f.is_readable end @@ -42,7 +42,7 @@ feature -- Access local f: RAW_FILE do - create f.make (file_name (a_session_uuid)) + create f.make_with_path (file_name (a_session_uuid)) if f.exists and then f.is_readable then f.open_read if attached data_from_file (f) as d then @@ -68,7 +68,7 @@ feature -- Persistence delete_session (a_session) else ensure_session_folder_exists - create f.make (file_name (a_session.uuid)) + create f.make_with_path (file_name (a_session.uuid)) if not f.exists or else f.is_writable then f.create_read_write a_session.data.set_expiration (a_session.expiration) @@ -91,7 +91,7 @@ feature -- Persistence rescued: BOOLEAN do if not rescued then - create f.make (file_name (a_session.uuid)) + create f.make_with_path (file_name (a_session.uuid)) if f.exists then f.delete end @@ -131,7 +131,7 @@ feature {NONE} -- Implementation local d: DIRECTORY once - create d.make (sessions_folder_name) + create d.make_with_path (sessions_folder_name) if not d.exists then d.recursive_create_dir end @@ -143,18 +143,13 @@ feature {NONE} -- Implementation local d: DIRECTORY do - create d.make (sessions_folder_name) + create d.make_with_path (sessions_folder_name) Result := d.exists and then d.is_writable end - file_name (a_uuid: like {WSF_SESSION}.uuid): READABLE_STRING_8 - local - fn: FILE_NAME + file_name (a_uuid: like {WSF_SESSION}.uuid): PATH do - create fn.make_from_string (sessions_folder_name) - fn.set_file_name (a_uuid.out) - fn.add_extension ("session") - Result := fn.string + Result := sessions_folder_name.extended (a_uuid.out).appended_with_extension ("session") end note diff --git a/library/server/wsf/src/implementation/shared_wsf_percent_encoder.e b/library/server/wsf/src/implementation/shared_wsf_percent_encoder.e new file mode 100644 index 00000000..9d00bd44 --- /dev/null +++ b/library/server/wsf/src/implementation/shared_wsf_percent_encoder.e @@ -0,0 +1,28 @@ +note + description: "Objects to access the shared once WSF_PERCENT_ENCODER ..." + date: "$Date$" + revision: "$Revision$" + +class + SHARED_WSF_PERCENT_ENCODER + +feature -- Encoder + + percent_encoder: WSF_PERCENT_ENCODER + -- Shared Percent encoding engine. + once + create Result + end + +note + copyright: "2011-2013, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + +end diff --git a/library/server/wsf/src/implementation/wsf_percent_encoder.e b/library/server/wsf/src/implementation/wsf_percent_encoder.e new file mode 100644 index 00000000..8d60b89a --- /dev/null +++ b/library/server/wsf/src/implementation/wsf_percent_encoder.e @@ -0,0 +1,523 @@ +note + description: "[ + Component to handle percent encoding + ]" + date: "$Date: 2013-05-21 01:15:17 +0200 (mar., 21 mai 2013) $" + revision: "$Revision: 92557 $" + EIS: "name=Percent-encoding", "protocol=URI", "src=http://en.wikipedia.org/wiki/Percent-encoding" + +class + WSF_PERCENT_ENCODER + +feature -- Percent encoding + + percent_encoded_string (v: READABLE_STRING_GENERAL): STRING_8 + -- Return `a_string' percent-encoded + do + create Result.make (v.count) + append_percent_encoded_string_to (v, Result) + end + + append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) + -- Append `a_string' as percent-encoded value to `a_result' + local + c: NATURAL_32 + i,n: INTEGER + do + from + i := 1 + n := s.count + until + i > n + loop + c := s.code (i) + if + --| unreserved ALPHA / DIGIT + (48 <= c and c <= 57) -- DIGIT: 0 .. 9 + or (65 <= c and c <= 90) -- ALPHA: A .. Z + or (97 <= c and c <= 122) -- ALPHA: a .. z + then + a_result.append_code (c) + else + inspect c + when + 45, 46, 95, 126 -- unreserved characters: -._~ + then + a_result.append_code (c) + when + 58, 64, -- reserved =+ gen-delims: :@ + 33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()* + 43, 44, 59, 61, -- reserved = sub-delims: +,;= + 37 -- percent encoding: % + then + append_percent_encoded_character_code_to (c, a_result) + else + append_percent_encoded_character_code_to (c, a_result) + end + end + i := i + 1 + end + end + +feature -- Percent encoding: character + + append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append character code `a_code' as percent-encoded content into `a_result' + do + if a_code > 0xFF then + -- Unicode + append_percent_encoded_unicode_character_code_to (a_code, a_result) + elseif a_code > 0x7F then + -- Extended ASCII + -- This requires percent-encoding on UTF-8 converted character. + append_percent_encoded_unicode_character_code_to (a_code, a_result) + else + -- ASCII + append_percent_encoded_ascii_character_code_to (a_code, a_result) + end + ensure + appended: a_result.count > old a_result.count + end + +feature {NONE} -- Implementation: character encoding + + append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append extended ascii character code `a_code' as percent-encoded content into `a_result' + -- Note: it does not UTF-8 convert this extended ASCII. + require + is_extended_ascii: a_code <= 0xFF + local + c: INTEGER + do + if a_code > 0xFF then + -- Unicode + append_percent_encoded_unicode_character_code_to (a_code, a_result) + else + -- Extended ASCII + c := a_code.to_integer_32 + a_result.append_code (37) -- 37 '%%' + a_result.append_code (hex_digit [c |>> 4]) + a_result.append_code (hex_digit [c & 0xF]) + end + ensure + appended: a_result.count > old a_result.count + end + + append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result' + -- Note: it does include UTF-8 conversion of extended ASCII and Unicode. + do + if a_code <= 0x7F then + -- 0xxxxxxx + append_percent_encoded_ascii_character_code_to (a_code, a_result) + elseif a_code <= 0x7FF then + -- 110xxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + elseif a_code <= 0xFFFF then + -- 1110xxxx 10xxxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + else + -- c <= 1FFFFF - there are no higher code points + -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + end + ensure + appended: a_result.count > old a_result.count + end + +feature -- Percent decoding + + percent_decoded_string (v: READABLE_STRING_GENERAL): STRING_32 + -- Return the percent decoded string equivalent to the percent-encoded string `v' + --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 + do + create Result.make (v.count) + append_percent_decoded_string_to (v, Result) + end + + append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) + -- Append to `a_result' a string equivalent to the percent-encoded string `v' + --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 + local + i,n: INTEGER + c: NATURAL_32 + pr: CELL [INTEGER] + a_result_is_string_32: BOOLEAN + do + a_result_is_string_32 := attached {STRING_32} a_result + from + i := 1 + create pr.put (i) + n := v.count + until + i > n + loop + c := v.code (i) + inspect c + when 43 then -- 43 '+' + -- Some implementation are replacing spaces with "+" instead of "%20" + a_result.append_code (32) -- 32 ' ' + when 37 then -- 37 '%%' + -- An escaped character ? + if i = n then -- Error? + a_result.append_code (c) + else + if a_result_is_string_32 then + -- Convert UTF-8 to UTF-32 + pr.replace (i) + c := next_percent_decoded_unicode_character_code (v, pr) + a_result.append_code (c) + i := pr.item + else + -- Keep UTF-8 + pr.replace (i) + c := next_percent_decoded_character_code (v, pr) + a_result.append_code (c) + i := pr.item + end + end + else + if c <= 0x7F then + a_result.append_code (c) + else + if a_result_is_string_32 then + a_result.append_code (c) + else + append_percent_encoded_character_code_to (c, a_result) + end + end + end + i := i + 1 + end + end + +feature {NONE} -- Implementation: decoding + + next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 + -- Character decoded from string `v' starting from index `a_position.item' + -- note: it also updates `a_position.item' to indicate the new index position. + require + valid_start: a_position.item <= v.count + is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' + local + c: NATURAL_32 + i, n: INTEGER + not_a_digit: BOOLEAN + ascii_pos: NATURAL_32 + ival: NATURAL_32 + pos: INTEGER + c_is_digit: BOOLEAN + do + --| pos is index in stream of escape character ('%') + pos := a_position.item + c := v.code (pos + 1) + if c = 85 or c = 117 then -- 117 'u' 85 'U' + -- NOTE: this is not a standard, but it can occur, so use this for decoding only + -- An escaped Unicode (ucs2) value, from ECMA scripts + -- has the form: %u where is the UCS value + -- of the character (two byte integer, one to 4 chars + -- after escape sequence). + -- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations + -- UTF-8 result can be 1 to 4 characters. + from + i := pos + 2 + n := v.count + until + (i > n) or not_a_digit + loop + c := v.code (i) + c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9 + if + c_is_digit + or (97 <= c and c <= 102) -- ALPHA: a..f + or (65 <= c and c <= 70) -- ALPHA: A..F + then + ival := ival * 16 + if c_is_digit then + ival := ival + (c - 48) -- 48 '0' + else + if c > 70 then -- a..f + ival := ival + (c - 97) + 10 -- 97 'a' + else -- A..F + ival := ival + (c - 65) + 10 -- 65 'A' + end + end + i := i + 1 + else + not_a_digit := True + i := i - 1 + end + end + a_position.replace (i) + Result := ival + else + -- ASCII char? + ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2)) + Result := ascii_pos + a_position.replace (pos + 2) + end + end + + next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 + -- Next decoded character from `v' at position `a_position.item' + -- note: it also updates `a_position' to indicate the new index position. + require + valid_start: a_position.item <= v.count + is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' + local + n, j: INTEGER + c: NATURAL_32 + c1, c2, c3, c4: NATURAL_32 + pr: CELL [INTEGER] + do + create pr.put (a_position.item) + c1 := next_percent_decoded_character_code (v, pr) + + j := pr.item + n := v.count + + Result := c1 + a_position.replace (j) + + if c1 <= 0x7F then + -- 0xxxxxxx + Result := c1 + elseif c1 <= 0xDF then + -- 110xxxxx 10xxxxxx + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + Result := ( + ((c1 & 0x1F) |<< 6) | + ( c2 & 0x3F ) + ) + a_position.replace (j) + else + -- Do not try to decode + end + end + elseif c1 <= 0xEF then + -- 1110xxxx 10xxxxxx 10xxxxxx + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c3 := next_percent_decoded_character_code (v, pr) + j := pr.item + + Result := ( + ((c1 & 0xF) |<< 12) | + ((c2 & 0x3F) |<< 6) | + ( c3 & 0x3F ) + ) + a_position.replace (j) + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + elseif c1 <= 0xF7 then + -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c3 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c4 := next_percent_decoded_character_code (v, pr) + j := pr.item + + a_position.replace (j) + + Result := ( + ((c1 & 0x7) |<< 18 ) | + ((c2 & 0x3F) |<< 12) | + ((c3 & 0x3F) |<< 6) | + ( c4 & 0x3F ) + ) + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + else + Result := c1 + end + end + +feature -- RFC and characters + + is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN + -- Is hexadecimal character ? + do + Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA + or ('0' <= c and c <= '9') -- DIGIT + end + + is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN + -- Is ALPHA or DIGIT character ? + do + Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA + or ('0' <= c and c <= '9') -- DIGIT + end + + is_alpha_character (c: CHARACTER_32): BOOLEAN + -- Is ALPHA character ? + do + Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') + end + + is_digit_character (c: CHARACTER_32): BOOLEAN + -- Is DIGIT character ? + do + Result := ('0' <= c and c <= '9') + end + + is_unreserved_character (c: CHARACTER_32): BOOLEAN + -- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + do + if + ('a' <= c and c <= 'z') -- ALPHA + or ('A' <= c and c <= 'Z') -- ALPHA + or ('0' <= c and c <= '9') -- DIGIT + then + Result := True + else + inspect c + when '-', '_', '.', '~' then -- unreserved + Result := True + else + end + end + end + + is_reserved_character (c: CHARACTER_32): BOOLEAN + -- reserved = gen-delims / sub-delims + do + Result := is_gen_delims_character (c) or is_sub_delims_character (c) + end + + is_gen_delims_character (c: CHARACTER_32): BOOLEAN + -- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + do + inspect c + when ':' , '/', '?' , '#' , '[' , ']' , '@' then + Result := True + else + end + end + + is_sub_delims_character (c: CHARACTER_32): BOOLEAN + -- sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + -- / "*" / "+" / "," / ";" / "=" + do + inspect c + when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims + Result := True + else + end + end + +feature {NONE} -- Implementation + + hex_digit: SPECIAL [NATURAL_32] + -- Hexadecimal digits. + once + create Result.make_filled (0, 16) + Result [0] := {NATURAL_32} 48 -- 48 '0' + Result [1] := {NATURAL_32} 49 -- 49 '1' + Result [2] := {NATURAL_32} 50 -- 50 '2' + Result [3] := {NATURAL_32} 51 -- 51 '3' + Result [4] := {NATURAL_32} 52 -- 52 '4' + Result [5] := {NATURAL_32} 53 -- 53 '5' + Result [6] := {NATURAL_32} 54 -- 54 '6' + Result [7] := {NATURAL_32} 55 -- 55 '7' + Result [8] := {NATURAL_32} 56 -- 56 '8' + Result [9] := {NATURAL_32} 57 -- 57 '9' + Result [10] := {NATURAL_32} 65 -- 65 'A' + Result [11] := {NATURAL_32} 66 -- 66 'B' + Result [12] := {NATURAL_32} 67 -- 67 'C' + Result [13] := {NATURAL_32} 68 -- 68 'D' + Result [14] := {NATURAL_32} 69 -- 69 'E' + Result [15] := {NATURAL_32} 70 -- 70 'F' + end + + is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN + -- Is `a_string' a valid hexadecimal sequence? + local + l_convertor: like ctoi_convertor + do + l_convertor := ctoi_convertor + l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32) + Result := l_convertor.is_integral_integer + end + + hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32 + -- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value. + require + is_hexa: is_hexa_decimal (a_hex_string) + local + l_convertor: like ctoi_convertor + do + l_convertor := ctoi_convertor + l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation) + Result := l_convertor.parsed_natural_32 + end + + ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER + -- Converter used to convert string to integer or natural. + once + create Result.make + Result.set_leading_separators_acceptable (False) + Result.set_trailing_separators_acceptable (False) + ensure + ctoi_convertor_not_void: Result /= Void + end + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/src/request/value/wsf_multiple_string.e b/library/server/wsf/src/request/value/wsf_multiple_string.e index c4d6ab08..69cf8ebe 100644 --- a/library/server/wsf/src/request/value/wsf_multiple_string.e +++ b/library/server/wsf/src/request/value/wsf_multiple_string.e @@ -62,7 +62,7 @@ feature -- Access url_encoded_name: READABLE_STRING_8 -- URL encoded string of `name'. do - Result := url_encoder.encoded_string (name) + Result := url_encoded_string (name) end values: LIST [WSF_STRING] diff --git a/library/server/wsf/src/request/value/wsf_string.e b/library/server/wsf/src/request/value/wsf_string.e index c51c3583..0da28891 100644 --- a/library/server/wsf/src/request/value/wsf_string.e +++ b/library/server/wsf/src/request/value/wsf_string.e @@ -50,20 +50,6 @@ feature -- Access url_encoded_value: READABLE_STRING_8 -- URL encoded string of `value'. - frozen string: like value - obsolete - "Use value [2012-May-31]" - do - Result := value - end - - frozen url_encoded_string: like url_encoded_value - obsolete - "Use url_encoded_value [2012-May-31]" - do - Result := url_encoded_value - end - feature -- Conversion integer_value: INTEGER @@ -137,7 +123,7 @@ feature -- Visitor end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/src/request/value/wsf_uploaded_file.e b/library/server/wsf/src/request/value/wsf_uploaded_file.e index 98912d5a..b194f9b0 100644 --- a/library/server/wsf/src/request/value/wsf_uploaded_file.e +++ b/library/server/wsf/src/request/value/wsf_uploaded_file.e @@ -36,17 +36,17 @@ feature -- Access feature -- Status report - debug_output: STRING + debug_output: STRING_32 -- String that should be displayed in debugger to represent `Current'. do Result := Precursor if exists and then - attached tmp_name as n + attached tmp_path as p then Result.append_character (' ') Result.append_character ('%"') - Result.append (n) + Result.append (p.name) Result.append_character ('%"') end Result.append (" filename=%"") @@ -108,9 +108,16 @@ feature -- Access: Uploaded File size: INTEGER -- Size of uploaded file - tmp_name: detachable STRING + tmp_path: detachable PATH -- Filename of tmp file + tmp_name: detachable READABLE_STRING_GENERAL + do + if attached tmp_path as p then + Result := p.name + end + end + tmp_basename: detachable STRING -- Basename of tmp file @@ -237,7 +244,7 @@ feature -- Implementation feature -- Basic operation - move_to (a_destination: STRING): BOOLEAN + move_to (a_destination: READABLE_STRING_GENERAL): BOOLEAN -- Move current uploaded file to `a_destination' --| Violates CQS principle. require @@ -246,10 +253,10 @@ feature -- Basic operation local f: RAW_FILE do - if attached tmp_name as n then - create f.make (n) + if attached tmp_path as p then + create f.make_with_path (p) if f.exists then - f.change_name (a_destination) + f.rename_file (a_destination) Result := True end end @@ -274,8 +281,8 @@ feature -- Status local f: PLAIN_TEXT_FILE do - if attached tmp_name as n then - create f.make (n) + if attached tmp_path as p then + create f.make_with_path (p) Result := f.exists end end @@ -288,10 +295,19 @@ feature -- Element change error := e end + set_tmp_path (p: like tmp_path) + do + tmp_path := p + end + set_tmp_name (n: like tmp_name) -- Set `tmp_name' to `n' do - tmp_name := n + if n /= Void then + set_tmp_path (create {PATH}.make_from_string (n)) + else + set_tmp_path (Void) + end end set_tmp_basename (n: like tmp_basename) diff --git a/library/server/wsf/src/request/wsf_value.e b/library/server/wsf/src/request/wsf_value.e index 5c42e804..6a1577e9 100644 --- a/library/server/wsf/src/request/wsf_value.e +++ b/library/server/wsf/src/request/wsf_value.e @@ -9,6 +9,13 @@ deferred class inherit DEBUG_OUTPUT + SHARED_WSF_PERCENT_ENCODER + rename + percent_encoder as url_encoder + export + {NONE} all + end + feature -- Access name: READABLE_STRING_32 @@ -91,23 +98,26 @@ feature -- Helper feature -- Status report - debug_output: STRING + debug_output: STRING_32 -- String that should be displayed in debugger to represent `Current'. do - create Result.make_from_string (url_encoder.encoded_string (name) + "=" + url_encoder.encoded_string (string_representation)) + create Result.make_from_string (name + {STRING_32} "=" + string_representation) end feature {NONE} -- Implementation - url_decoded_string (s: READABLE_STRING_8): READABLE_STRING_32 + url_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 -- Decoded url-encoded string `s' do - Result := url_encoder.decoded_string (s) + create Result.make (s.count) + url_encoder.append_percent_encoded_string_to (s, Result) end - url_encoder: URL_ENCODER - once - create {UTF8_URL_ENCODER} Result --| Chrome is UTF-8 encoding the non ascii in query + url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32 + -- Decoded url-encoded string `s' + do + create Result.make (s.count) + url_encoder.append_percent_decoded_string_to (s, Result) end feature -- Visitor @@ -117,7 +127,7 @@ feature -- Visitor end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/src/response/wsf_download_response.e b/library/server/wsf/src/response/wsf_download_response.e index cedc8a2a..2fbdc65d 100644 --- a/library/server/wsf/src/response/wsf_download_response.e +++ b/library/server/wsf/src/response/wsf_download_response.e @@ -10,6 +10,8 @@ class inherit WSF_RESPONSE_MESSAGE + SHARED_UTF8_URL_ENCODER + create make, make_with_content_type, @@ -17,26 +19,26 @@ create feature {NONE} -- Initialization - make (a_file_name: READABLE_STRING_8) + make (a_file_name: READABLE_STRING_GENERAL) do set_status_code ({HTTP_STATUS_CODE}.ok) - file_name := a_file_name - base_name := basename (a_file_name) + create file_path.make_from_string (a_file_name) + base_name := basename (file_path) get_content_type initialize end - make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8) + make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_GENERAL) -- Initialize `Current'. do set_status_code ({HTTP_STATUS_CODE}.ok) - file_name := a_filename - base_name := basename (a_filename) + create file_path.make_from_string (a_filename) + base_name := basename (file_path) content_type := a_content_type initialize end - make_html (a_filename: READABLE_STRING_8) + make_html (a_filename: READABLE_STRING_GENERAL) -- Initialize `Current'. do make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename) @@ -45,15 +47,14 @@ feature {NONE} -- Initialization initialize local h: like header - d: HTTP_DATE do create h.make header := h h.put_content_type (content_type) h.put_transfer_encoding_binary - h.put_content_length (filesize (file_name)) + h.put_content_length (filesize (file_path)) h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"") - if attached filedate (file_name) as dt then + if attached filedate (file_path) as dt then h.put_last_modified (dt) end end @@ -89,7 +90,14 @@ feature -- Access status_code: INTEGER assign set_status_code + file_path: PATH + file_name: READABLE_STRING_8 + obsolete + "Use `file_path.name' for unicode support [2013-may]" + do + Result := file_path.utf_8_name + end base_name: READABLE_STRING_8 @@ -125,73 +133,57 @@ feature {WSF_RESPONSE} -- Output res.set_status_code (status_code) res.put_header_text (header.string) if not answer_head_request_method then - send_file_content_to (file_name, res) + send_file_content_to (file_path, res) end end feature {NONE} -- Implementation: file system helper - filesize (fn: STRING): INTEGER + filesize (fn: PATH): INTEGER -- Size of the file `fn'. local f: RAW_FILE do - create f.make (fn) + create f.make_with_path (fn) if f.exists then Result := f.count end end - filedate (fn: STRING): detachable DATE_TIME + filedate (fn: PATH): detachable DATE_TIME -- Size of the file `fn'. local f: RAW_FILE d: HTTP_DATE do - create f.make (fn) + create f.make_with_path (fn) if f.exists then create d.make_from_timestamp (f.date) Result := d.date_time end end - file_extension (fn: STRING): STRING + file_extension (fn: PATH): STRING_32 -- Extension of file `fn'. - local - p: INTEGER do - p := fn.last_index_of ('.', fn.count) - if p > 0 then - Result := fn.substring (p + 1, fn.count) + if attached fn.extension as ext then + Result := ext else create Result.make_empty end end - basename (fn: STRING): STRING + basename (fn: PATH): STRING -- Basename of `fn'. local - p: INTEGER + s: READABLE_STRING_32 do - p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count) - if p > 0 then - Result := fn.substring (p + 1, fn.count) + if attached fn.entry as p then + s := p.name else - Result := fn - end - end - - dirname (fn: STRING): STRING - -- Dirname of `fn'. - local - p: INTEGER - do - p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count) - if p > 0 then - Result := fn.substring (1, p - 1) - else - create Result.make_empty + s := fn.name end + Result := url_encoder.encoded_string (s) end feature -- Content-type related @@ -203,7 +195,7 @@ feature -- Content-type related m: detachable READABLE_STRING_8 do create m_map.make_default - m := m_map.mime_type (file_extension (file_name).as_lower) + m := m_map.mime_type (file_extension (file_path).as_lower) if m = Void then m := {HTTP_MIME_TYPES}.application_force_download end @@ -212,15 +204,15 @@ feature -- Content-type related feature -- Implementation: output - send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE) + send_file_content_to (fn: PATH; res: WSF_RESPONSE) -- Send the content of file `fn' require string_not_empty: not fn.is_empty - is_readable: (create {RAW_FILE}.make (fn)).is_readable + is_readable: (create {RAW_FILE}.make_with_path (fn)).is_readable local f: RAW_FILE do - create f.make (fn) + create f.make_with_path (fn) check f.exists and then f.is_readable end f.open_read diff --git a/library/server/wsf/src/response/wsf_file_response.e b/library/server/wsf/src/response/wsf_file_response.e index 8d00455b..28b21ff2 100644 --- a/library/server/wsf/src/response/wsf_file_response.e +++ b/library/server/wsf/src/response/wsf_file_response.e @@ -11,30 +11,49 @@ inherit WSF_RESPONSE_MESSAGE create + make_with_path, + make_with_content_type_and_path, + make_html_with_path, make, make_with_content_type, make_html feature {NONE} -- Initialization - make (a_file_name: READABLE_STRING_8) + make_with_path (a_path: PATH) do set_status_code ({HTTP_STATUS_CODE}.ok) - file_name := a_file_name + file_path := a_path get_content_type initialize end - make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8) - -- Initialize `Current'. + make_with_content_type_and_path (a_content_type: READABLE_STRING_8; a_path: PATH) do set_status_code ({HTTP_STATUS_CODE}.ok) - file_name := a_filename + file_path := a_path content_type := a_content_type initialize end - make_html (a_filename: READABLE_STRING_8) + make_html_with_path (a_path: PATH) + -- Initialize `Current'. + do + make_with_content_type_and_path ({HTTP_MIME_TYPES}.text_html, a_path) + end + + make (a_file_name: READABLE_STRING_GENERAL) + do + make_with_path (create {PATH}.make_from_string (a_file_name)) + end + + make_with_content_type (a_content_type: READABLE_STRING_8; a_file_name: READABLE_STRING_GENERAL) + -- Initialize `Current'. + do + make_with_content_type_and_path (a_content_type, create {PATH}.make_from_string (a_file_name)) + end + + make_html (a_filename: READABLE_STRING_GENERAL) -- Initialize `Current'. do make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename) @@ -118,13 +137,21 @@ feature -- Access content_type: READABLE_STRING_8 -- Content-Type of the response + file_path: path + -- File path + file_name: READABLE_STRING_8 + obsolete + "Use `file_path.name' for unicode support [2013-may]" + do + Result := file_path.utf_8_name + end file_exists: BOOLEAN -- File exists? file_size: INTEGER - -- Size of file named `file_name' + -- Size of file `file_path' head, bottom: detachable READABLE_STRING_8 -- Eventual head and bottom part @@ -184,7 +211,7 @@ feature {WSF_RESPONSE} -- Output res.put_string (s) end if not answer_head_request_method then - send_file_content_to (file_name, res) + send_file_content_to (file_path, res) end s := bottom if s /= Void then @@ -200,40 +227,37 @@ feature {NONE} -- Implementation: file system helper local f: RAW_FILE do - create f.make (file_name) + create f.make_with_path (file_path) file_exists := f.exists end get_file_size - -- Get `file_size' from file named `file_name' + -- Get `file_size' from file named `file_path' require file_exists: file_exists local f: RAW_FILE do - create f.make (file_name) + create f.make_with_path (file_path) file_size := f.count end file_last_modified: detachable DATE_TIME - -- Get `file_size' from file named `file_name' + -- Get `file_size' from file named `file_path' require file_exists: file_exists local f: RAW_FILE do - create f.make (file_name) + create f.make_with_path (file_path) create Result.make_from_epoch (f.change_date) end - file_extension (fn: STRING): STRING + file_extension (fn: PATH): STRING_32 -- Extension of file `fn'. - local - p: INTEGER do - p := fn.last_index_of ('.', fn.count) - if p > 0 then - Result := fn.substring (p + 1, fn.count) + if attached fn.extension as ext then + Result := ext else create Result.make_empty end @@ -242,13 +266,13 @@ feature {NONE} -- Implementation: file system helper feature -- Content-type related get_content_type - -- Content type associated with `file_name' + -- Content type associated with `file_path' local m_map: HTTP_FILE_EXTENSION_MIME_MAPPING m: detachable READABLE_STRING_8 do create m_map.make_default - m := m_map.mime_type (file_extension (file_name).as_lower) + m := m_map.mime_type (file_extension (file_path).as_lower) if m = Void then m := {HTTP_MIME_TYPES}.application_force_download end @@ -257,16 +281,16 @@ feature -- Content-type related feature {NONE} -- Implementation: output - send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE) + send_file_content_to (fn: PATH; res: WSF_RESPONSE) -- Send the content of file `fn' require string_not_empty: not fn.is_empty - is_readable: (create {RAW_FILE}.make (fn)).is_readable + is_readable: (create {RAW_FILE}.make_with_path (fn)).is_readable file_exists: file_exists local f: RAW_FILE do - create f.make (fn) + create f.make_with_path (fn) check f.is_readable end f.open_read diff --git a/library/server/wsf/src/service/wsf_service_launcher_options.e b/library/server/wsf/src/service/wsf_service_launcher_options.e index bdcd88b5..fd690f88 100644 --- a/library/server/wsf/src/service/wsf_service_launcher_options.e +++ b/library/server/wsf/src/service/wsf_service_launcher_options.e @@ -15,7 +15,7 @@ class WSF_SERVICE_LAUNCHER_OPTIONS inherit - ANY + TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL] redefine default_create end @@ -23,7 +23,8 @@ inherit create default_create, make, - make_from_array + make_from_array, + make_from_iterable convert make_from_array ({ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]}) @@ -44,6 +45,19 @@ feature {NONE} -- Initialization make_from_array (a_options: ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]) do make + append_array_of_options (a_options) + end + + make_from_iterable (a_options: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]) + do + make + append_options (a_options) + end + +feature -- Merging + + append_array_of_options (a_options: ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]) + do across a_options as opt loop @@ -53,6 +67,15 @@ feature {NONE} -- Initialization end end + append_options (a_options: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]) + do + across + a_options as o + loop + set_option (o.key, o.item) + end + end + feature -- Access option (a_name: READABLE_STRING_GENERAL): detachable ANY @@ -60,6 +83,14 @@ feature -- Access Result := options.item (a_name) end +feature -- Access + + new_cursor: TABLE_ITERATION_CURSOR [detachable ANY, READABLE_STRING_GENERAL] + -- Fresh cursor associated with current structure + do + Result := options.new_cursor + end + feature -- Element change set_option (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY) @@ -75,13 +106,13 @@ feature -- Element change feature {NONE} -- Implementation - options: HASH_TABLE [detachable ANY, READABLE_STRING_GENERAL] + options: STRING_TABLE [detachable ANY] -- Custom options which might be support (or not) by the default service invariant options_attached: options /= Void note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/src/service/wsf_service_launcher_options_from_ini.e b/library/server/wsf/src/service/wsf_service_launcher_options_from_ini.e index edb60736..4b2658c5 100644 --- a/library/server/wsf/src/service/wsf_service_launcher_options_from_ini.e +++ b/library/server/wsf/src/service/wsf_service_launcher_options_from_ini.e @@ -11,28 +11,40 @@ inherit WSF_SERVICE_LAUNCHER_OPTIONS create - make_from_file + make_from_file, + make_from_file_and_defaults feature {NONE} -- Initialization - make_from_file (a_filename: READABLE_STRING_32) + make_from_file (a_filename: READABLE_STRING_GENERAL) -- Initialize `Current'. do make import (a_filename) end + make_from_file_and_defaults (a_filename: READABLE_STRING_GENERAL; dft: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + -- Initialize `Current'. + do + make + + if dft /= Void then + append_options (dft) + end + + import (a_filename) + end + feature {NONE} -- Implementation - import (a_filename: READABLE_STRING_32) + import (a_filename: READABLE_STRING_GENERAL) -- Import ini file content local f: PLAIN_TEXT_FILE l,v: STRING_8 p: INTEGER do - --FIXME: handle unicode filename here. - create f.make (a_filename) + create f.make_with_name (a_filename) if f.exists and f.is_readable then f.open_read from @@ -60,7 +72,7 @@ feature {NONE} -- Implementation end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index a59a839d..6b9eb159 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -26,6 +26,18 @@ class inherit DEBUG_OUTPUT + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + + SHARED_WSF_PERCENT_ENCODER + rename + percent_encoder as url_encoder + export + {NONE} all + end + create {WSF_TO_WGI_SERVICE} make_from_wgi @@ -40,26 +52,25 @@ feature {NONE} -- Initialization do wgi_request := r - create string_equality_tester if attached r.meta_variables as l_vars then - create tb.make_with_key_tester (l_vars.count, string_equality_tester) + create tb.make_equal (l_vars.count) across l_vars as c loop - tb.force (new_string_value (c.key, c.item), c.key) + if attached {READABLE_STRING_8} c.key as s8 then + tb.force (new_string_value (s8, c.item), c.key) + else + tb.force (new_string_value (url_encoded_string (c.key), c.item), c.key) + end end else - create tb.make_with_key_tester (0, string_equality_tester) + create tb.make_equal (0) end meta_variables_table := tb - meta_variables := tb create error_handler.make - create uploaded_files_table.make_with_key_tester (0, string_equality_tester) + create uploaded_files_table.make_equal (0) set_raw_input_data_recorded (False) - create {IMMUTABLE_STRING_32} empty_string.make_empty - - create execution_variables_table.make_with_key_tester (0, string_equality_tester) - execution_variables_table.compare_objects + create execution_variables_table.make_equal (0) initialize analyze @@ -96,12 +107,13 @@ feature {NONE} -- Initialization request_method := req.request_method --| PATH_INFO - path_info := raw_url_encoder.decoded_string (req.path_info) + percent_encoded_path_info := req.path_info + path_info := url_decoded_string (req.path_info) --| PATH_TRANSLATED s8 := req.path_translated if s8 /= Void then - path_translated := raw_url_encoder.decoded_string (s8) + path_translated := url_decoded_string (s8) end --| Here one can set its own environment entries if needed @@ -111,6 +123,7 @@ feature {NONE} -- Initialization end wgi_request: WGI_REQUEST + -- Associated WGI request feature -- Destroy @@ -125,6 +138,26 @@ feature -- Destroy loop delete_uploaded_file (c.item) end + + content_length_value := 0 + content_type := Void + execution_variables_table.wipe_out + internal_cookies_table := Void + internal_form_data_parameters_table := Void + internal_query_parameters_table := Void + internal_server_url := Void + internal_url_base := Void + form_parameters_table.wipe_out + mime_handlers := Void + path_info := empty_string_32 + path_parameters_source := Void + path_parameters_table := Void + path_translated := Void + raw_input_data := Void + raw_input_data_recorded := False + request_method := empty_string_8 + set_uploaded_file_path (Void) +-- wgi_request end feature -- Status report @@ -353,12 +386,12 @@ feature {WSF_REQUEST_EXPORTER} -- Override value feature {NONE} -- Access: global variable - items_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + items_table: STRING_TABLE [WSF_VALUE] -- Table containing all the various variables -- Warning: this is computed each time, if you change the content of other containers -- this won't update this Result's content, unless you query it again do - create Result.make_with_key_tester (20, string_equality_tester) + create Result.make_equal (20) if attached path_parameters as l_path_parameters then across @@ -558,7 +591,7 @@ feature -- Execution variables feature {NONE} -- Execution variables: implementation - execution_variables_table: HASH_TABLE_EX [detachable ANY, READABLE_STRING_GENERAL] + execution_variables_table: STRING_TABLE [detachable ANY] feature -- Access: CGI Meta variables @@ -582,6 +615,9 @@ feature -- Access: CGI Meta variables meta_variables: ITERABLE [WSF_STRING] -- CGI meta variables values + do + Result := meta_variables_table + end meta_string_variable_or_default (a_name: READABLE_STRING_GENERAL; a_default: READABLE_STRING_32; use_default_when_empty: BOOLEAN): READABLE_STRING_32 -- Value for meta parameter `a_name' @@ -617,7 +653,7 @@ feature -- Access: CGI Meta variables feature {NONE} -- Access: CGI meta parameters - meta_variables_table: HASH_TABLE_EX [WSF_STRING, READABLE_STRING_GENERAL] + meta_variables_table: STRING_TABLE [WSF_STRING] -- CGI Environment parameters feature -- Access: CGI meta parameters - 1.1 @@ -739,6 +775,11 @@ feature -- Access: CGI meta parameters - 1.1 Result := wgi_request.gateway_interface end + percent_encoded_path_info: READABLE_STRING_8 + -- Non decoded PATH_INFO value from CGI. + -- See `path_info' for the related percent decoded value. + --| This value should be used by component dealing only with ASCII path + path_info: READABLE_STRING_32 -- The PATH_INFO metavariable specifies a path to be interpreted -- by the CGI script. It identifies the resource or sub-resource @@ -767,6 +808,8 @@ feature -- Access: CGI meta parameters - 1.1 -- The PATH_INFO value is case-sensitive, and the server MUST -- preserve the case of the PATH_INFO element of the URI when -- making it available to scripts. + -- + -- See `percent_encoded_path_info' to get the original non decoded path info. path_translated: detachable READABLE_STRING_32 -- PATH_TRANSLATED is derived by taking any path-info component @@ -1150,7 +1193,7 @@ feature -- Cookies feature {NONE} -- Cookies - cookies_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + cookies_table: STRING_TABLE [WSF_VALUE] -- Expanded cookies variable local i,j,p,n: INTEGER @@ -1161,8 +1204,7 @@ feature {NONE} -- Cookies if l_cookies = Void then if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then s := val.value - create l_cookies.make_with_key_tester (5, string_equality_tester) - l_cookies.compare_objects + create l_cookies.make_equal (5) from n := s.count p := 1 @@ -1190,8 +1232,7 @@ feature {NONE} -- Cookies end end else - create l_cookies.make_with_key_tester (0, string_equality_tester) - l_cookies.compare_objects + create l_cookies.make_equal (0) end internal_cookies_table := l_cookies end @@ -1217,7 +1258,7 @@ feature -- Path parameters feature {NONE} -- Query parameters: implementation - path_parameters_table: detachable HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + path_parameters_table: detachable STRING_TABLE [WSF_VALUE] -- Parameters computed from `path_parameters_source' --| most often coming from the associated route from WSF_ROUTER @@ -1240,8 +1281,7 @@ feature {WSF_REQUEST_PATH_PARAMETERS_SOURCE} -- Path parameters: Element change if l_count = 0 then l_table := Void else - create l_table.make_with_key_tester (l_count, string_equality_tester) - l_table.compare_objects + create l_table.make_equal (l_count) if attached src.path_parameters as tb then across tb as c @@ -1278,7 +1318,7 @@ feature -- Query parameters feature {NONE} -- Query parameters: implementation - query_parameters_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + query_parameters_table: STRING_TABLE [WSF_VALUE] -- Parameters extracted from QUERY_STRING local vars: like internal_query_parameters_table @@ -1303,13 +1343,12 @@ feature {NONE} -- Query parameters: implementation end end vars := urlencoded_parameters (s) - vars.compare_objects internal_query_parameters_table := vars end Result := vars end - urlencoded_parameters (a_content: detachable READABLE_STRING_8): HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + urlencoded_parameters (a_content: detachable READABLE_STRING_8): STRING_TABLE [WSF_VALUE] -- Import `a_content' local n, p, i, j: INTEGER @@ -1317,13 +1356,13 @@ feature {NONE} -- Query parameters: implementation l_name, l_value: READABLE_STRING_8 do if a_content = Void then - create Result.make_with_key_tester (0, string_equality_tester) + create Result.make_equal (0) else n := a_content.count if n = 0 then - create Result.make_with_key_tester (0, string_equality_tester) + create Result.make_equal (0) else - create Result.make_with_key_tester (3, string_equality_tester) --| 3 = arbitrary value + create Result.make_equal (3) --| 3 = arbitrary value from p := 1 until @@ -1348,6 +1387,8 @@ feature {NONE} -- Query parameters: implementation end end end + ensure + result_with_object_comparison: Result.object_comparison end feature -- Form fields and related @@ -1452,7 +1493,7 @@ feature {NONE} -- Implementation: MIME handler feature {NONE} -- Form fields and related - uploaded_files_table: HASH_TABLE_EX [WSF_UPLOADED_FILE, READABLE_STRING_GENERAL] + uploaded_files_table: STRING_TABLE [WSF_UPLOADED_FILE] get_form_parameters -- Variables sent by POST, ... request @@ -1464,14 +1505,12 @@ feature {NONE} -- Form fields and related vars := internal_form_data_parameters_table if vars = Void then if not is_chunked_input and content_length_value = 0 then - create vars.make_with_key_tester (0, string_equality_tester) - vars.compare_objects + create vars.make_equal (0) else if raw_input_data_recorded then create l_raw_data_cell.put (Void) end - create vars.make_with_key_tester (5, string_equality_tester) - vars.compare_objects + create vars.make_equal (5) l_type := content_type if l_type /= Void and then attached mime_handler (l_type) as hdl then @@ -1488,7 +1527,7 @@ feature {NONE} -- Form fields and related internal_form_data_parameters_table /= Void end - form_parameters_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] + form_parameters_table: STRING_TABLE [WSF_VALUE] -- Variables sent by POST request local vars: like internal_form_data_parameters_table @@ -1497,14 +1536,14 @@ feature {NONE} -- Form fields and related vars := internal_form_data_parameters_table if vars = Void then check form_parameters_already_retrieved: False end - create vars.make_with_key_tester (0, string_equality_tester) + create vars.make_equal (0) end Result := vars end feature {NONE} -- Implementation: smart parameter identification - add_value_to_table (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8; a_table: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) + add_value_to_table (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8; a_table: STRING_TABLE [WSF_VALUE]) -- Add urlencoded parameter `a_name'=`a_value' to `a_table' -- following smart computation such as handling the "..[..]" as table local @@ -1548,7 +1587,7 @@ feature {NONE} -- Implementation: smart parameter identification if p > 0 then q := r.index_of ({CHARACTER_8} ']', p + 1) if q > p then - k32 := url_encoder.decoded_string (k) + k32 := url_decoded_string (k) if attached {WSF_TABLE} ptb.value (k32) as l_tb_value then tb := l_tb_value else @@ -1610,7 +1649,7 @@ feature -- Uploaded File Handling until l_files.after or Result loop - if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string_general (a_filename) then + if attached l_files.item_for_iteration.tmp_path as l_tmp_path and then a_filename.same_string (l_tmp_path.name) then Result := True end l_files.forth @@ -1686,7 +1725,7 @@ feature -- URL Utility elseif spos > 0 then i := spos end - spos := l_rq_uri.substring_index (path_info, i) + spos := l_rq_uri.substring_index (percent_encoded_path_info, i) if spos > 0 then l_base_url := l_rq_uri.substring (1, spos - 1) else @@ -1736,18 +1775,18 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling f: RAW_FILE do if uploaded_files_table.has_item (uf) then - if attached uf.tmp_name as fn then - create f.make (fn) + if attached uf.tmp_path as fn then + create f.make_with_path (fn) if f.exists and then f.is_writable then f.delete else - error_handler.add_custom_error (0, "Can not delete uploaded file", "Can not delete file %""+ fn +"%"") + error_handler.add_custom_error (0, "Can not delete uploaded file", {STRING_32} "Can not delete file %""+ fn.name + {STRING_32} "%"") end else - error_handler.add_custom_error (0, "Can not delete uploaded file", "Can not delete uploaded file %""+ uf.name +"%" Tmp File not found") + error_handler.add_custom_error (0, "Can not delete uploaded file", {STRING_32} "Can not delete uploaded file %""+ uf.name + {STRING_32} "%" Tmp File not found") end else - error_handler.add_custom_error (0, "Not an uploaded file", "This file %""+ uf.name +"%" is not an uploaded file.") + error_handler.add_custom_error (0, "Not an uploaded file", {STRING_32} "This file %""+ uf.name + {STRING_32} "%" is not an uploaded file.") end end @@ -1757,8 +1796,8 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling bn: STRING l_safe_name: STRING f: RAW_FILE - dn: STRING - fn: FILE_NAME + dn: PATH + fn: PATH d: DIRECTORY n: INTEGER rescued: BOOLEAN @@ -1768,30 +1807,28 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling dn := p else -- FIXME: should it be configured somewhere? - dn := (create {EXECUTION_ENVIRONMENT}).current_working_directory + dn := execution_environment.current_working_path end - create d.make (dn) + create d.make_with_path (dn) if d.exists and then d.is_writable then l_safe_name := a_up_file.safe_filename from - create fn.make_from_string (dn) - bn := "EWF_tmp-" + l_safe_name - fn.set_file_name (bn) - create f.make (fn.string) + bn := "tmp-" + l_safe_name + fn := dn.extended (bn) + create f.make_with_path (fn) n := 0 until not f.exists or else n > 1_000 loop n := n + 1 - fn.make_from_string (dn) - bn := "EWF_tmp-" + n.out + "-" + l_safe_name - fn.set_file_name (bn) - f.make (fn.string) + bn := "tmp-" + n.out + "-" + l_safe_name + fn := dn.extended (bn) + f.make_with_path (fn) end if not f.exists or else f.is_writable then - a_up_file.set_tmp_name (f.name) + a_up_file.set_tmp_path (f.path) a_up_file.set_tmp_basename (bn) f.open_write f.put_string (a_content) @@ -1800,7 +1837,7 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling a_up_file.set_error (-1) end else - error_handler.add_custom_error (0, "Directory not writable", "Can not create file in directory %""+ dn +"%"") + error_handler.add_custom_error (0, "Directory not writable", {STRING_32} "Can not create file in directory %""+ dn.name + {STRING_32} "%"") end uploaded_files_table.force (a_up_file, a_up_file.name) else @@ -1813,13 +1850,13 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling feature {WSF_REQUEST_EXPORTER} -- Settings - uploaded_file_path: detachable READABLE_STRING_8 + uploaded_file_path: detachable PATH -- Optional folder path used to store uploaded files set_uploaded_file_path (p: like uploaded_file_path) -- Set `uploaded_file_path' to `p'. require - path_exists: p /= Void implies (create {DIRECTORY}.make (p)).exists + path_exists: p /= Void implies (create {DIRECTORY}.make_with_path (p)).exists do uploaded_file_path := p end @@ -1874,8 +1911,6 @@ feature {NONE} -- Implementation feature {NONE} -- Implementation: utilities - string_equality_tester: STRING_EQUALITY_TESTER - single_slash_starting_string (s: READABLE_STRING_32): STRING_32 -- Return the string `s' (or twin) with one and only one starting slash local @@ -1927,17 +1962,27 @@ feature {NONE} -- Implementation: utilities create Result.make (a_name, a_value) end - empty_string: READABLE_STRING_32 + empty_string_32: IMMUTABLE_STRING_32 -- Reusable empty string - - raw_url_encoder: URL_ENCODER once - create {URL_ENCODER} Result + create Result.make_empty end - url_encoder: URL_ENCODER + empty_string_8: IMMUTABLE_STRING_8 once - create {UTF8_URL_ENCODER} Result + create Result.make_empty + end + + url_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 + do + create Result.make (s.count) + url_encoder.append_percent_encoded_string_to (s, Result) + end + + url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32 + do + create Result.make (s.count) + url_encoder.append_percent_decoded_string_to (s, Result) end date_time_utilities: HTTP_DATE_TIME_UTILITIES @@ -1947,7 +1992,8 @@ feature {NONE} -- Implementation: utilities end invariant - empty_string_unchanged: empty_string.is_empty + empty_string_32_unchanged: empty_string_32.is_empty + empty_string_8_unchanged: empty_string_8.is_empty wgi_request.content_type /= Void implies content_type /= Void note diff --git a/library/text/encoder/encoder-safe.ecf b/library/text/encoder/encoder-safe.ecf index 9bd23d7e..b7307359 100644 --- a/library/text/encoder/encoder-safe.ecf +++ b/library/text/encoder/encoder-safe.ecf @@ -11,22 +11,10 @@ - /tests$ - /spec$ - - - - - - - - - - diff --git a/library/text/encoder/encoder.ecf b/library/text/encoder/encoder.ecf index 71732816..781d7fb6 100644 --- a/library/text/encoder/encoder.ecf +++ b/library/text/encoder/encoder.ecf @@ -11,22 +11,10 @@ - /tests$ - /spec$ - - - - - - - - - - diff --git a/library/text/encoder/src/spec/70/utf8_encoder_helper.e b/library/text/encoder/src/shared_utf8_url_encoder.e similarity index 50% rename from library/text/encoder/src/spec/70/utf8_encoder_helper.e rename to library/text/encoder/src/shared_utf8_url_encoder.e index 8e82c656..be64e925 100644 --- a/library/text/encoder/src/spec/70/utf8_encoder_helper.e +++ b/library/text/encoder/src/shared_utf8_url_encoder.e @@ -1,26 +1,21 @@ note - description : "Objects that ..." - author : "$Author$" - date : "$Date$" - revision : "$Revision$" + description: "Objects to access the shared once UTF8_URL_ENCODER ..." + date: "$Date$" + revision: "$Revision$" -deferred class - UTF8_ENCODER_HELPER +class + SHARED_UTF8_URL_ENCODER -inherit - ANY +feature -- Encoder - UNICODE_CONVERSION - export - {NONE} all - {ANY} is_valid_utf8 - undefine - is_little_endian + url_encoder: UTF8_URL_ENCODER + -- Shared UTF8 URL encoder. + once + create Result end - note - copyright: "2011-2011, Eiffel Software and others" + copyright: "2011-2012, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software @@ -29,4 +24,5 @@ note Website http://www.eiffel.com Customer support http://support.eiffel.com ]" + end diff --git a/library/text/encoder/src/spec/before_70/utf8_encoder_helper.e b/library/text/encoder/src/spec/before_70/utf8_encoder_helper.e deleted file mode 100644 index e8e996e3..00000000 --- a/library/text/encoder/src/spec/before_70/utf8_encoder_helper.e +++ /dev/null @@ -1,73 +0,0 @@ -note - description : "Objects that ..." - author : "$Author$" - date : "$Date$" - revision : "$Revision$" - -deferred class - UTF8_ENCODER_HELPER - -inherit - ANY - - UNICODE_CONVERSION - export - {NONE} all - undefine - is_little_endian - end - -feature -- Status report - - is_valid_utf8 (a_string: STRING): BOOLEAN - -- Is `a_string' valid UTF-8 string? - require - a_string_not_void: a_string /= Void - local - l_nat8: NATURAL_8 - l_code: NATURAL_32 - i, nb: INTEGER - do - from - i := 1 - nb := a_string.count - Result := True - until - i > nb or not Result - loop - l_nat8 := a_string.code (i).to_natural_8 - if l_nat8 <= 127 then - -- Form 0xxxxxxx. - elseif (l_nat8 & 0xE0) = 0xC0 then - -- Form 110xxxxx 10xxxxxx. - l_code := (l_nat8 & 0x1F).to_natural_32 |<< 6 - i := i + 1 - elseif (l_nat8 & 0xF0) = 0xE0 then - -- Form 1110xxxx 10xxxxxx 10xxxxxx. - i := i + 2 - elseif (l_nat8 & 0xF8) = 0xF0 then - -- Form 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. - i := i + 3 - elseif (l_nat8 & 0xFC) = 0xF8 then - -- Starts with 111110xx - Result := False - else - -- Starts with 1111110x - Result := False - end - i := i + 1 - end - end - - -note - copyright: "2011-2011, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - source: "[ - Eiffel Software - 5949 Hollister Ave., Goleta, CA 93117 USA - Telephone 805-685-1006, Fax 805-685-6869 - Website http://www.eiffel.com - Customer support http://support.eiffel.com - ]" -end diff --git a/library/text/encoder/src/utf8_encoder.e b/library/text/encoder/src/utf8_encoder.e index 778ee000..50ad4acd 100644 --- a/library/text/encoder/src/utf8_encoder.e +++ b/library/text/encoder/src/utf8_encoder.e @@ -15,8 +15,6 @@ class inherit ENCODER [READABLE_STRING_32, READABLE_STRING_8] - UTF8_ENCODER_HELPER - PLATFORM export {NONE} all @@ -37,9 +35,13 @@ feature -- Encoder encoded_string (s: READABLE_STRING_32): STRING_8 -- UTF8-encoded value of `s'. + do + Result := general_encoded_string (s) + end + + general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 do Result := utf32_to_utf8 (s) - has_error := not last_conversion_successful end feature -- Decoder @@ -48,11 +50,34 @@ feature -- Decoder -- The UTF8-encoded equivalent of the given string do Result := utf8_to_utf32 (v) - has_error := not last_conversion_successful + has_error := not is_valid_utf8 (v) + end + +feature {NONE} -- UTF implementation + + utf32_to_utf8 (s: READABLE_STRING_GENERAL): STRING_8 + local + utf: UTF_CONVERTER + do + Result := utf.utf_32_string_to_utf_8_string_8 (s) + end + + utf8_to_utf32 (s: READABLE_STRING_8): STRING_32 + local + utf: UTF_CONVERTER + do + Result := utf.utf_8_string_8_to_string_32 (s) + end + + is_valid_utf8 (s: READABLE_STRING_8): BOOLEAN + local + utf: UTF_CONVERTER + do + Result := utf.is_valid_utf_8_string_8 (s) end note - copyright: "2011-2012, Eiffel Software and others" + copyright: "2011-2013, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/src/utf8_url_encoder.e b/library/text/encoder/src/utf8_url_encoder.e index 1931e95a..1edfef50 100644 --- a/library/text/encoder/src/utf8_url_encoder.e +++ b/library/text/encoder/src/utf8_url_encoder.e @@ -15,23 +15,24 @@ class inherit URL_ENCODER redefine - default_create, name, general_encoded_string, encoded_string, partial_encoded_string, decoded_string + select + encoded_string, + decoded_string, + has_error end - UTF8_ENCODER_HELPER + UTF8_ENCODER + rename + general_encoded_string as utf8_general_encoded_string, + encoded_string as utf8_encoded_string, + decoded_string as utf8_decoded_string, + has_error as utf8_has_error redefine - default_create - end - -feature {NONE} -- Initialization - - default_create - do - Precursor {UTF8_ENCODER_HELPER} + name end feature -- Access @@ -46,27 +47,22 @@ feature -- Encoder encoded_string (s: READABLE_STRING_32): STRING_8 -- URL-encoded value of `s'. do - Result := utf32_to_utf8 (s) - Result := Precursor (Result) + Result := general_encoded_string (s) end general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 do - if attached {READABLE_STRING_32} s as s32 then - Result := utf32_to_utf8 (s32) - else - Result := s.as_string_8 - end - Result := Precursor (Result) + Result := utf8_general_encoded_string (s) + Result := Precursor {URL_ENCODER} (Result) + has_error := has_error or utf8_has_error end partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8 -- URL-encoded value of `s'. do - Result := Precursor (s, a_ignore) - if not has_error then - Result := utf32_to_utf8 (Result) - end + Result := utf8_general_encoded_string (s) + Result := Precursor {URL_ENCODER} (Result, a_ignore) + has_error := has_error or utf8_has_error end feature -- Decoder @@ -74,17 +70,15 @@ feature -- Decoder decoded_string (v: READABLE_STRING_8): STRING_32 -- The URL-encoded equivalent of the given string do - Result := Precursor (v) + Result := Precursor {URL_ENCODER} (v) if not has_error then - if is_valid_utf8 (Result) then - Result := utf8_to_utf32 (Result) - has_error := not last_conversion_successful - end + Result := utf8_decoded_string (Result) + has_error := utf8_has_error end end note - copyright: "2011-2012, Eiffel Software and others" + copyright: "2011-2013, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/tests/tests-safe.ecf b/library/text/encoder/tests/tests-safe.ecf index be446169..77396f4e 100644 --- a/library/text/encoder/tests/tests-safe.ecf +++ b/library/text/encoder/tests/tests-safe.ecf @@ -1,7 +1,7 @@ - + /.git$ /EIFGENs$ diff --git a/library/utility/general/error/tests/tests-safe.ecf b/library/utility/general/error/tests/tests-safe.ecf index 46989ec4..26d20921 100644 --- a/library/utility/general/error/tests/tests-safe.ecf +++ b/library/utility/general/error/tests/tests-safe.ecf @@ -1,7 +1,7 @@ - + /.git$ /EIFGENs$ diff --git a/tests/all-safe.ecf b/tests/all-safe.ecf index b4c74b34..06f538ee 100644 --- a/tests/all-safe.ecf +++ b/tests/all-safe.ecf @@ -57,6 +57,7 @@ + Compiling as Windows , on other platforms than Windows diff --git a/tests/all-stable-safe.ecf b/tests/all-stable-safe.ecf index 534da848..3af09a8a 100644 --- a/tests/all-stable-safe.ecf +++ b/tests/all-stable-safe.ecf @@ -51,6 +51,7 @@ + Compiling as Windows , on other platforms than Windows diff --git a/tools/install_ewf.bat b/tools/install_ewf.bat index 005a5656..0e0882fd 100644 --- a/tools/install_ewf.bat +++ b/tools/install_ewf.bat @@ -87,6 +87,11 @@ echo Install library: openid echo Install library: uri_template %COPYCMD% %TMP_DIR%\library\text\parser\uri_template %TMP_CONTRIB_DIR%\library\text\parser\uri_template +echo Install library: notification_email +%SAFE_MD% %TMP_CONTRIB_DIR%\library\runtime +%SAFE_MD% %TMP_CONTRIB_DIR%\library\runtime\process +%COPYCMD% %TMP_DIR%\library\runtime\process\notification_email %TMP_CONTRIB_DIR%\library\runtime\process\notification_email + echo Install contrib library: nino %COPYCMD% %TMP_DIR%\contrib\library\network\server\nino %TMP_CONTRIB_DIR%\library\network\server\nino rem remove fonts folder from nino examples diff --git a/tools/install_ewf.sh b/tools/install_ewf.sh index 439f8acf..8d278a4f 100644 --- a/tools/install_ewf.sh +++ b/tools/install_ewf.sh @@ -79,7 +79,9 @@ COPYCMD $TMP_DIR/library/security/openid $TMP_CONTRIB_DIR/library/security/openi echo Install library: uri_template mkdir -p $TMP_CONTRIB_DIR/library/text/parser COPYCMD $TMP_DIR/library/text/parser/uri_template $TMP_CONTRIB_DIR/library/text/parser/uri_template - +echo Install library: notification_email +mkdir -p $TMP_CONTRIB_DIR/library/runtime/process +COPYCMD $TMP_DIR/library/runtime/process/notification_email $TMP_CONTRIB_DIR/library/runtime/process/notification_email echo Install contrib library: nino mkdir -p $TMP_CONTRIB_DIR/library/network/server COPYCMD $TMP_DIR/contrib/library/network/server/nino $TMP_CONTRIB_DIR/library/network/server/nino diff --git a/tools/uninstall_ewf.bat b/tools/uninstall_ewf.bat index 5bee1b31..78703184 100644 --- a/tools/uninstall_ewf.bat +++ b/tools/uninstall_ewf.bat @@ -60,6 +60,8 @@ echo Uninstall library: security\openid %RDCMD% %TMP_CONTRIB_DIR%\library\security\openid echo Uninstall library: uri_template %RDCMD% %TMP_CONTRIB_DIR%\library\text\parser\uri_template +echo Uninstall library: runtime\process\notification_email +%RDCMD% %TMP_CONTRIB_DIR%\library\runtime\process\notification_email echo Uninstall contrib library: nino %RDCMD% %TMP_CONTRIB_DIR%\library\network\server\nino