]> granicus.if.org Git - ejabberd/commitdiff
Replace dict with maps
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Thu, 15 Nov 2018 11:13:45 +0000 (14:13 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Thu, 15 Nov 2018 11:13:45 +0000 (14:13 +0300)
This will improve performance and memory consumptions of large MUCs

.travis.yml
README
include/mod_muc_room.hrl
src/mod_muc_admin.erl
src/mod_muc_log.erl
src/mod_muc_room.erl

index ae0d3abb36f42e3233f8b138413af19fb21f91cc..e3cb5d98c7f79584bf395339c8993b9079369888 100644 (file)
@@ -1,9 +1,9 @@
 language: erlang
 
 otp_release:
-  - 17.5
-  - 18.3
-  - 19.2
+  - 19.0
+  - 20.3
+  - 21.1
 
 services:
   - redis-server
diff --git a/README b/README
index 91945023808de65e5d2e419bbc862b45004b0c4f..6221eae375fd02fb0824e7a4876b0f39ed8aab8c 100644 (file)
--- a/README
+++ b/README
@@ -105,11 +105,11 @@ To compile ejabberd you need:
 
  - GNU Make.
  - GCC.
- - Libexpat 1.95 or higher.
- - Libyaml 0.1.4 or higher.
- - Erlang/OTP 17.5 or higher.
- - OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption.
- - Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional.
+ - Libexpat ≥ 1.95.
+ - Libyaml ≥ 0.1.4.
+ - Erlang/OTP ≥ 19.0.
+ - OpenSSL ≥ 1.0.0.
+ - Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138). Optional.
  - PAM library. Optional. For Pluggable Authentication Modules (PAM).
  - ImageMagick's Convert program. Optional. For CAPTCHA challenges.
 
index 35bdb6ff721fe730f5ed64f78065c1c6e3f879a7..65f3c54796554154edcc8de231287ed9434f87a7 100644 (file)
@@ -22,8 +22,6 @@
 
 -define(SETS, gb_sets).
 
--define(DICT, dict).
-
 -record(lqueue,
 {
     queue   :: p1_queue:queue(),
     access                  = {none,none,none,none} :: {atom(), atom(), atom(), atom()},
     jid                     = #jid{} :: jid(),
     config                  = #config{} :: config(),
-    users                   = (?DICT):new() :: dict:dict(),
-    subscribers             = (?DICT):new() :: dict:dict(),
-    subscriber_nicks        = (?DICT):new() :: dict:dict(),
+    users                   = #{} :: map(),
+    subscribers             = #{} :: map(),
+    subscriber_nicks        = #{} :: map(),
     last_voice_request_time = treap:empty() :: treap:treap(),
-    robots                  = (?DICT):new() :: dict:dict(),
-    nicks                   = (?DICT):new() :: dict:dict(),
-    affiliations            = (?DICT):new() :: dict:dict(),
+    robots                  = #{} :: map(),
+    nicks                   = #{} :: map(),
+    affiliations            = #{} :: map(),
     history                 :: lqueue(),
     subject                 = [] :: [text()],
     subject_author          = <<"">> :: binary(),
index f5213f212aa699d9d24f777731c11b2d42d586cb..c050d4fc24c4d5418973bbe91f5fea3c427be249 100644 (file)
@@ -357,7 +357,7 @@ build_summary_room(Name, Host, Pid) ->
     C = get_room_config(Pid),
     Public = C#config.public,
     S = get_room_state(Pid),
-    Participants = dict:size(S#state.users),
+    Participants = maps:size(S#state.users),
     {<<Name/binary, "@", Host/binary>>,
         misc:atom_to_binary(Public),
      Participants
@@ -523,7 +523,7 @@ build_info_room({Name, Host, Pid}) ->
 
     S = get_room_state(Pid),
     Just_created = S#state.just_created,
-    Num_participants = length(dict:fetch_keys(S#state.users)),
+    Num_participants = maps:size(S#state.users),
 
     History = (S#state.history)#lqueue.queue,
     Ts_last_message =
@@ -778,7 +778,7 @@ decide_room({_Room_name, _Host, Room_pid}, Last_allowed) ->
     Just_created = S#state.just_created,
 
     Room_users = S#state.users,
-    Num_users = length(?DICT:to_list(Room_users)),
+    Num_users = maps:size(Room_users),
 
     History = (S#state.history)#lqueue.queue,
     Ts_now = calendar:universal_time(),
@@ -854,7 +854,7 @@ get_room_occupants(Pid) ->
               Info#user.nick,
               atom_to_list(Info#user.role)}
       end,
-      dict:to_list(S#state.users)).
+      maps:to_list(S#state.users)).
 
 get_room_occupants_number(Room, Host) ->
     case get_room_pid(Room, Host) of
@@ -862,7 +862,7 @@ get_room_occupants_number(Room, Host) ->
            throw({error, room_not_found});
        Pid ->
            S = get_room_state(Pid),
-           dict:size(S#state.users)
+           maps:size(S#state.users)
     end.
 
 %%----------------------------
@@ -1039,7 +1039,7 @@ get_room_affiliations(Name, Service) ->
        {ok, Pid} ->
            %% Get the PID of the online room, then request its state
            {ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
-           Affiliations = ?DICT:to_list(StateData#state.affiliations),
+           Affiliations = maps:to_list(StateData#state.affiliations),
            lists:map(
              fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)->
                      {Uname, Domain, Aff, Reason};
@@ -1173,7 +1173,7 @@ get_config_opt_name(Pos) ->
         {get_config_opt_name(Opt), element(Opt, Config)}).
 make_opts(StateData) ->
     Config = StateData#state.config,
-    Subscribers = (?DICT):fold(
+    Subscribers = maps:fold(
                     fun(_LJID, Sub, Acc) ->
                             [{Sub#subscriber.jid,
                               Sub#subscriber.nick,
@@ -1205,7 +1205,7 @@ make_opts(StateData) ->
      {captcha_whitelist,
       (?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
      {affiliations,
-      (?DICT):to_list(StateData#state.affiliations)},
+      maps:to_list(StateData#state.affiliations)},
      {subject, StateData#state.subject},
      {subject_author, StateData#state.subject_author},
      {subscribers, Subscribers}].
index 7f5ca1b7184f58ca9d2be3eeffe0c289b9a7f51c..a847a3874bd487d0d5a0697c295e5c93246a77e3 100644 (file)
@@ -887,7 +887,7 @@ get_room_occupants(RoomJIDString) ->
     MucService = RoomJID#jid.lserver,
     StateData = get_room_state(RoomName, MucService),
     [{U#user.jid, U#user.nick, U#user.role}
-     || {_, U} <- (?DICT):to_list(StateData#state.users)].
+     || U <- maps:values(StateData#state.users)].
 
 -spec get_room_state(binary(), binary()) -> mod_muc_room:state().
 
index 020772781ee95a9fc623218d9305d67a683d0600..4cb44d2bc0f3fe4d4c689a7c6440d7b74d09aecf 100644 (file)
 -callback set_affiliation(binary(), binary(), binary(), jid(), affiliation(),
                          binary()) -> ok | {error, any()}.
 -callback set_affiliations(binary(), binary(), binary(),
-                          dict:dict()) -> ok | {error, any()}.
+                          map()) -> ok | {error, any()}.
 -callback get_affiliation(binary(), binary(), binary(),
                          binary(), binary()) -> {ok, affiliation()} | {error, any()}.
--callback get_affiliations(binary(), binary(), binary()) -> {ok, dict:dict()} | {error, any()}.
+-callback get_affiliations(binary(), binary(), binary()) -> {ok, map()} | {error, any()}.
 -callback search_affiliation(binary(), binary(), binary(), affiliation()) ->
     {ok, [{ljid(), {affiliation(), binary()}}]} | {error, any()}.
 
@@ -438,8 +438,8 @@ normal_state({route, ToNick,
              #iq{from = From, type = Type, lang = Lang} = Packet},
             StateData) ->
     case {(StateData#state.config)#config.allow_query_users,
-         (?DICT):find(jid:tolower(From), StateData#state.users)} of
-       {true, {ok, #user{nick = FromNick}}} ->
+         maps:get(jid:tolower(From), StateData#state.users, error)} of
+       {true, #user{nick = FromNick}} ->
            case find_jid_by_nick(ToNick, StateData) of
                false ->
                    ErrText = <<"Recipient is not in the conference room">>,
@@ -507,7 +507,7 @@ handle_event(_Event, StateName, StateData) ->
     {next_state, StateName, StateData}.
 
 handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
-    Len = ?DICT:size(StateData#state.nicks),
+    Len = maps:size(StateData#state.nicks),
     Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
        true ->
            get_roomdesc_reply(JID, StateData,
@@ -542,7 +542,7 @@ handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData
     end;
 handle_sync_event(get_subscribers, _From, StateName, StateData) ->
     JIDs = lists:map(fun jid:make/1,
-                    ?DICT:fetch_keys(StateData#state.subscribers)),
+                    maps:keys(StateData#state.subscribers)),
     {reply, {ok, JIDs}, StateName, StateData};
 handle_sync_event({muc_subscribe, From, Nick, Nodes}, _From,
                  StateName, StateData) ->
@@ -583,12 +583,10 @@ handle_sync_event({muc_unsubscribe, From}, _From, StateName, StateData) ->
            {reply, {error, get_error_text(Err)}, StateName, StateData}
     end;
 handle_sync_event({is_subscribed, From}, _From, StateName, StateData) ->
-    IsSubs = case (?DICT):find(jid:split(From), StateData#state.subscribers) of
-       {ok, #subscriber{nodes = Nodes}} ->
-           {true, Nodes};
-       error ->
-           false
-    end,
+    IsSubs = try maps:get(jid:split(From), StateData#state.subscribers) of
+                #subscriber{nodes = Nodes} -> {true, Nodes}
+            catch _:_ -> false
+            end,
     {reply, IsSubs, StateName, StateData};
 handle_sync_event(_Event, _From, StateName,
                  StateData) ->
@@ -643,30 +641,27 @@ handle_info(process_room_queue,
     end;
 handle_info({captcha_succeed, From}, normal_state,
            StateData) ->
-    NewState = case (?DICT):find(From,
-                                StateData#state.robots)
-                  of
-                {ok, {Nick, Packet}} ->
-                    Robots = (?DICT):store(From, passed,
-                                           StateData#state.robots),
-                    add_new_user(From, Nick, Packet,
-                                 StateData#state{robots = Robots});
-                _ -> StateData
+    NewState = case maps:get(From, StateData#state.robots, passed) of
+                  {Nick, Packet} ->
+                      Robots = maps:put(From, passed, StateData#state.robots),
+                      add_new_user(From, Nick, Packet,
+                                   StateData#state{robots = Robots});
+                  passed ->
+                      StateData
               end,
     {next_state, normal_state, NewState};
 handle_info({captcha_failed, From}, normal_state,
            StateData) ->
-    NewState = case (?DICT):find(From,
-                                StateData#state.robots)
-                  of
-                {ok, {_Nick, Packet}} ->
-                    Robots = (?DICT):erase(From, StateData#state.robots),
-                    Txt = <<"The CAPTCHA verification has failed">>,
-                    Lang = xmpp:get_lang(Packet),
-                    Err = xmpp:err_not_authorized(Txt, Lang),
-                    ejabberd_router:route_error(Packet, Err),
-                    StateData#state{robots = Robots};
-                _ -> StateData
+    NewState = case maps:get(From, StateData#state.robots, passed) of
+                  {_Nick, Packet} ->
+                      Robots = maps:remove(From, StateData#state.robots),
+                      Txt = <<"The CAPTCHA verification has failed">>,
+                      Lang = xmpp:get_lang(Packet),
+                      Err = xmpp:err_not_authorized(Txt, Lang),
+                      ejabberd_router:route_error(Packet, Err),
+                      StateData#state{robots = Robots};
+                  passed ->
+                      StateData
               end,
     {next_state, normal_state, NewState};
 handle_info(shutdown, _StateName, StateData) ->
@@ -714,20 +709,19 @@ terminate(Reason, _StateName, StateData) ->
                                                        reason = ReasonT,
                                                        role = none}],
                                     status_codes = [332,110]}]},
-    (?DICT):fold(fun (LJID, Info, _) ->
-                        Nick = Info#user.nick,
-                        case Reason of
-                          shutdown ->
-                              send_wrapped(jid:replace_resource(StateData#state.jid,
-                                                                Nick),
-                                           Info#user.jid, Packet,
-                                           ?NS_MUCSUB_NODES_PARTICIPANTS,
-                                           StateData);
-                          _ -> ok
-                        end,
-                        tab_remove_online_user(LJID, StateData)
-                end,
-                [], get_users_and_subscribers(StateData)),
+    maps:fold(
+      fun(LJID, Info, _) ->
+             Nick = Info#user.nick,
+             case Reason of
+                 shutdown ->
+                     send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
+                                  Info#user.jid, Packet,
+                                  ?NS_MUCSUB_NODES_PARTICIPANTS,
+                                  StateData);
+                 _ -> ok
+             end,
+             tab_remove_online_user(LJID, StateData)
+      end, [], get_users_and_subscribers(StateData)),
     add_to_log(room_existence, stopped, StateData),
     mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
                           StateData#state.server_host),
@@ -982,17 +976,15 @@ is_user_allowed_message_nonparticipant(JID,
 %% If the JID is not a participant, return values for a service message.
 -spec get_participant_data(jid(), state()) -> {binary(), role()}.
 get_participant_data(From, StateData) ->
-    case (?DICT):find(jid:tolower(From),
-                     StateData#state.users)
-       of
-      {ok, #user{nick = FromNick, role = Role}} ->
-         {FromNick, Role};
-      error ->
-           case ?DICT:find(jid:tolower(jid:remove_resource(From)),
-                           StateData#state.subscribers) of
-               {ok, #subscriber{nick = FromNick}} ->
-                   {FromNick, none};
-               error ->
+    try maps:get(jid:tolower(From), StateData#state.users) of
+       #user{nick = FromNick, role = Role} ->
+           {FromNick, Role}
+    catch _:_ ->
+           try maps:get(jid:tolower(jid:remove_resource(From)),
+                        StateData#state.subscribers) of
+               #subscriber{nick = FromNick} ->
+                   {FromNick, none}
+           catch _:_ ->
                    {<<"">>, moderator}
            end
     end.
@@ -1079,8 +1071,8 @@ do_process_presence(Nick, #presence{from = From, type = unavailable} = Packet,
                    _ -> Packet
                end,
     NewState = add_user_presence_un(From, NewPacket, StateData),
-    case (?DICT):find(Nick, StateData#state.nicks) of
-       {ok, [_, _ | _]} ->
+    case maps:get(Nick, StateData#state.nicks, []) of
+       [_, _ | _] ->
            Aff = get_affiliation(From, StateData),
            Item = #muc_item{affiliation = Aff, role = none, jid = From},
            Pres = xmpp:set_subtag(
@@ -1114,8 +1106,8 @@ maybe_strip_status_from_presence(From, Packet, StateData) ->
 -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):size(StateData1#state.users) == 0
-       andalso (?DICT):size(StateData1#state.subscribers) == 0 of
+       andalso maps:size(StateData1#state.users) == 0
+       andalso maps:size(StateData1#state.subscribers) == 0 of
       true ->
          ?INFO_MSG("Destroyed MUC room ~s because it's temporary "
                    "and empty",
@@ -1125,9 +1117,9 @@ close_room_if_temporary_and_empty(StateData1) ->
       _ -> {next_state, normal_state, StateData1}
     end.
 
--spec get_users_and_subscribers(state()) -> dict:dict().
+-spec get_users_and_subscribers(state()) -> map().
 get_users_and_subscribers(StateData) ->
-    OnlineSubscribers = ?DICT:fold(
+    OnlineSubscribers = maps:fold(
                           fun(LJID, _, Acc) ->
                                   LBareJID = jid:remove_resource(LJID),
                                   case is_subscriber(LBareJID, StateData) of
@@ -1137,16 +1129,16 @@ get_users_and_subscribers(StateData) ->
                                           Acc
                                   end
                           end, ?SETS:new(), StateData#state.users),
-    ?DICT:fold(
+    maps:fold(
        fun(LBareJID, #subscriber{nick = Nick}, Acc) ->
               case ?SETS:is_element(LBareJID, OnlineSubscribers) of
                   false ->
-                      ?DICT:store(LBareJID,
-                                  #user{jid = jid:make(LBareJID),
-                                        nick = Nick,
-                                        role = none,
-                                        last_presence = undefined},
-                                  Acc);
+                      maps:put(LBareJID,
+                               #user{jid = jid:make(LBareJID),
+                                     nick = Nick,
+                                     role = none,
+                                     last_presence = undefined},
+                               Acc);
                   true ->
                       Acc
               end
@@ -1155,12 +1147,12 @@ get_users_and_subscribers(StateData) ->
 -spec is_user_online(jid(), state()) -> boolean().
 is_user_online(JID, StateData) ->
     LJID = jid:tolower(JID),
-    (?DICT):is_key(LJID, StateData#state.users).
+    maps:is_key(LJID, StateData#state.users).
 
 -spec is_subscriber(jid(), state()) -> boolean().
 is_subscriber(JID, StateData) ->
     LJID = jid:tolower(jid:remove_resource(JID)),
-    (?DICT):is_key(LJID, StateData#state.subscribers).
+    maps:is_key(LJID, StateData#state.subscribers).
 
 %% 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().
@@ -1238,7 +1230,7 @@ get_error_text(#stanza_error{text = Txt}) ->
 
 -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),
+    #user{nick = FromNick} = maps:get(jid:tolower(From), StateData#state.users),
     Condition = get_error_condition(xmpp:get_error(Packet)),
     str:format(Reason1, [FromNick, Condition]).
 
@@ -1251,9 +1243,9 @@ expulse_participant(Packet, From, StateData, Reason1) ->
                                              status = xmpp:mk_text(Reason2)},
                                    StateData),
     LJID = jid:tolower(From),
-    {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users),
-    case (?DICT):find(Nick, StateData#state.nicks) of
-       {ok, [_, _ | _]} ->
+    #user{nick = Nick} = maps:get(LJID, StateData#state.users),
+    case maps:get(Nick, StateData#state.nicks, []) of
+       [_, _ | _] ->
            Aff = get_affiliation(From, StateData),
            Item = #muc_item{affiliation = Aff, role = none, jid = From},
            Pres = xmpp:set_subtag(
@@ -1268,7 +1260,7 @@ expulse_participant(Packet, From, StateData, Reason1) ->
 
 -spec get_owners(state()) -> [jid:jid()].
 get_owners(StateData) ->
-    ?DICT:fold(
+    maps:fold(
        fun(LJID, owner, Acc) ->
               [jid:make(LJID)|Acc];
          (LJID, {owner, _}, Acc) ->
@@ -1303,14 +1295,14 @@ set_affiliation_fallback(JID, Affiliation, StateData, Reason) ->
     LJID = jid:remove_resource(jid:tolower(JID)),
     Affiliations = case Affiliation of
                       none ->
-                          (?DICT):erase(LJID, StateData#state.affiliations);
+                          maps:remove(LJID, StateData#state.affiliations);
                       _ ->
-                          (?DICT):store(LJID, {Affiliation, Reason},
-                                        StateData#state.affiliations)
+                          maps:put(LJID, {Affiliation, Reason},
+                                   StateData#state.affiliations)
                   end,
     StateData#state{affiliations = Affiliations}.
 
--spec set_affiliations(dict:dict(), state()) -> state().
+-spec set_affiliations(map(), state()) -> state().
 set_affiliations(Affiliations,
                  #state{config = #config{persistent = false}} = StateData) ->
     set_affiliations_fallback(Affiliations, StateData);
@@ -1326,7 +1318,7 @@ set_affiliations(Affiliations, StateData) ->
            set_affiliations_fallback(Affiliations, StateData)
     end.
 
--spec set_affiliations_fallback(dict:dict(), state()) -> state().
+-spec set_affiliations_fallback(map(), state()) -> state().
 set_affiliations_fallback(Affiliations, StateData) ->
     StateData#state{affiliations = Affiliations}.
 
@@ -1364,32 +1356,23 @@ do_get_affiliation(JID, StateData) ->
 -spec do_get_affiliation_fallback(jid(), state()) -> affiliation().
 do_get_affiliation_fallback(JID, StateData) ->
     LJID = jid:tolower(JID),
-    case (?DICT):find(LJID, StateData#state.affiliations) of
-        {ok, Affiliation} -> Affiliation;
-        _ ->
-            LJID1 = jid:remove_resource(LJID),
-            case (?DICT):find(LJID1, StateData#state.affiliations)
-            of
-                {ok, Affiliation} -> Affiliation;
-                _ ->
-                    LJID2 = setelement(1, LJID, <<"">>),
-                    case (?DICT):find(LJID2,
-                                      StateData#state.affiliations)
-                    of
-                        {ok, Affiliation} -> Affiliation;
-                        _ ->
-                            LJID3 = jid:remove_resource(LJID2),
-                            case (?DICT):find(LJID3,
-                                              StateData#state.affiliations)
-                            of
-                                {ok, Affiliation} -> Affiliation;
-                                _ -> none
+    try maps:get(LJID, StateData#state.affiliations)
+    catch _:_ ->
+            BareLJID = jid:remove_resource(LJID),
+            try maps:get(BareLJID, StateData#state.affiliations)
+           catch _:_ ->
+                    DomainLJID = setelement(1, LJID, <<"">>),
+                    try maps:get(DomainLJID, StateData#state.affiliations)
+                   catch _:_ ->
+                            DomainBareLJID = jid:remove_resource(DomainLJID),
+                            try maps:get(DomainBareLJID, StateData#state.affiliations)
+                           catch _:_ -> none
                             end
                     end
             end
     end.
 
--spec get_affiliations(state()) -> dict:dict().
+-spec get_affiliations(state()) -> map().
 get_affiliations(#state{config = #config{persistent = false}} = StateData) ->
     get_affiliations_callback(StateData);
 get_affiliations(StateData) ->
@@ -1404,7 +1387,7 @@ get_affiliations(StateData) ->
            Affiliations
     end.
 
--spec get_affiliations_callback(state()) -> dict:dict().
+-spec get_affiliations_callback(state()) -> map().
 get_affiliations_callback(StateData) ->
     StateData#state.affiliations.
 
@@ -1425,56 +1408,52 @@ set_role(JID, Role, StateData) ->
     LJID = jid:tolower(JID),
     LJIDs = case LJID of
              {U, S, <<"">>} ->
-                 (?DICT):fold(fun (J, _, Js) ->
-                                      case J of
-                                        {U, S, _} -> [J | Js];
-                                        _ -> Js
-                                      end
-                              end,
-                              [], StateData#state.users);
+                 maps:fold(fun (J, _, Js) ->
+                                   case J of
+                                       {U, S, _} -> [J | Js];
+                                       _ -> Js
+                                   end
+                           end, [], StateData#state.users);
              _ ->
-                 case (?DICT):is_key(LJID, StateData#state.users) of
+                 case maps:is_key(LJID, StateData#state.users) of
                    true -> [LJID];
                    _ -> []
                  end
            end,
-    {Users, Nicks} = case Role of
-                      none ->
-                          lists:foldl(fun (J, {Us, Ns}) ->
-                                              NewNs = case (?DICT):find(J, Us)
-                                                          of
-                                                        {ok,
-                                                         #user{nick = Nick}} ->
-                                                            (?DICT):erase(Nick,
-                                                                          Ns);
-                                                        _ -> Ns
-                                                      end,
-                                              {(?DICT):erase(J, Us), NewNs}
-                                      end,
-                                      {StateData#state.users,
-                                       StateData#state.nicks},
-                                      LJIDs);
-                      _ ->
-                          {lists:foldl(
-                             fun (J, Us) ->
-                                     {ok, User} = (?DICT):find(J, Us),
-                                     if User#user.last_presence == undefined ->
-                                             Us;
-                                        true ->
-                                             (?DICT):store(J, User#user{role = Role}, Us)
-                                     end
-                             end,
-                             StateData#state.users, LJIDs),
-                           StateData#state.nicks}
-                    end,
+    {Users, Nicks} =
+       case Role of
+           none ->
+               lists:foldl(
+                 fun (J, {Us, Ns}) ->
+                         NewNs = try maps:get(J, Us) of
+                                     #user{nick = Nick} ->
+                                         maps:remove(Nick, Ns)
+                                 catch _:_ ->
+                                         Ns
+                                 end,
+                         {maps:remove(J, Us), NewNs}
+                 end,
+                 {StateData#state.users, StateData#state.nicks}, LJIDs);
+           _ ->
+               {lists:foldl(
+                  fun (J, Us) ->
+                          User = maps:get(J, Us),
+                          if User#user.last_presence == undefined ->
+                                  Us;
+                             true ->
+                                  maps:put(J, User#user{role = Role}, Us)
+                          end
+                  end, StateData#state.users, LJIDs),
+                StateData#state.nicks}
+       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
-      {ok, #user{role = Role}} -> Role;
-      _ -> none
+    try maps:get(LJID, StateData#state.users) of
+       #user{role = Role} -> Role
+    catch _:_ -> none
     end.
 
 -spec get_default_role(affiliation(), state()) -> role().
@@ -1659,29 +1638,29 @@ prepare_room_queue(StateData) ->
 update_online_user(JID, #user{nick = Nick} = User, StateData) ->
     LJID = jid:tolower(JID),
     add_to_log(join, Nick, StateData),
-    Nicks1 = case (?DICT):find(LJID, StateData#state.users) of
-                {ok, #user{nick = OldNick}} ->
+    Nicks1 = try maps:get(LJID, StateData#state.users) of
+                #user{nick = OldNick} ->
                     case lists:delete(
-                           LJID, ?DICT:fetch(OldNick, StateData#state.nicks)) of
+                           LJID, maps:get(OldNick, StateData#state.nicks)) of
                         [] ->
-                            ?DICT:erase(OldNick, StateData#state.nicks);
+                            maps:remove(OldNick, StateData#state.nicks);
                         LJIDs ->
-                            ?DICT:store(OldNick, LJIDs, StateData#state.nicks)
-                    end;
-                error ->
+                            maps:put(OldNick, LJIDs, StateData#state.nicks)
+                    end
+            catch _:_ ->
                     StateData#state.nicks
             end,
-    Nicks = (?DICT):update(Nick,
-                          fun (LJIDs) -> [LJID|LJIDs -- [LJID]] end,
-                          [LJID], Nicks1),
-    Users = (?DICT):update(LJID,
-                          fun(U) ->
-                                  U#user{nick = Nick}
-                          end, User, StateData#state.users),
+    Nicks = maps:update_with(Nick,
+                            fun (LJIDs) -> [LJID|LJIDs -- [LJID]] end,
+                            [LJID], Nicks1),
+    Users = maps:update_with(LJID,
+                            fun(U) ->
+                                    U#user{nick = Nick}
+                            end, User, StateData#state.users),
     NewStateData = StateData#state{users = Users, nicks = Nicks},
-    case {?DICT:find(LJID, StateData#state.users),
-         ?DICT:find(LJID, NewStateData#state.users)} of
-       {{ok, #user{nick = Old}}, {ok, #user{nick = New}}} when Old /= New ->
+    case {maps:get(LJID, StateData#state.users, error),
+         maps:get(LJID, NewStateData#state.users, error)} of
+       {#user{nick = Old}, #user{nick = New}} when Old /= New ->
            send_nick_changing(JID, Old, NewStateData, true, true);
        _ ->
            ok
@@ -1691,16 +1670,16 @@ update_online_user(JID, #user{nick = Nick} = User, StateData) ->
 set_subscriber(JID, Nick, Nodes, StateData) ->
     BareJID = jid:remove_resource(JID),
     LBareJID = jid:tolower(BareJID),
-    Subscribers = ?DICT:store(LBareJID,
-                             #subscriber{jid = BareJID,
-                                         nick = Nick,
-                                         nodes = Nodes},
-                             StateData#state.subscribers),
-    Nicks = ?DICT:store(Nick, [LBareJID], StateData#state.subscriber_nicks),
+    Subscribers = maps:put(LBareJID,
+                          #subscriber{jid = BareJID,
+                                      nick = Nick,
+                                      nodes = Nodes},
+                          StateData#state.subscribers),
+    Nicks = maps:put(Nick, [LBareJID], StateData#state.subscriber_nicks),
     NewStateData = StateData#state{subscribers = Subscribers,
                                   subscriber_nicks = Nicks},
     store_room(NewStateData, [{add_subscription, BareJID, Nick, Nodes}]),
-    case not ?DICT:is_key(LBareJID, StateData#state.subscribers) of
+    case not maps:is_key(LBareJID, StateData#state.subscribers) of
        true ->
            send_subscriptions_change_notifications(BareJID, Nick, subscribe, NewStateData);
        _ ->
@@ -1721,18 +1700,17 @@ remove_online_user(JID, StateData) ->
 -spec remove_online_user(jid(), state(), binary()) -> state().
 remove_online_user(JID, StateData, Reason) ->
     LJID = jid:tolower(JID),
-    {ok, #user{nick = Nick}} = (?DICT):find(LJID,
-                                           StateData#state.users),
+    #user{nick = Nick} = maps:get(LJID, StateData#state.users),
     add_to_log(leave, {Nick, Reason}, StateData),
     tab_remove_online_user(JID, StateData),
-    Users = (?DICT):erase(LJID, StateData#state.users),
-    Nicks = case (?DICT):find(Nick, StateData#state.nicks)
-               of
-             {ok, [LJID]} ->
-                 (?DICT):erase(Nick, StateData#state.nicks);
-             {ok, U} ->
-                 (?DICT):store(Nick, U -- [LJID], StateData#state.nicks);
-             error -> StateData#state.nicks
+    Users = maps:remove(LJID, StateData#state.users),
+    Nicks = try maps:get(Nick, StateData#state.nicks) of
+               [LJID] ->
+                   maps:remove(Nick, StateData#state.nicks);
+               U ->
+                   maps:put(Nick, U -- [LJID], StateData#state.nicks)
+           catch _:_ ->
+                   StateData#state.nicks
            end,
     StateData#state{users = Users, nicks = Nicks}.
 
@@ -1756,63 +1734,54 @@ strip_status(Presence) ->
 add_user_presence(JID, Presence, StateData) ->
     LJID = jid:tolower(JID),
     FPresence = filter_presence(Presence),
-    Users = (?DICT):update(LJID,
-                          fun (#user{} = User) ->
-                                  User#user{last_presence = FPresence}
-                          end,
-                          StateData#state.users),
+    Users = maps:update_with(LJID,
+                            fun (#user{} = User) ->
+                                    User#user{last_presence = FPresence}
+                            end, 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),
-    Users = (?DICT):update(LJID,
-                          fun (#user{} = User) ->
-                                  User#user{last_presence = FPresence,
-                                            role = none}
-                          end,
-                          StateData#state.users),
+    Users = maps:update_with(LJID,
+                            fun (#user{} = User) ->
+                                    User#user{last_presence = FPresence,
+                                              role = none}
+                            end, StateData#state.users),
     StateData#state{users = Users}.
 
 %% 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) ->
-    Nicks = ?DICT:merge(fun(_, Val, _) -> Val end,
-                       StateData#state.nicks,
-                       StateData#state.subscriber_nicks),
-    case (?DICT):find(Nick, Nicks) of
-      {ok, [User]} -> [jid:make(User)];
-      {ok, Users} -> [jid:make(LJID) || LJID <- Users];
-      error -> []
-    end.
+    Users = case maps:get(Nick, StateData#state.nicks, []) of
+               [] -> maps:get(Nick, StateData#state.subscriber_nicks, []);
+               Us -> Us
+           end,
+    [jid:make(LJID) || LJID <- Users].
 
 %% 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);
-      {ok, [FirstUser | Users]} ->
-         #user{last_presence = FirstPresence} =
-             (?DICT):fetch(FirstUser, StateData#state.users),
-         {LJID, _} = lists:foldl(fun (Compare,
-                                      {HighestUser, HighestPresence}) ->
-                                         #user{last_presence = P1} =
-                                             (?DICT):fetch(Compare,
-                                                           StateData#state.users),
-                                         case higher_presence(P1,
-                                                              HighestPresence)
-                                             of
-                                           true -> {Compare, P1};
-                                           false ->
-                                               {HighestUser, HighestPresence}
-                                         end
-                                 end,
-                                 {FirstUser, FirstPresence}, Users),
-         jid:make(LJID);
-      error -> false
+    try maps:get(Nick, StateData#state.nicks) of
+       [User] -> jid:make(User);
+       [FirstUser | Users] ->
+           #user{last_presence = FirstPresence} =
+               maps:get(FirstUser, StateData#state.users),
+           {LJID, _} = lists:foldl(
+                         fun(Compare, {HighestUser, HighestPresence}) ->
+                                 #user{last_presence = P1} =
+                                     maps:get(Compare, StateData#state.users),
+                                 case higher_presence(P1, HighestPresence) of
+                                     true -> {Compare, P1};
+                                     false -> {HighestUser, HighestPresence}
+                                 end
+                         end, {FirstUser, FirstPresence}, Users),
+           jid:make(LJID)
+    catch _:_ ->
+           false
     end.
 
 -spec higher_presence(undefined | presence(),
@@ -1834,7 +1803,7 @@ get_priority_from_presence(#presence{priority = Prio}) ->
 -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),
+    #user{nick = Nick} = maps:get(LJID, StateData#state.users),
     Nick.
 
 -spec is_nick_change(jid(), binary(), state()) -> boolean().
@@ -1843,8 +1812,7 @@ is_nick_change(JID, Nick, StateData) ->
     case Nick of
       <<"">> -> false;
       _ ->
-         {ok, #user{nick = OldNick}} = (?DICT):find(LJID,
-                                                    StateData#state.users),
+         #user{nick = OldNick} = maps:get(LJID, StateData#state.users),
          Nick /= OldNick
     end.
 
@@ -1852,9 +1820,9 @@ is_nick_change(JID, Nick, StateData) ->
 nick_collision(User, Nick, StateData) ->
     UserOfNick = case find_jid_by_nick(Nick, StateData) of
                     false ->
-                        case ?DICT:find(Nick, StateData#state.subscriber_nicks) of
-                            {ok, [J]} -> J;
-                            error -> false
+                        try maps:get(Nick, StateData#state.subscriber_nicks) of
+                            [J] -> J
+                        catch _:_ -> false
                         end;
                     J -> J
                 end,
@@ -1871,8 +1839,7 @@ add_new_user(From, Nick, Packet, StateData) ->
     MaxUsers = get_max_users(StateData),
     MaxAdminUsers = MaxUsers +
                      get_max_users_admin_threshold(StateData),
-    NUsers = dict:fold(fun (_, _, Acc) -> Acc + 1 end, 0,
-                      StateData#state.users),
+    NUsers = maps:size(StateData#state.users),
     Affiliation = get_affiliation(From, StateData),
     ServiceAffiliation = get_service_affiliation(From,
                                                 StateData),
@@ -1974,7 +1941,7 @@ add_new_user(From, Nick, Packet, StateData) ->
                          true ->
                              NewStateData#state{just_created = false};
                          false ->
-                             Robots = (?DICT):erase(From, StateData#state.robots),
+                             Robots = maps:remove(From, StateData#state.robots),
                              NewStateData#state{robots = Robots}
                      end,
                  if not IsSubscribeRequest -> ResultState;
@@ -2002,8 +1969,8 @@ add_new_user(From, Nick, Packet, StateData) ->
                                        to = From,
                                        id = ID, body = Body,
                                        sub_els = CaptchaEls},
-                     Robots = (?DICT):store(From, {Nick, Packet},
-                                            StateData#state.robots),
+                     Robots = maps:put(From, {Nick, Packet},
+                                       StateData#state.robots),
                      ejabberd_router:route(MsgPkt),
                      NewState = StateData#state{robots = Robots},
                      if not IsSubscribeRequest ->
@@ -2072,9 +2039,9 @@ check_captcha(Affiliation, From, StateData) ->
           andalso ejabberd_captcha:is_feature_available()
        of
       true when Affiliation == none ->
-         case (?DICT):find(From, StateData#state.robots) of
-           {ok, passed} -> true;
-           _ ->
+         case maps:get(From, StateData#state.robots, error) of
+             passed -> true;
+             _ ->
                WList =
                    (StateData#state.config)#config.captcha_whitelist,
                #jid{luser = U, lserver = S, lresource = R} = From,
@@ -2151,7 +2118,7 @@ is_room_overcrowded(StateData) ->
     MaxUsersPresence = gen_mod:get_module_opt(
                         StateData#state.server_host,
                         mod_muc, max_users_presence),
-    (?DICT):size(StateData#state.users) > MaxUsersPresence.
+    maps:size(StateData#state.users) > MaxUsersPresence.
 
 -spec presence_broadcast_allowed(jid(), state()) -> boolean().
 presence_broadcast_allowed(JID, StateData) ->
@@ -2189,7 +2156,7 @@ send_self_presence(JID, State) ->
 
 -spec send_initial_presence(jid(), state(), state()) -> ok.
 send_initial_presence(NJID, StateData, OldStateData) ->
-    send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
+    send_new_presence(NJID, <<"">>, true, StateData, OldStateData).
 
 -spec send_update_presence(jid(), state(), state()) -> ok.
 send_update_presence(JID, StateData, OldStateData) ->
@@ -2207,22 +2174,21 @@ send_update_presence1(JID, Reason, StateData, OldStateData) ->
     LJID = jid:tolower(JID),
     LJIDs = case LJID of
              {U, S, <<"">>} ->
-                 (?DICT):fold(fun (J, _, Js) ->
-                                      case J of
-                                        {U, S, _} -> [J | Js];
-                                        _ -> Js
-                                      end
-                              end,
-                              [], StateData#state.users);
+                   maps:fold(fun (J, _, Js) ->
+                                     case J of
+                                         {U, S, _} -> [J | Js];
+                                         _ -> Js
+                                     end
+                             end, [], StateData#state.users);
              _ ->
-                 case (?DICT):is_key(LJID, StateData#state.users) of
+                 case maps:is_key(LJID, StateData#state.users) of
                    true -> [LJID];
                    _ -> []
                  end
            end,
     lists:foreach(fun (J) ->
-                         send_new_presence1(J, Reason, false, StateData,
-                                            OldStateData)
+                         send_new_presence(J, Reason, false, StateData,
+                                           OldStateData)
                  end,
                  LJIDs).
 
@@ -2234,14 +2200,6 @@ send_new_presence(NJID, StateData, OldStateData) ->
 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;
-       false -> send_new_presence1(NJID, Reason, IsInitialPresence, StateData,
-                                   OldStateData)
-    end.
-
 -spec is_ra_changed(jid(), boolean(), state(), state()) -> boolean().
 is_ra_changed(_, _IsInitialPresence = true, _, _) ->
     false;
@@ -2257,32 +2215,31 @@ is_ra_changed(JID, _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) ->
+-spec send_new_presence(jid(), binary(), boolean(), state(), state()) -> ok.
+send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
     LNJID = jid:tolower(NJID),
-    #user{nick = Nick} = (?DICT):fetch(LNJID, StateData#state.users),
+    #user{nick = Nick} = maps:get(LNJID, StateData#state.users),
     LJID = find_jid_by_nick(Nick, StateData),
-    {ok,
-     #user{jid = RealJID, role = Role0,
-          last_presence = Presence0} = UserInfo} =
-       (?DICT):find(jid:tolower(LJID),
-                    StateData#state.users),
+    #user{jid = RealJID, role = Role0,
+         last_presence = Presence0} = UserInfo =
+       maps:get(jid:tolower(LJID), StateData#state.users),
     {Role1, Presence1} =
         case presence_broadcast_allowed(NJID, StateData) of
             true -> {Role0, Presence0};
             false -> {none, #presence{type = unavailable}}
         end,
     Affiliation = get_affiliation(LJID, StateData),
-    UserList =
-        case not (presence_broadcast_allowed(NJID, StateData) orelse
-             presence_broadcast_allowed(NJID, OldStateData)) of
+    UserMap =
+        case is_room_overcrowded(StateData) orelse
+            (not (presence_broadcast_allowed(NJID, StateData) orelse
+                  presence_broadcast_allowed(NJID, OldStateData))) of
             true ->
-                [{LNJID, UserInfo}];
+                #{LNJID => UserInfo};
             false ->
-                (?DICT):to_list(get_users_and_subscribers(StateData))
+                get_users_and_subscribers(StateData)
         end,
-    lists:foreach(
-      fun({LUJID, Info}) ->
+    maps:fold(
+      fun(LUJID, Info, _) ->
              IsSelfPresence = LNJID == LUJID,
              {Role, Presence} = if IsSelfPresence -> {Role0, Presence0};
                                    true -> {Role1, Presence1}
@@ -2321,8 +2278,7 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
                 true ->
                      ok
              end
-      end,
-      UserList).
+      end, ok, UserMap).
 
 -spec send_existing_presences(jid(), state()) -> ok.
 send_existing_presences(ToJID, StateData) ->
@@ -2334,15 +2290,13 @@ send_existing_presences(ToJID, StateData) ->
 -spec send_existing_presences1(jid(), state()) -> ok.
 send_existing_presences1(ToJID, StateData) ->
     LToJID = jid:tolower(ToJID),
-    {ok, #user{jid = RealToJID, role = Role}} =
-       (?DICT):find(LToJID, StateData#state.users),
-    lists:foreach(
-      fun({FromNick, _Users}) ->
+    #user{jid = RealToJID, role = Role} = maps:get(LToJID, StateData#state.users),
+    maps:fold(
+      fun(FromNick, _Users, _) ->
              LJID = find_jid_by_nick(FromNick, StateData),
              #user{jid = FromJID, role = FromRole,
                    last_presence = Presence} =
-                 (?DICT):fetch(jid:tolower(LJID),
-                               StateData#state.users),
+                 maps:get(jid:tolower(LJID), StateData#state.users),
              PresenceBroadcast =
                  lists:member(
                    FromRole, (StateData#state.config)#config.presence_broadcast),
@@ -2364,41 +2318,34 @@ send_existing_presences1(ToJID, StateData) ->
                      send_wrapped(jid:replace_resource(StateData#state.jid, FromNick),
                                   RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData)
              end
-      end,
-      (?DICT):to_list(StateData#state.nicks)).
+      end, ok, 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),
-    Users = (?DICT):update(LJID,
-                          fun (#user{} = User) -> User#user{nick = Nick} end,
-                          State#state.users),
-    OldNickUsers = (?DICT):fetch(OldNick, State#state.nicks),
-    NewNickUsers = case (?DICT):find(Nick, State#state.nicks) of
-                      {ok, U} -> U;
-                      error -> []
-                  end,
+    #user{nick = OldNick} = maps:get(LJID, State#state.users),
+    Users = maps:update_with(LJID,
+                            fun (#user{} = User) -> User#user{nick = Nick} end,
+                            State#state.users),
+    OldNickUsers = maps:get(OldNick, State#state.nicks),
+    NewNickUsers = maps:get(Nick, State#state.nicks, []),
     Nicks = case OldNickUsers of
                [LJID] ->
-                   (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]],
-                                 (?DICT):erase(OldNick, State#state.nicks));
+                   maps:put(Nick, [LJID | NewNickUsers -- [LJID]],
+                            maps:remove(OldNick, State#state.nicks));
                [_ | _] ->
-                   (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]],
-                                 (?DICT):store(OldNick, OldNickUsers -- [LJID],
-                                               State#state.nicks))
+                   maps:put(Nick, [LJID | NewNickUsers -- [LJID]],
+                            maps:put(OldNick, OldNickUsers -- [LJID],
+                                     State#state.nicks))
            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),
-    OldNickUsers = (?DICT):fetch(OldNick, StateData#state.nicks),
-    NewNickUsers = case (?DICT):find(Nick, StateData#state.nicks) of
-                      {ok, U} -> U;
-                      error -> []
-                  end,
+    #user{nick = OldNick} = maps:get(LJID, StateData#state.users),
+    OldNickUsers = maps:get(OldNick, StateData#state.nicks),
+    NewNickUsers = maps:get(Nick, StateData#state.nicks, []),
     SendOldUnavailable = length(OldNickUsers) == 1,
     SendNewAvailable = SendOldUnavailable orelse NewNickUsers == [],
     NewStateData = set_nick(JID, Nick, StateData),
@@ -2414,14 +2361,12 @@ change_nick(JID, Nick, StateData) ->
 -spec send_nick_changing(jid(), binary(), state(), boolean(), boolean()) -> ok.
 send_nick_changing(JID, OldNick, StateData,
                   SendOldUnavailable, SendNewAvailable) ->
-    {ok,
-     #user{jid = RealJID, nick = Nick, role = Role,
-          last_presence = Presence}} =
-       (?DICT):find(jid:tolower(JID),
-                    StateData#state.users),
+    #user{jid = RealJID, nick = Nick, role = Role,
+         last_presence = Presence} =
+       maps:get(jid:tolower(JID), StateData#state.users),
     Affiliation = get_affiliation(JID, StateData),
-    lists:foreach(
-      fun({LJID, Info}) when Presence /= undefined ->
+    maps:fold(
+      fun(LJID, Info, _) when Presence /= undefined ->
              IsSelfPresence = LJID == jid:tolower(JID),
              Item0 = #muc_item{affiliation = Affiliation, role = Role},
              Item = case Info#user.role == moderator orelse
@@ -2456,24 +2401,23 @@ send_nick_changing(JID, OldNick, StateData,
                        StateData);
                 true -> ok
              end;
-        (_) ->
+        (_, _, _) ->
              ok
-      end,
-                 ?DICT:to_list(get_users_and_subscribers(StateData))).
+      end, ok, get_users_and_subscribers(StateData)).
 
 -spec maybe_send_affiliation(jid(), affiliation(), state()) -> ok.
 maybe_send_affiliation(JID, Affiliation, StateData) ->
     LJID = jid:tolower(JID),
     Users = get_users_and_subscribers(StateData),
     IsOccupant = case LJID of
-                  {LUser, LServer, <<"">>} ->
-                      not (?DICT):is_empty(
-                            (?DICT):filter(fun({U, S, _}, _) ->
-                                                   U == LUser andalso
-                                                     S == LServer
-                                           end, Users));
-                  {_LUser, _LServer, _LResource} ->
-                      (?DICT):is_key(LJID, Users)
+                    {LUser, LServer, <<"">>} ->
+                        #{} /= maps:filter(
+                                 fun({U, S, _}, _) ->
+                                         U == LUser andalso
+                                             S == LServer
+                                 end, Users);
+                    {_LUser, _LServer, _LResource} ->
+                        maps:is_key(LJID, Users)
                 end,
     case IsOccupant of
       true ->
@@ -2492,11 +2436,11 @@ send_affiliation(JID, Affiliation, StateData) ->
     Users = get_users_and_subscribers(StateData),
     Recipients = case (StateData#state.config)#config.anonymous of
                   true ->
-                      (?DICT):filter(fun(_, #user{role = moderator}) ->
-                                             true;
-                                        (_, _) ->
-                                             false
-                                     end, Users);
+                      maps:filter(fun(_, #user{role = moderator}) ->
+                                          true;
+                                     (_, _) ->
+                                          false
+                                  end, Users);
                   false ->
                       Users
                 end,
@@ -2690,7 +2634,7 @@ user_to_item(#user{role = Role, nick = Nick, jid = JID},
 search_role(Role, StateData) ->
     lists:filter(fun ({_, #user{role = R}}) -> Role == R
                 end,
-                (?DICT):to_list(StateData#state.users)).
+                maps:to_list(StateData#state.users)).
 
 -spec search_affiliation(affiliation(), state()) ->
                         [{ljid(),
@@ -2720,8 +2664,7 @@ search_affiliation_fallback(Affiliation, StateData) ->
                  {A1, _Reason} -> Affiliation == A1;
                  _ -> Affiliation == A
              end
-      end,
-      (?DICT):to_list(StateData#state.affiliations)).
+      end, maps:to_list(StateData#state.affiliations)).
 
 -spec process_admin_items_set(jid(), [muc_item()], binary(),
                              #state{}) -> {result, undefined, #state{}} |
@@ -3087,23 +3030,21 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
                      StateData) ->
     LJID = jid:tolower(JID),
     LJIDs = case LJID of
-             {U, S, <<"">>} ->
-                 (?DICT):fold(fun (J, _, Js) ->
-                                      case J of
-                                        {U, S, _} -> [J | Js];
-                                        _ -> Js
-                                      end
-                              end,
-                              [], StateData#state.users);
-             _ ->
-                 case (?DICT):is_key(LJID, StateData#state.users) of
-                   true -> [LJID];
-                   _ -> []
-                 end
+               {U, S, <<"">>} ->
+                   maps:fold(fun (J, _, Js) ->
+                                     case J of
+                                         {U, S, _} -> [J | Js];
+                                         _ -> Js
+                                     end
+                             end, [], StateData#state.users);
+               _ ->
+                   case maps:is_key(LJID, StateData#state.users) of
+                       true -> [LJID];
+                       _ -> []
+                   end
            end,
     lists:foreach(fun (J) ->
-                         {ok, #user{nick = Nick}} = (?DICT):find(J,
-                                                                 StateData#state.users),
+                         #user{nick = Nick} = maps:get(J, StateData#state.users),
                          add_to_log(kickban, {Nick, Reason, Code}, StateData),
                          tab_remove_online_user(J, StateData),
                          send_kickban_presence1(UJID, J, Reason, Code,
@@ -3115,12 +3056,10 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
                             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),
+    #user{jid = RealJID, nick = Nick} = maps:get(jid:tolower(UJID), StateData#state.users),
     ActorNick = get_actor_nick(MJID, StateData),
-    lists:foreach(
-      fun({LJID, Info}) ->
+    maps:fold(
+      fun(LJID, Info, _) ->
              IsSelfPresence = jid:tolower(UJID) == LJID,
              Item0 = #muc_item{affiliation = Affiliation,
                                role = none},
@@ -3152,16 +3091,15 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
                 true ->
                      ok
              end
-      end,
-                 (?DICT):to_list(get_users_and_subscribers(StateData))).
+      end, ok, get_users_and_subscribers(StateData)).
 
 -spec get_actor_nick(undefined | jid(), state()) -> binary().
 get_actor_nick(undefined, _StateData) ->
     <<"">>;
 get_actor_nick(MJID, StateData) ->
-    case (?DICT):find(jid:tolower(MJID), StateData#state.users) of
-       {ok, #user{nick = ActorNick}} -> ActorNick;
-       _ -> <<"">>
+    try maps:get(jid:tolower(MJID), StateData#state.users) of
+       #user{nick = ActorNick} -> ActorNick
+    catch _:_ -> <<"">>
     end.
 
 convert_legacy_fields(Fs) ->
@@ -3416,7 +3354,7 @@ set_config(Options, StateData, Lang) ->
                   {_, _} -> roomconfig_change
               end,
        Users = [{U#user.jid, U#user.nick, U#user.role}
-                || {_, U} <- (?DICT):to_list(StateData#state.users)],
+                || U <- maps:values(StateData#state.users)],
        add_to_log(Type, Users, NSD),
        Res
     catch  _:{badmatch, {error, #stanza_error{}} = Err} ->
@@ -3539,15 +3477,15 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
                  _ -> [104]
                end,
     if Codes /= [] ->
-           lists:foreach(
-             fun({_LJID, #user{jid = JID}}) ->
+           maps:fold(
+             fun(_LJID, #user{jid = JID}, _) ->
                      send_self_presence(JID, StateData#state{config = New})
-             end, ?DICT:to_list(StateData#state.users)),
+             end, ok, StateData#state.users),
            Message = #message{type = groupchat,
                               id = p1_rand:get_string(),
                               sub_els = [#muc_user{status_codes = Codes}]},
            send_wrapped_multiple(StateData#state.jid,
-                         get_users_and_subscribers(StateData),
+                                 get_users_and_subscribers(StateData),
                                  Message,
                                  ?NS_MUCSUB_NODES_CONFIG,
                                  StateData);
@@ -3557,17 +3495,16 @@ send_config_change_info(New, #state{config = Old} = 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(undefined, JID, <<"">>,
-                                                         322, SD),
-                             set_role(JID, none, SD);
-                         _ -> SD
-                       end
-               end,
-               StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
+    maps:fold(
+      fun(_LJID, #user{jid = JID}, SD) ->
+             Affiliation = get_affiliation(JID, SD),
+             case Affiliation of
+                 none ->
+                     catch send_kickban_presence(undefined, JID, <<"">>, 322, SD),
+                     set_role(JID, none, SD);
+                 _ -> SD
+             end
+      end, StateData, get_users_and_subscribers(StateData)).
 
 -spec set_opts([{atom(), any()}], state()) -> state().
 set_opts([], StateData) ->
@@ -3704,18 +3641,18 @@ set_opts([{Opt, Val} | Opts], StateData) ->
                      lists:foldl(
                        fun({JID, Nick, Nodes}, {SubAcc, NickAcc}) ->
                                BareJID = jid:remove_resource(JID),
-                               {?DICT:store(
-                                   jid:tolower(BareJID),
-                                   #subscriber{jid = BareJID,
-                                               nick = Nick,
-                                               nodes = Nodes},
-                                   SubAcc),
-                                ?DICT:store(Nick, [jid:tolower(BareJID)], NickAcc)}
-                       end, {?DICT:new(), ?DICT:new()}, Val),
+                               {maps:put(
+                                  jid:tolower(BareJID),
+                                  #subscriber{jid = BareJID,
+                                              nick = Nick,
+                                              nodes = Nodes},
+                                  SubAcc),
+                                maps:put(Nick, [jid:tolower(BareJID)], NickAcc)}
+                       end, {#{}, #{}}, Val),
                  StateData#state{subscribers = Subscribers,
                                  subscriber_nicks = Nicks};
            affiliations ->
-               StateData#state{affiliations = (?DICT):from_list(Val)};
+               StateData#state{affiliations = maps:from_list(Val)};
            subject ->
                  Subj = if Val == <<"">> -> [];
                            is_binary(Val) -> [#text{data = Val}];
@@ -3747,7 +3684,7 @@ set_vcard_xupdate(State) ->
 -spec make_opts(state()) -> [{atom(), any()}].
 make_opts(StateData) ->
     Config = StateData#state.config,
-    Subscribers = (?DICT):fold(
+    Subscribers = maps:fold(
                    fun(_LJID, Sub, Acc) ->
                            [{Sub#subscriber.jid,
                              Sub#subscriber.nick,
@@ -3782,7 +3719,7 @@ make_opts(StateData) ->
      {captcha_whitelist,
       (?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
      {affiliations,
-      (?DICT):to_list(StateData#state.affiliations)},
+      maps:to_list(StateData#state.affiliations)},
      {subject, StateData#state.subject},
      {subject_author, StateData#state.subject_author},
      {subscribers, Subscribers}].
@@ -3819,8 +3756,8 @@ config_fields() ->
 -spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
 destroy_room(DEl, StateData) ->
     Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
-    lists:foreach(
-      fun({_LJID, Info}) ->
+    maps:fold(
+      fun(_LJID, Info, _) ->
              Nick = Info#user.nick,
              Item = #muc_item{affiliation = none,
                               role = none},
@@ -3831,8 +3768,7 @@ destroy_room(DEl, StateData) ->
              send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
                           Info#user.jid, Packet,
                           ?NS_MUCSUB_NODES_CONFIG, StateData)
-      end,
-                 (?DICT):to_list(get_users_and_subscribers(StateData))),
+      end, ok, get_users_and_subscribers(StateData)),
     case (StateData#state.config)#config.persistent of
       true ->
          mod_muc:forget_room(StateData#state.server_host,
@@ -3935,7 +3871,7 @@ iq_disco_info_extras(Lang, StateData, Static) ->
          end,
     Fs3 = case Static of
              false ->
-                 [{occupants, ?DICT:size(StateData#state.nicks)}|Fs2];
+                 [{occupants, maps:size(StateData#state.nicks)}|Fs2];
              true ->
                  Fs2
          end,
@@ -4035,8 +3971,8 @@ process_iq_mucsub(From,
                      sub_els = [#muc_subscribe{nick = Nick}]} = Packet,
                  StateData) ->
     LBareJID = jid:tolower(jid:remove_resource(From)),
-    case (?DICT):find(LBareJID, StateData#state.subscribers) of
-       {ok, #subscriber{nick = Nick1}} when Nick1 /= Nick ->
+    try maps:get(LBareJID, StateData#state.subscribers) of
+       #subscriber{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,
@@ -4052,11 +3988,11 @@ process_iq_mucsub(From,
                    NewStateData = set_subscriber(From, Nick, Nodes, StateData),
                    {result, subscribe_result(Packet), NewStateData}
            end;
-       {ok, #subscriber{}} ->
+       #subscriber{} ->
            Nodes = get_subscription_nodes(Packet),
            NewStateData = set_subscriber(From, Nick, Nodes, StateData),
-           {result, subscribe_result(Packet), NewStateData};
-       error ->
+           {result, subscribe_result(Packet), NewStateData}
+    catch _:_ ->
            SD2 = StateData#state{config = (StateData#state.config)#config{allow_subscription = true}},
            add_new_user(From, Nick, Packet, SD2)
     end;
@@ -4077,10 +4013,10 @@ process_iq_mucsub(From, #iq{type = set, lang = Lang,
 process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]},
                  StateData) ->
     LBareJID = jid:tolower(jid:remove_resource(From)),
-    case ?DICT:find(LBareJID, StateData#state.subscribers) of
-       {ok, #subscriber{nick = Nick}} ->
-           Nicks = ?DICT:erase(Nick, StateData#state.subscriber_nicks),
-           Subscribers = ?DICT:erase(LBareJID, StateData#state.subscribers),
+    try maps:get(LBareJID, StateData#state.subscribers) of
+       #subscriber{nick = Nick} ->
+           Nicks = maps:remove(Nick, StateData#state.subscriber_nicks),
+           Subscribers = maps:remove(LBareJID, StateData#state.subscribers),
            NewStateData = StateData#state{subscribers = Subscribers,
                                           subscriber_nicks = Nicks},
            store_room(NewStateData, [{del_subscription, LBareJID}]),
@@ -4089,8 +4025,8 @@ process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]},
                {stop, normal, _} -> stop;
                {next_state, normal_state, SD} -> SD
            end,
-           {result, undefined, NewStateData2};
-       _ ->
+           {result, undefined, NewStateData2}
+       catch _:_ ->
            {result, undefined, StateData}
     end;
 process_iq_mucsub(From, #iq{type = get, lang = Lang,
@@ -4099,7 +4035,7 @@ process_iq_mucsub(From, #iq{type = get, lang = Lang,
     FAffiliation = get_affiliation(From, StateData),
     FRole = get_role(From, StateData),
     if FRole == moderator; FAffiliation == owner; FAffiliation == admin ->
-           Subs = dict:fold(
+           Subs = maps:fold(
                     fun(_, #subscriber{jid = J, nodes = Nodes}, Acc) ->
                             [#muc_subscription{jid = J, events = Nodes}|Acc]
                     end, [], StateData#state.subscribers),
@@ -4114,8 +4050,8 @@ process_iq_mucsub(_From, #iq{type = get, lang = Lang}, _StateData) ->
 
 remove_subscriptions(StateData) ->
     if not (StateData#state.config)#config.allow_subscription ->
-           StateData#state{subscribers = ?DICT:new(),
-                           subscriber_nicks = ?DICT:new()};
+           StateData#state{subscribers = #{},
+                           subscriber_nicks = #{}};
        true ->
            StateData
     end.
@@ -4166,12 +4102,12 @@ get_roomdesc_tail(StateData, Lang) ->
             true -> <<"">>;
             _ -> translate:translate(Lang, <<"private, ">>)
           end,
-    Len = (?DICT):size(StateData#state.nicks),
+    Len = maps:size(StateData#state.nicks),
     <<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
 
 -spec get_mucroom_disco_items(state()) -> disco_items().
 get_mucroom_disco_items(StateData) ->
-    Items = ?DICT:fold(
+    Items = maps:fold(
               fun(Nick, _, Acc) ->
                       [#disco_item{jid = jid:make(StateData#state.room,
                                                   StateData#state.host,
@@ -4369,7 +4305,7 @@ store_room(StateData, ChangesHints) ->
 
 -spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok.
 send_subscriptions_change_notifications(From, Nick, Type, State) ->
-    ?DICT:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
+    maps:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
                    case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of
                        true ->
                            ShowJid = case (State#state.config)#config.anonymous == false orelse
@@ -4405,23 +4341,23 @@ send_subscriptions_change_notifications(From, Nick, Type, State) ->
 send_wrapped(From, To, Packet, Node, State) ->
     LTo = jid:tolower(To),
     LBareTo = jid:tolower(jid:remove_resource(To)),
-    IsOffline = case ?DICT:find(LTo, State#state.users) of
-                   {ok, #user{last_presence = undefined}} -> true;
+    IsOffline = case maps:get(LTo, State#state.users, error) of
+                   #user{last_presence = undefined} -> true;
                    error -> true;
                    _ -> false
                end,
     if IsOffline ->
-           case ?DICT:find(LBareTo, State#state.subscribers) of
-               {ok, #subscriber{nodes = Nodes, jid = JID}} ->
-           case lists:member(Node, Nodes) of
-               true ->
+           try maps:get(LBareTo, State#state.subscribers) of
+               #subscriber{nodes = Nodes, jid = JID} ->
+                   case lists:member(Node, Nodes) of
+                       true ->
                            NewPacket = wrap(From, JID, Packet, Node),
                            ejabberd_router:route(
                              xmpp:set_from_to(NewPacket, State#state.jid, JID));
-               false ->
-                   ok
-           end;
-       _ ->
+                       false ->
+                           ok
+                   end
+           catch _:_ ->
                    ok
            end;
        true ->
@@ -4461,17 +4397,12 @@ wrap(From, To, Packet, Node) ->
                                            id = p1_rand:get_string(),
                                            sub_els = [El]}]}}]}.
 
-%% -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(), dict:dict(), stanza(), binary(), state()) -> ok.
+-spec send_wrapped_multiple(jid(), map(), stanza(), binary(), state()) -> ok.
 send_wrapped_multiple(From, Users, Packet, Node, State) ->
-    lists:foreach(
-      fun({_, #user{jid = To}}) ->
+    maps:fold(
+      fun(_, #user{jid = To}, _) ->
              send_wrapped(From, To, Packet, Node, State)
-      end, ?DICT:to_list(Users)).
+      end, ok, Users).
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% Detect messange stanzas that don't have meaninful content