./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, config_spec/0]).
24
25 -export([process_iq/4]).
26
27 -ignore_xref([process_iq/4]).
28
29 -include("jlib.hrl").
30 -include("mongoose.hrl").
31 -include("mongoose_config_spec.hrl").
32
33 -spec start(jid:server(), list()) -> ok.
34 start(Host, Opts) ->
35
:-(
IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue),
36
:-(
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
37 ?NS_EXTDISCO, ?MODULE, process_iq, IQDisc).
38
39 -spec stop(jid:server()) -> ok.
40 stop(Host) ->
41
:-(
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_EXTDISCO).
42
43 -spec config_spec() -> mongoose_config_spec:config_section().
44 config_spec() ->
45 146 #section{
46 items = #{<<"service">> => #list{items = service_config_spec(),
47 wrap = none}}
48 }.
49
50 service_config_spec() ->
51 146 #section{
52 items = #{<<"type">> => #option{type = atom,
53 validate = non_empty},
54 <<"host">> => #option{type = string,
55 validate = non_empty},
56 <<"port">> => #option{type = integer,
57 validate = port},
58 <<"transport">> => #option{type = string,
59 validate = {enum, ["udp", "tcp"]}},
60 <<"username">> => #option{type = string,
61 validate = non_empty},
62 <<"password">> => #option{type = string,
63 validate = non_empty}
64 },
65 required = [<<"type">>, <<"host">>]
66 }.
67
68 -spec process_iq(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) ->
69 {mongoose_acc:t(), jlib:iq()}.
70 process_iq(_From, _To = #jid{lserver = LServer}, Acc, #iq{type = get, sub_el = SubEl} = IQ) ->
71
:-(
{ResponseType, Response} = case request_type(SubEl) of
72 all_services ->
73
:-(
RequestedServices = get_external_services(LServer),
74
:-(
{result, create_iq_response(RequestedServices)};
75 {credentials, {Type, Host}} ->
76
:-(
Services = get_external_services(LServer, Type),
77
:-(
RequestedServices = lists:filter(fun(Opts) ->
78
:-(
gen_mod:get_opt(host, Opts, undefined) == binary_to_list(Host)
79 end, Services),
80
:-(
{result, create_iq_response_credentials(RequestedServices)};
81 {selected_services, Type} ->
82
:-(
RequestedServices = get_external_services(LServer, Type),
83
:-(
{result, create_iq_response_services(RequestedServices, Type)};
84 _ ->
85
:-(
{error, [mongoose_xmpp_errors:bad_request()]}
86 end,
87
:-(
{Acc, IQ#iq{type = ResponseType, sub_el = Response}}.
88
89 request_type(#xmlel{name = <<"services">>} = Element) ->
90
:-(
case exml_query:attr(Element, <<"type">>) of
91
:-(
undefined -> all_services;
92 ServiceType ->
93
:-(
case catch binary_to_existing_atom(ServiceType, utf8) of
94
:-(
{'EXIT', _} -> {error, bad_request};
95
:-(
Type -> {selected_services, Type}
96 end
97 end;
98 request_type(#xmlel{name = <<"credentials">>, children = [Children]}) ->
99
:-(
Host = exml_query:attr(Children, <<"host">>),
100
:-(
case exml_query:attr(Children, <<"type">>) of
101
:-(
undefined -> {error, bad_request};
102 ServiceType ->
103
:-(
case catch binary_to_existing_atom(ServiceType, utf8) of
104
:-(
{'EXIT', _} -> {error, bad_request};
105
:-(
Type -> {credentials, {Type, Host}}
106 end
107 end;
108 request_type(_) ->
109
:-(
{error, bad_request}.
110
111 create_iq_response(Services) ->
112
:-(
#xmlel{name = <<"services">>,
113 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
114 children = prepare_services_element(Services)}.
115
116 create_iq_response_services(Services, Type) ->
117
:-(
#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
:-(
#xmlel{name = <<"credentials">>,
123 attrs = [{<<"xmlns">>, ?NS_EXTDISCO}],
124 children = prepare_services_element(Services)}.
125
126 get_external_services(LServer) ->
127
:-(
case gen_mod:get_module_opts(LServer, ?MODULE) of
128
:-(
[] -> [];
129
:-(
ServicesWithOpts -> ServicesWithOpts
130 end.
131
132 get_external_services(LServer, Type) ->
133
:-(
[Opts || Opts <- get_external_services(LServer), gen_mod:get_opt(type, Opts) == Type].
134
135 prepare_services_element(Services) ->
136
:-(
lists:reverse(
137 lists:foldl(
138 fun(Opts, Acc) ->
139
:-(
RequiredElements = required_elements(Opts),
140
:-(
OptionalElements = optional_elements(Opts),
141
:-(
NewResult = #xmlel{name = <<"service">>,
142 attrs = RequiredElements ++ OptionalElements},
143
:-(
[NewResult | Acc]
144 end, [], Services)).
145
146 required_elements(Opts) ->
147
:-(
Host = gen_mod:get_opt(host, Opts, <<"">>),
148
:-(
Type = gen_mod:get_opt(type, Opts),
149
:-(
[{<<"type">>, atom_to_binary(Type, utf8)}, {<<"host">>, Host}].
150
151 optional_elements(Opts) ->
152
:-(
Port = gen_mod:get_opt(port, Opts, undefined),
153
:-(
Transport = gen_mod:get_opt(transport, Opts, undefined),
154
:-(
Password = gen_mod:get_opt(password, Opts, undefined),
155
:-(
Username = gen_mod:get_opt(username, Opts, undefined),
156
:-(
Elements = [{<<"port">>, i2b(Port)},
157 {<<"transport">>, Transport},
158 {<<"password">>, Password},
159 {<<"username">>, Username}],
160
:-(
filter_undefined_elements(Elements).
161
162 filter_undefined_elements(Elements) ->
163
:-(
lists:filter(fun({_, undefined}) -> false;
164
:-(
(_) -> true
165 end, Elements).
166
167
:-(
i2b(X) when is_integer(X) -> integer_to_binary(X);
168
:-(
i2b(X) -> X.
Line Hits Source