./ct_report/coverage/mod_extdisco.COVER.html

1 %%==============================================================================
2 %% Copyright 2020 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%==============================================================================
16 -module (mod_extdisco).
17 -author('jan.ciesla@erlang-solutions.com').
18
19 -xep([{xep, 215}, {version, "1.0.0"}]).
20 -behaviour(gen_mod).
21
22 %% gen_mod callbacks.
23 -export([start/2, stop/1, supported_features/0, config_spec/0]).
24
25 -export([process_iq/5]).
26
27 -ignore_xref([process_iq/5]).
28
29 -include("jlib.hrl").
30 -include("mongoose_config_spec.hrl").
31
32 -spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
33 start(HostType, #{iqdisc := IQDisc}) ->
34 4 gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_EXTDISCO, ejabberd_local,
35 fun ?MODULE:process_iq/5, #{}, IQDisc).
36
37 -spec stop(mongooseim:host_type()) -> ok.
38 stop(HostType) ->
39 4 gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_EXTDISCO, ejabberd_local).
40
41 supported_features() ->
42 4 [dynamic_domains].
43
44 -spec config_spec() -> mongoose_config_spec:config_section().
45 config_spec() ->
46 186 #section{items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(),
47 <<"service">> => #list{items = service_config_spec()}},
48 defaults = #{<<"iqdisc">> => no_queue,
49 <<"service">> => []}}.
50
51 service_config_spec() ->
52 186 #section{items = #{<<"type">> => #option{type = atom,
53 validate = non_empty},
54 <<"host">> => #option{type = binary,
55 validate = non_empty},
56 <<"port">> => #option{type = integer,
57 validate = port},
58 <<"transport">> => #option{type = binary,
59 validate = {enum, [<<"udp">>, <<"tcp">>]}},
60 <<"username">> => #option{type = binary,
61 validate = non_empty},
62 <<"password">> => #option{type = binary,
63 validate = non_empty}
64 },
65 required = [<<"type">>, <<"host">>]}.
66
67 -spec process_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), map()) ->
68 {mongoose_acc:t(), jlib:iq()}.
69 process_iq(Acc, _From, _To, #iq{type = get, sub_el = SubEl} = IQ, _Extra) ->
70 16 HostType = mongoose_acc:host_type(Acc),
71 16 {ResponseType, Response} = case request_type(SubEl) of
72 all_services ->
73 4 RequestedServices = get_external_services(HostType),
74 4 {result, create_iq_response(RequestedServices)};
75 {credentials, {Type, Host}} ->
76 2 Services = get_external_services(HostType, Type),
77 2 RequestedServices = lists:filter(fun(#{host := H}) -> H =:= Host end, Services),
78 2 {result, create_iq_response_credentials(RequestedServices)};
79 {selected_services, Type} ->
80 2 RequestedServices = get_external_services(HostType, Type),
81 2 {result, create_iq_response_services(RequestedServices, Type)};
82 _ ->
83 8 {error, [mongoose_xmpp_errors:bad_request()]}
84 end,
85 16 {Acc, IQ#iq{type = ResponseType, sub_el = Response}}.
86
87 request_type(#xmlel{name = <<"services">>} = Element) ->
88 8 case exml_query:attr(Element, <<"type">>) of
89 4 undefined -> all_services;
90 ServiceType ->
91 4 case catch binary_to_existing_atom(ServiceType, utf8) of
92 2 {'EXIT', _} -> {error, bad_request};
93 2 Type -> {selected_services, Type}
94 end
95 end;
96 request_type(#xmlel{name = <<"credentials">>, children = [Children]}) ->
97 6 Host = exml_query:attr(Children, <<"host">>),
98 6 case exml_query:attr(Children, <<"type">>) of
99 2 undefined -> {error, bad_request};
100 ServiceType ->
101 4 case catch binary_to_existing_atom(ServiceType, utf8) of
102 2 {'EXIT', _} -> {error, bad_request};
103 2 Type -> {credentials, {Type, Host}}
104 end
105 end;
106 request_type(_) ->
107 2 {error, bad_request}.
108
109 create_iq_response(Services) ->
110 4 #xmlel{name = <<"services">>,
111 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
112 children = prepare_services_element(Services)}.
113
114 create_iq_response_services(Services, Type) ->
115 2 #xmlel{name = <<"services">>,
116 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}, {<<"type">>, atom_to_binary(Type, utf8)}],
117 children = prepare_services_element(Services)}.
118
119 create_iq_response_credentials(Services) ->
120 2 #xmlel{name = <<"credentials">>,
121 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
122 children = prepare_services_element(Services)}.
123
124 get_external_services(HostType) ->
125 8 gen_mod:get_module_opt(HostType, ?MODULE, service).
126
127 get_external_services(HostType, Type) ->
128 4 [Service || Service = #{type := T} <- get_external_services(HostType), T =:= Type].
129
130 prepare_services_element(Services) ->
131 8 [#xmlel{name = <<"service">>, attrs = make_attrs(Service)} || Service <- Services].
132
133 make_attrs(Service) ->
134 11 [{atom_to_binary(Key), format_value(Key, Value)} || {Key, Value} <- maps:to_list(Service)].
135
136 10 format_value(port, Port) -> integer_to_binary(Port);
137 11 format_value(type, Type) -> atom_to_binary(Type);
138 41 format_value(_, Value) when is_binary(Value) -> Value.
Line Hits Source