diff options
| author | Badlop <badlop@process-one.net> | 2022-03-28 15:47:17 +0200 | 
|---|---|---|
| committer | Badlop <badlop@process-one.net> | 2022-04-01 12:04:03 +0200 | 
| commit | f461bcb5973bd732b41b82a60ed07b9c8a7ef208 (patch) | |
| tree | a42f0b939aaadc3b69b5a9e1495582c3706bd40d /src | |
| parent | New mod_host_meta to serve host-meta files, see XEP-0156 (diff) | |
Improved mod_conversejs to support @HOST@, auto and conversejs_resources
Changes:
- The options can use the @HOST@ keyword
- default_domain option is @HOST@ by default, not the first defined vhost
- New conversejs_resources option to serve converse.js files (no need for web server)
- conversejs_* now support 'auto', which uses local or remote Converse files
Diffstat (limited to 'src')
| -rw-r--r-- | src/mod_conversejs.erl | 138 | ||||
| -rw-r--r-- | src/mod_conversejs_opt.erl | 11 | 
2 files changed, 128 insertions, 21 deletions
| diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 3b9efef6..48e0c255 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -50,15 +50,22 @@ reload(_Host, _NewOpts, _OldOpts) ->  depends(_Host, _Opts) ->      []. -process([], #request{method = 'GET'}) -> -    Host = ejabberd_config:get_myname(), -    Domain = gen_mod:get_module_opt(Host, ?MODULE, default_domain), -    Script = gen_mod:get_module_opt(Host, ?MODULE, conversejs_script), -    CSS = gen_mod:get_module_opt(Host, ?MODULE, conversejs_css), +process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> +    DomainRaw = gen_mod:get_module_opt(Host, ?MODULE, default_domain), +    Domain = misc:expand_keyword(<<"@HOST@">>, DomainRaw, Host), +    Script = get_file_url(Host, conversejs_script, +                          <<RawPath/binary, "/converse.min.js">>, +                          <<"https://cdn.conversejs.org/dist/converse.min.js">>), +    CSS = get_file_url(Host, conversejs_css, +                       <<RawPath/binary, "/converse.min.css">>, +                       <<"https://cdn.conversejs.org/dist/converse.min.css">>),      Init = [{<<"discover_connection_methods">>, false},              {<<"jid">>, Domain},              {<<"default_domain">>, Domain},              {<<"domain_placeholder">>, Domain}, +            {<<"registration_domain">>, Domain}, +            {<<"assets_path">>, RawPath}, +            {<<"i18n">>, ejabberd_option:language(Host)},              {<<"view_mode">>, <<"fullscreen">>}],      Init2 =          case gen_mod:get_module_opt(Host, ?MODULE, websocket_url) of @@ -85,13 +92,96 @@ process([], #request{method = 'GET'}) ->        <<"</script>">>,        <<"</body>">>,        <<"</html>">>]}; -process(_, _) -> -    ejabberd_web:error(not_found). +process(LocalPath, #request{host = Host}) -> +    case is_served_file(LocalPath) of +        true -> serve(Host, LocalPath); +        false -> ejabberd_web:error(not_found) +    end. + +%%---------------------------------------------------------------------- +%% File server +%%---------------------------------------------------------------------- + +is_served_file([<<"converse.min.js">>]) -> true; +is_served_file([<<"converse.min.css">>]) -> true; +is_served_file([<<"converse.min.js.map">>]) -> true; +is_served_file([<<"converse.min.css.map">>]) -> true; +is_served_file([<<"emojis.js">>]) -> true; +is_served_file([<<"locales">>, _]) -> true; +is_served_file([<<"locales">>, <<"dayjs">>, _]) -> true; +is_served_file([<<"webfonts">>, _]) -> true; +is_served_file(_) -> false. + +serve(Host, LocalPath) -> +    case get_conversejs_resources(Host) of +        undefined -> ejabberd_web:error(not_found); +        MainPath -> serve2(LocalPath, MainPath) +    end. + +get_conversejs_resources(Host) -> +    Opts = gen_mod:get_module_opts(Host, ?MODULE), +    mod_conversejs_opt:conversejs_resources(Opts). + +%% Copied from mod_muc_log_http.erl + +serve2(LocalPathBin, MainPathBin) -> +    LocalPath = [binary_to_list(LPB) || LPB <- LocalPathBin], +    MainPath = binary_to_list(MainPathBin), +    FileName = filename:join(filename:split(MainPath) ++ LocalPath), +    case file:read_file(FileName) of +        {ok, FileContents} -> +            ?DEBUG("Delivering content.", []), +            {200, +             [{<<"Content-Type">>, content_type(FileName)}], +             FileContents}; +        {error, eisdir} -> +            {403, [], "Forbidden"}; +        {error, Error} -> +            ?DEBUG("Delivering error: ~p", [Error]), +            case Error of +                eacces -> {403, [], "Forbidden"}; +                enoent -> {404, [], "Not found"}; +                _Else -> {404, [], atom_to_list(Error)} +            end +    end. + +content_type(Filename) -> +    case string:to_lower(filename:extension(Filename)) of +        ".css"  -> "text/css"; +        ".js"   -> "text/javascript"; +        ".map"  -> "application/json"; +        ".ttf"  -> "font/ttf"; +        ".woff"  -> "font/woff"; +        ".woff2"  -> "font/woff2" +    end. + +%%---------------------------------------------------------------------- +%% Options parsing +%%---------------------------------------------------------------------- + +get_file_url(Host, Option, Filename, Default) -> +    FileRaw = case gen_mod:get_module_opt(Host, ?MODULE, Option) of +                  auto -> get_auto_file_url(Host, Filename, Default); +                  F -> F +              end, +    misc:expand_keyword(<<"@HOST@">>, FileRaw, Host). + +get_auto_file_url(Host, Filename, Default) -> +    case get_conversejs_resources(Host) of +        undefined -> Default; +        _ -> Filename +    end. + +%%---------------------------------------------------------------------- +%% +%%----------------------------------------------------------------------  mod_opt_type(bosh_service_url) ->      econf:either(undefined, econf:binary());  mod_opt_type(websocket_url) ->      econf:either(undefined, econf:binary()); +mod_opt_type(conversejs_resources) -> +    econf:either(undefined, econf:directory());  mod_opt_type(conversejs_script) ->      econf:binary();  mod_opt_type(conversejs_css) -> @@ -102,9 +192,10 @@ mod_opt_type(default_domain) ->  mod_options(_) ->      [{bosh_service_url, undefined},       {websocket_url, undefined}, -     {default_domain, ejabberd_config:get_myname()}, -     {conversejs_script, <<"https://cdn.conversejs.org/dist/converse.min.js">>}, -     {conversejs_css, <<"https://cdn.conversejs.org/dist/converse.min.css">>}]. +     {default_domain, <<"@HOST@">>}, +     {conversejs_resources, undefined}, +     {conversejs_script, auto}, +     {conversejs_css, auto}].  mod_doc() ->      #{desc => @@ -115,9 +206,8 @@ mod_doc() ->                "section, you must also enable it in 'listen' -> 'ejabberd_http' -> "                "http://../listen-options/#request-handlers[request_handlers]."), "",             ?T("You must also setup either the option 'websocket_url' or 'bosh_service_url'."), "", -           ?T("By default, the options 'conversejs_css' and 'conversejs_script'" -              " point to the public Converse.js client. Alternatively, you can" -              " host the client locally using _`mod_http_fileserver`_.") +           ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " +              "by default they point to the public Converse client.")            ],       example =>           ["listen:", @@ -130,6 +220,7 @@ mod_doc() ->            "",            "modules:",            "  mod_conversejs:", +          "    conversejs_resources: \"/home/ejabberd/conversejs-9.0.0/package/dist\"",            "    websocket_url: \"ws://example.org:5280/websocket\""],        opts =>            [{websocket_url, @@ -144,14 +235,23 @@ mod_doc() ->              #{value => ?T("Domain"),                desc =>                    ?T("Specify a domain to act as the default for user JIDs. " -                     "The default value is the first domain defined in the " -                     "ejabberd configuration file.")}}, +                     "The keyword '@HOST@' is replaced with the hostname. " +                     "The default value is '@HOST@'.")}}, +           {conversejs_resources, +            #{value => ?T("Path"), +              desc => +                  ?T("Local path to the Converse files. " +                     "If not set, the public Converse client will be used instead.")}},             {conversejs_script, -            #{value => ?T("URL"), +            #{value => ?T("auto | URL"),                desc => -                  ?T("Converse.js main script URL.")}}, +                  ?T("Converse main script URL. " +                     "The keyword '@HOST@' is replaced with the hostname. " +                     "The default value is 'auto'.")}},             {conversejs_css, -            #{value => ?T("URL"), +            #{value => ?T("auto | URL"),                desc => -                  ?T("Converse.js CSS URL.")}}] +                  ?T("Converse CSS URL. " +                     "The keyword '@HOST@' is replaced with the hostname. " +                     "The default value is 'auto'.")}}]       }. diff --git a/src/mod_conversejs_opt.erl b/src/mod_conversejs_opt.erl index 9e53978e..f43b3e83 100644 --- a/src/mod_conversejs_opt.erl +++ b/src/mod_conversejs_opt.erl @@ -5,6 +5,7 @@  -export([bosh_service_url/1]).  -export([conversejs_css/1]). +-export([conversejs_resources/1]).  -export([conversejs_script/1]).  -export([default_domain/1]).  -export([websocket_url/1]). @@ -15,13 +16,19 @@ bosh_service_url(Opts) when is_map(Opts) ->  bosh_service_url(Host) ->      gen_mod:get_module_opt(Host, mod_conversejs, bosh_service_url). --spec conversejs_css(gen_mod:opts() | global | binary()) -> binary(). +-spec conversejs_css(gen_mod:opts() | global | binary()) -> 'auto' | binary().  conversejs_css(Opts) when is_map(Opts) ->      gen_mod:get_opt(conversejs_css, Opts);  conversejs_css(Host) ->      gen_mod:get_module_opt(Host, mod_conversejs, conversejs_css). --spec conversejs_script(gen_mod:opts() | global | binary()) -> binary(). +-spec conversejs_resources(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). +conversejs_resources(Opts) when is_map(Opts) -> +    gen_mod:get_opt(conversejs_resources, Opts); +conversejs_resources(Host) -> +    gen_mod:get_module_opt(Host, mod_conversejs, conversejs_resources). + +-spec conversejs_script(gen_mod:opts() | global | binary()) -> 'auto' | binary().  conversejs_script(Opts) when is_map(Opts) ->      gen_mod:get_opt(conversejs_script, Opts);  conversejs_script(Host) -> | 
