./ct_report/coverage/mongoose_c2s_stanzas.COVER.html

1 -module(mongoose_c2s_stanzas).
2
3 -include("jlib.hrl").
4 -include("mongoose.hrl").
5 -include_lib("exml/include/exml_stream.hrl").
6
7 -export([
8 stream_header/1,
9 stream_features_before_auth/1,
10 tls_proceed/0,
11 tls_failure/0,
12 stream_features_after_auth/1,
13 sasl_success_stanza/1,
14 sasl_failure_stanza/1,
15 sasl_challenge_stanza/1,
16 successful_resource_binding/2,
17 successful_session_establishment/1
18 ]).
19
20 -spec stream_header(mongoose_c2s:data()) -> exml_stream:start().
21 stream_header(StateData) ->
22 13672 Lang = mongoose_c2s:get_lang(StateData),
23 13672 LServer = mongoose_c2s:get_lserver(StateData),
24 13672 StreamId = mongoose_c2s:get_stream_id(StateData),
25 13672 MaybeFrom = [ {<<"to">>, jid:to_binary(Jid)}
26 13672 || Jid <- [mongoose_c2s:get_jid(StateData)], Jid =/= undefined],
27 13672 Attrs = [{<<"xmlns">>, ?NS_CLIENT},
28 {<<"xmlns:stream">>, <<"http://etherx.jabber.org/streams">>},
29 {<<"id">>, StreamId},
30 {<<"from">>, LServer},
31 {<<"version">>, ?XMPP_VERSION},
32 {<<"xml:lang">>, Lang} | MaybeFrom ],
33 13672 #xmlstreamstart{name = <<"stream:stream">>, attrs = Attrs}.
34
35 -spec stream_features([exml:element() | exml:cdata()]) -> exml:element().
36 stream_features(Features) ->
37 13678 #xmlel{name = <<"stream:features">>, children = Features}.
38
39 -spec stream_features_before_auth(mongoose_c2s:data()) -> exml:element().
40 stream_features_before_auth(StateData) ->
41 7070 HostType = mongoose_c2s:get_host_type(StateData),
42 7070 LServer = mongoose_c2s:get_lserver(StateData),
43 7070 Socket = mongoose_c2s:get_socket(StateData),
44 7070 LOpts = mongoose_c2s:get_listener_opts(StateData),
45 7070 IsSSL = mongoose_c2s_socket:is_ssl(Socket),
46 7070 Features = determine_features(StateData, HostType, LServer, LOpts, IsSSL),
47 7070 stream_features(Features).
48
49 %% From RFC 6120, section 5.3.1:
50 %%
51 %% If TLS is mandatory-to-negotiate, the receiving entity SHOULD NOT
52 %% advertise support for any stream feature except STARTTLS during the
53 %% initial stage of the stream negotiation process, because further stream
54 %% features might depend on prior negotiation of TLS given the order of
55 %% layers in XMPP (e.g., the particular SASL mechanisms offered by the
56 %% receiving entity will likely depend on whether TLS has been negotiated).
57 %%
58 %% http://xmpp.org/rfcs/rfc6120.html#tls-rules-mtn
59 determine_features(_, _, _, #{tls := #{mode := starttls_required}}, false) ->
60 20 [starttls_stanza(required)];
61 determine_features(StateData, HostType, LServer, #{tls := #{mode := starttls}}, false) ->
62 6731 InitialFeatures = [starttls_stanza(optional) | maybe_sasl_mechanisms(StateData)],
63 6731 StreamFeaturesParams = #{c2s_data => StateData, lserver => LServer},
64 6731 mongoose_hooks:c2s_stream_features(HostType, StreamFeaturesParams, InitialFeatures);
65 determine_features(StateData, HostType, LServer, _, _) ->
66 319 InitialFeatures = maybe_sasl_mechanisms(StateData),
67 319 StreamFeaturesParams = #{c2s_data => StateData, lserver => LServer},
68 319 mongoose_hooks:c2s_stream_features(HostType, StreamFeaturesParams, InitialFeatures).
69
70 -spec maybe_sasl_mechanisms(mongoose_c2s:data()) -> [exml:element()].
71 maybe_sasl_mechanisms(StateData) ->
72 7050 case mongoose_c2s:get_auth_mechs(StateData) of
73 65 [] -> [];
74 Mechanisms ->
75 6985 [#xmlel{name = <<"mechanisms">>,
76 attrs = [{<<"xmlns">>, ?NS_SASL}],
77 15046 children = [ mechanism(M) || M <- Mechanisms ]}]
78 end.
79
80 -spec mechanism(binary()) -> exml:element().
81 mechanism(M) ->
82 15046 #xmlel{name = <<"mechanism">>, children = [#xmlcdata{content = M}]}.
83
84 -spec starttls_stanza(required | optional) -> exml:element().
85 starttls_stanza(TLSRequired) when TLSRequired =:= required; TLSRequired =:= optional ->
86 6751 #xmlel{name = <<"starttls">>,
87 attrs = [{<<"xmlns">>, ?NS_TLS}],
88 20 children = [ #xmlel{name = <<"required">>} || TLSRequired =:= required ]}.
89
90 -spec tls_proceed() -> exml:element().
91 tls_proceed() ->
92 98 #xmlel{name = <<"proceed">>,
93 attrs = [{<<"xmlns">>, ?NS_TLS}]}.
94
95 -spec tls_failure() -> exml:element().
96 tls_failure() ->
97 2 #xmlel{name = <<"failure">>,
98 attrs = [{<<"xmlns">>, ?NS_TLS}]}.
99
100 -spec stream_features_after_auth(mongoose_c2s:data()) -> exml:element().
101 stream_features_after_auth(StateData) ->
102 6608 case mongoose_c2s:get_listener_opts(StateData) of
103 #{backwards_compatible_session := false} ->
104 7 Features = [#xmlel{name = <<"bind">>,
105 attrs = [{<<"xmlns">>, ?NS_BIND}]}
106 | hook_enabled_features(StateData)],
107 7 stream_features(Features);
108 #{backwards_compatible_session := true} ->
109 6601 Features = [#xmlel{name = <<"session">>,
110 attrs = [{<<"xmlns">>, ?NS_SESSION}]},
111 #xmlel{name = <<"bind">>,
112 attrs = [{<<"xmlns">>, ?NS_BIND}]}
113 | hook_enabled_features(StateData)],
114 6601 stream_features(Features)
115 end.
116
117 hook_enabled_features(StateData) ->
118 6608 HostType = mongoose_c2s:get_host_type(StateData),
119 6608 LServer = mongoose_c2s:get_lserver(StateData),
120 6608 InitialFeatures = mongoose_hooks:roster_get_versioning_feature(HostType),
121 6608 StreamFeaturesParams = #{c2s_data => StateData, lserver => LServer},
122 6608 mongoose_hooks:c2s_stream_features(HostType, StreamFeaturesParams, InitialFeatures).
123
124 -spec sasl_success_stanza(undefined | binary()) -> exml:element().
125 sasl_success_stanza(ServerOut) ->
126 6591 C = case ServerOut of
127 6548 undefined -> [];
128 43 _ -> [#xmlcdata{content = jlib:encode_base64(ServerOut)}]
129 end,
130 6591 #xmlel{name = <<"success">>,
131 attrs = [{<<"xmlns">>, ?NS_SASL}],
132 children = C}.
133
134 -spec sasl_failure_stanza(binary() | {binary(), iodata() | undefined}) -> exml:element().
135 sasl_failure_stanza(Error) when is_binary(Error) ->
136 68 sasl_failure_stanza({Error, undefined});
137 sasl_failure_stanza({Error, Text}) ->
138 69 #xmlel{name = <<"failure">>,
139 attrs = [{<<"xmlns">>, ?NS_SASL}],
140 children = [#xmlel{name = Error} | maybe_text_tag(Text)]}.
141
142 68 maybe_text_tag(undefined) -> [];
143 maybe_text_tag(Text) ->
144 1 [#xmlel{name = <<"text">>,
145 children = [#xmlcdata{content = Text}]}].
146
147 -spec sasl_challenge_stanza(binary()) -> exml:element().
148 sasl_challenge_stanza(ServerOut) ->
149 47 #xmlel{name = <<"challenge">>,
150 attrs = [{<<"xmlns">>, ?NS_SASL}],
151 children = [#xmlcdata{content = jlib:encode_base64(ServerOut)}]}.
152
153 -spec successful_resource_binding(jlib:iq(), jid:jid()) -> exml:element().
154 successful_resource_binding(IQ, Jid) ->
155 6567 JIDEl = #xmlel{name = <<"jid">>,
156 children = [#xmlcdata{content = jid:to_binary(Jid)}]},
157 6567 Res = IQ#iq{type = result,
158 sub_el = [#xmlel{name = <<"bind">>,
159 attrs = [{<<"xmlns">>, ?NS_BIND}],
160 children = [JIDEl]}]},
161 6567 jlib:iq_to_xml(Res).
162
163 -spec successful_session_establishment(jlib:iq()) -> exml:element().
164 successful_session_establishment(IQ) ->
165 6556 Res = IQ#iq{type = result,
166 sub_el = [#xmlel{name = <<"session">>,
167 attrs = [{<<"xmlns">>, ?NS_SESSION}]}]},
168 6556 jlib:iq_to_xml(Res).
Line Hits Source