aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-25 13:50:30 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-25 13:50:30 +0300
commit179fcd9521ef8db4626ca110ba80c502d810c814 (patch)
tree78e0b2410b0f8a4cbe95f84bfb30e58d1b205e3e
parentFix hooks de-registration (diff)
Rewrite mod_mam and mod_muc to use XML generator
-rw-r--r--include/mod_muc_room.hrl3
-rw-r--r--include/xmpp_codec.hrl155
-rw-r--r--src/ejabberd_router.erl31
-rw-r--r--src/mod_disco.erl4
-rw-r--r--src/mod_last.erl2
-rw-r--r--src/mod_mam.erl715
-rw-r--r--src/mod_mam_mnesia.erl28
-rw-r--r--src/mod_mam_sql.erl71
-rw-r--r--src/mod_muc.erl677
-rw-r--r--src/mod_muc_admin.erl12
-rw-r--r--src/mod_muc_log.erl36
-rw-r--r--src/mod_muc_room.erl4469
-rw-r--r--src/mod_private.erl2
-rw-r--r--src/xmpp.erl240
-rw-r--r--src/xmpp_codec.erl2388
-rw-r--r--src/xmpp_util.erl14
-rw-r--r--tools/xmpp_codec.spec264
17 files changed, 4875 insertions, 4236 deletions
diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl
index d985f3f3b..fc20f44c6 100644
--- a/include/mod_muc_room.hrl
+++ b/include/mod_muc_room.hrl
@@ -71,6 +71,7 @@
-type config() :: #config{}.
-type role() :: moderator | participant | visitor | none.
+-type affiliation() :: admin | member | outcast | owner | none.
-record(user,
{
@@ -120,5 +121,3 @@
host = <<>> :: binary() | '_' | '$2'}).
-type muc_online_users() :: #muc_online_users{}.
-
--type muc_room_state() :: #state{}.
diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl
index 7b8275ed5..85601035d 100644
--- a/include/xmpp_codec.hrl
+++ b/include/xmpp_codec.hrl
@@ -11,13 +11,20 @@
-record(csi, {type :: active | inactive}).
-type csi() :: #csi{}.
--record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
- 'store' | 'no-permanent-store'}).
+-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' |
+ 'no-permanent-store' | 'no-permanent-storage'}).
-type hint() :: #hint{}.
-record(feature_register, {}).
-type feature_register() :: #feature_register{}.
+-record(address, {type :: 'bcc' | 'cc' | 'noreply' | 'ofrom' | 'replyroom' | 'replyto' | 'to',
+ jid :: any(),
+ desc :: binary(),
+ node :: binary(),
+ delivered :: any()}).
+-type address() :: #address{}.
+
-record(sasl_success, {text :: any()}).
-type sasl_success() :: #sasl_success{}.
@@ -55,6 +62,9 @@
stored :: non_neg_integer()}).
-type expire() :: #expire{}.
+-record(muc_unsubscribe, {}).
+-type muc_unsubscribe() :: #muc_unsubscribe{}.
+
-record(pubsub_unsubscribe, {node :: binary(),
jid :: any(),
subid :: binary()}).
@@ -81,7 +91,7 @@
type :: 'member' | 'none' | 'outcast' | 'owner' | 'publish-only' | 'publisher'}).
-type pubsub_affiliation() :: #pubsub_affiliation{}.
--record(muc_decline, {reason :: binary(),
+-record(muc_decline, {reason = <<>> :: 'undefined' | binary(),
from :: any(),
to :: any()}).
-type muc_decline() :: #muc_decline{}.
@@ -90,9 +100,20 @@
xmlns :: binary()}).
-type sm_a() :: #sm_a{}.
+-record(muc_subscribe, {nick :: binary(),
+ events = [] :: [binary()]}).
+-type muc_subscribe() :: #muc_subscribe{}.
+
+-record(stanza_id, {by :: any(),
+ id :: binary()}).
+-type stanza_id() :: #stanza_id{}.
+
-record(starttls_proceed, {}).
-type starttls_proceed() :: #starttls_proceed{}.
+-record(client_id, {id :: binary()}).
+-type client_id() :: #client_id{}.
+
-record(sm_resumed, {h :: non_neg_integer(),
previd :: binary(),
xmlns :: binary()}).
@@ -116,9 +137,19 @@
-record(gone, {uri :: binary()}).
-type gone() :: #gone{}.
+-record(x_conference, {jid :: any(),
+ password = <<>> :: binary(),
+ reason = <<>> :: binary(),
+ continue :: any(),
+ thread = <<>> :: binary()}).
+-type x_conference() :: #x_conference{}.
+
-record(private, {xml_els = [] :: [any()]}).
-type private() :: #private{}.
+-record(nick, {name :: binary()}).
+-type nick() :: #nick{}.
+
-record(p1_ack, {}).
-type p1_ack() :: #p1_ack{}.
@@ -163,6 +194,9 @@
error = [] :: [{integer(),'undefined' | binary()}]}).
-type stat() :: #stat{}.
+-record(addresses, {list = [] :: [#address{}]}).
+-type addresses() :: #addresses{}.
+
-record('see-other-host', {host :: binary()}).
-type 'see-other-host'() :: #'see-other-host'{}.
@@ -194,6 +228,9 @@
-record(pubsub_event, {items = [] :: [#pubsub_event_items{}]}).
-type pubsub_event() :: #pubsub_event{}.
+-record(muc_unique, {name = <<>> :: binary()}).
+-type muc_unique() :: #muc_unique{}.
+
-record(sasl_response, {text :: any()}).
-type sasl_response() :: #sasl_response{}.
@@ -217,19 +254,11 @@
-record(feature_csi, {xmlns :: binary()}).
-type feature_csi() :: #feature_csi{}.
--record(muc_user_destroy, {reason :: binary(),
- jid :: any()}).
--type muc_user_destroy() :: #muc_user_destroy{}.
-
-record(disco_item, {jid :: any(),
name :: binary(),
node :: binary()}).
-type disco_item() :: #disco_item{}.
--record(disco_items, {node :: binary(),
- items = [] :: [#disco_item{}]}).
--type disco_items() :: #disco_items{}.
-
-record(unblock, {items = [] :: [any()]}).
-type unblock() :: #unblock{}.
@@ -239,10 +268,8 @@
-record(compression, {methods = [] :: [binary()]}).
-type compression() :: #compression{}.
--record(muc_owner_destroy, {jid :: any(),
- reason :: binary(),
- password :: binary()}).
--type muc_owner_destroy() :: #muc_owner_destroy{}.
+-record(muc_subscriptions, {list = [] :: [any()]}).
+-type muc_subscriptions() :: #muc_subscriptions{}.
-record(pubsub_subscription, {jid :: any(),
node :: binary(),
@@ -252,7 +279,7 @@
-record(muc_item, {actor :: #muc_actor{},
continue :: binary(),
- reason :: binary(),
+ reason = <<>> :: 'undefined' | binary(),
affiliation :: 'admin' | 'member' | 'none' | 'outcast' | 'owner',
role :: 'moderator' | 'none' | 'participant' | 'visitor',
jid :: any(),
@@ -267,8 +294,8 @@
-record(mam_prefs, {xmlns :: binary(),
default :: 'always' | 'never' | 'roster',
- always = [] :: [any()],
- never = [] :: [any()]}).
+ always :: [any()],
+ never :: [any()]}).
-type mam_prefs() :: #mam_prefs{}.
-record(caps, {node :: binary(),
@@ -350,13 +377,17 @@
-record(block_list, {items = [] :: [any()]}).
-type block_list() :: #block_list{}.
+-record(xdata_option, {label :: binary(),
+ value :: binary()}).
+-type xdata_option() :: #xdata_option{}.
+
-record(xdata_field, {label :: binary(),
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
var :: binary(),
required = false :: boolean(),
desc :: binary(),
values = [] :: [binary()],
- options = [] :: [binary()]}).
+ options = [] :: [#xdata_option{}]}).
-type xdata_field() :: #xdata_field{}.
-record(version, {name :: binary(),
@@ -364,11 +395,6 @@
os :: binary()}).
-type version() :: #version{}.
--record(muc_invite, {reason :: binary(),
- from :: any(),
- to :: any()}).
--type muc_invite() :: #muc_invite{}.
-
-record(bind, {jid :: any(),
resource :: any()}).
-type bind() :: #bind{}.
@@ -376,13 +402,11 @@
-record(rosterver_feature, {}).
-type rosterver_feature() :: #rosterver_feature{}.
--record(muc_user, {decline :: #muc_decline{},
- destroy :: #muc_user_destroy{},
- invites = [] :: [#muc_invite{}],
- items = [] :: [#muc_item{}],
- status_codes = [] :: [pos_integer()],
- password :: binary()}).
--type muc_user() :: #muc_user{}.
+-record(muc_invite, {reason = <<>> :: 'undefined' | binary(),
+ from :: any(),
+ to :: any(),
+ continue :: binary()}).
+-type muc_invite() :: #muc_invite{}.
-record(carbons_disable, {}).
-type carbons_disable() :: #carbons_disable{}.
@@ -400,7 +424,7 @@
-type vcard_org() :: #vcard_org{}.
-record(rsm_set, {'after' :: binary(),
- before :: 'none' | binary(),
+ before :: binary(),
count :: non_neg_integer(),
first :: #rsm_first{},
index :: non_neg_integer(),
@@ -414,6 +438,11 @@
complete :: any()}).
-type mam_fin() :: #mam_fin{}.
+-record(disco_items, {node :: binary(),
+ items = [] :: [#disco_item{}],
+ rsm :: #rsm_set{}}).
+-type disco_items() :: #disco_items{}.
+
-record(vcard_tel, {home = false :: boolean(),
work = false :: boolean(),
voice = false :: boolean(),
@@ -430,6 +459,20 @@
number :: binary()}).
-type vcard_tel() :: #vcard_tel{}.
+-record(muc_destroy, {xmlns :: binary(),
+ jid :: any(),
+ reason = <<>> :: 'undefined' | binary(),
+ password :: binary()}).
+-type muc_destroy() :: #muc_destroy{}.
+
+-record(muc_user, {decline :: #muc_decline{},
+ destroy :: #muc_destroy{},
+ invites = [] :: [#muc_invite{}],
+ items = [] :: [#muc_item{}],
+ status_codes = [] :: [pos_integer()],
+ password :: binary()}).
+-type muc_user() :: #muc_user{}.
+
-record(vcard_key, {type :: binary(),
cred :: binary()}).
-type vcard_key() :: #vcard_key{}.
@@ -530,14 +573,11 @@
start :: any(),
'end' :: any(),
with :: any(),
+ withtext :: binary(),
rsm :: #rsm_set{},
xdata :: #xdata{}}).
-type mam_query() :: #mam_query{}.
--record(muc_owner, {destroy :: #muc_owner_destroy{},
- config :: #xdata{}}).
--type muc_owner() :: #muc_owner{}.
-
-record(pubsub_options, {node :: binary(),
jid :: any(),
subid :: binary(),
@@ -592,6 +632,11 @@
fetch = false :: boolean()}).
-type offline() :: #offline{}.
+-record(muc_owner, {destroy :: #muc_destroy{},
+ config :: #xdata{},
+ items = [] :: [#muc_item{}]}).
+-type muc_owner() :: #muc_owner{}.
+
-record(sasl_mechanisms, {list = [] :: [binary()]}).
-type sasl_mechanisms() :: #sasl_mechanisms{}.
@@ -709,6 +754,7 @@
-type xmpp_element() :: compression() |
pubsub_subscription() |
+ xdata_option() |
version() |
pubsub_affiliation() |
muc_admin() |
@@ -726,7 +772,9 @@
rsm_set() |
'see-other-host'() |
hint() |
+ stanza_id() |
starttls_proceed() |
+ client_id() |
sm_resumed() |
forwarded() |
xevent() |
@@ -742,8 +790,11 @@
pubsub_event_item() |
muc_item() |
vcard_temp() |
+ address() |
sasl_success() |
+ addresses() |
pubsub_event_items() |
+ muc_subscriptions() |
disco_items() |
pubsub_options() |
compress() |
@@ -751,33 +802,25 @@
muc_history() |
identity() |
feature_csi() |
- muc_user_destroy() |
privacy_query() |
delay() |
vcard_tel() |
- vcard_logo() |
- disco_info() |
vcard_geo() |
vcard_photo() |
- feature_register() |
- register() |
- muc_owner() |
pubsub() |
- sm_r() |
+ muc_owner() |
muc_actor() |
- error() |
- stream_error() |
- muc_user() |
- vcard_adr() |
carbons_private() |
mix_leave() |
- muc_invite() |
+ muc_subscribe() |
rosterver_feature() |
+ muc_invite() |
vcard_xupdate() |
carbons_disable() |
bookmark_conference() |
offline() |
time() |
+ muc_unique() |
sasl_response() |
pubsub_subscribe() |
presence() |
@@ -786,6 +829,7 @@
starttls_failure() |
sasl_challenge() |
gone() |
+ x_conference() |
private() |
compress_failure() |
sasl_failure() |
@@ -794,6 +838,7 @@
sm_resume() |
carbons_enable() |
expire() |
+ muc_unsubscribe() |
pubsub_unsubscribe() |
muc_decline() |
chatstate() |
@@ -803,6 +848,7 @@
search() |
pubsub_publish() |
unblock() |
+ nick() |
p1_ack() |
block() |
mix_join() |
@@ -832,11 +878,20 @@
starttls() |
mam_prefs() |
sasl_mechanisms() |
+ muc_destroy() |
vcard_key() |
csi() |
roster_query() |
- muc_owner_destroy() |
mam_query() |
bookmark_url() |
vcard_email() |
- vcard_label().
+ vcard_label() |
+ vcard_logo() |
+ disco_info() |
+ feature_register() |
+ register() |
+ sm_r() |
+ error() |
+ stream_error() |
+ muc_user() |
+ vcard_adr().
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index 5924d92c0..83ffd932b 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -72,7 +72,7 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
--spec route(jid(), jid(), xmlel() | xmpp_element()) -> ok.
+-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
@@ -85,13 +85,21 @@ route(From, To, Packet) ->
%% Route the error packet only if the originating packet is not an error itself.
%% RFC3920 9.3.1
--spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok.
+-spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok;
+ (jid(), jid(), stanza(), error()) -> ok.
-route_error(From, To, ErrPacket, OrigPacket) ->
+route_error(From, To, #xmlel{} = ErrPacket, #xmlel{} = OrigPacket) ->
#xmlel{attrs = Attrs} = OrigPacket,
case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of
false -> route(From, To, ErrPacket);
true -> ok
+ end;
+route_error(From, To, Packet, #error{} = Err) ->
+ Type = xmpp:get_type(Packet),
+ if Type == error; Type == result ->
+ ok;
+ true ->
+ ejabberd_router:route(From, To, xmpp:make_error(Packet, Err))
end.
-spec register_route(binary()) -> term().
@@ -406,11 +414,16 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
end.
-spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any().
-do_route(From, To, Packet,
- #route{local_hint = {apply, Module, Function}, pid = Pid})
- when is_pid(Pid) andalso node(Pid) == node() ->
- try
- Module:Function(From, To, xmpp:decode(Packet, [ignore_els]))
+do_route(From, To, Packet, #route{local_hint = LocalHint,
+ pid = Pid}) when is_pid(Pid) ->
+ try xmpp:decode(Packet, [ignore_els]) of
+ Pkt ->
+ case LocalHint of
+ {apply, Module, Function} when node(Pid) == node() ->
+ Module:Function(From, To, Pkt);
+ _ ->
+ Pid ! {route, From, To, Pkt}
+ end
catch error:{xmpp_codec, Why} ->
?ERROR_MSG("failed to decode xml element ~p when "
"routing from ~s to ~s: ~s",
@@ -418,8 +431,6 @@ do_route(From, To, Packet,
xmpp:format_error(Why)]),
drop
end;
-do_route(From, To, Packet, #route{pid = Pid}) when is_pid(Pid) ->
- Pid ! {route, From, To, xmpp:encode(Packet)};
do_route(_From, _To, _Packet, _Route) ->
drop.
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 03fdab209..0d3f242a0 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -295,7 +295,7 @@ process_sm_iq_items(#iq{type = get, lang = Lang,
end;
false ->
Txt = <<"Not subscribed">>,
- xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang))
+ xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
-spec get_sm_items({error, error()} | {result, [disco_item()]} | empty,
@@ -371,7 +371,7 @@ process_sm_iq_info(#iq{type = get, lang = Lang,
end;
false ->
Txt = <<"Not subscribed">>,
- xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang))
+ xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
-spec get_sm_identity([identity()], jid(), jid(),
diff --git a/src/mod_last.erl b/src/mod_last.erl
index b267aa89b..6d0edebf0 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -140,7 +140,7 @@ process_sm_iq(#iq{from = From, to = To, lang = Lang} = IQ) ->
end;
true ->
Txt = <<"Not subscribed">>,
- xmpp:make_error(IQ, xmpp:err_not_subscribed(Txt, Lang))
+ xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
%% @spec (LUser::string(), LServer::string()) ->
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index 10eb098da..b4ee17720 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -34,12 +34,12 @@
-export([start/2, stop/1, depends/2]).
-export([user_send_packet/4, user_send_packet_strip_tag/4, user_receive_packet/5,
- process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
- remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/4,
+ process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5,
+ remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2,
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/4]).
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("logger.hrl").
-include("mod_muc_room.hrl").
-include("ejabberd_commands.hrl").
@@ -54,17 +54,12 @@
-callback delete_old_messages(binary() | global,
erlang:timestamp(),
all | chat | groupchat) -> any().
--callback extended_fields() -> [xmlel()].
+-callback extended_fields() -> [xdata_field()].
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
jid(), binary(), recv | send) -> {ok, binary()} | any().
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
--callback select(binary(), jid(), jid(),
- none | erlang:timestamp(),
- none | erlang:timestamp(),
- none | ljid() | {text, binary()},
- none | #rsm_in{},
- chat | groupchat) ->
+-callback select(binary(), jid(), jid(), mam_query(), chat | groupchat) ->
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
%%%===================================================================
@@ -200,14 +195,10 @@ get_room_config(X, RoomState, _From, Lang) ->
true -> <<"1">>;
_ -> <<"0">>
end,
- XField = #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"boolean">>},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]},
+ XField = #xdata_field{type = boolean,
+ label = translate:translate(Lang, Label),
+ var = Var,
+ values = [Val]},
X ++ [XField].
set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
@@ -222,7 +213,7 @@ set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
catch _:{case_clause, _} ->
Txt = <<"Value of '~s' should be boolean">>,
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
- {error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)}
+ {error, xmpp:err_bad_request(Lang, ErrTxt)}
end;
set_room_option(Acc, _Opt, _Vals, _Lang) ->
Acc.
@@ -236,16 +227,7 @@ user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
NewPkt = strip_my_archived_tag(Pkt, LServer),
case store_msg(C2SState, NewPkt, LUser, LServer, Peer, recv) of
{ok, ID} ->
- Archived = #xmlel{name = <<"archived">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_MAM_TMP},
- {<<"id">>, ID}]},
- StanzaID = #xmlel{name = <<"stanza-id">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_SID_0},
- {<<"id">>, ID}]},
- NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
- NewPkt#xmlel{children = NewEls};
+ set_stanza_id(NewPkt, LServer, ID);
_ ->
NewPkt
end;
@@ -259,19 +241,10 @@ user_send_packet(Pkt, C2SState, JID, Peer) ->
case should_archive(Pkt, LServer) of
true ->
NewPkt = strip_my_archived_tag(Pkt, LServer),
- case store_msg(C2SState, jlib:replace_from_to(JID, Peer, NewPkt),
+ case store_msg(C2SState, xmpp:set_from_to(NewPkt, JID, Peer),
LUser, LServer, Peer, send) of
{ok, ID} ->
- Archived = #xmlel{name = <<"archived">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_MAM_TMP},
- {<<"id">>, ID}]},
- StanzaID = #xmlel{name = <<"stanza-id">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_SID_0},
- {<<"id">>, ID}]},
- NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
- NewPkt#xmlel{children = NewEls};
+ set_stanza_id(NewPkt, LServer, ID);
_ ->
NewPkt
end;
@@ -291,16 +264,7 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
StorePkt = strip_x_jid_tags(NewPkt),
case store_muc(MUCState, StorePkt, RoomJID, From, FromNick) of
{ok, ID} ->
- Archived = #xmlel{name = <<"archived">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_MAM_TMP},
- {<<"id">>, ID}]},
- StanzaID = #xmlel{name = <<"stanza-id">>,
- attrs = [{<<"by">>, LServer},
- {<<"xmlns">>, ?NS_SID_0},
- {<<"id">>, ID}]},
- NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
- NewPkt#xmlel{children = NewEls};
+ set_stanza_id(NewPkt, LServer, ID);
_ ->
NewPkt
end;
@@ -308,71 +272,98 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
Pkt
end.
+set_stanza_id(Pkt, LServer, ID) ->
+ Archived = #mam_archived{by = jid:make(LServer), id = ID},
+ StanzaID = #stanza_id{by = jid:make(LServer), id = ID},
+ NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)],
+ xmpp:set_els(Pkt, NewEls).
+
% Query archive v0.2
-process_iq_v0_2(#jid{lserver = LServer} = From,
- #jid{lserver = LServer} = To,
- #iq{type = get, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
- Fs = parse_query_v0_2(SubEl),
- process_iq(LServer, From, To, IQ, SubEl, Fs, chat);
-process_iq_v0_2(From, To, IQ) ->
- process_iq(From, To, IQ).
+process_iq_v0_2(#iq{from = #jid{lserver = LServer},
+ to = #jid{lserver = LServer},
+ type = get, sub_els = [#mam_query{}]} = IQ) ->
+ process_iq(LServer, IQ, chat);
+process_iq_v0_2(IQ) ->
+ process_iq(IQ).
% Query archive v0.3
-process_iq_v0_3(#jid{lserver = LServer} = From,
- #jid{lserver = LServer} = To,
- #iq{type = set, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
- process_iq(LServer, From, To, IQ, SubEl, get_xdata_fields(SubEl), chat);
-process_iq_v0_3(#jid{lserver = LServer},
- #jid{lserver = LServer},
- #iq{type = get, sub_el = #xmlel{name = <<"query">>}} = IQ) ->
+process_iq_v0_3(#iq{from = #jid{lserver = LServer},
+ to = #jid{lserver = LServer},
+ type = set, sub_els = [#mam_query{}]} = IQ) ->
+ process_iq(LServer, IQ, chat);
+process_iq_v0_3(#iq{from = #jid{lserver = LServer},
+ to = #jid{lserver = LServer},
+ type = get, sub_els = [#mam_query{}]} = IQ) ->
process_iq(LServer, IQ);
-process_iq_v0_3(From, To, IQ) ->
- process_iq(From, To, IQ).
-
-muc_process_iq(#iq{type = set,
- sub_el = #xmlel{name = <<"query">>,
- attrs = Attrs} = SubEl} = IQ,
- MUCState, From, To) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
- muc_process_iq(IQ, MUCState, From, To, get_xdata_fields(SubEl));
- _ ->
- IQ
- end;
-muc_process_iq(#iq{type = get,
- sub_el = #xmlel{name = <<"query">>,
- attrs = Attrs} = SubEl} = IQ,
- MUCState, From, To) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_MAM_TMP ->
- muc_process_iq(IQ, MUCState, From, To, parse_query_v0_2(SubEl));
- NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
+process_iq_v0_3(IQ) ->
+ process_iq(IQ).
+
+muc_process_iq(#iq{type = T, lang = Lang,
+ from = From,
+ sub_els = [#mam_query{xmlns = NS}]} = IQ,
+ MUCState)
+ when (T == set andalso (NS == ?NS_MAM_0 orelse NS == ?NS_MAM_1)) orelse
+ (T == get andalso NS == ?NS_MAM_TMP) ->
+ case may_enter_room(From, MUCState) of
+ true ->
LServer = MUCState#state.server_host,
- process_iq(LServer, IQ);
- _ ->
- IQ
+ Role = mod_muc_room:get_role(From, MUCState),
+ process_iq(LServer, IQ, {groupchat, Role, MUCState});
+ false ->
+ Text = <<"Only members may query archives of this room">>,
+ xmpp:make_error(IQ, xmpp:err_forbidden(Text, Lang))
end;
-muc_process_iq(IQ, _MUCState, _From, _To) ->
+muc_process_iq(#iq{type = get,
+ sub_els = [#mam_query{xmlns = NS}]} = IQ,
+ MUCState) when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
+ LServer = MUCState#state.server_host,
+ process_iq(LServer, IQ);
+muc_process_iq(IQ, _MUCState) ->
IQ.
-get_xdata_fields(SubEl) ->
- case {fxml:get_subtag_with_xmlns(SubEl, <<"x">>, ?NS_XDATA),
- fxml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
- {#xmlel{} = XData, false} ->
- jlib:parse_xdata_submit(XData);
- {#xmlel{} = XData, #xmlel{}} ->
- [{<<"set">>, SubEl} | jlib:parse_xdata_submit(XData)];
- {false, #xmlel{}} ->
- [{<<"set">>, SubEl}];
- {false, false} ->
- []
- end.
+parse_query(#mam_query{xdata = #xdata{fields = Fs}} = Query, Lang) ->
+ try
+ lists:foldl(
+ fun(#xdata_field{var = <<"start">>, values = [Data|_]}, Q) ->
+ case jlib:datetime_string_to_timestamp(Data) of
+ undefined -> throw({error, <<"start">>});
+ {_, _, _} = TS -> Q#mam_query{start = TS}
+ end;
+ (#xdata_field{var = <<"end">>, values = [Data|_]}, Q) ->
+ case jlib:datetime_string_to_timestamp(Data) of
+ undefined -> throw({error, <<"end">>});
+ {_, _, _} = TS -> Q#mam_query{'end' = TS}
+ end;
+ (#xdata_field{var = <<"with">>, values = [Data|_]}, Q) ->
+ case jid:from_string(Data) of
+ error -> throw({error, <<"with">>});
+ J -> Q#mam_query{with = J}
+ end;
+ (#xdata_field{var = <<"withtext">>, values = [Data|_]}, Q) ->
+ case Data of
+ <<"">> -> throw({error, <<"withtext">>});
+ _ -> Q#mam_query{withtext = Data}
+ end;
+ (#xdata_field{var = <<"FORM_TYPE">>, values = [NS|_]}, Q) ->
+ case Query#mam_query.xmlns of
+ NS -> Q;
+ _ -> throw({error, <<"FORM_TYPE">>})
+ end;
+ (#xdata_field{}, Acc) ->
+ Acc
+ end, Query, Fs)
+ catch throw:{error, Var} ->
+ Txt = io_lib:format("Incorrect value of field '~s'", [Var]),
+ {error, xmpp:err_bad_request(iolist_to_binary(Txt), Lang)}
+ end;
+parse_query(Query, _Lang) ->
+ Query.
disco_sm_features(empty, From, To, Node, Lang) ->
disco_sm_features({result, []}, From, To, Node, Lang);
disco_sm_features({result, OtherFeatures},
#jid{luser = U, lserver = S},
- #jid{luser = U, lserver = S}, <<>>, _Lang) ->
+ #jid{luser = U, lserver = S}, undefined, _Lang) ->
{result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1 | OtherFeatures]};
disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
@@ -440,210 +431,155 @@ delete_old_messages(_TypeBin, _Days) ->
%%% Internal functions
%%%===================================================================
-process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
- NS = case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_MAM_0 ->
- ?NS_MAM_0;
- _ ->
- ?NS_MAM_1
- end,
- CommonFields = [#xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"hidden">>},
- {<<"var">>, <<"FORM_TYPE">>}],
- children = [#xmlel{name = <<"value">>,
- children = [{xmlcdata, NS}]}]},
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"jid-single">>},
- {<<"var">>, <<"with">>}]},
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"text-single">>},
- {<<"var">>, <<"start">>}]},
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"text-single">>},
- {<<"var">>, <<"end">>}]}],
+process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) ->
+ CommonFields = [#xdata_field{type = hidden,
+ var = <<"FORM_TYPE">>,
+ values = [NS]},
+ #xdata_field{type = 'jid-single', var = <<"with">>},
+ #xdata_field{type = 'text-single', var = <<"start">>},
+ #xdata_field{type = 'text-single', var = <<"end">>}],
Mod = gen_mod:db_mod(LServer, ?MODULE),
ExtendedFields = Mod:extended_fields(),
- Fields = ExtendedFields ++ CommonFields,
- Form = #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children = Fields},
- IQ#iq{type = result,
- sub_el = [#xmlel{name = <<"query">>,
- attrs = [{<<"xmlns">>, NS}],
- children = [Form]}]}.
+ Fields = CommonFields ++ ExtendedFields,
+ Form = #xdata{type = form, fields = Fields},
+ xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = Form}).
% Preference setting (both v0.2 & v0.3)
-process_iq(#jid{luser = LUser, lserver = LServer},
- #jid{lserver = LServer},
- #iq{type = set, lang = Lang, sub_el = #xmlel{name = <<"prefs">>} = SubEl} = IQ) ->
- try {case fxml:get_tag_attr_s(<<"default">>, SubEl) of
- <<"always">> -> always;
- <<"never">> -> never;
- <<"roster">> -> roster
- end,
- lists:foldl(
- fun(#xmlel{name = <<"always">>, children = Els}, {A, N}) ->
- {get_jids(Els) ++ A, N};
- (#xmlel{name = <<"never">>, children = Els}, {A, N}) ->
- {A, get_jids(Els) ++ N};
- (_, {A, N}) ->
- {A, N}
- end, {[], []}, SubEl#xmlel.children)} of
- {Default, {Always0, Never0}} ->
- Always = lists:usort(Always0),
- Never = lists:usort(Never0),
- case write_prefs(LUser, LServer, LServer, Default, Always, Never) of
- ok ->
- NewPrefs = prefs_el(Default, Always, Never, IQ#iq.xmlns),
- IQ#iq{type = result, sub_el = [NewPrefs]};
- _Err ->
- Txt = <<"Database failure">>,
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]}
- end
- catch _:_ ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
+process_iq(#iq{type = set, lang = Lang,
+ sub_els = [#mam_prefs{default = undefined, xmlns = NS}]} = IQ) ->
+ Why = {missing_attr, <<"default">>, <<"prefs">>, NS},
+ ErrTxt = xmpp:format_error(Why),
+ xmpp:make_error(IQ, xmpp:err_bad_request(ErrTxt, Lang));
+process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
+ to = #jid{lserver = LServer},
+ type = set, lang = Lang,
+ sub_els = [#mam_prefs{xmlns = NS,
+ default = Default,
+ always = Always0,
+ never = Never0}]} = IQ) ->
+ Always = lists:usort(get_jids(Always0)),
+ Never = lists:usort(get_jids(Never0)),
+ case write_prefs(LUser, LServer, LServer, Default, Always, Never) of
+ ok ->
+ NewPrefs = prefs_el(Default, Always, Never, NS),
+ xmpp:make_iq_result(IQ, NewPrefs);
+ _Err ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end;
-process_iq(#jid{luser = LUser, lserver = LServer},
- #jid{lserver = LServer},
- #iq{type = get, sub_el = #xmlel{name = <<"prefs">>}} = IQ) ->
+process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
+ to = #jid{lserver = LServer},
+ type = get, sub_els = [#mam_prefs{xmlns = NS}]} = IQ) ->
Prefs = get_prefs(LUser, LServer),
PrefsEl = prefs_el(Prefs#archive_prefs.default,
Prefs#archive_prefs.always,
Prefs#archive_prefs.never,
- IQ#iq.xmlns),
- IQ#iq{type = result, sub_el = [PrefsEl]};
-process_iq(_, _, #iq{sub_el = SubEl} = IQ) ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
+ NS),
+ xmpp:make_iq_result(IQ, PrefsEl);
+process_iq(IQ) ->
+ xmpp:make_error(IQ, xmpp:err_not_allowed()).
-process_iq(LServer, #jid{luser = LUser} = From, To, IQ, SubEl, Fs, MsgType) ->
+process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang,
+ sub_els = [SubEl]} = IQ, MsgType) ->
case MsgType of
chat ->
maybe_activate_mam(LUser, LServer);
{groupchat, _Role, _MUCState} ->
ok
end,
- case catch lists:foldl(
- fun({<<"start">>, [Data|_]}, {_, End, With, RSM}) ->
- {{_, _, _} = jlib:datetime_string_to_timestamp(Data),
- End, With, RSM};
- ({<<"end">>, [Data|_]}, {Start, _, With, RSM}) ->
- {Start,
- {_, _, _} = jlib:datetime_string_to_timestamp(Data),
- With, RSM};
- ({<<"with">>, [Data|_]}, {Start, End, _, RSM}) ->
- {Start, End, jid:tolower(jid:from_string(Data)), RSM};
- ({<<"withtext">>, [Data|_]}, {Start, End, _, RSM}) ->
- {Start, End, {text, Data}, RSM};
- ({<<"set">>, El}, {Start, End, With, _}) ->
- {Start, End, With, jlib:rsm_decode(El)};
- (_, Acc) ->
- Acc
- end, {none, [], none, none}, Fs) of
- {'EXIT', _} ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]};
- {_Start, _End, _With, #rsm_in{index = Index}} when is_integer(Index) ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
- {Start, End, With, RSM} ->
- NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
- select_and_send(LServer, From, To, Start, End,
- With, limit_max(RSM, NS), IQ, MsgType)
+ case parse_query(SubEl, Lang) of
+ #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) ->
+ xmpp:make_error(IQ, xmpp:err_feature_not_implemented());
+ #mam_query{rsm = RSM, xmlns = NS} = Query ->
+ NewRSM = limit_max(RSM, NS),
+ NewQuery = Query#mam_query{rsm = NewRSM},
+ select_and_send(LServer, NewQuery, IQ, MsgType);
+ {error, Err} ->
+ xmpp:make_error(IQ, Err)
end.
-muc_process_iq(#iq{lang = Lang, sub_el = SubEl} = IQ, MUCState, From, To, Fs) ->
- case may_enter_room(From, MUCState) of
+should_archive(#message{type = T}, _LServer) when T == error; T == result ->
+ false;
+should_archive(#message{body = Body} = Pkt, LServer) ->
+ case is_resent(Pkt, LServer) of
true ->
- LServer = MUCState#state.server_host,
- Role = mod_muc_room:get_role(From, MUCState),
- process_iq(LServer, From, To, IQ, SubEl, Fs,
- {groupchat, Role, MUCState});
- false ->
- Text = <<"Only members may query archives of this room">>,
- Error = ?ERRT_FORBIDDEN(Lang, Text),
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end.
-
-parse_query_v0_2(Query) ->
- lists:flatmap(
- fun (#xmlel{name = <<"start">>} = El) ->
- [{<<"start">>, [fxml:get_tag_cdata(El)]}];
- (#xmlel{name = <<"end">>} = El) ->
- [{<<"end">>, [fxml:get_tag_cdata(El)]}];
- (#xmlel{name = <<"with">>} = El) ->
- [{<<"with">>, [fxml:get_tag_cdata(El)]}];
- (#xmlel{name = <<"withtext">>} = El) ->
- [{<<"withtext">>, [fxml:get_tag_cdata(El)]}];
- (#xmlel{name = <<"set">>}) ->
- [{<<"set">>, Query}];
- (_) ->
- []
- end, Query#xmlel.children).
-
-should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
- case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
- <<"error">> ->
false;
- <<"groupchat">> ->
- false;
- _ ->
- case is_resent(Pkt, LServer) of
- true ->
+ false ->
+ case check_store_hint(Pkt) of
+ store ->
+ true;
+ no_store ->
false;
- false ->
- case check_store_hint(Pkt) of
- store ->
- true;
- no_store ->
+ none ->
+ case xmpp:get_text(Body) of
+ <<>> ->
+ %% Empty body
false;
- none ->
- case fxml:get_subtag_cdata(Pkt, <<"body">>) of
- <<>> ->
- %% Empty body
- false;
- _ ->
- true
- end
+ _ ->
+ true
end
end
end;
-should_archive(#xmlel{}, _LServer) ->
+should_archive(_, _LServer) ->
false.
strip_my_archived_tag(Pkt, LServer) ->
+ NewPkt = xmpp:decode_els(
+ Pkt, fun(El) ->
+ case xmpp:get_name(El) of
+ <<"archived">> ->
+ xmpp:get_ns(El) == ?NS_MAM_TMP;
+ <<"stanza-id">> ->
+ xmpp:get_ns(El) == ?NS_SID_0;
+ _ ->
+ false
+ end
+ end),
NewEls = lists:filter(
- fun(#xmlel{name = Tag, attrs = Attrs})
- when Tag == <<"archived">>; Tag == <<"stanza-id">> ->
- case catch jid:nameprep(
- fxml:get_attr_s(
- <<"by">>, Attrs)) of
- LServer ->
- false;
- _ ->
- true
- end;
- (_) ->
- true
- end, Pkt#xmlel.children),
- Pkt#xmlel{children = NewEls}.
+ fun(#mam_archived{by = #jid{luser = <<>>} = By}) ->
+ By#jid.lserver /= LServer;
+ (#stanza_id{by = #jid{luser = <<>>} = By}) ->
+ By#jid.lserver /= LServer;
+ (_) ->
+ true
+ end, xmpp:get_els(NewPkt)),
+ xmpp:set_els(NewPkt, NewEls).
strip_x_jid_tags(Pkt) ->
+ NewPkt = xmpp:decode_els(
+ Pkt, fun(El) ->
+ case xmpp:get_name(El) of
+ <<"x">> ->
+ case xmpp:get_ns(El) of
+ ?NS_MUC_USER -> true;
+ ?NS_MUC_ADMIN -> true;
+ ?NS_MUC_OWNER -> true;
+ _ -> false
+ end;
+ _ ->
+ false
+ end
+ end),
NewEls = lists:filter(
- fun(#xmlel{name = <<"x">>} = XEl) ->
- not lists:any(fun(ItemEl) ->
- fxml:get_tag_attr(<<"jid">>, ItemEl)
- /= false
- end, fxml:get_subtags(XEl, <<"item">>));
- (_) ->
- true
- end, Pkt#xmlel.children),
- Pkt#xmlel{children = NewEls}.
+ fun(El) ->
+ Items = case El of
+ #muc_user{items = Is} -> Is;
+ #muc_admin{items = Is} -> Is;
+ #muc_owner{items = Is} -> Is;
+ _ -> []
+ end,
+ not lists:any(fun(#muc_item{jid = JID}) ->
+ JID /= undefined
+ end, Items)
+ end, xmpp:get_els(NewPkt)),
+ xmpp:set_els(NewPkt, NewEls).
should_archive_peer(C2SState,
#archive_prefs{default = Default,
always = Always,
never = Never},
Peer) ->
- LPeer = jid:tolower(Peer),
+ LPeer = jid:remove_resource(jid:tolower(Peer)),
case lists:member(LPeer, Always) of
true ->
true;
@@ -667,30 +603,30 @@ should_archive_peer(C2SState,
end
end.
-should_archive_muc(Pkt) ->
- case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
- <<"groupchat">> ->
- case check_store_hint(Pkt) of
- store ->
- true;
- no_store ->
- false;
- none ->
- case fxml:get_subtag_cdata(Pkt, <<"body">>) of
- <<>> ->
- case fxml:get_subtag_cdata(Pkt, <<"subject">>) of
- <<>> ->
- false;
- _ ->
- true
- end;
+should_archive_muc(#message{type = groupchat,
+ body = Body, subject = Subj} = Pkt) ->
+ case check_store_hint(Pkt) of
+ store ->
+ true;
+ no_store ->
+ false;
+ none ->
+ case xmpp:get_text(Body) of
+ <<"">> ->
+ case xmpp:get_text(Subj) of
+ <<"">> ->
+ false;
_ ->
true
- end
+ end;
+ _ ->
+ true
end;
_ ->
false
- end.
+ end;
+should_archive_muc(_) ->
+ false.
check_store_hint(Pkt) ->
case has_store_hint(Pkt) of
@@ -705,30 +641,24 @@ check_store_hint(Pkt) ->
end
end.
+
+-spec has_store_hint(message()) -> boolean().
has_store_hint(Message) ->
- fxml:get_subtag_with_xmlns(Message, <<"store">>, ?NS_HINTS)
- /= false.
+ xmpp:has_subtag(Message, #hint{type = 'store'}).
+-spec has_no_store_hint(message()) -> boolean().
has_no_store_hint(Message) ->
- fxml:get_subtag_with_xmlns(Message, <<"no-store">>, ?NS_HINTS)
- /= false orelse
- fxml:get_subtag_with_xmlns(Message, <<"no-storage">>, ?NS_HINTS)
- /= false orelse
- fxml:get_subtag_with_xmlns(Message, <<"no-permanent-store">>, ?NS_HINTS)
- /= false orelse
- fxml:get_subtag_with_xmlns(Message, <<"no-permanent-storage">>, ?NS_HINTS)
- /= false.
+ xmpp:has_subtag(Message, #hint{type = 'no-store'}) orelse
+ xmpp:has_subtag(Message, #hint{type = 'no-storage'}) orelse
+ xmpp:has_subtag(Message, #hint{type = 'no-permanent-store'}) orelse
+ xmpp:has_subtag(Message, #hint{type = 'no-permanent-storage'}).
+-spec is_resent(message(), binary()) -> boolean().
is_resent(Pkt, LServer) ->
- case fxml:get_subtag_with_xmlns(Pkt, <<"stanza-id">>, ?NS_SID_0) of
- #xmlel{attrs = Attrs} ->
- case fxml:get_attr(<<"by">>, Attrs) of
- {value, LServer} ->
- true;
- _ ->
- false
- end;
- false ->
+ case xmpp:get_subtag(Pkt, #stanza_id{}) of
+ #stanza_id{by = #jid{luser = <<>>, lserver = LServer}} ->
+ true;
+ _ ->
false
end.
@@ -744,7 +674,8 @@ store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
true ->
US = {LUser, LServer},
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:store(Pkt, LServer, US, chat, Peer, <<"">>, Dir);
+ El = xmpp:encode(Pkt),
+ Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir);
false ->
pass
end.
@@ -755,7 +686,8 @@ store_muc(MUCState, Pkt, RoomJID, Peer, Nick) ->
LServer = MUCState#state.server_host,
{U, S, _} = jid:tolower(RoomJID),
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv);
+ El = xmpp:encode(Pkt),
+ Mod:store(El, LServer, {U, S}, groupchat, Peer, Nick, recv);
false ->
pass
end.
@@ -796,20 +728,10 @@ get_prefs(LUser, LServer) ->
end.
prefs_el(Default, Always, Never, NS) ->
- Default1 = jlib:atom_to_binary(Default),
- JFun = fun(L) ->
- [#xmlel{name = <<"jid">>,
- children = [{xmlcdata, jid:to_string(J)}]}
- || J <- L]
- end,
- Always1 = #xmlel{name = <<"always">>,
- children = JFun(Always)},
- Never1 = #xmlel{name = <<"never">>,
- children = JFun(Never)},
- #xmlel{name = <<"prefs">>,
- attrs = [{<<"xmlns">>, NS},
- {<<"default">>, Default1}],
- children = [Always1, Never1]}.
+ #mam_prefs{default = Default,
+ always = [jid:make(LJ) || LJ <- Always],
+ never = [jid:make(LJ) || LJ <- Never],
+ xmlns = NS}.
maybe_activate_mam(LUser, LServer) ->
ActivateOpt = gen_mod:get_module_opt(LServer, ?MODULE,
@@ -838,21 +760,19 @@ maybe_activate_mam(LUser, LServer) ->
ok
end.
-select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType) ->
- {Msgs, IsComplete, Count} = select_and_start(LServer, From, To, Start, End,
- With, RSM, MsgType),
+select_and_send(LServer, Query, #iq{from = From, to = To} = IQ, MsgType) ->
+ {Msgs, IsComplete, Count} =
+ case MsgType of
+ chat ->
+ select(LServer, From, From, Query, MsgType);
+ {groupchat, _Role, _MUCState} ->
+ select(LServer, From, To, Query, MsgType)
+ end,
SortedMsgs = lists:keysort(2, Msgs),
- send(From, To, SortedMsgs, RSM, Count, IsComplete, IQ).
+ send(SortedMsgs, Count, IsComplete, IQ).
-select_and_start(LServer, From, To, Start, End, With, RSM, MsgType) ->
- case MsgType of
- chat ->
- select(LServer, From, From, Start, End, With, RSM, MsgType);
- {groupchat, _Role, _MUCState} ->
- select(LServer, From, To, Start, End, With, RSM, MsgType)
- end.
-
-select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
+select(_LServer, JidRequestor, JidArchive,
+ #mam_query{start = Start, 'end' = End, rsm = RSM},
{groupchat, _Role, #state{config = #config{mam = false},
history = History}} = MsgType) ->
#lqueue{len = L, queue = Q} = History,
@@ -864,7 +784,7 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
case match_interval(Now, Start, End) and
match_rsm(Now, RSM) of
true ->
- {[{jlib:integer_to_binary(TS), TS,
+ {[{integer_to_binary(TS), TS,
msg_to_el(#archive_msg{
type = groupchat,
timestamp = Now,
@@ -879,31 +799,27 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
end, 0, queue:to_list(Q)),
Msgs = lists:flatten(Msgs0),
case RSM of
- #rsm_in{max = Max, direction = before} ->
+ #rsm_set{max = Max, before = Before} when is_binary(Before) ->
{NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max),
{NewMsgs, IsComplete, L};
- #rsm_in{max = Max} ->
+ #rsm_set{max = Max} ->
{NewMsgs, IsComplete} = filter_by_max(Msgs, Max),
{NewMsgs, IsComplete, L};
_ ->
{Msgs, true, L}
end;
-select(LServer, JidRequestor, JidArchive, Start, End, With, RSM, MsgType) ->
+select(LServer, JidRequestor, JidArchive, Query, MsgType) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:select(LServer, JidRequestor, JidArchive, Start, End, With, RSM,
- MsgType).
+ Mod:select(LServer, JidRequestor, JidArchive, Query, MsgType).
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
Pkt2 = maybe_update_from_to(Pkt1, JidRequestor, JidArchive, Peer, MsgType,
Nick),
- Pkt3 = #xmlel{name = <<"forwarded">>,
- attrs = [{<<"xmlns">>, ?NS_FORWARD}],
- children = [fxml:replace_tag_attr(
- <<"xmlns">>, <<"jabber:client">>, Pkt2)]},
- jlib:add_delay_info(Pkt3, LServer, TS).
+ #forwarded{sub_els = [Pkt2],
+ delay = #delay{stamp = TS, from = jid:make(LServer)}}.
-maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor, JidArchive,
+maybe_update_from_to(#message{sub_els = Els} = Pkt, JidRequestor, JidArchive,
Peer, {groupchat, Role,
#state{config = #config{anonymous = Anon}}},
Nick) ->
@@ -919,18 +835,13 @@ maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor, JidArchive,
end,
Items = case ExposeJID of
true ->
- [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
- children =
- [#xmlel{name = <<"item">>,
- attrs = [{<<"jid">>,
- jid:to_string(Peer)}]}]}];
+ [#muc_user{items = [#muc_item{jid = Peer}]}];
false ->
[]
end,
- Pkt1 = Pkt#xmlel{children = Items ++ Els},
- Pkt2 = jlib:replace_from(jid:replace_resource(JidArchive, Nick), Pkt1),
- jlib:remove_attr(<<"to">>, Pkt2);
+ Pkt#message{from = jid:replace_resource(JidArchive, Nick),
+ to = undefined,
+ sub_els = Items ++ Els};
maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) ->
Pkt.
@@ -966,62 +877,46 @@ is_bare_copy(#jid{luser = U, lserver = S, lresource = R}, To) ->
false
end.
-send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
- QID = fxml:get_tag_attr_s(<<"queryid">>, SubEl),
- NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
- QIDAttr = if QID /= <<>> ->
- [{<<"queryid">>, QID}];
- true ->
- []
- end,
- CompleteAttr = if NS == ?NS_MAM_TMP ->
- [];
- NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
- [{<<"complete">>, jlib:atom_to_binary(IsComplete)}]
- end,
+-spec send([{binary(), integer(), xmlel()}],
+ non_neg_integer(), boolean(), iq()) -> iq() | ignore.
+send(Msgs, Count, IsComplete,
+ #iq{from = From, to = To,
+ sub_els = [#mam_query{id = QID, xmlns = NS}]} = IQ) ->
Els = lists:map(
fun({ID, _IDInt, El}) ->
- #xmlel{name = <<"message">>,
- children = [#xmlel{name = <<"result">>,
- attrs = [{<<"xmlns">>, NS},
- {<<"id">>, ID}|QIDAttr],
- children = [El]}]}
+ #message{sub_els = [#mam_result{xmlns = NS,
+ id = ID,
+ queryid = QID,
+ sub_els = [El]}]}
end, Msgs),
- RSMOut = make_rsm_out(Msgs, RSM, Count, QIDAttr ++ CompleteAttr, NS),
+ RSMOut = make_rsm_out(Msgs, Count),
+ Result = if NS == ?NS_MAM_TMP ->
+ #mam_query{xmlns = NS, id = QID, rsm = RSMOut};
+ true ->
+ #mam_fin{id = QID, rsm = RSMOut, complete = IsComplete}
+ end,
if NS == ?NS_MAM_TMP; NS == ?NS_MAM_1 ->
lists:foreach(
fun(El) ->
ejabberd_router:route(To, From, El)
end, Els),
- IQ#iq{type = result, sub_el = RSMOut};
+ xmpp:make_iq_result(IQ, Result);
NS == ?NS_MAM_0 ->
- ejabberd_router:route(
- To, From, jlib:iq_to_xml(IQ#iq{type = result, sub_el = []})),
+ ejabberd_router:route(To, From, xmpp:make_iq_result(IQ)),
lists:foreach(
fun(El) ->
ejabberd_router:route(To, From, El)
end, Els),
- ejabberd_router:route(
- To, From, #xmlel{name = <<"message">>,
- children = RSMOut}),
+ ejabberd_router:route(To, From, #message{sub_els = [Result]}),
ignore
end.
-make_rsm_out([], _, Count, Attrs, NS) ->
- Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
- true -> <<"fin">>
- end,
- [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
- children = jlib:rsm_encode(#rsm_out{count = Count})}];
-make_rsm_out([{FirstID, _, _}|_] = Msgs, _, Count, Attrs, NS) ->
+-spec make_rsm_out([{binary(), integer(), xmlel()}], non_neg_integer()) -> rsm_set().
+make_rsm_out([], Count) ->
+ #rsm_set{count = Count};
+make_rsm_out([{FirstID, _, _}|_] = Msgs, Count) ->
{LastID, _, _} = lists:last(Msgs),
- Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
- true -> <<"fin">>
- end,
- [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
- children = jlib:rsm_encode(
- #rsm_out{first = FirstID, count = Count,
- last = LastID})}].
+ #rsm_set{first = #rsm_first{data = FirstID}, last = LastID, count = Count}.
filter_by_max(Msgs, undefined) ->
{Msgs, true};
@@ -1030,23 +925,24 @@ filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
filter_by_max(_Msgs, _Junk) ->
{[], true}.
+-spec limit_max(rsm_set(), binary()) -> rsm_set().
limit_max(RSM, ?NS_MAM_TMP) ->
RSM; % XEP-0313 v0.2 doesn't require clients to support RSM.
-limit_max(#rsm_in{max = Max} = RSM, _NS) when not is_integer(Max) ->
- RSM#rsm_in{max = ?DEF_PAGE_SIZE};
-limit_max(#rsm_in{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE ->
- RSM#rsm_in{max = ?MAX_PAGE_SIZE};
+limit_max(#rsm_set{max = Max} = RSM, _NS) when not is_integer(Max) ->
+ RSM#rsm_set{max = ?DEF_PAGE_SIZE};
+limit_max(#rsm_set{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE ->
+ RSM#rsm_set{max = ?MAX_PAGE_SIZE};
limit_max(RSM, _NS) ->
RSM.
match_interval(Now, Start, End) ->
(Now >= Start) and (Now =< End).
-match_rsm(Now, #rsm_in{id = ID, direction = aft}) when ID /= <<"">> ->
- Now1 = (catch usec_to_now(jlib:binary_to_integer(ID))),
+match_rsm(Now, #rsm_set{'after' = ID}) when is_binary(ID), ID /= <<"">> ->
+ Now1 = (catch usec_to_now(binary_to_integer(ID))),
Now > Now1;
-match_rsm(Now, #rsm_in{id = ID, direction = before}) when ID /= <<"">> ->
- Now1 = (catch usec_to_now(jlib:binary_to_integer(ID))),
+match_rsm(Now, #rsm_set{before = ID}) when is_binary(ID), ID /= <<"">> ->
+ Now1 = (catch usec_to_now(binary_to_integer(ID))),
Now < Now1;
match_rsm(_Now, _) ->
true.
@@ -1066,15 +962,10 @@ datetime_to_now(DateTime, USecs) ->
calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
{Seconds div 1000000, Seconds rem 1000000, USecs}.
-get_jids(Els) ->
- lists:flatmap(
- fun(#xmlel{name = <<"jid">>} = El) ->
- J = jid:from_string(fxml:get_tag_cdata(El)),
- [jid:tolower(jid:remove_resource(J)),
- jid:tolower(J)];
- (_) ->
- []
- end, Els).
+get_jids(undefined) ->
+ [];
+get_jids(Js) ->
+ [jid:tolower(jid:remove_resource(J)) || J <- Js].
get_commands_spec() ->
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl
index be14d0fff..cbe7c336c 100644
--- a/src/mod_mam_mnesia.erl
+++ b/src/mod_mam_mnesia.erl
@@ -12,10 +12,10 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
- extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
+ extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
-include_lib("stdlib/include/ms_transform.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("logger.hrl").
-include("mod_mam.hrl").
@@ -132,7 +132,8 @@ get_prefs(LUser, LServer) ->
select(_LServer, JidRequestor,
#jid{luser = LUser, lserver = LServer} = JidArchive,
- Start, End, With, RSM, MsgType) ->
+ #mam_query{start = Start, 'end' = End,
+ with = With, rsm = RSM}, MsgType) ->
MS = make_matchspec(LUser, LServer, Start, End, With),
Msgs = mnesia:dirty_select(archive_msg, MS),
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
@@ -174,7 +175,7 @@ make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) ->
Peer == With ->
Msg
end);
-make_matchspec(LUser, LServer, Start, End, none) ->
+make_matchspec(LUser, LServer, Start, End, undefined) ->
ets:fun2ms(
fun(#archive_msg{timestamp = TS,
us = US,
@@ -184,28 +185,27 @@ make_matchspec(LUser, LServer, Start, End, none) ->
Msg
end).
-filter_by_rsm(Msgs, none) ->
+filter_by_rsm(Msgs, undefined) ->
{Msgs, true};
-filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 ->
+filter_by_rsm(_Msgs, #rsm_set{max = Max}) when Max < 0 ->
{[], true};
-filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
- NewMsgs = case Direction of
- aft when ID /= <<"">> ->
+filter_by_rsm(Msgs, #rsm_set{max = Max, before = Before, 'after' = After}) ->
+ NewMsgs = if is_binary(After), After /= <<"">> ->
lists:filter(
fun(#archive_msg{id = I}) ->
- ?BIN_GREATER_THAN(I, ID)
+ ?BIN_GREATER_THAN(I, After)
end, Msgs);
- before when ID /= <<"">> ->
+ is_binary(Before), Before /= <<"">> ->
lists:foldl(
fun(#archive_msg{id = I} = Msg, Acc)
- when ?BIN_LESS_THAN(I, ID) ->
+ when ?BIN_LESS_THAN(I, Before) ->
[Msg|Acc];
(_, Acc) ->
Acc
end, [], Msgs);
- before when ID == <<"">> ->
+ is_binary(Before), Before == <<"">> ->
lists:reverse(Msgs);
- _ ->
+ true ->
Msgs
end,
filter_by_max(NewMsgs, Max).
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index 20ed8d4f1..6e5231989 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -14,10 +14,10 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
- extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
+ extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
-include_lib("stdlib/include/ms_transform.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_mam.hrl").
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
@@ -51,9 +51,7 @@ delete_old_messages(ServerHost, TimeStamp, Type) ->
ok.
extended_fields() ->
- [#xmlel{name = <<"field">>,
- attrs = [{<<"type">>, <<"text-single">>},
- {<<"var">>, <<"withtext">>}]}].
+ [#xdata_field{type = 'text-single', var = <<"withtext">>}].
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) ->
TSinteger = p1_time_compat:system_time(micro_seconds),
@@ -126,13 +124,12 @@ get_prefs(LUser, LServer) ->
end.
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
- Start, End, With, RSM, MsgType) ->
+ MAMQuery, MsgType) ->
User = case MsgType of
chat -> LUser;
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
end,
- {Query, CountQuery} = make_sql_query(User, LServer,
- Start, End, With, RSM),
+ {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery),
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
% reasonable limit on how many stanzas may be pushed to a client in one
% request. If a query returns a number of stanzas greater than this limit
@@ -142,10 +139,7 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
case {ejabberd_sql:sql_query(LServer, Query),
ejabberd_sql:sql_query(LServer, CountQuery)} of
{{selected, _, Res}, {selected, _, [[Count]]}} ->
- {Max, Direction} = case RSM of
- #rsm_in{max = M, direction = D} -> {M, D};
- _ -> {undefined, undefined}
- end,
+ {Max, Direction, _} = get_max_direction_id(MAMQuery#mam_query.rsm),
{Res1, IsComplete} =
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
if Direction == before ->
@@ -200,15 +194,10 @@ usec_to_now(Int) ->
Sec = Secs rem 1000000,
{MSec, Sec, USec}.
-make_sql_query(User, LServer, Start, End, With, RSM) ->
- {Max, Direction, ID} = case RSM of
- #rsm_in{} ->
- {RSM#rsm_in.max,
- RSM#rsm_in.direction,
- RSM#rsm_in.id};
- none ->
- {none, none, <<>>}
- end,
+make_sql_query(User, LServer,
+ #mam_query{start = Start, 'end' = End, with = With,
+ withtext = WithText, rsm = RSM}) ->
+ {Max, Direction, ID} = get_max_direction_id(RSM),
ODBCType = ejabberd_config:get_option(
{sql_type, LServer},
ejabberd_sql:opt_type(sql_type)),
@@ -228,12 +217,16 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
true ->
[]
end,
- WithClause = case With of
- {text, <<>>} ->
- [];
- {text, Txt} ->
- [<<" and match (txt) against ('">>,
- Escape(Txt), <<"')">>];
+ WithTextClause = case WithText of
+ {text, <<>>} ->
+ [];
+ {text, Txt} ->
+ [<<" and match (txt) against ('">>,
+ Escape(Txt), <<"')">>];
+ undefined ->
+ []
+ end,
+ WithClause = case catch jid:tolower(With) of
{_, _, <<>>} ->
[<<" and bare_peer='">>,
Escape(jid:to_string(With)),
@@ -242,7 +235,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
[<<" and peer='">>,
Escape(jid:to_string(With)),
<<"'">>];
- none ->
+ _ ->
[]
end,
PageClause = case catch jlib:binary_to_integer(ID) of
@@ -250,7 +243,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
case Direction of
before ->
[<<" AND timestamp < ">>, ID];
- aft ->
+ 'after' ->
[<<" AND timestamp > ">>, ID];
_ ->
[]
@@ -276,7 +269,7 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
" FROM archive WHERE username='">>,
- SUser, <<"'">>, WithClause, StartClause, EndClause,
+ SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause,
PageClause],
QueryPage =
@@ -294,4 +287,20 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
end,
{QueryPage,
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
- SUser, <<"'">>, WithClause, StartClause, EndClause, <<";">>]}.
+ SUser, <<"'">>, WithClause, WithTextClause, StartClause, EndClause, <<";">>]}.
+
+-spec get_max_direction_id(rsm_set() | undefined) ->
+ {integer() | undefined,
+ before | 'after' | undefined,
+ binary()}.
+get_max_direction_id(RSM) ->
+ case RSM of
+ #rsm_set{max = Max, before = Before} when is_binary(Before) ->
+ {Max, before, Before};
+ #rsm_set{max = Max, 'after' = After} when is_binary(After) ->
+ {Max, 'after', After};
+ #rsm_set{max = Max} ->
+ {Max, undefined, <<>>};
+ _ ->
+ {undefined, undefined, <<>>}
+ end.
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index a86f580d3..294456ee2 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -43,7 +43,12 @@
forget_room/3,
create_room/5,
shutdown_rooms/1,
- process_iq_disco_items/4,
+ process_disco_info/1,
+ process_disco_items/1,
+ process_vcard/1,
+ process_register/1,
+ process_muc_unique/1,
+ process_mucsub/1,
broadcast_service_message/2,
export/1,
import/1,
@@ -58,7 +63,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_muc.hrl").
-record(state,
@@ -154,17 +159,6 @@ forget_room(ServerHost, Host, Name) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:forget_room(LServer, Host, Name).
-process_iq_disco_items(Host, From, To,
- #iq{lang = Lang} = IQ) ->
- Rsm = jlib:rsm_decode(IQ),
- DiscoNode = fxml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el),
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}],
- children = iq_disco_items(Host, From, Lang, DiscoNode, Rsm)}]},
- ejabberd_router:route(To, From, jlib:iq_to_xml(Res)).
-
can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false;
can_use_nick(ServerHost, Host, JID, Nick) ->
LServer = jid:nameprep(ServerHost),
@@ -176,6 +170,8 @@ can_use_nick(ServerHost, Host, JID, Nick) ->
%%====================================================================
init([Host, Opts]) ->
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
+ one_queue),
MyHost = gen_mod:get_opt_host(Host, Opts,
<<"conference.@HOST@">>),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
@@ -255,6 +251,18 @@ init([Host, Opts]) ->
RoomShaper = gen_mod:get_opt(room_shaper, Opts,
fun(A) when is_atom(A) -> A end,
none),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER,
+ ?MODULE, process_register, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD,
+ ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB,
+ ?MODULE, process_mucsub, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE,
+ ?MODULE, process_muc_unique, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
+ ?MODULE, process_disco_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS,
+ ?MODULE, process_disco_items, IQDisc),
ejabberd_router:register_route(MyHost, Host),
load_permanent_rooms(MyHost, Host,
{Access, AccessCreate, AccessAdmin, AccessPersistent},
@@ -314,8 +322,14 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
{noreply, State};
handle_info(_Info, State) -> {noreply, State}.
-terminate(_Reason, State) ->
- ejabberd_router:unregister_route(State#state.host),
+terminate(_Reason, #state{host = MyHost}) ->
+ ejabberd_router:unregister_route(MyHost),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
@@ -331,197 +345,162 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
allow ->
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
From, To, Packet, DefRoomOpts);
- _ ->
- #xmlel{attrs = Attrs} = Packet,
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+ deny ->
+ Lang = xmpp:get_lang(Packet),
ErrText = <<"Access denied by service policy">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route_error(To, From, Err, Packet)
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ ejabberd_router:route_error(To, From, Packet, Err)
end.
-
+do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
+ From, #jid{luser = <<"">>, lresource = <<"">>} = To,
+ #iq{} = IQ, _DefRoomOpts) ->
+ ejabberd_local:process_iq(From, To, IQ);
+do_route1(Host, ServerHost, Access, _HistorySize, _RoomShaper,
+ From, #jid{luser = <<"">>, lresource = <<"">>} = To,
+ #message{lang = Lang, body = Body, type = Type} = Packet, _) ->
+ {_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = Access,
+ if Type == error ->
+ ok;
+ true ->
+ case acl:match_rule(ServerHost, AccessAdmin, From) of
+ allow ->
+ Msg = xmpp:get_text(Body),
+ broadcast_service_message(Host, Msg);
+ deny ->
+ ErrText = <<"Only service administrators are allowed "
+ "to send service messages">>,
+ Err = xmpp:make_error(
+ Packet, xmpp:err_forbidden(ErrText, Lang)),
+ ejabberd_router:route(To, From, Err)
+ end
+ end;
+do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
+ From, #jid{luser = <<"">>} = To, Packet, _DefRoomOpts) ->
+ Err = xmpp:err_item_not_found(),
+ ejabberd_router:route_error(To, From, Packet, Err);
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
From, To, Packet, DefRoomOpts) ->
- {_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access,
+ {_AccessRoute, AccessCreate, _AccessAdmin, _AccessPersistent} = Access,
{Room, _, Nick} = jid:tolower(To),
- #xmlel{name = Name, attrs = Attrs} = Packet,
- case Room of
- <<"">> ->
- case Nick of
- <<"">> ->
- case Name of
- <<"iq">> ->
- case jlib:iq_query_info(Packet) of
- #iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS,
- sub_el = _SubEl, lang = Lang} =
- IQ ->
- Info = ejabberd_hooks:run_fold(disco_info,
- ServerHost, [],
- [ServerHost, ?MODULE,
- <<"">>, <<"">>]),
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, XMLNS}],
- children =
- iq_disco_info(
- ServerHost, Lang) ++
- Info}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(Res));
- #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ ->
- spawn(?MODULE, process_iq_disco_items,
- [Host, From, To, IQ]);
- #iq{type = get, xmlns = (?NS_REGISTER) = XMLNS,
- lang = Lang, sub_el = _SubEl} =
- IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, XMLNS}],
- children =
- iq_get_register_info(ServerHost,
- Host,
- From,
- Lang)}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(Res));
- #iq{type = set, xmlns = (?NS_REGISTER) = XMLNS,
- lang = Lang, sub_el = SubEl} =
- IQ ->
- case process_iq_register_set(ServerHost, Host, From,
- SubEl, Lang)
- of
- {result, IQRes} ->
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>,
- XMLNS}],
- children = IQRes}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(Res));
- {error, Error} ->
- Err = jlib:make_error_reply(Packet, Error),
- ejabberd_router:route(To, From, Err)
- end;
- #iq{type = get, xmlns = (?NS_VCARD) = XMLNS,
- lang = Lang, sub_el = _SubEl} =
- IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"vCard">>,
- attrs =
- [{<<"xmlns">>, XMLNS}],
- children =
- iq_get_vcard(Lang)}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(Res));
- #iq{type = get, xmlns = ?NS_MUCSUB,
- sub_el = #xmlel{name = <<"subscriptions">>} = SubEl} = IQ ->
- RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
- Subs = lists:map(
- fun(J) ->
- #xmlel{name = <<"subscription">>,
- attrs = [{<<"jid">>,
- jid:to_string(J)}]}
- end, RoomJIDs),
- Res = IQ#iq{type = result,
- sub_el = [SubEl#xmlel{children = Subs}]},
- ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
- #iq{type = get, xmlns = ?NS_MUC_UNIQUE} = IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"unique">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_UNIQUE}],
- children =
- [iq_get_unique(From)]}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(Res));
- #iq{} ->
- Err = jlib:make_error_reply(Packet,
- ?ERR_FEATURE_NOT_IMPLEMENTED),
- ejabberd_router:route(To, From, Err);
- _ -> ok
- end;
- <<"message">> ->
- case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"error">> -> ok;
- _ ->
- case acl:match_rule(ServerHost, AccessAdmin, From)
- of
- allow ->
- Msg = fxml:get_path_s(Packet,
- [{elem, <<"body">>},
- cdata]),
- broadcast_service_message(Host, Msg);
- _ ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- ErrText =
- <<"Only service administrators are allowed "
- "to send service messages">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang,
- ErrText)),
- ejabberd_router:route(To, From, Err)
- end
- end;
- <<"presence">> -> ok
- end;
- _ ->
- case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"error">> -> ok;
- <<"result">> -> ok;
- _ ->
- Err = jlib:make_error_reply(Packet,
- ?ERR_ITEM_NOT_FOUND),
- ejabberd_router:route(To, From, Err)
- end
- end;
- _ ->
- case mnesia:dirty_read(muc_online_room, {Room, Host}) of
- [] ->
- Type = fxml:get_attr_s(<<"type">>, Attrs),
- case {Name, Type} of
- {<<"presence">>, <<"">>} ->
- case check_user_can_create_room(ServerHost,
- AccessCreate, From, Room) and
- check_create_roomid(ServerHost, Room) of
- true ->
- {ok, Pid} = start_new_room(Host, ServerHost, Access,
- Room, HistorySize,
- RoomShaper, From, Nick, DefRoomOpts),
- register_room(Host, Room, Pid),
- mod_muc_room:route(Pid, From, Nick, Packet),
- ok;
- false ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- ErrText = <<"Room creation is denied by service policy">>,
- Err = jlib:make_error_reply(
- Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(To, From, Err)
- end;
- _ ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- ErrText = <<"Conference room does not exist">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
+ case mnesia:dirty_read(muc_online_room, {Room, Host}) of
+ [] ->
+ case Packet of
+ #presence{type = available, lang = Lang} ->
+ case check_user_can_create_room(
+ ServerHost, AccessCreate, From, Room) and
+ check_create_roomid(ServerHost, Room) of
+ true ->
+ {ok, Pid} = start_new_room(
+ Host, ServerHost, Access,
+ Room, HistorySize,
+ RoomShaper, From, Nick, DefRoomOpts),
+ register_room(Host, Room, Pid),
+ mod_muc_room:route(Pid, From, Nick, Packet),
+ ok;
+ false ->
+ ErrText = <<"Room creation is denied by service policy">>,
+ Err = xmpp:make_error(
+ Packet, xmpp:err_forbidden(ErrText, Lang)),
ejabberd_router:route(To, From, Err)
end;
- [R] ->
- Pid = R#muc_online_room.pid,
- ?DEBUG("MUC: send to process ~p~n", [Pid]),
- mod_muc_room:route(Pid, From, Nick, Packet),
- ok
- end
+ _ ->
+ Lang = xmpp:get_lang(Packet),
+ ErrText = <<"Conference room does not exist">>,
+ Err = xmpp:err_item_not_found(ErrText, Lang),
+ ejabberd_router:route_error(To, From, Packet, Err)
+ end;
+ [R] ->
+ Pid = R#muc_online_room.pid,
+ ?DEBUG("MUC: send to process ~p~n", [Pid]),
+ mod_muc_room:route(Pid, From, Nick, Packet),
+ ok
+ end.
+
+-spec process_vcard(iq()) -> iq().
+process_vcard(#iq{type = get, lang = Lang} = IQ) ->
+ Desc = translate:translate(Lang, <<"ejabberd MUC module">>),
+ Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
+ xmpp:make_iq_result(
+ IQ, #vcard_temp{fn = <<"ejabberd/mod_muc">>,
+ url = ?EJABBERD_URI,
+ desc = <<Desc/binary, $\n, Copyright/binary>>});
+process_vcard(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)).
+
+-spec process_register(iq()) -> iq().
+process_register(#iq{type = get, from = From, to = To, lang = Lang} = IQ) ->
+ Host = To#jid.lserver,
+ ServerHost = ejabberd_router:host_of_route(Host),
+ xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
+process_register(#iq{type = set, from = From, to = To,
+ lang = Lang, sub_els = [El]} = IQ) ->
+ Host = To#jid.lserver,
+ ServerHost = ejabberd_router:host_of_route(Host),
+ case process_iq_register_set(ServerHost, Host, From, El, Lang) of
+ {result, Result} ->
+ xmpp:make_iq_result(IQ, Result);
+ {error, Err} ->
+ xmpp:make_error(IQ, Err)
end.
+-spec process_disco_info(iq()) -> iq().
+process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_disco_info(#iq{type = get, to = To, lang = Lang,
+ sub_els = [#disco_info{node = undefined}]} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
+ [ServerHost, ?MODULE, undefined, Lang]),
+ MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of
+ true -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1];
+ false -> []
+ end,
+ Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
+ ?NS_REGISTER, ?NS_MUC, ?NS_RSM,
+ ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE | MAMFeatures],
+ Identity = #identity{category = <<"conference">>,
+ type = <<"text">>,
+ name = translate:translate(Lang, <<"Chatrooms">>)},
+ xmpp:make_iq_result(
+ IQ, #disco_info{features = Features,
+ identities = [Identity],
+ xdata = X});
+process_disco_info(#iq{type = get, lang = Lang} = IQ) ->
+ xmpp:make_error(IQ, xmpp:err_item_not_found(<<"No info available">>, Lang)).
+
+-spec process_disco_items(iq()) -> iq().
+process_disco_items(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
+ sub_els = [#disco_items{node = Node, rsm = RSM}]} = IQ) ->
+ Host = To#jid.lserver,
+ xmpp:make_iq_result(
+ IQ, #disco_items{node = Node,
+ items = iq_disco_items(Host, From, Lang, Node, RSM)}).
+
+-spec process_muc_unique(iq()) -> iq().
+process_muc_unique(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_muc_unique(#iq{from = From, type = get} = IQ) ->
+ Name = p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
+ randoms:get_string()])),
+ xmpp:make_iq_result(IQ, #muc_unique{name = Name}).
+
+-spec process_mucsub(iq()) -> iq().
+process_mucsub(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_mucsub(#iq{type = get, from = From, to = To} = IQ) ->
+ Host = To#jid.lserver,
+ ServerHost = ejabberd_router:host_of_route(Host),
+ RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
+ xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs}).
+
check_user_can_create_room(ServerHost, AccessCreate,
From, _RoomID) ->
case acl:match_rule(ServerHost, AccessCreate, From) of
@@ -583,61 +562,21 @@ register_room(Host, Room, Pid) ->
end,
mnesia:transaction(F).
-
-iq_disco_info(ServerHost, Lang) ->
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"conference">>},
- {<<"type">>, <<"text">>},
- {<<"name">>,
- translate:translate(Lang, <<"Chatrooms">>)}],
- children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MUC}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MUC_UNIQUE}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_REGISTER}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_RSM}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MUCSUB}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_VCARD}], children = []}] ++
- case gen_mod:is_loaded(ServerHost, mod_mam) of
- true ->
- [#xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MAM_TMP}]},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MAM_0}]},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MAM_1}]}];
- false ->
- []
- end.
-
-iq_disco_items(Host, From, Lang, <<>>, none) ->
+iq_disco_items(Host, From, Lang, undefined, undefined) ->
Rooms = get_vh_rooms(Host),
case erlang:length(Rooms) < ?MAX_ROOMS_DISCOITEMS of
true ->
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang});
false ->
- iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, none)
+ iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, undefined)
end;
-iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, none) ->
- XmlEmpty = #xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>, <<"conference.localhost">>},
- {<<"node">>, <<"emptyrooms">>},
- {<<"name">>, translate:translate(Lang, <<"Empty Rooms">>)}],
- children = []},
+iq_disco_items(Host, From, Lang, <<"nonemptyrooms">>, undefined) ->
+ Empty = #disco_item{jid = jid:make(<<"conference.localhost">>),
+ node = <<"emptyrooms">>,
+ name = translate:translate(Lang, <<"Empty Rooms">>)},
Query = {get_disco_item, only_non_empty, From, Lang},
- [XmlEmpty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
-iq_disco_items(Host, From, Lang, <<"emptyrooms">>, none) ->
+ [Empty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
+iq_disco_items(Host, From, Lang, <<"emptyrooms">>, undefined) ->
iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang});
iq_disco_items(Host, From, Lang, _DiscoNode, Rsm) ->
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
@@ -645,62 +584,55 @@ iq_disco_items(Host, From, Lang, _DiscoNode, Rsm) ->
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut.
iq_disco_items_list(Host, Rooms, Query) ->
- lists:zf(fun (#muc_online_room{name_host =
- {Name, _Host},
- pid = Pid}) ->
- case catch gen_fsm:sync_send_all_state_event(Pid,
- Query,
- 100)
- of
- {item, Desc} ->
- flush(),
- {true,
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>,
- jid:to_string({Name, Host,
- <<"">>})},
- {<<"name">>, Desc}],
- children = []}};
- _ -> false
- end
- end, Rooms).
-
-get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
- AllRooms = lists:sort(get_vh_rooms(Host)),
- Count = erlang:length(AllRooms),
- Guard = case Direction of
- _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
- aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
- before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
- _ -> [{'==', {element, 2, '$1'}, Host}]
- end,
- L = lists:sort(
- mnesia:dirty_select(muc_online_room,
- [{#muc_online_room{name_host = '$1', _ = '_'},
- Guard,
- ['$_']}])),
- L2 = if
- Index == undefined andalso Direction == before ->
- lists:reverse(lists:sublist(lists:reverse(L), 1, M));
- Index == undefined ->
- lists:sublist(L, 1, M);
- Index > Count orelse Index < 0 ->
- [];
- true ->
- lists:sublist(L, Index+1, M)
- end,
- if L2 == [] -> {L2, #rsm_out{count = Count}};
- true ->
- H = hd(L2),
- NewIndex = get_room_pos(H, AllRooms),
- T = lists:last(L2),
- {F, _} = H#muc_online_room.name_host,
- {Last, _} = T#muc_online_room.name_host,
- {L2,
- #rsm_out{first = F, last = Last, count = Count,
- index = NewIndex}}
- end.
+ lists:zf(
+ fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
+ case catch gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
+ {item, Desc} ->
+ flush(),
+ {true, #disco_item{jid = jid:make(Name, Host),
+ name = Desc}};
+ _ ->
+ false
+ end
+ end, Rooms).
+
+get_vh_rooms(_, _) ->
+ todo.
+%% get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
+%% AllRooms = lists:sort(get_vh_rooms(Host)),
+%% Count = erlang:length(AllRooms),
+%% Guard = case Direction of
+%% _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
+%% aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
+%% before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
+%% _ -> [{'==', {element, 2, '$1'}, Host}]
+%% end,
+%% L = lists:sort(
+%% mnesia:dirty_select(muc_online_room,
+%% [{#muc_online_room{name_host = '$1', _ = '_'},
+%% Guard,
+%% ['$_']}])),
+%% L2 = if
+%% Index == undefined andalso Direction == before ->
+%% lists:reverse(lists:sublist(lists:reverse(L), 1, M));
+%% Index == undefined ->
+%% lists:sublist(L, 1, M);
+%% Index > Count orelse Index < 0 ->
+%% [];
+%% true ->
+%% lists:sublist(L, Index+1, M)
+%% end,
+%% if L2 == [] -> {L2, #rsm_out{count = Count}};
+%% true ->
+%% H = hd(L2),
+%% NewIndex = get_room_pos(H, AllRooms),
+%% T = lists:last(L2),
+%% {F, _} = H#muc_online_room.name_host,
+%% {Last, _} = T#muc_online_room.name_host,
+%% {L2,
+%% #rsm_out{first = F, last = Last, count = Count,
+%% index = NewIndex}}
+%% end.
get_subscribed_rooms(ServerHost, Host, From) ->
Rooms = get_rooms(ServerHost, Host),
@@ -730,60 +662,32 @@ get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
flush() -> receive _ -> flush() after 0 -> ok end.
--define(XFIELD(Type, Label, Var, Val),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
-
-iq_get_unique(From) ->
- {xmlcdata,
- p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
- randoms:get_string()]))}.
-
get_nick(ServerHost, Host, From) ->
LServer = jid:nameprep(ServerHost),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:get_nick(LServer, Host, From).
iq_get_register_info(ServerHost, Host, From, Lang) ->
- {Nick, Registered} = case get_nick(ServerHost, Host,
- From)
- of
- error -> {<<"">>, []};
- N ->
- {N,
- [#xmlel{name = <<"registered">>, attrs = [],
- children = []}]}
+ {Nick, Registered} = case get_nick(ServerHost, Host, From) of
+ error -> {<<"">>, false};
+ N -> {N, true}
end,
- Registered ++
- [#xmlel{name = <<"instructions">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"You need a client that supports x:data "
- "to register the nickname">>)}]},
- #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA},
- {<<"type">>, <<"form">>}],
- children =
- [#xmlel{name = <<"title">>, attrs = [],
- children =
- [{xmlcdata,
- <<(translate:translate(Lang,
- <<"Nickname Registration at ">>))/binary,
- Host/binary>>}]},
- #xmlel{name = <<"instructions">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"Enter nickname you want to register">>)}]},
- ?XFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>,
- Nick)]}].
+ Title = <<(translate:translate(
+ Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>,
+ Inst = translate:translate(Lang, <<"Enter nickname you want to register">>),
+ Field = #xdata_field{type = 'text-single',
+ label = translate:translate(Lang, <<"Nickname">>),
+ var = <<"nick">>,
+ values = [Nick]},
+ X = #xdata{type = form, title = Title,
+ instructions = [Inst], fields = [Field]},
+ #register{nick = Nick,
+ registered = Registered,
+ instructions =
+ translate:translate(
+ Lang, <<"You need a client that supports x:data "
+ "to register the nickname">>),
+ xdata = X}.
set_nick(ServerHost, Host, From, Nick) ->
LServer = jid:nameprep(ServerHost),
@@ -793,66 +697,43 @@ set_nick(ServerHost, Host, From, Nick) ->
iq_set_register_info(ServerHost, Host, From, Nick,
Lang) ->
case set_nick(ServerHost, Host, From, Nick) of
- {atomic, ok} -> {result, []};
+ {atomic, ok} -> {result, undefined};
{atomic, false} ->
ErrText = <<"That nickname is registered by another "
"person">>,
- {error, ?ERRT_CONFLICT(Lang, ErrText)};
+ {error, xmpp:err_conflict(ErrText, Lang)};
_ ->
Txt = <<"Database failure">>,
- {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)}
+ {error, xmpp:err_internal_server_error(Txt, Lang)}
end.
-process_iq_register_set(ServerHost, Host, From, SubEl,
- Lang) ->
- #xmlel{children = Els} = SubEl,
- case fxml:get_subtag(SubEl, <<"remove">>) of
- false ->
- case fxml:remove_cdata(Els) of
- [#xmlel{name = <<"x">>} = XEl] ->
- case {fxml:get_tag_attr_s(<<"xmlns">>, XEl),
- fxml:get_tag_attr_s(<<"type">>, XEl)}
- of
- {?NS_XDATA, <<"cancel">>} -> {result, []};
- {?NS_XDATA, <<"submit">>} ->
- XData = jlib:parse_xdata_submit(XEl),
- case XData of
- invalid ->
- Txt = <<"Incorrect data form">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
- _ ->
- case lists:keysearch(<<"nick">>, 1, XData) of
- {value, {_, [Nick]}} when Nick /= <<"">> ->
- iq_set_register_info(ServerHost, Host, From,
- Nick, Lang);
- _ ->
- ErrText =
- <<"You must fill in field \"Nickname\" "
- "in the form">>,
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
- end
- end;
- _ -> {error, ?ERR_BAD_REQUEST}
- end;
- _ -> {error, ?ERR_BAD_REQUEST}
- end;
- _ ->
- iq_set_register_info(ServerHost, Host, From, <<"">>,
- Lang)
+process_iq_register_set(ServerHost, Host, From,
+ #register{remove = true}, Lang) ->
+ iq_set_register_info(ServerHost, Host, From, <<"">>, Lang);
+process_iq_register_set(_ServerHost, _Host, _From,
+ #register{xdata = #xdata{type = cancel}}, _Lang) ->
+ {result, undefined};
+process_iq_register_set(ServerHost, Host, From,
+ #register{nick = Nick, xdata = XData}, Lang) ->
+ case XData of
+ #xdata{type = submit, fields = Fs} ->
+ case lists:keyfind(<<"nick">>, #xdata_field.var, Fs) of
+ #xdata_field{values = [N]} ->
+ iq_set_register_info(ServerHost, Host, From, N, Lang);
+ _ ->
+ ErrText = <<"You must fill in field \"Nickname\" in the form">>,
+ {error, xmpp:err_not_acceptable(ErrText, Lang)}
+ end;
+ #xdata{} ->
+ Txt = <<"Incorrect data form">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
+ _ when is_binary(Nick), Nick /= <<"">> ->
+ iq_set_register_info(ServerHost, Host, From, Nick, Lang);
+ _ ->
+ ErrText = <<"You must fill in field \"Nickname\" in the form">>,
+ {error, xmpp:err_not_acceptable(ErrText, Lang)}
end.
-iq_get_vcard(Lang) ->
- [#xmlel{name = <<"FN">>, attrs = [],
- children = [{xmlcdata, <<"ejabberd/mod_muc">>}]},
- #xmlel{name = <<"URL">>, attrs = [],
- children = [{xmlcdata, ?EJABBERD_URI}]},
- #xmlel{name = <<"DESC">>, attrs = [],
- children =
- [{xmlcdata,
- <<(translate:translate(Lang,
- <<"ejabberd MUC module">>))/binary,
- "\nCopyright (c) 2003-2016 ProcessOne">>}]}].
-
broadcast_service_message(Host, Msg) ->
lists:foreach(
fun(#muc_online_room{pid = Pid}) ->
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index 0b5e79f60..a7ba16138 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -24,7 +24,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_muc_room.hrl").
-include("mod_muc.hrl").
-include("ejabberd_http.hrl").
@@ -241,7 +241,7 @@ web_menu_host(Acc, _Host, Lang) ->
-define(TDTD(L, N),
?XE(<<"tr">>, [?XCT(<<"td">>, L),
- ?XC(<<"td">>, jlib:integer_to_binary(N))
+ ?XC(<<"td">>, integer_to_binary(N))
])).
web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) ->
@@ -283,7 +283,7 @@ get_sort_query(Q) ->
get_sort_query2(Q) ->
{value, {_, String}} = lists:keysearch(<<"sort">>, 1, Q),
- Integer = jlib:binary_to_integer(String),
+ Integer = binary_to_integer(String),
case Integer >= 0 of
true -> {ok, {normal, Integer}};
false -> {ok, {reverse, abs(Integer)}}
@@ -309,7 +309,7 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
{Titles_TR, _} =
lists:mapfoldl(
fun(Title, Num_column) ->
- NCS = jlib:integer_to_binary(Num_column),
+ NCS = integer_to_binary(Num_column),
TD = ?XE(<<"td">>, [?CT(Title),
?C(<<" ">>),
?AC(<<"?sort=", NCS/binary>>, <<"<">>),
@@ -383,7 +383,7 @@ prepare_room_info(Room_info) ->
Just_created,
Title} = Room_info,
[NameHost,
- jlib:integer_to_binary(Num_participants),
+ integer_to_binary(Num_participants),
Ts_last_message,
jlib:atom_to_binary(Public),
jlib:atom_to_binary(Persistent),
@@ -830,7 +830,7 @@ get_options(Config) ->
Fields = [jlib:atom_to_binary(Field) || Field <- record_info(fields, config)],
[config | ValuesRaw] = tuple_to_list(Config),
Values = lists:map(fun(V) when is_atom(V) -> jlib:atom_to_binary(V);
- (V) when is_integer(V) -> jlib:integer_to_binary(V);
+ (V) when is_integer(V) -> integer_to_binary(V);
(V) when is_tuple(V); is_list(V) -> list_to_binary(hd(io_lib:format("~w", [V])));
(V) -> V end, ValuesRaw),
lists:zip(Fields, Values).
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index ec4711b43..4b129ce81 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -46,7 +46,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_muc.hrl").
-include("mod_muc_room.hrl").
@@ -196,15 +196,13 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
case has_no_permanent_store_hint(Packet) of
false ->
- case {fxml:get_subtag(Packet, <<"subject">>),
- fxml:get_subtag(Packet, <<"body">>)}
- of
- {false, false} -> ok;
- {false, SubEl} ->
- Message = {body, fxml:get_tag_cdata(SubEl)},
+ case {Packet#message.subject, Packet#message.body} of
+ {[], []} -> ok;
+ {[], Body} ->
+ Message = {body, xmpp:get_text(Body)},
add_message_to_log(Nick, Message, Room, Opts, State);
- {SubEl, _} ->
- Message = {subject, fxml:get_tag_cdata(SubEl)},
+ {Subj, _} ->
+ Message = {subject, xmpp:get_text(Subj)},
add_message_to_log(Nick, Message, Room, Opts, State)
end;
true -> ok
@@ -1035,7 +1033,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
max_users ->
<<"<div class=\"rcot\">",
OptText/binary, ": \"",
- (htmlize(jlib:integer_to_binary(T),
+ (htmlize(integer_to_binary(T),
FileFormat))/binary,
"\"</div>">>;
title ->
@@ -1053,7 +1051,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
allow_private_messages_from_visitors ->
<<"<div class=\"rcot\">",
OptText/binary, ": \"",
- (htmlize(?T((jlib:atom_to_binary(T))),
+ (htmlize(?T(jlib:atom_to_binary(T)),
FileFormat))/binary,
"\"</div>">>;
_ -> <<"\"", T/binary, "\"">>
@@ -1168,7 +1166,7 @@ get_room_occupants(RoomJIDString) ->
[{U#user.jid, U#user.nick, U#user.role}
|| {_, U} <- (?DICT):to_list(StateData#state.users)].
--spec get_room_state(binary(), binary()) -> muc_room_state().
+-spec get_room_state(binary(), binary()) -> mod_muc_room:state().
get_room_state(RoomName, MucService) ->
case mnesia:dirty_read(muc_online_room,
@@ -1180,7 +1178,7 @@ get_room_state(RoomName, MucService) ->
[] -> #state{}
end.
--spec get_room_state(pid()) -> muc_room_state().
+-spec get_room_state(pid()) -> mod_muc_room:state().
get_room_state(RoomPid) ->
{ok, R} = gen_fsm:sync_send_all_state_event(RoomPid,
@@ -1204,14 +1202,10 @@ fjoin(FileList) ->
list_to_binary(filename:join([binary_to_list(File) || File <- FileList])).
has_no_permanent_store_hint(Packet) ->
- fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-store">>, ?NS_HINTS)
- =/= false orelse
- fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-storage">>, ?NS_HINTS)
- =/= false.
+ xmpp:has_subtag(Packet, #hint{type = 'no-store'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-storage'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-permanent-store'}) orelse
+ xmpp:has_subtag(Packet, #hint{type = 'no-permanent-storage'}).
mod_opt_type(access_log) ->
fun (A) when is_atom(A) -> A end;
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 773953c4a..29b7942cf 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -51,7 +51,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_muc_room.hrl").
@@ -72,6 +72,18 @@
-endif.
+-type state() :: #state{}.
+-type fsm_stop() :: {stop, normal, state()}.
+-type fsm_next() :: {next_state, normal_state, state()}.
+-type fsm_transition() :: fsm_stop() | fsm_next().
+-type history_element() :: {binary(), %% nick
+ message(), %% message itself
+ boolean(), %% have subject
+ erlang:timestamp(),
+ non_neg_integer()}.
+
+-export_type([state/0]).
+
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
@@ -133,349 +145,187 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts]) ->
{ok, normal_state, State}.
normal_state({route, From, <<"">>,
- #xmlel{name = <<"message">>, attrs = Attrs,
- children = Els} =
- Packet},
- StateData) ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+ #message{type = Type, lang = Lang} = Packet}, StateData) ->
case is_user_online(From, StateData) orelse
- is_user_allowed_message_nonparticipant(From, StateData)
- of
- true ->
- case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"groupchat">> ->
- Activity = get_user_activity(From, StateData),
- Now = p1_time_compat:system_time(micro_seconds),
- MinMessageInterval =
- trunc(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, min_message_interval, fun(MMI) when is_number(MMI) -> MMI end, 0)
- * 1000000),
- Size = element_size(Packet),
- {MessageShaper, MessageShaperInterval} =
- shaper:update(Activity#activity.message_shaper, Size),
- if Activity#activity.message /= undefined ->
- ErrText = <<"Traffic rate limit is exceeded">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_RESOURCE_CONSTRAINT(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid, From, Err),
- {next_state, normal_state, StateData};
- Now >=
- Activity#activity.message_time + MinMessageInterval,
- MessageShaperInterval == 0 ->
- {RoomShaper, RoomShaperInterval} =
- shaper:update(StateData#state.room_shaper, Size),
- RoomQueueEmpty =
- queue:is_empty(StateData#state.room_queue),
- if RoomShaperInterval == 0, RoomQueueEmpty ->
- NewActivity = Activity#activity{message_time =
- Now,
- message_shaper =
- MessageShaper},
- StateData1 = store_user_activity(From,
- NewActivity,
- StateData),
- StateData2 = StateData1#state{room_shaper =
- RoomShaper},
- process_groupchat_message(From, Packet,
- StateData2);
- true ->
- StateData1 = if RoomQueueEmpty ->
- erlang:send_after(RoomShaperInterval,
- self(),
- process_room_queue),
- StateData#state{room_shaper =
- RoomShaper};
- true -> StateData
- end,
- NewActivity = Activity#activity{message_time =
- Now,
- message_shaper =
- MessageShaper,
- message = Packet},
- RoomQueue = queue:in({message, From},
- StateData#state.room_queue),
- StateData2 = store_user_activity(From,
- NewActivity,
- StateData1),
- StateData3 = StateData2#state{room_queue =
- RoomQueue},
- {next_state, normal_state, StateData3}
- end;
- true ->
- MessageInterval = (Activity#activity.message_time +
- MinMessageInterval
- - Now)
- div 1000,
- Interval = lists:max([MessageInterval,
- MessageShaperInterval]),
- erlang:send_after(Interval, self(),
- {process_user_message, From}),
- NewActivity = Activity#activity{message = Packet,
- message_shaper =
- MessageShaper},
- StateData1 = store_user_activity(From, NewActivity,
- StateData),
- {next_state, normal_state, StateData1}
- end;
- <<"error">> ->
- case is_user_online(From, StateData) of
- true ->
- ErrorText = <<"It is not allowed to send error messages to the"
- " room. The participant (~s) has sent an error "
- "message (~s) and got kicked from the room">>,
- NewState = expulse_participant(Packet, From, StateData,
- translate:translate(Lang,
- ErrorText)),
- close_room_if_temporary_and_empty(NewState);
- _ -> {next_state, normal_state, StateData}
- end;
- <<"chat">> ->
- ErrText =
- <<"It is not allowed to send private messages "
- "to the conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid, From, Err),
- {next_state, normal_state, StateData};
- Type when (Type == <<"">>) or (Type == <<"normal">>) ->
- IsInvitation = is_invitation(Els),
- IsVoiceRequest = is_voice_request(Els) and
- is_visitor(From, StateData),
- IsVoiceApprovement = is_voice_approvement(Els) and
- not is_visitor(From, StateData),
- if IsInvitation ->
- case catch check_invitation(From, Packet, Lang, StateData)
- of
- {error, Error} ->
- Err = jlib:make_error_reply(Packet, Error),
- ejabberd_router:route(StateData#state.jid, From, Err),
- {next_state, normal_state, StateData};
- IJID ->
- Config = StateData#state.config,
- case Config#config.members_only of
- true ->
- case get_affiliation(IJID, StateData) of
- none ->
- NSD = set_affiliation(IJID, member,
- StateData),
- send_affiliation(IJID, member,
- StateData),
- store_room(NSD),
- {next_state, normal_state, NSD};
- _ -> {next_state, normal_state, StateData}
- end;
- false -> {next_state, normal_state, StateData}
- end
- end;
- IsVoiceRequest ->
- NewStateData = case
- (StateData#state.config)#config.allow_voice_requests
- of
- true ->
- MinInterval =
- (StateData#state.config)#config.voice_request_min_interval,
- BareFrom =
- jid:remove_resource(jid:tolower(From)),
- NowPriority = -p1_time_compat:system_time(micro_seconds),
- CleanPriority = NowPriority +
- MinInterval *
- 1000000,
- Times =
- clean_treap(StateData#state.last_voice_request_time,
- CleanPriority),
- case treap:lookup(BareFrom, Times)
- of
- error ->
- Times1 =
- treap:insert(BareFrom,
- NowPriority,
- true, Times),
- NSD =
- StateData#state{last_voice_request_time
- =
- Times1},
- send_voice_request(From, NSD),
- NSD;
- {ok, _, _} ->
- ErrText =
- <<"Please, wait for a while before sending "
- "new voice request">>,
- Err =
- jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid,
- From, Err),
- StateData#state{last_voice_request_time
- = Times}
- end;
- false ->
- ErrText =
- <<"Voice requests are disabled in this "
- "conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid,
- From, Err),
- StateData
- end,
- {next_state, normal_state, NewStateData};
- IsVoiceApprovement ->
- NewStateData = case is_moderator(From, StateData) of
- true ->
- case
- extract_jid_from_voice_approvement(Els)
- of
- error ->
- ErrText =
- <<"Failed to extract JID from your voice "
- "request approval">>,
- Err =
- jlib:make_error_reply(Packet,
- ?ERRT_BAD_REQUEST(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid,
- From, Err),
- StateData;
- {ok, TargetJid} ->
- case is_visitor(TargetJid,
- StateData)
- of
- true ->
- Reason = <<>>,
- NSD =
- set_role(TargetJid,
- participant,
- StateData),
- catch
- send_new_presence(TargetJid,
- Reason,
- NSD,
- StateData),
- NSD;
- _ -> StateData
- end
- end;
- _ ->
- ErrText =
- <<"Only moderators can approve voice requests">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ALLOWED(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid,
- From, Err),
- StateData
- end,
- {next_state, normal_state, NewStateData};
- true -> {next_state, normal_state, StateData}
- end;
- _ ->
- ErrText = <<"Improper message type">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang,
- ErrText)),
- ejabberd_router:route(StateData#state.jid, From, Err),
- {next_state, normal_state, StateData}
- end;
- _ ->
- case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"error">> -> ok;
- _ ->
- handle_roommessage_from_nonparticipant(Packet, Lang,
- StateData, From)
- end,
- {next_state, normal_state, StateData}
- end;
-normal_state({route, From, <<"">>,
- #xmlel{name = <<"iq">>} = Packet},
- StateData) ->
- case jlib:iq_query_info(Packet) of
- reply ->
- {next_state, normal_state, StateData};
- IQ0 ->
- case ejabberd_hooks:run_fold(
- muc_process_iq,
- StateData#state.server_host,
- IQ0, [StateData, From, StateData#state.jid]) of
- ignore ->
- {next_state, normal_state, StateData};
- #iq{type = T} = IQRes when T == error; T == result ->
- ejabberd_router:route(StateData#state.jid, From, jlib:iq_to_xml(IQRes)),
+ is_user_allowed_message_nonparticipant(From, StateData) of
+ true when Type == groupchat ->
+ Activity = get_user_activity(From, StateData),
+ Now = p1_time_compat:system_time(micro_seconds),
+ MinMessageInterval = trunc(gen_mod:get_module_opt(
+ StateData#state.server_host,
+ mod_muc, min_message_interval,
+ fun(MMI) when is_number(MMI) -> MMI end, 0)
+ * 1000000),
+ Size = element_size(Packet),
+ {MessageShaper, MessageShaperInterval} =
+ shaper:update(Activity#activity.message_shaper, Size),
+ if Activity#activity.message /= undefined ->
+ ErrText = <<"Traffic rate limit is exceeded">>,
+ Err = xmpp:make_error(
+ Packet,
+ xmpp:err_resource_constraint(ErrText, Lang)),
+ ejabberd_router:route(StateData#state.jid, From, Err),
{next_state, normal_state, StateData};
- #iq{type = Type, xmlns = XMLNS, lang = Lang,
- sub_el = #xmlel{name = SubElName, attrs = Attrs} = SubEl} = IQ
- when (XMLNS == (?NS_MUC_ADMIN)) or
- (XMLNS == (?NS_MUC_OWNER))
- or (XMLNS == (?NS_DISCO_INFO))
- or (XMLNS == (?NS_DISCO_ITEMS))
- or (XMLNS == (?NS_VCARD))
- or (XMLNS == (?NS_MUCSUB))
- or (XMLNS == (?NS_CAPTCHA)) ->
- Res1 = case XMLNS of
- ?NS_MUC_ADMIN ->
- process_iq_admin(From, Type, Lang, SubEl, StateData);
- ?NS_MUC_OWNER ->
- process_iq_owner(From, Type, Lang, SubEl, StateData);
- ?NS_DISCO_INFO ->
- case fxml:get_attr(<<"node">>, Attrs) of
- false -> process_iq_disco_info(From, Type, Lang, StateData);
- {value, _} ->
- Txt = <<"Disco info is not available for this node">>,
- {error, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)}
- end;
- ?NS_DISCO_ITEMS ->
- process_iq_disco_items(From, Type, Lang, StateData);
- ?NS_VCARD ->
- process_iq_vcard(From, Type, Lang, SubEl, StateData);
- ?NS_MUCSUB ->
- process_iq_mucsub(From, Packet, IQ, StateData);
- ?NS_CAPTCHA ->
- process_iq_captcha(From, Type, Lang, SubEl, StateData)
- end,
- {IQRes, NewStateData} =
- case Res1 of
- {result, Res, SD} ->
- {IQ#iq{type = result,
- sub_el =
- [#xmlel{name = SubElName,
- attrs =
- [{<<"xmlns">>,
- XMLNS}],
- children = Res}]},
- SD};
- {ignore, SD} -> {ignore, SD};
- {error, Error, ResStateData} ->
- {IQ#iq{type = error,
- sub_el = [SubEl, Error]},
- ResStateData};
- {error, Error} ->
- {IQ#iq{type = error,
- sub_el = [SubEl, Error]},
- StateData}
- end,
- if IQRes /= ignore ->
- ejabberd_router:route(
- StateData#state.jid, From, jlib:iq_to_xml(IQRes));
+ Now >= Activity#activity.message_time + MinMessageInterval,
+ MessageShaperInterval == 0 ->
+ {RoomShaper, RoomShaperInterval} =
+ shaper:update(StateData#state.room_shaper, Size),
+ RoomQueueEmpty = queue:is_empty(StateData#state.room_queue),
+ if RoomShaperInterval == 0, RoomQueueEmpty ->
+ NewActivity = Activity#activity{
+ message_time = Now,
+ message_shaper = MessageShaper},
+ StateData1 = store_user_activity(From,
+ NewActivity,
+ StateData),
+ StateData2 = StateData1#state{room_shaper =
+ RoomShaper},
+ process_groupchat_message(From, Packet,
+ StateData2);
true ->
- ok
- end,
- case NewStateData of
- stop -> {stop, normal, StateData};
- _ -> {next_state, normal_state, NewStateData}
+ StateData1 = if RoomQueueEmpty ->
+ erlang:send_after(RoomShaperInterval,
+ self(),
+ process_room_queue),
+ StateData#state{room_shaper =
+ RoomShaper};
+ true -> StateData
+ end,
+ NewActivity = Activity#activity{
+ message_time = Now,
+ message_shaper = MessageShaper,
+ message = Packet},
+ RoomQueue = queue:in({message, From},
+ StateData#state.room_queue),
+ StateData2 = store_user_activity(From,
+ NewActivity,
+ StateData1),
+ StateData3 = StateData2#state{room_queue = RoomQueue},
+ {next_state, normal_state, StateData3}
end;
+ true ->
+ MessageInterval = (Activity#activity.message_time +
+ MinMessageInterval - Now) div 1000,
+ Interval = lists:max([MessageInterval,
+ MessageShaperInterval]),
+ erlang:send_after(Interval, self(),
+ {process_user_message, From}),
+ NewActivity = Activity#activity{
+ message = Packet,
+ message_shaper = MessageShaper},
+ StateData1 = store_user_activity(From, NewActivity, StateData),
+ {next_state, normal_state, StateData1}
+ end;
+ true when Type == error ->
+ case is_user_online(From, StateData) of
+ true ->
+ ErrorText = <<"It is not allowed to send error messages to the"
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room">>,
+ NewState = expulse_participant(Packet, From, StateData,
+ translate:translate(Lang,
+ ErrorText)),
+ close_room_if_temporary_and_empty(NewState);
_ ->
- Err = jlib:make_error_reply(Packet,
- ?ERR_FEATURE_NOT_IMPLEMENTED),
- ejabberd_router:route(StateData#state.jid, From, Err),
{next_state, normal_state, StateData}
- end
+ end;
+ true when Type == chat ->
+ ErrText = <<"It is not allowed to send private messages "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(StateData#state.jid, From, Packet, Err),
+ {next_state, normal_state, StateData};
+ true when Type == normal ->
+ {next_state, normal_state,
+ try xmpp:decode_els(Packet) of
+ Pkt -> process_normal_message(From, Pkt, StateData)
+ catch _:{xmpp_codec, Why} ->
+ Txt = xmpp:format_error(Why),
+ Err = xmpp:err_bad_request(Txt, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Packet, Err),
+ StateData
+ end};
+ true ->
+ ErrText = <<"Improper message type">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(StateData#state.jid, From, Packet, Err),
+ {next_state, normal_state, StateData};
+ false when Type /= error ->
+ handle_roommessage_from_nonparticipant(Packet, StateData, From);
+ false ->
+ {next_state, normal_state, StateData}
end;
-normal_state({route, From, Nick,
- #xmlel{name = <<"presence">>} = Packet},
- StateData) ->
+normal_state({route, From, <<"">>,
+ #iq{type = Type, lang = Lang, sub_els = [_]} = IQ0},
+ StateData) when Type == get; Type == set ->
+ try
+ case ejabberd_hooks:run_fold(
+ muc_process_iq,
+ StateData#state.server_host,
+ xmpp:set_from_to(xmpp:decode_els(IQ0),
+ From, StateData#state.jid),
+ [StateData]) of
+ ignore ->
+ {next_state, normal_state, StateData};
+ #iq{type = T} = IQRes when T == error; T == result ->
+ ejabberd_router:route(StateData#state.jid, From, IQRes),
+ {next_state, normal_state, StateData};
+ #iq{sub_els = [SubEl]} = IQ ->
+ Res1 = case xmpp:get_ns(SubEl) of
+ ?NS_MUC_ADMIN ->
+ process_iq_admin(From, IQ, StateData);
+ ?NS_MUC_OWNER ->
+ process_iq_owner(From, IQ, StateData);
+ ?NS_DISCO_INFO when SubEl#disco_info.node == undefined ->
+ process_iq_disco_info(From, IQ, StateData);
+ ?NS_DISCO_INFO ->
+ Txt = <<"Disco info is not available for this node">>,
+ {error, xmpp:err_service_unavailable(Txt, Lang)};
+ ?NS_DISCO_ITEMS ->
+ process_iq_disco_items(From, IQ, StateData);
+ ?NS_VCARD ->
+ process_iq_vcard(From, IQ, StateData);
+ ?NS_MUCSUB ->
+ process_iq_mucsub(From, IQ, StateData);
+ ?NS_CAPTCHA ->
+ process_iq_captcha(From, IQ, StateData);
+ _ ->
+ {error, xmpp:err_feature_not_implemented()}
+ end,
+ {IQRes, NewStateData} =
+ case Res1 of
+ {result, Res, SD} ->
+ {xmpp:make_iq_result(IQ, Res), SD};
+ {result, Res} ->
+ {xmpp:make_iq_result(IQ, Res), StateData};
+ {ignore, SD} ->
+ {ignore, SD};
+ {error, Error, ResStateData} ->
+ {xmpp:make_error(IQ0, Error), ResStateData};
+ {error, Error} ->
+ {xmpp:make_error(IQ0, Error), StateData}
+ end,
+ if IQRes /= ignore ->
+ ejabberd_router:route(StateData#state.jid, From, IQRes);
+ true ->
+ ok
+ end,
+ case NewStateData of
+ stop -> {stop, normal, StateData};
+ _ -> {next_state, normal_state, NewStateData}
+ end
+ end
+ catch _:{xmpp_codec, Why} ->
+ ErrTxt = xmpp:format_error(Why),
+ Err = xmpp:make_error(IQ0, xmpp:err_bad_request(ErrTxt, Lang)),
+ ejabberd_router:route(StateData#state.jid, From, Err)
+ end;
+normal_state({route, From, <<"">>, #iq{} = IQ}, StateData) ->
+ Err = xmpp:err_bad_request(),
+ ejabberd_router:route_error(StateData#state.jid, From, IQ, Err),
+ {next_state, normal_state, StateData};
+normal_state({route, From, Nick, #presence{} = Packet}, StateData) ->
Activity = get_user_activity(From, StateData),
Now = p1_time_compat:system_time(micro_seconds),
MinPresenceInterval =
@@ -485,185 +335,135 @@ normal_state({route, From, Nick,
I
end, 0)
* 1000000),
- if (Now >=
- Activity#activity.presence_time + MinPresenceInterval)
- and (Activity#activity.presence == undefined) ->
- NewActivity = Activity#activity{presence_time = Now},
- StateData1 = store_user_activity(From, NewActivity,
- StateData),
- process_presence(From, Nick, Packet, StateData1);
+ if (Now >= Activity#activity.presence_time + MinPresenceInterval)
+ and (Activity#activity.presence == undefined) ->
+ NewActivity = Activity#activity{presence_time = Now},
+ StateData1 = store_user_activity(From, NewActivity,
+ StateData),
+ process_presence(From, Nick, Packet, StateData1);
true ->
- if Activity#activity.presence == undefined ->
- Interval = (Activity#activity.presence_time +
- MinPresenceInterval
- - Now)
- div 1000,
- erlang:send_after(Interval, self(),
- {process_user_presence, From});
- true -> ok
- end,
- NewActivity = Activity#activity{presence =
- {Nick, Packet}},
- StateData1 = store_user_activity(From, NewActivity,
- StateData),
- {next_state, normal_state, StateData1}
+ if Activity#activity.presence == undefined ->
+ Interval = (Activity#activity.presence_time +
+ MinPresenceInterval - Now) div 1000,
+ erlang:send_after(Interval, self(),
+ {process_user_presence, From});
+ true -> ok
+ end,
+ NewActivity = Activity#activity{presence = {Nick, Packet}},
+ StateData1 = store_user_activity(From, NewActivity,
+ StateData),
+ {next_state, normal_state, StateData1}
end;
normal_state({route, From, ToNick,
- #xmlel{name = <<"message">>, attrs = Attrs} = Packet},
+ #message{type = Type, lang = Lang} = Packet},
StateData) ->
- Type = fxml:get_attr_s(<<"type">>, Attrs),
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- case decide_fate_message(Type, Packet, From, StateData)
- of
- {expulse_sender, Reason} ->
- ?DEBUG(Reason, []),
- ErrorText = <<"It is not allowed to send error messages to the"
- " room. The participant (~s) has sent an error "
- "message (~s) and got kicked from the room">>,
- NewState = expulse_participant(Packet, From, StateData,
- translate:translate(Lang, ErrorText)),
- {next_state, normal_state, NewState};
- forget_message -> {next_state, normal_state, StateData};
- continue_delivery ->
- case
- {(StateData#state.config)#config.allow_private_messages,
- is_user_online(From, StateData)}
- of
- {true, true} ->
- case Type of
- <<"groupchat">> ->
- ErrText =
- <<"It is not allowed to send private messages "
- "of type \"groupchat\"">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_BAD_REQUEST(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err);
- _ ->
- case find_jids_by_nick(ToNick, StateData) of
- false ->
- ErrText =
- <<"Recipient is not in the conference room">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_ITEM_NOT_FOUND(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err);
+ case decide_fate_message(Packet, From, StateData) of
+ {expulse_sender, Reason} ->
+ ?DEBUG(Reason, []),
+ ErrorText = <<"It is not allowed to send error messages to the"
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room">>,
+ NewState = expulse_participant(Packet, From, StateData,
+ translate:translate(Lang, ErrorText)),
+ {next_state, normal_state, NewState};
+ forget_message ->
+ {next_state, normal_state, StateData};
+ continue_delivery ->
+ case {(StateData#state.config)#config.allow_private_messages,
+ is_user_online(From, StateData)} of
+ {true, true} when Type == groupchat ->
+ ErrText = <<"It is not allowed to send private messages "
+ "of type \"groupchat\"">>,
+ Err = xmpp:err_bad_request(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err);
+ {true, true} ->
+ case find_jids_by_nick(ToNick, StateData) of
+ [] ->
+ ErrText = <<"Recipient is not in the conference room">>,
+ Err = xmpp:err_item_not_found(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err);
ToJIDs ->
SrcIsVisitor = is_visitor(From, StateData),
- DstIsModerator = is_moderator(hd(ToJIDs),
- StateData),
+ DstIsModerator = is_moderator(hd(ToJIDs), StateData),
PmFromVisitors =
(StateData#state.config)#config.allow_private_messages_from_visitors,
if SrcIsVisitor == false;
PmFromVisitors == anyone;
(PmFromVisitors == moderators) and
- DstIsModerator ->
- {ok, #user{nick = FromNick}} =
- (?DICT):find(jid:tolower(From),
- StateData#state.users),
- FromNickJID =
- jid:replace_resource(StateData#state.jid,
- FromNick),
- X = #xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}]},
- PrivMsg = fxml:append_subtags(Packet, [X]),
- [ejabberd_router:route(FromNickJID, ToJID, PrivMsg)
- || ToJID <- ToJIDs];
+ DstIsModerator ->
+ {ok, #user{nick = FromNick}} =
+ (?DICT):find(jid:tolower(From),
+ StateData#state.users),
+ FromNickJID =
+ jid:replace_resource(StateData#state.jid,
+ FromNick),
+ X = #muc_user{},
+ PrivMsg = xmpp:set_subtag(Packet, X),
+ [ejabberd_router:route(FromNickJID, ToJID, PrivMsg)
+ || ToJID <- ToJIDs];
true ->
- ErrText =
- <<"It is not allowed to send private messages">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err)
+ ErrText = <<"It is not allowed to send private messages">>,
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err)
end
- end
- end;
- {true, false} ->
- ErrText =
- <<"Only occupants are allowed to send messages "
- "to the conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err);
- {false, _} ->
- ErrText =
- <<"It is not allowed to send private messages">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err)
- end,
+ end;
+ {true, false} ->
+ ErrText = <<"Only occupants are allowed to send messages "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err);
+ {false, _} ->
+ ErrText = <<"It is not allowed to send private messages">>,
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err)
+ end,
{next_state, normal_state, StateData}
end;
normal_state({route, From, ToNick,
- #xmlel{name = <<"iq">>, attrs = Attrs} = Packet},
+ #iq{id = StanzaId, lang = Lang} = Packet},
StateData) ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- StanzaId = fxml:get_attr_s(<<"id">>, Attrs),
case {(StateData#state.config)#config.allow_query_users,
- is_user_online_iq(StanzaId, From, StateData)}
- of
- {true, {true, NewId, FromFull}} ->
- case find_jid_by_nick(ToNick, StateData) of
- false ->
- case jlib:iq_query_info(Packet) of
- reply -> ok;
- _ ->
- ErrText = <<"Recipient is not in the conference room">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_ITEM_NOT_FOUND(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err)
- end;
- ToJID ->
- {ok, #user{nick = FromNick}} =
- (?DICT):find(jid:tolower(FromFull),
- StateData#state.users),
- {ToJID2, Packet2} = handle_iq_vcard(FromFull, ToJID,
- StanzaId, NewId, Packet),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- FromNick),
- ToJID2, Packet2)
- end;
- {_, {false, _, _}} ->
- case jlib:iq_query_info(Packet) of
- reply -> ok;
- _ ->
- ErrText =
- <<"Only occupants are allowed to send queries "
- "to the conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err)
- end;
- _ ->
- case jlib:iq_query_info(Packet) of
- reply -> ok;
- _ ->
- ErrText = <<"Queries to the conference members are "
- "not allowed in this room">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ALLOWED(Lang, ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- ToNick),
- From, Err)
- end
+ is_user_online_iq(StanzaId, From, StateData)} of
+ {true, {true, NewId, FromFull}} ->
+ case find_jid_by_nick(ToNick, StateData) of
+ false ->
+ ErrText = <<"Recipient is not in the conference room">>,
+ Err = xmpp:err_item_not_found(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err);
+ ToJID ->
+ {ok, #user{nick = FromNick}} =
+ (?DICT):find(jid:tolower(FromFull), StateData#state.users),
+ {ToJID2, Packet2} = handle_iq_vcard(ToJID, NewId, Packet),
+ ejabberd_router:route(
+ jid:replace_resource(StateData#state.jid, FromNick),
+ ToJID2, Packet2)
+ end;
+ {_, {false, _, _}} ->
+ ErrText = <<"Only occupants are allowed to send queries "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err);
+ _ ->
+ ErrText = <<"Queries to the conference members are "
+ "not allowed in this room">>,
+ Err = xmpp:err_not_allowed(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, ToNick),
+ From, Packet, Err)
end,
{next_state, normal_state, StateData};
normal_state(_Event, StateData) ->
@@ -671,11 +471,7 @@ normal_state(_Event, StateData) ->
handle_event({service_message, Msg}, _StateName,
StateData) ->
- MessagePkt = #xmlel{name = <<"message">>,
- attrs = [{<<"type">>, <<"groupchat">>}],
- children =
- [#xmlel{name = <<"body">>, attrs = [],
- children = [{xmlcdata, Msg}]}]},
+ MessagePkt = #message{type = groupchat, body = xmpp:mk_text(Msg)},
send_wrapped_multiple(
StateData#state.jid,
StateData#state.users,
@@ -687,22 +483,9 @@ handle_event({service_message, Msg}, _StateName,
{next_state, normal_state, NSD};
handle_event({destroy, Reason}, _StateName,
StateData) ->
- {result, [], stop} = destroy_room(#xmlel{name =
- <<"destroy">>,
- attrs =
- [{<<"xmlns">>, ?NS_MUC_OWNER}],
- children =
- case Reason of
- none -> [];
- _Else ->
- [#xmlel{name =
- <<"reason">>,
- attrs = [],
- children =
- [{xmlcdata,
- Reason}]}]
- end},
- StateData),
+ {result, undefined, stop} =
+ destroy_room(#muc_destroy{xmlns = ?NS_MUC_OWNER, reason = Reason},
+ StateData),
?INFO_MSG("Destroyed MUC room ~s with reason: ~p",
[jid:to_string(StateData#state.jid), Reason]),
add_to_log(room_existence, destroyed, StateData),
@@ -710,7 +493,7 @@ handle_event({destroy, Reason}, _StateName,
handle_event(destroy, StateName, StateData) ->
?INFO_MSG("Destroyed MUC room ~s",
[jid:to_string(StateData#state.jid)]),
- handle_event({destroy, none}, StateName, StateData);
+ handle_event({destroy, undefined}, StateName, StateData);
handle_event({set_affiliations, Affiliations},
StateName, StateData) ->
{next_state, StateName,
@@ -741,7 +524,7 @@ handle_sync_event(get_state, _From, StateName,
{reply, {ok, StateData}, StateName, StateData};
handle_sync_event({change_config, Config}, _From,
StateName, StateData) ->
- {result, [], NSD} = change_config(Config, StateData),
+ {result, undefined, NSD} = change_config(Config, StateData),
{reply, {ok, NSD#state.config}, StateName, NSD};
handle_sync_event({change_state, NewStateData}, _From,
StateName, _StateData) ->
@@ -821,12 +604,11 @@ handle_info({captcha_failed, From}, normal_state,
{ok, {Nick, Packet}} ->
Robots = (?DICT):erase(From, StateData#state.robots),
Txt = <<"The CAPTCHA verification has failed">>,
- Err = jlib:make_error_reply(
- Packet, ?ERRT_NOT_AUTHORIZED(?MYLANG, Txt)),
- ejabberd_router:route % TODO: s/Nick/""/
- (jid:replace_resource(StateData#state.jid,
- Nick),
- From, Err),
+ Lang = xmpp:get_lang(Packet),
+ Err = xmpp:err_not_authorized(Txt, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, Nick),
+ From, Packet, Err),
StateData#state{robots = Robots};
_ -> StateData
end,
@@ -845,22 +627,12 @@ terminate(Reason, _StateName, StateData) ->
"because of a system shutdown">>;
_ -> <<"Room terminates">>
end,
- ItemAttrs = [{<<"affiliation">>, <<"none">>},
- {<<"role">>, <<"none">>}],
- ReasonEl = #xmlel{name = <<"reason">>, attrs = [],
- children = [{xmlcdata, ReasonT}]},
- Packet = #xmlel{name = <<"presence">>,
- attrs = [{<<"type">>, <<"unavailable">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
- children =
- [#xmlel{name = <<"item">>,
- attrs = ItemAttrs,
- children = [ReasonEl]},
- #xmlel{name = <<"status">>,
- attrs = [{<<"code">>, <<"332">>}],
- children = []}]}]},
+ Packet = #presence{
+ type = unavailable,
+ sub_els = [#muc_user{items = [#muc_item{affiliation = none,
+ reason = ReasonT,
+ role = none}],
+ status_codes = [332]}]},
(?DICT):fold(fun (LJID, Info, _) ->
Nick = Info#user.nick,
case Reason of
@@ -883,14 +655,12 @@ terminate(Reason, _StateName, StateData) ->
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
-
+-spec route(pid(), jid(), binary(), stanza()) -> ok.
route(Pid, From, ToNick, Packet) ->
gen_fsm:send_event(Pid, {route, From, ToNick, Packet}).
-process_groupchat_message(From,
- #xmlel{name = <<"message">>, attrs = Attrs} = Packet,
- StateData) ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+-spec process_groupchat_message(jid(), message(), state()) -> fsm_next().
+process_groupchat_message(From, #message{lang = Lang} = Packet, StateData) ->
case is_user_online(From, StateData) orelse
is_user_allowed_message_nonparticipant(From, StateData)
of
@@ -932,7 +702,7 @@ process_groupchat_message(From,
drop ->
{next_state, normal_state, StateData};
NewPacket1 ->
- NewPacket = fxml:remove_subtags(NewPacket1, <<"nick">>, {<<"xmlns">>, ?NS_NICK}),
+ NewPacket = xmpp:remove_subtag(NewPacket1, #nick{}),
Node = if Subject == false -> ?NS_MUCSUB_NODES_MESSAGES;
true -> ?NS_MUCSUB_NODES_SUBJECT
end,
@@ -951,41 +721,136 @@ process_groupchat_message(From,
{next_state, normal_state, NewStateData2}
end;
_ ->
- Err = case
- (StateData#state.config)#config.allow_change_subj
- of
+ Err = case (StateData#state.config)#config.allow_change_subj of
true ->
- ?ERRT_FORBIDDEN(Lang,
- <<"Only moderators and participants are "
- "allowed to change the subject in this "
- "room">>);
+ xmpp:err_forbidden(
+ <<"Only moderators and participants are "
+ "allowed to change the subject in this "
+ "room">>, Lang);
_ ->
- ?ERRT_FORBIDDEN(Lang,
- <<"Only moderators are allowed to change "
- "the subject in this room">>)
+ xmpp:err_forbidden(
+ <<"Only moderators are allowed to change "
+ "the subject in this room">>, Lang)
end,
- ejabberd_router:route(StateData#state.jid, From,
- jlib:make_error_reply(Packet, Err)),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Packet, Err),
{next_state, normal_state, StateData}
end;
true ->
ErrText = <<"Visitors are not allowed to send messages "
"to all occupants">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(StateData#state.jid, From, Err),
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Packet, Err),
{next_state, normal_state, StateData}
end;
false ->
- ErrText =
- <<"Only occupants are allowed to send messages "
- "to the conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
- ejabberd_router:route(StateData#state.jid, From, Err),
+ ErrText = <<"Only occupants are allowed to send messages "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(StateData#state.jid, From, Packet, Err),
{next_state, normal_state, StateData}
end.
+-spec process_normal_message(jid(), message(), state()) -> state().
+process_normal_message(From, #message{lang = Lang} = Pkt, StateData) ->
+ IsInvitation = is_invitation(Pkt),
+ IsVoiceRequest = is_voice_request(Pkt) and
+ is_visitor(From, StateData),
+ IsVoiceApprovement = is_voice_approvement(Pkt) and
+ not is_visitor(From, StateData),
+ if IsInvitation ->
+ case check_invitation(From, Pkt, StateData) of
+ {error, Error} ->
+ ejabberd_router:route_error(StateData#state.jid, From, Pkt, Error),
+ StateData;
+ IJID ->
+ Config = StateData#state.config,
+ case Config#config.members_only of
+ true ->
+ case get_affiliation(IJID, StateData) of
+ none ->
+ NSD = set_affiliation(IJID, member, StateData),
+ send_affiliation(IJID, member, StateData),
+ store_room(NSD),
+ NSD;
+ _ ->
+ StateData
+ end;
+ false ->
+ StateData
+ end
+ end;
+ IsVoiceRequest ->
+ case (StateData#state.config)#config.allow_voice_requests of
+ true ->
+ MinInterval = (StateData#state.config)#config.voice_request_min_interval,
+ BareFrom = jid:remove_resource(jid:tolower(From)),
+ NowPriority = -p1_time_compat:system_time(micro_seconds),
+ CleanPriority = NowPriority + MinInterval * 1000000,
+ Times = clean_treap(StateData#state.last_voice_request_time,
+ CleanPriority),
+ case treap:lookup(BareFrom, Times) of
+ error ->
+ Times1 = treap:insert(BareFrom,
+ NowPriority,
+ true, Times),
+ NSD = StateData#state{last_voice_request_time = Times1},
+ send_voice_request(From, Lang, NSD),
+ NSD;
+ {ok, _, _} ->
+ ErrText = <<"Please, wait for a while before sending "
+ "new voice request">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Pkt, Err),
+ StateData#state{last_voice_request_time = Times}
+ end;
+ false ->
+ ErrText = <<"Voice requests are disabled in this conference">>,
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Pkt, Err),
+ StateData
+ end;
+ IsVoiceApprovement ->
+ case is_moderator(From, StateData) of
+ true ->
+ case extract_jid_from_voice_approvement(Pkt) of
+ error ->
+ ErrText = <<"Failed to extract JID from your voice "
+ "request approval">>,
+ Err = xmpp:err_bad_request(ErrText, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Pkt, Err),
+ StateData;
+ TargetJid ->
+ case is_visitor(TargetJid, StateData) of
+ true ->
+ Reason = <<>>,
+ NSD = set_role(TargetJid,
+ participant,
+ StateData),
+ catch send_new_presence(TargetJid,
+ Reason,
+ NSD,
+ StateData),
+ NSD;
+ _ ->
+ StateData
+ end
+ end;
+ _ ->
+ ErrText = <<"Only moderators can approve voice requests">>,
+ Err = xmpp:err_not_allowed(ErrText, Lang),
+ ejabberd_router:route_error(
+ StateData#state.jid, From, Pkt, Err),
+ StateData
+ end;
+ true ->
+ StateData
+ end.
+
%% @doc Check if this non participant can send message to room.
%%
%% XEP-0045 v1.23:
@@ -993,6 +858,7 @@ process_groupchat_message(From,
%% an implementation MAY allow users with certain privileges
%% (e.g., a room owner, room admin, or service-level admin)
%% to send messages to the room even if those users are not occupants.
+-spec is_user_allowed_message_nonparticipant(jid(), state()) -> boolean().
is_user_allowed_message_nonparticipant(JID,
StateData) ->
case get_service_affiliation(JID, StateData) of
@@ -1002,6 +868,7 @@ is_user_allowed_message_nonparticipant(JID,
%% @doc Get information of this participant, or default values.
%% If the JID is not a participant, return values for a service message.
+-spec get_participant_data(jid(), state()) -> {binary(), role(), boolean()}.
get_participant_data(From, StateData) ->
case (?DICT):find(jid:tolower(From),
StateData#state.users)
@@ -1011,14 +878,11 @@ get_participant_data(From, StateData) ->
error -> {<<"">>, moderator, false}
end.
-process_presence(From, Nick,
- #xmlel{name = <<"presence">>, attrs = Attrs0} = Packet0,
- StateData) ->
- Type0 = fxml:get_attr_s(<<"type">>, Attrs0),
+-spec process_presence(jid(), binary(), presence(), state()) -> fsm_transition().
+process_presence(From, Nick, #presence{type = Type0} = Packet0, StateData) ->
IsOnline = is_user_online(From, StateData),
- IsSubscriber = is_subscriber(From, StateData),
- if Type0 == <<"">>;
- IsOnline and ((Type0 == <<"unavailable">>) or (Type0 == <<"error">>)) ->
+ if Type0 == available;
+ IsOnline and ((Type0 == unavailable) or (Type0 == error)) ->
case ejabberd_hooks:run_fold(muc_filter_presence,
StateData#state.server_host,
Packet0,
@@ -1027,119 +891,104 @@ process_presence(From, Nick,
From, Nick]) of
drop ->
{next_state, normal_state, StateData};
- #xmlel{attrs = Attrs} = Packet ->
- Type = fxml:get_attr_s(<<"type">>, Attrs),
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- StateData1 = case Type of
- <<"unavailable">> ->
- NewPacket = case
- {(StateData#state.config)#config.allow_visitor_status,
- is_visitor(From, StateData)}
- of
- {false, true} ->
- strip_status(Packet);
- _ -> Packet
- end,
- NewState = add_user_presence_un(From, NewPacket,
- StateData),
- case (?DICT):find(Nick, StateData#state.nicks) of
- {ok, [_, _ | _]} -> ok;
- _ -> send_new_presence(From, NewState, StateData)
- end,
- Reason = case fxml:get_subtag(NewPacket,
- <<"status">>)
- of
- false -> <<"">>;
- Status_el ->
- fxml:get_tag_cdata(Status_el)
- end,
- remove_online_user(From, NewState, IsSubscriber, Reason);
- <<"error">> ->
- ErrorText = <<"It is not allowed to send error messages to the"
- " room. The participant (~s) has sent an error "
- "message (~s) and got kicked from the room">>,
- expulse_participant(Packet, From, StateData,
- translate:translate(Lang,
- ErrorText));
- <<"">> ->
- if not IsOnline ->
- add_new_user(From, Nick, Packet, StateData);
- true ->
- case is_nick_change(From, Nick, StateData) of
- true ->
- case {nick_collision(From, Nick, StateData),
- mod_muc:can_use_nick(StateData#state.server_host,
- StateData#state.host,
- From, Nick),
- {(StateData#state.config)#config.allow_visitor_nickchange,
- is_visitor(From, StateData)}}
- of
- {_, _, {false, true}} ->
- ErrText =
- <<"Visitors are not allowed to change their "
- "nicknames in this room">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ALLOWED(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- Nick),
- From, Err),
- StateData;
- {true, _, _} ->
- Lang = fxml:get_attr_s(<<"xml:lang">>,
- Attrs),
- ErrText =
- <<"That nickname is already in use by another "
- "occupant">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_CONFLICT(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- Nick), % TODO: s/Nick/""/
- From, Err),
- StateData;
- {_, false, _} ->
- ErrText =
- <<"That nickname is registered by another "
- "person">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_CONFLICT(Lang,
- ErrText)),
- ejabberd_router:route(jid:replace_resource(StateData#state.jid,
- Nick),
- From, Err),
- StateData;
- _ ->
- case is_initial_presence(From, StateData) of
- true ->
- subscriber_becomes_available(
- From, Nick, Packet, StateData);
- false ->
- change_nick(From, Nick, StateData)
- end
- end;
- _NotNickChange ->
- case is_initial_presence(From, StateData) of
- true ->
- subscriber_becomes_available(
- From, Nick, Packet, StateData);
- false ->
- Stanza = maybe_strip_status_from_presence(
- From, Packet, StateData),
- NewState = add_user_presence(From, Stanza,
- StateData),
- send_new_presence(From, NewState, StateData),
- NewState
- end
- end
- end
- end,
- close_room_if_temporary_and_empty(StateData1)
+ #presence{} = Packet ->
+ close_room_if_temporary_and_empty(
+ do_process_presence(From, Nick, Packet, StateData))
end;
true ->
- {next_state, normal_state, StateData}
+ {next_state, normal_state, StateData}
end.
+-spec do_process_presence(jid(), binary(), presence(), state()) ->
+ state().
+do_process_presence(From, Nick, #presence{type = available, lang = Lang} = Packet,
+ StateData) ->
+ case is_user_online(From, StateData) of
+ false ->
+ add_new_user(From, Nick, Packet, StateData);
+ true ->
+ case is_nick_change(From, Nick, StateData) of
+ true ->
+ case {nick_collision(From, Nick, StateData),
+ mod_muc:can_use_nick(StateData#state.server_host,
+ StateData#state.host,
+ From, Nick),
+ {(StateData#state.config)#config.allow_visitor_nickchange,
+ is_visitor(From, StateData)}} of
+ {_, _, {false, true}} ->
+ ErrText = <<"Visitors are not allowed to change their "
+ "nicknames in this room">>,
+ Err = xmpp:err_not_allowed(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, Nick),
+ From, Packet, Err),
+ StateData;
+ {true, _, _} ->
+ ErrText = <<"That nickname is already in use by another "
+ "occupant">>,
+ Err = xmpp:err_conflict(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, Nick),
+ From, Packet, Err),
+ StateData;
+ {_, false, _} ->
+ ErrText = <<"That nickname is registered by another "
+ "person">>,
+ Err = xmpp:err_conflict(ErrText, Lang),
+ ejabberd_router:route_error(
+ jid:replace_resource(StateData#state.jid, Nick),
+ From, Packet, Err),
+ StateData;
+ _ ->
+ case is_initial_presence(From, StateData) of
+ true ->
+ subscriber_becomes_available(
+ From, Nick, Packet, StateData);
+ false ->
+ change_nick(From, Nick, StateData)
+ end
+ end;
+ _NotNickChange ->
+ case is_initial_presence(From, StateData) of
+ true ->
+ subscriber_becomes_available(
+ From, Nick, Packet, StateData);
+ false ->
+ Stanza = maybe_strip_status_from_presence(
+ From, Packet, StateData),
+ NewState = add_user_presence(From, Stanza,
+ StateData),
+ send_new_presence(From, NewState, StateData),
+ NewState
+ end
+ end
+ end;
+do_process_presence(From, Nick, #presence{type = unavailable} = Packet,
+ StateData) ->
+ IsSubscriber = is_subscriber(From, StateData),
+ NewPacket = case {(StateData#state.config)#config.allow_visitor_status,
+ is_visitor(From, StateData)} of
+ {false, true} ->
+ strip_status(Packet);
+ _ -> Packet
+ end,
+ NewState = add_user_presence_un(From, NewPacket, StateData),
+ case (?DICT):find(Nick, StateData#state.nicks) of
+ {ok, [_, _ | _]} -> ok;
+ _ -> send_new_presence(From, NewState, StateData)
+ end,
+ Reason = xmpp:get_text(NewPacket#presence.status),
+ remove_online_user(From, NewState, IsSubscriber, Reason);
+do_process_presence(From, _Nick, #presence{type = error, lang = Lang} = Packet,
+ StateData) ->
+ ErrorText = <<"It is not allowed to send error messages to the"
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room">>,
+ expulse_participant(Packet, From, StateData,
+ translate:translate(Lang, ErrorText)).
+
+-spec maybe_strip_status_from_presence(jid(), presence(),
+ state()) -> presence().
maybe_strip_status_from_presence(From, Packet, StateData) ->
case {(StateData#state.config)#config.allow_visitor_status,
is_visitor(From, StateData)} of
@@ -1148,6 +997,8 @@ maybe_strip_status_from_presence(From, Packet, StateData) ->
_Allowed -> Packet
end.
+-spec subscriber_becomes_available(jid(), binary(), presence(),
+ state()) -> state().
subscriber_becomes_available(From, Nick, Packet, StateData) ->
Stanza = maybe_strip_status_from_presence(From, Packet, StateData),
State1 = add_user_presence(From, Stanza, StateData),
@@ -1159,9 +1010,10 @@ subscriber_becomes_available(From, Nick, Packet, StateData) ->
send_initial_presence(From, State3, StateData),
State3.
+-spec close_room_if_temporary_and_empty(state()) -> fsm_transition().
close_room_if_temporary_and_empty(StateData1) ->
case not (StateData1#state.config)#config.persistent
- andalso (?DICT):to_list(StateData1#state.users) == []
+ andalso (?DICT):size(StateData1#state.users) == 0
of
true ->
?INFO_MSG("Destroyed MUC room ~s because it's temporary "
@@ -1172,10 +1024,12 @@ close_room_if_temporary_and_empty(StateData1) ->
_ -> {next_state, normal_state, StateData1}
end.
+-spec is_user_online(jid(), state()) -> boolean().
is_user_online(JID, StateData) ->
LJID = jid:tolower(JID),
(?DICT):is_key(LJID, StateData#state.users).
+-spec is_subscriber(jid(), state()) -> boolean().
is_subscriber(JID, StateData) ->
LJID = jid:tolower(JID),
case (?DICT):find(LJID, StateData#state.users) of
@@ -1186,6 +1040,7 @@ is_subscriber(JID, StateData) ->
end.
%% Check if the user is occupant of the room, or at least is an admin or owner.
+-spec is_occupant_or_admin(jid(), state()) -> boolean().
is_occupant_or_admin(JID, StateData) ->
FAffiliation = get_affiliation(JID, StateData),
FRole = get_role(JID, StateData),
@@ -1200,6 +1055,8 @@ is_occupant_or_admin(JID, StateData) ->
%%%
%%% Handle IQ queries of vCard
%%%
+-spec is_user_online_iq(binary(), jid(), state()) ->
+ {boolean(), binary(), jid()}.
is_user_online_iq(StanzaId, JID, StateData)
when JID#jid.lresource /= <<"">> ->
{is_user_online(JID, StateData), StanzaId, JID};
@@ -1207,93 +1064,55 @@ is_user_online_iq(StanzaId, JID, StateData)
when JID#jid.lresource == <<"">> ->
try stanzaid_unpack(StanzaId) of
{OriginalId, Resource} ->
- JIDWithResource = jid:replace_resource(JID,
- Resource),
+ JIDWithResource = jid:replace_resource(JID, Resource),
{is_user_online(JIDWithResource, StateData), OriginalId,
JIDWithResource}
catch
_:_ -> {is_user_online(JID, StateData), StanzaId, JID}
end.
-handle_iq_vcard(FromFull, ToJID, StanzaId, NewId,
- Packet) ->
+-spec handle_iq_vcard(jid(), binary(), iq()) -> {jid(), iq()}.
+handle_iq_vcard(ToJID, NewId, #iq{type = Type, sub_els = SubEls} = IQ) ->
ToBareJID = jid:remove_resource(ToJID),
- IQ = jlib:iq_query_info(Packet),
- handle_iq_vcard2(FromFull, ToJID, ToBareJID, StanzaId,
- NewId, IQ, Packet).
-
-handle_iq_vcard2(_FromFull, ToJID, ToBareJID, StanzaId,
- _NewId, #iq{type = get, xmlns = ?NS_VCARD}, Packet)
- when ToBareJID /= ToJID ->
- {ToBareJID, change_stanzaid(StanzaId, ToJID, Packet)};
-handle_iq_vcard2(_FromFull, ToJID, _ToBareJID,
- _StanzaId, NewId, _IQ, Packet) ->
- {ToJID, change_stanzaid(NewId, Packet)}.
+ case SubEls of
+ [SubEl] when Type == get, ToBareJID /= ToJID ->
+ case xmpp:get_ns(SubEl) of
+ ?NS_VCARD ->
+ {ToBareJID, change_stanzaid(ToJID, IQ)};
+ _ ->
+ {ToJID, xmpp:set_id(IQ, NewId)}
+ end;
+ _ ->
+ {ToJID, xmpp:set_id(IQ, NewId)}
+ end.
+-spec stanzaid_pack(binary(), binary()) -> binary().
stanzaid_pack(OriginalId, Resource) ->
<<"berd",
(jlib:encode_base64(<<"ejab\000",
OriginalId/binary, "\000",
Resource/binary>>))/binary>>.
+-spec stanzaid_unpack(binary()) -> {binary(), binary()}.
stanzaid_unpack(<<"berd", StanzaIdBase64/binary>>) ->
StanzaId = jlib:decode_base64(StanzaIdBase64),
[<<"ejab">>, OriginalId, Resource] =
str:tokens(StanzaId, <<"\000">>),
{OriginalId, Resource}.
-change_stanzaid(NewId, Packet) ->
- #xmlel{name = Name, attrs = Attrs, children = Els} =
- jlib:remove_attr(<<"id">>, Packet),
- #xmlel{name = Name, attrs = [{<<"id">>, NewId} | Attrs],
- children = Els}.
-
-change_stanzaid(PreviousId, ToJID, Packet) ->
+-spec change_stanzaid(jid(), iq()) -> iq().
+change_stanzaid(ToJID, #iq{id = PreviousId} = Packet) ->
NewId = stanzaid_pack(PreviousId, ToJID#jid.lresource),
- change_stanzaid(NewId, Packet).
-
-%%%
-%%%
-
-role_to_list(Role) ->
- case Role of
- moderator -> <<"moderator">>;
- participant -> <<"participant">>;
- visitor -> <<"visitor">>;
- none -> <<"none">>
- end.
-
-affiliation_to_list(Affiliation) ->
- case Affiliation of
- owner -> <<"owner">>;
- admin -> <<"admin">>;
- member -> <<"member">>;
- outcast -> <<"outcast">>;
- none -> <<"none">>
- end.
-
-list_to_role(Role) ->
- case Role of
- <<"moderator">> -> moderator;
- <<"participant">> -> participant;
- <<"visitor">> -> visitor;
- <<"none">> -> none
- end.
-
-list_to_affiliation(Affiliation) ->
- case Affiliation of
- <<"owner">> -> owner;
- <<"admin">> -> admin;
- <<"member">> -> member;
- <<"outcast">> -> outcast;
- <<"none">> -> none
- end.
+ xmpp:set_id(Packet, NewId).
%% Decide the fate of the message and its sender
%% Returns: continue_delivery | forget_message | {expulse_sender, Reason}
-decide_fate_message(<<"error">>, Packet, From,
- StateData) ->
- PD = case check_error_kick(Packet) of
+-spec decide_fate_message(message(), jid(), state()) ->
+ continue_delivery | forget_message |
+ {expulse_sender, binary()}.
+decide_fate_message(#message{type = error, error = Err},
+ From, StateData) ->
+ PD = case check_error_kick(Err) of
%% If this is an error stanza and its condition matches a criteria
true ->
Reason =
@@ -1311,67 +1130,61 @@ decide_fate_message(<<"error">>, Packet, From,
end;
Other -> Other
end;
-decide_fate_message(_, _, _, _) -> continue_delivery.
+decide_fate_message(_, _, _) -> continue_delivery.
%% Check if the elements of this error stanza indicate
%% that the sender is a dead participant.
%% If so, return true to kick the participant.
-check_error_kick(Packet) ->
- case get_error_condition(Packet) of
- <<"gone">> -> true;
- <<"internal-server-error">> -> true;
- <<"item-not-found">> -> true;
- <<"jid-malformed">> -> true;
- <<"recipient-unavailable">> -> true;
- <<"redirect">> -> true;
- <<"remote-server-not-found">> -> true;
- <<"remote-server-timeout">> -> true;
- <<"service-unavailable">> -> true;
- _ -> false
- end.
-
-get_error_condition(Packet) ->
- case catch get_error_condition2(Packet) of
- {condition, ErrorCondition} -> ErrorCondition;
- {'EXIT', _} -> <<"badformed error stanza">>
- end.
+-spec check_error_kick(error()) -> boolean().
+check_error_kick(#error{reason = Reason}) ->
+ case Reason of
+ #gone{} -> true;
+ 'internal-server-error' -> true;
+ 'item-not-found' -> true;
+ 'jid-malformed' -> true;
+ 'recipient-unavailable' -> true;
+ #redirect{} -> true;
+ 'remote-server-not-found' -> true;
+ 'remote-server-timeout' -> true;
+ 'service-unavailable' -> true;
+ _ -> false
+ end;
+check_error_kick(undefined) ->
+ false.
-get_error_condition2(Packet) ->
- #xmlel{children = EEls} = fxml:get_subtag(Packet,
- <<"error">>),
- [Condition] = [Name
- || #xmlel{name = Name,
- attrs = [{<<"xmlns">>, ?NS_STANZAS}],
- children = []}
- <- EEls],
- {condition, Condition}.
+-spec get_error_condition(error()) -> string().
+get_error_condition(#error{reason = Reason}) ->
+ case Reason of
+ #gone{} -> "gone";
+ #redirect{} -> "redirect";
+ Atom -> atom_to_list(Atom)
+ end;
+get_error_condition(undefined) ->
+ "undefined".
+-spec make_reason(stanza(), jid(), state(), binary()) -> binary().
make_reason(Packet, From, StateData, Reason1) ->
{ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users),
- Condition = get_error_condition(Packet),
+ Condition = get_error_condition(xmpp:get_error(Packet)),
iolist_to_binary(io_lib:format(Reason1, [FromNick, Condition])).
+-spec expulse_participant(stanza(), jid(), state(), binary()) ->
+ state().
expulse_participant(Packet, From, StateData, Reason1) ->
IsSubscriber = is_subscriber(From, StateData),
Reason2 = make_reason(Packet, From, StateData, Reason1),
NewState = add_user_presence_un(From,
- #xmlel{name = <<"presence">>,
- attrs =
- [{<<"type">>,
- <<"unavailable">>}],
- children =
- [#xmlel{name = <<"status">>,
- attrs = [],
- children =
- [{xmlcdata,
- Reason2}]}]},
+ #presence{type = unavailable,
+ status = xmpp:mk_text(Reason2)},
StateData),
send_new_presence(From, NewState, StateData),
remove_online_user(From, NewState, IsSubscriber).
+-spec set_affiliation(jid(), affiliation(), state()) -> state().
set_affiliation(JID, Affiliation, StateData) ->
set_affiliation(JID, Affiliation, StateData, <<"">>).
+-spec set_affiliation(jid(), affiliation(), state(), binary()) -> state().
set_affiliation(JID, Affiliation, StateData, Reason) ->
LJID = jid:remove_resource(jid:tolower(JID)),
Affiliations = case Affiliation of
@@ -1383,6 +1196,7 @@ set_affiliation(JID, Affiliation, StateData, Reason) ->
end,
StateData#state{affiliations = Affiliations}.
+-spec get_affiliation(jid(), state()) -> affiliation().
get_affiliation(JID, StateData) ->
{_AccessRoute, _AccessCreate, AccessAdmin,
_AccessPersistent} =
@@ -1423,6 +1237,7 @@ get_affiliation(JID, StateData) ->
_ -> Res
end.
+-spec get_service_affiliation(jid(), state()) -> owner | none.
get_service_affiliation(JID, StateData) ->
{_AccessRoute, _AccessCreate, AccessAdmin,
_AccessPersistent} =
@@ -1434,6 +1249,7 @@ get_service_affiliation(JID, StateData) ->
_ -> none
end.
+-spec set_role(jid(), role(), state()) -> state().
set_role(JID, Role, StateData) ->
LJID = jid:tolower(JID),
LJIDs = case LJID of
@@ -1482,6 +1298,7 @@ set_role(JID, Role, StateData) ->
end,
StateData#state{users = Users, nicks = Nicks}.
+-spec get_role(jid(), state()) -> role().
get_role(JID, StateData) ->
LJID = jid:tolower(JID),
case (?DICT):find(LJID, StateData#state.users) of
@@ -1489,6 +1306,7 @@ get_role(JID, StateData) ->
_ -> none
end.
+-spec get_default_role(affiliation(), state()) -> role().
get_default_role(Affiliation, StateData) ->
case Affiliation of
owner -> moderator;
@@ -1507,12 +1325,15 @@ get_default_role(Affiliation, StateData) ->
end
end.
+-spec is_visitor(jid(), state()) -> boolean().
is_visitor(Jid, StateData) ->
get_role(Jid, StateData) =:= visitor.
+-spec is_moderator(jid(), state()) -> boolean().
is_moderator(Jid, StateData) ->
get_role(Jid, StateData) =:= moderator.
+-spec get_max_users(state()) -> non_neg_integer().
get_max_users(StateData) ->
MaxUsers = (StateData#state.config)#config.max_users,
ServiceMaxUsers = get_service_max_users(StateData),
@@ -1520,18 +1341,21 @@ get_max_users(StateData) ->
true -> ServiceMaxUsers
end.
+-spec get_service_max_users(state()) -> pos_integer().
get_service_max_users(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_users,
fun(I) when is_integer(I), I>0 -> I end,
?MAX_USERS_DEFAULT).
+-spec get_max_users_admin_threshold(state()) -> pos_integer().
get_max_users_admin_threshold(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_users_admin_threshold,
fun(I) when is_integer(I), I>0 -> I end,
5).
+-spec get_user_activity(jid(), state()) -> #activity{}.
get_user_activity(JID, StateData) ->
case treap:lookup(jid:tolower(JID),
StateData#state.activity)
@@ -1552,6 +1376,7 @@ get_user_activity(JID, StateData) ->
presence_shaper = PresenceShaper}
end.
+-spec store_user_activity(jid(), #activity{}, state()) -> state().
store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
trunc(gen_mod:get_module_opt(StateData#state.server_host,
@@ -1613,6 +1438,7 @@ store_user_activity(JID, UserActivity, StateData) ->
end,
StateData1.
+-spec clean_treap(treap:treap(), integer()) -> treap:treap().
clean_treap(Treap, CleanPriority) ->
case treap:is_empty(Treap) of
true -> Treap;
@@ -1624,6 +1450,7 @@ clean_treap(Treap, CleanPriority) ->
end
end.
+-spec prepare_room_queue(state()) -> state().
prepare_room_queue(StateData) ->
case queue:out(StateData#state.room_queue) of
{{value, {message, From}}, _RoomQueue} ->
@@ -1647,6 +1474,7 @@ prepare_room_queue(StateData) ->
{empty, _} -> StateData
end.
+-spec update_online_user(jid(), #user{}, state()) -> state().
update_online_user(JID, #user{nick = Nick, subscriptions = Nodes,
is_subscriber = IsSubscriber} = User, StateData) ->
LJID = jid:tolower(JID),
@@ -1681,6 +1509,7 @@ update_online_user(JID, #user{nick = Nick, subscriptions = Nodes,
end,
NewStateData.
+-spec add_online_user(jid(), binary(), role(), boolean(), [binary()], state()) -> state().
add_online_user(JID, Nick, Role, IsSubscriber, Nodes, StateData) ->
tab_add_online_user(JID, StateData),
User = #user{jid = JID, nick = Nick, role = Role,
@@ -1693,9 +1522,11 @@ add_online_user(JID, Nick, Role, IsSubscriber, Nodes, StateData) ->
end,
StateData1.
+-spec remove_online_user(jid(), state(), boolean()) -> state().
remove_online_user(JID, StateData, IsSubscriber) ->
remove_online_user(JID, StateData, IsSubscriber, <<"">>).
+-spec remove_online_user(jid(), state(), boolean(), binary()) -> state().
remove_online_user(JID, StateData, _IsSubscriber = true, _Reason) ->
LJID = jid:tolower(JID),
Users = case (?DICT):find(LJID, StateData#state.users) of
@@ -1723,38 +1554,23 @@ remove_online_user(JID, StateData, _IsSubscriber, Reason) ->
end,
StateData#state{users = Users, nicks = Nicks}.
-filter_presence(#xmlel{name = <<"presence">>,
- attrs = Attrs, children = Els}) ->
- FEls = lists:filter(fun (El) ->
- case El of
- {xmlcdata, _} -> false;
- #xmlel{attrs = Attrs1} ->
- XMLNS = fxml:get_attr_s(<<"xmlns">>,
- Attrs1),
- NS_MUC = ?NS_MUC,
- Size = byte_size(NS_MUC),
- case XMLNS of
- <<NS_MUC:Size/binary, _/binary>> ->
- false;
- _ ->
- true
- end
- end
- end,
- Els),
- #xmlel{name = <<"presence">>, attrs = Attrs,
- children = FEls}.
-
-strip_status(#xmlel{name = <<"presence">>,
- attrs = Attrs, children = Els}) ->
- FEls = lists:filter(fun (#xmlel{name = <<"status">>}) ->
- false;
- (_) -> true
- end,
- Els),
- #xmlel{name = <<"presence">>, attrs = Attrs,
- children = FEls}.
-
+-spec filter_presence(presence()) -> presence().
+filter_presence(Presence) ->
+ Els = lists:filter(
+ fun(El) ->
+ XMLNS = xmpp:get_ns(El),
+ case catch binary:part(XMLNS, 0, size(?NS_MUC)) of
+ ?NS_MUC -> false;
+ _ -> true
+ end
+ end, xmpp:get_els(Presence)),
+ xmpp:set_els(Presence, Els).
+
+-spec strip_status(presence()) -> presence().
+strip_status(Presence) ->
+ Presence#presence{status = []}.
+
+-spec add_user_presence(jid(), presence(), state()) -> state().
add_user_presence(JID, Presence, StateData) ->
LJID = jid:tolower(JID),
FPresence = filter_presence(Presence),
@@ -1765,6 +1581,7 @@ add_user_presence(JID, Presence, StateData) ->
StateData#state.users),
StateData#state{users = Users}.
+-spec add_user_presence_un(jid(), presence(), state()) -> state().
add_user_presence_un(JID, Presence, StateData) ->
LJID = jid:tolower(JID),
FPresence = filter_presence(Presence),
@@ -1778,15 +1595,17 @@ add_user_presence_un(JID, Presence, StateData) ->
%% Find and return a list of the full JIDs of the users of Nick.
%% Return jid record.
+-spec find_jids_by_nick(binary(), state()) -> [jid()].
find_jids_by_nick(Nick, StateData) ->
case (?DICT):find(Nick, StateData#state.nicks) of
{ok, [User]} -> [jid:make(User)];
{ok, Users} -> [jid:make(LJID) || LJID <- Users];
- error -> false
+ error -> []
end.
%% Find and return the full JID of the user of Nick with
%% highest-priority presence. Return jid record.
+-spec find_jid_by_nick(binary(), state()) -> jid() | false.
find_jid_by_nick(Nick, StateData) ->
case (?DICT):find(Nick, StateData#state.nicks) of
{ok, [User]} -> jid:make(User);
@@ -1811,6 +1630,8 @@ find_jid_by_nick(Nick, StateData) ->
error -> false
end.
+-spec higher_presence(undefined | presence(),
+ undefined | presence()) -> boolean().
higher_presence(Pres1, Pres2) when Pres1 /= undefined, Pres2 /= undefined ->
Pri1 = get_priority_from_presence(Pres1),
Pri2 = get_priority_from_presence(Pres2),
@@ -1818,26 +1639,20 @@ higher_presence(Pres1, Pres2) when Pres1 /= undefined, Pres2 /= undefined ->
higher_presence(Pres1, Pres2) ->
Pres1 > Pres2.
-get_priority_from_presence(PresencePacket) ->
- case fxml:get_subtag(PresencePacket, <<"priority">>) of
- false -> 0;
- SubEl ->
- case catch
- jlib:binary_to_integer(fxml:get_tag_cdata(SubEl))
- of
- P when is_integer(P) -> P;
- _ -> 0
- end
+-spec get_priority_from_presence(presence()) -> integer().
+get_priority_from_presence(#presence{priority = Prio}) ->
+ case Prio of
+ undefined -> 0;
+ _ -> Prio
end.
-find_nick_by_jid(Jid, StateData) ->
- [{_, #user{nick = Nick}}] = lists:filter(fun ({_,
- #user{jid = FJid}}) ->
- FJid == Jid
- end,
- (?DICT):to_list(StateData#state.users)),
+-spec find_nick_by_jid(jid(), state()) -> binary().
+find_nick_by_jid(JID, StateData) ->
+ LJID = jid:tolower(JID),
+ {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users),
Nick.
+-spec is_nick_change(jid(), binary(), state()) -> boolean().
is_nick_change(JID, Nick, StateData) ->
LJID = jid:tolower(JID),
case Nick of
@@ -1848,16 +1663,20 @@ is_nick_change(JID, Nick, StateData) ->
Nick /= OldNick
end.
+-spec nick_collision(jid(), binary(), state()) -> boolean().
nick_collision(User, Nick, StateData) ->
UserOfNick = find_jid_by_nick(Nick, StateData),
(UserOfNick /= false andalso
jid:remove_resource(jid:tolower(UserOfNick))
/= jid:remove_resource(jid:tolower(User))).
-add_new_user(From, Nick,
- #xmlel{name = Name, attrs = Attrs, children = Els} = Packet,
- StateData) ->
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+-spec add_new_user(jid(), binary(), presence() | iq(), state()) ->
+ state() |
+ {error, error()} |
+ {ignore, state()} |
+ {result, xmpp_element(), state()}.
+add_new_user(From, Nick, Packet, StateData) ->
+ Lang = xmpp:get_lang(Packet),
UserRoomJID = jid:replace_resource(StateData#state.jid, Nick),
MaxUsers = get_max_users(StateData),
MaxAdminUsers = MaxUsers +
@@ -1874,7 +1693,7 @@ add_new_user(From, Nick,
fun(I) when is_integer(I), I>0 -> I end,
10),
Collision = nick_collision(From, Nick, StateData),
- IsSubscribeRequest = Name /= <<"presence">>,
+ IsSubscribeRequest = not is_record(Packet, presence),
case {(ServiceAffiliation == owner orelse
((Affiliation == admin orelse Affiliation == owner)
andalso NUsers < MaxAdminUsers)
@@ -1887,72 +1706,72 @@ add_new_user(From, Nick,
of
{false, _, _, _} when NUsers >= MaxUsers orelse NUsers >= MaxAdminUsers ->
Txt = <<"Too many users in this conference">>,
- Err = ?ERRT_RESOURCE_CONSTRAINT(Lang, Txt),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_resource_constraint(Txt, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{false, _, _, _} when NConferences >= MaxConferences ->
Txt = <<"You have joined too many conferences">>,
- Err = ?ERRT_RESOURCE_CONSTRAINT(Lang, Txt),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_resource_constraint(Txt, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{false, _, _, _} ->
- Err = ?ERR_SERVICE_UNAVAILABLE,
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_service_unavailable(),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{_, _, _, none} ->
Err = case Affiliation of
outcast ->
ErrText = <<"You have been banned from this room">>,
- ?ERRT_FORBIDDEN(Lang, ErrText);
+ xmpp:err_forbidden(ErrText, Lang);
_ ->
ErrText = <<"Membership is required to enter this room">>,
- ?ERRT_REGISTRATION_REQUIRED(Lang, ErrText)
+ xmpp:err_registration_required(ErrText, Lang)
end,
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{_, true, _, _} ->
ErrText = <<"That nickname is already in use by another occupant">>,
- Err = ?ERRT_CONFLICT(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_conflict(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{_, _, false, _} ->
ErrText = <<"That nickname is registered by another person">>,
- Err = ?ERRT_CONFLICT(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_conflict(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
{_, _, _, Role} ->
case check_password(ServiceAffiliation, Affiliation,
- Els, From, StateData)
+ Packet, From, StateData)
of
true ->
Nodes = get_subscription_nodes(Packet),
@@ -1965,7 +1784,7 @@ add_new_user(From, Nick,
Nodes, StateData)),
send_existing_presences(From, NewState),
send_initial_presence(From, NewState, StateData),
- Shift = count_stanza_shift(Nick, Els, NewState),
+ Shift = count_stanza_shift(Nick, Packet, NewState),
case send_history(From, Shift, NewState) of
true -> ok;
_ -> send_subject(From, StateData)
@@ -1985,20 +1804,20 @@ add_new_user(From, Nick,
NewStateData#state{robots = Robots}
end,
if not IsSubscribeRequest -> ResultState;
- true -> {result, subscription_nodes_to_events(Nodes), ResultState}
+ true -> {result, subscribe_result(Packet), ResultState}
end;
nopass ->
ErrText = <<"A password is required to enter this room">>,
- Err = ?ERRT_NOT_AUTHORIZED(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_not_authorized(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
captcha_required ->
- SID = fxml:get_attr_s(<<"id">>, Attrs),
+ SID = xmpp:get_id(Packet),
RoomJID = StateData#state.jid,
To = jid:replace_resource(RoomJID, Nick),
Limiter = {From#jid.luser, From#jid.lserver},
@@ -2006,9 +1825,7 @@ add_new_user(From, Nick,
Lang, Limiter, From)
of
{ok, ID, CaptchaEls} ->
- MsgPkt = #xmlel{name = <<"message">>,
- attrs = [{<<"id">>, ID}],
- children = CaptchaEls},
+ MsgPkt = #message{id = ID, sub_els = CaptchaEls},
Robots = (?DICT):store(From, {Nick, Packet},
StateData#state.robots),
ejabberd_router:route(RoomJID, From, MsgPkt),
@@ -2020,49 +1837,51 @@ add_new_user(From, Nick,
end;
{error, limit} ->
ErrText = <<"Too many CAPTCHA requests">>,
- Err = ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_resource_constraint(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end;
_ ->
ErrText = <<"Unable to generate a CAPTCHA">>,
- Err = ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_internal_server_error(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end
end;
_ ->
ErrText = <<"Incorrect password">>,
- Err = ?ERRT_NOT_AUTHORIZED(Lang, ErrText),
- ErrPacket = jlib:make_error_reply(Packet, Err),
+ Err = xmpp:err_not_authorized(ErrText, Lang),
+ ErrPacket = xmpp:make_error(Packet, Err),
if not IsSubscribeRequest ->
ejabberd_router:route(UserRoomJID, From, ErrPacket),
StateData;
true ->
- {error, Err, StateData}
+ {error, Err}
end
end
end.
-check_password(owner, _Affiliation, _Els, _From,
+-spec check_password(affiliation(), affiliation(),
+ stanza(), jid(), state()) -> boolean() | nopass.
+check_password(owner, _Affiliation, _Packet, _From,
_StateData) ->
%% Don't check pass if user is owner in MUC service (access_admin option)
true;
-check_password(_ServiceAffiliation, Affiliation, Els,
+check_password(_ServiceAffiliation, Affiliation, Packet,
From, StateData) ->
case (StateData#state.config)#config.password_protected
of
false -> check_captcha(Affiliation, From, StateData);
true ->
- Pass = extract_password(Els),
+ Pass = extract_password(Packet),
case Pass of
false -> nopass;
_ ->
@@ -2073,6 +1892,7 @@ check_password(_ServiceAffiliation, Affiliation, Els,
end
end.
+-spec check_captcha(affiliation(), jid(), state()) -> true | captcha_required.
check_captcha(Affiliation, From, StateData) ->
case (StateData#state.config)#config.captcha_protected
andalso ejabberd_captcha:is_feature_available()
@@ -2101,47 +1921,52 @@ check_captcha(Affiliation, From, StateData) ->
_ -> true
end.
-extract_password([]) -> false;
-extract_password([#xmlel{attrs = Attrs} = El | Els]) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_MUC ->
- case fxml:get_subtag(El, <<"password">>) of
- false -> false;
- SubEl -> fxml:get_tag_cdata(SubEl)
- end;
- _ -> extract_password(Els)
- end;
-extract_password([_ | Els]) -> extract_password(Els).
+-spec extract_password(stanza()) -> binary() | false.
+extract_password(Packet) ->
+ case xmpp:get_subtag(Packet, #muc{}) of
+ #muc{password = Password} when is_binary(Password) ->
+ Password;
+ _ ->
+ false
+ end.
-count_stanza_shift(Nick, Els, StateData) ->
- HL = lqueue_to_list(StateData#state.history),
- Since = extract_history(Els, <<"since">>),
- Shift0 = case Since of
- false -> 0;
- _ ->
- Sin = calendar:datetime_to_gregorian_seconds(Since),
- count_seconds_shift(Sin, HL)
- end,
- Seconds = extract_history(Els, <<"seconds">>),
- Shift1 = case Seconds of
- false -> 0;
- _ ->
- Sec = calendar:datetime_to_gregorian_seconds(calendar:universal_time())
- - Seconds,
- count_seconds_shift(Sec, HL)
- end,
- MaxStanzas = extract_history(Els, <<"maxstanzas">>),
- Shift2 = case MaxStanzas of
- false -> 0;
- _ -> count_maxstanzas_shift(MaxStanzas, HL)
- end,
- MaxChars = extract_history(Els, <<"maxchars">>),
- Shift3 = case MaxChars of
- false -> 0;
- _ -> count_maxchars_shift(Nick, MaxChars, HL)
- end,
- lists:max([Shift0, Shift1, Shift2, Shift3]).
+-spec count_stanza_shift(binary(), stanza(), state()) -> non_neg_integer().
+count_stanza_shift(Nick, Packet, StateData) ->
+ case xmpp:get_subtag(Packet, #muc_history{}) of
+ #muc_history{since = Since,
+ seconds = Seconds,
+ maxstanzas = MaxStanzas,
+ maxchars = MaxChars} ->
+ HL = lqueue_to_list(StateData#state.history),
+ Shift0 = case Since of
+ undefined -> 0;
+ _ ->
+ Sin = calendar:datetime_to_gregorian_seconds(
+ calendar:now_to_datetime(Since)),
+ count_seconds_shift(Sin, HL)
+ end,
+ Shift1 = case Seconds of
+ undefined -> 0;
+ _ ->
+ Sec = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - Seconds,
+ count_seconds_shift(Sec, HL)
+ end,
+ Shift2 = case MaxStanzas of
+ undefined -> 0;
+ _ -> count_maxstanzas_shift(MaxStanzas, HL)
+ end,
+ Shift3 = case MaxChars of
+ undefined -> 0;
+ _ -> count_maxchars_shift(Nick, MaxChars, HL)
+ end,
+ lists:max([Shift0, Shift1, Shift2, Shift3]);
+ false ->
+ 0
+ end.
+-spec count_seconds_shift(non_neg_integer(),
+ [history_element()]) -> non_neg_integer().
count_seconds_shift(Seconds, HistoryList) ->
lists:sum(lists:map(fun ({_Nick, _Packet, _HaveSubject,
TimeStamp, _Size}) ->
@@ -2153,12 +1978,16 @@ count_seconds_shift(Seconds, HistoryList) ->
end,
HistoryList)).
+-spec count_maxstanzas_shift(non_neg_integer(),
+ [history_element()]) -> non_neg_integer().
count_maxstanzas_shift(MaxStanzas, HistoryList) ->
S = length(HistoryList) - MaxStanzas,
if S =< 0 -> 0;
true -> S
end.
+-spec count_maxchars_shift(binary(), non_neg_integer(),
+ [history_element()]) -> integer().
count_maxchars_shift(Nick, MaxSize, HistoryList) ->
NLen = byte_size(Nick) + 1,
Sizes = lists:map(fun ({_Nick, _Packet, _HaveSubject,
@@ -2168,41 +1997,20 @@ count_maxchars_shift(Nick, MaxSize, HistoryList) ->
HistoryList),
calc_shift(MaxSize, Sizes).
+-spec calc_shift(non_neg_integer(), [non_neg_integer()]) -> integer().
calc_shift(MaxSize, Sizes) ->
Total = lists:sum(Sizes),
calc_shift(MaxSize, Total, 0, Sizes).
+-spec calc_shift(non_neg_integer(), integer(), integer(),
+ [non_neg_integer()]) -> integer().
calc_shift(_MaxSize, _Size, Shift, []) -> Shift;
calc_shift(MaxSize, Size, Shift, [S | TSizes]) ->
if MaxSize >= Size -> Shift;
true -> calc_shift(MaxSize, Size - S, Shift + 1, TSizes)
end.
-extract_history([], _Type) -> false;
-extract_history([#xmlel{attrs = Attrs} = El | Els],
- Type) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_MUC ->
- AttrVal = fxml:get_path_s(El,
- [{elem, <<"history">>}, {attr, Type}]),
- case Type of
- <<"since">> ->
- case jlib:datetime_string_to_timestamp(AttrVal) of
- undefined -> false;
- TS -> calendar:now_to_universal_time(TS)
- end;
- _ ->
- case catch jlib:binary_to_integer(AttrVal) of
- IntVal when is_integer(IntVal) and (IntVal >= 0) ->
- IntVal;
- _ -> false
- end
- end;
- _ -> extract_history(Els, Type)
- end;
-extract_history([_ | Els], Type) ->
- extract_history(Els, Type).
-
+-spec is_room_overcrowded(state()) -> boolean().
is_room_overcrowded(StateData) ->
MaxUsersPresence = gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_users_presence,
@@ -2210,10 +2018,12 @@ is_room_overcrowded(StateData) ->
?DEFAULT_MAX_USERS_PRESENCE),
(?DICT):size(StateData#state.users) > MaxUsersPresence.
+-spec presence_broadcast_allowed(jid(), state()) -> boolean().
presence_broadcast_allowed(JID, StateData) ->
Role = get_role(JID, StateData),
lists:member(Role, (StateData#state.config)#config.presence_broadcast).
+-spec is_initial_presence(jid(), state()) -> boolean().
is_initial_presence(From, StateData) ->
LJID = jid:tolower(From),
case (?DICT):find(LJID, StateData#state.users) of
@@ -2223,18 +2033,22 @@ is_initial_presence(From, StateData) ->
true
end.
+-spec send_initial_presence(jid(), state(), state()) -> ok.
send_initial_presence(NJID, StateData, OldStateData) ->
send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
+-spec send_update_presence(jid(), state(), state()) -> ok.
send_update_presence(JID, StateData, OldStateData) ->
send_update_presence(JID, <<"">>, StateData, OldStateData).
+-spec send_update_presence(jid(), binary(), state(), state()) -> ok.
send_update_presence(JID, Reason, StateData, OldStateData) ->
case is_room_overcrowded(StateData) of
true -> ok;
false -> send_update_presence1(JID, Reason, StateData, OldStateData)
end.
+-spec send_update_presence1(jid(), binary(), state(), state()) -> ok.
send_update_presence1(JID, Reason, StateData, OldStateData) ->
LJID = jid:tolower(JID),
LJIDs = case LJID of
@@ -2258,12 +2072,15 @@ send_update_presence1(JID, Reason, StateData, OldStateData) ->
end,
LJIDs).
+-spec send_new_presence(jid(), state(), state()) -> ok.
send_new_presence(NJID, StateData, OldStateData) ->
send_new_presence(NJID, <<"">>, false, StateData, OldStateData).
+-spec send_new_presence(jid(), binary(), state(), state()) -> ok.
send_new_presence(NJID, Reason, StateData, OldStateData) ->
send_new_presence(NJID, Reason, false, StateData, OldStateData).
+-spec send_new_presence(jid(), binary(), boolean(), state(), state()) -> ok.
send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
case is_room_overcrowded(StateData) of
true -> ok;
@@ -2271,6 +2088,7 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
OldStateData)
end.
+-spec is_ra_changed(jid() | ljid(), boolean(), state(), state()) -> boolean().
is_ra_changed(_, _IsInitialPresence = true, _, _) ->
false;
is_ra_changed(LJID, _IsInitialPresence = false, NewStateData, OldStateData) ->
@@ -2289,6 +2107,7 @@ is_ra_changed(LJID, _IsInitialPresence = false, NewStateData, OldStateData) ->
(NewRole /= OldRole) or (NewAff /= OldAff)
end.
+-spec send_new_presence1(jid(), binary(), boolean(), state(), state()) -> ok.
send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
LNJID = jid:tolower(NJID),
#user{nick = Nick} = (?DICT):fetch(LNJID, StateData#state.users),
@@ -2301,15 +2120,9 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
{Role1, Presence1} =
case presence_broadcast_allowed(NJID, StateData) of
true -> {Role0, Presence0};
- false ->
- {none,
- #xmlel{name = <<"presence">>,
- attrs = [{<<"type">>, <<"unavailable">>}],
- children = []}
- }
+ false -> {none, #presence{type = unavailable}}
end,
Affiliation = get_affiliation(LJID, StateData),
- SAffiliation = affiliation_to_list(Affiliation),
UserList =
case not (presence_broadcast_allowed(NJID, StateData) orelse
presence_broadcast_allowed(NJID, OldStateData)) of
@@ -2323,59 +2136,38 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
{Role, Presence} = if LNJID == LUJID -> {Role0, Presence0};
true -> {Role1, Presence1}
end,
- SRole = role_to_list(Role),
- ItemAttrs = case Info#user.role == moderator orelse
- (StateData#state.config)#config.anonymous
- == false
- of
- true ->
- [{<<"jid">>,
- jid:to_string(RealJID)},
- {<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole}];
- _ ->
- [{<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole}]
- end,
- ItemEls = case Reason of
- <<"">> -> [];
- _ ->
- [#xmlel{name = <<"reason">>,
- attrs = [],
- children =
- [{xmlcdata, Reason}]}]
- end,
- StatusEls = status_els(IsInitialPresence, NJID, Info,
- StateData),
- Pres = if Presence == undefined -> #xmlel{name = <<"presence">>};
+ Item0 = #muc_item{affiliation = Affiliation,
+ role = Role},
+ Item1 = case Info#user.role == moderator orelse
+ (StateData#state.config)#config.anonymous
+ == false of
+ true -> Item0#muc_item{jid = RealJID};
+ false -> Item0
+ end,
+ Item = if is_binary(Reason), Reason /= <<"">> ->
+ Item1#muc_item{reason = Reason};
+ true ->
+ Item1
+ end,
+ StatusCodes = status_codes(IsInitialPresence, NJID, Info,
+ StateData),
+ Pres = if Presence == undefined -> #presence{};
true -> Presence
end,
- Packet = fxml:append_subtags(Pres,
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name =
- <<"item">>,
- attrs
- =
- ItemAttrs,
- children
- =
- ItemEls}
- | StatusEls]}]),
+ Packet = xmpp:set_subtag(
+ Pres, #muc_user{items = [Item],
+ status_codes = StatusCodes}),
Node1 = case is_ra_changed(NJID, IsInitialPresence, StateData, OldStateData) of
true -> ?NS_MUCSUB_NODES_AFFILIATIONS;
false -> ?NS_MUCSUB_NODES_PRESENCE
end,
send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
Info#user.jid, Packet, Node1, StateData),
- Type = fxml:get_tag_attr_s(<<"type">>, Packet),
+ Type = xmpp:get_type(Packet),
IsSubscriber = Info#user.is_subscriber,
IsOccupant = Info#user.last_presence /= undefined,
if (IsSubscriber and not IsOccupant) and
- (IsInitialPresence or (Type == <<"unavailable">>)) ->
+ (IsInitialPresence or (Type == unavailable)) ->
Node2 = ?NS_MUCSUB_NODES_PARTICIPANTS,
send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
Info#user.jid, Packet, Node2, StateData);
@@ -2385,12 +2177,14 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
end,
UserList).
+-spec send_existing_presences(jid(), state()) -> ok.
send_existing_presences(ToJID, StateData) ->
case is_room_overcrowded(StateData) of
true -> ok;
false -> send_existing_presences1(ToJID, StateData)
end.
+-spec send_existing_presences1(jid(), state()) -> ok.
send_existing_presences1(ToJID, StateData) ->
LToJID = jid:tolower(ToJID),
{ok, #user{jid = RealToJID, role = Role}} =
@@ -2410,46 +2204,23 @@ send_existing_presences1(ToJID, StateData) ->
{_, false} -> ok;
_ ->
FromAffiliation = get_affiliation(LJID, StateData),
- ItemAttrs = case Role == moderator orelse
- (StateData#state.config)#config.anonymous
- == false
- of
- true ->
- [{<<"jid">>,
- jid:to_string(FromJID)},
- {<<"affiliation">>,
- affiliation_to_list(FromAffiliation)},
- {<<"role">>,
- role_to_list(FromRole)}];
- _ ->
- [{<<"affiliation">>,
- affiliation_to_list(FromAffiliation)},
- {<<"role">>,
- role_to_list(FromRole)}]
- end,
- Packet = fxml:append_subtags(
- Presence,
- [#xmlel{name =
- <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name
- =
- <<"item">>,
- attrs
- =
- ItemAttrs,
- children
- =
- []}]}]),
+ Item0 = #muc_item{affiliation = FromAffiliation,
+ role = FromRole},
+ Item = case Role == moderator orelse
+ (StateData#state.config)#config.anonymous
+ == false of
+ true -> Item0#muc_item{jid = FromJID};
+ false -> Item0
+ end,
+ Packet = xmpp:set_subtag(
+ Presence, #muc_user{items = [Item]}),
send_wrapped(jid:replace_resource(StateData#state.jid, FromNick),
RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData)
end
end,
(?DICT):to_list(StateData#state.nicks)).
+-spec set_nick(jid(), binary(), state()) -> state().
set_nick(JID, Nick, State) ->
LJID = jid:tolower(JID),
{ok, #user{nick = OldNick}} = (?DICT):find(LJID, State#state.users),
@@ -2472,6 +2243,7 @@ set_nick(JID, Nick, State) ->
end,
State#state{users = Users, nicks = Nicks}.
+-spec change_nick(jid(), binary(), state()) -> state().
change_nick(JID, Nick, StateData) ->
LJID = jid:tolower(JID),
{ok, #user{nick = OldNick}} = (?DICT):find(LJID, StateData#state.users),
@@ -2492,6 +2264,7 @@ change_nick(JID, Nick, StateData) ->
add_to_log(nickchange, {OldNick, Nick}, StateData),
NewStateData.
+-spec send_nick_changing(jid(), binary(), state(), boolean(), boolean()) -> ok.
send_nick_changing(JID, OldNick, StateData,
SendOldUnavailable, SendNewAvailable) ->
{ok,
@@ -2500,104 +2273,52 @@ send_nick_changing(JID, OldNick, StateData,
(?DICT):find(jid:tolower(JID),
StateData#state.users),
Affiliation = get_affiliation(JID, StateData),
- SAffiliation = affiliation_to_list(Affiliation),
- SRole = role_to_list(Role),
- lists:foreach(fun ({_LJID, Info}) when Presence /= undefined ->
- ItemAttrs1 = case Info#user.role == moderator orelse
- (StateData#state.config)#config.anonymous
- == false
- of
- true ->
- [{<<"jid">>,
- jid:to_string(RealJID)},
- {<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole},
- {<<"nick">>, Nick}];
- _ ->
- [{<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole},
- {<<"nick">>, Nick}]
- end,
- ItemAttrs2 = case Info#user.role == moderator orelse
- (StateData#state.config)#config.anonymous
- == false
- of
- true ->
- [{<<"jid">>,
- jid:to_string(RealJID)},
- {<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole}];
- _ ->
- [{<<"affiliation">>, SAffiliation},
- {<<"role">>, SRole}]
- end,
- Status110 = case JID == Info#user.jid of
- true ->
- [#xmlel{name = <<"status">>,
- attrs = [{<<"code">>, <<"110">>}]
- }];
- false ->
- []
- end,
- Packet1 = #xmlel{name = <<"presence">>,
- attrs =
- [{<<"type">>,
- <<"unavailable">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name =
- <<"item">>,
- attrs =
- ItemAttrs1,
- children =
- []},
- #xmlel{name =
- <<"status">>,
- attrs =
- [{<<"code">>,
- <<"303">>}],
- children =
- []}|Status110]}]},
- Packet2 = fxml:append_subtags(Presence,
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name
- =
- <<"item">>,
- attrs
- =
- ItemAttrs2,
- children
- =
- []}|Status110]}]),
- if SendOldUnavailable ->
- send_wrapped(jid:replace_resource(StateData#state.jid,
- OldNick),
- Info#user.jid, Packet1,
- ?NS_MUCSUB_NODES_PRESENCE,
- StateData);
- true -> ok
+ lists:foreach(
+ fun({_LJID, Info}) when Presence /= undefined ->
+ Item0 = #muc_item{affiliation = Affiliation, role = Role},
+ Item1 = case Info#user.role == moderator orelse
+ (StateData#state.config)#config.anonymous
+ == false of
+ true -> Item0#muc_item{jid = RealJID, nick = Nick};
+ false -> Item0#muc_item{nick = Nick}
+ end,
+ Item2 = case Info#user.role == moderator orelse
+ (StateData#state.config)#config.anonymous
+ == false of
+ true -> Item0#muc_item{jid = RealJID};
+ false -> Item0
+ end,
+ Status110 = case JID == Info#user.jid of
+ true -> [110];
+ false -> []
end,
- if SendNewAvailable ->
- send_wrapped(jid:replace_resource(StateData#state.jid,
- Nick),
- Info#user.jid, Packet2,
- ?NS_MUCSUB_NODES_PRESENCE,
- StateData);
- true -> ok
- end;
- (_) ->
- ok
- end,
- (?DICT):to_list(StateData#state.users)).
+ Packet1 = #presence{type = unavailable,
+ sub_els = [#muc_user{
+ items = [Item1],
+ status_codes = [303|Status110]}]},
+ Packet2 = xmpp:set_subtag(Presence,
+ #muc_user{items = [Item2],
+ status_codes = Status110}),
+ if SendOldUnavailable ->
+ send_wrapped(
+ jid:replace_resource(StateData#state.jid, OldNick),
+ Info#user.jid, Packet1, ?NS_MUCSUB_NODES_PRESENCE,
+ StateData);
+ true -> ok
+ end,
+ if SendNewAvailable ->
+ send_wrapped(
+ jid:replace_resource(StateData#state.jid, Nick),
+ Info#user.jid, Packet2, ?NS_MUCSUB_NODES_PRESENCE,
+ StateData);
+ true -> ok
+ end;
+ (_) ->
+ ok
+ end,
+ (?DICT):to_list(StateData#state.users)).
+-spec maybe_send_affiliation(jid(), affiliation(), state()) -> ok.
maybe_send_affiliation(JID, Affiliation, StateData) ->
LJID = jid:tolower(JID),
IsOccupant = case LJID of
@@ -2617,18 +2338,13 @@ maybe_send_affiliation(JID, Affiliation, StateData) ->
send_affiliation(LJID, Affiliation, StateData)
end.
+-spec send_affiliation(ljid(), affiliation(), state()) -> ok.
send_affiliation(LJID, Affiliation, StateData) ->
- ItemAttrs = [{<<"jid">>, jid:to_string(LJID)},
- {<<"affiliation">>, affiliation_to_list(Affiliation)},
- {<<"role">>, <<"none">>}],
- Message = #xmlel{name = <<"message">>,
- attrs = [{<<"id">>, randoms:get_string()}],
- children =
- [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
- children =
- [#xmlel{name = <<"item">>,
- attrs = ItemAttrs}]}]},
+ Item = #muc_item{jid = jid:make(LJID),
+ affiliation = Affiliation,
+ role = none},
+ Message = #message{id = randoms:get_string(),
+ sub_els = [#muc_user{items = [Item]}]},
Recipients = case (StateData#state.config)#config.anonymous of
true ->
(?DICT):filter(fun(_, #user{role = moderator}) ->
@@ -2643,43 +2359,33 @@ send_affiliation(LJID, Affiliation, StateData) ->
StateData#state.server_host,
Recipients, Message).
-status_els(IsInitialPresence, JID, #user{jid = JID}, StateData) ->
- Status = case IsInitialPresence of
- true ->
- S1 = case StateData#state.just_created of
- true ->
- [#xmlel{name = <<"status">>,
- attrs = [{<<"code">>, <<"201">>}],
- children = []}];
- false -> []
- end,
- S2 = case (StateData#state.config)#config.anonymous of
- true -> S1;
- false ->
- [#xmlel{name = <<"status">>,
- attrs = [{<<"code">>, <<"100">>}],
- children = []} | S1]
- end,
- S3 = case (StateData#state.config)#config.logging of
- true ->
- [#xmlel{name = <<"status">>,
- attrs = [{<<"code">>, <<"170">>}],
- children = []} | S2];
- false -> S2
- end,
- S3;
- false -> []
- end,
- [#xmlel{name = <<"status">>,
- attrs =
- [{<<"code">>,
- <<"110">>}],
- children = []} | Status];
-status_els(_IsInitialPresence, _JID, _Info, _StateData) -> [].
+-spec status_codes(boolean(), jid(), #user{}, state()) -> [pos_integer()].
+status_codes(IsInitialPresence, JID, #user{jid = JID}, StateData) ->
+ S0 = [110],
+ case IsInitialPresence of
+ true ->
+ S1 = case StateData#state.just_created of
+ true -> [201|S0];
+ false -> S0
+ end,
+ S2 = case (StateData#state.config)#config.anonymous of
+ true -> S1;
+ false -> [100|S1]
+ end,
+ S3 = case (StateData#state.config)#config.logging of
+ true -> [170|S2];
+ false -> S2
+ end,
+ S3;
+ false -> S0
+ end;
+status_codes(_IsInitialPresence, _JID, _Info, _StateData) -> [].
+-spec lqueue_new(non_neg_integer()) -> lqueue().
lqueue_new(Max) ->
#lqueue{queue = queue:new(), len = 0, max = Max}.
+-spec lqueue_in(term(), lqueue()) -> lqueue().
%% If the message queue limit is set to 0, do not store messages.
lqueue_in(_Item, LQ = #lqueue{max = 0}) -> LQ;
%% Otherwise, rotate messages in the queue store.
@@ -2692,39 +2398,33 @@ lqueue_in(Item,
true -> #lqueue{queue = Q2, len = Len + 1, max = Max}
end.
+-spec lqueue_cut(queue:queue(), non_neg_integer()) -> queue:queue().
lqueue_cut(Q, 0) -> Q;
lqueue_cut(Q, N) ->
{_, Q1} = queue:out(Q), lqueue_cut(Q1, N - 1).
+-spec lqueue_to_list(lqueue()) -> list().
lqueue_to_list(#lqueue{queue = Q1}) ->
queue:to_list(Q1).
-
+-spec add_message_to_history(binary(), jid(), message(), state()) -> state().
add_message_to_history(FromNick, FromJID, Packet, StateData) ->
- HaveSubject = case fxml:get_subtag(Packet, <<"subject">>)
- of
- false -> false;
- _ -> true
- end,
+ HaveSubject = Packet#message.subject /= [],
TimeStamp = p1_time_compat:timestamp(),
AddrPacket = case (StateData#state.config)#config.anonymous of
true -> Packet;
false ->
- Address = #xmlel{name = <<"address">>,
- attrs = [{<<"type">>, <<"ofrom">>},
- {<<"jid">>,
- jid:to_string(FromJID)}],
- children = []},
- Addresses = #xmlel{name = <<"addresses">>,
- attrs = [{<<"xmlns">>, ?NS_ADDRESS}],
- children = [Address]},
- fxml:append_subtags(Packet, [Addresses])
+ Addresses = #addresses{
+ list = [#address{type = ofrom,
+ jid = FromJID}]},
+ xmpp:set_subtag(Packet, Addresses)
end,
- TSPacket = jlib:add_delay_info(AddrPacket, StateData#state.jid, TimeStamp),
- SPacket =
- jlib:replace_from_to(jid:replace_resource(StateData#state.jid,
- FromNick),
- StateData#state.jid, TSPacket),
+ TSPacket = xmpp_util:add_delay_info(
+ AddrPacket, StateData#state.jid, TimeStamp),
+ SPacket = xmpp:set_from_to(
+ TSPacket,
+ jid:replace_resource(StateData#state.jid, FromNick),
+ StateData#state.jid),
Size = element_size(SPacket),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject,
calendar:now_to_universal_time(TimeStamp), Size},
@@ -2732,6 +2432,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
add_to_log(text, {FromNick, Packet}, StateData),
StateData#state{history = Q1}.
+-spec send_history(jid(), integer(), state()) -> boolean().
send_history(JID, Shift, StateData) ->
lists:foldl(fun ({Nick, Packet, HaveSubject, _TimeStamp,
_Size},
@@ -2745,23 +2446,19 @@ send_history(JID, Shift, StateData) ->
lists:nthtail(Shift,
lqueue_to_list(StateData#state.history))).
+-spec send_subject(jid(), state()) -> ok.
send_subject(_JID, #state{subject_author = <<"">>}) -> ok;
send_subject(JID, #state{subject_author = Nick} = StateData) ->
Subject = StateData#state.subject,
- Packet = #xmlel{name = <<"message">>,
- attrs = [{<<"type">>, <<"groupchat">>}],
- children =
- [#xmlel{name = <<"subject">>, attrs = [],
- children = [{xmlcdata, Subject}]}]},
+ Packet = #message{type = groupchat, subject = xmpp:mk_text(Subject)},
ejabberd_router:route(jid:replace_resource(StateData#state.jid, Nick), JID,
Packet).
-check_subject(Packet) ->
- case fxml:get_subtag(Packet, <<"subject">>) of
- false -> false;
- SubjEl -> fxml:get_tag_cdata(SubjEl)
- end.
+-spec check_subject(message()) -> false | binary().
+check_subject(#message{subject = []}) -> false;
+check_subject(#message{subject = Subj}) -> xmpp:get_text(Subj).
+-spec can_change_subject(role(), state()) -> boolean().
can_change_subject(Role, StateData) ->
case (StateData#state.config)#config.allow_change_subj
of
@@ -2772,99 +2469,89 @@ can_change_subject(Role, StateData) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Admin stuff
-process_iq_admin(From, set, Lang, SubEl, StateData) ->
- #xmlel{children = Items} = SubEl,
+-spec process_iq_admin(jid(), iq(), #state{}) -> {error, error()} |
+ {result, undefined, #state{}} |
+ {result, muc_admin()}.
+process_iq_admin(_From, #iq{lang = Lang, sub_els = [#muc_admin{items = []}]},
+ _StateData) ->
+ Txt = <<"No 'item' element found">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
+process_iq_admin(From, #iq{type = set, lang = Lang,
+ sub_els = [#muc_admin{items = Items}]},
+ StateData) ->
process_admin_items_set(From, Items, Lang, StateData);
-process_iq_admin(From, get, Lang, SubEl, StateData) ->
- case fxml:get_subtag(SubEl, <<"item">>) of
- false ->
- Txt = <<"No 'item' element found">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
- Item ->
- FAffiliation = get_affiliation(From, StateData),
- FRole = get_role(From, StateData),
- case fxml:get_tag_attr(<<"role">>, Item) of
- false ->
- case fxml:get_tag_attr(<<"affiliation">>, Item) of
- false ->
- Txt = <<"No 'affiliation' attribute found">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
- {value, StrAffiliation} ->
- case catch list_to_affiliation(StrAffiliation) of
- {'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
- SAffiliation ->
- if (FAffiliation == owner) or
- (FAffiliation == admin) or
- ((FAffiliation == member) and not
- (StateData#state.config)#config.anonymous) ->
- Items = items_with_affiliation(SAffiliation,
- StateData),
- {result, Items, StateData};
- true ->
- ErrText =
- <<"Administrator privileges required">>,
- {error, ?ERRT_FORBIDDEN(Lang, ErrText)}
- end
- end
- end;
- {value, StrRole} ->
- case catch list_to_role(StrRole) of
- {'EXIT', _} ->
- Txt = <<"Incorrect value of 'role' attribute">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
- SRole ->
- if FRole == moderator ->
- Items = items_with_role(SRole, StateData),
- {result, Items, StateData};
- true ->
- ErrText = <<"Moderator privileges required">>,
- {error, ?ERRT_FORBIDDEN(Lang, ErrText)}
- end
- end
- end
- end.
+process_iq_admin(From, #iq{type = get, lang = Lang,
+ sub_els = [#muc_admin{items = [Item]}]},
+ StateData) ->
+ FAffiliation = get_affiliation(From, StateData),
+ FRole = get_role(From, StateData),
+ case Item of
+ #muc_item{role = undefined, affiliation = undefined} ->
+ Txt = <<"Neither 'role' nor 'affiliation' attribute found">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
+ #muc_item{role = undefined, affiliation = Affiliation} ->
+ if (FAffiliation == owner) or
+ (FAffiliation == admin) or
+ ((FAffiliation == member) and
+ not (StateData#state.config)#config.anonymous) ->
+ Items = items_with_affiliation(Affiliation, StateData),
+ {result, #muc_admin{items = Items}};
+ true ->
+ ErrText = <<"Administrator privileges required">>,
+ {error, xmpp:err_forbidden(ErrText, Lang)}
+ end;
+ #muc_item{role = Role} ->
+ if FRole == moderator ->
+ Items = items_with_role(Role, StateData),
+ {result, #muc_admin{items = Items}};
+ true ->
+ ErrText = <<"Moderator privileges required">>,
+ {error, xmpp:err_forbidden(ErrText, Lang)}
+ end
+ end;
+process_iq_admin(_From, #iq{type = get, lang = Lang}, _StateData) ->
+ ErrText = <<"Too many <item/> elements">>,
+ {error, xmpp:err_bad_request(ErrText, Lang)}.
+-spec items_with_role(role(), state()) -> [muc_item()].
items_with_role(SRole, StateData) ->
lists:map(fun ({_, U}) -> user_to_item(U, StateData)
end,
search_role(SRole, StateData)).
+-spec items_with_affiliation(affiliation(), state()) -> [muc_item()].
items_with_affiliation(SAffiliation, StateData) ->
- lists:map(fun ({JID, {Affiliation, Reason}}) ->
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"affiliation">>,
- affiliation_to_list(Affiliation)},
- {<<"jid">>, jid:to_string(JID)}],
- children =
- [#xmlel{name = <<"reason">>, attrs = [],
- children = [{xmlcdata, Reason}]}]};
- ({JID, Affiliation}) ->
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"affiliation">>,
- affiliation_to_list(Affiliation)},
- {<<"jid">>, jid:to_string(JID)}],
- children = []}
- end,
- search_affiliation(SAffiliation, StateData)).
+ lists:map(
+ fun({JID, {Affiliation, Reason}}) ->
+ #muc_item{affiliation = Affiliation, jid = JID,
+ reason = if is_binary(Reason), Reason /= <<"">> ->
+ Reason;
+ true ->
+ undefined
+ end};
+ ({JID, Affiliation}) ->
+ #muc_item{affiliation = Affiliation, jid = JID}
+ end,
+ search_affiliation(SAffiliation, StateData)).
+-spec user_to_item(#user{}, state()) -> muc_item().
user_to_item(#user{role = Role, nick = Nick, jid = JID},
StateData) ->
Affiliation = get_affiliation(JID, StateData),
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"role">>, role_to_list(Role)},
- {<<"affiliation">>, affiliation_to_list(Affiliation)},
- {<<"nick">>, Nick},
- {<<"jid">>, jid:to_string(JID)}],
- children = []}.
+ #muc_item{role = Role,
+ affiliation = Affiliation,
+ nick = Nick,
+ jid = JID}.
+-spec search_role(role(), state()) -> [{ljid(), #user{}}].
search_role(Role, StateData) ->
lists:filter(fun ({_, #user{role = R}}) -> Role == R
end,
(?DICT):to_list(StateData#state.users)).
+-spec search_affiliation(affiliation(), state()) ->
+ [{ljid(),
+ affiliation() | {affiliation(), binary()}}].
search_affiliation(Affiliation, StateData) ->
lists:filter(fun ({_, A}) ->
case A of
@@ -2874,11 +2561,14 @@ search_affiliation(Affiliation, StateData) ->
end,
(?DICT):to_list(StateData#state.affiliations)).
+-spec process_admin_items_set(jid(), [muc_item()], binary() | undefined,
+ #state{}) -> {result, undefined, #state{}} |
+ {error, error()}.
process_admin_items_set(UJID, Items, Lang, StateData) ->
UAffiliation = get_affiliation(UJID, StateData),
URole = get_role(UJID, StateData),
- case find_changed_items(UJID, UAffiliation, URole,
- Items, Lang, StateData, [])
+ case catch find_changed_items(UJID, UAffiliation, URole,
+ Items, Lang, StateData, [])
of
{result, Res} ->
?INFO_MSG("Processing MUC admin query from ~s in "
@@ -2888,249 +2578,161 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
NSD = lists:foldl(process_item_change(UJID),
StateData, lists:flatten(Res)),
store_room(NSD),
- {result, [], NSD};
- Err -> Err
+ {result, undefined, NSD};
+ {error, Err} -> {error, Err}
end.
+-spec process_item_change(jid()) -> function().
process_item_change(UJID) ->
fun(E, SD) ->
process_item_change(E, SD, UJID)
end.
-process_item_change(E, SD, UJID) ->
- case catch case E of
- {JID, affiliation, owner, _} when JID#jid.luser == <<"">> ->
- %% If the provided JID does not have username,
- %% forget the affiliation completely
- SD;
- {JID, role, none, Reason} ->
- catch
- send_kickban_presence(UJID, JID,
- Reason,
- <<"307">>,
- SD),
- set_role(JID, none, SD);
- {JID, affiliation, none, Reason} ->
- case (SD#state.config)#config.members_only of
- true ->
- catch
- send_kickban_presence(UJID, JID,
- Reason,
- <<"321">>,
- none,
- SD),
- maybe_send_affiliation(JID, none, SD),
- SD1 = set_affiliation(JID, none, SD),
- set_role(JID, none, SD1);
- _ ->
- SD1 = set_affiliation(JID, none, SD),
- send_update_presence(JID, SD1, SD),
- maybe_send_affiliation(JID, none, SD1),
- SD1
- end;
- {JID, affiliation, outcast, Reason} ->
- catch
- send_kickban_presence(UJID, JID,
- Reason,
- <<"301">>,
- outcast,
- SD),
- maybe_send_affiliation(JID, outcast, SD),
- set_affiliation(JID,
- outcast,
- set_role(JID, none, SD),
- Reason);
- {JID, affiliation, A, Reason}
- when (A == admin) or (A == owner) ->
- SD1 = set_affiliation(JID, A, SD, Reason),
- SD2 = set_role(JID, moderator, SD1),
- send_update_presence(JID, Reason, SD2, SD),
- maybe_send_affiliation(JID, A, SD2),
- SD2;
- {JID, affiliation, member, Reason} ->
- SD1 = set_affiliation(JID, member, SD, Reason),
- SD2 = set_role(JID, participant, SD1),
- send_update_presence(JID, Reason, SD2, SD),
- maybe_send_affiliation(JID, member, SD2),
- SD2;
- {JID, role, Role, Reason} ->
- SD1 = set_role(JID, Role, SD),
- catch
- send_new_presence(JID, Reason, SD1, SD),
- SD1;
- {JID, affiliation, A, _Reason} ->
- SD1 = set_affiliation(JID, A, SD),
- send_update_presence(JID, SD1, SD),
- maybe_send_affiliation(JID, A, SD1),
- SD1
- end
- of
- {'EXIT', ErrReason} ->
- ?ERROR_MSG("MUC ITEMS SET ERR: ~p~n", [ErrReason]),
- SD;
- NSD -> NSD
+-type admin_action() :: {jid(), affiliation | role,
+ affiliation() | role(), binary()}.
+
+-spec process_item_change(admin_action(), state(), jid()) -> state().
+process_item_change(Item, SD, UJID) ->
+ try case Item of
+ {JID, affiliation, owner, _} when JID#jid.luser == <<"">> ->
+ %% If the provided JID does not have username,
+ %% forget the affiliation completely
+ SD;
+ {JID, role, none, Reason} ->
+ catch send_kickban_presence(UJID, JID, Reason, 307, SD),
+ set_role(JID, none, SD);
+ {JID, affiliation, none, Reason} ->
+ case (SD#state.config)#config.members_only of
+ true ->
+ catch send_kickban_presence(UJID, JID, Reason, 321, none, SD),
+ maybe_send_affiliation(JID, none, SD),
+ SD1 = set_affiliation(JID, none, SD),
+ set_role(JID, none, SD1);
+ _ ->
+ SD1 = set_affiliation(JID, none, SD),
+ send_update_presence(JID, SD1, SD),
+ maybe_send_affiliation(JID, none, SD1),
+ SD1
+ end;
+ {JID, affiliation, outcast, Reason} ->
+ catch send_kickban_presence(UJID, JID, Reason, 301, outcast, SD),
+ maybe_send_affiliation(JID, outcast, SD),
+ set_affiliation(JID, outcast, set_role(JID, none, SD), Reason);
+ {JID, affiliation, A, Reason} when (A == admin) or (A == owner) ->
+ SD1 = set_affiliation(JID, A, SD, Reason),
+ SD2 = set_role(JID, moderator, SD1),
+ send_update_presence(JID, Reason, SD2, SD),
+ maybe_send_affiliation(JID, A, SD2),
+ SD2;
+ {JID, affiliation, member, Reason} ->
+ SD1 = set_affiliation(JID, member, SD, Reason),
+ SD2 = set_role(JID, participant, SD1),
+ send_update_presence(JID, Reason, SD2, SD),
+ maybe_send_affiliation(JID, member, SD2),
+ SD2;
+ {JID, role, Role, Reason} ->
+ SD1 = set_role(JID, Role, SD),
+ catch send_new_presence(JID, Reason, SD1, SD),
+ SD1;
+ {JID, affiliation, A, _Reason} ->
+ SD1 = set_affiliation(JID, A, SD),
+ send_update_presence(JID, SD1, SD),
+ maybe_send_affiliation(JID, A, SD1),
+ SD1
+ end
+ catch E:R ->
+ ?ERROR_MSG("failed to set item ~p from ~s: ~p",
+ [Item, jid:to_string(UJID),
+ {E, {R, erlang:get_stacktrace()}}]),
+ SD
end.
+-spec find_changed_items(jid(), affiliation(), role(),
+ [muc_item()], binary(), state(), [admin_action()]) ->
+ {result, [admin_action()]}.
find_changed_items(_UJID, _UAffiliation, _URole, [],
_Lang, _StateData, Res) ->
{result, Res};
+find_changed_items(_UJID, _UAffiliation, _URole,
+ [#muc_item{jid = undefined, nick = undefined}|_],
+ Lang, _StateData, _Res) ->
+ Txt = <<"Neither 'jid' nor 'nick' attribute found">>,
+ throw({error, xmpp:err_bad_request(Txt, Lang)});
+find_changed_items(_UJID, _UAffiliation, _URole,
+ [#muc_item{role = undefined, affiliation = undefined}|_],
+ Lang, _StateData, _Res) ->
+ Txt = <<"Neither 'role' nor 'affiliation' attribute found">>,
+ throw({error, xmpp:err_bad_request(Txt, Lang)});
find_changed_items(UJID, UAffiliation, URole,
- [{xmlcdata, _} | Items], Lang, StateData, Res) ->
- find_changed_items(UJID, UAffiliation, URole, Items,
- Lang, StateData, Res);
-find_changed_items(UJID, UAffiliation, URole,
- [#xmlel{name = <<"item">>, attrs = Attrs} = Item
- | Items],
+ [#muc_item{jid = J, nick = Nick, reason = Reason0,
+ role = Role, affiliation = Affiliation}|Items],
Lang, StateData, Res) ->
- TJID = case fxml:get_attr(<<"jid">>, Attrs) of
- {value, S} ->
- case jid:from_string(S) of
- error ->
- ErrText = iolist_to_binary(
- io_lib:format(translate:translate(
- Lang,
- <<"Jabber ID ~s is invalid">>),
- [S])),
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
- J -> {value, [J]}
- end;
- _ ->
- case fxml:get_attr(<<"nick">>, Attrs) of
- {value, N} ->
- case find_jids_by_nick(N, StateData) of
- false ->
- ErrText = iolist_to_binary(
- io_lib:format(
- translate:translate(
- Lang,
- <<"Nickname ~s does not exist in the room">>),
- [N])),
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
- J -> {value, J}
- end;
- _ ->
- Txt1 = <<"No 'nick' attribute found">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt1)}
- end
- end,
- case TJID of
- {value, [JID | _] = JIDs} ->
- TAffiliation = get_affiliation(JID, StateData),
- TRole = get_role(JID, StateData),
- case fxml:get_attr(<<"role">>, Attrs) of
- false ->
- case fxml:get_attr(<<"affiliation">>, Attrs) of
- false ->
- Txt2 = <<"No 'affiliation' attribute found">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt2)};
- {value, StrAffiliation} ->
- case catch list_to_affiliation(StrAffiliation) of
- {'EXIT', _} ->
- ErrText1 = iolist_to_binary(
- io_lib:format(
- translate:translate(
- Lang,
- <<"Invalid affiliation: ~s">>),
- [StrAffiliation])),
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText1)};
- SAffiliation ->
- ServiceAf = get_service_affiliation(JID, StateData),
- CanChangeRA = case can_change_ra(UAffiliation,
- URole,
- TAffiliation,
- TRole, affiliation,
- SAffiliation,
- ServiceAf)
- of
- nothing -> nothing;
- true -> true;
- check_owner ->
- case search_affiliation(owner,
- StateData)
- of
- [{OJID, _}] ->
- jid:remove_resource(OJID)
- /=
- jid:tolower(jid:remove_resource(UJID));
- _ -> true
- end;
- _ -> false
- end,
- case CanChangeRA of
- nothing ->
- find_changed_items(UJID, UAffiliation, URole,
- Items, Lang, StateData,
- Res);
- true ->
- Reason = fxml:get_path_s(Item,
- [{elem, <<"reason">>},
- cdata]),
- MoreRes = [{jid:remove_resource(Jidx),
- affiliation, SAffiliation, Reason}
- || Jidx <- JIDs],
- find_changed_items(UJID, UAffiliation, URole,
- Items, Lang, StateData,
- [MoreRes | Res]);
- false ->
- Txt3 = <<"Changing role/affiliation is not allowed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt3)}
- end
- end
- end;
- {value, StrRole} ->
- case catch list_to_role(StrRole) of
- {'EXIT', _} ->
- ErrText1 = iolist_to_binary(
- io_lib:format(translate:translate(
- Lang,
- <<"Invalid role: ~s">>),
- [StrRole])),
- {error, ?ERRT_BAD_REQUEST(Lang, ErrText1)};
- SRole ->
- ServiceAf = get_service_affiliation(JID, StateData),
- CanChangeRA = case can_change_ra(UAffiliation, URole,
- TAffiliation, TRole,
- role, SRole, ServiceAf)
- of
- nothing -> nothing;
- true -> true;
- check_owner ->
- case search_affiliation(owner,
- StateData)
- of
- [{OJID, _}] ->
- jid:remove_resource(OJID)
- /=
- jid:tolower(jid:remove_resource(UJID));
- _ -> true
- end;
- _ -> false
- end,
- case CanChangeRA of
- nothing ->
- find_changed_items(UJID, UAffiliation, URole, Items,
- Lang, StateData, Res);
- true ->
- Reason = fxml:get_path_s(Item,
- [{elem, <<"reason">>},
- cdata]),
- MoreRes = [{Jidx, role, SRole, Reason}
- || Jidx <- JIDs],
- find_changed_items(UJID, UAffiliation, URole, Items,
- Lang, StateData,
- [MoreRes | Res]);
- _ ->
- Txt4 = <<"Changing role/affiliation is not allowed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt4)}
- end
+ [JID | _] = JIDs =
+ if J /= undefined ->
+ [J];
+ Nick /= undefined ->
+ case find_jids_by_nick(Nick, StateData) of
+ [] ->
+ ErrText = iolist_to_binary(
+ io_lib:format(
+ translate:translate(
+ Lang,
+ <<"Nickname ~s does not exist in the room">>),
+ [Nick])),
+ throw({error, xmpp:err_not_acceptable(ErrText, Lang)});
+ JIDList ->
+ JIDList
end
- end;
- Err -> Err
- end;
-find_changed_items(_UJID, _UAffiliation, _URole, _Items,
- _Lang, _StateData, _Res) ->
- {error, ?ERR_BAD_REQUEST}.
+ end,
+ {RoleOrAff, RoleOrAffValue} = if Role == undefined ->
+ {affiliation, Affiliation};
+ true ->
+ {role, Role}
+ end,
+ TAffiliation = get_affiliation(JID, StateData),
+ TRole = get_role(JID, StateData),
+ ServiceAf = get_service_affiliation(JID, StateData),
+ CanChangeRA = case can_change_ra(UAffiliation,
+ URole,
+ TAffiliation,
+ TRole, RoleOrAff, RoleOrAffValue,
+ ServiceAf) of
+ nothing -> nothing;
+ true -> true;
+ check_owner ->
+ case search_affiliation(owner, StateData) of
+ [{OJID, _}] ->
+ jid:remove_resource(OJID)
+ /=
+ jid:tolower(jid:remove_resource(UJID));
+ _ -> true
+ end;
+ _ -> false
+ end,
+ case CanChangeRA of
+ nothing ->
+ find_changed_items(UJID, UAffiliation, URole,
+ Items, Lang, StateData,
+ Res);
+ true ->
+ Reason = if is_binary(Reason0) -> Reason0;
+ true -> <<"">>
+ end,
+ MoreRes = [{jid:remove_resource(Jidx),
+ RoleOrAff, RoleOrAffValue, Reason}
+ || Jidx <- JIDs],
+ find_changed_items(UJID, UAffiliation, URole,
+ Items, Lang, StateData,
+ [MoreRes | Res]);
+ false ->
+ Txt = <<"Changing role/affiliation is not allowed">>,
+ throw({error, xmpp:err_not_allowed(Txt, Lang)})
+ end.
+-spec can_change_ra(affiliation(), role(), affiliation(), role(),
+ affiliation, affiliation(), affiliation()) -> boolean();
+ (affiliation(), role(), affiliation(), role(),
+ role, role(), affiliation()) -> boolean().
can_change_ra(_FAffiliation, _FRole, owner, _TRole,
affiliation, owner, owner) ->
%% A room owner tries to add as persistent owner a
@@ -3255,11 +2857,15 @@ can_change_ra(_FAffiliation, _FRole, _TAffiliation,
_TRole, role, _Value, _ServiceAf) ->
false.
+-spec send_kickban_presence(jid(), jid(), binary(),
+ pos_integer(), state()) -> ok.
send_kickban_presence(UJID, JID, Reason, Code, StateData) ->
NewAffiliation = get_affiliation(JID, StateData),
send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
StateData).
+-spec send_kickban_presence(jid(), jid(), binary(), pos_integer(),
+ affiliation(), state()) -> ok.
send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
StateData) ->
LJID = jid:tolower(JID),
@@ -3288,77 +2894,51 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
end,
LJIDs).
+-spec send_kickban_presence1(jid(), jid(), binary(), pos_integer(),
+ affiliation(), state()) -> ok.
send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
StateData) ->
{ok, #user{jid = RealJID, nick = Nick}} =
(?DICT):find(jid:tolower(UJID),
StateData#state.users),
- SAffiliation = affiliation_to_list(Affiliation),
- BannedJIDString = jid:to_string(RealJID),
ActorNick = get_actor_nick(MJID, StateData),
- lists:foreach(fun ({_LJID, Info}) ->
- JidAttrList = case Info#user.role == moderator orelse
- (StateData#state.config)#config.anonymous
- == false
- of
- true ->
- [{<<"jid">>, BannedJIDString}];
- false -> []
- end,
- ItemAttrs = [{<<"affiliation">>, SAffiliation},
- {<<"role">>, <<"none">>}]
- ++ JidAttrList,
- ItemEls = case Reason of
- <<"">> -> [];
- _ ->
- [#xmlel{name = <<"reason">>,
- attrs = [],
- children =
- [{xmlcdata, Reason}]}]
- end,
- ItemElsActor = case MJID of
- <<"">> -> [];
- _ -> [#xmlel{name = <<"actor">>,
- attrs =
- [{<<"nick">>, ActorNick}]}]
- end,
- Packet = #xmlel{name = <<"presence">>,
- attrs =
- [{<<"type">>, <<"unavailable">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name =
- <<"item">>,
- attrs =
- ItemAttrs,
- children =
- ItemElsActor ++ ItemEls},
- #xmlel{name =
- <<"status">>,
- attrs =
- [{<<"code">>,
- Code}],
- children =
- []}]}]},
- RoomJIDNick = jid:replace_resource(
- StateData#state.jid, Nick),
- send_wrapped(RoomJIDNick, Info#user.jid, Packet,
- ?NS_MUCSUB_NODES_AFFILIATIONS, StateData),
- IsSubscriber = Info#user.is_subscriber,
- IsOccupant = Info#user.last_presence /= undefined,
- if (IsSubscriber and not IsOccupant) ->
- send_wrapped(RoomJIDNick, Info#user.jid, Packet,
- ?NS_MUCSUB_NODES_PARTICIPANTS, StateData);
- true ->
- ok
- end
- end,
- (?DICT):to_list(StateData#state.users)).
+ lists:foreach(
+ fun({_LJID, Info}) ->
+ Item0 = #muc_item{affiliation = Affiliation,
+ role = none},
+ Item1 = case Info#user.role == moderator orelse
+ (StateData#state.config)#config.anonymous
+ == false of
+ true -> Item0#muc_item{jid = RealJID};
+ false -> Item0
+ end,
+ Item2 = if is_binary(Reason), Reason /= <<"">> ->
+ Item1#muc_item{reason = Reason};
+ true ->
+ Item1
+ end,
+ Item = case ActorNick of
+ <<"">> -> Item2;
+ _ -> Item2#muc_item{actor = #muc_actor{nick = ActorNick}}
+ end,
+ Packet = #presence{type = unavailable,
+ sub_els = [#muc_user{items = [Item],
+ status_codes = [Code]}]},
+ RoomJIDNick = jid:replace_resource(StateData#state.jid, Nick),
+ send_wrapped(RoomJIDNick, Info#user.jid, Packet,
+ ?NS_MUCSUB_NODES_AFFILIATIONS, StateData),
+ IsSubscriber = Info#user.is_subscriber,
+ IsOccupant = Info#user.last_presence /= undefined,
+ if (IsSubscriber and not IsOccupant) ->
+ send_wrapped(RoomJIDNick, Info#user.jid, Packet,
+ ?NS_MUCSUB_NODES_PARTICIPANTS, StateData);
+ true ->
+ ok
+ end
+ end,
+ (?DICT):to_list(StateData#state.users)).
+-spec get_actor_nick(binary() | jid(), state()) -> binary().
get_actor_nick(<<"">>, _StateData) ->
<<"">>;
get_actor_nick(MJID, StateData) ->
@@ -3369,97 +2949,86 @@ get_actor_nick(MJID, StateData) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Owner stuff
-
-process_iq_owner(From, set, Lang, SubEl, StateData) ->
+-spec process_iq_owner(jid(), iq(), state()) ->
+ {result, undefined | muc_owner()} |
+ {result, undefined | muc_owner(), state() | stop} |
+ {error, error()}.
+process_iq_owner(From, #iq{type = set, lang = Lang,
+ sub_els = [#muc_owner{destroy = Destroy,
+ config = Config,
+ items = Items}]},
+ StateData) ->
FAffiliation = get_affiliation(From, StateData),
- case FAffiliation of
- owner ->
- #xmlel{children = Els} = SubEl,
- case fxml:remove_cdata(Els) of
- [#xmlel{name = <<"x">>} = XEl] ->
- case {fxml:get_tag_attr_s(<<"xmlns">>, XEl),
- fxml:get_tag_attr_s(<<"type">>, XEl)}
- of
- {?NS_XDATA, <<"cancel">>} -> {result, [], StateData};
- {?NS_XDATA, <<"submit">>} ->
- case is_allowed_log_change(XEl, StateData, From) andalso
- is_allowed_persistent_change(XEl, StateData, From)
- andalso
- is_allowed_room_name_desc_limits(XEl, StateData)
- andalso
- is_password_settings_correct(XEl, StateData)
- of
- true -> set_config(XEl, StateData, Lang);
- false -> {error, ?ERR_NOT_ACCEPTABLE}
- end;
- _ ->
- Txt = <<"Incorrect data form">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}
- end;
- [#xmlel{name = <<"destroy">>} = SubEl1] ->
- ?INFO_MSG("Destroyed MUC room ~s by the owner ~s",
- [jid:to_string(StateData#state.jid),
- jid:to_string(From)]),
- add_to_log(room_existence, destroyed, StateData),
- destroy_room(SubEl1, StateData);
- Items ->
- process_admin_items_set(From, Items, Lang, StateData)
- end;
- _ ->
- ErrText = <<"Owner privileges required">>,
- {error, ?ERRT_FORBIDDEN(Lang, ErrText)}
+ if FAffiliation /= owner ->
+ ErrText = <<"Owner privileges required">>,
+ {error, xmpp:err_forbidden(ErrText, Lang)};
+ Destroy /= undefined, Config == undefined, Items == [] ->
+ ?INFO_MSG("Destroyed MUC room ~s by the owner ~s",
+ [jid:to_string(StateData#state.jid), jid:to_string(From)]),
+ add_to_log(room_existence, destroyed, StateData),
+ destroy_room(Destroy, StateData);
+ Config /= undefined, Destroy == undefined, Items == [] ->
+ case Config of
+ #xdata{type = cancel} ->
+ {result, undefined};
+ #xdata{type = submit} ->
+ case is_allowed_log_change(Config, StateData, From) andalso
+ is_allowed_persistent_change(Config, StateData, From) andalso
+ is_allowed_room_name_desc_limits(Config, StateData) andalso
+ is_password_settings_correct(Config, StateData) of
+ true -> set_config(Config, StateData, Lang);
+ false -> {error, xmpp:err_not_acceptable()}
+ end;
+ _ ->
+ Txt = <<"Incorrect data form">>,
+ {error, xmpp:err_bad_request(Txt, Lang)}
+ end;
+ Items /= [], Config == undefined, Destroy == undefined ->
+ process_admin_items_set(From, Items, Lang, StateData);
+ true ->
+ {error, xmpp:err_bad_request()}
end;
-process_iq_owner(From, get, Lang, SubEl, StateData) ->
+process_iq_owner(From, #iq{type = get, lang = Lang,
+ sub_els = [#muc_owner{destroy = Destroy,
+ config = Config,
+ items = Items}]},
+ StateData) ->
FAffiliation = get_affiliation(From, StateData),
- case FAffiliation of
- owner ->
- #xmlel{children = Els} = SubEl,
- case fxml:remove_cdata(Els) of
- [] -> get_config(Lang, StateData, From);
- [Item] ->
- case fxml:get_tag_attr(<<"affiliation">>, Item) of
- false ->
- Txt = <<"No 'affiliation' attribute found">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
- {value, StrAffiliation} ->
- case catch list_to_affiliation(StrAffiliation) of
- {'EXIT', _} ->
- ErrText = iolist_to_binary(
- io_lib:format(
- translate:translate(
- Lang,
- <<"Invalid affiliation: ~s">>),
- [StrAffiliation])),
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
- SAffiliation ->
- Items = items_with_affiliation(SAffiliation,
- StateData),
- {result, Items, StateData}
- end
- end;
- _ -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
- end;
- _ ->
- ErrText = <<"Owner privileges required">>,
- {error, ?ERRT_FORBIDDEN(Lang, ErrText)}
+ if FAffiliation /= owner ->
+ ErrText = <<"Owner privileges required">>,
+ {error, xmpp:err_forbidden(ErrText, Lang)};
+ Destroy == undefined, Config == undefined ->
+ case Items of
+ [] ->
+ {result,
+ #muc_owner{config = get_config(Lang, StateData, From)}};
+ [#muc_item{affiliation = undefined}] ->
+ Txt = <<"No 'affiliation' attribute found">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
+ [#muc_item{affiliation = Affiliation}] ->
+ Items = items_with_affiliation(Affiliation, StateData),
+ {result, #muc_owner{items = Items}};
+ [_|_] ->
+ Txt = <<"Too many <item/> elements">>,
+ {error, xmpp:err_bad_request(Txt, Lang)}
+ end;
+ true ->
+ {error, xmpp:err_bad_request()}
end.
-is_allowed_log_change(XEl, StateData, From) ->
- case lists:keymember(<<"muc#roomconfig_enablelogging">>,
- 1, jlib:parse_xdata_submit(XEl))
- of
- false -> true;
- true ->
- allow ==
- mod_muc_log:check_access_log(StateData#state.server_host,
- From)
+-spec is_allowed_log_change(xdata(), state(), jid()) -> boolean().
+is_allowed_log_change(X, StateData, From) ->
+ case xmpp_util:has_xdata_var(<<"muc#roomconfig_enablelogging">>, X) of
+ false -> true;
+ true ->
+ allow ==
+ mod_muc_log:check_access_log(StateData#state.server_host,
+ From)
end.
-is_allowed_persistent_change(XEl, StateData, From) ->
- case
- lists:keymember(<<"muc#roomconfig_persistentroom">>, 1,
- jlib:parse_xdata_submit(XEl))
- of
+-spec is_allowed_persistent_change(xdata(), state(), jid()) -> boolean().
+is_allowed_persistent_change(X, StateData, From) ->
+ case xmpp_util:has_xdata_var(<<"muc#roomconfig_persistentroom">>, X) of
false -> true;
true ->
{_AccessRoute, _AccessCreate, _AccessAdmin,
@@ -3472,58 +3041,57 @@ is_allowed_persistent_change(XEl, StateData, From) ->
%% Check if the Room Name and Room Description defined in the Data Form
%% are conformant to the configured limits
-is_allowed_room_name_desc_limits(XEl, StateData) ->
- IsNameAccepted = case
- lists:keysearch(<<"muc#roomconfig_roomname">>, 1,
- jlib:parse_xdata_submit(XEl))
- of
- {value, {_, [N]}} ->
- byte_size(N) =<
- gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, max_room_name,
- fun(infinity) -> infinity;
- (I) when is_integer(I),
- I>0 -> I
- end, infinity);
- _ -> true
+-spec is_allowed_room_name_desc_limits(xdata(), state()) -> boolean().
+is_allowed_room_name_desc_limits(XData, StateData) ->
+ IsNameAccepted = case xmpp_util:get_xdata_values(
+ <<"muc#roomconfig_roomname">>, XData) of
+ [N] ->
+ byte_size(N) =<
+ gen_mod:get_module_opt(
+ StateData#state.server_host,
+ mod_muc, max_room_name,
+ fun(infinity) -> infinity;
+ (I) when is_integer(I),
+ I>0 -> I
+ end, infinity);
+ _ ->
+ true
end,
- IsDescAccepted = case
- lists:keysearch(<<"muc#roomconfig_roomdesc">>, 1,
- jlib:parse_xdata_submit(XEl))
- of
- {value, {_, [D]}} ->
- byte_size(D) =<
- gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, max_room_desc,
- fun(infinity) -> infinity;
- (I) when is_integer(I),
- I>0 ->
- I
- end, infinity);
- _ -> true
+ IsDescAccepted = case xmpp_util:get_xdata_values(
+ <<"muc#roomconfig_roomdesc">>, XData) of
+ [D] ->
+ byte_size(D) =<
+ gen_mod:get_module_opt(
+ StateData#state.server_host,
+ mod_muc, max_room_desc,
+ fun(infinity) -> infinity;
+ (I) when is_integer(I),
+ I>0 ->
+ I
+ end, infinity);
+ _ -> true
end,
IsNameAccepted and IsDescAccepted.
%% Return false if:
%% "the password for a password-protected room is blank"
-is_password_settings_correct(XEl, StateData) ->
+-spec is_password_settings_correct(xdata(), state()) -> boolean().
+is_password_settings_correct(XData, StateData) ->
Config = StateData#state.config,
OldProtected = Config#config.password_protected,
OldPassword = Config#config.password,
- NewProtected = case
- lists:keysearch(<<"muc#roomconfig_passwordprotectedroom">>,
- 1, jlib:parse_xdata_submit(XEl))
- of
- {value, {_, [<<"1">>]}} -> true;
- {value, {_, [<<"0">>]}} -> false;
- _ -> undefined
+ NewProtected = case xmpp_util:get_xdata_values(
+ <<"muc#roomconfig_passwordprotectedroom">>, XData) of
+ [<<"1">>] -> true;
+ [<<"true">>] -> true;
+ [<<"0">>] -> false;
+ [<<"false">>] -> false;
+ _ -> undefined
end,
- NewPassword = case
- lists:keysearch(<<"muc#roomconfig_roomsecret">>, 1,
- jlib:parse_xdata_submit(XEl))
- of
- {value, {_, [P]}} -> P;
- _ -> undefined
+ NewPassword = case xmpp_util:get_xdata_values(
+ <<"muc#roomconfig_roomsecret">>, XData) of
+ [P] -> P;
+ _ -> undefined
end,
case {OldProtected, NewProtected, OldPassword,
NewPassword}
@@ -3535,40 +3103,35 @@ is_password_settings_correct(XEl, StateData) ->
_ -> true
end.
--define(XFIELD(Type, Label, Var, Val),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
+-define(XFIELD(Type, Label, Var, Vals),
+ #xdata_field{type = Type,
+ label = translate:translate(Lang, Label),
+ var = Var,
+ values = Vals}).
-define(BOOLXFIELD(Label, Var, Val),
- ?XFIELD(<<"boolean">>, Label, Var,
+ ?XFIELD(boolean, Label, Var,
case Val of
- true -> <<"1">>;
- _ -> <<"0">>
+ true -> [<<"1">>];
+ _ -> [<<"0">>]
end)).
-define(STRINGXFIELD(Label, Var, Val),
- ?XFIELD(<<"text-single">>, Label, Var, Val)).
+ ?XFIELD('text-single', Label, Var, [Val])).
-define(PRIVATEXFIELD(Label, Var, Val),
- ?XFIELD(<<"text-private">>, Label, Var, Val)).
+ ?XFIELD('text-private', Label, Var, [Val])).
-define(JIDMULTIXFIELD(Label, Var, JIDList),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"jid-multi">>},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, jid:to_string(JID)}]}
- || JID <- JIDList]}).
+ ?XFIELD('jid-multi', Label, Var,
+ [jid:to_string(JID) || JID <- JIDList])).
+-spec make_options([{binary(), binary()}], binary()) -> [xdata_option()].
+make_options(Options, Lang) ->
+ [#xdata_option{label = translate:translate(Lang, Label),
+ value = Value} || {Label, Value} <- Options].
+
+-spec get_default_room_maxusers(state()) -> non_neg_integer().
get_default_room_maxusers(RoomState) ->
DefRoomOpts =
gen_mod:get_module_opt(RoomState#state.server_host,
@@ -3578,342 +3141,193 @@ get_default_room_maxusers(RoomState) ->
RoomState2 = set_opts(DefRoomOpts, RoomState),
(RoomState2#state.config)#config.max_users.
+-spec get_config(binary(), state(), jid()) -> xdata().
get_config(Lang, StateData, From) ->
- {_AccessRoute, _AccessCreate, _AccessAdmin,
- AccessPersistent} =
+ {_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} =
StateData#state.access,
ServiceMaxUsers = get_service_max_users(StateData),
- DefaultRoomMaxUsers =
- get_default_room_maxusers(StateData),
+ DefaultRoomMaxUsers = get_default_room_maxusers(StateData),
Config = StateData#state.config,
- {MaxUsersRoomInteger, MaxUsersRoomString} = case
- get_max_users(StateData)
- of
- N when is_integer(N) ->
- {N,
- jlib:integer_to_binary(N)};
- _ -> {0, <<"none">>}
- end,
- Res = [#xmlel{name = <<"title">>, attrs = [],
- children =
- [{xmlcdata,
- iolist_to_binary(
- io_lib:format(
- translate:translate(
- Lang,
- <<"Configuration of room ~s">>),
- [jid:to_string(StateData#state.jid)]))}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"hidden">>},
- {<<"var">>, <<"FORM_TYPE">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"http://jabber.org/protocol/muc#roomconfig">>}]}]},
- ?STRINGXFIELD(<<"Room title">>,
- <<"muc#roomconfig_roomname">>, (Config#config.title)),
- ?STRINGXFIELD(<<"Room description">>,
- <<"muc#roomconfig_roomdesc">>,
- (Config#config.description))]
- ++
- case acl:match_rule(StateData#state.server_host,
- AccessPersistent, From)
- of
- allow ->
- [?BOOLXFIELD(<<"Make room persistent">>,
- <<"muc#roomconfig_persistentroom">>,
- (Config#config.persistent))];
- _ -> []
- end
- ++
- [?BOOLXFIELD(<<"Make room public searchable">>,
- <<"muc#roomconfig_publicroom">>,
- (Config#config.public)),
- ?BOOLXFIELD(<<"Make participants list public">>,
- <<"public_list">>, (Config#config.public_list)),
- ?BOOLXFIELD(<<"Make room password protected">>,
- <<"muc#roomconfig_passwordprotectedroom">>,
- (Config#config.password_protected)),
- ?PRIVATEXFIELD(<<"Password">>,
- <<"muc#roomconfig_roomsecret">>,
- case Config#config.password_protected of
- true -> Config#config.password;
- false -> <<"">>
- end),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"list-single">>},
- {<<"label">>,
- translate:translate(Lang,
- <<"Maximum Number of Occupants">>)},
- {<<"var">>, <<"muc#roomconfig_maxusers">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, MaxUsersRoomString}]}]
- ++
- if is_integer(ServiceMaxUsers) -> [];
- true ->
- [#xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"No limit">>)}],
- children =
- [#xmlel{name = <<"value">>,
- attrs = [],
- children =
- [{xmlcdata,
- <<"none">>}]}]}]
- end
- ++
- [#xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- jlib:integer_to_binary(N)}],
- children =
- [#xmlel{name = <<"value">>,
- attrs = [],
- children =
- [{xmlcdata,
- jlib:integer_to_binary(N)}]}]}
- || N
- <- lists:usort([ServiceMaxUsers,
- DefaultRoomMaxUsers,
- MaxUsersRoomInteger
- | ?MAX_USERS_DEFAULT_LIST]),
- N =< ServiceMaxUsers]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"list-single">>},
- {<<"label">>,
- translate:translate(Lang,
- <<"Present real Jabber IDs to">>)},
- {<<"var">>, <<"muc#roomconfig_whois">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- if Config#config.anonymous ->
- <<"moderators">>;
- true -> <<"anyone">>
- end}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"moderators only">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"moderators">>}]}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"anyone">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"anyone">>}]}]}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"list-multi">>},
- {<<"label">>,
- translate:translate(Lang,
- <<"Roles for which Presence is Broadcasted">>)},
- {<<"var">>, <<"muc#roomconfig_presencebroadcast">>}],
- children =
- lists:map(
- fun(Role) ->
- #xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- atom_to_binary(Role, utf8)}]}
- end, Config#config.presence_broadcast
- ) ++
- [#xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"Moderator">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"moderator">>}]}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"Participant">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"participant">>}]}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"Visitor">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"visitor">>}]}]}
- ]},
- ?BOOLXFIELD(<<"Make room members-only">>,
- <<"muc#roomconfig_membersonly">>,
- (Config#config.members_only)),
- ?BOOLXFIELD(<<"Make room moderated">>,
- <<"muc#roomconfig_moderatedroom">>,
- (Config#config.moderated)),
- ?BOOLXFIELD(<<"Default users as participants">>,
- <<"members_by_default">>,
- (Config#config.members_by_default)),
- ?BOOLXFIELD(<<"Allow users to change the subject">>,
- <<"muc#roomconfig_changesubject">>,
- (Config#config.allow_change_subj)),
- ?BOOLXFIELD(<<"Allow users to send private messages">>,
- <<"allow_private_messages">>,
- (Config#config.allow_private_messages)),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, <<"list-single">>},
- {<<"label">>,
- translate:translate(Lang,
- <<"Allow visitors to send private messages to">>)},
- {<<"var">>,
- <<"allow_private_messages_from_visitors">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- case
- Config#config.allow_private_messages_from_visitors
- of
- anyone -> <<"anyone">>;
- moderators -> <<"moderators">>;
- nobody -> <<"nobody">>
- end}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"nobody">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata, <<"nobody">>}]}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"moderators only">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"moderators">>}]}]},
- #xmlel{name = <<"option">>,
- attrs =
- [{<<"label">>,
- translate:translate(Lang,
- <<"anyone">>)}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"anyone">>}]}]}]},
- ?BOOLXFIELD(<<"Allow users to query other users">>,
- <<"allow_query_users">>,
- (Config#config.allow_query_users)),
- ?BOOLXFIELD(<<"Allow users to send invites">>,
- <<"muc#roomconfig_allowinvites">>,
- (Config#config.allow_user_invites)),
- ?BOOLXFIELD(<<"Allow visitors to send status text in "
- "presence updates">>,
- <<"muc#roomconfig_allowvisitorstatus">>,
- (Config#config.allow_visitor_status)),
- ?BOOLXFIELD(<<"Allow visitors to change nickname">>,
- <<"muc#roomconfig_allowvisitornickchange">>,
- (Config#config.allow_visitor_nickchange)),
- ?BOOLXFIELD(<<"Allow visitors to send voice requests">>,
- <<"muc#roomconfig_allowvoicerequests">>,
- (Config#config.allow_voice_requests)),
- ?BOOLXFIELD(<<"Allow subscription">>,
- <<"muc#roomconfig_allow_subscription">>,
- (Config#config.allow_subscription)),
- ?STRINGXFIELD(<<"Minimum interval between voice requests "
- "(in seconds)">>,
- <<"muc#roomconfig_voicerequestmininterval">>,
- (jlib:integer_to_binary(Config#config.voice_request_min_interval)))]
- ++
- case ejabberd_captcha:is_feature_available() of
- true ->
- [?BOOLXFIELD(<<"Make room CAPTCHA protected">>,
- <<"captcha_protected">>,
- (Config#config.captcha_protected))];
- false -> []
- end ++
- [?JIDMULTIXFIELD(<<"Exclude Jabber IDs from CAPTCHA challenge">>,
- <<"muc#roomconfig_captcha_whitelist">>,
- ((?SETS):to_list(Config#config.captcha_whitelist)))]
- ++
- case
- mod_muc_log:check_access_log(StateData#state.server_host,
- From)
- of
- allow ->
- [?BOOLXFIELD(<<"Enable logging">>,
- <<"muc#roomconfig_enablelogging">>,
- (Config#config.logging))];
- _ -> []
- end,
- X = ejabberd_hooks:run_fold(get_room_config,
- StateData#state.server_host,
- Res,
- [StateData, From, Lang]),
- {result,
- [#xmlel{name = <<"instructions">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"You need an x:data capable client to "
- "configure room">>)}]},
- #xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children = X}],
- StateData}.
-
-set_config(XEl, StateData, Lang) ->
- XData = jlib:parse_xdata_submit(XEl),
- case XData of
- invalid -> {error, ?ERRT_BAD_REQUEST(Lang, <<"Incorrect data form">>)};
- _ ->
- case set_xoption(XData, StateData#state.config,
- StateData#state.server_host, Lang) of
- #config{} = Config ->
- Res = change_config(Config, StateData),
- {result, _, NSD} = Res,
- Type = case {(StateData#state.config)#config.logging,
- Config#config.logging}
- of
- {true, false} -> roomconfig_change_disabledlogging;
- {false, true} -> roomconfig_change_enabledlogging;
- {_, _} -> roomconfig_change
- end,
- Users = [{U#user.jid, U#user.nick, U#user.role}
- || {_, U} <- (?DICT):to_list(StateData#state.users)],
- add_to_log(Type, Users, NSD),
- Res;
- Err -> Err
- end
+ {MaxUsersRoomInteger, MaxUsersRoomString} =
+ case get_max_users(StateData) of
+ N when is_integer(N) ->
+ {N, integer_to_binary(N)};
+ _ -> {0, <<"none">>}
+ end,
+ Title = iolist_to_binary(
+ io_lib:format(
+ translate:translate(Lang, <<"Configuration of room ~s">>),
+ [jid:to_string(StateData#state.jid)])),
+ Fs = [#xdata_field{type = hidden,
+ var = <<"FORM_TYPE">>,
+ values = [<<"http://jabber.org/protocol/muc#roomconfig">>]},
+ ?STRINGXFIELD(<<"Room title">>,
+ <<"muc#roomconfig_roomname">>, (Config#config.title)),
+ ?STRINGXFIELD(<<"Room description">>,
+ <<"muc#roomconfig_roomdesc">>,
+ (Config#config.description))] ++
+ case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of
+ allow ->
+ [?BOOLXFIELD(<<"Make room persistent">>,
+ <<"muc#roomconfig_persistentroom">>,
+ (Config#config.persistent))];
+ deny -> []
+ end ++
+ [?BOOLXFIELD(<<"Make room public searchable">>,
+ <<"muc#roomconfig_publicroom">>,
+ (Config#config.public)),
+ ?BOOLXFIELD(<<"Make participants list public">>,
+ <<"public_list">>, (Config#config.public_list)),
+ ?BOOLXFIELD(<<"Make room password protected">>,
+ <<"muc#roomconfig_passwordprotectedroom">>,
+ (Config#config.password_protected)),
+ ?PRIVATEXFIELD(<<"Password">>,
+ <<"muc#roomconfig_roomsecret">>,
+ case Config#config.password_protected of
+ true -> Config#config.password;
+ false -> <<"">>
+ end),
+ #xdata_field{type = 'list-single',
+ label = translate:translate(
+ Lang, <<"Maximum Number of Occupants">>),
+ var = <<"muc#roomconfig_maxusers">>,
+ values = [MaxUsersRoomString],
+ options =
+ if is_integer(ServiceMaxUsers) -> [];
+ true -> make_options(
+ [{<<"No limit">>, <<"none">>}],
+ Lang)
+ end ++
+ make_options(
+ [{integer_to_binary(N), integer_to_binary(N)}
+ || N <- lists:usort([ServiceMaxUsers,
+ DefaultRoomMaxUsers,
+ MaxUsersRoomInteger
+ | ?MAX_USERS_DEFAULT_LIST]),
+ N =< ServiceMaxUsers],
+ Lang)},
+ #xdata_field{type = 'list-single',
+ label = translate:translate(
+ Lang, <<"Present real Jabber IDs to">>),
+ var = <<"muc#roomconfig_whois">>,
+ values = [if Config#config.anonymous -> <<"moderators">>;
+ true -> <<"anyone">>
+ end],
+ options = make_options(
+ [{<<"moderators only">>, <<"moderators">>},
+ {<<"anyone">>, <<"anyone">>}],
+ Lang)},
+ #xdata_field{type = 'list-multi',
+ label = translate:translate(
+ Lang,
+ <<"Roles for which Presence is Broadcasted">>),
+ var = <<"muc#roomconfig_presencebroadcast">>,
+ values = [atom_to_binary(Role, utf8)
+ || Role <- Config#config.presence_broadcast],
+ options = make_options(
+ [{<<"Moderator">>, <<"moderator">>},
+ {<<"Participant">>, <<"participant">>},
+ {<<"Visitor">>, <<"visitor">>}],
+ Lang)},
+ ?BOOLXFIELD(<<"Make room members-only">>,
+ <<"muc#roomconfig_membersonly">>,
+ (Config#config.members_only)),
+ ?BOOLXFIELD(<<"Make room moderated">>,
+ <<"muc#roomconfig_moderatedroom">>,
+ (Config#config.moderated)),
+ ?BOOLXFIELD(<<"Default users as participants">>,
+ <<"members_by_default">>,
+ (Config#config.members_by_default)),
+ ?BOOLXFIELD(<<"Allow users to change the subject">>,
+ <<"muc#roomconfig_changesubject">>,
+ (Config#config.allow_change_subj)),
+ ?BOOLXFIELD(<<"Allow users to send private messages">>,
+ <<"allow_private_messages">>,
+ (Config#config.allow_private_messages)),
+ #xdata_field{type = 'list-single',
+ label = translate:translate(
+ Lang,
+ <<"Allow visitors to send private messages to">>),
+ var = <<"allow_private_messages_from_visitors">>,
+ values = [case Config#config.allow_private_messages_from_visitors of
+ anyone -> <<"anyone">>;
+ moderators -> <<"moderators">>;
+ nobody -> <<"nobody">>
+ end],
+ options = make_options(
+ [{<<"nobody">>, <<"nobody">>},
+ {<<"moderators only">>, <<"moderators">>},
+ {<<"anyone">>, <<"anyone">>}],
+ Lang)},
+ ?BOOLXFIELD(<<"Allow users to query other users">>,
+ <<"allow_query_users">>,
+ (Config#config.allow_query_users)),
+ ?BOOLXFIELD(<<"Allow users to send invites">>,
+ <<"muc#roomconfig_allowinvites">>,
+ (Config#config.allow_user_invites)),
+ ?BOOLXFIELD(<<"Allow visitors to send status text in "
+ "presence updates">>,
+ <<"muc#roomconfig_allowvisitorstatus">>,
+ (Config#config.allow_visitor_status)),
+ ?BOOLXFIELD(<<"Allow visitors to change nickname">>,
+ <<"muc#roomconfig_allowvisitornickchange">>,
+ (Config#config.allow_visitor_nickchange)),
+ ?BOOLXFIELD(<<"Allow visitors to send voice requests">>,
+ <<"muc#roomconfig_allowvoicerequests">>,
+ (Config#config.allow_voice_requests)),
+ ?BOOLXFIELD(<<"Allow subscription">>,
+ <<"muc#roomconfig_allow_subscription">>,
+ (Config#config.allow_subscription)),
+ ?STRINGXFIELD(<<"Minimum interval between voice requests "
+ "(in seconds)">>,
+ <<"muc#roomconfig_voicerequestmininterval">>,
+ integer_to_binary(Config#config.voice_request_min_interval))]
+ ++
+ case ejabberd_captcha:is_feature_available() of
+ true ->
+ [?BOOLXFIELD(<<"Make room CAPTCHA protected">>,
+ <<"captcha_protected">>,
+ (Config#config.captcha_protected))];
+ false -> []
+ end ++
+ [?JIDMULTIXFIELD(<<"Exclude Jabber IDs from CAPTCHA challenge">>,
+ <<"muc#roomconfig_captcha_whitelist">>,
+ ((?SETS):to_list(Config#config.captcha_whitelist)))]
+ ++
+ case mod_muc_log:check_access_log(StateData#state.server_host, From) of
+ allow ->
+ [?BOOLXFIELD(<<"Enable logging">>,
+ <<"muc#roomconfig_enablelogging">>,
+ (Config#config.logging))];
+ deny -> []
+ end,
+ Fields = ejabberd_hooks:run_fold(get_room_config,
+ StateData#state.server_host,
+ Fs,
+ [StateData, From, Lang]),
+ #xdata{type = form, title = Title, fields = Fields}.
+
+-spec set_config(xdata(), state(), binary()) -> {error, error()} |
+ {result, undefined, state()}.
+set_config(#xdata{fields = Fields}, StateData, Lang) ->
+ Options = [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- Fields],
+ case set_xoption(Options, StateData#state.config,
+ StateData#state.server_host, Lang) of
+ #config{} = Config ->
+ Res = change_config(Config, StateData),
+ {result, _, NSD} = Res,
+ Type = case {(StateData#state.config)#config.logging,
+ Config#config.logging}
+ of
+ {true, false} -> roomconfig_change_disabledlogging;
+ {false, true} -> roomconfig_change_enabledlogging;
+ {_, _} -> roomconfig_change
+ end,
+ Users = [{U#user.jid, U#user.nick, U#user.role}
+ || {_, U} <- (?DICT):to_list(StateData#state.users)],
+ add_to_log(Type, Users, NSD),
+ Res;
+ Err -> Err
end.
-define(SET_BOOL_XOPT(Opt, Val),
@@ -3928,21 +3342,32 @@ set_config(XEl, StateData, Lang) ->
_ ->
Txt = <<"Value of '~s' should be boolean">>,
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
- {error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)}
+ {error, xmpp:err_bad_request(ErrTxt, Lang)}
end).
-define(SET_NAT_XOPT(Opt, Val),
- case catch jlib:binary_to_integer(Val) of
+ case catch binary_to_integer(Val) of
I when is_integer(I), I > 0 ->
set_xoption(Opts, Config#config{Opt = I}, ServerHost, Lang);
_ ->
Txt = <<"Value of '~s' should be integer">>,
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
- {error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)}
+ {error, xmpp:err_bad_request(ErrTxt, Lang)}
end).
--define(SET_STRING_XOPT(Opt, Val),
- set_xoption(Opts, Config#config{Opt = Val}, ServerHost, Lang)).
+-define(SET_STRING_XOPT(Opt, Vals),
+ try
+ V = case Vals of
+ [] -> <<"">>;
+ [Val] -> Val;
+ _ when is_atom(Vals) -> Vals
+ end,
+ set_xoption(Opts, Config#config{Opt = V}, ServerHost, Lang)
+ catch _:_ ->
+ Txt = <<"Incorrect value of option '~s'">>,
+ ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
+ {error, xmpp:err_bad_request(ErrTxt, Lang)}
+ end).
-define(SET_JIDMULTI_XOPT(Opt, Vals),
begin
@@ -3957,15 +3382,17 @@ set_config(XEl, StateData, Lang) ->
set_xoption(Opts, Config#config{Opt = Set}, ServerHost, Lang)
end).
+-spec set_xoption([{binary(), [binary()]}], #config{},
+ binary(), binary()) -> #config{} | {error, error()}.
set_xoption([], Config, _ServerHost, _Lang) -> Config;
-set_xoption([{<<"muc#roomconfig_roomname">>, [Val]}
+set_xoption([{<<"muc#roomconfig_roomname">>, Vals}
| Opts],
Config, ServerHost, Lang) ->
- ?SET_STRING_XOPT(title, Val);
-set_xoption([{<<"muc#roomconfig_roomdesc">>, [Val]}
+ ?SET_STRING_XOPT(title, Vals);
+set_xoption([{<<"muc#roomconfig_roomdesc">>, Vals}
| Opts],
Config, ServerHost, Lang) ->
- ?SET_STRING_XOPT(description, Val);
+ ?SET_STRING_XOPT(description, Vals);
set_xoption([{<<"muc#roomconfig_changesubject">>, [Val]}
| Opts],
Config, ServerHost, Lang) ->
@@ -3994,7 +3421,7 @@ set_xoption([{<<"allow_private_messages_from_visitors">>,
_ ->
Txt = <<"Value of 'allow_private_messages_from_visitors' "
"should be anyone|moderators|nobody">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}
+ {error, xmpp:err_bad_request(Txt, Lang)}
end;
set_xoption([{<<"muc#roomconfig_allowvisitorstatus">>,
[Val]}
@@ -4045,10 +3472,10 @@ set_xoption([{<<"muc#roomconfig_passwordprotectedroom">>,
| Opts],
Config, ServerHost, Lang) ->
?SET_BOOL_XOPT(password_protected, Val);
-set_xoption([{<<"muc#roomconfig_roomsecret">>, [Val]}
+set_xoption([{<<"muc#roomconfig_roomsecret">>, Vals}
| Opts],
Config, ServerHost, Lang) ->
- ?SET_STRING_XOPT(password, Val);
+ ?SET_STRING_XOPT(password, Vals);
set_xoption([{<<"anonymous">>, [Val]} | Opts],
Config, ServerHost, Lang) ->
?SET_BOOL_XOPT(anonymous, Val);
@@ -4069,7 +3496,7 @@ set_xoption([{<<"muc#roomconfig_presencebroadcast">>, Vals} | Opts],
error ->
Txt = <<"Value of 'muc#roomconfig_presencebroadcast' should "
"be moderator|participant|visitor">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
+ {error, xmpp:err_bad_request(Txt, Lang)};
{M, P, V} ->
Res =
if M -> [moderator]; true -> [] end ++
@@ -4101,7 +3528,7 @@ set_xoption([{<<"muc#roomconfig_whois">>, [Val]}
_ ->
Txt = <<"Value of 'muc#roomconfig_whois' should be "
"moderators|anyone">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}
+ {error, xmpp:err_bad_request(Txt, Lang)}
end;
set_xoption([{<<"muc#roomconfig_maxusers">>, [Val]}
| Opts],
@@ -4125,7 +3552,7 @@ set_xoption([{<<"FORM_TYPE">>, _} | Opts], Config, ServerHost, Lang) ->
set_xoption([{Opt, Vals} | Opts], Config, ServerHost, Lang) ->
Txt = <<"Unknown option '~s'">>,
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
- Err = {error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)},
+ Err = {error, xmpp:err_bad_request(ErrTxt, Lang)},
case ejabberd_hooks:run_fold(set_room_option,
ServerHost,
Err,
@@ -4136,6 +3563,7 @@ set_xoption([{Opt, Vals} | Opts], Config, ServerHost, Lang) ->
set_xoption(Opts, setelement(Pos, Config, Val), ServerHost, Lang)
end.
+-spec change_config(#config{}, state()) -> {result, undefined, state()}.
change_config(Config, StateData) ->
send_config_change_info(Config, StateData),
NSD = remove_subscriptions(StateData#state{config = Config}),
@@ -4154,57 +3582,54 @@ change_config(Config, StateData) ->
Config#config.members_only}
of
{false, true} ->
- NSD1 = remove_nonmembers(NSD), {result, [], NSD1};
- _ -> {result, [], NSD}
+ NSD1 = remove_nonmembers(NSD), {result, undefined, NSD1};
+ _ -> {result, undefined, NSD}
end.
+-spec send_config_change_info(#config{}, state()) -> ok.
send_config_change_info(Config, #state{config = Config}) -> ok;
send_config_change_info(New, #state{config = Old} = StateData) ->
Codes = case {Old#config.logging, New#config.logging} of
- {false, true} -> [<<"170">>];
- {true, false} -> [<<"171">>];
+ {false, true} -> [170];
+ {true, false} -> [171];
_ -> []
end
++
case {Old#config.anonymous, New#config.anonymous} of
- {true, false} -> [<<"172">>];
- {false, true} -> [<<"173">>];
+ {true, false} -> [172];
+ {false, true} -> [173];
_ -> []
end
++
case Old#config{anonymous = New#config.anonymous,
logging = New#config.logging} of
New -> [];
- _ -> [<<"104">>]
+ _ -> [104]
end,
- StatusEls = [#xmlel{name = <<"status">>,
- attrs = [{<<"code">>, Code}],
- children = []} || Code <- Codes],
- Message = #xmlel{name = <<"message">>,
- attrs = [{<<"type">>, <<"groupchat">>},
- {<<"id">>, randoms:get_string()}],
- children = [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
- children = StatusEls}]},
+ Message = #message{type = groupchat,
+ id = randoms:get_string(),
+ sub_els = [#muc_user{status_codes = Codes}]},
send_wrapped_multiple(StateData#state.jid,
StateData#state.users,
Message,
?NS_MUCSUB_NODES_CONFIG,
StateData).
+-spec remove_nonmembers(state()) -> state().
remove_nonmembers(StateData) ->
lists:foldl(fun ({_LJID, #user{jid = JID}}, SD) ->
Affiliation = get_affiliation(JID, SD),
case Affiliation of
none ->
catch send_kickban_presence(<<"">>, JID, <<"">>,
- <<"322">>, SD),
+ 322, SD),
set_role(JID, none, SD);
_ -> SD
end
end,
StateData, (?DICT):to_list(StateData#state.users)).
+-spec set_opts([{atom(), any()}], state()) -> state().
set_opts([], StateData) -> StateData;
set_opts([{Opt, Val} | Opts], StateData) ->
NSD = case Opt of
@@ -4342,7 +3767,7 @@ set_opts([{Opt, Val} | Opts], StateData) ->
-define(MAKE_CONFIG_OPT(Opt), {Opt, Config#config.Opt}).
-
+-spec make_opts(state()) -> [{atom(), any()}].
make_opts(StateData) ->
Config = StateData#state.config,
Subscribers = (?DICT):fold(
@@ -4381,243 +3806,221 @@ make_opts(StateData) ->
{subject_author, StateData#state.subject_author},
{subscribers, Subscribers}].
+-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
destroy_room(DEl, StateData) ->
- lists:foreach(fun ({_LJID, Info}) ->
- Nick = Info#user.nick,
- ItemAttrs = [{<<"affiliation">>, <<"none">>},
- {<<"role">>, <<"none">>}],
- Packet = #xmlel{name = <<"presence">>,
- attrs =
- [{<<"type">>, <<"unavailable">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_MUC_USER}],
- children =
- [#xmlel{name =
- <<"item">>,
- attrs =
- ItemAttrs,
- children =
- []},
- DEl]}]},
- send_wrapped(jid:replace_resource(StateData#state.jid,
- Nick),
- Info#user.jid, Packet,
- ?NS_MUCSUB_NODES_CONFIG, StateData)
- end,
- (?DICT):to_list(StateData#state.users)),
+ Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
+ lists:foreach(
+ fun({_LJID, Info}) ->
+ Nick = Info#user.nick,
+ Item = #muc_item{affiliation = none,
+ role = none},
+ Packet = #presence{
+ type = unavailable,
+ sub_els = [#muc_user{items = [Item],
+ destroy = Destroy}]},
+ send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
+ Info#user.jid, Packet,
+ ?NS_MUCSUB_NODES_CONFIG, StateData)
+ end,
+ (?DICT):to_list(StateData#state.users)),
case (StateData#state.config)#config.persistent of
true ->
mod_muc:forget_room(StateData#state.server_host,
StateData#state.host, StateData#state.room);
false -> ok
end,
- {result, [], stop}.
+ {result, undefined, stop}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Disco
--define(FEATURE(Var),
- #xmlel{name = <<"feature">>, attrs = [{<<"var">>, Var}],
- children = []}).
-
-define(CONFIG_OPT_TO_FEATURE(Opt, Fiftrue, Fiffalse),
case Opt of
- true -> ?FEATURE(Fiftrue);
- false -> ?FEATURE(Fiffalse)
+ true -> Fiftrue;
+ false -> Fiffalse
end).
-process_iq_disco_info(_From, set, Lang, _StateData) ->
+-spec process_iq_disco_info(jid(), iq(), state()) ->
+ {result, disco_info()} | {error, error()}.
+process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)};
-process_iq_disco_info(_From, get, Lang, StateData) ->
+ {error, xmpp:err_not_allowed(Txt, Lang)};
+process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
Config = StateData#state.config,
- {result,
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"conference">>},
- {<<"type">>, <<"text">>},
- {<<"name">>, get_title(StateData)}],
- children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_VCARD}], children = []},
- #xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_MUC}], children = []},
- ?CONFIG_OPT_TO_FEATURE((Config#config.public),
- <<"muc_public">>, <<"muc_hidden">>),
- ?CONFIG_OPT_TO_FEATURE((Config#config.persistent),
- <<"muc_persistent">>, <<"muc_temporary">>),
- ?CONFIG_OPT_TO_FEATURE((Config#config.members_only),
- <<"muc_membersonly">>, <<"muc_open">>),
- ?CONFIG_OPT_TO_FEATURE((Config#config.anonymous),
- <<"muc_semianonymous">>, <<"muc_nonanonymous">>),
- ?CONFIG_OPT_TO_FEATURE((Config#config.moderated),
- <<"muc_moderated">>, <<"muc_unmoderated">>),
- ?CONFIG_OPT_TO_FEATURE((Config#config.password_protected),
- <<"muc_passwordprotected">>, <<"muc_unsecured">>)]
- ++ case Config#config.allow_subscription of
- true -> [?FEATURE(?NS_MUCSUB)];
- false -> []
- end
- ++ case {gen_mod:is_loaded(StateData#state.server_host, mod_mam),
- Config#config.mam} of
- {true, true} ->
- [?FEATURE(?NS_MAM_TMP),
- ?FEATURE(?NS_MAM_0),
- ?FEATURE(?NS_MAM_1)];
- _ ->
- []
- end
- ++ iq_disco_info_extras(Lang, StateData),
- StateData}.
-
--define(RFIELDT(Type, Var, Val),
- #xmlel{name = <<"field">>,
- attrs = [{<<"type">>, Type}, {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
-
--define(RFIELD(Label, Var, Val),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
-
+ Feats = [?NS_VCARD, ?NS_MUC,
+ ?CONFIG_OPT_TO_FEATURE((Config#config.public),
+ <<"muc_public">>, <<"muc_hidden">>),
+ ?CONFIG_OPT_TO_FEATURE((Config#config.persistent),
+ <<"muc_persistent">>, <<"muc_temporary">>),
+ ?CONFIG_OPT_TO_FEATURE((Config#config.members_only),
+ <<"muc_membersonly">>, <<"muc_open">>),
+ ?CONFIG_OPT_TO_FEATURE((Config#config.anonymous),
+ <<"muc_semianonymous">>, <<"muc_nonanonymous">>),
+ ?CONFIG_OPT_TO_FEATURE((Config#config.moderated),
+ <<"muc_moderated">>, <<"muc_unmoderated">>),
+ ?CONFIG_OPT_TO_FEATURE((Config#config.password_protected),
+ <<"muc_passwordprotected">>, <<"muc_unsecured">>)]
+ ++ case Config#config.allow_subscription of
+ true -> [?NS_MUCSUB];
+ false -> []
+ end
+ ++ case {gen_mod:is_loaded(StateData#state.server_host, mod_mam),
+ Config#config.mam} of
+ {true, true} ->
+ [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1];
+ _ ->
+ []
+ end,
+ {result, #disco_info{xdata = [iq_disco_info_extras(Lang, StateData)],
+ identities = [#identity{category = <<"conference">>,
+ type = <<"text">>,
+ name = get_title(StateData)}],
+ features = Feats}}.
+
+-spec mk_rfieldt('boolean' | 'fixed' | 'hidden' |
+ 'jid-multi' | 'jid-single' | 'list-multi' |
+ 'list-single' | 'text-multi' | 'text-private' |
+ 'text-single', binary(), binary()) -> xdata_field().
+mk_rfieldt(Type, Var, Val) ->
+ #xdata_field{type = Type, var = Var, values = [Val]}.
+
+-spec mk_rfield(binary(), binary(), binary(), binary()) -> xdata_field().
+mk_rfield(Label, Var, Val, Lang) ->
+ #xdata_field{type = 'text-single',
+ label = translate:translate(Lang, Label),
+ var = Var,
+ values = [Val]}.
+
+-spec iq_disco_info_extras(binary(), state()) -> xdata().
iq_disco_info_extras(Lang, StateData) ->
Len = (?DICT):size(StateData#state.users),
- RoomDescription =
- (StateData#state.config)#config.description,
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
- children =
- [?RFIELDT(<<"hidden">>, <<"FORM_TYPE">>,
- <<"http://jabber.org/protocol/muc#roominfo">>),
- ?RFIELD(<<"Room description">>,
- <<"muc#roominfo_description">>, RoomDescription),
- ?RFIELD(<<"Number of occupants">>,
- <<"muc#roominfo_occupants">>,
- (iolist_to_binary(integer_to_list(Len))))]}].
-
-process_iq_disco_items(_From, set, Lang, _StateData) ->
+ RoomDescription = (StateData#state.config)#config.description,
+ #xdata{type = result,
+ fields = [mk_rfieldt(hidden, <<"FORM_TYPE">>,
+ "http://jabber.org/protocol/muc#roominfo"),
+ mk_rfield(<<"Room description">>,
+ <<"muc#roominfo_description">>,
+ RoomDescription, Lang),
+ mk_rfield(<<"Number of occupants">>,
+ <<"muc#roominfo_occupants">>,
+ integer_to_binary(Len), Lang)]}.
+
+-spec process_iq_disco_items(jid(), iq(), state()) ->
+ {error, error()} | {result, disco_items()}.
+process_iq_disco_items(_From, #iq{type = set, lang = Lang}, _StateData) ->
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)};
-process_iq_disco_items(From, get, Lang, StateData) ->
+ {error, xmpp:err_not_allowed(Txt, Lang)};
+process_iq_disco_items(From, #iq{type = get, lang = Lang}, StateData) ->
case (StateData#state.config)#config.public_list of
true ->
- {result, get_mucroom_disco_items(StateData), StateData};
+ {result, get_mucroom_disco_items(StateData)};
_ ->
case is_occupant_or_admin(From, StateData) of
true ->
- {result, get_mucroom_disco_items(StateData), StateData};
+ {result, get_mucroom_disco_items(StateData)};
_ ->
Txt = <<"Only occupants or administrators can perform this query">>,
- {error, ?ERRT_FORBIDDEN(Lang, Txt)}
+ {error, xmpp:err_forbidden(Txt, Lang)}
end
end.
-process_iq_captcha(_From, get, Lang, _SubEl,
- _StateData) ->
+-spec process_iq_captcha(jid(), iq(), state()) -> {error, error()} |
+ {result, undefined}.
+process_iq_captcha(_From, #iq{type = get, lang = Lang}, _StateData) ->
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)};
-process_iq_captcha(_From, set, Lang, SubEl,
- StateData) ->
+ {error, xmpp:err_not_allowed(Txt, Lang)};
+process_iq_captcha(_From, #iq{type = set, lang = Lang, sub_els = [SubEl]},
+ _StateData) ->
case ejabberd_captcha:process_reply(SubEl) of
- ok -> {result, [], StateData};
+ ok -> {result, undefined};
{error, malformed} ->
Txt = <<"Incorrect CAPTCHA submit">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
+ {error, xmpp:err_bad_request(Txt, Lang)};
_ ->
Txt = <<"The CAPTCHA verification has failed">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)}
+ {error, xmpp:err_not_allowed(Txt, Lang)}
end.
-process_iq_vcard(_From, get, _Lang, _SubEl, StateData) ->
+-spec process_iq_vcard(jid(), iq(), state()) ->
+ {result, vcard_temp() | xmlel()} |
+ {result, undefined, state()} |
+ {error, error()}.
+process_iq_vcard(_From, #iq{type = get}, StateData) ->
#state{config = #config{vcard = VCardRaw}} = StateData,
case fxml_stream:parse_element(VCardRaw) of
- #xmlel{children = VCardEls} ->
- {result, VCardEls, StateData};
+ #xmlel{} = VCard ->
+ {result, VCard};
{error, _} ->
- {result, [], StateData}
+ {result, #vcard_temp{}}
end;
-process_iq_vcard(From, set, Lang, SubEl, StateData) ->
+process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [SubEl]},
+ StateData) ->
case get_affiliation(From, StateData) of
owner ->
- VCardRaw = fxml:element_to_binary(SubEl),
+ VCardRaw = fxml:element_to_binary(xmpp:encode(SubEl)),
Config = StateData#state.config,
NewConfig = Config#config{vcard = VCardRaw},
change_config(NewConfig, StateData);
_ ->
ErrText = <<"Owner privileges required">>,
- {error, ?ERRT_FORBIDDEN(Lang, ErrText)}
+ {error, xmpp:err_forbidden(ErrText, Lang)}
end.
-process_iq_mucsub(From, Packet,
+-spec process_iq_mucsub(jid(), iq(), state()) ->
+ {error, error()} |
+ {result, undefined | muc_subscribe(), state()} |
+ {ignore, state()}.
+process_iq_mucsub(_From, #iq{type = set, lang = Lang,
+ sub_els = [#muc_subscribe{}]},
+ #state{config = #config{allow_subscription = false}}) ->
+ {error, xmpp:err_not_allowed(<<"Subscriptions are not allowed">>, Lang)};
+process_iq_mucsub(From,
#iq{type = set, lang = Lang,
- sub_el = #xmlel{name = <<"subscribe">>} = SubEl},
- #state{config = Config} = StateData) ->
- case fxml:get_tag_attr_s(<<"nick">>, SubEl) of
- <<"">> ->
- Err = ?ERRT_BAD_REQUEST(Lang, <<"Missing 'nick' attribute">>),
- {error, Err};
- Nick when Config#config.allow_subscription ->
- LJID = jid:tolower(From),
- case (?DICT):find(LJID, StateData#state.users) of
- {ok, #user{role = Role, nick = Nick1}} when Nick1 /= Nick ->
- Nodes = get_subscription_nodes(Packet),
- case {nick_collision(From, Nick, StateData),
- mod_muc:can_use_nick(StateData#state.server_host,
- StateData#state.host,
- From, Nick)} of
- {true, _} ->
- ErrText = <<"That nickname is already in use by another occupant">>,
- {error, ?ERRT_CONFLICT(Lang, ErrText)};
- {_, false} ->
- ErrText = <<"That nickname is registered by another person">>,
- {error, ?ERRT_CONFLICT(Lang, ErrText)};
- _ ->
- NewStateData = add_online_user(
- From, Nick, Role, true, Nodes, StateData),
- {result, subscription_nodes_to_events(Nodes), NewStateData}
- end;
- {ok, #user{role = Role}} ->
- Nodes = get_subscription_nodes(Packet),
+ sub_els = [#muc_subscribe{nick = Nick}]} = Packet,
+ StateData) ->
+ LJID = jid:tolower(From),
+ case (?DICT):find(LJID, StateData#state.users) of
+ {ok, #user{role = Role, nick = Nick1}} when Nick1 /= Nick ->
+ Nodes = get_subscription_nodes(Packet),
+ case {nick_collision(From, Nick, StateData),
+ mod_muc:can_use_nick(StateData#state.server_host,
+ StateData#state.host,
+ From, Nick)} of
+ {true, _} ->
+ ErrText = <<"That nickname is already in use by another occupant">>,
+ {error, xmpp:err_conflict(ErrText, Lang)};
+ {_, false} ->
+ ErrText = <<"That nickname is registered by another person">>,
+ {error, xmpp:err_conflict(ErrText, Lang)};
+ _ ->
NewStateData = add_online_user(
From, Nick, Role, true, Nodes, StateData),
- {result, subscription_nodes_to_events(Nodes), NewStateData};
- error ->
- add_new_user(From, Nick, Packet, StateData)
+ {result, subscribe_result(Packet), NewStateData}
end;
- _ ->
- Err = ?ERRT_NOT_ALLOWED(Lang, <<"Subscriptions are not allowed">>),
- {error, Err}
+ {ok, #user{role = Role}} ->
+ Nodes = get_subscription_nodes(Packet),
+ NewStateData = add_online_user(
+ From, Nick, Role, true, Nodes, StateData),
+ {result, subscribe_result(Packet), NewStateData};
+ error ->
+ add_new_user(From, Nick, Packet, StateData)
end;
-process_iq_mucsub(From, _Packet,
- #iq{type = set,
- sub_el = #xmlel{name = <<"unsubscribe">>}},
+process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]},
StateData) ->
LJID = jid:tolower(From),
case ?DICT:find(LJID, StateData#state.users) of
{ok, #user{is_subscriber = true} = User} ->
NewStateData = remove_subscription(From, User, StateData),
store_room(NewStateData),
- {result, [], NewStateData};
+ {result, undefined, NewStateData};
_ ->
- {result, [], StateData}
+ {result, undefined, StateData}
end;
-process_iq_mucsub(_From, _Packet, #iq{type = set, lang = Lang}, _StateData) ->
- Txt = <<"Unrecognized subscription command">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)};
-process_iq_mucsub(_From, _Packet, #iq{type = get, lang = Lang}, _StateData) ->
+process_iq_mucsub(_From, #iq{type = get, lang = Lang}, _StateData) ->
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}.
+ {error, xmpp:err_bad_request(Txt, Lang)}.
+-spec remove_subscription(jid(), #user{}, state()) -> state().
remove_subscription(JID, #user{is_subscriber = true} = User, StateData) ->
case User#user.last_presence of
undefined ->
@@ -4631,6 +4034,7 @@ remove_subscription(JID, #user{is_subscriber = true} = User, StateData) ->
remove_subscription(_JID, #user{}, StateData) ->
StateData.
+-spec remove_subscriptions(state()) -> state().
remove_subscriptions(StateData) ->
if not (StateData#state.config)#config.allow_subscription ->
dict:fold(
@@ -4641,41 +4045,32 @@ remove_subscriptions(StateData) ->
StateData
end.
-get_subscription_nodes(#xmlel{name = <<"iq">>} = Packet) ->
- case fxml:get_subtag_with_xmlns(Packet, <<"subscribe">>, ?NS_MUCSUB) of
- #xmlel{children = Els} ->
- lists:flatmap(
- fun(#xmlel{name = <<"event">>, attrs = Attrs}) ->
- Node = fxml:get_attr_s(<<"node">>, Attrs),
- case lists:member(Node, [?NS_MUCSUB_NODES_PRESENCE,
- ?NS_MUCSUB_NODES_MESSAGES,
- ?NS_MUCSUB_NODES_AFFILIATIONS,
- ?NS_MUCSUB_NODES_SUBJECT,
- ?NS_MUCSUB_NODES_CONFIG,
- ?NS_MUCSUB_NODES_PARTICIPANTS]) of
- true ->
- [Node];
- false ->
- []
- end;
- (_) ->
- []
- end, Els);
- false ->
- []
- end;
+-spec get_subscription_nodes(iq()) -> [binary()].
+get_subscription_nodes(#iq{sub_els = [#muc_subscribe{events = Nodes}]}) ->
+ lists:filter(
+ fun(Node) ->
+ lists:member(Node, [?NS_MUCSUB_NODES_PRESENCE,
+ ?NS_MUCSUB_NODES_MESSAGES,
+ ?NS_MUCSUB_NODES_AFFILIATIONS,
+ ?NS_MUCSUB_NODES_SUBJECT,
+ ?NS_MUCSUB_NODES_CONFIG,
+ ?NS_MUCSUB_NODES_PARTICIPANTS])
+ end, Nodes);
get_subscription_nodes(_) ->
[].
-subscription_nodes_to_events(Nodes) ->
- [#xmlel{name = <<"event">>, attrs = [{<<"node">>, Node}]} || Node <- Nodes].
+-spec subscribe_result(iq()) -> muc_subscribe().
+subscribe_result(#iq{sub_els = [#muc_subscribe{nick = Nick}]} = Packet) ->
+ #muc_subscribe{nick = Nick, events = get_subscription_nodes(Packet)}.
+-spec get_title(state()) -> binary().
get_title(StateData) ->
case (StateData#state.config)#config.title of
<<"">> -> StateData#state.room;
Name -> Name
end.
+-spec get_roomdesc_reply(jid(), state(), binary()) -> {item, binary()} | false.
get_roomdesc_reply(JID, StateData, Tail) ->
IsOccupantOrAdmin = is_occupant_or_admin(JID,
StateData),
@@ -4689,352 +4084,215 @@ get_roomdesc_reply(JID, StateData, Tail) ->
true -> false
end.
+-spec get_roomdesc_tail(state(), binary()) -> binary().
get_roomdesc_tail(StateData, Lang) ->
Desc = case (StateData#state.config)#config.public of
true -> <<"">>;
_ -> translate:translate(Lang, <<"private, ">>)
end,
- Len = (?DICT):fold(fun (_, _, Acc) -> Acc + 1 end, 0,
- StateData#state.users),
+ Len = (?DICT):size(StateData#state.users),
<<" (", Desc/binary,
(iolist_to_binary(integer_to_list(Len)))/binary, ")">>.
+-spec get_mucroom_disco_items(state()) -> disco_items().
get_mucroom_disco_items(StateData) ->
- lists:map(fun ({_LJID, Info}) ->
+ Items = lists:map(
+ fun({_LJID, Info}) ->
Nick = Info#user.nick,
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>,
- jid:to_string({StateData#state.room,
- StateData#state.host,
- Nick})},
- {<<"name">>, Nick}],
- children = []}
+ #disco_item{jid = jid:make(StateData#state.room,
+ StateData#state.host,
+ Nick),
+ name = Nick}
end,
- (?DICT):to_list(StateData#state.users)).
+ (?DICT):to_list(StateData#state.users)),
+ #disco_items{items = Items}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Voice request support
-is_voice_request(Els) ->
- lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
- El,
- false) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_XDATA ->
- case jlib:parse_xdata_submit(El) of
- [_ | _] = Fields ->
- case {lists:keysearch(<<"FORM_TYPE">>, 1,
- Fields),
- lists:keysearch(<<"muc#role">>, 1,
- Fields)}
- of
- {{value,
- {_,
- [<<"http://jabber.org/protocol/muc#request">>]}},
- {value, {_, [<<"participant">>]}}} ->
- true;
- _ -> false
- end;
- _ -> false
- end;
- _ -> false
- end;
- (_, Acc) -> Acc
- end,
- false, Els).
+-spec is_voice_request(message()) -> boolean().
+is_voice_request(Packet) ->
+ Els = xmpp:get_els(Packet),
+ lists:any(
+ fun(#xdata{} = X) ->
+ case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X),
+ xmpp_util:get_xdata_values(<<"muc#role">>, X)} of
+ {[<<"http://jabber.org/protocol/muc#request">>],
+ [<<"participant">>]} ->
+ true;
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, Els).
+-spec prepare_request_form(jid(), binary(), binary()) -> message().
prepare_request_form(Requester, Nick, Lang) ->
- #xmlel{name = <<"message">>,
- attrs = [{<<"type">>, <<"normal">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children =
- [#xmlel{name = <<"title">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"Voice request">>)}]},
- #xmlel{name = <<"instructions">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"Either approve or decline the voice "
- "request.">>)}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>, <<"FORM_TYPE">>},
- {<<"type">>, <<"hidden">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"http://jabber.org/protocol/muc#request">>}]}]},
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>, <<"muc#role">>},
- {<<"type">>, <<"hidden">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children =
- [{xmlcdata,
- <<"participant">>}]}]},
- ?STRINGXFIELD(<<"User JID">>, <<"muc#jid">>,
- (jid:to_string(Requester))),
- ?STRINGXFIELD(<<"Nickname">>, <<"muc#roomnick">>,
- Nick),
- ?BOOLXFIELD(<<"Grant voice to this person?">>,
- <<"muc#request_allow">>,
- (jlib:binary_to_atom(<<"false">>)))]}]}.
-
-send_voice_request(From, StateData) ->
+ Title = translate:translate(Lang, <<"Voice request">>),
+ Instruction = translate:translate(
+ Lang, <<"Either approve or decline the voice request.">>),
+ Fs = [#xdata_field{var = <<"FORM_TYPE">>,
+ type = hidden,
+ values = [<<"http://jabber.org/protocol/muc#request">>]},
+ #xdata_field{var = <<"muc#role">>,
+ type = hidden,
+ values = [<<"participant">>]},
+ ?STRINGXFIELD(<<"User JID">>, <<"muc#jid">>,
+ jid:to_string(Requester)),
+ ?STRINGXFIELD(<<"Nickname">>, <<"muc#roomnick">>, Nick),
+ ?BOOLXFIELD(<<"Grant voice to this person?">>,
+ <<"muc#request_allow">>, false)],
+ #message{type = normal,
+ sub_els = [#xdata{type = form,
+ title = Title,
+ instructions = [Instruction],
+ fields = Fs}]}.
+
+-spec send_voice_request(jid(), binary(), state()) -> ok.
+send_voice_request(From, Lang, StateData) ->
Moderators = search_role(moderator, StateData),
FromNick = find_nick_by_jid(From, StateData),
lists:foreach(fun ({_, User}) ->
ejabberd_router:route(
StateData#state.jid, User#user.jid,
- prepare_request_form(From, FromNick, <<"">>))
+ prepare_request_form(From, FromNick, Lang))
end,
Moderators).
-is_voice_approvement(Els) ->
- lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
- El,
- false) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_XDATA ->
- case jlib:parse_xdata_submit(El) of
- [_ | _] = Fs ->
- case {lists:keysearch(<<"FORM_TYPE">>, 1,
- Fs),
- lists:keysearch(<<"muc#role">>, 1,
- Fs),
- lists:keysearch(<<"muc#request_allow">>,
- 1, Fs)}
- of
- {{value,
- {_,
- [<<"http://jabber.org/protocol/muc#request">>]}},
- {value, {_, [<<"participant">>]}},
- {value, {_, [Flag]}}}
- when Flag == <<"true">>;
- Flag == <<"1">> ->
- true;
- _ -> false
- end;
- _ -> false
- end;
- _ -> false
- end;
- (_, Acc) -> Acc
- end,
- false, Els).
-
-extract_jid_from_voice_approvement(Els) ->
- lists:foldl(fun (#xmlel{name = <<"x">>} = El, error) ->
- Fields = case jlib:parse_xdata_submit(El) of
- invalid -> [];
- Res -> Res
- end,
- lists:foldl(fun ({<<"muc#jid">>, [JIDStr]}, error) ->
- case jid:from_string(JIDStr) of
- error -> error;
- J -> {ok, J}
- end;
- (_, Acc) -> Acc
- end,
- error, Fields);
- (_, Acc) -> Acc
- end,
- error, Els).
+-spec is_voice_approvement(message()) -> boolean().
+is_voice_approvement(Packet) ->
+ Els = xmpp:get_els(Packet),
+ lists:any(
+ fun(#xdata{} = X) ->
+ case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X),
+ xmpp_util:get_xdata_values(<<"muc#role">>, X),
+ xmpp_util:get_xdata_values(<<"muc#request_allow">>, X)} of
+ {[<<"http://jabber.org/protocol/muc#request">>],
+ [<<"participant">>], [Flag]} when Flag == <<"true">>;
+ Flag == <<"1">> ->
+ true;
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, Els).
+
+-spec extract_jid_from_voice_approvement(message()) -> jid() | error.
+extract_jid_from_voice_approvement(Packet) ->
+ Els = xmpp:get_els(Packet),
+ lists:foldl(
+ fun(#xdata{} = X, error) ->
+ case {xmpp_util:get_xdata_values(<<"FORM_TYPE">>, X),
+ xmpp_util:get_xdata_values(<<"muc#role">>, X),
+ xmpp_util:get_xdata_values(<<"muc#request_allow">>, X),
+ xmpp_util:get_xdata_values(<<"muc#jid">>, X)} of
+ {[<<"http://jabber.org/protocol/muc#request">>],
+ [<<"participant">>], [Flag], [J]} when Flag == <<"true">>;
+ Flag == <<"1">> ->
+ jid:from_string(J);
+ _ ->
+ error
+ end;
+ (_, Acc) ->
+ Acc
+ end, error, Els).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Invitation support
-is_invitation(Els) ->
- lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
- El,
- false) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- ?NS_MUC_USER ->
- case fxml:get_subtag(El, <<"invite">>) of
- false -> false;
- _ -> true
- end;
- _ -> false
- end;
- (_, Acc) -> Acc
- end,
- false, Els).
-
-check_invitation(From, Packet, Lang, StateData) ->
+-spec is_invitation(message()) -> boolean().
+is_invitation(Packet) ->
+ Els = xmpp:get_els(Packet),
+ lists:any(
+ fun(#muc_user{invites = [_|_]}) -> true;
+ (_) -> false
+ end, Els).
+
+-spec check_invitation(jid(), message(), state()) -> {error, error()} | jid().
+check_invitation(From, Packet, StateData) ->
+ Lang = xmpp:get_lang(Packet),
FAffiliation = get_affiliation(From, StateData),
- CanInvite =
- (StateData#state.config)#config.allow_user_invites
- orelse
- FAffiliation == admin orelse FAffiliation == owner,
- InviteEl = case fxml:get_subtag_with_xmlns(Packet, <<"x">>, ?NS_MUC_USER) of
- false ->
- Txt1 = <<"No 'x' element found">>,
- throw({error, ?ERRT_BAD_REQUEST(Lang, Txt1)});
- XEl ->
- case fxml:get_subtag(XEl, <<"invite">>) of
- false ->
- Txt2 = <<"No 'invite' element found">>,
- throw({error, ?ERRT_BAD_REQUEST(Lang, Txt2)});
- InviteEl1 ->
- InviteEl1
- end
- end,
- JID = case
- jid:from_string(fxml:get_tag_attr_s(<<"to">>,
- InviteEl))
- of
- error ->
- Txt = <<"Incorrect value of 'to' attribute">>,
- throw({error, ?ERRT_JID_MALFORMED(Lang, Txt)});
- JID1 -> JID1
- end,
+ CanInvite = (StateData#state.config)#config.allow_user_invites
+ orelse
+ FAffiliation == admin orelse FAffiliation == owner,
case CanInvite of
- false ->
- Txt3 = <<"Invitations are not allowed in this conference">>,
- throw({error, ?ERRT_NOT_ALLOWED(Lang, Txt3)});
- true ->
- Reason = fxml:get_path_s(InviteEl,
- [{elem, <<"reason">>}, cdata]),
- ContinueEl = case fxml:get_path_s(InviteEl,
- [{elem, <<"continue">>}])
- of
- <<>> -> [];
- Continue1 -> [Continue1]
- end,
- IEl = [#xmlel{name = <<"invite">>,
- attrs = [{<<"from">>, jid:to_string(From)}],
- children =
- [#xmlel{name = <<"reason">>, attrs = [],
- children = [{xmlcdata, Reason}]}]
- ++ ContinueEl}],
- PasswdEl = case
- (StateData#state.config)#config.password_protected
- of
- true ->
- [#xmlel{name = <<"password">>, attrs = [],
- children =
- [{xmlcdata,
- (StateData#state.config)#config.password}]}];
- _ -> []
- end,
- Body = #xmlel{name = <<"body">>, attrs = [],
- children =
- [{xmlcdata,
- iolist_to_binary(
- [io_lib:format(
- translate:translate(
- Lang,
- <<"~s invites you to the room ~s">>),
- [jid:to_string(From),
- jid:to_string({StateData#state.room,
- StateData#state.host,
- <<"">>})]),
- case
- (StateData#state.config)#config.password_protected
- of
+ false ->
+ Txt = <<"Invitations are not allowed in this conference">>,
+ {error, xmpp:err_not_allowed(Txt, Lang)};
+ true ->
+ case xmpp:get_subtag(Packet, #muc_user{}) of
+ #muc_user{invites = [#muc_invite{to = undefined}]} ->
+ Txt = <<"No 'to' attribute found">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
+ #muc_user{invites = [#muc_invite{to = JID, reason = Reason} = I]} ->
+ Invite = I#muc_invite{to = undefined, from = From},
+ Password = case (StateData#state.config)#config.password_protected of
+ true ->
+ (StateData#state.config)#config.password;
+ false ->
+ undefined
+ end,
+ XUser = #muc_user{password = Password, invites = [Invite]},
+ XConference = #x_conference{jid = jid:make(StateData#state.room,
+ StateData#state.host),
+ reason = Reason},
+ Body = iolist_to_binary(
+ [io_lib:format(
+ translate:translate(
+ Lang,
+ <<"~s invites you to the room ~s">>),
+ [jid:to_string(From),
+ jid:to_string({StateData#state.room,
+ StateData#state.host,
+ <<"">>})]),
+ case (StateData#state.config)#config.password_protected of
true ->
<<", ",
- (translate:translate(Lang,
- <<"the password is">>))/binary,
+ (translate:translate(
+ Lang, <<"the password is">>))/binary,
" '",
((StateData#state.config)#config.password)/binary,
"'">>;
_ -> <<"">>
- end
- ,
- case Reason of
- <<"">> -> <<"">>;
- _ -> <<" (", Reason/binary, ") ">>
- end])}]},
- Msg = #xmlel{name = <<"message">>,
- attrs = [{<<"type">>, <<"normal">>}],
- children =
- [#xmlel{name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
- children = IEl ++ PasswdEl},
- #xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>, ?NS_XCONFERENCE},
- {<<"jid">>,
- jid:to_string({StateData#state.room,
- StateData#state.host,
- <<"">>})}],
- children = [{xmlcdata, Reason}]},
- Body]},
- ejabberd_router:route(StateData#state.jid, JID, Msg),
- JID
+ end,
+ case Reason of
+ <<"">> -> <<"">>;
+ _ -> <<" (", Reason/binary, ") ">>
+ end]),
+ Msg = #message{type = normal,
+ body = xmpp:mk_text(Body),
+ sub_els = [XUser, XConference]},
+ ejabberd_router:route(StateData#state.jid, JID, Msg),
+ JID;
+ #muc_user{invites = [_|_]} ->
+ Txt = <<"Multiple <invite/> elements are not allowed">>,
+ {error, xmpp:err_forbidden(Txt, Lang)};
+ _ ->
+ Txt = <<"No <invite/> element found">>,
+ {error, xmpp:err_bad_request(Txt, Lang)}
+ end
end.
%% Handle a message sent to the room by a non-participant.
%% If it is a decline, send to the inviter.
%% Otherwise, an error message is sent to the sender.
-handle_roommessage_from_nonparticipant(Packet, Lang,
- StateData, From) ->
- case catch check_decline_invitation(Packet) of
- {true, Decline_data} ->
- send_decline_invitation(Decline_data,
- StateData#state.jid, From);
- _ ->
- send_error_only_occupants(Packet, Lang,
- StateData#state.jid, From)
+-spec handle_roommessage_from_nonparticipant(message(), state(), jid()) -> ok.
+handle_roommessage_from_nonparticipant(Packet, StateData, From) ->
+ case xmpp:get_subtag(Packet, #muc_user{}) of
+ #muc_user{decline = #muc_decline{to = #jid{} = To} = Decline} = XUser ->
+ NewDecline = Decline#muc_decline{to = undefined, from = From},
+ NewXUser = XUser#muc_user{decline = NewDecline},
+ NewPacket = xmpp:set_subtag(Packet, NewXUser),
+ ejabberd_router:route(StateData#state.jid, To, NewPacket);
+ _ ->
+ ErrText = <<"Only occupants are allowed to send messages "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, xmpp:get_lang(Packet)),
+ ejabberd_router:route_error(StateData#state.jid, From, Packet, Err)
end.
-%% Check in the packet is a decline.
-%% If so, also returns the splitted packet.
-%% This function must be catched,
-%% because it crashes when the packet is not a decline message.
-check_decline_invitation(Packet) ->
- #xmlel{name = <<"message">>} = Packet,
- XEl = fxml:get_subtag(Packet, <<"x">>),
- (?NS_MUC_USER) = fxml:get_tag_attr_s(<<"xmlns">>, XEl),
- DEl = fxml:get_subtag(XEl, <<"decline">>),
- ToString = fxml:get_tag_attr_s(<<"to">>, DEl),
- ToJID = jid:from_string(ToString),
- {true, {Packet, XEl, DEl, ToJID}}.
-
-%% Send the decline to the inviter user.
-%% The original stanza must be slightly modified.
-send_decline_invitation({Packet, XEl, DEl, ToJID},
- RoomJID, FromJID) ->
- FromString =
- jid:to_string(jid:remove_resource(FromJID)),
- #xmlel{name = <<"decline">>, attrs = DAttrs,
- children = DEls} =
- DEl,
- DAttrs2 = lists:keydelete(<<"to">>, 1, DAttrs),
- DAttrs3 = [{<<"from">>, FromString} | DAttrs2],
- DEl2 = #xmlel{name = <<"decline">>, attrs = DAttrs3,
- children = DEls},
- XEl2 = replace_subelement(XEl, DEl2),
- Packet2 = replace_subelement(Packet, XEl2),
- ejabberd_router:route(RoomJID, ToJID, Packet2).
-
-%% Given an element and a new subelement,
-%% replace the instance of the subelement in element with the new subelement.
-replace_subelement(#xmlel{name = Name, attrs = Attrs,
- children = SubEls},
- NewSubEl) ->
- {_, NameNewSubEl, _, _} = NewSubEl,
- SubEls2 = lists:keyreplace(NameNewSubEl, 2, SubEls, NewSubEl),
- #xmlel{name = Name, attrs = Attrs, children = SubEls2}.
-
-send_error_only_occupants(Packet, Lang, RoomJID, From) ->
- ErrText =
- <<"Only occupants are allowed to send messages "
- "to the conference">>,
- Err = jlib:make_error_reply(Packet,
- ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
- ejabberd_router:route(RoomJID, From, Err).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Logging
@@ -5055,6 +4313,7 @@ add_to_log(Type, Data, StateData) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Users number checking
+-spec tab_add_online_user(jid(), state()) -> ok.
tab_add_online_user(JID, StateData) ->
{LUser, LServer, LResource} = jid:tolower(JID),
US = {LUser, LServer},
@@ -5062,8 +4321,10 @@ tab_add_online_user(JID, StateData) ->
Host = StateData#state.host,
catch ets:insert(muc_online_users,
#muc_online_users{us = US, resource = LResource,
- room = Room, host = Host}).
+ room = Room, host = Host}),
+ ok.
+-spec tab_remove_online_user(jid(), state()) -> ok.
tab_remove_online_user(JID, StateData) ->
{LUser, LServer, LResource} = jid:tolower(JID),
US = {LUser, LServer},
@@ -5071,8 +4332,10 @@ tab_remove_online_user(JID, StateData) ->
Host = StateData#state.host,
catch ets:delete_object(muc_online_users,
#muc_online_users{us = US, resource = LResource,
- room = Room, host = Host}).
+ room = Room, host = Host}),
+ ok.
+-spec tab_count_user(jid()) -> non_neg_integer().
tab_count_user(JID) ->
{LUser, LServer, _} = jid:tolower(JID),
US = {LUser, LServer},
@@ -5083,9 +4346,11 @@ tab_count_user(JID) ->
_ -> 0
end.
+-spec element_size(stanza()) -> non_neg_integer().
element_size(El) ->
- byte_size(fxml:element_to_binary(El)).
+ byte_size(fxml:element_to_binary(xmpp:encode(El))).
+-spec store_room(state()) -> ok.
store_room(StateData) ->
if (StateData#state.config)#config.persistent ->
mod_muc:store_room(StateData#state.server_host,
@@ -5095,6 +4360,7 @@ store_room(StateData) ->
ok
end.
+-spec send_wrapped(jid(), jid(), stanza(), binary(), state()) -> ok.
send_wrapped(From, To, Packet, Node, State) ->
LTo = jid:tolower(To),
case ?DICT:find(LTo, State#state.users) of
@@ -5112,27 +4378,26 @@ send_wrapped(From, To, Packet, Node, State) ->
ejabberd_router:route(From, To, Packet)
end.
+-spec wrap(jid(), jid(), stanza(), binary()) -> message().
wrap(From, To, Packet, Node) ->
- Pkt1 = jlib:replace_from_to(From, To, Packet),
- Pkt2 = #xmlel{attrs = Attrs} = jlib:remove_attr(<<"xmlns">>, Pkt1),
- Pkt3 = Pkt2#xmlel{attrs = [{<<"xmlns">>, <<"jabber:client">>}|Attrs]},
- Item = #xmlel{name = <<"item">>,
- attrs = [{<<"id">>, randoms:get_string()}],
- children = [Pkt3]},
- Items = #xmlel{name = <<"items">>, attrs = [{<<"node">>, Node}],
- children = [Item]},
- Event = #xmlel{name = <<"event">>,
- attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
- children = [Items]},
- #xmlel{name = <<"message">>, children = [Event]}.
+ El = xmpp:encode(xmpp:set_from_to(Packet, From, To)),
+ #message{
+ sub_els = [#pubsub_event{
+ items = [#pubsub_event_items{
+ node = Node,
+ items = [#pubsub_event_item{
+ id = randoms:get_string(),
+ xml_els = [El]}]}]}]}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Multicast
+-spec send_multiple(jid(), binary(), [#user{}], stanza()) -> ok.
send_multiple(From, Server, Users, Packet) ->
JIDs = [ User#user.jid || {_, User} <- ?DICT:to_list(Users)],
ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet).
+-spec send_wrapped_multiple(jid(), [#user{}], stanza(), binary(), state()) -> ok.
send_wrapped_multiple(From, Users, Packet, Node, State) ->
lists:foreach(
fun({_, #user{jid = To}}) ->
@@ -5141,10 +4406,6 @@ send_wrapped_multiple(From, Users, Packet, Node, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Detect messange stanzas that don't have meaninful content
-
-has_body_or_subject(Packet) ->
- [] /= lists:dropwhile(fun
- (#xmlel{name = <<"body">>}) -> false;
- (#xmlel{name = <<"subject">>}) -> false;
- (_) -> true
- end, Packet#xmlel.children).
+-spec has_body_or_subject(message()) -> boolean().
+has_body_or_subject(#message{body = Body, subject = Subj}) ->
+ Body /= [] orelse Subj /= [].
diff --git a/src/mod_private.erl b/src/mod_private.erl
index 28d49bb3f..e6d0fd7cd 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -72,7 +72,7 @@ process_sm_iq(#iq{type = Type, lang = Lang,
case filter_xmlels(Els0) of
[] ->
Txt = <<"No private data found in this query">>,
- xmpp:make_error(IQ, xmpp:err_bad_format(Txt, Lang));
+ xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
Data when Type == set ->
set_data(LUser, LServer, Data),
xmpp:make_iq_result(IQ);
diff --git a/src/xmpp.erl b/src/xmpp.erl
index f17eefa21..369fb90c5 100644
--- a/src/xmpp.erl
+++ b/src/xmpp.erl
@@ -16,22 +16,33 @@
set_type/2, set_to/2, set_from/2, set_id/2,
set_lang/2, set_error/2, set_els/2, set_from_to/3,
format_error/1, is_stanza/1, set_subtag/2, get_subtag/2,
- remove_subtag/2, has_subtag/2, decode_els/1, pp/1,
- get_name/1, get_text/1, mk_text/1, mk_text/2]).
+ remove_subtag/2, has_subtag/2, decode_els/1, decode_els/2,
+ pp/1, get_name/1, get_text/1, mk_text/1, mk_text/2]).
%% XMPP errors
-export([err_bad_request/0, err_bad_request/2,
- err_bad_format/0, err_bad_format/2,
- err_not_allowed/0, err_not_allowed/2,
- err_conflict/0, err_conflict/2,
- err_forbidden/0, err_forbidden/2,
- err_not_acceptable/0, err_not_acceptable/2,
- err_internal_server_error/0, err_internal_server_error/2,
- err_service_unavailable/0, err_service_unavailable/2,
- err_item_not_found/0, err_item_not_found/2,
- err_jid_malformed/0, err_jid_malformed/2,
- err_not_authorized/0, err_not_authorized/2,
- err_feature_not_implemented/0, err_feature_not_implemented/2]).
+ err_conflict/0, err_conflict/2,
+ err_feature_not_implemented/0, err_feature_not_implemented/2,
+ err_forbidden/0, err_forbidden/2,
+ err_gone/0, err_gone/2,
+ err_internal_server_error/0, err_internal_server_error/2,
+ err_item_not_found/0, err_item_not_found/2,
+ err_jid_malformed/0, err_jid_malformed/2,
+ err_not_acceptable/0, err_not_acceptable/2,
+ err_not_allowed/0, err_not_allowed/2,
+ err_not_authorized/0, err_not_authorized/2,
+ err_payment_required/0, err_payment_required/2,
+ err_policy_violation/0, err_policy_violation/2,
+ err_recipient_unavailable/0, err_recipient_unavailable/2,
+ err_redirect/0, err_redirect/2,
+ err_registration_required/0, err_registration_required/2,
+ err_remote_server_not_found/0, err_remote_server_not_found/2,
+ err_remote_server_timeout/0, err_remote_server_timeout/2,
+ err_resource_constraint/0, err_resource_constraint/2,
+ err_service_unavailable/0, err_service_unavailable/2,
+ err_subscription_required/0, err_subscription_required/2,
+ err_undefined_condition/0, err_undefined_condition/2,
+ err_unexpected_request/0, err_unexpected_request/2]).
%% XMPP stream errors
-export([serr_bad_format/0, serr_bad_format/2,
@@ -246,9 +257,12 @@ decode(Pkt, _Opts) ->
(message()) -> message();
(presence()) -> presence().
decode_els(Stanza) ->
+ decode_els(Stanza, fun xmpp_codec:is_known_tag/1).
+
+decode_els(Stanza, MatchFun) ->
Els = lists:map(
fun(#xmlel{} = El) ->
- case xmpp_codec:is_known_tag(El) of
+ case MatchFun(El) of
true -> decode(El);
false -> El
end;
@@ -287,10 +301,10 @@ set_subtag(Stanza, Tag) ->
set_els(Stanza, NewEls).
set_subtag([El|Els], Tag, TagName, XMLNS) ->
- case {get_name(El), get_ns(El)} of
- {TagName, XMLNS} ->
+ case match_tag(El, TagName, XMLNS) of
+ true ->
[Tag|Els];
- _ ->
+ false ->
[El|set_subtag(Els, Tag, TagName, XMLNS)]
end;
set_subtag([], Tag, _, _) ->
@@ -304,14 +318,14 @@ get_subtag(Stanza, Tag) ->
get_subtag(Els, TagName, XMLNS).
get_subtag([El|Els], TagName, XMLNS) ->
- case {get_name(El), get_ns(El)} of
- {TagName, XMLNS} ->
+ case match_tag(El, TagName, XMLNS) of
+ true ->
try
decode(El)
catch _:{xmpp_codec, _Why} ->
get_subtag(Els, TagName, XMLNS)
end;
- _ ->
+ false ->
get_subtag(Els, TagName, XMLNS)
end;
get_subtag([], _, _) ->
@@ -328,10 +342,10 @@ remove_subtag(Stanza, Tag) ->
set_els(Stanza, NewEls).
remove_subtag([El|Els], TagName, XMLNS) ->
- case {get_name(El), get_ns(El)} of
- {TagName, XMLNS} ->
+ case match_tag(El, TagName, XMLNS) of
+ true ->
remove_subtag(Els, TagName, XMLNS);
- _ ->
+ false ->
[El|remove_subtag(Els, TagName, XMLNS)]
end;
remove_subtag([], _, _) ->
@@ -345,10 +359,10 @@ has_subtag(Stanza, Tag) ->
has_subtag(Els, TagName, XMLNS).
has_subtag([El|Els], TagName, XMLNS) ->
- case {get_name(El), get_ns(El)} of
- {TagName, XMLNS} ->
+ case match_tag(El, TagName, XMLNS) of
+ true ->
true;
- _ ->
+ false ->
has_subtag(Els, TagName, XMLNS)
end;
has_subtag([], _, _) ->
@@ -385,14 +399,6 @@ err_bad_request() ->
err_bad_request(Text, Lang) ->
err(modify, 'bad-request', 400, Text, Lang).
--spec err_bad_format() -> error().
-err_bad_format() ->
- err(modify, 'bad-format', 406).
-
--spec err_bad_format(binary(), binary() | undefined) -> error().
-err_bad_format(Text, Lang) ->
- err(modify, 'bad-format', 406, Text, Lang).
-
-spec err_conflict() -> error().
err_conflict() ->
err(cancel, 'conflict', 409).
@@ -401,14 +407,6 @@ err_conflict() ->
err_conflict(Text, Lang) ->
err(cancel, 'conflict', 409, Text, Lang).
--spec err_not_allowed() -> error().
-err_not_allowed() ->
- err(cancel, 'not-allowed', 405).
-
--spec err_not_allowed(binary(), binary() | undefined) -> error().
-err_not_allowed(Text, Lang) ->
- err(cancel, 'not-allowed', 405, Text, Lang).
-
-spec err_feature_not_implemented() -> error().
err_feature_not_implemented() ->
err(cancel, 'feature-not-implemented', 501).
@@ -417,14 +415,6 @@ err_feature_not_implemented() ->
err_feature_not_implemented(Text, Lang) ->
err(cancel, 'feature-not-implemented', 501, Text, Lang).
--spec err_item_not_found() -> error().
-err_item_not_found() ->
- err(cancel, 'item-not-found', 404).
-
--spec err_item_not_found(binary(), binary() | undefined) -> error().
-err_item_not_found(Text, Lang) ->
- err(cancel, 'item-not-found', 404, Text, Lang).
-
-spec err_forbidden() -> error().
err_forbidden() ->
err(auth, 'forbidden', 403).
@@ -433,14 +423,18 @@ err_forbidden() ->
err_forbidden(Text, Lang) ->
err(auth, 'forbidden', 403, Text, Lang).
--spec err_not_acceptable() -> error().
-err_not_acceptable() ->
- err(modify, 'not-acceptable', 406).
+%% RFC 6120 says error type SHOULD be "cancel".
+%% RFC 3920 and XEP-0082 says it SHOULD be "modify".
+-spec err_gone() -> error().
+err_gone() ->
+ err(modify, 'gone', 302).
--spec err_not_acceptable(binary(), binary() | undefined) -> error().
-err_not_acceptable(Text, Lang) ->
- err(modify, 'not-acceptable', 406, Text, Lang).
+-spec err_gone(binary(), binary() | undefined) -> error().
+err_gone(Text, Lang) ->
+ err(modify, 'gone', 302, Text, Lang).
+%% RFC 6120 sasy error type SHOULD be "cancel".
+%% RFC 3920 and XEP-0082 says it SHOULD be "wait".
-spec err_internal_server_error() -> error().
err_internal_server_error() ->
err(wait, 'internal-server-error', 500).
@@ -449,13 +443,13 @@ err_internal_server_error() ->
err_internal_server_error(Text, Lang) ->
err(wait, 'internal-server-error', 500, Text, Lang).
--spec err_service_unavailable() -> error().
-err_service_unavailable() ->
- err(cancel, 'service-unavailable', 503).
+-spec err_item_not_found() -> error().
+err_item_not_found() ->
+ err(cancel, 'item-not-found', 404).
--spec err_service_unavailable(binary(), binary() | undefined) -> error().
-err_service_unavailable(Text, Lang) ->
- err(cancel, 'service-unavailable', 503, Text, Lang).
+-spec err_item_not_found(binary(), binary() | undefined) -> error().
+err_item_not_found(Text, Lang) ->
+ err(cancel, 'item-not-found', 404, Text, Lang).
-spec err_jid_malformed() -> error().
err_jid_malformed() ->
@@ -465,6 +459,22 @@ err_jid_malformed() ->
err_jid_malformed(Text, Lang) ->
err(modify, 'jid-malformed', 400, Text, Lang).
+-spec err_not_acceptable() -> error().
+err_not_acceptable() ->
+ err(modify, 'not-acceptable', 406).
+
+-spec err_not_acceptable(binary(), binary() | undefined) -> error().
+err_not_acceptable(Text, Lang) ->
+ err(modify, 'not-acceptable', 406, Text, Lang).
+
+-spec err_not_allowed() -> error().
+err_not_allowed() ->
+ err(cancel, 'not-allowed', 405).
+
+-spec err_not_allowed(binary(), binary() | undefined) -> error().
+err_not_allowed(Text, Lang) ->
+ err(cancel, 'not-allowed', 405, Text, Lang).
+
-spec err_not_authorized() -> error().
err_not_authorized() ->
err(auth, 'not-authorized', 401).
@@ -473,6 +483,108 @@ err_not_authorized() ->
err_not_authorized(Text, Lang) ->
err(auth, 'not-authorized', 401, Text, Lang).
+-spec err_payment_required() -> error().
+err_payment_required() ->
+ err(auth, 'not-authorized', 402).
+
+-spec err_payment_required(binary(), binary() | undefined) -> error().
+err_payment_required(Text, Lang) ->
+ err(auth, 'not-authorized', 402, Text, Lang).
+
+%% <policy-violation/> is defined in neither RFC 3920 nor XEP-0086.
+%% We choose '403' error code (as in <forbidden/>).
+-spec err_policy_violation() -> error().
+err_policy_violation() ->
+ err(modify, 'policy-violation', 403).
+
+-spec err_policy_violation(binary(), binary() | undefined) -> error().
+err_policy_violation(Text, Lang) ->
+ err(modify, 'policy-violation', 403, Text, Lang).
+
+-spec err_recipient_unavailable() -> error().
+err_recipient_unavailable() ->
+ err(wait, 'recipient-unavailable', 404).
+
+-spec err_recipient_unavailable(binary(), binary() | undefined) -> error().
+err_recipient_unavailable(Text, Lang) ->
+ err(wait, 'recipient-unavailable', 404, Text, Lang).
+
+-spec err_redirect() -> error().
+err_redirect() ->
+ err(modify, 'redirect', 302).
+
+-spec err_redirect(binary(), binary() | undefined) -> error().
+err_redirect(Text, Lang) ->
+ err(modify, 'redirect', 302, Text, Lang).
+
+-spec err_registration_required() -> error().
+err_registration_required() ->
+ err(auth, 'registration-required', 407).
+
+-spec err_registration_required(binary(), binary() | undefined) -> error().
+err_registration_required(Text, Lang) ->
+ err(auth, 'registration-required', 407, Text, Lang).
+
+-spec err_remote_server_not_found() -> error().
+err_remote_server_not_found() ->
+ err(cancel, 'remote-server-not-found', 404).
+
+-spec err_remote_server_not_found(binary(), binary() | undefined) -> error().
+err_remote_server_not_found(Text, Lang) ->
+ err(cancel, 'remote-server-not-found', 404, Text, Lang).
+
+-spec err_remote_server_timeout() -> error().
+err_remote_server_timeout() ->
+ err(wait, 'remote-server-timeout', 504).
+
+-spec err_remote_server_timeout(binary(), binary() | undefined) -> error().
+err_remote_server_timeout(Text, Lang) ->
+ err(wait, 'remote-server-timeout', 504, Text, Lang).
+
+-spec err_resource_constraint() -> error().
+err_resource_constraint() ->
+ err(wait, 'resource-constraint', 500).
+
+-spec err_resource_constraint(binary(), binary() | undefined) -> error().
+err_resource_constraint(Text, Lang) ->
+ err(wait, 'resource-constraint', 500, Text, Lang).
+
+-spec err_service_unavailable() -> error().
+err_service_unavailable() ->
+ err(cancel, 'service-unavailable', 503).
+
+-spec err_service_unavailable(binary(), binary() | undefined) -> error().
+err_service_unavailable(Text, Lang) ->
+ err(cancel, 'service-unavailable', 503, Text, Lang).
+
+-spec err_subscription_required() -> error().
+err_subscription_required() ->
+ err(auth, 'subscription-required', 407).
+
+-spec err_subscription_required(binary(), binary() | undefined) -> error().
+err_subscription_required(Text, Lang) ->
+ err(auth, 'subscription-required', 407, Text, Lang).
+
+%% No error type is defined for <undefined-confition/>.
+%% We choose "modify" as it's used in RFC 6120 example.
+-spec err_undefined_condition() -> error().
+err_undefined_condition() ->
+ err(modify, 'undefined-condition', 500).
+
+-spec err_undefined_condition(binary(), binary() | undefined) -> error().
+err_undefined_condition(Text, Lang) ->
+ err(modify, 'undefined-condition', 500, Text, Lang).
+
+%% RFC 6120 says error type SHOULD be "wait" or "modify".
+%% RFC 3920 and XEP-0082 says it SHOULD be "wait".
+-spec err_unexpected_request() -> error().
+err_unexpected_request() ->
+ err(wait, 'unexpected-request', 400).
+
+-spec err_unexpected_request(binary(), binary() | undefined) -> error().
+err_unexpected_request(Text, Lang) ->
+ err(wait, 'unexpected-request', 400, Text, Lang).
+
%%%===================================================================
%%% Functions to construct stream errors
%%%===================================================================
@@ -712,3 +824,7 @@ add_ns(#xmlel{name = Name} = El) when Name == <<"message">>;
El#xmlel{attrs = Attrs};
add_ns(El) ->
El.
+
+-spec match_tag(xmlel() | xmpp_element(), binary(), binary()) -> boolean().
+match_tag(El, TagName, XMLNS) ->
+ get_name(El) == TagName andalso get_ns(El) == XMLNS.
diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl
index f6e5f0f1a..c59c347f9 100644
--- a/src/xmpp_codec.erl
+++ b/src/xmpp_codec.erl
@@ -15,6 +15,21 @@ decode(_el) -> decode(_el, []).
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
+ {<<"client-id">>, <<"urn:xmpp:sid:0">>} ->
+ decode_client_id(<<"urn:xmpp:sid:0">>, IgnoreEls, _el);
+ {<<"stanza-id">>, <<"urn:xmpp:sid:0">>} ->
+ decode_stanza_id(<<"urn:xmpp:sid:0">>, IgnoreEls, _el);
+ {<<"addresses">>,
+ <<"http://jabber.org/protocol/address">>} ->
+ decode_addresses(<<"http://jabber.org/protocol/address">>,
+ IgnoreEls, _el);
+ {<<"address">>,
+ <<"http://jabber.org/protocol/address">>} ->
+ decode_address(<<"http://jabber.org/protocol/address">>,
+ IgnoreEls, _el);
+ {<<"nick">>, <<"http://jabber.org/protocol/nick">>} ->
+ decode_nick(<<"http://jabber.org/protocol/nick">>,
+ IgnoreEls, _el);
{<<"x">>, <<"jabber:x:expire">>} ->
decode_expire(<<"jabber:x:expire">>, IgnoreEls, _el);
{<<"x">>, <<"jabber:x:event">>} ->
@@ -53,6 +68,9 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
{<<"instructions">>, <<"jabber:iq:search">>} ->
decode_search_instructions(<<"jabber:iq:search">>,
IgnoreEls, _el);
+ {<<"no-permanent-storage">>, <<"urn:xmpp:hints">>} ->
+ decode_hint_no_permanent_storage(<<"urn:xmpp:hints">>,
+ IgnoreEls, _el);
{<<"no-permanent-store">>, <<"urn:xmpp:hints">>} ->
decode_hint_no_permanent_store(<<"urn:xmpp:hints">>,
IgnoreEls, _el);
@@ -160,12 +178,24 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
{<<"prefs">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_prefs(<<"urn:xmpp:mam:tmp">>, IgnoreEls,
_el);
+ {<<"always">>, <<"urn:xmpp:mam:0">>} ->
+ decode_mam_always(<<"urn:xmpp:mam:0">>, IgnoreEls, _el);
+ {<<"always">>, <<"urn:xmpp:mam:1">>} ->
+ decode_mam_always(<<"urn:xmpp:mam:1">>, IgnoreEls, _el);
{<<"always">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_always(<<"urn:xmpp:mam:tmp">>, IgnoreEls,
_el);
+ {<<"never">>, <<"urn:xmpp:mam:0">>} ->
+ decode_mam_never(<<"urn:xmpp:mam:0">>, IgnoreEls, _el);
+ {<<"never">>, <<"urn:xmpp:mam:1">>} ->
+ decode_mam_never(<<"urn:xmpp:mam:1">>, IgnoreEls, _el);
{<<"never">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_never(<<"urn:xmpp:mam:tmp">>, IgnoreEls,
_el);
+ {<<"jid">>, <<"urn:xmpp:mam:0">>} ->
+ decode_mam_jid(<<"urn:xmpp:mam:0">>, IgnoreEls, _el);
+ {<<"jid">>, <<"urn:xmpp:mam:1">>} ->
+ decode_mam_jid(<<"urn:xmpp:mam:1">>, IgnoreEls, _el);
{<<"jid">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_jid(<<"urn:xmpp:mam:tmp">>, IgnoreEls, _el);
{<<"result">>, <<"urn:xmpp:mam:0">>} ->
@@ -185,6 +215,9 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
{<<"query">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_query(<<"urn:xmpp:mam:tmp">>, IgnoreEls,
_el);
+ {<<"withtext">>, <<"urn:xmpp:mam:tmp">>} ->
+ decode_mam_withtext(<<"urn:xmpp:mam:tmp">>, IgnoreEls,
+ _el);
{<<"with">>, <<"urn:xmpp:mam:tmp">>} ->
decode_mam_with(<<"urn:xmpp:mam:tmp">>, IgnoreEls, _el);
{<<"end">>, <<"urn:xmpp:mam:tmp">>} ->
@@ -216,6 +249,28 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
{<<"after">>, <<"http://jabber.org/protocol/rsm">>} ->
decode_rsm_after(<<"http://jabber.org/protocol/rsm">>,
IgnoreEls, _el);
+ {<<"unsubscribe">>, <<"urn:xmpp:mucsub:0">>} ->
+ decode_muc_unsubscribe(<<"urn:xmpp:mucsub:0">>,
+ IgnoreEls, _el);
+ {<<"subscribe">>, <<"urn:xmpp:mucsub:0">>} ->
+ decode_muc_subscribe(<<"urn:xmpp:mucsub:0">>, IgnoreEls,
+ _el);
+ {<<"event">>, <<"urn:xmpp:mucsub:0">>} ->
+ decode_muc_subscribe_event(<<"urn:xmpp:mucsub:0">>,
+ IgnoreEls, _el);
+ {<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>} ->
+ decode_muc_subscriptions(<<"urn:xmpp:mucsub:0">>,
+ IgnoreEls, _el);
+ {<<"subscription">>, <<"urn:xmpp:mucsub:0">>} ->
+ decode_muc_subscription(<<"urn:xmpp:mucsub:0">>,
+ IgnoreEls, _el);
+ {<<"x">>, <<"jabber:x:conference">>} ->
+ decode_x_conference(<<"jabber:x:conference">>,
+ IgnoreEls, _el);
+ {<<"unique">>,
+ <<"http://jabber.org/protocol/muc#unique">>} ->
+ decode_muc_unique(<<"http://jabber.org/protocol/muc#unique">>,
+ IgnoreEls, _el);
{<<"x">>, <<"http://jabber.org/protocol/muc">>} ->
decode_muc(<<"http://jabber.org/protocol/muc">>,
IgnoreEls, _el);
@@ -223,10 +278,6 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
<<"http://jabber.org/protocol/muc#admin">>} ->
decode_muc_admin(<<"http://jabber.org/protocol/muc#admin">>,
IgnoreEls, _el);
- {<<"reason">>,
- <<"http://jabber.org/protocol/muc#admin">>} ->
- decode_muc_admin_reason(<<"http://jabber.org/protocol/muc#admin">>,
- IgnoreEls, _el);
{<<"continue">>,
<<"http://jabber.org/protocol/muc#admin">>} ->
decode_muc_admin_continue(<<"http://jabber.org/protocol/muc#admin">>,
@@ -239,22 +290,26 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
<<"http://jabber.org/protocol/muc#admin">>} ->
decode_muc_admin_item(<<"http://jabber.org/protocol/muc#admin">>,
IgnoreEls, _el);
+ {<<"item">>,
+ <<"http://jabber.org/protocol/muc#owner">>} ->
+ decode_muc_owner_item(<<"http://jabber.org/protocol/muc#owner">>,
+ IgnoreEls, _el);
{<<"query">>,
<<"http://jabber.org/protocol/muc#owner">>} ->
decode_muc_owner(<<"http://jabber.org/protocol/muc#owner">>,
IgnoreEls, _el);
- {<<"destroy">>,
- <<"http://jabber.org/protocol/muc#owner">>} ->
- decode_muc_owner_destroy(<<"http://jabber.org/protocol/muc#owner">>,
- IgnoreEls, _el);
- {<<"reason">>,
- <<"http://jabber.org/protocol/muc#owner">>} ->
- decode_muc_owner_reason(<<"http://jabber.org/protocol/muc#owner">>,
- IgnoreEls, _el);
{<<"password">>,
<<"http://jabber.org/protocol/muc#owner">>} ->
- decode_muc_owner_password(<<"http://jabber.org/protocol/muc#owner">>,
- IgnoreEls, _el);
+ decode_muc_password(<<"http://jabber.org/protocol/muc#owner">>,
+ IgnoreEls, _el);
+ {<<"password">>,
+ <<"http://jabber.org/protocol/muc#user">>} ->
+ decode_muc_password(<<"http://jabber.org/protocol/muc#user">>,
+ IgnoreEls, _el);
+ {<<"password">>,
+ <<"http://jabber.org/protocol/muc">>} ->
+ decode_muc_password(<<"http://jabber.org/protocol/muc">>,
+ IgnoreEls, _el);
{<<"x">>, <<"http://jabber.org/protocol/muc#user">>} ->
decode_muc_user(<<"http://jabber.org/protocol/muc#user">>,
IgnoreEls, _el);
@@ -280,16 +335,28 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls, _el);
{<<"destroy">>,
<<"http://jabber.org/protocol/muc#user">>} ->
- decode_muc_user_destroy(<<"http://jabber.org/protocol/muc#user">>,
- IgnoreEls, _el);
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#user">>,
+ IgnoreEls, _el);
+ {<<"destroy">>,
+ <<"http://jabber.org/protocol/muc#owner">>} ->
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#owner">>,
+ IgnoreEls, _el);
{<<"decline">>,
<<"http://jabber.org/protocol/muc#user">>} ->
decode_muc_user_decline(<<"http://jabber.org/protocol/muc#user">>,
IgnoreEls, _el);
{<<"reason">>,
<<"http://jabber.org/protocol/muc#user">>} ->
- decode_muc_user_reason(<<"http://jabber.org/protocol/muc#user">>,
- IgnoreEls, _el);
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ IgnoreEls, _el);
+ {<<"reason">>,
+ <<"http://jabber.org/protocol/muc#admin">>} ->
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ IgnoreEls, _el);
+ {<<"reason">>,
+ <<"http://jabber.org/protocol/muc#owner">>} ->
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ IgnoreEls, _el);
{<<"history">>, <<"http://jabber.org/protocol/muc">>} ->
decode_muc_history(<<"http://jabber.org/protocol/muc">>,
IgnoreEls, _el);
@@ -1002,6 +1069,10 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
decode_error_policy_violation(<<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
IgnoreEls, _el);
+ {<<"payment-required">>,
+ <<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
+ decode_error_payment_required(<<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
+ IgnoreEls, _el);
{<<"not-authorized">>,
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
decode_error_not_authorized(<<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
@@ -1183,6 +1254,16 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
+ {<<"client-id">>, <<"urn:xmpp:sid:0">>} -> true;
+ {<<"stanza-id">>, <<"urn:xmpp:sid:0">>} -> true;
+ {<<"addresses">>,
+ <<"http://jabber.org/protocol/address">>} ->
+ true;
+ {<<"address">>,
+ <<"http://jabber.org/protocol/address">>} ->
+ true;
+ {<<"nick">>, <<"http://jabber.org/protocol/nick">>} ->
+ true;
{<<"x">>, <<"jabber:x:expire">>} -> true;
{<<"x">>, <<"jabber:x:event">>} -> true;
{<<"id">>, <<"jabber:x:event">>} -> true;
@@ -1197,6 +1278,8 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"last">>, <<"jabber:iq:search">>} -> true;
{<<"first">>, <<"jabber:iq:search">>} -> true;
{<<"instructions">>, <<"jabber:iq:search">>} -> true;
+ {<<"no-permanent-storage">>, <<"urn:xmpp:hints">>} ->
+ true;
{<<"no-permanent-store">>, <<"urn:xmpp:hints">>} ->
true;
{<<"store">>, <<"urn:xmpp:hints">>} -> true;
@@ -1248,8 +1331,14 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"prefs">>, <<"urn:xmpp:mam:0">>} -> true;
{<<"prefs">>, <<"urn:xmpp:mam:1">>} -> true;
{<<"prefs">>, <<"urn:xmpp:mam:tmp">>} -> true;
+ {<<"always">>, <<"urn:xmpp:mam:0">>} -> true;
+ {<<"always">>, <<"urn:xmpp:mam:1">>} -> true;
{<<"always">>, <<"urn:xmpp:mam:tmp">>} -> true;
+ {<<"never">>, <<"urn:xmpp:mam:0">>} -> true;
+ {<<"never">>, <<"urn:xmpp:mam:1">>} -> true;
{<<"never">>, <<"urn:xmpp:mam:tmp">>} -> true;
+ {<<"jid">>, <<"urn:xmpp:mam:0">>} -> true;
+ {<<"jid">>, <<"urn:xmpp:mam:1">>} -> true;
{<<"jid">>, <<"urn:xmpp:mam:tmp">>} -> true;
{<<"result">>, <<"urn:xmpp:mam:0">>} -> true;
{<<"result">>, <<"urn:xmpp:mam:1">>} -> true;
@@ -1258,6 +1347,7 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"query">>, <<"urn:xmpp:mam:0">>} -> true;
{<<"query">>, <<"urn:xmpp:mam:1">>} -> true;
{<<"query">>, <<"urn:xmpp:mam:tmp">>} -> true;
+ {<<"withtext">>, <<"urn:xmpp:mam:tmp">>} -> true;
{<<"with">>, <<"urn:xmpp:mam:tmp">>} -> true;
{<<"end">>, <<"urn:xmpp:mam:tmp">>} -> true;
{<<"start">>, <<"urn:xmpp:mam:tmp">>} -> true;
@@ -1277,13 +1367,19 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
true;
{<<"after">>, <<"http://jabber.org/protocol/rsm">>} ->
true;
+ {<<"unsubscribe">>, <<"urn:xmpp:mucsub:0">>} -> true;
+ {<<"subscribe">>, <<"urn:xmpp:mucsub:0">>} -> true;
+ {<<"event">>, <<"urn:xmpp:mucsub:0">>} -> true;
+ {<<"subscriptions">>, <<"urn:xmpp:mucsub:0">>} -> true;
+ {<<"subscription">>, <<"urn:xmpp:mucsub:0">>} -> true;
+ {<<"x">>, <<"jabber:x:conference">>} -> true;
+ {<<"unique">>,
+ <<"http://jabber.org/protocol/muc#unique">>} ->
+ true;
{<<"x">>, <<"http://jabber.org/protocol/muc">>} -> true;
{<<"query">>,
<<"http://jabber.org/protocol/muc#admin">>} ->
true;
- {<<"reason">>,
- <<"http://jabber.org/protocol/muc#admin">>} ->
- true;
{<<"continue">>,
<<"http://jabber.org/protocol/muc#admin">>} ->
true;
@@ -1293,17 +1389,20 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"item">>,
<<"http://jabber.org/protocol/muc#admin">>} ->
true;
- {<<"query">>,
+ {<<"item">>,
<<"http://jabber.org/protocol/muc#owner">>} ->
true;
- {<<"destroy">>,
+ {<<"query">>,
<<"http://jabber.org/protocol/muc#owner">>} ->
true;
- {<<"reason">>,
+ {<<"password">>,
<<"http://jabber.org/protocol/muc#owner">>} ->
true;
{<<"password">>,
- <<"http://jabber.org/protocol/muc#owner">>} ->
+ <<"http://jabber.org/protocol/muc#user">>} ->
+ true;
+ {<<"password">>,
+ <<"http://jabber.org/protocol/muc">>} ->
true;
{<<"x">>, <<"http://jabber.org/protocol/muc#user">>} ->
true;
@@ -1325,12 +1424,21 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"destroy">>,
<<"http://jabber.org/protocol/muc#user">>} ->
true;
+ {<<"destroy">>,
+ <<"http://jabber.org/protocol/muc#owner">>} ->
+ true;
{<<"decline">>,
<<"http://jabber.org/protocol/muc#user">>} ->
true;
{<<"reason">>,
<<"http://jabber.org/protocol/muc#user">>} ->
true;
+ {<<"reason">>,
+ <<"http://jabber.org/protocol/muc#admin">>} ->
+ true;
+ {<<"reason">>,
+ <<"http://jabber.org/protocol/muc#owner">>} ->
+ true;
{<<"history">>, <<"http://jabber.org/protocol/muc">>} ->
true;
{<<"query">>,
@@ -1773,6 +1881,9 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"policy-violation">>,
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
true;
+ {<<"payment-required">>,
+ <<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
+ true;
{<<"not-authorized">>,
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>} ->
true;
@@ -1914,7 +2025,7 @@ encode({disco_item, _, _, _} = Item) ->
encode_disco_item(Item,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/disco#items">>}]);
-encode({disco_items, _, _} = Query) ->
+encode({disco_items, _, _, _} = Query) ->
encode_disco_items(Query,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/disco#items">>}]);
@@ -2107,6 +2218,9 @@ encode({vcard_temp, _, _, _, _, _, _, _, _, _, _, _, _,
encode({vcard_xupdate, undefined, _} = X) ->
encode_vcard_xupdate(X,
[{<<"xmlns">>, <<"vcard-temp:x:update">>}]);
+encode({xdata_option, _, _} = Option) ->
+ encode_xdata_field_option(Option,
+ [{<<"xmlns">>, <<"jabber:x:data">>}]);
encode({xdata_field, _, _, _, _, _, _, _} = Field) ->
encode_xdata_field(Field,
[{<<"xmlns">>, <<"jabber:x:data">>}]);
@@ -2206,11 +2320,9 @@ encode({muc_decline, _, _, _} = Decline) ->
encode_muc_user_decline(Decline,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/muc#user">>}]);
-encode({muc_user_destroy, _, _} = Destroy) ->
- encode_muc_user_destroy(Destroy,
- [{<<"xmlns">>,
- <<"http://jabber.org/protocol/muc#user">>}]);
-encode({muc_invite, _, _, _} = Invite) ->
+encode({muc_destroy, _, _, _, _} = Destroy) ->
+ encode_muc_destroy(Destroy, []);
+encode({muc_invite, _, _, _, _} = Invite) ->
encode_muc_user_invite(Invite,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/muc#user">>}]);
@@ -2218,11 +2330,7 @@ encode({muc_user, _, _, _, _, _, _} = X) ->
encode_muc_user(X,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/muc#user">>}]);
-encode({muc_owner_destroy, _, _, _} = Destroy) ->
- encode_muc_owner_destroy(Destroy,
- [{<<"xmlns">>,
- <<"http://jabber.org/protocol/muc#owner">>}]);
-encode({muc_owner, _, _} = Query) ->
+encode({muc_owner, _, _, _} = Query) ->
encode_muc_owner(Query,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/muc#owner">>}]);
@@ -2237,13 +2345,29 @@ encode({muc_admin, _} = Query) ->
encode({muc, _, _} = X) ->
encode_muc(X,
[{<<"xmlns">>, <<"http://jabber.org/protocol/muc">>}]);
+encode({muc_unique, _} = Unique) ->
+ encode_muc_unique(Unique,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/muc#unique">>}]);
+encode({x_conference, _, _, _, _, _} = X) ->
+ encode_x_conference(X,
+ [{<<"xmlns">>, <<"jabber:x:conference">>}]);
+encode({muc_subscriptions, _} = Subscriptions) ->
+ encode_muc_subscriptions(Subscriptions,
+ [{<<"xmlns">>, <<"urn:xmpp:mucsub:0">>}]);
+encode({muc_subscribe, _, _} = Subscribe) ->
+ encode_muc_subscribe(Subscribe,
+ [{<<"xmlns">>, <<"urn:xmpp:mucsub:0">>}]);
+encode({muc_unsubscribe} = Unsubscribe) ->
+ encode_muc_unsubscribe(Unsubscribe,
+ [{<<"xmlns">>, <<"urn:xmpp:mucsub:0">>}]);
encode({rsm_first, _, _} = First) ->
encode_rsm_first(First,
[{<<"xmlns">>, <<"http://jabber.org/protocol/rsm">>}]);
encode({rsm_set, _, _, _, _, _, _, _} = Set) ->
encode_rsm_set(Set,
[{<<"xmlns">>, <<"http://jabber.org/protocol/rsm">>}]);
-encode({mam_query, _, _, _, _, _, _, _} = Query) ->
+encode({mam_query, _, _, _, _, _, _, _, _} = Query) ->
encode_mam_query(Query, []);
encode({mam_archived, _, _} = Archived) ->
encode_mam_archived(Archived,
@@ -2328,6 +2452,10 @@ encode({hint, 'no-permanent-store'} =
No_permanent_store) ->
encode_hint_no_permanent_store(No_permanent_store,
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
+encode({hint, 'no-permanent-storage'} =
+ No_permanent_storage) ->
+ encode_hint_no_permanent_storage(No_permanent_storage,
+ [{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
encode({search_item, _, _, _, _, _} = Item) ->
encode_search_item(Item,
[{<<"xmlns">>, <<"jabber:iq:search">>}]);
@@ -2338,7 +2466,24 @@ encode({xevent, _, _, _, _, _} = X) ->
encode_xevent(X, [{<<"xmlns">>, <<"jabber:x:event">>}]);
encode({expire, _, _} = X) ->
encode_expire(X,
- [{<<"xmlns">>, <<"jabber:x:expire">>}]).
+ [{<<"xmlns">>, <<"jabber:x:expire">>}]);
+encode({nick, _} = Nick) ->
+ encode_nick(Nick,
+ [{<<"xmlns">>, <<"http://jabber.org/protocol/nick">>}]);
+encode({address, _, _, _, _, _} = Address) ->
+ encode_address(Address,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/address">>}]);
+encode({addresses, _} = Addresses) ->
+ encode_addresses(Addresses,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/address">>}]);
+encode({stanza_id, _, _} = Stanza_id) ->
+ encode_stanza_id(Stanza_id,
+ [{<<"xmlns">>, <<"urn:xmpp:sid:0">>}]);
+encode({client_id, _} = Client_id) ->
+ encode_client_id(Client_id,
+ [{<<"xmlns">>, <<"urn:xmpp:sid:0">>}]).
get_name({last, _, _}) -> <<"query">>;
get_name({version, _, _, _}) -> <<"query">>;
@@ -2355,7 +2500,7 @@ get_name({block_list, _}) -> <<"blocklist">>;
get_name({identity, _, _, _, _}) -> <<"identity">>;
get_name({disco_info, _, _, _, _}) -> <<"query">>;
get_name({disco_item, _, _, _}) -> <<"item">>;
-get_name({disco_items, _, _}) -> <<"query">>;
+get_name({disco_items, _, _, _}) -> <<"query">>;
get_name({private, _}) -> <<"query">>;
get_name({bookmark_conference, _, _, _, _, _}) ->
<<"conference">>;
@@ -2424,6 +2569,7 @@ get_name({vcard_temp, _, _, _, _, _, _, _, _, _, _, _,
_}) ->
<<"vCard">>;
get_name({vcard_xupdate, undefined, _}) -> <<"x">>;
+get_name({xdata_option, _, _}) -> <<"option">>;
get_name({xdata_field, _, _, _, _, _, _, _}) ->
<<"field">>;
get_name({xdata, _, _, _, _, _, _}) -> <<"x">>;
@@ -2456,18 +2602,22 @@ get_name({bytestreams, _, _, _, _, _, _}) ->
<<"query">>;
get_name({muc_history, _, _, _, _}) -> <<"history">>;
get_name({muc_decline, _, _, _}) -> <<"decline">>;
-get_name({muc_user_destroy, _, _}) -> <<"destroy">>;
-get_name({muc_invite, _, _, _}) -> <<"invite">>;
+get_name({muc_destroy, _, _, _, _}) -> <<"destroy">>;
+get_name({muc_invite, _, _, _, _}) -> <<"invite">>;
get_name({muc_user, _, _, _, _, _, _}) -> <<"x">>;
-get_name({muc_owner_destroy, _, _, _}) -> <<"destroy">>;
-get_name({muc_owner, _, _}) -> <<"query">>;
+get_name({muc_owner, _, _, _}) -> <<"query">>;
get_name({muc_item, _, _, _, _, _, _, _}) -> <<"item">>;
get_name({muc_actor, _, _}) -> <<"actor">>;
get_name({muc_admin, _}) -> <<"query">>;
get_name({muc, _, _}) -> <<"x">>;
+get_name({muc_unique, _}) -> <<"unique">>;
+get_name({x_conference, _, _, _, _, _}) -> <<"x">>;
+get_name({muc_subscriptions, _}) -> <<"subscriptions">>;
+get_name({muc_subscribe, _, _}) -> <<"subscribe">>;
+get_name({muc_unsubscribe}) -> <<"unsubscribe">>;
get_name({rsm_first, _, _}) -> <<"first">>;
get_name({rsm_set, _, _, _, _, _, _, _}) -> <<"set">>;
-get_name({mam_query, _, _, _, _, _, _, _}) ->
+get_name({mam_query, _, _, _, _, _, _, _, _}) ->
<<"query">>;
get_name({mam_archived, _, _}) -> <<"archived">>;
get_name({mam_result, _, _, _, _}) -> <<"result">>;
@@ -2501,10 +2651,17 @@ get_name({hint, 'no-storage'}) -> <<"no-storage">>;
get_name({hint, store}) -> <<"store">>;
get_name({hint, 'no-permanent-store'}) ->
<<"no-permanent-store">>;
+get_name({hint, 'no-permanent-storage'}) ->
+ <<"no-permanent-storage">>;
get_name({search_item, _, _, _, _, _}) -> <<"item">>;
get_name({search, _, _, _, _, _, _, _}) -> <<"query">>;
get_name({xevent, _, _, _, _, _}) -> <<"x">>;
-get_name({expire, _, _}) -> <<"x">>.
+get_name({expire, _, _}) -> <<"x">>;
+get_name({nick, _}) -> <<"nick">>;
+get_name({address, _, _, _, _, _}) -> <<"address">>;
+get_name({addresses, _}) -> <<"addresses">>;
+get_name({stanza_id, _, _}) -> <<"stanza-id">>;
+get_name({client_id, _}) -> <<"client-id">>.
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@@ -2527,7 +2684,7 @@ get_ns({disco_info, _, _, _, _}) ->
<<"http://jabber.org/protocol/disco#info">>;
get_ns({disco_item, _, _, _}) ->
<<"http://jabber.org/protocol/disco#items">>;
-get_ns({disco_items, _, _}) ->
+get_ns({disco_items, _, _, _}) ->
<<"http://jabber.org/protocol/disco#items">>;
get_ns({private, _}) -> <<"jabber:iq:private">>;
get_ns({bookmark_conference, _, _, _, _, _}) ->
@@ -2624,6 +2781,7 @@ get_ns({vcard_temp, _, _, _, _, _, _, _, _, _, _, _, _,
<<"vcard-temp">>;
get_ns({vcard_xupdate, undefined, _}) ->
<<"vcard-temp:x:update">>;
+get_ns({xdata_option, _, _}) -> <<"jabber:x:data">>;
get_ns({xdata_field, _, _, _, _, _, _, _}) ->
<<"jabber:x:data">>;
get_ns({xdata, _, _, _, _, _, _}) ->
@@ -2675,25 +2833,32 @@ get_ns({muc_history, _, _, _, _}) ->
<<"http://jabber.org/protocol/muc">>;
get_ns({muc_decline, _, _, _}) ->
<<"http://jabber.org/protocol/muc#user">>;
-get_ns({muc_user_destroy, _, _}) ->
- <<"http://jabber.org/protocol/muc#user">>;
-get_ns({muc_invite, _, _, _}) ->
+get_ns({muc_destroy, Xmlns, _, _, _}) -> Xmlns;
+get_ns({muc_invite, _, _, _, _}) ->
<<"http://jabber.org/protocol/muc#user">>;
get_ns({muc_user, _, _, _, _, _, _}) ->
<<"http://jabber.org/protocol/muc#user">>;
-get_ns({muc_owner_destroy, _, _, _}) ->
- <<"http://jabber.org/protocol/muc#owner">>;
-get_ns({muc_owner, _, _}) ->
+get_ns({muc_owner, _, _, _}) ->
<<"http://jabber.org/protocol/muc#owner">>;
get_ns({muc_admin, _}) ->
<<"http://jabber.org/protocol/muc#admin">>;
get_ns({muc, _, _}) ->
<<"http://jabber.org/protocol/muc">>;
+get_ns({muc_unique, _}) ->
+ <<"http://jabber.org/protocol/muc#unique">>;
+get_ns({x_conference, _, _, _, _, _}) ->
+ <<"jabber:x:conference">>;
+get_ns({muc_subscriptions, _}) ->
+ <<"urn:xmpp:mucsub:0">>;
+get_ns({muc_subscribe, _, _}) ->
+ <<"urn:xmpp:mucsub:0">>;
+get_ns({muc_unsubscribe}) -> <<"urn:xmpp:mucsub:0">>;
get_ns({rsm_first, _, _}) ->
<<"http://jabber.org/protocol/rsm">>;
get_ns({rsm_set, _, _, _, _, _, _, _}) ->
<<"http://jabber.org/protocol/rsm">>;
-get_ns({mam_query, Xmlns, _, _, _, _, _, _}) -> Xmlns;
+get_ns({mam_query, Xmlns, _, _, _, _, _, _, _}) ->
+ Xmlns;
get_ns({mam_archived, _, _}) -> <<"urn:xmpp:mam:tmp">>;
get_ns({mam_result, Xmlns, _, _, _}) -> Xmlns;
get_ns({mam_prefs, Xmlns, _, _, _}) -> Xmlns;
@@ -2729,12 +2894,22 @@ get_ns({hint, 'no-storage'}) -> <<"urn:xmpp:hints">>;
get_ns({hint, store}) -> <<"urn:xmpp:hints">>;
get_ns({hint, 'no-permanent-store'}) ->
<<"urn:xmpp:hints">>;
+get_ns({hint, 'no-permanent-storage'}) ->
+ <<"urn:xmpp:hints">>;
get_ns({search_item, _, _, _, _, _}) ->
<<"jabber:iq:search">>;
get_ns({search, _, _, _, _, _, _, _}) ->
<<"jabber:iq:search">>;
get_ns({xevent, _, _, _, _, _}) -> <<"jabber:x:event">>;
-get_ns({expire, _, _}) -> <<"jabber:x:expire">>.
+get_ns({expire, _, _}) -> <<"jabber:x:expire">>;
+get_ns({nick, _}) ->
+ <<"http://jabber.org/protocol/nick">>;
+get_ns({address, _, _, _, _, _}) ->
+ <<"http://jabber.org/protocol/address">>;
+get_ns({addresses, _}) ->
+ <<"http://jabber.org/protocol/address">>;
+get_ns({stanza_id, _, _}) -> <<"urn:xmpp:sid:0">>;
+get_ns({client_id, _}) -> <<"urn:xmpp:sid:0">>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
@@ -2801,7 +2976,7 @@ pp(identity, 4) -> [category, type, lang, name];
pp(disco_info, 4) ->
[node, identities, features, xdata];
pp(disco_item, 3) -> [jid, name, node];
-pp(disco_items, 2) -> [node, items];
+pp(disco_items, 3) -> [node, items, rsm];
pp(private, 1) -> [xml_els];
pp(bookmark_conference, 5) ->
[name, jid, autojoin, nick, password];
@@ -2876,6 +3051,7 @@ pp(vcard_temp, 29) ->
org, categories, note, prodid, rev, sort_string, sound,
uid, url, class, key, desc];
pp(vcard_xupdate, 2) -> [us, hash];
+pp(xdata_option, 2) -> [label, value];
pp(xdata_field, 7) ->
[label, type, var, required, desc, values, options];
pp(xdata, 6) ->
@@ -2905,23 +3081,28 @@ pp(bytestreams, 6) ->
pp(muc_history, 4) ->
[maxchars, maxstanzas, seconds, since];
pp(muc_decline, 3) -> [reason, from, to];
-pp(muc_user_destroy, 2) -> [reason, jid];
-pp(muc_invite, 3) -> [reason, from, to];
+pp(muc_destroy, 4) -> [xmlns, jid, reason, password];
+pp(muc_invite, 4) -> [reason, from, to, continue];
pp(muc_user, 6) ->
[decline, destroy, invites, items, status_codes,
password];
-pp(muc_owner_destroy, 3) -> [jid, reason, password];
-pp(muc_owner, 2) -> [destroy, config];
+pp(muc_owner, 3) -> [destroy, config, items];
pp(muc_item, 7) ->
[actor, continue, reason, affiliation, role, jid, nick];
pp(muc_actor, 2) -> [jid, nick];
pp(muc_admin, 1) -> [items];
pp(muc, 2) -> [history, password];
+pp(muc_unique, 1) -> [name];
+pp(x_conference, 5) ->
+ [jid, password, reason, continue, thread];
+pp(muc_subscriptions, 1) -> [list];
+pp(muc_subscribe, 2) -> [nick, events];
+pp(muc_unsubscribe, 0) -> [];
pp(rsm_first, 2) -> [index, data];
pp(rsm_set, 7) ->
['after', before, count, first, index, last, max];
-pp(mam_query, 7) ->
- [xmlns, id, start, 'end', with, rsm, xdata];
+pp(mam_query, 8) ->
+ [xmlns, id, start, 'end', with, withtext, rsm, xdata];
pp(mam_archived, 2) -> [by, id];
pp(mam_result, 4) -> [xmlns, queryid, id, sub_els];
pp(mam_prefs, 4) -> [xmlns, default, always, never];
@@ -2954,6 +3135,11 @@ pp(search, 7) ->
pp(xevent, 5) ->
[offline, delivered, displayed, composing, id];
pp(expire, 2) -> [seconds, stored];
+pp(nick, 1) -> [name];
+pp(address, 5) -> [type, jid, desc, node, delivered];
+pp(addresses, 1) -> [list];
+pp(stanza_id, 2) -> [by, id];
+pp(client_id, 1) -> [id];
pp(_, _) -> no.
join([], _Sep) -> <<>>;
@@ -3000,6 +3186,274 @@ dec_tzo(Val) ->
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
+decode_client_id(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"client-id">>, _attrs, _els}) ->
+ Id = decode_client_id_attrs(__TopXMLNS, _attrs,
+ undefined),
+ {client_id, Id}.
+
+decode_client_id_attrs(__TopXMLNS,
+ [{<<"id">>, _val} | _attrs], _Id) ->
+ decode_client_id_attrs(__TopXMLNS, _attrs, _val);
+decode_client_id_attrs(__TopXMLNS, [_ | _attrs], Id) ->
+ decode_client_id_attrs(__TopXMLNS, _attrs, Id);
+decode_client_id_attrs(__TopXMLNS, [], Id) ->
+ decode_client_id_attr_id(__TopXMLNS, Id).
+
+encode_client_id({client_id, Id}, _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_client_id_attr_id(Id, _xmlns_attrs),
+ {xmlel, <<"client-id">>, _attrs, _els}.
+
+decode_client_id_attr_id(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"id">>, <<"client-id">>, __TopXMLNS}});
+decode_client_id_attr_id(__TopXMLNS, _val) -> _val.
+
+encode_client_id_attr_id(_val, _acc) ->
+ [{<<"id">>, _val} | _acc].
+
+decode_stanza_id(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"stanza-id">>, _attrs, _els}) ->
+ {Id, By} = decode_stanza_id_attrs(__TopXMLNS, _attrs,
+ undefined, undefined),
+ {stanza_id, By, Id}.
+
+decode_stanza_id_attrs(__TopXMLNS,
+ [{<<"id">>, _val} | _attrs], _Id, By) ->
+ decode_stanza_id_attrs(__TopXMLNS, _attrs, _val, By);
+decode_stanza_id_attrs(__TopXMLNS,
+ [{<<"by">>, _val} | _attrs], Id, _By) ->
+ decode_stanza_id_attrs(__TopXMLNS, _attrs, Id, _val);
+decode_stanza_id_attrs(__TopXMLNS, [_ | _attrs], Id,
+ By) ->
+ decode_stanza_id_attrs(__TopXMLNS, _attrs, Id, By);
+decode_stanza_id_attrs(__TopXMLNS, [], Id, By) ->
+ {decode_stanza_id_attr_id(__TopXMLNS, Id),
+ decode_stanza_id_attr_by(__TopXMLNS, By)}.
+
+encode_stanza_id({stanza_id, By, Id}, _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_stanza_id_attr_by(By,
+ encode_stanza_id_attr_id(Id,
+ _xmlns_attrs)),
+ {xmlel, <<"stanza-id">>, _attrs, _els}.
+
+decode_stanza_id_attr_id(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"id">>, <<"stanza-id">>, __TopXMLNS}});
+decode_stanza_id_attr_id(__TopXMLNS, _val) -> _val.
+
+encode_stanza_id_attr_id(_val, _acc) ->
+ [{<<"id">>, _val} | _acc].
+
+decode_stanza_id_attr_by(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"by">>, <<"stanza-id">>, __TopXMLNS}});
+decode_stanza_id_attr_by(__TopXMLNS, _val) ->
+ case catch dec_jid(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"by">>, <<"stanza-id">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_stanza_id_attr_by(_val, _acc) ->
+ [{<<"by">>, enc_jid(_val)} | _acc].
+
+decode_addresses(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"addresses">>, _attrs, _els}) ->
+ List = decode_addresses_els(__TopXMLNS, __IgnoreEls,
+ _els, []),
+ {addresses, List}.
+
+decode_addresses_els(__TopXMLNS, __IgnoreEls, [],
+ List) ->
+ lists:reverse(List);
+decode_addresses_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"address">>, _attrs, _} = _el | _els],
+ List) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/address">> ->
+ decode_addresses_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_address(__TopXMLNS, __IgnoreEls, _el)
+ | List]);
+ <<"http://jabber.org/protocol/address">> ->
+ decode_addresses_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_address(<<"http://jabber.org/protocol/address">>,
+ __IgnoreEls, _el)
+ | List]);
+ _ ->
+ decode_addresses_els(__TopXMLNS, __IgnoreEls, _els,
+ List)
+ end;
+decode_addresses_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], List) ->
+ decode_addresses_els(__TopXMLNS, __IgnoreEls, _els,
+ List).
+
+encode_addresses({addresses, List}, _xmlns_attrs) ->
+ _els = lists:reverse('encode_addresses_$list'(List,
+ [])),
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"addresses">>, _attrs, _els}.
+
+'encode_addresses_$list'([], _acc) -> _acc;
+'encode_addresses_$list'([List | _els], _acc) ->
+ 'encode_addresses_$list'(_els,
+ [encode_address(List, []) | _acc]).
+
+decode_address(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"address">>, _attrs, _els}) ->
+ {Type, Jid, Desc, Node, Delivered} =
+ decode_address_attrs(__TopXMLNS, _attrs, undefined,
+ undefined, undefined, undefined, undefined),
+ {address, Type, Jid, Desc, Node, Delivered}.
+
+decode_address_attrs(__TopXMLNS,
+ [{<<"type">>, _val} | _attrs], _Type, Jid, Desc, Node,
+ Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, _val, Jid,
+ Desc, Node, Delivered);
+decode_address_attrs(__TopXMLNS,
+ [{<<"jid">>, _val} | _attrs], Type, _Jid, Desc, Node,
+ Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, Type, _val,
+ Desc, Node, Delivered);
+decode_address_attrs(__TopXMLNS,
+ [{<<"desc">>, _val} | _attrs], Type, Jid, _Desc, Node,
+ Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, Type, Jid,
+ _val, Node, Delivered);
+decode_address_attrs(__TopXMLNS,
+ [{<<"node">>, _val} | _attrs], Type, Jid, Desc, _Node,
+ Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, Type, Jid,
+ Desc, _val, Delivered);
+decode_address_attrs(__TopXMLNS,
+ [{<<"delivered">>, _val} | _attrs], Type, Jid, Desc,
+ Node, _Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, Type, Jid,
+ Desc, Node, _val);
+decode_address_attrs(__TopXMLNS, [_ | _attrs], Type,
+ Jid, Desc, Node, Delivered) ->
+ decode_address_attrs(__TopXMLNS, _attrs, Type, Jid,
+ Desc, Node, Delivered);
+decode_address_attrs(__TopXMLNS, [], Type, Jid, Desc,
+ Node, Delivered) ->
+ {decode_address_attr_type(__TopXMLNS, Type),
+ decode_address_attr_jid(__TopXMLNS, Jid),
+ decode_address_attr_desc(__TopXMLNS, Desc),
+ decode_address_attr_node(__TopXMLNS, Node),
+ decode_address_attr_delivered(__TopXMLNS, Delivered)}.
+
+encode_address({address, Type, Jid, Desc, Node,
+ Delivered},
+ _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_address_attr_delivered(Delivered,
+ encode_address_attr_node(Node,
+ encode_address_attr_desc(Desc,
+ encode_address_attr_jid(Jid,
+ encode_address_attr_type(Type,
+ _xmlns_attrs))))),
+ {xmlel, <<"address">>, _attrs, _els}.
+
+decode_address_attr_type(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"type">>, <<"address">>, __TopXMLNS}});
+decode_address_attr_type(__TopXMLNS, _val) ->
+ case catch dec_enum(_val,
+ [bcc, cc, noreply, ofrom, replyroom, replyto, to])
+ of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"type">>, <<"address">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_address_attr_type(_val, _acc) ->
+ [{<<"type">>, enc_enum(_val)} | _acc].
+
+decode_address_attr_jid(__TopXMLNS, undefined) ->
+ undefined;
+decode_address_attr_jid(__TopXMLNS, _val) ->
+ case catch dec_jid(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"jid">>, <<"address">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_address_attr_jid(undefined, _acc) -> _acc;
+encode_address_attr_jid(_val, _acc) ->
+ [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_address_attr_desc(__TopXMLNS, undefined) ->
+ undefined;
+decode_address_attr_desc(__TopXMLNS, _val) -> _val.
+
+encode_address_attr_desc(undefined, _acc) -> _acc;
+encode_address_attr_desc(_val, _acc) ->
+ [{<<"desc">>, _val} | _acc].
+
+decode_address_attr_node(__TopXMLNS, undefined) ->
+ undefined;
+decode_address_attr_node(__TopXMLNS, _val) -> _val.
+
+encode_address_attr_node(undefined, _acc) -> _acc;
+encode_address_attr_node(_val, _acc) ->
+ [{<<"node">>, _val} | _acc].
+
+decode_address_attr_delivered(__TopXMLNS, undefined) ->
+ undefined;
+decode_address_attr_delivered(__TopXMLNS, _val) ->
+ case catch dec_bool(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"delivered">>, <<"address">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_address_attr_delivered(undefined, _acc) -> _acc;
+encode_address_attr_delivered(_val, _acc) ->
+ [{<<"delivered">>, enc_bool(_val)} | _acc].
+
+decode_nick(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"nick">>, _attrs, _els}) ->
+ Name = decode_nick_els(__TopXMLNS, __IgnoreEls, _els,
+ <<>>),
+ {nick, Name}.
+
+decode_nick_els(__TopXMLNS, __IgnoreEls, [], Name) ->
+ decode_nick_cdata(__TopXMLNS, Name);
+decode_nick_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Name) ->
+ decode_nick_els(__TopXMLNS, __IgnoreEls, _els,
+ <<Name/binary, _data/binary>>);
+decode_nick_els(__TopXMLNS, __IgnoreEls, [_ | _els],
+ Name) ->
+ decode_nick_els(__TopXMLNS, __IgnoreEls, _els, Name).
+
+encode_nick({nick, Name}, _xmlns_attrs) ->
+ _els = encode_nick_cdata(Name, []),
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"nick">>, _attrs, _els}.
+
+decode_nick_cdata(__TopXMLNS, <<>>) ->
+ erlang:error({xmpp_codec,
+ {missing_cdata, <<>>, <<"nick">>, __TopXMLNS}});
+decode_nick_cdata(__TopXMLNS, _val) -> _val.
+
+encode_nick_cdata(_val, _acc) ->
+ [{xmlcdata, _val} | _acc].
+
decode_expire(__TopXMLNS, __IgnoreEls,
{xmlel, <<"x">>, _attrs, _els}) ->
{Seconds, Stored} = decode_expire_attrs(__TopXMLNS,
@@ -3745,6 +4199,19 @@ encode_search_instructions_cdata(undefined, _acc) ->
encode_search_instructions_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
+decode_hint_no_permanent_storage(__TopXMLNS,
+ __IgnoreEls,
+ {xmlel, <<"no-permanent-storage">>, _attrs,
+ _els}) ->
+ {hint, 'no-permanent-storage'}.
+
+encode_hint_no_permanent_storage({hint,
+ 'no-permanent-storage'},
+ _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"no-permanent-storage">>, _attrs, _els}.
+
decode_hint_no_permanent_store(__TopXMLNS, __IgnoreEls,
{xmlel, <<"no-permanent-store">>, _attrs,
_els}) ->
@@ -5380,7 +5847,8 @@ encode_mam_fin_attr_complete(_val, _acc) ->
decode_mam_prefs(__TopXMLNS, __IgnoreEls,
{xmlel, <<"prefs">>, _attrs, _els}) ->
{Never, Always} = decode_mam_prefs_els(__TopXMLNS,
- __IgnoreEls, _els, [], []),
+ __IgnoreEls, _els, undefined,
+ undefined),
{Default, Xmlns} = decode_mam_prefs_attrs(__TopXMLNS,
_attrs, undefined, undefined),
{mam_prefs, Xmlns, Default, Always, Never}.
@@ -5392,10 +5860,23 @@ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"always">>, _attrs, _} = _el | _els], Never,
Always) ->
case get_attr(<<"xmlns">>, _attrs) of
- <<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
+ <<"">>
+ when __TopXMLNS == <<"urn:xmpp:mam:1">>;
+ __TopXMLNS == <<"urn:xmpp:mam:0">>;
+ __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
Never,
decode_mam_always(__TopXMLNS, __IgnoreEls, _el));
+ <<"urn:xmpp:mam:0">> ->
+ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
+ Never,
+ decode_mam_always(<<"urn:xmpp:mam:0">>,
+ __IgnoreEls, _el));
+ <<"urn:xmpp:mam:1">> ->
+ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
+ Never,
+ decode_mam_always(<<"urn:xmpp:mam:1">>,
+ __IgnoreEls, _el));
<<"urn:xmpp:mam:tmp">> ->
decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
Never,
@@ -5409,10 +5890,23 @@ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"never">>, _attrs, _} = _el | _els], Never,
Always) ->
case get_attr(<<"xmlns">>, _attrs) of
- <<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
+ <<"">>
+ when __TopXMLNS == <<"urn:xmpp:mam:1">>;
+ __TopXMLNS == <<"urn:xmpp:mam:0">>;
+ __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
decode_mam_never(__TopXMLNS, __IgnoreEls, _el),
Always);
+ <<"urn:xmpp:mam:0">> ->
+ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_mam_never(<<"urn:xmpp:mam:0">>,
+ __IgnoreEls, _el),
+ Always);
+ <<"urn:xmpp:mam:1">> ->
+ decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_mam_never(<<"urn:xmpp:mam:1">>,
+ __IgnoreEls, _el),
+ Always);
<<"urn:xmpp:mam:tmp">> ->
decode_mam_prefs_els(__TopXMLNS, __IgnoreEls, _els,
decode_mam_never(<<"urn:xmpp:mam:tmp">>,
@@ -5454,11 +5948,11 @@ encode_mam_prefs({mam_prefs, Xmlns, Default, Always,
_xmlns_attrs)),
{xmlel, <<"prefs">>, _attrs, _els}.
-'encode_mam_prefs_$never'([], _acc) -> _acc;
+'encode_mam_prefs_$never'(undefined, _acc) -> _acc;
'encode_mam_prefs_$never'(Never, _acc) ->
[encode_mam_never(Never, []) | _acc].
-'encode_mam_prefs_$always'([], _acc) -> _acc;
+'encode_mam_prefs_$always'(undefined, _acc) -> _acc;
'encode_mam_prefs_$always'(Always, _acc) ->
[encode_mam_always(Always, []) | _acc].
@@ -5497,12 +5991,31 @@ decode_mam_always_els(__TopXMLNS, __IgnoreEls, [],
decode_mam_always_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"jid">>, _attrs, _} = _el | _els], Jids) ->
case get_attr(<<"xmlns">>, _attrs) of
- <<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
+ <<"">>
+ when __TopXMLNS == <<"urn:xmpp:mam:1">>;
+ __TopXMLNS == <<"urn:xmpp:mam:0">>;
+ __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_always_els(__TopXMLNS, __IgnoreEls, _els,
case decode_mam_jid(__TopXMLNS, __IgnoreEls,
_el)
of
- [] -> Jids;
+ undefined -> Jids;
+ _new_el -> [_new_el | Jids]
+ end);
+ <<"urn:xmpp:mam:0">> ->
+ decode_mam_always_els(__TopXMLNS, __IgnoreEls, _els,
+ case decode_mam_jid(<<"urn:xmpp:mam:0">>,
+ __IgnoreEls, _el)
+ of
+ undefined -> Jids;
+ _new_el -> [_new_el | Jids]
+ end);
+ <<"urn:xmpp:mam:1">> ->
+ decode_mam_always_els(__TopXMLNS, __IgnoreEls, _els,
+ case decode_mam_jid(<<"urn:xmpp:mam:1">>,
+ __IgnoreEls, _el)
+ of
+ undefined -> Jids;
_new_el -> [_new_el | Jids]
end);
<<"urn:xmpp:mam:tmp">> ->
@@ -5510,7 +6023,7 @@ decode_mam_always_els(__TopXMLNS, __IgnoreEls,
case decode_mam_jid(<<"urn:xmpp:mam:tmp">>,
__IgnoreEls, _el)
of
- [] -> Jids;
+ undefined -> Jids;
_new_el -> [_new_el | Jids]
end);
_ ->
@@ -5545,11 +6058,30 @@ decode_mam_never_els(__TopXMLNS, __IgnoreEls, [],
decode_mam_never_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"jid">>, _attrs, _} = _el | _els], Jids) ->
case get_attr(<<"xmlns">>, _attrs) of
- <<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
+ <<"">>
+ when __TopXMLNS == <<"urn:xmpp:mam:1">>;
+ __TopXMLNS == <<"urn:xmpp:mam:0">>;
+ __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_never_els(__TopXMLNS, __IgnoreEls, _els,
case decode_mam_jid(__TopXMLNS, __IgnoreEls, _el)
of
- [] -> Jids;
+ undefined -> Jids;
+ _new_el -> [_new_el | Jids]
+ end);
+ <<"urn:xmpp:mam:0">> ->
+ decode_mam_never_els(__TopXMLNS, __IgnoreEls, _els,
+ case decode_mam_jid(<<"urn:xmpp:mam:0">>,
+ __IgnoreEls, _el)
+ of
+ undefined -> Jids;
+ _new_el -> [_new_el | Jids]
+ end);
+ <<"urn:xmpp:mam:1">> ->
+ decode_mam_never_els(__TopXMLNS, __IgnoreEls, _els,
+ case decode_mam_jid(<<"urn:xmpp:mam:1">>,
+ __IgnoreEls, _el)
+ of
+ undefined -> Jids;
_new_el -> [_new_el | Jids]
end);
<<"urn:xmpp:mam:tmp">> ->
@@ -5557,7 +6089,7 @@ decode_mam_never_els(__TopXMLNS, __IgnoreEls,
case decode_mam_jid(<<"urn:xmpp:mam:tmp">>,
__IgnoreEls, _el)
of
- [] -> Jids;
+ undefined -> Jids;
_new_el -> [_new_el | Jids]
end);
_ ->
@@ -5759,104 +6291,125 @@ encode_mam_archived_attr_by(_val, _acc) ->
decode_mam_query(__TopXMLNS, __IgnoreEls,
{xmlel, <<"query">>, _attrs, _els}) ->
- {Xdata, End, Start, With, Rsm} =
+ {Xdata, Withtext, End, Start, With, Rsm} =
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
undefined, undefined, undefined, undefined,
- undefined),
+ undefined, undefined),
{Id, Xmlns} = decode_mam_query_attrs(__TopXMLNS, _attrs,
undefined, undefined),
- {mam_query, Xmlns, Id, Start, End, With, Rsm, Xdata}.
+ {mam_query, Xmlns, Id, Start, End, With, Withtext, Rsm,
+ Xdata}.
decode_mam_query_els(__TopXMLNS, __IgnoreEls, [], Xdata,
- End, Start, With, Rsm) ->
- {Xdata, End, Start, With, Rsm};
+ Withtext, End, Start, With, Rsm) ->
+ {Xdata, Withtext, End, Start, With, Rsm};
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"start">>, _attrs, _} = _el | _els], Xdata,
- End, Start, With, Rsm) ->
+ Withtext, End, Start, With, Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End,
+ Xdata, Withtext, End,
decode_mam_start(__TopXMLNS, __IgnoreEls, _el),
With, Rsm);
<<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End,
+ Xdata, Withtext, End,
decode_mam_start(<<"urn:xmpp:mam:tmp">>,
__IgnoreEls, _el),
With, Rsm);
_ ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm)
+ Xdata, Withtext, End, Start, With, Rsm)
end;
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"end">>, _attrs, _} = _el | _els], Xdata,
- End, Start, With, Rsm) ->
+ Withtext, End, Start, With, Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata,
+ Xdata, Withtext,
decode_mam_end(__TopXMLNS, __IgnoreEls, _el),
Start, With, Rsm);
<<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata,
+ Xdata, Withtext,
decode_mam_end(<<"urn:xmpp:mam:tmp">>,
__IgnoreEls, _el),
Start, With, Rsm);
_ ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm)
+ Xdata, Withtext, End, Start, With, Rsm)
end;
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"with">>, _attrs, _} = _el | _els], Xdata,
- End, Start, With, Rsm) ->
+ Withtext, End, Start, With, Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start,
+ Xdata, Withtext, End, Start,
decode_mam_with(__TopXMLNS, __IgnoreEls, _el),
Rsm);
<<"urn:xmpp:mam:tmp">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start,
+ Xdata, Withtext, End, Start,
decode_mam_with(<<"urn:xmpp:mam:tmp">>,
__IgnoreEls, _el),
Rsm);
_ ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm)
+ Xdata, Withtext, End, Start, With, Rsm)
+ end;
+decode_mam_query_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"withtext">>, _attrs, _} = _el | _els],
+ Xdata, Withtext, End, Start, With, Rsm) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">> when __TopXMLNS == <<"urn:xmpp:mam:tmp">> ->
+ decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata,
+ decode_mam_withtext(__TopXMLNS, __IgnoreEls,
+ _el),
+ End, Start, With, Rsm);
+ <<"urn:xmpp:mam:tmp">> ->
+ decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata,
+ decode_mam_withtext(<<"urn:xmpp:mam:tmp">>,
+ __IgnoreEls, _el),
+ End, Start, With, Rsm);
+ _ ->
+ decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Withtext, End, Start, With, Rsm)
end;
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"set">>, _attrs, _} = _el | _els], Xdata,
- End, Start, With, Rsm) ->
+ Withtext, End, Start, With, Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"http://jabber.org/protocol/rsm">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With,
+ Xdata, Withtext, End, Start, With,
decode_rsm_set(<<"http://jabber.org/protocol/rsm">>,
__IgnoreEls, _el));
_ ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm)
+ Xdata, Withtext, End, Start, With, Rsm)
end;
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"x">>, _attrs, _} = _el | _els], Xdata, End,
- Start, With, Rsm) ->
+ [{xmlel, <<"x">>, _attrs, _} = _el | _els], Xdata,
+ Withtext, End, Start, With, Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"jabber:x:data">> ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
decode_xdata(<<"jabber:x:data">>, __IgnoreEls,
_el),
- End, Start, With, Rsm);
+ Withtext, End, Start, With, Rsm);
_ ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm)
+ Xdata, Withtext, End, Start, With, Rsm)
end;
decode_mam_query_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Xdata, End, Start, With, Rsm) ->
+ [_ | _els], Xdata, Withtext, End, Start, With, Rsm) ->
decode_mam_query_els(__TopXMLNS, __IgnoreEls, _els,
- Xdata, End, Start, With, Rsm).
+ Xdata, Withtext, End, Start, With, Rsm).
decode_mam_query_attrs(__TopXMLNS,
[{<<"queryid">>, _val} | _attrs], _Id, Xmlns) ->
@@ -5872,14 +6425,15 @@ decode_mam_query_attrs(__TopXMLNS, [], Id, Xmlns) ->
decode_mam_query_attr_xmlns(__TopXMLNS, Xmlns)}.
encode_mam_query({mam_query, Xmlns, Id, Start, End,
- With, Rsm, Xdata},
+ With, Withtext, Rsm, Xdata},
_xmlns_attrs) ->
_els = lists:reverse('encode_mam_query_$xdata'(Xdata,
- 'encode_mam_query_$end'(End,
- 'encode_mam_query_$start'(Start,
- 'encode_mam_query_$with'(With,
- 'encode_mam_query_$rsm'(Rsm,
- [])))))),
+ 'encode_mam_query_$withtext'(Withtext,
+ 'encode_mam_query_$end'(End,
+ 'encode_mam_query_$start'(Start,
+ 'encode_mam_query_$with'(With,
+ 'encode_mam_query_$rsm'(Rsm,
+ []))))))),
_attrs = encode_mam_query_attr_xmlns(Xmlns,
encode_mam_query_attr_queryid(Id,
_xmlns_attrs)),
@@ -5891,6 +6445,10 @@ encode_mam_query({mam_query, Xmlns, Id, Start, End,
[{<<"xmlns">>, <<"jabber:x:data">>}])
| _acc].
+'encode_mam_query_$withtext'(undefined, _acc) -> _acc;
+'encode_mam_query_$withtext'(Withtext, _acc) ->
+ [encode_mam_withtext(Withtext, []) | _acc].
+
'encode_mam_query_$end'(undefined, _acc) -> _acc;
'encode_mam_query_$end'(End, _acc) ->
[encode_mam_end(End, []) | _acc].
@@ -5925,6 +6483,37 @@ encode_mam_query_attr_xmlns(undefined, _acc) -> _acc;
encode_mam_query_attr_xmlns(_val, _acc) ->
[{<<"xmlns">>, _val} | _acc].
+decode_mam_withtext(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"withtext">>, _attrs, _els}) ->
+ Cdata = decode_mam_withtext_els(__TopXMLNS, __IgnoreEls,
+ _els, <<>>),
+ Cdata.
+
+decode_mam_withtext_els(__TopXMLNS, __IgnoreEls, [],
+ Cdata) ->
+ decode_mam_withtext_cdata(__TopXMLNS, Cdata);
+decode_mam_withtext_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Cdata) ->
+ decode_mam_withtext_els(__TopXMLNS, __IgnoreEls, _els,
+ <<Cdata/binary, _data/binary>>);
+decode_mam_withtext_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Cdata) ->
+ decode_mam_withtext_els(__TopXMLNS, __IgnoreEls, _els,
+ Cdata).
+
+encode_mam_withtext(Cdata, _xmlns_attrs) ->
+ _els = encode_mam_withtext_cdata(Cdata, []),
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"withtext">>, _attrs, _els}.
+
+decode_mam_withtext_cdata(__TopXMLNS, <<>>) ->
+ erlang:error({xmpp_codec,
+ {missing_cdata, <<>>, <<"withtext">>, __TopXMLNS}});
+decode_mam_withtext_cdata(__TopXMLNS, _val) -> _val.
+
+encode_mam_withtext_cdata(_val, _acc) ->
+ [{xmlcdata, _val} | _acc].
+
decode_mam_with(__TopXMLNS, __IgnoreEls,
{xmlel, <<"with">>, _attrs, _els}) ->
Cdata = decode_mam_with_els(__TopXMLNS, __IgnoreEls,
@@ -6455,10 +7044,10 @@ encode_rsm_before(Cdata, _xmlns_attrs) ->
_attrs = _xmlns_attrs,
{xmlel, <<"before">>, _attrs, _els}.
-decode_rsm_before_cdata(__TopXMLNS, <<>>) -> none;
+decode_rsm_before_cdata(__TopXMLNS, <<>>) -> <<>>;
decode_rsm_before_cdata(__TopXMLNS, _val) -> _val.
-encode_rsm_before_cdata(none, _acc) -> _acc;
+encode_rsm_before_cdata(<<>>, _acc) -> _acc;
encode_rsm_before_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
@@ -6492,63 +7081,438 @@ encode_rsm_after_cdata(undefined, _acc) -> _acc;
encode_rsm_after_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
+decode_muc_unsubscribe(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"unsubscribe">>, _attrs, _els}) ->
+ {muc_unsubscribe}.
+
+encode_muc_unsubscribe({muc_unsubscribe},
+ _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"unsubscribe">>, _attrs, _els}.
+
+decode_muc_subscribe(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"subscribe">>, _attrs, _els}) ->
+ Events = decode_muc_subscribe_els(__TopXMLNS,
+ __IgnoreEls, _els, []),
+ Nick = decode_muc_subscribe_attrs(__TopXMLNS, _attrs,
+ undefined),
+ {muc_subscribe, Nick, Events}.
+
+decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls, [],
+ Events) ->
+ lists:reverse(Events);
+decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"event">>, _attrs, _} = _el | _els],
+ Events) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">> when __TopXMLNS == <<"urn:xmpp:mucsub:0">> ->
+ decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_muc_subscribe_event(__TopXMLNS,
+ __IgnoreEls, _el)
+ | Events]);
+ <<"urn:xmpp:mucsub:0">> ->
+ decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_muc_subscribe_event(<<"urn:xmpp:mucsub:0">>,
+ __IgnoreEls, _el)
+ | Events]);
+ _ ->
+ decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls, _els,
+ Events)
+ end;
+decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Events) ->
+ decode_muc_subscribe_els(__TopXMLNS, __IgnoreEls, _els,
+ Events).
+
+decode_muc_subscribe_attrs(__TopXMLNS,
+ [{<<"nick">>, _val} | _attrs], _Nick) ->
+ decode_muc_subscribe_attrs(__TopXMLNS, _attrs, _val);
+decode_muc_subscribe_attrs(__TopXMLNS, [_ | _attrs],
+ Nick) ->
+ decode_muc_subscribe_attrs(__TopXMLNS, _attrs, Nick);
+decode_muc_subscribe_attrs(__TopXMLNS, [], Nick) ->
+ decode_muc_subscribe_attr_nick(__TopXMLNS, Nick).
+
+encode_muc_subscribe({muc_subscribe, Nick, Events},
+ _xmlns_attrs) ->
+ _els =
+ lists:reverse('encode_muc_subscribe_$events'(Events,
+ [])),
+ _attrs = encode_muc_subscribe_attr_nick(Nick,
+ _xmlns_attrs),
+ {xmlel, <<"subscribe">>, _attrs, _els}.
+
+'encode_muc_subscribe_$events'([], _acc) -> _acc;
+'encode_muc_subscribe_$events'([Events | _els], _acc) ->
+ 'encode_muc_subscribe_$events'(_els,
+ [encode_muc_subscribe_event(Events, [])
+ | _acc]).
+
+decode_muc_subscribe_attr_nick(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"nick">>, <<"subscribe">>,
+ __TopXMLNS}});
+decode_muc_subscribe_attr_nick(__TopXMLNS, _val) ->
+ _val.
+
+encode_muc_subscribe_attr_nick(_val, _acc) ->
+ [{<<"nick">>, _val} | _acc].
+
+decode_muc_subscribe_event(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"event">>, _attrs, _els}) ->
+ Node = decode_muc_subscribe_event_attrs(__TopXMLNS,
+ _attrs, undefined),
+ Node.
+
+decode_muc_subscribe_event_attrs(__TopXMLNS,
+ [{<<"node">>, _val} | _attrs], _Node) ->
+ decode_muc_subscribe_event_attrs(__TopXMLNS, _attrs,
+ _val);
+decode_muc_subscribe_event_attrs(__TopXMLNS,
+ [_ | _attrs], Node) ->
+ decode_muc_subscribe_event_attrs(__TopXMLNS, _attrs,
+ Node);
+decode_muc_subscribe_event_attrs(__TopXMLNS, [],
+ Node) ->
+ decode_muc_subscribe_event_attr_node(__TopXMLNS, Node).
+
+encode_muc_subscribe_event(Node, _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_muc_subscribe_event_attr_node(Node,
+ _xmlns_attrs),
+ {xmlel, <<"event">>, _attrs, _els}.
+
+decode_muc_subscribe_event_attr_node(__TopXMLNS,
+ undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"node">>, <<"event">>, __TopXMLNS}});
+decode_muc_subscribe_event_attr_node(__TopXMLNS,
+ _val) ->
+ _val.
+
+encode_muc_subscribe_event_attr_node(_val, _acc) ->
+ [{<<"node">>, _val} | _acc].
+
+decode_muc_subscriptions(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"subscriptions">>, _attrs, _els}) ->
+ List = decode_muc_subscriptions_els(__TopXMLNS,
+ __IgnoreEls, _els, []),
+ {muc_subscriptions, List}.
+
+decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ [], List) ->
+ lists:reverse(List);
+decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"subscription">>, _attrs, _} = _el
+ | _els],
+ List) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">> when __TopXMLNS == <<"urn:xmpp:mucsub:0">> ->
+ decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ _els,
+ case decode_muc_subscription(__TopXMLNS,
+ __IgnoreEls,
+ _el)
+ of
+ undefined -> List;
+ _new_el -> [_new_el | List]
+ end);
+ <<"urn:xmpp:mucsub:0">> ->
+ decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ _els,
+ case
+ decode_muc_subscription(<<"urn:xmpp:mucsub:0">>,
+ __IgnoreEls,
+ _el)
+ of
+ undefined -> List;
+ _new_el -> [_new_el | List]
+ end);
+ _ ->
+ decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ _els, List)
+ end;
+decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], List) ->
+ decode_muc_subscriptions_els(__TopXMLNS, __IgnoreEls,
+ _els, List).
+
+encode_muc_subscriptions({muc_subscriptions, List},
+ _xmlns_attrs) ->
+ _els =
+ lists:reverse('encode_muc_subscriptions_$list'(List,
+ [])),
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"subscriptions">>, _attrs, _els}.
+
+'encode_muc_subscriptions_$list'([], _acc) -> _acc;
+'encode_muc_subscriptions_$list'([List | _els], _acc) ->
+ 'encode_muc_subscriptions_$list'(_els,
+ [encode_muc_subscription(List, [])
+ | _acc]).
+
+decode_muc_subscription(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"subscription">>, _attrs, _els}) ->
+ Jid = decode_muc_subscription_attrs(__TopXMLNS, _attrs,
+ undefined),
+ Jid.
+
+decode_muc_subscription_attrs(__TopXMLNS,
+ [{<<"jid">>, _val} | _attrs], _Jid) ->
+ decode_muc_subscription_attrs(__TopXMLNS, _attrs, _val);
+decode_muc_subscription_attrs(__TopXMLNS, [_ | _attrs],
+ Jid) ->
+ decode_muc_subscription_attrs(__TopXMLNS, _attrs, Jid);
+decode_muc_subscription_attrs(__TopXMLNS, [], Jid) ->
+ decode_muc_subscription_attr_jid(__TopXMLNS, Jid).
+
+encode_muc_subscription(Jid, _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_muc_subscription_attr_jid(Jid,
+ _xmlns_attrs),
+ {xmlel, <<"subscription">>, _attrs, _els}.
+
+decode_muc_subscription_attr_jid(__TopXMLNS,
+ undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"jid">>, <<"subscription">>,
+ __TopXMLNS}});
+decode_muc_subscription_attr_jid(__TopXMLNS, _val) ->
+ case catch dec_jid(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"jid">>, <<"subscription">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_muc_subscription_attr_jid(_val, _acc) ->
+ [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_x_conference(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"x">>, _attrs, _els}) ->
+ {Jid, Password, Reason, Thread, Continue} =
+ decode_x_conference_attrs(__TopXMLNS, _attrs, undefined,
+ undefined, undefined, undefined, undefined),
+ {x_conference, Jid, Password, Reason, Continue, Thread}.
+
+decode_x_conference_attrs(__TopXMLNS,
+ [{<<"jid">>, _val} | _attrs], _Jid, Password, Reason,
+ Thread, Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, _val,
+ Password, Reason, Thread, Continue);
+decode_x_conference_attrs(__TopXMLNS,
+ [{<<"password">>, _val} | _attrs], Jid, _Password,
+ Reason, Thread, Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, Jid, _val,
+ Reason, Thread, Continue);
+decode_x_conference_attrs(__TopXMLNS,
+ [{<<"reason">>, _val} | _attrs], Jid, Password,
+ _Reason, Thread, Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, Jid,
+ Password, _val, Thread, Continue);
+decode_x_conference_attrs(__TopXMLNS,
+ [{<<"thread">>, _val} | _attrs], Jid, Password,
+ Reason, _Thread, Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, Jid,
+ Password, Reason, _val, Continue);
+decode_x_conference_attrs(__TopXMLNS,
+ [{<<"continue">>, _val} | _attrs], Jid, Password,
+ Reason, Thread, _Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, Jid,
+ Password, Reason, Thread, _val);
+decode_x_conference_attrs(__TopXMLNS, [_ | _attrs], Jid,
+ Password, Reason, Thread, Continue) ->
+ decode_x_conference_attrs(__TopXMLNS, _attrs, Jid,
+ Password, Reason, Thread, Continue);
+decode_x_conference_attrs(__TopXMLNS, [], Jid, Password,
+ Reason, Thread, Continue) ->
+ {decode_x_conference_attr_jid(__TopXMLNS, Jid),
+ decode_x_conference_attr_password(__TopXMLNS, Password),
+ decode_x_conference_attr_reason(__TopXMLNS, Reason),
+ decode_x_conference_attr_thread(__TopXMLNS, Thread),
+ decode_x_conference_attr_continue(__TopXMLNS,
+ Continue)}.
+
+encode_x_conference({x_conference, Jid, Password,
+ Reason, Continue, Thread},
+ _xmlns_attrs) ->
+ _els = [],
+ _attrs = encode_x_conference_attr_continue(Continue,
+ encode_x_conference_attr_thread(Thread,
+ encode_x_conference_attr_reason(Reason,
+ encode_x_conference_attr_password(Password,
+ encode_x_conference_attr_jid(Jid,
+ _xmlns_attrs))))),
+ {xmlel, <<"x">>, _attrs, _els}.
+
+decode_x_conference_attr_jid(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"jid">>, <<"x">>, __TopXMLNS}});
+decode_x_conference_attr_jid(__TopXMLNS, _val) ->
+ case catch dec_jid(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"jid">>, <<"x">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_x_conference_attr_jid(_val, _acc) ->
+ [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_x_conference_attr_password(__TopXMLNS,
+ undefined) ->
+ <<>>;
+decode_x_conference_attr_password(__TopXMLNS, _val) ->
+ _val.
+
+encode_x_conference_attr_password(<<>>, _acc) -> _acc;
+encode_x_conference_attr_password(_val, _acc) ->
+ [{<<"password">>, _val} | _acc].
+
+decode_x_conference_attr_reason(__TopXMLNS,
+ undefined) ->
+ <<>>;
+decode_x_conference_attr_reason(__TopXMLNS, _val) ->
+ _val.
+
+encode_x_conference_attr_reason(<<>>, _acc) -> _acc;
+encode_x_conference_attr_reason(_val, _acc) ->
+ [{<<"reason">>, _val} | _acc].
+
+decode_x_conference_attr_thread(__TopXMLNS,
+ undefined) ->
+ <<>>;
+decode_x_conference_attr_thread(__TopXMLNS, _val) ->
+ _val.
+
+encode_x_conference_attr_thread(<<>>, _acc) -> _acc;
+encode_x_conference_attr_thread(_val, _acc) ->
+ [{<<"thread">>, _val} | _acc].
+
+decode_x_conference_attr_continue(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_x_conference_attr_continue(__TopXMLNS, _val) ->
+ case catch dec_bool(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"continue">>, <<"x">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_x_conference_attr_continue(undefined, _acc) ->
+ _acc;
+encode_x_conference_attr_continue(_val, _acc) ->
+ [{<<"continue">>, enc_bool(_val)} | _acc].
+
+decode_muc_unique(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"unique">>, _attrs, _els}) ->
+ Name = decode_muc_unique_els(__TopXMLNS, __IgnoreEls,
+ _els, <<>>),
+ {muc_unique, Name}.
+
+decode_muc_unique_els(__TopXMLNS, __IgnoreEls, [],
+ Name) ->
+ decode_muc_unique_cdata(__TopXMLNS, Name);
+decode_muc_unique_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Name) ->
+ decode_muc_unique_els(__TopXMLNS, __IgnoreEls, _els,
+ <<Name/binary, _data/binary>>);
+decode_muc_unique_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Name) ->
+ decode_muc_unique_els(__TopXMLNS, __IgnoreEls, _els,
+ Name).
+
+encode_muc_unique({muc_unique, Name}, _xmlns_attrs) ->
+ _els = encode_muc_unique_cdata(Name, []),
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"unique">>, _attrs, _els}.
+
+decode_muc_unique_cdata(__TopXMLNS, <<>>) -> <<>>;
+decode_muc_unique_cdata(__TopXMLNS, _val) -> _val.
+
+encode_muc_unique_cdata(<<>>, _acc) -> _acc;
+encode_muc_unique_cdata(_val, _acc) ->
+ [{xmlcdata, _val} | _acc].
+
decode_muc(__TopXMLNS, __IgnoreEls,
{xmlel, <<"x">>, _attrs, _els}) ->
- History = decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
- undefined),
- Password = decode_muc_attrs(__TopXMLNS, _attrs,
- undefined),
+ {Password, History} = decode_muc_els(__TopXMLNS,
+ __IgnoreEls, _els, undefined,
+ undefined),
{muc, History, Password}.
-decode_muc_els(__TopXMLNS, __IgnoreEls, [], History) ->
- History;
+decode_muc_els(__TopXMLNS, __IgnoreEls, [], Password,
+ History) ->
+ {Password, History};
decode_muc_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"history">>, _attrs, _} = _el | _els],
- History) ->
+ Password, History) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"http://jabber.org/protocol/muc">> ->
- decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els, Password,
decode_muc_history(__TopXMLNS, __IgnoreEls, _el));
<<"http://jabber.org/protocol/muc">> ->
- decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els, Password,
decode_muc_history(<<"http://jabber.org/protocol/muc">>,
__IgnoreEls, _el));
_ ->
- decode_muc_els(__TopXMLNS, __IgnoreEls, _els, History)
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els, Password,
+ History)
+ end;
+decode_muc_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"password">>, _attrs, _} = _el | _els],
+ Password, History) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc">> ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(__TopXMLNS, __IgnoreEls, _el),
+ History);
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el),
+ History);
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el),
+ History);
+ <<"http://jabber.org/protocol/muc">> ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc">>,
+ __IgnoreEls, _el),
+ History);
+ _ ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els, Password,
+ History)
end;
decode_muc_els(__TopXMLNS, __IgnoreEls, [_ | _els],
- History) ->
- decode_muc_els(__TopXMLNS, __IgnoreEls, _els, History).
-
-decode_muc_attrs(__TopXMLNS,
- [{<<"password">>, _val} | _attrs], _Password) ->
- decode_muc_attrs(__TopXMLNS, _attrs, _val);
-decode_muc_attrs(__TopXMLNS, [_ | _attrs], Password) ->
- decode_muc_attrs(__TopXMLNS, _attrs, Password);
-decode_muc_attrs(__TopXMLNS, [], Password) ->
- decode_muc_attr_password(__TopXMLNS, Password).
+ Password, History) ->
+ decode_muc_els(__TopXMLNS, __IgnoreEls, _els, Password,
+ History).
encode_muc({muc, History, Password}, _xmlns_attrs) ->
- _els = lists:reverse('encode_muc_$history'(History,
- [])),
- _attrs = encode_muc_attr_password(Password,
- _xmlns_attrs),
+ _els = lists:reverse('encode_muc_$password'(Password,
+ 'encode_muc_$history'(History,
+ []))),
+ _attrs = _xmlns_attrs,
{xmlel, <<"x">>, _attrs, _els}.
+'encode_muc_$password'(undefined, _acc) -> _acc;
+'encode_muc_$password'(Password, _acc) ->
+ [encode_muc_password(Password, []) | _acc].
+
'encode_muc_$history'(undefined, _acc) -> _acc;
'encode_muc_$history'(History, _acc) ->
[encode_muc_history(History, []) | _acc].
-decode_muc_attr_password(__TopXMLNS, undefined) ->
- undefined;
-decode_muc_attr_password(__TopXMLNS, _val) -> _val.
-
-encode_muc_attr_password(undefined, _acc) -> _acc;
-encode_muc_attr_password(_val, _acc) ->
- [{<<"password">>, _val} | _acc].
-
decode_muc_admin(__TopXMLNS, __IgnoreEls,
{xmlel, <<"query">>, _attrs, _els}) ->
Items = decode_muc_admin_els(__TopXMLNS, __IgnoreEls,
@@ -6593,37 +7557,6 @@ encode_muc_admin({muc_admin, Items}, _xmlns_attrs) ->
'encode_muc_admin_$items'(_els,
[encode_muc_admin_item(Items, []) | _acc]).
-decode_muc_admin_reason(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"reason">>, _attrs, _els}) ->
- Cdata = decode_muc_admin_reason_els(__TopXMLNS,
- __IgnoreEls, _els, <<>>),
- Cdata.
-
-decode_muc_admin_reason_els(__TopXMLNS, __IgnoreEls, [],
- Cdata) ->
- decode_muc_admin_reason_cdata(__TopXMLNS, Cdata);
-decode_muc_admin_reason_els(__TopXMLNS, __IgnoreEls,
- [{xmlcdata, _data} | _els], Cdata) ->
- decode_muc_admin_reason_els(__TopXMLNS, __IgnoreEls,
- _els, <<Cdata/binary, _data/binary>>);
-decode_muc_admin_reason_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Cdata) ->
- decode_muc_admin_reason_els(__TopXMLNS, __IgnoreEls,
- _els, Cdata).
-
-encode_muc_admin_reason(Cdata, _xmlns_attrs) ->
- _els = encode_muc_admin_reason_cdata(Cdata, []),
- _attrs = _xmlns_attrs,
- {xmlel, <<"reason">>, _attrs, _els}.
-
-decode_muc_admin_reason_cdata(__TopXMLNS, <<>>) ->
- undefined;
-decode_muc_admin_reason_cdata(__TopXMLNS, _val) -> _val.
-
-encode_muc_admin_reason_cdata(undefined, _acc) -> _acc;
-encode_muc_admin_reason_cdata(_val, _acc) ->
- [{xmlcdata, _val} | _acc].
-
decode_muc_admin_continue(__TopXMLNS, __IgnoreEls,
{xmlel, <<"continue">>, _attrs, _els}) ->
Thread = decode_muc_admin_continue_attrs(__TopXMLNS,
@@ -6724,7 +7657,7 @@ decode_muc_admin_item(__TopXMLNS, __IgnoreEls,
{xmlel, <<"item">>, _attrs, _els}) ->
{Actor, Continue, Reason} =
decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
- undefined, undefined, undefined),
+ undefined, undefined, <<>>),
{Affiliation, Role, Jid, Nick} =
decode_muc_admin_item_attrs(__TopXMLNS, _attrs,
undefined, undefined, undefined, undefined),
@@ -6785,13 +7718,23 @@ decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls,
<<"http://jabber.org/protocol/muc#admin">> ->
decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue,
- decode_muc_admin_reason(__TopXMLNS,
- __IgnoreEls, _el));
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
<<"http://jabber.org/protocol/muc#admin">> ->
decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue,
- decode_muc_admin_reason(<<"http://jabber.org/protocol/muc#admin">>,
- __IgnoreEls, _el));
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
decode_muc_admin_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue, Reason)
@@ -6857,10 +7800,9 @@ encode_muc_admin_item({muc_item, Actor, Continue,
'encode_muc_admin_item_$continue'(Continue, _acc) ->
[encode_muc_admin_continue(Continue, []) | _acc].
-'encode_muc_admin_item_$reason'(undefined, _acc) ->
- _acc;
+'encode_muc_admin_item_$reason'(<<>>, _acc) -> _acc;
'encode_muc_admin_item_$reason'(Reason, _acc) ->
- [encode_muc_admin_reason(Reason, []) | _acc].
+ [encode_muc_reason(Reason, []) | _acc].
decode_muc_admin_item_attr_affiliation(__TopXMLNS,
undefined) ->
@@ -6926,62 +7868,302 @@ encode_muc_admin_item_attr_nick(undefined, _acc) ->
encode_muc_admin_item_attr_nick(_val, _acc) ->
[{<<"nick">>, _val} | _acc].
+decode_muc_owner_item(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"item">>, _attrs, _els}) ->
+ {Actor, Continue, Reason} =
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ undefined, undefined, <<>>),
+ {Affiliation, Role, Jid, Nick} =
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs,
+ undefined, undefined, undefined, undefined),
+ {muc_item, Actor, Continue, Reason, Affiliation, Role,
+ Jid, Nick}.
+
+decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, [],
+ Actor, Continue, Reason) ->
+ {Actor, Continue, Reason};
+decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"actor">>, _attrs, _} = _el | _els], Actor,
+ Continue, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_admin_actor(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el),
+ Continue, Reason);
+ _ ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue, Reason)
+ end;
+decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"continue">>, _attrs, _} = _el | _els],
+ Actor, Continue, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor,
+ decode_muc_admin_continue(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el),
+ Reason);
+ _ ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue, Reason)
+ end;
+decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"reason">>, _attrs, _} = _el | _els],
+ Actor, Continue, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
+ _ ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue, Reason)
+ end;
+decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Actor, Continue, Reason) ->
+ decode_muc_owner_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue, Reason).
+
+decode_muc_owner_item_attrs(__TopXMLNS,
+ [{<<"affiliation">>, _val} | _attrs], _Affiliation,
+ Role, Jid, Nick) ->
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs, _val,
+ Role, Jid, Nick);
+decode_muc_owner_item_attrs(__TopXMLNS,
+ [{<<"role">>, _val} | _attrs], Affiliation, _Role,
+ Jid, Nick) ->
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs,
+ Affiliation, _val, Jid, Nick);
+decode_muc_owner_item_attrs(__TopXMLNS,
+ [{<<"jid">>, _val} | _attrs], Affiliation, Role,
+ _Jid, Nick) ->
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs,
+ Affiliation, Role, _val, Nick);
+decode_muc_owner_item_attrs(__TopXMLNS,
+ [{<<"nick">>, _val} | _attrs], Affiliation, Role,
+ Jid, _Nick) ->
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs,
+ Affiliation, Role, Jid, _val);
+decode_muc_owner_item_attrs(__TopXMLNS, [_ | _attrs],
+ Affiliation, Role, Jid, Nick) ->
+ decode_muc_owner_item_attrs(__TopXMLNS, _attrs,
+ Affiliation, Role, Jid, Nick);
+decode_muc_owner_item_attrs(__TopXMLNS, [], Affiliation,
+ Role, Jid, Nick) ->
+ {decode_muc_owner_item_attr_affiliation(__TopXMLNS,
+ Affiliation),
+ decode_muc_owner_item_attr_role(__TopXMLNS, Role),
+ decode_muc_owner_item_attr_jid(__TopXMLNS, Jid),
+ decode_muc_owner_item_attr_nick(__TopXMLNS, Nick)}.
+
+encode_muc_owner_item({muc_item, Actor, Continue,
+ Reason, Affiliation, Role, Jid, Nick},
+ _xmlns_attrs) ->
+ _els =
+ lists:reverse('encode_muc_owner_item_$actor'(Actor,
+ 'encode_muc_owner_item_$continue'(Continue,
+ 'encode_muc_owner_item_$reason'(Reason,
+ [])))),
+ _attrs = encode_muc_owner_item_attr_nick(Nick,
+ encode_muc_owner_item_attr_jid(Jid,
+ encode_muc_owner_item_attr_role(Role,
+ encode_muc_owner_item_attr_affiliation(Affiliation,
+ _xmlns_attrs)))),
+ {xmlel, <<"item">>, _attrs, _els}.
+
+'encode_muc_owner_item_$actor'(undefined, _acc) -> _acc;
+'encode_muc_owner_item_$actor'(Actor, _acc) ->
+ [encode_muc_admin_actor(Actor,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/muc#admin">>}])
+ | _acc].
+
+'encode_muc_owner_item_$continue'(undefined, _acc) ->
+ _acc;
+'encode_muc_owner_item_$continue'(Continue, _acc) ->
+ [encode_muc_admin_continue(Continue,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/muc#admin">>}])
+ | _acc].
+
+'encode_muc_owner_item_$reason'(<<>>, _acc) -> _acc;
+'encode_muc_owner_item_$reason'(Reason, _acc) ->
+ [encode_muc_reason(Reason, []) | _acc].
+
+decode_muc_owner_item_attr_affiliation(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_muc_owner_item_attr_affiliation(__TopXMLNS,
+ _val) ->
+ case catch dec_enum(_val,
+ [admin, member, none, outcast, owner])
+ of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"affiliation">>, <<"item">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_muc_owner_item_attr_affiliation(undefined,
+ _acc) ->
+ _acc;
+encode_muc_owner_item_attr_affiliation(_val, _acc) ->
+ [{<<"affiliation">>, enc_enum(_val)} | _acc].
+
+decode_muc_owner_item_attr_role(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_muc_owner_item_attr_role(__TopXMLNS, _val) ->
+ case catch dec_enum(_val,
+ [moderator, none, participant, visitor])
+ of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"role">>, <<"item">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_muc_owner_item_attr_role(undefined, _acc) ->
+ _acc;
+encode_muc_owner_item_attr_role(_val, _acc) ->
+ [{<<"role">>, enc_enum(_val)} | _acc].
+
+decode_muc_owner_item_attr_jid(__TopXMLNS, undefined) ->
+ undefined;
+decode_muc_owner_item_attr_jid(__TopXMLNS, _val) ->
+ case catch dec_jid(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"jid">>, <<"item">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_muc_owner_item_attr_jid(undefined, _acc) -> _acc;
+encode_muc_owner_item_attr_jid(_val, _acc) ->
+ [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_muc_owner_item_attr_nick(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_muc_owner_item_attr_nick(__TopXMLNS, _val) ->
+ _val.
+
+encode_muc_owner_item_attr_nick(undefined, _acc) ->
+ _acc;
+encode_muc_owner_item_attr_nick(_val, _acc) ->
+ [{<<"nick">>, _val} | _acc].
+
decode_muc_owner(__TopXMLNS, __IgnoreEls,
{xmlel, <<"query">>, _attrs, _els}) ->
- {Config, Destroy} = decode_muc_owner_els(__TopXMLNS,
- __IgnoreEls, _els, undefined,
- undefined),
- {muc_owner, Destroy, Config}.
+ {Items, Config, Destroy} =
+ decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els, [],
+ undefined, undefined),
+ {muc_owner, Destroy, Config, Items}.
-decode_muc_owner_els(__TopXMLNS, __IgnoreEls, [],
+decode_muc_owner_els(__TopXMLNS, __IgnoreEls, [], Items,
Config, Destroy) ->
- {Config, Destroy};
+ {lists:reverse(Items), Config, Destroy};
decode_muc_owner_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"destroy">>, _attrs, _} = _el | _els],
+ [{xmlel, <<"destroy">>, _attrs, _} = _el | _els], Items,
Config, Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"http://jabber.org/protocol/muc#owner">> ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
- Config,
- decode_muc_owner_destroy(__TopXMLNS, __IgnoreEls,
- _el));
+ Items, Config,
+ decode_muc_destroy(__TopXMLNS, __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
+ Items, Config,
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
<<"http://jabber.org/protocol/muc#owner">> ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
- Config,
- decode_muc_owner_destroy(<<"http://jabber.org/protocol/muc#owner">>,
- __IgnoreEls, _el));
+ Items, Config,
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
- Config, Destroy)
+ Items, Config, Destroy)
end;
decode_muc_owner_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"x">>, _attrs, _} = _el | _els], Config,
- Destroy) ->
+ [{xmlel, <<"x">>, _attrs, _} = _el | _els], Items,
+ Config, Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"jabber:x:data">> ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
+ Items,
decode_xdata(<<"jabber:x:data">>, __IgnoreEls,
_el),
Destroy);
_ ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
- Config, Destroy)
+ Items, Config, Destroy)
end;
decode_muc_owner_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Config, Destroy) ->
+ [{xmlel, <<"item">>, _attrs, _} = _el | _els], Items,
+ Config, Destroy) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_muc_owner_item(__TopXMLNS, __IgnoreEls,
+ _el)
+ | Items],
+ Config, Destroy);
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
+ [decode_muc_owner_item(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el)
+ | Items],
+ Config, Destroy);
+ _ ->
+ decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
+ Items, Config, Destroy)
+ end;
+decode_muc_owner_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Items, Config, Destroy) ->
decode_muc_owner_els(__TopXMLNS, __IgnoreEls, _els,
- Config, Destroy).
+ Items, Config, Destroy).
-encode_muc_owner({muc_owner, Destroy, Config},
+encode_muc_owner({muc_owner, Destroy, Config, Items},
_xmlns_attrs) ->
- _els = lists:reverse('encode_muc_owner_$config'(Config,
- 'encode_muc_owner_$destroy'(Destroy,
- []))),
+ _els = lists:reverse('encode_muc_owner_$items'(Items,
+ 'encode_muc_owner_$config'(Config,
+ 'encode_muc_owner_$destroy'(Destroy,
+ [])))),
_attrs = _xmlns_attrs,
{xmlel, <<"query">>, _attrs, _els}.
+'encode_muc_owner_$items'([], _acc) -> _acc;
+'encode_muc_owner_$items'([Items | _els], _acc) ->
+ 'encode_muc_owner_$items'(_els,
+ [encode_muc_owner_item(Items, []) | _acc]).
+
'encode_muc_owner_$config'(undefined, _acc) -> _acc;
'encode_muc_owner_$config'(Config, _acc) ->
[encode_xdata(Config,
@@ -6990,242 +8172,142 @@ encode_muc_owner({muc_owner, Destroy, Config},
'encode_muc_owner_$destroy'(undefined, _acc) -> _acc;
'encode_muc_owner_$destroy'(Destroy, _acc) ->
- [encode_muc_owner_destroy(Destroy, []) | _acc].
-
-decode_muc_owner_destroy(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"destroy">>, _attrs, _els}) ->
- {Password, Reason} =
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, undefined, undefined),
- Jid = decode_muc_owner_destroy_attrs(__TopXMLNS, _attrs,
- undefined),
- {muc_owner_destroy, Jid, Reason, Password}.
+ [encode_muc_destroy(Destroy, []) | _acc].
-decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- [], Password, Reason) ->
- {Password, Reason};
-decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"password">>, _attrs, _} = _el | _els],
- Password, Reason) ->
- case get_attr(<<"xmlns">>, _attrs) of
- <<"">>
- when __TopXMLNS ==
- <<"http://jabber.org/protocol/muc#owner">> ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els,
- decode_muc_owner_password(__TopXMLNS,
- __IgnoreEls,
- _el),
- Reason);
- <<"http://jabber.org/protocol/muc#owner">> ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els,
- decode_muc_owner_password(<<"http://jabber.org/protocol/muc#owner">>,
- __IgnoreEls,
- _el),
- Reason);
- _ ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Password, Reason)
- end;
-decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"reason">>, _attrs, _} = _el | _els],
- Password, Reason) ->
- case get_attr(<<"xmlns">>, _attrs) of
- <<"">>
- when __TopXMLNS ==
- <<"http://jabber.org/protocol/muc#owner">> ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Password,
- decode_muc_owner_reason(__TopXMLNS,
- __IgnoreEls,
- _el));
- <<"http://jabber.org/protocol/muc#owner">> ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Password,
- decode_muc_owner_reason(<<"http://jabber.org/protocol/muc#owner">>,
- __IgnoreEls,
- _el));
- _ ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Password, Reason)
- end;
-decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Password, Reason) ->
- decode_muc_owner_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Password, Reason).
-
-decode_muc_owner_destroy_attrs(__TopXMLNS,
- [{<<"jid">>, _val} | _attrs], _Jid) ->
- decode_muc_owner_destroy_attrs(__TopXMLNS, _attrs,
- _val);
-decode_muc_owner_destroy_attrs(__TopXMLNS, [_ | _attrs],
- Jid) ->
- decode_muc_owner_destroy_attrs(__TopXMLNS, _attrs, Jid);
-decode_muc_owner_destroy_attrs(__TopXMLNS, [], Jid) ->
- decode_muc_owner_destroy_attr_jid(__TopXMLNS, Jid).
-
-encode_muc_owner_destroy({muc_owner_destroy, Jid,
- Reason, Password},
- _xmlns_attrs) ->
- _els =
- lists:reverse('encode_muc_owner_destroy_$password'(Password,
- 'encode_muc_owner_destroy_$reason'(Reason,
- []))),
- _attrs = encode_muc_owner_destroy_attr_jid(Jid,
- _xmlns_attrs),
- {xmlel, <<"destroy">>, _attrs, _els}.
-
-'encode_muc_owner_destroy_$password'(undefined, _acc) ->
- _acc;
-'encode_muc_owner_destroy_$password'(Password, _acc) ->
- [encode_muc_owner_password(Password, []) | _acc].
-
-'encode_muc_owner_destroy_$reason'(undefined, _acc) ->
- _acc;
-'encode_muc_owner_destroy_$reason'(Reason, _acc) ->
- [encode_muc_owner_reason(Reason, []) | _acc].
-
-decode_muc_owner_destroy_attr_jid(__TopXMLNS,
- undefined) ->
- undefined;
-decode_muc_owner_destroy_attr_jid(__TopXMLNS, _val) ->
- case catch dec_jid(_val) of
- {'EXIT', _} ->
- erlang:error({xmpp_codec,
- {bad_attr_value, <<"jid">>, <<"destroy">>,
- __TopXMLNS}});
- _res -> _res
- end.
-
-encode_muc_owner_destroy_attr_jid(undefined, _acc) ->
- _acc;
-encode_muc_owner_destroy_attr_jid(_val, _acc) ->
- [{<<"jid">>, enc_jid(_val)} | _acc].
-
-decode_muc_owner_reason(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"reason">>, _attrs, _els}) ->
- Cdata = decode_muc_owner_reason_els(__TopXMLNS,
- __IgnoreEls, _els, <<>>),
- Cdata.
-
-decode_muc_owner_reason_els(__TopXMLNS, __IgnoreEls, [],
- Cdata) ->
- decode_muc_owner_reason_cdata(__TopXMLNS, Cdata);
-decode_muc_owner_reason_els(__TopXMLNS, __IgnoreEls,
- [{xmlcdata, _data} | _els], Cdata) ->
- decode_muc_owner_reason_els(__TopXMLNS, __IgnoreEls,
- _els, <<Cdata/binary, _data/binary>>);
-decode_muc_owner_reason_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Cdata) ->
- decode_muc_owner_reason_els(__TopXMLNS, __IgnoreEls,
- _els, Cdata).
-
-encode_muc_owner_reason(Cdata, _xmlns_attrs) ->
- _els = encode_muc_owner_reason_cdata(Cdata, []),
- _attrs = _xmlns_attrs,
- {xmlel, <<"reason">>, _attrs, _els}.
-
-decode_muc_owner_reason_cdata(__TopXMLNS, <<>>) ->
- undefined;
-decode_muc_owner_reason_cdata(__TopXMLNS, _val) -> _val.
-
-encode_muc_owner_reason_cdata(undefined, _acc) -> _acc;
-encode_muc_owner_reason_cdata(_val, _acc) ->
- [{xmlcdata, _val} | _acc].
-
-decode_muc_owner_password(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"password">>, _attrs, _els}) ->
- Cdata = decode_muc_owner_password_els(__TopXMLNS,
- __IgnoreEls, _els, <<>>),
+decode_muc_password(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"password">>, _attrs, _els}) ->
+ Cdata = decode_muc_password_els(__TopXMLNS, __IgnoreEls,
+ _els, <<>>),
Cdata.
-decode_muc_owner_password_els(__TopXMLNS, __IgnoreEls,
- [], Cdata) ->
- decode_muc_owner_password_cdata(__TopXMLNS, Cdata);
-decode_muc_owner_password_els(__TopXMLNS, __IgnoreEls,
- [{xmlcdata, _data} | _els], Cdata) ->
- decode_muc_owner_password_els(__TopXMLNS, __IgnoreEls,
- _els, <<Cdata/binary, _data/binary>>);
-decode_muc_owner_password_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Cdata) ->
- decode_muc_owner_password_els(__TopXMLNS, __IgnoreEls,
- _els, Cdata).
+decode_muc_password_els(__TopXMLNS, __IgnoreEls, [],
+ Cdata) ->
+ decode_muc_password_cdata(__TopXMLNS, Cdata);
+decode_muc_password_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Cdata) ->
+ decode_muc_password_els(__TopXMLNS, __IgnoreEls, _els,
+ <<Cdata/binary, _data/binary>>);
+decode_muc_password_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Cdata) ->
+ decode_muc_password_els(__TopXMLNS, __IgnoreEls, _els,
+ Cdata).
-encode_muc_owner_password(Cdata, _xmlns_attrs) ->
- _els = encode_muc_owner_password_cdata(Cdata, []),
+encode_muc_password(Cdata, _xmlns_attrs) ->
+ _els = encode_muc_password_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"password">>, _attrs, _els}.
-decode_muc_owner_password_cdata(__TopXMLNS, <<>>) ->
+decode_muc_password_cdata(__TopXMLNS, <<>>) ->
undefined;
-decode_muc_owner_password_cdata(__TopXMLNS, _val) ->
- _val.
+decode_muc_password_cdata(__TopXMLNS, _val) -> _val.
-encode_muc_owner_password_cdata(undefined, _acc) ->
- _acc;
-encode_muc_owner_password_cdata(_val, _acc) ->
+encode_muc_password_cdata(undefined, _acc) -> _acc;
+encode_muc_password_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_muc_user(__TopXMLNS, __IgnoreEls,
{xmlel, <<"x">>, _attrs, _els}) ->
- {Status_codes, Items, Invites, Decline, Destroy} =
+ {Status_codes, Items, Invites, Password, Decline,
+ Destroy} =
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els, [],
- [], [], undefined, undefined),
- Password = decode_muc_user_attrs(__TopXMLNS, _attrs,
- undefined),
+ [], [], undefined, undefined, undefined),
{muc_user, Decline, Destroy, Invites, Items,
Status_codes, Password}.
decode_muc_user_els(__TopXMLNS, __IgnoreEls, [],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
{lists:reverse(Status_codes), lists:reverse(Items),
- lists:reverse(Invites), Decline, Destroy};
+ lists:reverse(Invites), Password, Decline, Destroy};
decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"decline">>, _attrs, _} = _el | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites,
+ Status_codes, Items, Invites, Password,
decode_muc_user_decline(__TopXMLNS, __IgnoreEls,
_el),
Destroy);
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites,
+ Status_codes, Items, Invites, Password,
decode_muc_user_decline(<<"http://jabber.org/protocol/muc#user">>,
__IgnoreEls, _el),
Destroy);
_ ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy)
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
end;
decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"destroy">>, _attrs, _} = _el | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline,
- decode_muc_user_destroy(__TopXMLNS, __IgnoreEls,
- _el));
+ Status_codes, Items, Invites, Password, Decline,
+ decode_muc_destroy(__TopXMLNS, __IgnoreEls, _el));
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline,
- decode_muc_user_destroy(<<"http://jabber.org/protocol/muc#user">>,
- __IgnoreEls, _el));
+ Status_codes, Items, Invites, Password, Decline,
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites, Password, Decline,
+ decode_muc_destroy(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy)
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
+ end;
+decode_muc_user_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"password">>, _attrs, _} = _el | _els],
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites,
+ decode_muc_password(__TopXMLNS, __IgnoreEls, _el),
+ Decline, Destroy);
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el),
+ Decline, Destroy);
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el),
+ Decline, Destroy);
+ <<"http://jabber.org/protocol/muc">> ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites,
+ decode_muc_password(<<"http://jabber.org/protocol/muc">>,
+ __IgnoreEls, _el),
+ Decline, Destroy);
+ _ ->
+ decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
end;
decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"invite">>, _attrs, _} = _el | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
@@ -7235,21 +8317,23 @@ decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[decode_muc_user_invite(__TopXMLNS, __IgnoreEls,
_el)
| Invites],
- Decline, Destroy);
+ Password, Decline, Destroy);
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
Status_codes, Items,
[decode_muc_user_invite(<<"http://jabber.org/protocol/muc#user">>,
__IgnoreEls, _el)
| Invites],
- Decline, Destroy);
+ Password, Decline, Destroy);
_ ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy)
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
end;
decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"item">>, _attrs, _} = _el | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
@@ -7259,21 +8343,23 @@ decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[decode_muc_user_item(__TopXMLNS, __IgnoreEls,
_el)
| Items],
- Invites, Decline, Destroy);
+ Invites, Password, Decline, Destroy);
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
Status_codes,
[decode_muc_user_item(<<"http://jabber.org/protocol/muc#user">>,
__IgnoreEls, _el)
| Items],
- Invites, Decline, Destroy);
+ Invites, Password, Decline, Destroy);
_ ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy)
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
end;
decode_muc_user_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"status">>, _attrs, _} = _el | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
@@ -7285,7 +8371,7 @@ decode_muc_user_els(__TopXMLNS, __IgnoreEls,
undefined -> Status_codes;
_new_el -> [_new_el | Status_codes]
end,
- Items, Invites, Decline, Destroy);
+ Items, Invites, Password, Decline, Destroy);
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
case
@@ -7295,24 +8381,18 @@ decode_muc_user_els(__TopXMLNS, __IgnoreEls,
undefined -> Status_codes;
_new_el -> [_new_el | Status_codes]
end,
- Items, Invites, Decline, Destroy);
+ Items, Invites, Password, Decline, Destroy);
_ ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy)
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy)
end;
decode_muc_user_els(__TopXMLNS, __IgnoreEls, [_ | _els],
- Status_codes, Items, Invites, Decline, Destroy) ->
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy) ->
decode_muc_user_els(__TopXMLNS, __IgnoreEls, _els,
- Status_codes, Items, Invites, Decline, Destroy).
-
-decode_muc_user_attrs(__TopXMLNS,
- [{<<"password">>, _val} | _attrs], _Password) ->
- decode_muc_user_attrs(__TopXMLNS, _attrs, _val);
-decode_muc_user_attrs(__TopXMLNS, [_ | _attrs],
- Password) ->
- decode_muc_user_attrs(__TopXMLNS, _attrs, Password);
-decode_muc_user_attrs(__TopXMLNS, [], Password) ->
- decode_muc_user_attr_password(__TopXMLNS, Password).
+ Status_codes, Items, Invites, Password, Decline,
+ Destroy).
encode_muc_user({muc_user, Decline, Destroy, Invites,
Items, Status_codes, Password},
@@ -7321,11 +8401,11 @@ encode_muc_user({muc_user, Decline, Destroy, Invites,
lists:reverse('encode_muc_user_$status_codes'(Status_codes,
'encode_muc_user_$items'(Items,
'encode_muc_user_$invites'(Invites,
- 'encode_muc_user_$decline'(Decline,
- 'encode_muc_user_$destroy'(Destroy,
- [])))))),
- _attrs = encode_muc_user_attr_password(Password,
- _xmlns_attrs),
+ 'encode_muc_user_$password'(Password,
+ 'encode_muc_user_$decline'(Decline,
+ 'encode_muc_user_$destroy'(Destroy,
+ []))))))),
+ _attrs = _xmlns_attrs,
{xmlel, <<"x">>, _attrs, _els}.
'encode_muc_user_$status_codes'([], _acc) -> _acc;
@@ -7345,27 +8425,23 @@ encode_muc_user({muc_user, Decline, Destroy, Invites,
'encode_muc_user_$invites'(_els,
[encode_muc_user_invite(Invites, []) | _acc]).
+'encode_muc_user_$password'(undefined, _acc) -> _acc;
+'encode_muc_user_$password'(Password, _acc) ->
+ [encode_muc_password(Password, []) | _acc].
+
'encode_muc_user_$decline'(undefined, _acc) -> _acc;
'encode_muc_user_$decline'(Decline, _acc) ->
[encode_muc_user_decline(Decline, []) | _acc].
'encode_muc_user_$destroy'(undefined, _acc) -> _acc;
'encode_muc_user_$destroy'(Destroy, _acc) ->
- [encode_muc_user_destroy(Destroy, []) | _acc].
-
-decode_muc_user_attr_password(__TopXMLNS, undefined) ->
- undefined;
-decode_muc_user_attr_password(__TopXMLNS, _val) -> _val.
-
-encode_muc_user_attr_password(undefined, _acc) -> _acc;
-encode_muc_user_attr_password(_val, _acc) ->
- [{<<"password">>, _val} | _acc].
+ [encode_muc_destroy(Destroy, []) | _acc].
decode_muc_user_item(__TopXMLNS, __IgnoreEls,
{xmlel, <<"item">>, _attrs, _els}) ->
{Actor, Continue, Reason} =
decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
- undefined, undefined, undefined),
+ undefined, undefined, <<>>),
{Affiliation, Role, Jid, Nick} =
decode_muc_user_item_attrs(__TopXMLNS, _attrs,
undefined, undefined, undefined, undefined),
@@ -7426,13 +8502,23 @@ decode_muc_user_item_els(__TopXMLNS, __IgnoreEls,
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue,
- decode_muc_user_reason(__TopXMLNS,
- __IgnoreEls, _el));
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue,
- decode_muc_user_reason(<<"http://jabber.org/protocol/muc#user">>,
- __IgnoreEls, _el));
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
+ Actor, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
decode_muc_user_item_els(__TopXMLNS, __IgnoreEls, _els,
Actor, Continue, Reason)
@@ -7498,9 +8584,9 @@ encode_muc_user_item({muc_item, Actor, Continue, Reason,
'encode_muc_user_item_$continue'(Continue, _acc) ->
[encode_muc_user_continue(Continue, []) | _acc].
-'encode_muc_user_item_$reason'(undefined, _acc) -> _acc;
+'encode_muc_user_item_$reason'(<<>>, _acc) -> _acc;
'encode_muc_user_item_$reason'(Reason, _acc) ->
- [encode_muc_user_reason(Reason, []) | _acc].
+ [encode_muc_reason(Reason, []) | _acc].
decode_muc_user_item_attr_affiliation(__TopXMLNS,
undefined) ->
@@ -7695,39 +8781,72 @@ encode_muc_user_actor_attr_nick(_val, _acc) ->
decode_muc_user_invite(__TopXMLNS, __IgnoreEls,
{xmlel, <<"invite">>, _attrs, _els}) ->
- Reason = decode_muc_user_invite_els(__TopXMLNS,
- __IgnoreEls, _els, undefined),
+ {Continue, Reason} =
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, undefined, <<>>),
{To, From} = decode_muc_user_invite_attrs(__TopXMLNS,
_attrs, undefined, undefined),
- {muc_invite, Reason, From, To}.
+ {muc_invite, Reason, From, To, Continue}.
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls, [],
- Reason) ->
- Reason;
+ Continue, Reason) ->
+ {Continue, Reason};
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"reason">>, _attrs, _} = _el | _els],
- Reason) ->
+ Continue, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, Continue,
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, Continue,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
+ _ ->
+ decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ _els, Continue, Reason)
+ end;
+decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"continue">>, _attrs, _} = _el | _els],
+ Continue, Reason) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
_els,
- decode_muc_user_reason(__TopXMLNS,
- __IgnoreEls, _el));
+ decode_muc_user_continue(__TopXMLNS,
+ __IgnoreEls, _el),
+ Reason);
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
_els,
- decode_muc_user_reason(<<"http://jabber.org/protocol/muc#user">>,
- __IgnoreEls, _el));
+ decode_muc_user_continue(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el),
+ Reason);
_ ->
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
- _els, Reason)
+ _els, Continue, Reason)
end;
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Reason) ->
+ [_ | _els], Continue, Reason) ->
decode_muc_user_invite_els(__TopXMLNS, __IgnoreEls,
- _els, Reason).
+ _els, Continue, Reason).
decode_muc_user_invite_attrs(__TopXMLNS,
[{<<"to">>, _val} | _attrs], _To, From) ->
@@ -7746,20 +8865,26 @@ decode_muc_user_invite_attrs(__TopXMLNS, [], To,
{decode_muc_user_invite_attr_to(__TopXMLNS, To),
decode_muc_user_invite_attr_from(__TopXMLNS, From)}.
-encode_muc_user_invite({muc_invite, Reason, From, To},
+encode_muc_user_invite({muc_invite, Reason, From, To,
+ Continue},
_xmlns_attrs) ->
_els =
- lists:reverse('encode_muc_user_invite_$reason'(Reason,
- [])),
+ lists:reverse('encode_muc_user_invite_$continue'(Continue,
+ 'encode_muc_user_invite_$reason'(Reason,
+ []))),
_attrs = encode_muc_user_invite_attr_from(From,
encode_muc_user_invite_attr_to(To,
_xmlns_attrs)),
{xmlel, <<"invite">>, _attrs, _els}.
-'encode_muc_user_invite_$reason'(undefined, _acc) ->
+'encode_muc_user_invite_$continue'(undefined, _acc) ->
_acc;
+'encode_muc_user_invite_$continue'(Continue, _acc) ->
+ [encode_muc_user_continue(Continue, []) | _acc].
+
+'encode_muc_user_invite_$reason'(<<>>, _acc) -> _acc;
'encode_muc_user_invite_$reason'(Reason, _acc) ->
- [encode_muc_user_reason(Reason, []) | _acc].
+ [encode_muc_reason(Reason, []) | _acc].
decode_muc_user_invite_attr_to(__TopXMLNS, undefined) ->
undefined;
@@ -7792,69 +8917,125 @@ encode_muc_user_invite_attr_from(undefined, _acc) ->
encode_muc_user_invite_attr_from(_val, _acc) ->
[{<<"from">>, enc_jid(_val)} | _acc].
-decode_muc_user_destroy(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"destroy">>, _attrs, _els}) ->
- Reason = decode_muc_user_destroy_els(__TopXMLNS,
- __IgnoreEls, _els, undefined),
- Jid = decode_muc_user_destroy_attrs(__TopXMLNS, _attrs,
- undefined),
- {muc_user_destroy, Reason, Jid}.
+decode_muc_destroy(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"destroy">>, _attrs, _els}) ->
+ {Password, Reason} = decode_muc_destroy_els(__TopXMLNS,
+ __IgnoreEls, _els, undefined,
+ <<>>),
+ {Jid, Xmlns} = decode_muc_destroy_attrs(__TopXMLNS,
+ _attrs, undefined, undefined),
+ {muc_destroy, Xmlns, Jid, Reason, Password}.
-decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls, [],
- Reason) ->
- Reason;
-decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"reason">>, _attrs, _} = _el | _els],
- Reason) ->
+decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, [],
+ Password, Reason) ->
+ {Password, Reason};
+decode_muc_destroy_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"reason">>, _attrs, _} = _el | _els],
+ Password, Reason) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
- <<"http://jabber.org/protocol/muc#user">> ->
- decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- _els,
- decode_muc_user_reason(__TopXMLNS,
- __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#user">>;
+ __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password,
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
<<"http://jabber.org/protocol/muc#user">> ->
- decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- _els,
- decode_muc_user_reason(<<"http://jabber.org/protocol/muc#user">>,
- __IgnoreEls, _el));
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
- decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Reason)
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password, Reason)
end;
-decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Reason) ->
- decode_muc_user_destroy_els(__TopXMLNS, __IgnoreEls,
- _els, Reason).
-
-decode_muc_user_destroy_attrs(__TopXMLNS,
- [{<<"jid">>, _val} | _attrs], _Jid) ->
- decode_muc_user_destroy_attrs(__TopXMLNS, _attrs, _val);
-decode_muc_user_destroy_attrs(__TopXMLNS, [_ | _attrs],
- Jid) ->
- decode_muc_user_destroy_attrs(__TopXMLNS, _attrs, Jid);
-decode_muc_user_destroy_attrs(__TopXMLNS, [], Jid) ->
- decode_muc_user_destroy_attr_jid(__TopXMLNS, Jid).
-
-encode_muc_user_destroy({muc_user_destroy, Reason, Jid},
- _xmlns_attrs) ->
+decode_muc_destroy_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"password">>, _attrs, _} = _el | _els],
+ Password, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#user">>;
+ __TopXMLNS ==
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(__TopXMLNS, __IgnoreEls,
+ _el),
+ Reason);
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el),
+ Reason);
+ <<"http://jabber.org/protocol/muc#user">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el),
+ Reason);
+ <<"http://jabber.org/protocol/muc">> ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_muc_password(<<"http://jabber.org/protocol/muc">>,
+ __IgnoreEls, _el),
+ Reason);
+ _ ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password, Reason)
+ end;
+decode_muc_destroy_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Password, Reason) ->
+ decode_muc_destroy_els(__TopXMLNS, __IgnoreEls, _els,
+ Password, Reason).
+
+decode_muc_destroy_attrs(__TopXMLNS,
+ [{<<"jid">>, _val} | _attrs], _Jid, Xmlns) ->
+ decode_muc_destroy_attrs(__TopXMLNS, _attrs, _val,
+ Xmlns);
+decode_muc_destroy_attrs(__TopXMLNS,
+ [{<<"xmlns">>, _val} | _attrs], Jid, _Xmlns) ->
+ decode_muc_destroy_attrs(__TopXMLNS, _attrs, Jid, _val);
+decode_muc_destroy_attrs(__TopXMLNS, [_ | _attrs], Jid,
+ Xmlns) ->
+ decode_muc_destroy_attrs(__TopXMLNS, _attrs, Jid,
+ Xmlns);
+decode_muc_destroy_attrs(__TopXMLNS, [], Jid, Xmlns) ->
+ {decode_muc_destroy_attr_jid(__TopXMLNS, Jid),
+ decode_muc_destroy_attr_xmlns(__TopXMLNS, Xmlns)}.
+
+encode_muc_destroy({muc_destroy, Xmlns, Jid, Reason,
+ Password},
+ _xmlns_attrs) ->
_els =
- lists:reverse('encode_muc_user_destroy_$reason'(Reason,
- [])),
- _attrs = encode_muc_user_destroy_attr_jid(Jid,
- _xmlns_attrs),
+ lists:reverse('encode_muc_destroy_$password'(Password,
+ 'encode_muc_destroy_$reason'(Reason,
+ []))),
+ _attrs = encode_muc_destroy_attr_xmlns(Xmlns,
+ encode_muc_destroy_attr_jid(Jid,
+ _xmlns_attrs)),
{xmlel, <<"destroy">>, _attrs, _els}.
-'encode_muc_user_destroy_$reason'(undefined, _acc) ->
- _acc;
-'encode_muc_user_destroy_$reason'(Reason, _acc) ->
- [encode_muc_user_reason(Reason, []) | _acc].
+'encode_muc_destroy_$password'(undefined, _acc) -> _acc;
+'encode_muc_destroy_$password'(Password, _acc) ->
+ [encode_muc_password(Password, []) | _acc].
-decode_muc_user_destroy_attr_jid(__TopXMLNS,
- undefined) ->
+'encode_muc_destroy_$reason'(<<>>, _acc) -> _acc;
+'encode_muc_destroy_$reason'(Reason, _acc) ->
+ [encode_muc_reason(Reason, []) | _acc].
+
+decode_muc_destroy_attr_jid(__TopXMLNS, undefined) ->
undefined;
-decode_muc_user_destroy_attr_jid(__TopXMLNS, _val) ->
+decode_muc_destroy_attr_jid(__TopXMLNS, _val) ->
case catch dec_jid(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
@@ -7863,15 +9044,22 @@ decode_muc_user_destroy_attr_jid(__TopXMLNS, _val) ->
_res -> _res
end.
-encode_muc_user_destroy_attr_jid(undefined, _acc) ->
- _acc;
-encode_muc_user_destroy_attr_jid(_val, _acc) ->
+encode_muc_destroy_attr_jid(undefined, _acc) -> _acc;
+encode_muc_destroy_attr_jid(_val, _acc) ->
[{<<"jid">>, enc_jid(_val)} | _acc].
+decode_muc_destroy_attr_xmlns(__TopXMLNS, undefined) ->
+ undefined;
+decode_muc_destroy_attr_xmlns(__TopXMLNS, _val) -> _val.
+
+encode_muc_destroy_attr_xmlns(undefined, _acc) -> _acc;
+encode_muc_destroy_attr_xmlns(_val, _acc) ->
+ [{<<"xmlns">>, _val} | _acc].
+
decode_muc_user_decline(__TopXMLNS, __IgnoreEls,
{xmlel, <<"decline">>, _attrs, _els}) ->
Reason = decode_muc_user_decline_els(__TopXMLNS,
- __IgnoreEls, _els, undefined),
+ __IgnoreEls, _els, <<>>),
{To, From} = decode_muc_user_decline_attrs(__TopXMLNS,
_attrs, undefined, undefined),
{muc_decline, Reason, From, To}.
@@ -7888,13 +9076,23 @@ decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
_els,
- decode_muc_user_reason(__TopXMLNS,
- __IgnoreEls, _el));
+ decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ _el));
<<"http://jabber.org/protocol/muc#user">> ->
decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
_els,
- decode_muc_user_reason(<<"http://jabber.org/protocol/muc#user">>,
- __IgnoreEls, _el));
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#user">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#admin">> ->
+ decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
+ _els,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#admin">>,
+ __IgnoreEls, _el));
+ <<"http://jabber.org/protocol/muc#owner">> ->
+ decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
+ _els,
+ decode_muc_reason(<<"http://jabber.org/protocol/muc#owner">>,
+ __IgnoreEls, _el));
_ ->
decode_muc_user_decline_els(__TopXMLNS, __IgnoreEls,
_els, Reason)
@@ -7931,10 +9129,9 @@ encode_muc_user_decline({muc_decline, Reason, From, To},
_xmlns_attrs)),
{xmlel, <<"decline">>, _attrs, _els}.
-'encode_muc_user_decline_$reason'(undefined, _acc) ->
- _acc;
+'encode_muc_user_decline_$reason'(<<>>, _acc) -> _acc;
'encode_muc_user_decline_$reason'(Reason, _acc) ->
- [encode_muc_user_reason(Reason, []) | _acc].
+ [encode_muc_reason(Reason, []) | _acc].
decode_muc_user_decline_attr_to(__TopXMLNS,
undefined) ->
@@ -7969,35 +9166,34 @@ encode_muc_user_decline_attr_from(undefined, _acc) ->
encode_muc_user_decline_attr_from(_val, _acc) ->
[{<<"from">>, enc_jid(_val)} | _acc].
-decode_muc_user_reason(__TopXMLNS, __IgnoreEls,
- {xmlel, <<"reason">>, _attrs, _els}) ->
- Cdata = decode_muc_user_reason_els(__TopXMLNS,
- __IgnoreEls, _els, <<>>),
+decode_muc_reason(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"reason">>, _attrs, _els}) ->
+ Cdata = decode_muc_reason_els(__TopXMLNS, __IgnoreEls,
+ _els, <<>>),
Cdata.
-decode_muc_user_reason_els(__TopXMLNS, __IgnoreEls, [],
- Cdata) ->
- decode_muc_user_reason_cdata(__TopXMLNS, Cdata);
-decode_muc_user_reason_els(__TopXMLNS, __IgnoreEls,
- [{xmlcdata, _data} | _els], Cdata) ->
- decode_muc_user_reason_els(__TopXMLNS, __IgnoreEls,
- _els, <<Cdata/binary, _data/binary>>);
-decode_muc_user_reason_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Cdata) ->
- decode_muc_user_reason_els(__TopXMLNS, __IgnoreEls,
- _els, Cdata).
+decode_muc_reason_els(__TopXMLNS, __IgnoreEls, [],
+ Cdata) ->
+ decode_muc_reason_cdata(__TopXMLNS, Cdata);
+decode_muc_reason_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Cdata) ->
+ decode_muc_reason_els(__TopXMLNS, __IgnoreEls, _els,
+ <<Cdata/binary, _data/binary>>);
+decode_muc_reason_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Cdata) ->
+ decode_muc_reason_els(__TopXMLNS, __IgnoreEls, _els,
+ Cdata).
-encode_muc_user_reason(Cdata, _xmlns_attrs) ->
- _els = encode_muc_user_reason_cdata(Cdata, []),
+encode_muc_reason(Cdata, _xmlns_attrs) ->
+ _els = encode_muc_reason_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"reason">>, _attrs, _els}.
-decode_muc_user_reason_cdata(__TopXMLNS, <<>>) ->
- undefined;
-decode_muc_user_reason_cdata(__TopXMLNS, _val) -> _val.
+decode_muc_reason_cdata(__TopXMLNS, <<>>) -> undefined;
+decode_muc_reason_cdata(__TopXMLNS, _val) -> _val.
-encode_muc_user_reason_cdata(undefined, _acc) -> _acc;
-encode_muc_user_reason_cdata(_val, _acc) ->
+encode_muc_reason_cdata(undefined, _acc) -> _acc;
+encode_muc_reason_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_muc_history(__TopXMLNS, __IgnoreEls,
@@ -10554,23 +11750,15 @@ decode_xdata_field_els(__TopXMLNS, __IgnoreEls,
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:data">> ->
decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els,
- case decode_xdata_field_option(__TopXMLNS,
- __IgnoreEls,
- _el)
- of
- undefined -> Options;
- _new_el -> [_new_el | Options]
- end,
+ [decode_xdata_field_option(__TopXMLNS,
+ __IgnoreEls, _el)
+ | Options],
Values, Desc, Required);
<<"jabber:x:data">> ->
decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els,
- case
- decode_xdata_field_option(<<"jabber:x:data">>,
- __IgnoreEls, _el)
- of
- undefined -> Options;
- _new_el -> [_new_el | Options]
- end,
+ [decode_xdata_field_option(<<"jabber:x:data">>,
+ __IgnoreEls, _el)
+ | Options],
Values, Desc, Required);
_ ->
decode_xdata_field_els(__TopXMLNS, __IgnoreEls, _els,
@@ -10675,7 +11863,9 @@ decode_xdata_field_option(__TopXMLNS, __IgnoreEls,
{xmlel, <<"option">>, _attrs, _els}) ->
Value = decode_xdata_field_option_els(__TopXMLNS,
__IgnoreEls, _els, error),
- Value.
+ Label = decode_xdata_field_option_attrs(__TopXMLNS,
+ _attrs, undefined),
+ {xdata_option, Label, Value}.
decode_xdata_field_option_els(__TopXMLNS, __IgnoreEls,
[], Value) ->
@@ -10712,16 +11902,42 @@ decode_xdata_field_option_els(__TopXMLNS, __IgnoreEls,
decode_xdata_field_option_els(__TopXMLNS, __IgnoreEls,
_els, Value).
-encode_xdata_field_option(Value, _xmlns_attrs) ->
+decode_xdata_field_option_attrs(__TopXMLNS,
+ [{<<"label">>, _val} | _attrs], _Label) ->
+ decode_xdata_field_option_attrs(__TopXMLNS, _attrs,
+ _val);
+decode_xdata_field_option_attrs(__TopXMLNS,
+ [_ | _attrs], Label) ->
+ decode_xdata_field_option_attrs(__TopXMLNS, _attrs,
+ Label);
+decode_xdata_field_option_attrs(__TopXMLNS, [],
+ Label) ->
+ decode_xdata_field_option_attr_label(__TopXMLNS, Label).
+
+encode_xdata_field_option({xdata_option, Label, Value},
+ _xmlns_attrs) ->
_els =
lists:reverse('encode_xdata_field_option_$value'(Value,
[])),
- _attrs = _xmlns_attrs,
+ _attrs = encode_xdata_field_option_attr_label(Label,
+ _xmlns_attrs),
{xmlel, <<"option">>, _attrs, _els}.
'encode_xdata_field_option_$value'(Value, _acc) ->
[encode_xdata_field_value(Value, []) | _acc].
+decode_xdata_field_option_attr_label(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_xdata_field_option_attr_label(__TopXMLNS,
+ _val) ->
+ _val.
+
+encode_xdata_field_option_attr_label(undefined, _acc) ->
+ _acc;
+encode_xdata_field_option_attr_label(_val, _acc) ->
+ [{<<"label">>, _val} | _acc].
+
decode_xdata_field_value(__TopXMLNS, __IgnoreEls,
{xmlel, <<"value">>, _attrs, _els}) ->
Cdata = decode_xdata_field_value_els(__TopXMLNS,
@@ -19882,6 +21098,19 @@ decode_error_els(__TopXMLNS, __IgnoreEls,
Reason)
end;
decode_error_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"payment-required">>, _attrs, _} = _el
+ | _els],
+ Text, Reason) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"urn:ietf:params:xml:ns:xmpp-stanzas">> ->
+ decode_error_els(__TopXMLNS, __IgnoreEls, _els, Text,
+ decode_error_payment_required(<<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
+ __IgnoreEls, _el));
+ _ ->
+ decode_error_els(__TopXMLNS, __IgnoreEls, _els, Text,
+ Reason)
+ end;
+decode_error_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"policy-violation">>, _attrs, _} = _el
| _els],
Text, Reason) ->
@@ -20132,6 +21361,12 @@ encode_error({error, Type, Code, By, Reason, Text},
[{<<"xmlns">>,
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>}])
| _acc];
+'encode_error_$reason'('payment-required' = Reason,
+ _acc) ->
+ [encode_error_payment_required(Reason,
+ [{<<"xmlns">>,
+ <<"urn:ietf:params:xml:ns:xmpp-stanzas">>}])
+ | _acc];
'encode_error_$reason'('policy-violation' = Reason,
_acc) ->
[encode_error_policy_violation(Reason,
@@ -20438,6 +21673,16 @@ encode_error_policy_violation('policy-violation',
_attrs = _xmlns_attrs,
{xmlel, <<"policy-violation">>, _attrs, _els}.
+decode_error_payment_required(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"payment-required">>, _attrs, _els}) ->
+ 'payment-required'.
+
+encode_error_payment_required('payment-required',
+ _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"payment-required">>, _attrs, _els}.
+
decode_error_not_authorized(__TopXMLNS, __IgnoreEls,
{xmlel, <<"not-authorized">>, _attrs, _els}) ->
'not-authorized'.
@@ -22069,17 +23314,18 @@ encode_private({private, __Xmls}, _xmlns_attrs) ->
decode_disco_items(__TopXMLNS, __IgnoreEls,
{xmlel, <<"query">>, _attrs, _els}) ->
- Items = decode_disco_items_els(__TopXMLNS, __IgnoreEls,
- _els, []),
+ {Items, Rsm} = decode_disco_items_els(__TopXMLNS,
+ __IgnoreEls, _els, [], undefined),
Node = decode_disco_items_attrs(__TopXMLNS, _attrs,
undefined),
- {disco_items, Node, Items}.
+ {disco_items, Node, Items, Rsm}.
decode_disco_items_els(__TopXMLNS, __IgnoreEls, [],
- Items) ->
- lists:reverse(Items);
+ Items, Rsm) ->
+ {lists:reverse(Items), Rsm};
decode_disco_items_els(__TopXMLNS, __IgnoreEls,
- [{xmlel, <<"item">>, _attrs, _} = _el | _els], Items) ->
+ [{xmlel, <<"item">>, _attrs, _} = _el | _els], Items,
+ Rsm) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
@@ -22087,20 +23333,35 @@ decode_disco_items_els(__TopXMLNS, __IgnoreEls,
decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
[decode_disco_item(__TopXMLNS, __IgnoreEls,
_el)
- | Items]);
+ | Items],
+ Rsm);
<<"http://jabber.org/protocol/disco#items">> ->
decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
[decode_disco_item(<<"http://jabber.org/protocol/disco#items">>,
__IgnoreEls, _el)
- | Items]);
+ | Items],
+ Rsm);
+ _ ->
+ decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
+ Items, Rsm)
+ end;
+decode_disco_items_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"set">>, _attrs, _} = _el | _els], Items,
+ Rsm) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"http://jabber.org/protocol/rsm">> ->
+ decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
+ Items,
+ decode_rsm_set(<<"http://jabber.org/protocol/rsm">>,
+ __IgnoreEls, _el));
_ ->
decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
- Items)
+ Items, Rsm)
end;
decode_disco_items_els(__TopXMLNS, __IgnoreEls,
- [_ | _els], Items) ->
+ [_ | _els], Items, Rsm) ->
decode_disco_items_els(__TopXMLNS, __IgnoreEls, _els,
- Items).
+ Items, Rsm).
decode_disco_items_attrs(__TopXMLNS,
[{<<"node">>, _val} | _attrs], _Node) ->
@@ -22111,10 +23372,11 @@ decode_disco_items_attrs(__TopXMLNS, [_ | _attrs],
decode_disco_items_attrs(__TopXMLNS, [], Node) ->
decode_disco_items_attr_node(__TopXMLNS, Node).
-encode_disco_items({disco_items, Node, Items},
+encode_disco_items({disco_items, Node, Items, Rsm},
_xmlns_attrs) ->
_els = lists:reverse('encode_disco_items_$items'(Items,
- [])),
+ 'encode_disco_items_$rsm'(Rsm,
+ []))),
_attrs = encode_disco_items_attr_node(Node,
_xmlns_attrs),
{xmlel, <<"query">>, _attrs, _els}.
@@ -22124,6 +23386,12 @@ encode_disco_items({disco_items, Node, Items},
'encode_disco_items_$items'(_els,
[encode_disco_item(Items, []) | _acc]).
+'encode_disco_items_$rsm'(undefined, _acc) -> _acc;
+'encode_disco_items_$rsm'(Rsm, _acc) ->
+ [encode_rsm_set(Rsm,
+ [{<<"xmlns">>, <<"http://jabber.org/protocol/rsm">>}])
+ | _acc].
+
decode_disco_items_attr_node(__TopXMLNS, undefined) ->
undefined;
decode_disco_items_attr_node(__TopXMLNS, _val) -> _val.
diff --git a/src/xmpp_util.erl b/src/xmpp_util.erl
index 42b251fc1..83f1f4adc 100644
--- a/src/xmpp_util.erl
+++ b/src/xmpp_util.erl
@@ -10,7 +10,8 @@
%% API
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
- is_standalone_chat_state/1]).
+ is_standalone_chat_state/1, get_xdata_values/2,
+ has_xdata_var/2]).
-include("xmpp.hrl").
@@ -76,6 +77,17 @@ is_standalone_chat_state(Stanza) ->
false
end.
+-spec get_xdata_values(binary(), xdata()) -> [binary()].
+get_xdata_values(Var, #xdata{fields = Fields}) ->
+ case lists:keyfind(Var, #xdata_field.var, Fields) of
+ #xdata_field{values = Vals} -> Vals;
+ false -> []
+ end.
+
+-spec has_xdata_var(binary(), xdata()) -> boolean().
+has_xdata_var(Var, #xdata{fields = Fields}) ->
+ lists:keymember(Var, #xdata_field.var, Fields).
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec
index 6c74a7ab1..fdf16ed37 100644
--- a/tools/xmpp_codec.spec
+++ b/tools/xmpp_codec.spec
@@ -224,10 +224,12 @@
-xml(disco_items,
#elem{name = <<"query">>,
xmlns = <<"http://jabber.org/protocol/disco#items">>,
- result = {disco_items, '$node', '$items'},
+ result = {disco_items, '$node', '$items', '$rsm'},
attrs = [#attr{name = <<"node">>}],
refs = [#ref{name = disco_item,
- label = '$items'}]}).
+ label = '$items'},
+ #ref{name = rsm_set, min = 0, max = 1,
+ label = '$rsm'}]}).
-xml(private,
#elem{name = <<"query">>,
@@ -468,6 +470,10 @@
#elem{name = <<"not-authorized">>,
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
result = 'not-authorized'}).
+-xml(error_payment_required,
+ #elem{name = <<"payment-required">>,
+ xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
+ result = 'payment-required'}).
-xml(error_policy_violation,
#elem{name = <<"policy-violation">>,
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
@@ -561,6 +567,8 @@
min = 0, max = 1, label = '$reason'},
#ref{name = error_not_authorized,
min = 0, max = 1, label = '$reason'},
+ #ref{name = error_payment_required,
+ min = 0, max = 1, label = '$reason'},
#ref{name = error_policy_violation,
min = 0, max = 1, label = '$reason'},
#ref{name = error_recipient_unavailable,
@@ -1582,7 +1590,8 @@
-xml(xdata_field_option,
#elem{name = <<"option">>,
xmlns = <<"jabber:x:data">>,
- result = '$value',
+ result = {xdata_option, '$label', '$value'},
+ attrs = [#attr{name = <<"label">>}],
refs = [#ref{name = xdata_field_value,
label = '$value',
min = 1, max = 1}]}).
@@ -1945,9 +1954,11 @@
dec = {dec_utc, []},
enc = {enc_utc, []}}]}).
--xml(muc_user_reason,
+-xml(muc_reason,
#elem{name = <<"reason">>,
- xmlns = <<"http://jabber.org/protocol/muc#user">>,
+ xmlns = [<<"http://jabber.org/protocol/muc#user">>,
+ <<"http://jabber.org/protocol/muc#admin">>,
+ <<"http://jabber.org/protocol/muc#owner">>],
result = '$cdata'}).
-xml(muc_user_decline,
@@ -1960,31 +1971,39 @@
#attr{name = <<"from">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
- refs = [#ref{name = muc_user_reason, min = 0,
+ refs = [#ref{name = muc_reason, min = 0,
+ default = <<"">>,
max = 1, label = '$reason'}]}).
--xml(muc_user_destroy,
+-xml(muc_destroy,
#elem{name = <<"destroy">>,
- xmlns = <<"http://jabber.org/protocol/muc#user">>,
- result = {muc_user_destroy, '$reason', '$jid'},
- attrs = [#attr{name = <<"jid">>,
+ xmlns = [<<"http://jabber.org/protocol/muc#user">>,
+ <<"http://jabber.org/protocol/muc#owner">>],
+ result = {muc_destroy, '$xmlns', '$jid', '$reason', '$password'},
+ attrs = [#attr{name = <<"jid">>,
dec = {dec_jid, []},
- enc = {enc_jid, []}}],
- refs = [#ref{name = muc_user_reason, min = 0,
- max = 1, label = '$reason'}]}).
+ enc = {enc_jid, []}},
+ #attr{name = <<"xmlns">>}],
+ refs = [#ref{name = muc_reason, min = 0,
+ default = <<"">>,
+ max = 1, label = '$reason'},
+ #ref{name = muc_password, min = 0, max = 1,
+ label = '$password'}]}).
-xml(muc_user_invite,
#elem{name = <<"invite">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
- result = {muc_invite, '$reason', '$from', '$to'},
+ result = {muc_invite, '$reason', '$from', '$to', '$continue'},
attrs = [#attr{name = <<"to">>,
dec = {dec_jid, []},
enc = {enc_jid, []}},
#attr{name = <<"from">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
- refs = [#ref{name = muc_user_reason, min = 0,
- max = 1, label = '$reason'}]}).
+ refs = [#ref{name = muc_reason, min = 0, default = <<"">>,
+ max = 1, label = '$reason'},
+ #ref{name = muc_user_continue, min = 0, max = 1,
+ label = '$continue'}]}).
-xml(muc_user_actor,
#elem{name = <<"actor">>,
@@ -2018,7 +2037,7 @@
min = 0, max = 1, label = '$actor'},
#ref{name = muc_user_continue,
min = 0, max = 1, label = '$continue'},
- #ref{name = muc_user_reason,
+ #ref{name = muc_reason, default = <<"">>,
min = 0, max = 1, label = '$reason'}],
attrs = [#attr{name = <<"affiliation">>,
dec = {dec_enum, [[admin, member, none,
@@ -2038,44 +2057,56 @@
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_user, '$decline', '$destroy', '$invites',
'$items', '$status_codes', '$password'},
- attrs = [#attr{name = <<"password">>}],
refs = [#ref{name = muc_user_decline, min = 0,
max = 1, label = '$decline'},
- #ref{name = muc_user_destroy, min = 0, max = 1,
+ #ref{name = muc_destroy, min = 0, max = 1,
label = '$destroy'},
+ #ref{name = muc_password, min = 0, max = 1,
+ label = '$password'},
#ref{name = muc_user_invite, label = '$invites'},
#ref{name = muc_user_item, label = '$items'},
#ref{name = muc_user_status, label = '$status_codes'}]}).
--xml(muc_owner_password,
+-xml(muc_password,
#elem{name = <<"password">>,
- xmlns = <<"http://jabber.org/protocol/muc#owner">>,
- result = '$cdata'}).
-
--xml(muc_owner_reason,
- #elem{name = <<"reason">>,
- xmlns = <<"http://jabber.org/protocol/muc#owner">>,
+ xmlns = [<<"http://jabber.org/protocol/muc#owner">>,
+ <<"http://jabber.org/protocol/muc#user">>,
+ <<"http://jabber.org/protocol/muc">>],
result = '$cdata'}).
--xml(muc_owner_destroy,
- #elem{name = <<"destroy">>,
- xmlns = <<"http://jabber.org/protocol/muc#owner">>,
- result = {muc_owner_destroy, '$jid', '$reason', '$password'},
- attrs = [#attr{name = <<"jid">>,
- dec = {dec_jid, []},
- enc = {enc_jid, []}}],
- refs = [#ref{name = muc_owner_password, min = 0, max = 1,
- label = '$password'},
- #ref{name = muc_owner_reason, min = 0, max = 1,
- label = '$reason'}]}).
-
-xml(muc_owner,
#elem{name = <<"query">>,
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
- result = {muc_owner, '$destroy', '$config'},
- refs = [#ref{name = muc_owner_destroy, min = 0, max = 1,
- label = '$destroy'},
- #ref{name = xdata, min = 0, max = 1, label = '$config'}]}).
+ result = {muc_owner, '$destroy', '$config', '$items'},
+ refs = [#ref{name = muc_destroy, min = 0, max = 1,
+ label = '$destroy'},
+ #ref{name = xdata, min = 0, max = 1,
+ label = '$config'},
+ #ref{name = muc_owner_item, label = '$items'}]}).
+
+-xml(muc_owner_item,
+ #elem{name = <<"item">>,
+ xmlns = <<"http://jabber.org/protocol/muc#owner">>,
+ result = {muc_item, '$actor', '$continue', '$reason',
+ '$affiliation', '$role', '$jid', '$nick'},
+ refs = [#ref{name = muc_admin_actor,
+ min = 0, max = 1, label = '$actor'},
+ #ref{name = muc_admin_continue,
+ min = 0, max = 1, label = '$continue'},
+ #ref{name = muc_reason, default = <<"">>,
+ min = 0, max = 1, label = '$reason'}],
+ attrs = [#attr{name = <<"affiliation">>,
+ dec = {dec_enum, [[admin, member, none,
+ outcast, owner]]},
+ enc = {enc_enum, []}},
+ #attr{name = <<"role">>,
+ dec = {dec_enum, [[moderator, none,
+ participant, visitor]]},
+ enc = {enc_enum, []}},
+ #attr{name = <<"jid">>,
+ dec = {dec_jid, []},
+ enc = {enc_jid, []}},
+ #attr{name = <<"nick">>}]}).
-xml(muc_admin_item,
#elem{name = <<"item">>,
@@ -2086,7 +2117,7 @@
min = 0, max = 1, label = '$actor'},
#ref{name = muc_admin_continue,
min = 0, max = 1, label = '$continue'},
- #ref{name = muc_admin_reason,
+ #ref{name = muc_reason, default = <<"">>,
min = 0, max = 1, label = '$reason'}],
attrs = [#attr{name = <<"affiliation">>,
dec = {dec_enum, [[admin, member, none,
@@ -2116,11 +2147,6 @@
result = '$thread',
attrs = [#attr{name = <<"thread">>}]}).
--xml(muc_admin_reason,
- #elem{name = <<"reason">>,
- xmlns = <<"http://jabber.org/protocol/muc#admin">>,
- result = '$cdata'}).
-
-xml(muc_admin,
#elem{name = <<"query">>,
xmlns = <<"http://jabber.org/protocol/muc#admin">>,
@@ -2131,9 +2157,66 @@
#elem{name = <<"x">>,
xmlns = <<"http://jabber.org/protocol/muc">>,
result = {muc, '$history', '$password'},
- attrs = [#attr{name = <<"password">>}],
refs = [#ref{name = muc_history, min = 0, max = 1,
- label = '$history'}]}).
+ label = '$history'},
+ #ref{name = muc_password, min = 0, max = 1,
+ label = '$password'}]}).
+
+-xml(muc_unique,
+ #elem{name = <<"unique">>,
+ xmlns = <<"http://jabber.org/protocol/muc#unique">>,
+ result = {muc_unique, '$name'},
+ cdata = #cdata{default = <<"">>,
+ label = '$name'}}).
+
+-xml(x_conference,
+ #elem{name = <<"x">>,
+ xmlns = <<"jabber:x:conference">>,
+ result = {x_conference, '$jid', '$password', '$reason',
+ '$continue', '$thread'},
+ attrs = [#attr{name = <<"jid">>,
+ required = true,
+ dec = {dec_jid, []},
+ enc = {enc_jid, []}},
+ #attr{name = <<"password">>, default = <<"">>},
+ #attr{name = <<"reason">>, default = <<"">>},
+ #attr{name = <<"thread">>, default = <<"">>},
+ #attr{name = <<"continue">>,
+ dec = {dec_bool, []},
+ enc = {enc_bool, []}}]}).
+
+-xml(muc_subscription,
+ #elem{name = <<"subscription">>,
+ xmlns = <<"urn:xmpp:mucsub:0">>,
+ result = '$jid',
+ attrs = [#attr{name = <<"jid">>,
+ required = true,
+ dec = {dec_jid, []},
+ enc = {enc_jid, []}}]}).
+
+-xml(muc_subscriptions,
+ #elem{name = <<"subscriptions">>,
+ xmlns = <<"urn:xmpp:mucsub:0">>,
+ result = {muc_subscriptions, '$list'},
+ refs = [#ref{name = muc_subscription, label = '$list'}]}).
+
+-xml(muc_subscribe_event,
+ #elem{name = <<"event">>,
+ xmlns = <<"urn:xmpp:mucsub:0">>,
+ result = '$node',
+ attrs = [#attr{name = <<"node">>, required = true}]}).
+
+-xml(muc_subscribe,
+ #elem{name = <<"subscribe">>,
+ xmlns = <<"urn:xmpp:mucsub:0">>,
+ result = {muc_subscribe, '$nick', '$events'},
+ attrs = [#attr{name = <<"nick">>, required = true}],
+ refs = [#ref{name = muc_subscribe_event, label = '$events'}]}).
+
+-xml(muc_unsubscribe,
+ #elem{name = <<"unsubscribe">>,
+ xmlns = <<"urn:xmpp:mucsub:0">>,
+ result = {muc_unsubscribe}}).
-xml(rsm_after,
#elem{name = <<"after">>,
@@ -2143,7 +2226,7 @@
-xml(rsm_before,
#elem{name = <<"before">>,
xmlns = <<"http://jabber.org/protocol/rsm">>,
- cdata = #cdata{default = none},
+ cdata = #cdata{default = <<"">>},
result = '$cdata'}).
-xml(rsm_last,
@@ -2215,16 +2298,23 @@
dec = {dec_jid, []},
enc = {enc_jid, []}}}).
+-xml(mam_withtext,
+ #elem{name = <<"withtext">>,
+ xmlns = <<"urn:xmpp:mam:tmp">>,
+ result = '$cdata',
+ cdata = #cdata{required = true}}).
+
-xml(mam_query,
#elem{name = <<"query">>,
xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
result = {mam_query, '$xmlns', '$id', '$start', '$end', '$with',
- '$rsm', '$xdata'},
+ '$withtext', '$rsm', '$xdata'},
attrs = [#attr{name = <<"queryid">>, label = '$id'},
#attr{name = <<"xmlns">>}],
refs = [#ref{name = mam_start, min = 0, max = 1, label = '$start'},
#ref{name = mam_end, min = 0, max = 1, label = '$end'},
#ref{name = mam_with, min = 0, max = 1, label = '$with'},
+ #ref{name = mam_withtext, min = 0, max = 1, label = '$withtext'},
#ref{name = rsm_set, min = 0, max = 1, label = '$rsm'},
#ref{name = xdata, min = 0, max = 1, label = '$xdata'}]}).
@@ -2248,7 +2338,7 @@
-xml(mam_jid,
#elem{name = <<"jid">>,
- xmlns = <<"urn:xmpp:mam:tmp">>,
+ xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
result = '$cdata',
cdata = #cdata{required = true,
dec = {dec_jid, []},
@@ -2256,15 +2346,15 @@
-xml(mam_never,
#elem{name = <<"never">>,
- xmlns = <<"urn:xmpp:mam:tmp">>,
+ xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
result = '$jids',
- refs = [#ref{name = mam_jid, label = '$jids', default = []}]}).
+ refs = [#ref{name = mam_jid, label = '$jids'}]}).
-xml(mam_always,
#elem{name = <<"always">>,
- xmlns = <<"urn:xmpp:mam:tmp">>,
+ xmlns = [<<"urn:xmpp:mam:0">>, <<"urn:xmpp:mam:1">>, <<"urn:xmpp:mam:tmp">>],
result = '$jids',
- refs = [#ref{name = mam_jid, label = '$jids', default = []}]}).
+ refs = [#ref{name = mam_jid, label = '$jids'}]}).
-xml(mam_prefs,
#elem{name = <<"prefs">>,
@@ -2275,9 +2365,9 @@
enc = {enc_enum, []}},
#attr{name = <<"xmlns">>}],
refs = [#ref{name = mam_always, label = '$always',
- min = 0, max = 1, default = []},
+ min = 0, max = 1},
#ref{name = mam_never, label = '$never',
- min = 0, max = 1, default = []}]}).
+ min = 0, max = 1}]}).
-xml(mam_fin,
#elem{name = <<"fin">>,
@@ -2538,8 +2628,8 @@
#attr{name = <<"nick">>,
label = '$nick'}]}).
--record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
- 'store' | 'no-permanent-store'}).
+-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' | 'store' |
+ 'no-permanent-store' | 'no-permanent-storage'}).
-type hint() :: #hint{}.
-xml(hint_no_copy,
@@ -2567,6 +2657,11 @@
xmlns = <<"urn:xmpp:hints">>,
result = {hint, 'no-permanent-store'}}).
+-xml(hint_no_permanent_storage,
+ #elem{name = <<"no-permanent-storage">>,
+ xmlns = <<"urn:xmpp:hints">>,
+ result = {hint, 'no-permanent-storage'}}).
+
-xml(search_instructions,
#elem{name = <<"instructions">>,
xmlns = <<"jabber:iq:search">>,
@@ -2679,6 +2774,53 @@
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}}]}).
+-xml(nick,
+ #elem{name = <<"nick">>,
+ xmlns = <<"http://jabber.org/protocol/nick">>,
+ result = {nick, '$name'},
+ cdata = #cdata{label = '$name',
+ required = true}}).
+
+-xml(address,
+ #elem{name = <<"address">>,
+ xmlns = <<"http://jabber.org/protocol/address">>,
+ result = {address, '$type', '$jid', '$desc', '$node', '$delivered'},
+ attrs = [#attr{name = <<"type">>,
+ required = true,
+ dec = {dec_enum, [[bcc, cc, noreply, ofrom,
+ replyroom, replyto, to]]},
+ enc = {enc_enum, []}},
+ #attr{name = <<"jid">>,
+ enc = {enc_jid, []},
+ dec = {dec_jid, []}},
+ #attr{name = <<"desc">>},
+ #attr{name = <<"node">>},
+ #attr{name = <<"delivered">>,
+ enc = {enc_bool, []},
+ dec = {dec_bool, []}}]}).
+
+-xml(addresses,
+ #elem{name = <<"addresses">>,
+ xmlns = <<"http://jabber.org/protocol/address">>,
+ result = {addresses, '$list'},
+ %% TODO: 'min' should be '1', but this is not implemented
+ refs = [#ref{name = address, label = '$list'}]}).
+
+-xml(stanza_id,
+ #elem{name = <<"stanza-id">>,
+ xmlns = <<"urn:xmpp:sid:0">>,
+ result = {stanza_id, '$by', '$id'},
+ attrs = [#attr{name = <<"id">>, required = true},
+ #attr{name = <<"by">>, required = true,
+ enc = {enc_jid, []},
+ dec = {dec_jid, []}}]}).
+
+-xml(client_id,
+ #elem{name = <<"client-id">>,
+ xmlns = <<"urn:xmpp:sid:0">>,
+ result = {client_id, '$id'},
+ attrs = [#attr{name = <<"id">>, required = true}]}).
+
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),