diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 33107040e..e362913a1 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -19,7 +19,8 @@ wait_for_master/1, wait_for_slave/1, make_iq_result/1, start_event_relay/0, stop_event_relay/1, put_event/2, get_event/1, - bind/1, auth/1, open_session/1, zlib/1, starttls/1]). + bind/1, auth/1, open_session/1, zlib/1, starttls/1, + close_socket/1]). -include("suite.hrl"). @@ -150,6 +151,8 @@ init_per_testcase(TestCase, OrigConfig) -> connect(Config); test_bind -> auth(connect(Config)); + sm_resume -> + auth(connect(Config)); test_open_session -> bind(auth(connect(Config))); _ when IsMaster or IsSlave -> @@ -176,6 +179,8 @@ no_db_tests() -> version, time, stats, + sm, + sm_resume, disco]}, {test_proxy65, [parallel], [proxy65_master, proxy65_slave]}]. @@ -502,6 +507,43 @@ disco(Config) -> end, Items), disconnect(Config). +sm(Config) -> + Server = ?config(server, Config), + ServerJID = jlib:make_jid(<<"">>, Server, <<"">>), + Msg = #message{to = ServerJID, body = [#text{data = <<"body">>}]}, + true = ?config(sm, Config), + %% Enable the session management with resumption enabled + send(Config, #sm_enable{resume = true}), + #sm_enabled{id = ID, resume = true} = recv(), + %% Initial request; 'h' should be 0. + send(Config, #sm_r{}), + #sm_a{h = 0} = recv(), + %% sending two messages and requesting again; 'h' should be 3. + send(Config, Msg), + send(Config, Msg), + send(Config, Msg), + send(Config, #sm_r{}), + #sm_a{h = 3} = recv(), + close_socket(Config), + {save_config, set_opt(sm_previd, ID, Config)}. + +sm_resume(Config) -> + {sm, SMConfig} = ?config(saved_config, Config), + ID = ?config(sm_previd, SMConfig), + Server = ?config(server, Config), + ServerJID = jlib:make_jid(<<"">>, Server, <<"">>), + MyJID = my_jid(Config), + Txt = #text{data = <<"body">>}, + Msg = #message{from = ServerJID, to = MyJID, body = [Txt]}, + %% Route message. The message should be queued by the C2S process. + ejabberd_router:route(ServerJID, MyJID, xmpp_codec:encode(Msg)), + send(Config, #sm_resume{previd = ID, h = 0}), + #sm_resumed{previd = ID, h = 3} = recv(), + #message{from = ServerJID, to = MyJID, body = [Txt]} = recv(), + #sm_r{} = recv(), + send(Config, #sm_a{h = 1}), + disconnect(Config). + private(Config) -> Conference = #bookmark_conference{name = <<"Some name">>, autojoin = true, diff --git a/test/suite.erl b/test/suite.erl index 180be40fb..a1043526f 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -88,6 +88,11 @@ disconnect(Config) -> ejabberd_socket:close(Socket), Config. +close_socket(Config) -> + Socket = ?config(socket, Config), + ejabberd_socket:close(Socket), + Config. + starttls(Config) -> send(Config, #starttls{}), #starttls_proceed{} = recv(), @@ -146,8 +151,13 @@ wait_auth_SASL_result(Config) -> {xmlstreamstart, <<"stream:stream">>, Attrs} = recv(), <<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs), <<"1.0">> = xml:get_attr_s(<<"version">>, Attrs), - #stream_features{} = recv(), - Config; + #stream_features{sub_els = Fs} = recv(), + lists:foldl( + fun(#feature_sm{}, ConfigAcc) -> + set_opt(sm, true, ConfigAcc); + (_, ConfigAcc) -> + ConfigAcc + end, Config, Fs); #sasl_challenge{text = ClientIn} -> {Response, SASL} = (?config(sasl, Config))(ClientIn), send(Config, #sasl_response{text = Response}), diff --git a/tools/xmpp_codec.erl b/tools/xmpp_codec.erl index 9758ae49d..3ad90fe1f 100644 --- a/tools/xmpp_codec.erl +++ b/tools/xmpp_codec.erl @@ -12,6 +12,20 @@ decode({xmlel, _name, _attrs, _} = _el) -> case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"failed">>, <<"urn:xmpp:sm:3">>} -> + decode_sm_failed(_el); + {<<"a">>, <<"urn:xmpp:sm:3">>} -> decode_sm_a(_el); + {<<"r">>, <<"urn:xmpp:sm:3">>} -> decode_sm_r(_el); + {<<"resumed">>, <<"urn:xmpp:sm:3">>} -> + decode_sm_resumed(_el); + {<<"resume">>, <<"urn:xmpp:sm:3">>} -> + decode_sm_resume(_el); + {<<"enabled">>, <<"urn:xmpp:sm:3">>} -> + decode_sm_enabled(_el); + {<<"enable">>, <<"urn:xmpp:sm:3">>} -> + decode_sm_enable(_el); + {<<"sm">>, <<"urn:xmpp:sm:3">>} -> + decode_feature_sm(_el); {<<"sent">>, <<"urn:xmpp:carbons:2">>} -> decode_carbons_sent(_el); {<<"received">>, <<"urn:xmpp:carbons:2">>} -> @@ -717,6 +731,14 @@ decode({xmlel, _name, _attrs, _} = _el) -> is_known_tag({xmlel, _name, _attrs, _} = _el) -> case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"failed">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"a">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"r">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"resumed">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"resume">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"enabled">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"enable">>, <<"urn:xmpp:sm:3">>} -> true; + {<<"sm">>, <<"urn:xmpp:sm:3">>} -> true; {<<"sent">>, <<"urn:xmpp:carbons:2">>} -> true; {<<"received">>, <<"urn:xmpp:carbons:2">>} -> true; {<<"private">>, <<"urn:xmpp:carbons:2">>} -> true; @@ -1285,6 +1307,28 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) -> _ -> false end. +encode({sm_failed, _} = Failed) -> + encode_sm_failed(Failed, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_a, _} = A) -> + encode_sm_a(A, [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_r} = R) -> + encode_sm_r(R, [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_resumed, _, _} = Resumed) -> + encode_sm_resumed(Resumed, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_resume, _, _} = Resume) -> + encode_sm_resume(Resume, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_enabled, _, _, _, _} = Enabled) -> + encode_sm_enabled(Enabled, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({sm_enable, _, _} = Enable) -> + encode_sm_enable(Enable, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); +encode({feature_sm} = Sm) -> + encode_feature_sm(Sm, + [{<<"xmlns">>, <<"urn:xmpp:sm:3">>}]); encode({carbons_sent, _} = Sent) -> encode_carbons_sent(Sent, [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); @@ -1820,13 +1864,23 @@ pp(carbons_enable, 0) -> []; pp(carbons_private, 0) -> []; pp(carbons_received, 1) -> [forwarded]; pp(carbons_sent, 1) -> [forwarded]; +pp(feature_sm, 0) -> []; +pp(sm_enable, 2) -> [max, resume]; +pp(sm_enabled, 4) -> [id, location, max, resume]; +pp(sm_resume, 2) -> [h, previd]; +pp(sm_resumed, 2) -> [h, previd]; +pp(sm_r, 0) -> []; +pp(sm_a, 1) -> [h]; +pp(sm_failed, 1) -> [reason]; pp(_, _) -> no. enc_bool(false) -> <<"false">>; enc_bool(true) -> <<"true">>. dec_bool(<<"false">>) -> false; -dec_bool(<<"true">>) -> true. +dec_bool(<<"0">>) -> false; +dec_bool(<<"true">>) -> true; +dec_bool(<<"1">>) -> true. resourceprep(R) -> case jlib:resourceprep(R) of @@ -1860,6 +1914,706 @@ dec_tzo(Val) -> M = jlib:binary_to_integer(M1), if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end. +decode_sm_failed({xmlel, <<"failed">>, _attrs, _els}) -> + Reason = decode_sm_failed_els(_els, undefined), + {sm_failed, Reason}. + +decode_sm_failed_els([], Reason) -> Reason; +decode_sm_failed_els([{xmlel, <<"bad-request">>, _attrs, + _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_bad_request(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"conflict">>, _attrs, + _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, decode_error_conflict(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"feature-not-implemented">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_feature_not_implemented(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"forbidden">>, _attrs, + _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, decode_error_forbidden(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"gone">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, decode_error_gone(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"internal-server-error">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_internal_server_error(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"item-not-found">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_item_not_found(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"jid-malformed">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_jid_malformed(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"not-acceptable">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_not_acceptable(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"not-allowed">>, _attrs, + _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_not_allowed(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"not-authorized">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_not_authorized(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"policy-violation">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_policy_violation(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"recipient-unavailable">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_recipient_unavailable(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"redirect">>, _attrs, + _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, decode_error_redirect(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"registration-required">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_registration_required(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"remote-server-not-found">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_remote_server_not_found(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"remote-server-timeout">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_remote_server_timeout(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"resource-constraint">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_resource_constraint(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"service-unavailable">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_service_unavailable(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, + <<"subscription-required">>, _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_subscription_required(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"undefined-condition">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_undefined_condition(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([{xmlel, <<"unexpected-request">>, + _attrs, _} = + _el + | _els], + Reason) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == + <<"urn:ietf:params:xml:ns:xmpp-stanzas">> -> + decode_sm_failed_els(_els, + decode_error_unexpected_request(_el)); + true -> decode_sm_failed_els(_els, Reason) + end; +decode_sm_failed_els([_ | _els], Reason) -> + decode_sm_failed_els(_els, Reason). + +encode_sm_failed({sm_failed, Reason}, _xmlns_attrs) -> + _els = 'encode_sm_failed_$reason'(Reason, []), + _attrs = _xmlns_attrs, + {xmlel, <<"failed">>, _attrs, _els}. + +'encode_sm_failed_$reason'(undefined, _acc) -> _acc; +'encode_sm_failed_$reason'('bad-request' = Reason, + _acc) -> + [encode_error_bad_request(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'(conflict = Reason, _acc) -> + [encode_error_conflict(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('feature-not-implemented' = + Reason, + _acc) -> + [encode_error_feature_not_implemented(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'(forbidden = Reason, _acc) -> + [encode_error_forbidden(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'({gone, _} = Reason, _acc) -> + [encode_error_gone(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('internal-server-error' = + Reason, + _acc) -> + [encode_error_internal_server_error(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('item-not-found' = Reason, + _acc) -> + [encode_error_item_not_found(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('jid-malformed' = Reason, + _acc) -> + [encode_error_jid_malformed(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('not-acceptable' = Reason, + _acc) -> + [encode_error_not_acceptable(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('not-allowed' = Reason, + _acc) -> + [encode_error_not_allowed(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('not-authorized' = Reason, + _acc) -> + [encode_error_not_authorized(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('policy-violation' = Reason, + _acc) -> + [encode_error_policy_violation(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('recipient-unavailable' = + Reason, + _acc) -> + [encode_error_recipient_unavailable(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'({redirect, _} = Reason, + _acc) -> + [encode_error_redirect(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('registration-required' = + Reason, + _acc) -> + [encode_error_registration_required(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('remote-server-not-found' = + Reason, + _acc) -> + [encode_error_remote_server_not_found(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('remote-server-timeout' = + Reason, + _acc) -> + [encode_error_remote_server_timeout(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('resource-constraint' = + Reason, + _acc) -> + [encode_error_resource_constraint(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('service-unavailable' = + Reason, + _acc) -> + [encode_error_service_unavailable(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('subscription-required' = + Reason, + _acc) -> + [encode_error_subscription_required(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('undefined-condition' = + Reason, + _acc) -> + [encode_error_undefined_condition(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]; +'encode_sm_failed_$reason'('unexpected-request' = + Reason, + _acc) -> + [encode_error_unexpected_request(Reason, + [{<<"xmlns">>, + <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}]) + | _acc]. + +decode_sm_a({xmlel, <<"a">>, _attrs, _els}) -> + H = decode_sm_a_attrs(_attrs, undefined), {sm_a, H}. + +decode_sm_a_attrs([{<<"h">>, _val} | _attrs], _H) -> + decode_sm_a_attrs(_attrs, _val); +decode_sm_a_attrs([_ | _attrs], H) -> + decode_sm_a_attrs(_attrs, H); +decode_sm_a_attrs([], H) -> decode_sm_a_attr_h(H). + +encode_sm_a({sm_a, H}, _xmlns_attrs) -> + _els = [], + _attrs = encode_sm_a_attr_h(H, _xmlns_attrs), + {xmlel, <<"a">>, _attrs, _els}. + +decode_sm_a_attr_h(undefined) -> + erlang:error({xmpp_codec, + {missing_attr, <<"h">>, <<"a">>, <<"urn:xmpp:sm:3">>}}); +decode_sm_a_attr_h(_val) -> + case catch dec_int(_val, 0, infinity) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"h">>, <<"a">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_a_attr_h(_val, _acc) -> + [{<<"h">>, enc_int(_val)} | _acc]. + +decode_sm_r({xmlel, <<"r">>, _attrs, _els}) -> {sm_r}. + +encode_sm_r({sm_r}, _xmlns_attrs) -> + _els = [], + _attrs = _xmlns_attrs, + {xmlel, <<"r">>, _attrs, _els}. + +decode_sm_resumed({xmlel, <<"resumed">>, _attrs, + _els}) -> + {H, Previd} = decode_sm_resumed_attrs(_attrs, undefined, + undefined), + {sm_resumed, H, Previd}. + +decode_sm_resumed_attrs([{<<"h">>, _val} | _attrs], _H, + Previd) -> + decode_sm_resumed_attrs(_attrs, _val, Previd); +decode_sm_resumed_attrs([{<<"previd">>, _val} | _attrs], + H, _Previd) -> + decode_sm_resumed_attrs(_attrs, H, _val); +decode_sm_resumed_attrs([_ | _attrs], H, Previd) -> + decode_sm_resumed_attrs(_attrs, H, Previd); +decode_sm_resumed_attrs([], H, Previd) -> + {decode_sm_resumed_attr_h(H), + decode_sm_resumed_attr_previd(Previd)}. + +encode_sm_resumed({sm_resumed, H, Previd}, + _xmlns_attrs) -> + _els = [], + _attrs = encode_sm_resumed_attr_previd(Previd, + encode_sm_resumed_attr_h(H, + _xmlns_attrs)), + {xmlel, <<"resumed">>, _attrs, _els}. + +decode_sm_resumed_attr_h(undefined) -> + erlang:error({xmpp_codec, + {missing_attr, <<"h">>, <<"resumed">>, + <<"urn:xmpp:sm:3">>}}); +decode_sm_resumed_attr_h(_val) -> + case catch dec_int(_val, 0, infinity) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"h">>, <<"resumed">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_resumed_attr_h(_val, _acc) -> + [{<<"h">>, enc_int(_val)} | _acc]. + +decode_sm_resumed_attr_previd(undefined) -> + erlang:error({xmpp_codec, + {missing_attr, <<"previd">>, <<"resumed">>, + <<"urn:xmpp:sm:3">>}}); +decode_sm_resumed_attr_previd(_val) -> _val. + +encode_sm_resumed_attr_previd(_val, _acc) -> + [{<<"previd">>, _val} | _acc]. + +decode_sm_resume({xmlel, <<"resume">>, _attrs, _els}) -> + {H, Previd} = decode_sm_resume_attrs(_attrs, undefined, + undefined), + {sm_resume, H, Previd}. + +decode_sm_resume_attrs([{<<"h">>, _val} | _attrs], _H, + Previd) -> + decode_sm_resume_attrs(_attrs, _val, Previd); +decode_sm_resume_attrs([{<<"previd">>, _val} | _attrs], + H, _Previd) -> + decode_sm_resume_attrs(_attrs, H, _val); +decode_sm_resume_attrs([_ | _attrs], H, Previd) -> + decode_sm_resume_attrs(_attrs, H, Previd); +decode_sm_resume_attrs([], H, Previd) -> + {decode_sm_resume_attr_h(H), + decode_sm_resume_attr_previd(Previd)}. + +encode_sm_resume({sm_resume, H, Previd}, + _xmlns_attrs) -> + _els = [], + _attrs = encode_sm_resume_attr_previd(Previd, + encode_sm_resume_attr_h(H, + _xmlns_attrs)), + {xmlel, <<"resume">>, _attrs, _els}. + +decode_sm_resume_attr_h(undefined) -> + erlang:error({xmpp_codec, + {missing_attr, <<"h">>, <<"resume">>, + <<"urn:xmpp:sm:3">>}}); +decode_sm_resume_attr_h(_val) -> + case catch dec_int(_val, 0, infinity) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"h">>, <<"resume">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_resume_attr_h(_val, _acc) -> + [{<<"h">>, enc_int(_val)} | _acc]. + +decode_sm_resume_attr_previd(undefined) -> + erlang:error({xmpp_codec, + {missing_attr, <<"previd">>, <<"resume">>, + <<"urn:xmpp:sm:3">>}}); +decode_sm_resume_attr_previd(_val) -> _val. + +encode_sm_resume_attr_previd(_val, _acc) -> + [{<<"previd">>, _val} | _acc]. + +decode_sm_enabled({xmlel, <<"enabled">>, _attrs, + _els}) -> + {Id, Location, Max, Resume} = + decode_sm_enabled_attrs(_attrs, undefined, undefined, + undefined, undefined), + {sm_enabled, Id, Location, Max, Resume}. + +decode_sm_enabled_attrs([{<<"id">>, _val} | _attrs], + _Id, Location, Max, Resume) -> + decode_sm_enabled_attrs(_attrs, _val, Location, Max, + Resume); +decode_sm_enabled_attrs([{<<"location">>, _val} + | _attrs], + Id, _Location, Max, Resume) -> + decode_sm_enabled_attrs(_attrs, Id, _val, Max, Resume); +decode_sm_enabled_attrs([{<<"max">>, _val} | _attrs], + Id, Location, _Max, Resume) -> + decode_sm_enabled_attrs(_attrs, Id, Location, _val, + Resume); +decode_sm_enabled_attrs([{<<"resume">>, _val} | _attrs], + Id, Location, Max, _Resume) -> + decode_sm_enabled_attrs(_attrs, Id, Location, Max, + _val); +decode_sm_enabled_attrs([_ | _attrs], Id, Location, Max, + Resume) -> + decode_sm_enabled_attrs(_attrs, Id, Location, Max, + Resume); +decode_sm_enabled_attrs([], Id, Location, Max, + Resume) -> + {decode_sm_enabled_attr_id(Id), + decode_sm_enabled_attr_location(Location), + decode_sm_enabled_attr_max(Max), + decode_sm_enabled_attr_resume(Resume)}. + +encode_sm_enabled({sm_enabled, Id, Location, Max, + Resume}, + _xmlns_attrs) -> + _els = [], + _attrs = encode_sm_enabled_attr_resume(Resume, + encode_sm_enabled_attr_max(Max, + encode_sm_enabled_attr_location(Location, + encode_sm_enabled_attr_id(Id, + _xmlns_attrs)))), + {xmlel, <<"enabled">>, _attrs, _els}. + +decode_sm_enabled_attr_id(undefined) -> undefined; +decode_sm_enabled_attr_id(_val) -> _val. + +encode_sm_enabled_attr_id(undefined, _acc) -> _acc; +encode_sm_enabled_attr_id(_val, _acc) -> + [{<<"id">>, _val} | _acc]. + +decode_sm_enabled_attr_location(undefined) -> undefined; +decode_sm_enabled_attr_location(_val) -> _val. + +encode_sm_enabled_attr_location(undefined, _acc) -> + _acc; +encode_sm_enabled_attr_location(_val, _acc) -> + [{<<"location">>, _val} | _acc]. + +decode_sm_enabled_attr_max(undefined) -> undefined; +decode_sm_enabled_attr_max(_val) -> + case catch dec_int(_val, 0, infinity) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"max">>, <<"enabled">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_enabled_attr_max(undefined, _acc) -> _acc; +encode_sm_enabled_attr_max(_val, _acc) -> + [{<<"max">>, enc_int(_val)} | _acc]. + +decode_sm_enabled_attr_resume(undefined) -> false; +decode_sm_enabled_attr_resume(_val) -> + case catch dec_bool(_val) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"resume">>, <<"enabled">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_enabled_attr_resume(false, _acc) -> _acc; +encode_sm_enabled_attr_resume(_val, _acc) -> + [{<<"resume">>, enc_bool(_val)} | _acc]. + +decode_sm_enable({xmlel, <<"enable">>, _attrs, _els}) -> + {Max, Resume} = decode_sm_enable_attrs(_attrs, + undefined, undefined), + {sm_enable, Max, Resume}. + +decode_sm_enable_attrs([{<<"max">>, _val} | _attrs], + _Max, Resume) -> + decode_sm_enable_attrs(_attrs, _val, Resume); +decode_sm_enable_attrs([{<<"resume">>, _val} | _attrs], + Max, _Resume) -> + decode_sm_enable_attrs(_attrs, Max, _val); +decode_sm_enable_attrs([_ | _attrs], Max, Resume) -> + decode_sm_enable_attrs(_attrs, Max, Resume); +decode_sm_enable_attrs([], Max, Resume) -> + {decode_sm_enable_attr_max(Max), + decode_sm_enable_attr_resume(Resume)}. + +encode_sm_enable({sm_enable, Max, Resume}, + _xmlns_attrs) -> + _els = [], + _attrs = encode_sm_enable_attr_resume(Resume, + encode_sm_enable_attr_max(Max, + _xmlns_attrs)), + {xmlel, <<"enable">>, _attrs, _els}. + +decode_sm_enable_attr_max(undefined) -> undefined; +decode_sm_enable_attr_max(_val) -> + case catch dec_int(_val, 0, infinity) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"max">>, <<"enable">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_enable_attr_max(undefined, _acc) -> _acc; +encode_sm_enable_attr_max(_val, _acc) -> + [{<<"max">>, enc_int(_val)} | _acc]. + +decode_sm_enable_attr_resume(undefined) -> false; +decode_sm_enable_attr_resume(_val) -> + case catch dec_bool(_val) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"resume">>, <<"enable">>, + <<"urn:xmpp:sm:3">>}}); + _res -> _res + end. + +encode_sm_enable_attr_resume(false, _acc) -> _acc; +encode_sm_enable_attr_resume(_val, _acc) -> + [{<<"resume">>, enc_bool(_val)} | _acc]. + +decode_feature_sm({xmlel, <<"sm">>, _attrs, _els}) -> + {feature_sm}. + +encode_feature_sm({feature_sm}, _xmlns_attrs) -> + _els = [], + _attrs = _xmlns_attrs, + {xmlel, <<"sm">>, _attrs, _els}. + decode_carbons_sent({xmlel, <<"sent">>, _attrs, _els}) -> Forwarded = decode_carbons_sent_els(_els, error), diff --git a/tools/xmpp_codec.hrl b/tools/xmpp_codec.hrl index d8516201a..d2455a234 100644 --- a/tools/xmpp_codec.hrl +++ b/tools/xmpp_codec.hrl @@ -9,6 +9,9 @@ host :: binary(), port = 1080 :: non_neg_integer()}). +-record(sm_resume, {h :: non_neg_integer(), + previd :: binary()}). + -record(carbons_enable, {}). -record(carbons_private, {}). @@ -34,11 +37,19 @@ from :: any(), to :: any()}). +-record(sm_a, {h :: non_neg_integer()}). + -record(starttls_proceed, {}). +-record(sm_resumed, {h :: non_neg_integer(), + previd :: binary()}). + -record(forwarded, {delay :: #delay{}, sub_els = [] :: [any()]}). +-record(sm_enable, {max :: non_neg_integer(), + resume = false :: any()}). + -record(starttls_failure, {}). -record(sasl_challenge, {text :: any()}). @@ -49,6 +60,8 @@ -record(p1_ack, {}). +-record(feature_sm, {}). + -record(pubsub_item, {id :: binary(), xml_els = [] :: [any()]}). @@ -68,6 +81,8 @@ node :: binary(), publisher :: binary()}). +-record(sm_r, {}). + -record(stat, {name :: binary(), units :: binary(), value :: binary(), @@ -82,14 +97,19 @@ -record(last, {seconds :: non_neg_integer(), text :: binary()}). +-record(redirect, {uri :: binary()}). + +-record(sm_enabled, {id :: binary(), + location :: binary(), + max :: non_neg_integer(), + resume = false :: any()}). + -record(pubsub_event_items, {node :: binary(), retract = [] :: [binary()], items = [] :: [#pubsub_event_item{}]}). -record(pubsub_event, {items = [] :: [#pubsub_event_items{}]}). --record(redirect, {uri :: binary()}). - -record(sasl_response, {text :: any()}). -record(pubsub_subscribe, {node :: binary(), @@ -358,6 +378,8 @@ -record(sasl_mechanisms, {list = [] :: [binary()]}). +-record(sm_failed, {reason :: atom() | #gone{} | #redirect{}}). + -record(error, {type :: 'auth' | 'cancel' | 'continue' | 'modify' | 'wait', by :: binary(), reason :: atom() | #gone{} | #redirect{}, diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index ad1e24f98..be18ce307 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -2068,6 +2068,122 @@ refs = [#ref{name = forwarded, min = 1, max = 1, label = '$forwarded'}]}). +-xml(feature_sm, + #elem{name = <<"sm">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {feature_sm}}). + +-xml(sm_enable, + #elem{name = <<"enable">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_enable, '$max', '$resume'}, + attrs = [#attr{name = <<"max">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"resume">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}]}). + +-xml(sm_enabled, + #elem{name = <<"enabled">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_enabled, '$id', '$location', '$max', '$resume'}, + attrs = [#attr{name = <<"id">>}, + #attr{name = <<"location">>}, + #attr{name = <<"max">>, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"resume">>, + default = false, + dec = {dec_bool, []}, + enc = {enc_bool, []}}]}). + +-xml(sm_resume, + #elem{name = <<"resume">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_resume, '$h', '$previd'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"previd">>, + required = true}]}). + +-xml(sm_resumed, + #elem{name = <<"resumed">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_resumed, '$h', '$previd'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}, + #attr{name = <<"previd">>, + required = true}]}). + +-xml(sm_r, + #elem{name = <<"r">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_r}}). + +-xml(sm_a, + #elem{name = <<"a">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_a, '$h'}, + attrs = [#attr{name = <<"h">>, + required = true, + dec = {dec_int, [0, infinity]}, + enc = {enc_int, []}}]}). + +-xml(sm_failed, + #elem{name = <<"failed">>, + xmlns = <<"urn:xmpp:sm:3">>, + result = {sm_failed, '$reason'}, + refs = [#ref{name = error_bad_request, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_conflict, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_feature_not_implemented, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_forbidden, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_gone, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_internal_server_error, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_item_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_jid_malformed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_acceptable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_allowed, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_not_authorized, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_policy_violation, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_recipient_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_redirect, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_registration_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_not_found, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_remote_server_timeout, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_resource_constraint, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_service_unavailable, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_subscription_required, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_undefined_condition, + min = 0, max = 1, label = '$reason'}, + #ref{name = error_unexpected_request, + min = 0, max = 1, label = '$reason'}]}). + dec_tzo(Val) -> [H1, M1] = str:tokens(Val, <<":">>), H = jlib:binary_to_integer(H1), @@ -2110,7 +2226,9 @@ resourceprep(R) -> end. dec_bool(<<"false">>) -> false; -dec_bool(<<"true">>) -> true. +dec_bool(<<"0">>) -> false; +dec_bool(<<"true">>) -> true; +dec_bool(<<"1">>) -> true. enc_bool(false) -> <<"false">>; enc_bool(true) -> <<"true">>.