From 1fc58ace2f139ea01c13bac0a8f30735d9f8af29 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Tue, 9 Aug 2016 13:36:43 +0300 Subject: [PATCH] Add commands for MUC subscriptions management --- src/mod_muc_admin.erl | 71 ++++++++++++++++++++++++++++++++++++++++- src/mod_muc_room.erl | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 0b5e79f60..a07ac0bc9 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -20,6 +20,7 @@ change_room_option/4, get_room_options/2, set_room_affiliation/4, get_room_affiliations/2, web_menu_main/2, web_page_main/2, web_menu_host/3, + subscribe_room/4, unsubscribe_room/2, web_page_host/3, mod_opt_type/1, get_commands_spec/0]). -include("ejabberd.hrl"). @@ -151,7 +152,17 @@ get_commands_spec() -> {value, string} ]}} }}}, - + #ejabberd_commands{name = subscribe_room, tags = [muc_room], + desc = "Subscribe to a MUC conference", + module = ?MODULE, function = subscribe_room, + args = [{user, binary}, {nick, binary}, {room, binary}, + {nodes, binary}], + result = {list, {node, string}}}, + #ejabberd_commands{name = unsubscribe_room, tags = [muc_room], + desc = "Unsubscribe from a MUC conference", + module = ?MODULE, function = unsubscribe_room, + args = [{user, binary}, {room, binary}], + result = {res, rescode}}, #ejabberd_commands{name = set_room_affiliation, tags = [muc_room], desc = "Change an affiliation in a MUC room", module = ?MODULE, function = set_room_affiliation, @@ -884,6 +895,64 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> error end. +%%% +%%% MUC Subscription +%%% + +subscribe_room(_User, Nick, _Room, _Nodes) when Nick == <<"">> -> + throw({error, "Nickname must be set"}); +subscribe_room(User, Nick, Room, Nodes) -> + NodeList = re:split(Nodes, "\\h*,\\h*"), + case jid:from_string(Room) of + #jid{luser = Name, lserver = Host} when Name /= <<"">> -> + case jid:from_string(User) of + error -> + throw({error, "Malformed user JID"}); + JID -> + UserJID = jid:replace_resource(JID, Nick), + case get_room_pid(Name, Host) of + Pid when is_pid(Pid) -> + case gen_fsm:sync_send_all_state_event( + Pid, + {muc_subscribe, UserJID, Nick, NodeList}) of + {ok, SubscribedNodes} -> + SubscribedNodes; + {error, Reason} -> + throw({error, binary_to_list(Reason)}) + end; + _ -> + throw({error, "The room does not exist"}) + end + end; + _ -> + throw({error, "Malformed room JID"}) + end. + +unsubscribe_room(User, Room) -> + case jid:from_string(Room) of + #jid{luser = Name, lserver = Host} when Name /= <<"">> -> + case jid:from_string(User) of + error -> + throw({error, "Malformed user JID"}); + UserJID -> + case get_room_pid(Name, Host) of + Pid when is_pid(Pid) -> + case gen_fsm:sync_send_all_state_event( + Pid, + {muc_unsubscribe, UserJID}) of + ok -> + ok; + {error, Reason} -> + throw({error, binary_to_list(Reason)}) + end; + _ -> + throw({error, "The room does not exist"}) + end + end; + _ -> + throw({error, "Malformed room JID"}) + end. + make_opts(StateData) -> Config = StateData#state.config, [ diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 773953c4a..e5ed4cc68 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -749,6 +749,60 @@ handle_sync_event({change_state, NewStateData}, _From, handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) -> NSD = process_item_change(Item, StateData, UJID), {reply, {ok, NSD}, StateName, NSD}; +handle_sync_event({muc_subscribe, From, Nick, Nodes}, _From, + StateName, StateData) -> + SubEl = #xmlel{name = <<"subscribe">>, + attrs = [{<<"xmlns">>, ?NS_MUCSUB}, {<<"nick">>, Nick}], + children = [#xmlel{name = <<"event">>, + attrs = [{<<"node">>, Node}]} + || Node <- Nodes]}, + IQ = #iq{type = set, id = randoms:get_string(), + xmlns = ?NS_MUCSUB, sub_el = SubEl}, + Packet = jlib:iq_to_xml(IQ#iq{sub_el = [SubEl]}), + Config = StateData#state.config, + CaptchaRequired = Config#config.captcha_protected, + PasswordProtected = Config#config.password_protected, + TmpConfig = Config#config{captcha_protected = false, + password_protected = false}, + TmpState = StateData#state{config = TmpConfig}, + case process_iq_mucsub(From, Packet, IQ, TmpState) of + {result, _, NewState} -> + NewConfig = (NewState#state.config)#config{ + captcha_protected = CaptchaRequired, + password_protected = PasswordProtected}, + {reply, {ok, get_subscription_nodes(Packet)}, StateName, + NewState#state{config = NewConfig}}; + {ignore, NewState} -> + NewConfig = (NewState#state.config)#config{ + captcha_protected = CaptchaRequired, + password_protected = PasswordProtected}, + {reply, {error, <<"Requrest is ignored">>}, + NewState#state{config = NewConfig}}; + {error, Err, NewState} -> + NewConfig = (NewState#state.config)#config{ + captcha_protected = CaptchaRequired, + password_protected = PasswordProtected}, + {reply, {error, get_error_text(Err)}, StateName, + NewState#state{config = NewConfig}}; + {error, Err} -> + {reply, {error, get_error_text(Err)}, StateName, StateData} + end; +handle_sync_event({muc_unsubscribe, From}, _From, StateName, StateData) -> + SubEl = #xmlel{name = <<"unsubscribe">>, + attrs = [{<<"xmlns">>, ?NS_MUCSUB}]}, + IQ = #iq{type = set, id = randoms:get_string(), + xmlns = ?NS_MUCSUB, sub_el = SubEl}, + Packet = jlib:iq_to_xml(IQ), + case process_iq_mucsub(From, Packet, IQ, StateData) of + {result, _, NewState} -> + {reply, ok, StateName, NewState}; + {ignore, NewState} -> + {reply, {error, <<"Requrest is ignored">>}, NewState}; + {error, Err, NewState} -> + {reply, {error, get_error_text(Err)}, StateName, NewState}; + {error, Err} -> + {reply, {error, get_error_text(Err)}, StateName, StateData} + end; handle_sync_event(_Event, _From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData}. @@ -1346,6 +1400,14 @@ get_error_condition2(Packet) -> <- EEls], {condition, Condition}. +get_error_text(Error) -> + case fxml:get_subtag_with_xmlns(Error, <<"text">>, ?NS_STANZAS) of + #xmlel{} = Tag -> + fxml:get_tag_cdata(Tag); + false -> + <<"">> + end. + make_reason(Packet, From, StateData, Reason1) -> {ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users), Condition = get_error_condition(Packet), @@ -4608,6 +4670,18 @@ process_iq_mucsub(From, _Packet, NewStateData = remove_subscription(From, User, StateData), store_room(NewStateData), {result, [], NewStateData}; + error when From#jid.lresource == <<"">> -> + {LUser, LServer, _} = LJID, + NewStateData = + dict:fold( + fun({U, S, _}, #user{jid = J, is_subscriber = true} = User, + AccState) when U == LUser, S == LServer -> + remove_subscription(J, User, AccState); + (_, _, AccState) -> + AccState + end, StateData, StateData#state.users), + store_room(NewStateData), + {result, [], NewStateData}; _ -> {result, [], StateData} end;