]> granicus.if.org Git - ejabberd/commitdiff
Add commands for MUC subscriptions management
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Tue, 9 Aug 2016 10:36:43 +0000 (13:36 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Tue, 9 Aug 2016 10:36:43 +0000 (13:36 +0300)
src/mod_muc_admin.erl
src/mod_muc_room.erl

index 0b5e79f603ce57af9dfc3c4dc1b2db32bee0056a..a07ac0bc99eede3c348e94475581845cdfbd013f 100644 (file)
@@ -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,
     [
index 773953c4a13b5e5601f0a0174df836eb9ff1281c..e5ed4cc681598c809693d1daf49c3e1def3d5865 100644 (file)
@@ -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;