./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, "0.7"}]).
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 152 #section{items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(),
47 <<"service">> => #list{items = service_config_spec()}},
48 defaults = #{<<"iqdisc">> => no_queue,
49 <<"service">> => []},
50 format_items = map}.
51
52 service_config_spec() ->
53 152 #section{items = #{<<"type">> => #option{type = atom,
54 validate = non_empty},
55 <<"host">> => #option{type = binary,
56 validate = non_empty},
57 <<"port">> => #option{type = integer,
58 validate = port},
59 <<"transport">> => #option{type = binary,
60 validate = {enum, [<<"udp">>, <<"tcp">>]}},
61 <<"username">> => #option{type = binary,
62 validate = non_empty},
63 <<"password">> => #option{type = binary,
64 validate = non_empty}
65 },
66 required = [<<"type">>, <<"host">>],
67 format_items = map}.
68
69 -spec process_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), map()) ->
70 {mongoose_acc:t(), jlib:iq()}.
71 process_iq(Acc, _From, _To, #iq{type = get, sub_el = SubEl} = IQ, _Extra) ->
72 16 HostType = mongoose_acc:host_type(Acc),
73 16 {ResponseType, Response} = case request_type(SubEl) of
74 all_services ->
75 4 RequestedServices = get_external_services(HostType),
76 4 {result, create_iq_response(RequestedServices)};
77 {credentials, {Type, Host}} ->
78 2 Services = get_external_services(HostType, Type),
79 2 RequestedServices = lists:filter(fun(#{host := H}) -> H =:= Host end, Services),
80 2 {result, create_iq_response_credentials(RequestedServices)};
81 {selected_services, Type} ->
82 2 RequestedServices = get_external_services(HostType, Type),
83 2 {result, create_iq_response_services(RequestedServices, Type)};
84 _ ->
85 8 {error, [mongoose_xmpp_errors:bad_request()]}
86 end,
87 16 {Acc, IQ#iq{type = ResponseType, sub_el = Response}}.
88
89 request_type(#xmlel{name = <<"services">>} = Element) ->
90 8 case exml_query:attr(Element, <<"type">>) of
91 4 undefined -> all_services;
92 ServiceType ->
93 4 case catch binary_to_existing_atom(ServiceType, utf8) of
94 2 {'EXIT', _} -> {error, bad_request};
95 2 Type -> {selected_services, Type}
96 end
97 end;
98 request_type(#xmlel{name = <<"credentials">>, children = [Children]}) ->
99 6 Host = exml_query:attr(Children, <<"host">>),
100 6 case exml_query:attr(Children, <<"type">>) of
101 2 undefined -> {error, bad_request};
102 ServiceType ->
103 4 case catch binary_to_existing_atom(ServiceType, utf8) of
104 2 {'EXIT', _} -> {error, bad_request};
105 2 Type -> {credentials, {Type, Host}}
106 end
107 end;
108 request_type(_) ->
109 2 {error, bad_request}.
110
111 create_iq_response(Services) ->
112 4 #xmlel{name = <<"services">>,
113 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
114 children = prepare_services_element(Services)}.
115
116 create_iq_response_services(Services, Type) ->
117 2 #xmlel{name = <<"services">>,
118 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}, {<<"type">>, atom_to_binary(Type, utf8)}],
119 children = prepare_services_element(Services)}.
120
121 create_iq_response_credentials(Services) ->
122 2 #xmlel{name = <<"credentials">>,
123 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
124 children = prepare_services_element(Services)}.
125
126 get_external_services(HostType) ->
127 8 gen_mod:get_module_opt(HostType, ?MODULE, service).
128
129 get_external_services(HostType, Type) ->
130 4 [Service || Service = #{type := T} <- get_external_services(HostType), T =:= Type].
131
132 prepare_services_element(Services) ->
133 8 [#xmlel{name = <<"service">>, attrs = make_attrs(Service)} || Service <- Services].
134
135 make_attrs(Service) ->
136 11 [{atom_to_binary(Key), format_value(Key, Value)} || {Key, Value} <- maps:to_list(Service)].
137
138 10 format_value(port, Port) -> integer_to_binary(Port);
139 11 format_value(type, Type) -> atom_to_binary(Type);
140 41 format_value(_, Value) when is_binary(Value) -> Value.
Line Hits Source