mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
New mod_host_meta to serve host-meta files, see XEP-0156
This commit is contained in:
parent
4c51f6e1fe
commit
8065ec831e
237
src/mod_host_meta.erl
Normal file
237
src/mod_host_meta.erl
Normal file
@ -0,0 +1,237 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_host_meta.erl
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : Serve host-meta files as described in XEP-0156
|
||||
%%% Created : 25 March 2022 by Badlop <badlop@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2022 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_host_meta).
|
||||
|
||||
-author('badlop@process-one.net').
|
||||
|
||||
-protocol({xep, 156, '1.4.0'}).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, reload/3, process/2,
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
-export([mod_doc/0]).
|
||||
-export([get_url/4]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% gen_mod callbacks
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
start(_Host, _Opts) ->
|
||||
ok.
|
||||
|
||||
stop(_Host) ->
|
||||
ok.
|
||||
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[{mod_bosh, soft}].
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% HTTP handlers
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
process([], #request{method = 'GET', tp = https, host = Host, path = Path}) ->
|
||||
case lists:last(Path) of
|
||||
<<"host-meta">> ->
|
||||
file_xml(Host);
|
||||
<<"host-meta.json">> ->
|
||||
file_json(Host)
|
||||
end;
|
||||
process(_Path, _Request) ->
|
||||
{404, [], "Not Found"}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% When set to 'auto', it only takes the first valid listener options it finds
|
||||
|
||||
file_xml(Host) ->
|
||||
BoshList = case get_url(?MODULE, bosh, true, Host) of
|
||||
undefined -> [];
|
||||
BoshUrl ->
|
||||
[?XA(<<"Link">>,
|
||||
[{<<"rel">>, <<"urn:xmpp:alt-connections:xbosh">>},
|
||||
{<<"href">>, BoshUrl}]
|
||||
)]
|
||||
end,
|
||||
WsList = case get_url(?MODULE, websocket, true, Host) of
|
||||
undefined -> [];
|
||||
WsUrl ->
|
||||
[?XA(<<"Link">>,
|
||||
[{<<"rel">>, <<"urn:xmpp:alt-connections:websocket">>},
|
||||
{<<"href">>, WsUrl}]
|
||||
)]
|
||||
end,
|
||||
{200, [html,
|
||||
{<<"Content-Type">>, <<"application/xrd+xml">>},
|
||||
{<<"Access-Control-Allow-Origin">>, <<"*">>}],
|
||||
[<<"<?xml version='1.0' encoding='utf-8'?>\n">>,
|
||||
fxml:element_to_binary(
|
||||
?XAE(<<"XRD">>,
|
||||
[{<<"xmlns">>,<<"http://docs.oasis-open.org/ns/xri/xrd-1.0">>}],
|
||||
BoshList ++ WsList)
|
||||
)]}.
|
||||
|
||||
file_json(Host) ->
|
||||
BoshList = case get_url(?MODULE, bosh, true, Host) of
|
||||
undefined -> [];
|
||||
BoshUrl -> [#{rel => <<"urn:xmpp:alt-connections:xbosh">>,
|
||||
href => BoshUrl}]
|
||||
end,
|
||||
WsList = case get_url(?MODULE, websocket, true, Host) of
|
||||
undefined -> [];
|
||||
WsUrl -> [#{rel => <<"urn:xmpp:alt-connections:websocket">>,
|
||||
href => WsUrl}]
|
||||
end,
|
||||
{200, [html,
|
||||
{<<"Content-Type">>, <<"application/json">>},
|
||||
{<<"Access-Control-Allow-Origin">>, <<"*">>}],
|
||||
[jiffy:encode(#{links => BoshList ++ WsList})]}.
|
||||
|
||||
get_url(M, bosh, Tls, Host) ->
|
||||
get_url(M, Tls, Host, bosh_service_url, mod_bosh);
|
||||
get_url(M, websocket, Tls, Host) ->
|
||||
get_url(M, Tls, Host, websocket_url, ejabberd_http_ws).
|
||||
|
||||
get_url(M, Tls, Host, Option, Module) ->
|
||||
case get_url_preliminar(M, Tls, Host, Option, Module) of
|
||||
undefined -> undefined;
|
||||
Url -> misc:expand_keyword(<<"@HOST@">>, Url, Host)
|
||||
end.
|
||||
|
||||
get_url_preliminar(M, Tls, Host, Option, Module) ->
|
||||
case gen_mod:get_module_opt(Host, M, Option) of
|
||||
undefined -> undefined;
|
||||
auto -> get_auto_url(Tls, Module);
|
||||
<<"auto">> -> get_auto_url(Tls, Module);
|
||||
U when is_binary(U) -> U
|
||||
end.
|
||||
|
||||
get_auto_url(Tls, Module) ->
|
||||
case find_handler_port_path(Tls, Module) of
|
||||
[] -> undefined;
|
||||
[{ThisTls, Port, Path} | _] ->
|
||||
Protocol = case {ThisTls, Module} of
|
||||
{false, mod_bosh} -> <<"http">>;
|
||||
{true, mod_bosh} -> <<"https">>;
|
||||
{false, ejabberd_http_ws} -> <<"ws">>;
|
||||
{true, ejabberd_http_ws} -> <<"wss">>
|
||||
end,
|
||||
<<Protocol/binary,
|
||||
"://@HOST@:",
|
||||
(integer_to_binary(Port))/binary,
|
||||
"/",
|
||||
(str:join(Path, <<"/">>))/binary>>
|
||||
end.
|
||||
|
||||
find_handler_port_path(Tls, Module) ->
|
||||
lists:filtermap(
|
||||
fun({{Port, _, _},
|
||||
ejabberd_http,
|
||||
#{tls := ThisTls, request_handlers := Handlers}})
|
||||
when (Tls == any) or (Tls == ThisTls) ->
|
||||
case lists:keyfind(Module, 2, Handlers) of
|
||||
false -> false;
|
||||
{Path, Module} -> {true, {ThisTls, Port, Path}}
|
||||
end;
|
||||
(_) -> false
|
||||
end, ets:tab2list(ejabberd_listener)).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Options and Doc
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
mod_opt_type(bosh_service_url) ->
|
||||
econf:either(undefined, econf:binary());
|
||||
mod_opt_type(websocket_url) ->
|
||||
econf:either(undefined, econf:binary()).
|
||||
|
||||
mod_options(_) ->
|
||||
[{bosh_service_url, <<"auto">>},
|
||||
{websocket_url, <<"auto">>}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("This module serves small 'host-meta' files as described in "
|
||||
"https://xmpp.org/extensions/xep-0156.html[XEP-0156: Discovering "
|
||||
"Alternative XMPP Connection Methods]."), "",
|
||||
?T("This module is available since ejabberd 22.xx."), "",
|
||||
?T("To use this module, in addition to adding it to the 'modules' "
|
||||
"section, you must also enable it in 'listen' -> 'ejabberd_http' -> "
|
||||
"http://../listen-options/#request-handlers[request_handlers]."), "",
|
||||
?T("Notice it only works if ejabberd_http has tls enabled.")],
|
||||
example =>
|
||||
["listen:",
|
||||
" -",
|
||||
" port: 443",
|
||||
" module: ejabberd_http",
|
||||
" tls: true",
|
||||
" request_handlers:",
|
||||
" /bosh: mod_bosh",
|
||||
" /ws: ejabberd_http_ws",
|
||||
" /.well-known/host-meta: mod_host_meta",
|
||||
" /.well-known/host-meta.json: mod_host_meta",
|
||||
"",
|
||||
"modules:",
|
||||
" mod_bosh: {}",
|
||||
" mod_host_meta:",
|
||||
" bosh_service_url: \"https://@HOST@:5443/bosh\"",
|
||||
" websocket_url: \"wss://@HOST@:5443/ws\""],
|
||||
|
||||
opts =>
|
||||
[{websocket_url,
|
||||
#{value => "undefined | auto | WebSocketURL",
|
||||
desc =>
|
||||
?T("WebSocket URL to announce. "
|
||||
"The keyword '@HOST@' is replaced with the real virtual "
|
||||
"host name. "
|
||||
"If set to 'auto', it will build the URL of the first "
|
||||
"configured WebSocket request handler. "
|
||||
"The default value is 'auto'.")}},
|
||||
{bosh_service_url,
|
||||
#{value => "undefined | auto | BoshURL",
|
||||
desc =>
|
||||
?T("BOSH service URL to announce. "
|
||||
"The keyword '@HOST@' is replaced with the real "
|
||||
"virtual host name. "
|
||||
"If set to 'auto', it will build the URL of the first "
|
||||
"configured BOSH request handler. "
|
||||
"The default value is 'auto'.")}}]
|
||||
}.
|
20
src/mod_host_meta_opt.erl
Normal file
20
src/mod_host_meta_opt.erl
Normal file
@ -0,0 +1,20 @@
|
||||
%% Generated automatically
|
||||
%% DO NOT EDIT: run `make options` instead
|
||||
|
||||
-module(mod_host_meta_opt).
|
||||
|
||||
-export([bosh_service_url/1]).
|
||||
-export([websocket_url/1]).
|
||||
|
||||
-spec bosh_service_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
|
||||
bosh_service_url(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(bosh_service_url, Opts);
|
||||
bosh_service_url(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_host_meta, bosh_service_url).
|
||||
|
||||
-spec websocket_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
|
||||
websocket_url(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(websocket_url, Opts);
|
||||
websocket_url(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_host_meta, websocket_url).
|
||||
|
Loading…
Reference in New Issue
Block a user