diff options
-rw-r--r-- | mix.exs | 2 | ||||
-rw-r--r-- | mix.lock | 23 | ||||
-rw-r--r-- | rebar.config | 18 | ||||
-rw-r--r-- | src/ejabberd_admin.erl | 1 | ||||
-rw-r--r-- | src/ejabberd_c2s.erl | 19 | ||||
-rw-r--r-- | src/ejabberd_commands.erl | 7 | ||||
-rw-r--r-- | src/ejabberd_oauth.erl | 4 | ||||
-rw-r--r-- | src/mod_announce.erl | 12 | ||||
-rw-r--r-- | src/mod_client_state.erl | 51 | ||||
-rw-r--r-- | src/mod_mam.erl | 102 | ||||
-rw-r--r-- | src/mod_muc_admin.erl | 72 | ||||
-rw-r--r-- | src/mod_muc_room.erl | 74 | ||||
-rw-r--r-- | src/mod_offline.erl | 55 |
13 files changed, 283 insertions, 157 deletions
@@ -63,7 +63,7 @@ defmodule Ejabberd.Mixfile do # version 3.20: {:relx, "~> 3.19.0", only: :dev}, {:meck, "~> 0.8.4", only: :test}, - {:moka, github: "processone/moka", tag: "1.0.5b", only: :test}] + {:moka, github: "processone/moka", tag: "1.0.5c", only: :test}] end defp package do @@ -1,29 +1,30 @@ %{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []}, - "cache_tab": {:hex, :cache_tab, "1.0.3", "0e3c40dde2fe2a6a4db241d7583cea0cc1bcf29e546a0a22f15b75366b2f336e", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, + "cache_tab": {:hex, :cache_tab, "1.0.4", "3fd2b1ab40c36e7830a4e09e836c6b0fa89191cd4e5fd471873e4eb42f5cd37c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, "cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []}, "eredis": {:hex, :eredis, "1.0.8", "ab4fda1c4ba7fbe6c19c26c249dc13da916d762502c4b4fa2df401a8d51c5364", [:rebar], []}, "erlware_commons": {:hex, :erlware_commons, "0.19.0", "7b43caf2c91950c5f60dc20451e3c3afba44d3d4f7f27bcdc52469285a5a3e70", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]}, - "esip": {:hex, :esip, "1.0.7", "f75f6a5cac6814e506f0ff96141fbe276dee3261fca1471c8edfdde25b74f877", [:rebar3], [{:fast_tls, "1.0.6", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}, {:stun, "1.0.6", [hex: :stun, optional: false]}]}, + "esip": {:hex, :esip, "1.0.8", "69885a6c07964aabc6c077fe1372aa810a848bd3d9a415b160dabdce9c7a79b5", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}, {:stun, "1.0.7", [hex: :stun, optional: false]}]}, "exrm": {:hex, :exrm, "1.0.8", "5aa8990cdfe300282828b02cefdc339e235f7916388ce99f9a1f926a9271a45d", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]}, "ezlib": {:hex, :ezlib, "1.0.1", "add8b2770a1a70c174aaea082b4a8668c0c7fdb03ee6cc81c6c68d3a6c3d767d", [:rebar3], []}, - "fast_tls": {:hex, :fast_tls, "1.0.6", "750a74aabb05056f0f222910f0955883649e6c5d67df6ca504ff676160d22b89", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, - "fast_xml": {:hex, :fast_xml, "1.1.14", "23d4de66e645bca1d8a557444e83062cc42f235d7a7e2d4072d525bac3986f04", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, - "fast_yaml": {:hex, :fast_yaml, "1.0.5", "a67772c75abb84181c6c9899e1f988b08ac214ea0d764ff1f9889bb7e27f74d4", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, + "fast_tls": {:hex, :fast_tls, "1.0.7", "9b72ecfcdcad195ab072c196fab8334f49d8fea76bf1a51f536d69e7527d902a", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, + "fast_xml": {:hex, :fast_xml, "1.1.15", "6d23eb7f874e1357cf80a48d75a7bd0c8f6318029dc4b70122e9f54911f57f83", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, + "fast_yaml": {:hex, :fast_yaml, "1.0.6", "3fe6feb7935ae8028b337e53e1db29e73ad3bca8041108f6a8f73b7175ece75c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, "getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []}, "goldrush": {:hex, :goldrush, "0.1.7", "349a351d17c71c2fdaa18a6c2697562abe136fec945f147b381f0cf313160228", [:rebar3], []}, - "iconv": {:hex, :iconv, "1.0.1", "dbb8700070577e7a021a095cc5ead221069a0c4034bfadca2516c1f1109ee7fd", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, + "iconv": {:hex, :iconv, "1.0.2", "a0792f06ab4b5ea1b5bb49789405739f1281a91c44cf3879cb70e4d777666217", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, "jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:rebar, :make], []}, "lager": {:hex, :lager, "3.0.2", "25dc81bc3659b62f5ab9bd073e97ddd894fc4c242019fccef96f3889d7366c97", [:rebar3], [{:goldrush, "0.1.7", [hex: :goldrush, optional: false]}]}, "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []}, - "moka": {:git, "https://github.com/processone/moka.git", "768efea96443c57125e6247dbebee687f17be149", [tag: "1.0.5b"]}, + "moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.1", "d2be1cfc71bb4f1391090b62b74c3f5cb8e7a45b0076b8cb290cd6b2856c581b", [:rebar3], []}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.0", "ca525c42878eac095e5feb19563acc9915c845648f48fdec7ba6266c625d4ac7", [:rebar3], []}, - "p1_utils": {:hex, :p1_utils, "1.0.4", "7face65db102b5d1ebe7ad3c7517c5ee8cfbe174c6658e3affbb00eb66e06787", [:rebar3], []}, + "p1_utils": {:hex, :p1_utils, "1.0.5", "3e698354fdc1fea5491d991457b0cb986c0a00a47d224feb841dc3ec82b9f721", [:rebar3], []}, "p1_xmlrpc": {:hex, :p1_xmlrpc, "1.15.1", "a382b62dc21bb372281c2488f99294d84f2b4020ed0908a1c4ad710ace3cf35a", [:rebar3], []}, + "pc": {:hex, :pc, "1.2.0", "5e07731d1f8bf997a8d0271510983e570f910b42cd59bf612e664ad6dc35742e", [:rebar3], []}, "providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]}, "relx": {:hex, :relx, "3.19.0", "286dd5244b4786f56aac75d5c8e2d1fb4cfd306810d4ec8548f3ae1b3aadb8f7", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.19.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]}, - "samerlib": {:git, "https://github.com/processone/samerlib", "9158f65d18ec63f8b409543b6fb46dd5fce46160", [tag: "0.8.0b"]}, + "samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]}, "sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], []}, - "stringprep": {:hex, :stringprep, "1.0.5", "f29395275c35af5051b29bf875b44ac632dc4d0287880f0e143b536c61fd0ed5", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}, - "stun": {:hex, :stun, "1.0.6", "1ca9dea574e09f60971bd8de9cb7e34f327cbf435462cf56aa30f05c1ee2f231", [:rebar3], [{:fast_tls, "1.0.6", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}} + "stringprep": {:hex, :stringprep, "1.0.6", "1cf1c439eb038aa590da5456e019f86afbfbfeb5a2d37b6e5f873041624c6701", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, + "stun": {:hex, :stun, "1.0.7", "904dc6f26a3c30c54881c4c3003699f2a4968067ee6b3aecdf9895aad02df75e", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}} diff --git a/rebar.config b/rebar.config index 693f4f58b..ab5562858 100644 --- a/rebar.config +++ b/rebar.config @@ -9,13 +9,13 @@ {deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.5"}}}, - {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.3"}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.6"}}}, - {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.5"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.14"}}}, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.6"}}}, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.7"}}}, - {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.5"}}}, + {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.4"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.7"}}}, + {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.6"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.15"}}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.7"}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.8"}}}, + {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.6"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.7"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}}, {p1_xmlrpc, ".*", {git, "https://github.com/processone/p1_xmlrpc", {tag, "1.15.1"}}}, @@ -44,11 +44,11 @@ {if_var_true, elixir, {rebar_elixir_plugin, ".*", {git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}, {if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv", - {tag, "1.0.1"}}}}, + {tag, "1.0.2"}}}}, {if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck", {tag, "0.8.4"}}}}, {if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka.git", - {tag, "1.0.5b"}}}}, + {tag, "1.0.5c"}}}}, {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.0.8"}}}}]}. diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 615459f77..ae2fac3e1 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -131,7 +131,6 @@ get_commands_spec() -> #ejabberd_commands{name = register, tags = [accounts], desc = "Register a user", policy = admin, - access = [{mod_register, access, configure}], module = ?MODULE, function = register, args = [{user, binary}, {host, binary}, {password, binary}], result = {res, restuple}}, diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 74b78512a..270ef1dc5 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -2800,8 +2800,7 @@ handle_resume(StateData, Attrs) -> #xmlel{name = <<"r">>, attrs = [{<<"xmlns">>, AttrXmlns}], children = []}), - FlushedState = csi_flush_queue(NewState), - NewStateData = FlushedState#state{csi_state = active}, + NewStateData = csi_flush_queue(NewState), ?INFO_MSG("Resumed session for ~s", [jid:to_string(NewStateData#state.jid)]), {ok, NewStateData}; @@ -3048,13 +3047,13 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) -> pres_timestamp = OldStateData#state.pres_timestamp, privacy_list = OldStateData#state.privacy_list, aux_fields = OldStateData#state.aux_fields, - csi_state = OldStateData#state.csi_state, mgmt_xmlns = OldStateData#state.mgmt_xmlns, mgmt_queue = OldStateData#state.mgmt_queue, mgmt_timeout = OldStateData#state.mgmt_timeout, mgmt_stanzas_in = OldStateData#state.mgmt_stanzas_in, mgmt_stanzas_out = OldStateData#state.mgmt_stanzas_out, - mgmt_state = active}}; + mgmt_state = active, + csi_state = active}}; {error, Msg} -> {error, Msg}; _ -> @@ -3081,20 +3080,22 @@ add_resent_delay_info(#state{server = From}, El, Time) -> %%% XEP-0352 %%%---------------------------------------------------------------------- -csi_filter_stanza(#state{csi_state = CsiState, server = Server} = StateData, - Stanza) -> +csi_filter_stanza(#state{csi_state = CsiState, jid = JID, server = Server} = + StateData, Stanza) -> {StateData1, Stanzas} = ejabberd_hooks:run_fold(csi_filter_stanza, Server, {StateData, [Stanza]}, - [Server, Stanza]), + [Server, JID, Stanza]), StateData2 = lists:foldl(fun(CurStanza, AccState) -> send_stanza(AccState, CurStanza) end, StateData1#state{csi_state = active}, Stanzas), StateData2#state{csi_state = CsiState}. -csi_flush_queue(#state{csi_state = CsiState, server = Server} = StateData) -> +csi_flush_queue(#state{csi_state = CsiState, jid = JID, server = Server} = + StateData) -> {StateData1, Stanzas} = ejabberd_hooks:run_fold(csi_flush_queue, Server, - {StateData, []}, [Server]), + {StateData, []}, + [Server, JID]), StateData2 = lists:foldl(fun(CurStanza, AccState) -> send_stanza(AccState, CurStanza) end, StateData1#state{csi_state = active}, diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index a8b3e25ab..d5649b2d7 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -274,7 +274,6 @@ get_commands_spec() -> args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], result_example = ok}]. init() -> - mnesia:delete_table(ejabberd_commands), mnesia:create_table(ejabberd_commands, [{ram_copies, [node()]}, {local_content, true}, @@ -538,6 +537,9 @@ execute_check_policy( _Auth, _JID, #ejabberd_commands{policy = open} = Command, Arguments) -> do_execute_command(Command, Arguments); execute_check_policy( + noauth, _JID, Command, Arguments) -> + do_execute_command(Command, Arguments); +execute_check_policy( _Auth, _JID, #ejabberd_commands{policy = restricted} = Command, Arguments) -> do_execute_command(Command, Arguments); execute_check_policy( @@ -547,9 +549,6 @@ execute_check_policy( admin, JID, #ejabberd_commands{policy = user} = Command, Arguments) -> execute_check_access(JID, Command, Arguments); execute_check_policy( - noauth, _JID, #ejabberd_commands{policy = user} = Command, Arguments) -> - do_execute_command(Command, Arguments); -execute_check_policy( {User, Server, _, _}, JID, #ejabberd_commands{policy = user} = Command, Arguments) -> execute_check_access(JID, Command, [User, Server | Arguments]). diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index ca0693645..36c679cb6 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -102,7 +102,9 @@ get_commands_spec() -> args = [{jid, string},{ttl, integer}, {scopes, string}], policy = restricted, args_example = ["user@server.com", "connected_users_number;muc_online_rooms"], - args_desc = ["List of scopes to allow, separated by ';'"], + args_desc = ["Jid for which issue token", + "Time to live of generated token in seconds", + "List of scopes to allow, separated by ';'"], result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 52ff2de92..d74c46bf9 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -696,7 +696,7 @@ announce_all(From, To, Packet) -> lists:foreach( fun({User, Server}) -> Dest = jid:make(User, Server, <<>>), - ejabberd_router:route(Local, Dest, Packet) + ejabberd_router:route(Local, Dest, add_store_hint(Packet)) end, ejabberd_auth:get_vh_registered_users(Host)) end. @@ -713,7 +713,7 @@ announce_all_hosts_all(From, To, Packet) -> lists:foreach( fun({User, Server}) -> Dest = jid:make(User, Server, <<>>), - ejabberd_router:route(Local, Dest, Packet) + ejabberd_router:route(Local, Dest, add_store_hint(Packet)) end, ejabberd_auth:dirty_get_registered_users()) end. @@ -899,7 +899,7 @@ send_announcement_to_all(Host, SubjectS, BodyS) -> lists:foreach( fun({U, S, R}) -> Dest = jid:make(U, S, R), - ejabberd_router:route(Local, Dest, Packet) + ejabberd_router:route(Local, Dest, add_store_hint(Packet)) end, Sessions). -spec get_access(global | binary()) -> atom(). @@ -909,6 +909,12 @@ get_access(Host) -> fun(A) -> A end, none). +-spec add_store_hint(xmlel()) -> xmlel(). + +add_store_hint(El) -> + Hint = #xmlel{name = <<"store">>, attrs = [{<<"xmlns">>, ?NS_HINTS}]}, + fxml:append_subtags(El, [Hint]). + %%------------------------------------------------------------------------- export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index 036175cce..9d37d4f5b 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -34,8 +34,8 @@ -export([start/2, stop/1, mod_opt_type/1, depends/2]). %% ejabberd_hooks callbacks. --export([filter_presence/3, filter_chat_states/3, filter_pep/3, filter_other/3, - flush_queue/2, add_stream_feature/2]). +-export([filter_presence/4, filter_chat_states/4, filter_pep/4, filter_other/4, + flush_queue/3, add_stream_feature/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -151,69 +151,70 @@ depends(_Host, _Opts) -> %% ejabberd_hooks callbacks. %%-------------------------------------------------------------------- --spec filter_presence({term(), [xmlel()]}, binary(), xmlel()) +-spec filter_presence({term(), [xmlel()]}, binary(), jid(), xmlel()) -> {term(), [xmlel()]} | {stop, {term(), [xmlel()]}}. -filter_presence({C2SState, _OutStanzas} = Acc, Host, +filter_presence({C2SState, _OutStanzas} = Acc, Host, To, #xmlel{name = <<"presence">>, attrs = Attrs} = Stanza) -> case fxml:get_attr(<<"type">>, Attrs) of {value, Type} when Type /= <<"unavailable">> -> Acc; _ -> - ?DEBUG("Got availability presence stanza", []), + ?DEBUG("Got availability presence stanza for ~s", + [jid:to_string(To)]), queue_add(presence, Stanza, Host, C2SState) end; -filter_presence(Acc, _Host, _Stanza) -> Acc. +filter_presence(Acc, _Host, _To, _Stanza) -> Acc. --spec filter_chat_states({term(), [xmlel()]}, binary(), xmlel()) +-spec filter_chat_states({term(), [xmlel()]}, binary(), jid(), xmlel()) -> {term(), [xmlel()]} | {stop, {term(), [xmlel()]}}. -filter_chat_states({C2SState, _OutStanzas} = Acc, Host, +filter_chat_states({C2SState, _OutStanzas} = Acc, Host, To, #xmlel{name = <<"message">>} = Stanza) -> case jlib:is_standalone_chat_state(Stanza) of true -> From = fxml:get_tag_attr_s(<<"from">>, Stanza), - To = fxml:get_tag_attr_s(<<"to">>, Stanza), - case {jid:from_string(From), jid:from_string(To)} of + case {jid:from_string(From), To} of {#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}} -> %% Don't queue (carbon copies of) chat states from other %% resources, as they might be used to sync the state of %% conversations across clients. Acc; _ -> - ?DEBUG("Got standalone chat state notification", []), + ?DEBUG("Got standalone chat state notification for ~s", + [jid:to_string(To)]), queue_add(chatstate, Stanza, Host, C2SState) end; false -> Acc end; -filter_chat_states(Acc, _Host, _Stanza) -> Acc. +filter_chat_states(Acc, _Host, _To, _Stanza) -> Acc. --spec filter_pep({term(), [xmlel()]}, binary(), xmlel()) +-spec filter_pep({term(), [xmlel()]}, binary(), jid(), xmlel()) -> {term(), [xmlel()]} | {stop, {term(), [xmlel()]}}. -filter_pep({C2SState, _OutStanzas} = Acc, Host, +filter_pep({C2SState, _OutStanzas} = Acc, Host, To, #xmlel{name = <<"message">>} = Stanza) -> case get_pep_node(Stanza) of {value, Node} -> - ?DEBUG("Got PEP notification", []), + ?DEBUG("Got PEP notification for ~s", [jid:to_string(To)]), queue_add({pep, Node}, Stanza, Host, C2SState); false -> Acc end; -filter_pep(Acc, _Host, _Stanza) -> Acc. +filter_pep(Acc, _Host, _To, _Stanza) -> Acc. --spec filter_other({term(), [xmlel()]}, binary(), xmlel()) - -> {stop, {term(), [xmlel()]}}. +-spec filter_other({term(), [xmlel()]}, binary(), jid(), xmlel()) + -> {term(), [xmlel()]}. -filter_other({C2SState, _OutStanzas}, Host, Stanza) -> - ?DEBUG("Won't add stanza to CSI queue", []), +filter_other({C2SState, _OutStanzas}, Host, To, Stanza) -> + ?DEBUG("Won't add stanza for ~s to CSI queue", [jid:to_string(To)]), queue_take(Stanza, Host, C2SState). --spec flush_queue({term(), [xmlel()]}, binary()) -> {term(), [xmlel()]}. +-spec flush_queue({term(), [xmlel()]}, binary(), jid()) -> {term(), [xmlel()]}. -flush_queue({C2SState, _OutStanzas}, Host) -> - ?DEBUG("Going to flush CSI queue", []), +flush_queue({C2SState, _OutStanzas}, Host, JID) -> + ?DEBUG("Going to flush CSI queue of ~s", [jid:to_string(JID)]), Queue = get_queue(C2SState), NewState = set_queue([], C2SState), {NewState, get_stanzas(Queue, Host)}. @@ -249,7 +250,7 @@ queue_add(Type, Stanza, Host, C2SState) -> {stop, {NewState, []}} end. --spec queue_take(xmlel(), binary(), term()) -> {stop, {term(), [xmlel()]}}. +-spec queue_take(xmlel(), binary(), term()) -> {term(), [xmlel()]}. queue_take(Stanza, Host, C2SState) -> From = fxml:get_tag_attr_s(<<"from">>, Stanza), @@ -259,7 +260,7 @@ queue_take(Stanza, Host, C2SState) -> U == LUser andalso S == LServer end, get_queue(C2SState)), NewState = set_queue(Rest, C2SState), - {stop, {NewState, get_stanzas(Selected, Host) ++ [Stanza]}}. + {NewState, get_stanzas(Selected, Host) ++ [Stanza]}. -spec set_queue(csi_queue(), term()) -> term(). diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 7e1460695..6ea757223 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -111,15 +111,12 @@ start(Host, Opts) -> ejabberd_hooks:add(anonymous_purge_hook, Host, ?MODULE, remove_user, 50), case gen_mod:get_opt(assume_mam_usage, Opts, - fun(if_enabled) -> if_enabled; - (on_request) -> on_request; - (never) -> never - end, never) of - never -> - ok; - _ -> + fun(B) when is_boolean(B) -> B end, false) of + true -> ejabberd_hooks:add(message_is_archived, Host, ?MODULE, - message_is_archived, 50) + message_is_archived, 50); + false -> + ok end, ejabberd_commands:register_commands(get_commands_spec()), ok. @@ -164,15 +161,12 @@ stop(Host) -> ejabberd_hooks:delete(anonymous_purge_hook, Host, ?MODULE, remove_user, 50), case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage, - fun(if_enabled) -> if_enabled; - (on_request) -> on_request; - (never) -> never - end, never) of - never -> - ok; - _ -> + fun(B) when is_boolean(B) -> B end, false) of + true -> ejabberd_hooks:delete(message_is_archived, Host, ?MODULE, - message_is_archived, 50) + message_is_archived, 50); + false -> + ok end, ejabberd_commands:unregister_commands(get_commands_spec()), ok. @@ -381,32 +375,13 @@ message_is_archived(true, _C2SState, _Peer, _JID, _Pkt) -> true; message_is_archived(false, C2SState, Peer, #jid{luser = LUser, lserver = LServer}, Pkt) -> - Res = case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage, - fun(if_enabled) -> if_enabled; - (on_request) -> on_request; - (never) -> never - end, never) of - if_enabled -> - case get_prefs(LUser, LServer) of - #archive_prefs{} = P -> - {ok, P}; - error -> - error - end; - on_request -> - Mod = gen_mod:db_mod(LServer, ?MODULE), - cache_tab:lookup(archive_prefs, {LUser, LServer}, - fun() -> - Mod:get_prefs(LUser, LServer) - end); - never -> - error - end, - case Res of - {ok, Prefs} -> + case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage, + fun(B) when is_boolean(B) -> B end, false) of + true -> should_archive(strip_my_archived_tag(Pkt, LServer), LServer) - andalso should_archive_peer(C2SState, Prefs, Peer); - error -> + andalso should_archive_peer(C2SState, get_prefs(LUser, LServer), + Peer); + false -> false end. @@ -580,29 +555,29 @@ parse_query_v0_2(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">> -> + case is_resent(Pkt, LServer) of + true -> false; - _ -> - case is_resent(Pkt, LServer) of - true -> + false -> + case {check_store_hint(Pkt), + fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs)} of + {_Hint, <<"error">>} -> false; - false -> - case check_store_hint(Pkt) of - store -> - true; - no_store -> + {store, _Type} -> + true; + {no_store, _Type} -> + false; + {none, <<"groupchat">>} -> + false; + {none, <<"headline">>} -> + false; + {none, _Type} -> + case fxml:get_subtag_cdata(Pkt, <<"body">>) of + <<>> -> + %% Empty body false; - none -> - case fxml:get_subtag_cdata(Pkt, <<"body">>) of - <<>> -> - %% Empty body - false; - _ -> - true - end + _ -> + true end end end; @@ -1088,10 +1063,7 @@ get_commands_spec() -> result = {res, rescode}}]. mod_opt_type(assume_mam_usage) -> - fun(if_enabled) -> if_enabled; - (on_request) -> on_request; - (never) -> never - end; + fun (B) when is_boolean(B) -> B end; mod_opt_type(cache_life_time) -> fun (I) when is_integer(I), I > 0 -> I end; mod_opt_type(cache_size) -> diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 0b5e79f60..e1d48cdab 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 = {nodes, {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,65 @@ 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{lresource = <<"">>} -> + throw({error, "User's JID should have a resource"}); + UserJID -> + 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; diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 799605c69..87a136853 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -437,35 +437,36 @@ remove_msg_by_node(To, Seq) -> end. need_to_store(LServer, Packet) -> - Type = fxml:get_tag_attr_s(<<"type">>, Packet), - if (Type /= <<"error">>) and (Type /= <<"groupchat">>) - and (Type /= <<"headline">>) -> - case has_offline_tag(Packet) of - false -> - case check_store_hint(Packet) of - store -> + case has_offline_tag(Packet) of + false -> + case {check_store_hint(Packet), + fxml:get_tag_attr_s(<<"type">>, Packet)} of + {_Hint, <<"error">>} -> + false; + {store, _Type} -> + true; + {no_store, _Type} -> + false; + {none, <<"groupchat">>} -> + false; + {none, <<"headline">>} -> + false; + {none, _Type} -> + case gen_mod:get_module_opt( + LServer, ?MODULE, store_empty_body, + fun(V) when is_boolean(V) -> V; + (unless_chat_state) -> unless_chat_state + end, + unless_chat_state) of + true -> true; - no_store -> - false; - none -> - case gen_mod:get_module_opt( - LServer, ?MODULE, store_empty_body, - fun(V) when is_boolean(V) -> V; - (unless_chat_state) -> unless_chat_state - end, - unless_chat_state) of - false -> - fxml:get_subtag(Packet, <<"body">>) /= false; - unless_chat_state -> - not jlib:is_standalone_chat_state(Packet); - true -> - true - end - end; - true -> - false + false -> + fxml:get_subtag(Packet, <<"body">>) /= false; + unless_chat_state -> + not jlib:is_standalone_chat_state(Packet) + end end; - true -> + true -> false end. |