./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 4 IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue),
36 4 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 4 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 160 #section{
46 items = #{<<"service">> => #list{items = service_config_spec(),
47 wrap = none}}
48 }.
49
50 service_config_spec() ->
51 160 #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 16 {ResponseType, Response} = case request_type(SubEl) of
72 all_services ->
73 4 RequestedServices = get_external_services(LServer),
74 4 {result, create_iq_response(RequestedServices)};
75 {credentials, {Type, Host}} ->
76 2 Services = get_external_services(LServer, Type),
77 2 RequestedServices = lists:filter(fun(Opts) ->
78 3 gen_mod:get_opt(host, Opts, undefined) == binary_to_list(Host)
79 end, Services),
80 2 {result, create_iq_response_credentials(RequestedServices)};
81 {selected_services, Type} ->
82 2 RequestedServices = get_external_services(LServer, 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(LServer) ->
127 8 case gen_mod:get_module_opts(LServer, ?MODULE) of
128 1 [] -> [];
129 7 ServicesWithOpts -> ServicesWithOpts
130 end.
131
132 get_external_services(LServer, Type) ->
133 4 [Opts || Opts <- get_external_services(LServer), gen_mod:get_opt(type, Opts) == Type].
134
135 prepare_services_element(Services) ->
136 8 lists:reverse(
137 lists:foldl(
138 fun(Opts, Acc) ->
139 11 RequiredElements = required_elements(Opts),
140 11 OptionalElements = optional_elements(Opts),
141 11 NewResult = #xmlel{name = <<"service">>,
142 attrs = RequiredElements ++ OptionalElements},
143 11 [NewResult | Acc]
144 end, [], Services)).
145
146 required_elements(Opts) ->
147 11 Host = gen_mod:get_opt(host, Opts, <<"">>),
148 11 Type = gen_mod:get_opt(type, Opts),
149 11 [{<<"type">>, atom_to_binary(Type, utf8)}, {<<"host">>, Host}].
150
151 optional_elements(Opts) ->
152 11 Port = gen_mod:get_opt(port, Opts, undefined),
153 11 Transport = gen_mod:get_opt(transport, Opts, undefined),
154 11 Password = gen_mod:get_opt(password, Opts, undefined),
155 11 Username = gen_mod:get_opt(username, Opts, undefined),
156 11 Elements = [{<<"port">>, i2b(Port)},
157 {<<"transport">>, Transport},
158 {<<"password">>, Password},
159 {<<"username">>, Username}],
160 11 filter_undefined_elements(Elements).
161
162 filter_undefined_elements(Elements) ->
163 11 lists:filter(fun({_, undefined}) -> false;
164 40 (_) -> true
165 end, Elements).
166
167 10 i2b(X) when is_integer(X) -> integer_to_binary(X);
168 1 i2b(X) -> X.
Line Hits Source