summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adhoc.erl8
-rw-r--r--src/cyrsasl.erl4
-rw-r--r--src/cyrsasl_digest.erl8
-rw-r--r--src/ejabberd_admin.erl5
-rw-r--r--src/ejabberd_app.erl9
-rw-r--r--src/ejabberd_auth_external.erl4
-rw-r--r--src/ejabberd_auth_odbc.erl177
-rw-r--r--src/ejabberd_c2s.erl251
-rw-r--r--src/ejabberd_captcha.erl2
-rw-r--r--src/ejabberd_commands_doc.erl13
-rw-r--r--src/ejabberd_config.erl10
-rw-r--r--src/ejabberd_ctl.erl4
-rw-r--r--src/ejabberd_frontend_socket.erl16
-rw-r--r--src/ejabberd_http.erl12
-rw-r--r--src/ejabberd_http_bind.erl52
-rw-r--r--src/ejabberd_http_ws.erl20
-rw-r--r--src/ejabberd_local.erl5
-rw-r--r--src/ejabberd_logger.erl118
-rw-r--r--src/ejabberd_oauth.erl8
-rw-r--r--src/ejabberd_odbc.erl232
-rw-r--r--src/ejabberd_piefxis.erl111
-rw-r--r--src/ejabberd_receiver.erl30
-rw-r--r--src/ejabberd_router.erl111
-rw-r--r--src/ejabberd_s2s.erl4
-rw-r--r--src/ejabberd_s2s_in.erl44
-rw-r--r--src/ejabberd_s2s_out.erl64
-rw-r--r--src/ejabberd_service.erl22
-rw-r--r--src/ejabberd_sm.erl83
-rw-r--r--src/ejabberd_sm_odbc.erl4
-rw-r--r--src/ejabberd_sm_redis.erl4
-rw-r--r--src/ejabberd_socket.erl16
-rw-r--r--src/ejabberd_sql_pt.erl544
-rw-r--r--src/ejabberd_stun.erl4
-rw-r--r--src/ejabberd_system_monitor.erl12
-rw-r--r--src/ejabberd_web_admin.erl43
-rw-r--r--src/ejabberd_websocket.erl6
-rw-r--r--src/ejabberd_xmlrpc.erl8
-rw-r--r--src/elixir_logger_backend.erl122
-rw-r--r--src/ext_mod.erl9
-rw-r--r--src/jd2ejd.erl14
-rw-r--r--src/jlib.erl54
-rw-r--r--src/mod_admin_extra.erl130
-rw-r--r--src/mod_announce.erl16
-rw-r--r--src/mod_blocking.erl44
-rw-r--r--src/mod_caps.erl40
-rw-r--r--src/mod_carboncopy.erl12
-rw-r--r--src/mod_client_state.erl2
-rw-r--r--src/mod_configure.erl6
-rw-r--r--src/mod_configure2.erl4
-rw-r--r--src/mod_disco.erl12
-rw-r--r--src/mod_echo.erl4
-rw-r--r--src/mod_http_upload.erl74
-rw-r--r--src/mod_http_upload_quota.erl3
-rw-r--r--src/mod_irc.erl24
-rw-r--r--src/mod_irc_connection.erl36
-rw-r--r--src/mod_last.erl28
-rw-r--r--src/mod_mam.erl178
-rw-r--r--src/mod_metrics.erl5
-rw-r--r--src/mod_mix.erl347
-rw-r--r--src/mod_muc.erl54
-rw-r--r--src/mod_muc_admin.erl57
-rw-r--r--src/mod_muc_log.erl16
-rw-r--r--src/mod_muc_room.erl230
-rw-r--r--src/mod_multicast.erl53
-rw-r--r--src/mod_offline.erl526
-rw-r--r--src/mod_pres_counter.erl2
-rw-r--r--src/mod_privacy.erl222
-rw-r--r--src/mod_private.erl67
-rw-r--r--src/mod_proxy65_service.erl8
-rw-r--r--src/mod_pubsub.erl191
-rw-r--r--src/mod_register.erl16
-rw-r--r--src/mod_register_web.erl32
-rw-r--r--src/mod_roster.erl234
-rw-r--r--src/mod_shared_roster.erl8
-rw-r--r--src/mod_shared_roster_ldap.erl1041
-rw-r--r--src/mod_stats.erl4
-rw-r--r--src/mod_vcard.erl149
-rw-r--r--src/mod_vcard_ldap.erl6
-rw-r--r--src/mod_vcard_xupdate.erl4
-rw-r--r--src/node_flat_odbc.erl4
-rw-r--r--src/node_mix.erl167
-rw-r--r--src/node_mix_odbc.erl170
-rw-r--r--src/node_pep.erl2
-rw-r--r--src/odbc_queries.erl764
-rw-r--r--src/prosody2ejabberd.erl341
-rw-r--r--src/pubsub_subscription.erl2
-rw-r--r--src/pubsub_subscription_odbc.erl2
87 files changed, 5084 insertions, 2520 deletions
diff --git a/src/adhoc.erl b/src/adhoc.erl
index d252a6cb..788bf65a 100644
--- a/src/adhoc.erl
+++ b/src/adhoc.erl
@@ -51,9 +51,9 @@
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
- SessionID = xml:get_tag_attr_s(<<"sessionid">>, SubEl),
- Action = xml:get_tag_attr_s(<<"action">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
+ SessionID = fxml:get_tag_attr_s(<<"sessionid">>, SubEl),
+ Action = fxml:get_tag_attr_s(<<"action">>, SubEl),
XData = find_xdata_el(SubEl),
#xmlel{children = AllEls} = SubEl,
Others = case XData of
@@ -76,7 +76,7 @@ find_xdata_el(#xmlel{children = SubEls}) ->
find_xdata_el1([]) -> false;
find_xdata_el1([El | Els]) when is_record(El, xmlel) ->
- case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, El) of
?NS_XDATA -> El;
_ -> find_xdata_el1(Els)
end;
diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
index 5d2d89d6..cc03a49c 100644
--- a/src/cyrsasl.erl
+++ b/src/cyrsasl.erl
@@ -111,12 +111,12 @@ register_mechanism(Mechanism, Module, PasswordType) ->
%%-include("ejabberd.hrl").
%%-include("jlib.hrl").
%%check_authzid(_State, Props) ->
-%% AuthzId = xml:get_attr_s(authzid, Props),
+%% AuthzId = fxml:get_attr_s(authzid, Props),
%% case jid:from_string(AuthzId) of
%% error ->
%% {error, "invalid-authzid"};
%% JID ->
-%% LUser = jid:nodeprep(xml:get_attr_s(username, Props)),
+%% LUser = jid:nodeprep(fxml:get_attr_s(username, Props)),
%% {U, S, R} = jid:tolower(JID),
%% case R of
%% "" ->
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
index 8ccd9095..976e49e8 100644
--- a/src/cyrsasl_digest.erl
+++ b/src/cyrsasl_digest.erl
@@ -83,9 +83,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
bad -> {error, <<"bad-protocol">>};
KeyVals ->
DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
- %DigestURI = xml:get_attr_s(<<"digest-uri">>, KeyVals),
+ %DigestURI = fxml:get_attr_s(<<"digest-uri">>, KeyVals),
UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
- %UserName = xml:get_attr_s(<<"username">>, KeyVals),
+ %UserName = fxml:get_attr_s(<<"username">>, KeyVals),
case is_digesturi_valid(DigestURI, State#state.host,
State#state.hostfqdn)
of
@@ -97,13 +97,13 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
{error, <<"not-authorized">>, UserName};
true ->
AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
- %AuthzId = xml:get_attr_s(<<"authzid">>, KeyVals),
+ %AuthzId = fxml:get_attr_s(<<"authzid">>, KeyVals),
case (State#state.get_password)(UserName) of
{false, _} -> {error, <<"not-authorized">>, UserName};
{Passwd, AuthModule} ->
case (State#state.check_password)(UserName, <<"">>,
proplists:get_value(<<"response">>, KeyVals, <<>>),
- %xml:get_attr_s(<<"response">>, KeyVals),
+ %fxml:get_attr_s(<<"response">>, KeyVals),
fun (PW) ->
response(KeyVals,
UserName,
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index 4aa5faed..c9e5b178 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -201,6 +201,11 @@ get_commands_spec() ->
module = ejabberd_auth_odbc, function = convert_to_scram,
args = [{host, binary}], result = {res, rescode}},
+ #ejabberd_commands{name = import_prosody, tags = [mnesia, odbc, riak],
+ desc = "Import data from Prosody",
+ module = prosody2ejabberd, function = from_dir,
+ args = [{dir, string}], result = {res, rescode}},
+
#ejabberd_commands{name = convert_to_yaml, tags = [config],
desc = "Convert the input file from Erlang to YAML format",
module = ejabberd_config, function = convert_to_yaml,
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 628b4a8a..e493eac0 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -252,11 +252,10 @@ start_apps() ->
crypto:start(),
ejabberd:start_app(sasl),
ejabberd:start_app(ssl),
- ejabberd:start_app(p1_yaml),
- ejabberd:start_app(p1_tls),
- ejabberd:start_app(p1_xml),
- ejabberd:start_app(p1_stringprep),
- ejabberd:start_app(p1_zlib),
+ ejabberd:start_app(fast_yaml),
+ ejabberd:start_app(fast_tls),
+ ejabberd:start_app(fast_xml),
+ ejabberd:start_app(stringprep),
ejabberd:start_app(cache_tab).
opt_type(net_ticktime) ->
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index 2a1cbf08..2944ac3f 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -222,7 +222,7 @@ check_password_cache(User, Server, Password,
get_password_internal(User, Server) ->
ejabberd_auth_internal:get_password(User, Server).
-%% @spec (User, Server, CacheTime) -> false | Password::string()
+-spec get_password_cache(User::binary(), Server::binary(), CacheTime::integer()) -> Password::string() | false.
get_password_cache(User, Server, CacheTime) ->
case get_last_access(User, Server) of
online -> get_password_internal(User, Server);
@@ -273,10 +273,10 @@ is_fresh_enough(TimeStampLast, CacheTime) ->
Now = p1_time_compat:system_time(seconds),
TimeStampLast + CacheTime > Now.
-%% @spec (User, Server) -> online | never | mod_last_required | TimeStamp::integer()
%% Code copied from mod_configure.erl
%% Code copied from web/ejabberd_web_admin.erl
%% TODO: Update time format to XEP-0202: Entity Time
+-spec(get_last_access(User::binary(), Server::binary()) -> (online | never | mod_last_required | integer())).
get_last_access(User, Server) ->
case ejabberd_sm:get_user_resources(User, Server) of
[] ->
diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl
index b8b4594b..18d06201 100644
--- a/src/ejabberd_auth_odbc.erl
+++ b/src/ejabberd_auth_odbc.erl
@@ -72,22 +72,18 @@ check_password(User, Server, Password) ->
(LUser == <<>>) or (LServer == <<>>) ->
false;
true ->
- Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
- try odbc_queries:get_password_scram(LServer, Username) of
- {selected, [<<"password">>, <<"serverkey">>,
- <<"salt">>, <<"iterationcount">>],
- [[StoredKey, ServerKey, Salt, IterationCount]]} ->
+ try odbc_queries:get_password_scram(LServer, LUser) of
+ {selected,
+ [{StoredKey, ServerKey, Salt, IterationCount}]} ->
Scram =
#scram{storedkey = StoredKey,
serverkey = ServerKey,
salt = Salt,
- iterationcount = binary_to_integer(
- IterationCount)},
+ iterationcount = IterationCount},
is_password_scram_valid(Password, Scram);
- {selected, [<<"password">>, <<"serverkey">>,
- <<"salt">>, <<"iterationcount">>], []} ->
+ {selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -96,12 +92,12 @@ check_password(User, Server, Password) ->
false %% Typical error is database not accessible
end;
false ->
- try odbc_queries:get_password(LServer, Username) of
- {selected, [<<"password">>], [[Password]]} ->
+ try odbc_queries:get_password(LServer, LUser) of
+ {selected, [{Password}]} ->
Password /= <<"">>;
- {selected, [<<"password">>], [[_Password2]]} ->
+ {selected, [{_Password2}]} ->
false; %% Password is not correct
- {selected, [<<"password">>], []} ->
+ {selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -124,10 +120,9 @@ check_password(User, Server, Password, Digest,
true ->
case is_scrammed() of
false ->
- Username = ejabberd_odbc:escape(LUser),
- try odbc_queries:get_password(LServer, Username) of
+ try odbc_queries:get_password(LServer, LUser) of
%% Account exists, check if password is valid
- {selected, [<<"password">>], [[Passwd]]} ->
+ {selected, [{Passwd}]} ->
DigRes = if Digest /= <<"">> ->
Digest == DigestGen(Passwd);
true -> false
@@ -135,7 +130,7 @@ check_password(User, Server, Password, Digest,
if DigRes -> true;
true -> (Passwd == Password) and (Password /= <<"">>)
end;
- {selected, [<<"password">>], []} ->
+ {selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -158,26 +153,24 @@ set_password(User, Server, Password) ->
(LUser == <<>>) or (LServer == <<>>) ->
{error, invalid_jid};
true ->
- Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
Scram = password_to_scram(Password),
case catch odbc_queries:set_password_scram_t(
LServer,
- Username,
- ejabberd_odbc:escape(Scram#scram.storedkey),
- ejabberd_odbc:escape(Scram#scram.serverkey),
- ejabberd_odbc:escape(Scram#scram.salt),
- integer_to_binary(Scram#scram.iterationcount)
+ LUser,
+ Scram#scram.storedkey,
+ Scram#scram.serverkey,
+ Scram#scram.salt,
+ Scram#scram.iterationcount
)
of
{atomic, ok} -> ok;
Other -> {error, Other}
end;
false ->
- Pass = ejabberd_odbc:escape(Password),
case catch odbc_queries:set_password_t(LServer,
- Username, Pass)
+ LUser, Password)
of
{atomic, ok} -> ok;
Other -> {error, Other}
@@ -194,26 +187,23 @@ try_register(User, Server, Password) ->
(LUser == <<>>) or (LServer == <<>>) ->
{error, invalid_jid};
true ->
- Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
Scram = password_to_scram(Password),
case catch odbc_queries:add_user_scram(
LServer,
- Username,
- ejabberd_odbc:escape(Scram#scram.storedkey),
- ejabberd_odbc:escape(Scram#scram.serverkey),
- ejabberd_odbc:escape(Scram#scram.salt),
- integer_to_binary(Scram#scram.iterationcount)
+ LUser,
+ Scram#scram.storedkey,
+ Scram#scram.serverkey,
+ Scram#scram.salt,
+ Scram#scram.iterationcount
) of
{updated, 1} -> {atomic, ok};
_ -> {atomic, exists}
end;
false ->
- Pass = ejabberd_odbc:escape(Password),
- case catch odbc_queries:add_user(LServer, Username,
- Pass)
- of
+ case catch odbc_queries:add_user(LServer, LUser,
+ Password) of
{updated, 1} -> {atomic, ok};
_ -> {atomic, exists}
end
@@ -228,35 +218,51 @@ dirty_get_registered_users() ->
Servers).
get_vh_registered_users(Server) ->
- LServer = jid:nameprep(Server),
- case catch odbc_queries:list_users(LServer) of
- {selected, [<<"username">>], Res} ->
- [{U, LServer} || [U] <- Res];
- _ -> []
+ case jid:nameprep(Server) of
+ error -> [];
+ <<>> -> [];
+ LServer ->
+ case catch odbc_queries:list_users(LServer) of
+ {selected, Res} ->
+ [{U, LServer} || {U} <- Res];
+ _ -> []
+ end
end.
get_vh_registered_users(Server, Opts) ->
- LServer = jid:nameprep(Server),
- case catch odbc_queries:list_users(LServer, Opts) of
- {selected, [<<"username">>], Res} ->
- [{U, LServer} || [U] <- Res];
- _ -> []
+ case jid:nameprep(Server) of
+ error -> [];
+ <<>> -> [];
+ LServer ->
+ case catch odbc_queries:list_users(LServer, Opts) of
+ {selected, Res} ->
+ [{U, LServer} || {U} <- Res];
+ _ -> []
+ end
end.
get_vh_registered_users_number(Server) ->
- LServer = jid:nameprep(Server),
- case catch odbc_queries:users_number(LServer) of
- {selected, [_], [[Res]]} ->
- jlib:binary_to_integer(Res);
- _ -> 0
+ case jid:nameprep(Server) of
+ error -> 0;
+ <<>> -> 0;
+ LServer ->
+ case catch odbc_queries:users_number(LServer) of
+ {selected, [{Res}]} ->
+ Res;
+ _ -> 0
+ end
end.
get_vh_registered_users_number(Server, Opts) ->
- LServer = jid:nameprep(Server),
- case catch odbc_queries:users_number(LServer, Opts) of
- {selected, [_], [[Res]]} ->
- jlib:binary_to_integer(Res);
- _Other -> 0
+ case jid:nameprep(Server) of
+ error -> 0;
+ <<>> -> 0;
+ LServer ->
+ case catch odbc_queries:users_number(LServer, Opts) of
+ {selected, [{Res}]} ->
+ Res;
+ _Other -> 0
+ end
end.
get_password(User, Server) ->
@@ -267,24 +273,22 @@ get_password(User, Server) ->
(LUser == <<>>) or (LServer == <<>>) ->
false;
true ->
- Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
case catch odbc_queries:get_password_scram(
- LServer, Username) of
- {selected, [<<"password">>, <<"serverkey">>,
- <<"salt">>, <<"iterationcount">>],
- [[StoredKey, ServerKey, Salt, IterationCount]]} ->
+ LServer, LUser) of
+ {selected,
+ [{StoredKey, ServerKey, Salt, IterationCount}]} ->
{jlib:decode_base64(StoredKey),
jlib:decode_base64(ServerKey),
jlib:decode_base64(Salt),
- binary_to_integer(IterationCount)};
+ IterationCount};
_ -> false
end;
false ->
- case catch odbc_queries:get_password(LServer, Username)
+ case catch odbc_queries:get_password(LServer, LUser)
of
- {selected, [<<"password">>], [[Password]]} -> Password;
+ {selected, [{Password}]} -> Password;
_ -> false
end
end
@@ -300,9 +304,8 @@ get_password_s(User, Server) ->
true ->
case is_scrammed() of
false ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_password(LServer, Username) of
- {selected, [<<"password">>], [[Password]]} -> Password;
+ case catch odbc_queries:get_password(LServer, LUser) of
+ {selected, [{Password}]} -> Password;
_ -> <<"">>
end;
true -> <<"">>
@@ -311,15 +314,17 @@ get_password_s(User, Server) ->
%% @spec (User, Server) -> true | false | {error, Error}
is_user_exists(User, Server) ->
- case jid:nodeprep(User) of
- error -> false;
- LUser ->
- Username = ejabberd_odbc:escape(LUser),
- LServer = jid:nameprep(Server),
- try odbc_queries:get_password(LServer, Username) of
- {selected, [<<"password">>], [[_Password]]} ->
+ LServer = jid:nameprep(Server),
+ LUser = jid:nodeprep(User),
+ if (LUser == error) or (LServer == error) ->
+ false;
+ (LUser == <<>>) or (LServer == <<>>) ->
+ false;
+ true ->
+ try odbc_queries:get_password(LServer, LUser) of
+ {selected, [{_Password}]} ->
true; %% Account exists
- {selected, [<<"password">>], []} ->
+ {selected, []} ->
false; %% Account does not exist
{error, Error} -> {error, Error}
catch
@@ -331,12 +336,14 @@ is_user_exists(User, Server) ->
%% @doc Remove user.
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
- case jid:nodeprep(User) of
- error -> error;
- LUser ->
- Username = ejabberd_odbc:escape(LUser),
- LServer = jid:nameprep(Server),
- catch odbc_queries:del_user(LServer, Username),
+ LServer = jid:nameprep(Server),
+ LUser = jid:nodeprep(User),
+ if (LUser == error) or (LServer == error) ->
+ error;
+ (LUser == <<>>) or (LServer == <<>>) ->
+ error;
+ true ->
+ catch odbc_queries:del_user(LServer, LUser),
ok
end.
@@ -359,16 +366,12 @@ remove_user(User, Server, Password) ->
false -> not_allowed
end;
false ->
- Username = ejabberd_odbc:escape(LUser),
- Pass = ejabberd_odbc:escape(Password),
F = fun () ->
Result = odbc_queries:del_user_return_password(
- LServer, Username, Pass),
+ LServer, LUser, Password),
case Result of
- {selected, [<<"password">>],
- [[Password]]} -> ok;
- {selected, [<<"password">>],
- []} -> not_exists;
+ {selected, [{Password}]} -> ok;
+ {selected, []} -> not_exists;
_ -> not_allowed
end
end,
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 04e41f46..a5802a5b 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -115,6 +115,7 @@
mgmt_resend,
mgmt_stanzas_in = 0,
mgmt_stanzas_out = 0,
+ ask_offline = true,
lang = <<"">>}).
%-define(DBGFSM, true).
@@ -133,13 +134,13 @@
%% session:
-define(C2S_OPEN_TIMEOUT, 60000).
--define(C2S_HIBERNATE_TIMEOUT, 90000).
+-define(C2S_HIBERNATE_TIMEOUT, ejabberd_config:get_option(c2s_hibernate, fun(X) when is_integer(X); X == hibernate-> X end, 90000)).
-define(STREAM_HEADER,
<<"<?xml version='1.0'?><stream:stream "
"xmlns='jabber:client' xmlns:stream='http://et"
- "herx.jabber.org/streams' id='~s' from='~s'~s~"
- "s>">>).
+ "herx.jabber.org/streams' id='~s' from='~s'~s"
+ "~s>">>).
-define(STREAM_TRAILER, <<"</stream:stream>">>).
@@ -342,15 +343,15 @@ get_subscribed(FsmRef) ->
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
DefaultLang = ?MYLANG,
- case xml:get_attr_s(<<"xmlns:stream">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns:stream">>, Attrs) of
?NS_STREAM ->
Server =
case StateData#state.server of
<<"">> ->
- jid:nameprep(xml:get_attr_s(<<"to">>, Attrs));
+ jid:nameprep(fxml:get_attr_s(<<"to">>, Attrs));
S -> S
end,
- Lang = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
+ Lang = case fxml:get_attr_s(<<"xml:lang">>, Attrs) of
Lang1 when byte_size(Lang1) =< 35 ->
%% As stated in BCP47, 4.4.1:
%% Protocols or specifications that
@@ -367,7 +368,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
case lists:member(Server, ?MYHOSTS) of
true when IsBlacklistedIP == false ->
change_shaper(StateData, jid:make(<<"">>, Server, <<"">>)),
- case xml:get_attr_s(<<"version">>, Attrs) of
+ case fxml:get_attr_s(<<"version">>, Attrs) of
<<"1.0">> ->
send_header(StateData, Server, <<"1.0">>, DefaultLang),
case StateData#state.authenticated of
@@ -408,7 +409,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
(StateData#state.sockmod):get_sockmod(StateData#state.socket),
Zlib = StateData#state.zlib,
CompressFeature = case Zlib andalso
- ((SockMod == gen_tcp) orelse (SockMod == p1_tls)) of
+ ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of
true ->
[#xmlel{name = <<"compression">>,
attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
@@ -468,6 +469,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
false ->
[]
end,
+ SockMod =
+ (StateData#state.sockmod):get_sockmod(
+ StateData#state.socket),
+ Zlib = StateData#state.zlib,
+ CompressFeature =
+ case Zlib andalso
+ ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of
+ true ->
+ [#xmlel{name = <<"compression">>,
+ attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
+ children = [#xmlel{name = <<"method">>,
+ attrs = [],
+ children = [{xmlcdata, <<"zlib">>}]}]}];
+ _ ->
+ []
+ end,
StreamFeatures1 = [#xmlel{name = <<"bind">>,
attrs = [{<<"xmlns">>, ?NS_BIND}],
children = []},
@@ -478,6 +495,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
++
RosterVersioningFeature ++
StreamManagementFeature ++
+ CompressFeature ++
ejabberd_hooks:run_fold(c2s_post_auth_features,
Server, [], [Server]),
StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features,
@@ -724,19 +742,19 @@ wait_for_feature_request({xmlstreamelement, El},
TLSRequired = StateData#state.tls_required,
SockMod =
(StateData#state.sockmod):get_sockmod(StateData#state.socket),
- case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs), Name} of
{?NS_SASL, <<"auth">>}
when TLSEnabled or not TLSRequired ->
- Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
- ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
+ Mech = fxml:get_attr_s(<<"mechanism">>, Attrs),
+ ClientIn = jlib:decode_base64(fxml:get_cdata(Els)),
case cyrsasl:server_start(StateData#state.sasl_state,
Mech, ClientIn)
of
{ok, Props} ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
- %U = xml:get_attr_s(username, Props),
+ %U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
- %AuthModule = xml:get_attr_s(auth_module, Props),
+ %AuthModule = fxml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -802,7 +820,7 @@ wait_for_feature_request({xmlstreamelement, El},
StateData#state.tls_options)]
end,
Socket = StateData#state.socket,
- BProceed = xml:element_to_binary(#xmlel{name = <<"proceed">>,
+ BProceed = fxml:element_to_binary(#xmlel{name = <<"proceed">>,
attrs = [{<<"xmlns">>, ?NS_TLS}]}),
TLSSocket = (StateData#state.sockmod):starttls(Socket,
TLSOpts,
@@ -813,39 +831,8 @@ wait_for_feature_request({xmlstreamelement, El},
tls_enabled = true});
{?NS_COMPRESS, <<"compress">>}
when Zlib == true,
- (SockMod == gen_tcp) or (SockMod == p1_tls) ->
- case xml:get_subtag(El, <<"method">>) of
- false ->
- send_element(StateData,
- #xmlel{name = <<"failure">>,
- attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
- children =
- [#xmlel{name = <<"setup-failed">>,
- attrs = [], children = []}]}),
- fsm_next_state(wait_for_feature_request, StateData);
- Method ->
- case xml:get_tag_cdata(Method) of
- <<"zlib">> ->
- Socket = StateData#state.socket,
- BCompressed = xml:element_to_binary(#xmlel{name = <<"compressed">>,
- attrs = [{<<"xmlns">>, ?NS_COMPRESS}]}),
- ZlibSocket = (StateData#state.sockmod):compress(Socket,
- BCompressed),
- fsm_next_state(wait_for_stream,
- StateData#state{socket = ZlibSocket,
- streamid = new_id()});
- _ ->
- send_element(StateData,
- #xmlel{name = <<"failure">>,
- attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
- children =
- [#xmlel{name =
- <<"unsupported-method">>,
- attrs = [],
- children = []}]}),
- fsm_next_state(wait_for_feature_request, StateData)
- end
- end;
+ (SockMod == gen_tcp) or (SockMod == fast_tls) ->
+ process_compression_request(El, wait_for_feature_request, StateData);
_ ->
if TLSRequired and not TLSEnabled ->
Lang = StateData#state.lang,
@@ -880,18 +867,18 @@ wait_for_sasl_response({xmlstreamelement, #xmlel{name = Name} = El}, StateData)
wait_for_sasl_response({xmlstreamelement, El},
StateData) ->
#xmlel{name = Name, attrs = Attrs, children = Els} = El,
- case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs), Name} of
{?NS_SASL, <<"response">>} ->
- ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
+ ClientIn = jlib:decode_base64(fxml:get_cdata(Els)),
case cyrsasl:server_step(StateData#state.sasl_state,
ClientIn)
of
{ok, Props} ->
catch
(StateData#state.sockmod):reset_stream(StateData#state.socket),
-% U = xml:get_attr_s(username, Props),
+% U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
-% AuthModule = xml:get_attr_s(auth_module, Props),
+% AuthModule = fxml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, <<>>),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -912,9 +899,9 @@ wait_for_sasl_response({xmlstreamelement, El},
user = U});
{ok, Props, ServerOut} ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
-% U = xml:get_attr_s(username, Props),
+% U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
-% AuthModule = xml:get_attr_s(auth_module, Props),
+% AuthModule = fxml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -1012,7 +999,7 @@ resource_conflict_action(U, S, R) ->
acceptnew -> {accept_resource, R};
closenew -> closenew;
setresource ->
- Rnew = iolist_to_binary([randoms:get_string(),randoms:get_string(),randoms:get_string()]),
+ Rnew = new_uniq_id(),
{accept_resource, Rnew}
end.
@@ -1035,12 +1022,11 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
#iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} =
IQ ->
U = StateData#state.user,
- R1 = xml:get_path_s(SubEl,
+ R1 = fxml:get_path_s(SubEl,
[{elem, <<"resource">>}, cdata]),
R = case jid:resourceprep(R1) of
error -> error;
- <<"">> ->
- iolist_to_binary([randoms:get_string(),randoms:get_string(),randoms:get_string()]);
+ <<"">> -> new_uniq_id();
Resource -> Resource
end,
case R of
@@ -1088,7 +1074,19 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
end
end
end;
- _ -> fsm_next_state(wait_for_bind, StateData)
+ _ ->
+ #xmlel{name = Name, attrs = Attrs, children = _Els} = El,
+ Zlib = StateData#state.zlib,
+ SockMod =
+ (StateData#state.sockmod):get_sockmod(StateData#state.socket),
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ {?NS_COMPRESS, <<"compress">>}
+ when Zlib == true,
+ (SockMod == gen_tcp) or (SockMod == fast_tls) ->
+ process_compression_request(El, wait_for_bind, StateData);
+ _ ->
+ fsm_next_state(wait_for_bind, StateData)
+ end
end;
wait_for_bind(timeout, StateData) ->
{stop, normal, StateData};
@@ -1207,24 +1205,24 @@ session_established2(El, StateData) ->
User = NewStateData#state.user,
Server = NewStateData#state.server,
FromJID = NewStateData#state.jid,
- To = xml:get_attr_s(<<"to">>, Attrs),
+ To = fxml:get_attr_s(<<"to">>, Attrs),
ToJID = case To of
<<"">> -> jid:make(User, Server, <<"">>);
_ -> jid:from_string(To)
end,
NewEl1 = jlib:remove_attr(<<"xmlns">>, El),
- NewEl = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
+ NewEl = case fxml:get_attr_s(<<"xml:lang">>, Attrs) of
<<"">> ->
case NewStateData#state.lang of
<<"">> -> NewEl1;
Lang ->
- xml:replace_tag_attr(<<"xml:lang">>, Lang, NewEl1)
+ fxml:replace_tag_attr(<<"xml:lang">>, Lang, NewEl1)
end;
_ -> NewEl1
end,
NewState = case ToJID of
error ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> NewStateData;
<<"result">> -> NewStateData;
_ ->
@@ -1408,7 +1406,7 @@ handle_info({route, From, To,
StateData,
[{From, To,
Packet}]),
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"probe">> ->
LFrom = jid:tolower(From),
LBFrom =
@@ -1625,7 +1623,7 @@ handle_info({route, From, To,
allow ->
{true, Attrs, StateData};
deny ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
<<"groupchat">> -> ok;
<<"headline">> -> ok;
@@ -1737,6 +1735,8 @@ handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
From, jid:make(USR), Packet)
end, lists:usort(Recipients)),
fsm_next_state(StateName, StateData);
+handle_info(dont_ask_offline, StateName, StateData) ->
+ fsm_next_state(StateName, StateData#state{ask_offline = false});
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
fsm_next_state(StateName, StateData).
@@ -1844,7 +1844,7 @@ send_element(StateData, El) when StateData#state.xml_socket ->
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamelement, El});
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_binary(El)).
+ send_text(StateData, fxml:element_to_binary(El)).
send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive ->
csi_filter_stanza(StateData, Stanza);
@@ -1912,6 +1912,10 @@ send_trailer(StateData) ->
new_id() -> randoms:get_string().
+new_uniq_id() ->
+ iolist_to_binary([randoms:get_string(),
+ jlib:integer_to_binary(p1_time_compat:unique_integer(positive))]).
+
is_auth_packet(El) ->
case jlib:iq_query_info(El) of
#iq{id = ID, type = Type, xmlns = ?NS_AUTH, sub_el = SubEl} ->
@@ -1924,7 +1928,7 @@ is_auth_packet(El) ->
is_stanza(#xmlel{name = Name, attrs = Attrs}) when Name == <<"message">>;
Name == <<"presence">>;
Name == <<"iq">> ->
- case xml:get_attr(<<"xmlns">>, Attrs) of
+ case fxml:get_attr(<<"xmlns">>, Attrs) of
{value, NS} when NS /= <<"jabber:client">>,
NS /= <<"jabber:server">> ->
false;
@@ -1936,7 +1940,7 @@ is_stanza(_El) ->
get_auth_tags([#xmlel{name = Name, children = Els} | L],
U, P, D, R) ->
- CData = xml:get_cdata(Els),
+ CData = fxml:get_cdata(Els),
case Name of
<<"username">> -> get_auth_tags(L, CData, P, D, R);
<<"password">> -> get_auth_tags(L, U, CData, D, R);
@@ -1955,11 +1959,11 @@ get_auth_tags([], U, P, D, R) ->
get_conn_type(StateData) ->
case (StateData#state.sockmod):get_sockmod(StateData#state.socket) of
gen_tcp -> c2s;
- p1_tls -> c2s_tls;
+ fast_tls -> c2s_tls;
ezlib ->
case ezlib:get_sockmod((StateData#state.socket)#socket_state.socket) of
gen_tcp -> c2s_compressed;
- p1_tls -> c2s_compressed_tls
+ fast_tls -> c2s_compressed_tls
end;
ejabberd_http_bind -> http_bind;
ejabberd_http_ws -> websocket;
@@ -2003,11 +2007,11 @@ process_presence_probe(From, To, StateData) ->
%% User updates his presence (non-directed presence packet)
presence_update(From, Packet, StateData) ->
#xmlel{attrs = Attrs} = Packet,
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"unavailable">> ->
- Status = case xml:get_subtag(Packet, <<"status">>) of
+ Status = case fxml:get_subtag(Packet, <<"status">>) of
false -> <<"">>;
- StatusTag -> xml:get_tag_cdata(StatusTag)
+ StatusTag -> fxml:get_tag_cdata(StatusTag)
end,
Info = [{ip, StateData#state.ip},
{conn, StateData#state.conn},
@@ -2068,7 +2072,7 @@ presence_track(From, To, Packet, StateData) ->
LTo = jid:tolower(To),
User = StateData#state.user,
Server = StateData#state.server,
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"unavailable">> ->
A = remove_element(LTo, StateData#state.pres_a),
check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet);
@@ -2265,11 +2269,11 @@ update_priority(Priority, Packet, StateData) ->
StateData#state.resource, Priority, Packet, Info).
get_priority_from_presence(PresencePacket) ->
- case xml:get_subtag(PresencePacket, <<"priority">>) of
+ case fxml:get_subtag(PresencePacket, <<"priority">>) of
false -> 0;
SubEl ->
case catch
- jlib:binary_to_integer(xml:get_tag_cdata(SubEl))
+ jlib:binary_to_integer(fxml:get_tag_cdata(SubEl))
of
P when is_integer(P) -> P;
_ -> 0
@@ -2310,7 +2314,7 @@ process_privacy_iq(From, To,
ejabberd_router:route(To, From, jlib:iq_to_xml(IQRes)),
NewStateData.
-resend_offline_messages(StateData) ->
+resend_offline_messages(#state{ask_offline = true} = StateData) ->
case ejabberd_hooks:run_fold(resend_offline_messages_hook,
StateData#state.server, [],
[StateData#state.user, StateData#state.server])
@@ -2331,7 +2335,9 @@ resend_offline_messages(StateData) ->
end
end,
Rs)
- end.
+ end;
+resend_offline_messages(_StateData) ->
+ ok.
resend_subscription_requests(#state{user = User,
server = Server} = StateData) ->
@@ -2346,21 +2352,21 @@ resend_subscription_requests(#state{user = User,
get_showtag(undefined) -> <<"unavailable">>;
get_showtag(Presence) ->
- case xml:get_path_s(Presence, [{elem, <<"show">>}, cdata]) of
+ case fxml:get_path_s(Presence, [{elem, <<"show">>}, cdata]) of
<<"">> -> <<"available">>;
ShowTag -> ShowTag
end.
get_statustag(undefined) -> <<"">>;
get_statustag(Presence) ->
- xml:get_path_s(Presence, [{elem, <<"status">>}, cdata]).
+ fxml:get_path_s(Presence, [{elem, <<"status">>}, cdata]).
process_unauthenticated_stanza(StateData, El) ->
- NewEl = case xml:get_tag_attr_s(<<"xml:lang">>, El) of
+ NewEl = case fxml:get_tag_attr_s(<<"xml:lang">>, El) of
<<"">> ->
case StateData#state.lang of
<<"">> -> El;
- Lang -> xml:replace_tag_attr(<<"xml:lang">>, Lang, El)
+ Lang -> fxml:replace_tag_attr(<<"xml:lang">>, Lang, El)
end;
_ -> El
end,
@@ -2462,7 +2468,7 @@ is_ip_blacklisted({IP, _Port}, Lang) ->
%% Check from attributes
%% returns invalid-from|NewElement
check_from(El, FromJID) ->
- case xml:get_tag_attr(<<"from">>, El) of
+ case fxml:get_tag_attr(<<"from">>, El) of
false ->
El;
{value, SJID} ->
@@ -2505,6 +2511,41 @@ bounce_messages() ->
after 0 -> ok
end.
+process_compression_request(El, StateName, StateData) ->
+ case fxml:get_subtag(El, <<"method">>) of
+ false ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
+ children =
+ [#xmlel{name = <<"setup-failed">>,
+ attrs = [], children = []}]}),
+ fsm_next_state(StateName, StateData);
+ Method ->
+ case fxml:get_tag_cdata(Method) of
+ <<"zlib">> ->
+ Socket = StateData#state.socket,
+ BCompressed = fxml:element_to_binary(
+ #xmlel{name = <<"compressed">>,
+ attrs = [{<<"xmlns">>,
+ ?NS_COMPRESS}]}),
+ ZlibSocket = (StateData#state.sockmod):compress(
+ Socket, BCompressed),
+ fsm_next_state(wait_for_stream,
+ StateData#state{socket = ZlibSocket,
+ streamid = new_id()});
+ _ ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
+ children =
+ [#xmlel{name = <<"unsupported-method">>,
+ attrs = [],
+ children = []}]}),
+ fsm_next_state(StateName, StateData)
+ end
+ end.
+
%%%----------------------------------------------------------------------
%%% XEP-0191
%%%----------------------------------------------------------------------
@@ -2573,7 +2614,7 @@ negotiate_stream_mgmt(_El, #state{resource = <<"">>} = StateData) ->
send_element(StateData, ?MGMT_UNEXPECTED_REQUEST(?NS_STREAM_MGMT_3)),
StateData;
negotiate_stream_mgmt(#xmlel{name = Name, attrs = Attrs}, StateData) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
Xmlns when ?IS_SUPPORTED_MGMT_XMLNS(Xmlns) ->
case stream_mgmt_enabled(StateData) of
true ->
@@ -2601,7 +2642,7 @@ negotiate_stream_mgmt(#xmlel{name = Name, attrs = Attrs}, StateData) ->
end.
perform_stream_mgmt(#xmlel{name = Name, attrs = Attrs}, StateData) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
Xmlns when Xmlns == StateData#state.mgmt_xmlns ->
case Name of
<<"r">> ->
@@ -2626,10 +2667,10 @@ perform_stream_mgmt(#xmlel{name = Name, attrs = Attrs}, StateData) ->
handle_enable(#state{mgmt_timeout = DefaultTimeout,
mgmt_max_timeout = MaxTimeout} = StateData, Attrs) ->
- Timeout = case xml:get_attr_s(<<"resume">>, Attrs) of
+ Timeout = case fxml:get_attr_s(<<"resume">>, Attrs) of
ResumeAttr when ResumeAttr == <<"true">>;
ResumeAttr == <<"1">> ->
- MaxAttr = xml:get_attr_s(<<"max">>, Attrs),
+ MaxAttr = fxml:get_attr_s(<<"max">>, Attrs),
case catch jlib:binary_to_integer(MaxAttr) of
Max when is_integer(Max), Max > 0, Max =< MaxTimeout ->
Max;
@@ -2669,7 +2710,7 @@ handle_r(StateData) ->
StateData.
handle_a(StateData, Attrs) ->
- case catch jlib:binary_to_integer(xml:get_attr_s(<<"h">>, Attrs)) of
+ case catch jlib:binary_to_integer(fxml:get_attr_s(<<"h">>, Attrs)) of
H when is_integer(H), H >= 0 ->
check_h_attribute(StateData, H);
_ ->
@@ -2679,12 +2720,12 @@ handle_a(StateData, Attrs) ->
end.
handle_resume(StateData, Attrs) ->
- R = case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ R = case fxml:get_attr_s(<<"xmlns">>, Attrs) of
Xmlns when ?IS_SUPPORTED_MGMT_XMLNS(Xmlns) ->
case stream_mgmt_enabled(StateData) of
true ->
- case {xml:get_attr(<<"previd">>, Attrs),
- catch jlib:binary_to_integer(xml:get_attr_s(<<"h">>, Attrs))}
+ case {fxml:get_attr(<<"previd">>, Attrs),
+ catch jlib:binary_to_integer(fxml:get_attr_s(<<"h">>, Attrs))}
of
{{value, PrevID}, H} when is_integer(H), H >= 0 ->
case inherit_session_state(StateData, PrevID) of
@@ -2815,9 +2856,9 @@ handle_unacked_stanzas(StateData, F)
[N, jid:to_string(StateData#state.jid)]),
lists:foreach(
fun({_, Time, #xmlel{attrs = Attrs} = El}) ->
- From_s = xml:get_attr_s(<<"from">>, Attrs),
+ From_s = fxml:get_attr_s(<<"from">>, Attrs),
From = jid:from_string(From_s),
- To_s = xml:get_attr_s(<<"to">>, Attrs),
+ To_s = fxml:get_attr_s(<<"to">>, Attrs),
To = jid:from_string(To_s),
F(From, To, El, Time)
end, queue:to_list(Queue))
@@ -2833,8 +2874,16 @@ handle_unacked_stanzas(StateData)
Resend when is_boolean(Resend) ->
Resend;
if_offline ->
- ejabberd_sm:get_user_resources(StateData#state.user,
- StateData#state.server) == []
+ Resource = StateData#state.resource,
+ case ejabberd_sm:get_user_resources(StateData#state.user,
+ StateData#state.server) of
+ [Resource] -> % Same resource opened new session
+ true;
+ [] ->
+ true;
+ _ ->
+ false
+ end
end,
ReRoute = case ResendOnTimeout of
true ->
@@ -2867,7 +2916,7 @@ handle_unacked_stanzas(StateData)
case is_encapsulated_forward(El) of
true ->
?DEBUG("Dropping forwarded message stanza from ~s",
- [xml:get_attr_s(<<"from">>, El#xmlel.attrs)]);
+ [fxml:get_attr_s(<<"from">>, El#xmlel.attrs)]);
false ->
case ejabberd_hooks:run_fold(message_is_archived,
StateData#state.server,
@@ -2886,9 +2935,9 @@ handle_unacked_stanzas(_StateData) ->
ok.
is_encapsulated_forward(#xmlel{name = <<"message">>} = El) ->
- SubTag = case {xml:get_subtag(El, <<"sent">>),
- xml:get_subtag(El, <<"received">>),
- xml:get_subtag(El, <<"result">>)} of
+ SubTag = case {fxml:get_subtag(El, <<"sent">>),
+ fxml:get_subtag(El, <<"received">>),
+ fxml:get_subtag(El, <<"result">>)} of
{false, false, false} ->
false;
{Tag, false, false} ->
@@ -2901,7 +2950,7 @@ is_encapsulated_forward(#xmlel{name = <<"message">>} = El) ->
if SubTag == false ->
false;
true ->
- case xml:get_subtag(SubTag, <<"forwarded">>) of
+ case fxml:get_subtag(SubTag, <<"forwarded">>) of
false ->
false;
_ ->
@@ -2989,7 +3038,7 @@ csi_filter_stanza(#state{csi_state = CsiState, jid = JID} = StateData,
queue -> csi_queue_add(StateData, Stanza);
drop -> StateData;
send ->
- From = xml:get_tag_attr_s(<<"from">>, Stanza),
+ From = fxml:get_tag_attr_s(<<"from">>, Stanza),
StateData1 = csi_queue_send(StateData, From),
StateData2 = send_stanza(StateData1#state{csi_state = active},
Stanza),
@@ -3000,7 +3049,7 @@ csi_queue_add(#state{csi_queue = Queue} = StateData, Stanza) ->
case length(StateData#state.csi_queue) >= csi_max_queue(StateData) of
true -> csi_queue_add(csi_queue_flush(StateData), Stanza);
false ->
- From = xml:get_tag_attr_s(<<"from">>, Stanza),
+ From = fxml:get_tag_attr_s(<<"from">>, Stanza),
NewQueue = lists:keystore(From, 1, Queue, {From, p1_time_compat:timestamp(), Stanza}),
StateData#state{csi_queue = NewQueue}
end.
diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl
index cc3e2e9f..157700c4 100644
--- a/src/ejabberd_captcha.erl
+++ b/src/ejabberd_captcha.erl
@@ -320,7 +320,7 @@ build_captcha_html(Id, Lang) ->
-spec process_reply(xmlel()) -> ok | {error, bad_match | not_found | malformed}.
process_reply(#xmlel{} = El) ->
- case xml:get_subtag(El, <<"x">>) of
+ case fxml:get_subtag(El, <<"x">>) of
false -> {error, malformed};
Xdata ->
Fields = jlib:parse_xdata_submit(Xdata),
diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl
index adcbc6d9..dc00c5d2 100644
--- a/src/ejabberd_commands_doc.erl
+++ b/src/ejabberd_commands_doc.erl
@@ -32,7 +32,7 @@
-include("ejabberd_commands.hrl").
-include("ejabberd.hrl").
--define(RAW(V), if HTMLOutput -> xml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end).
+-define(RAW(V), if HTMLOutput -> fxml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end).
-define(TAG(N), if HTMLOutput -> [<<"<", ??N, "/>">>]; true -> md_tag(N, <<"">>) end).
-define(TAG(N, V), if HTMLOutput -> [<<"<", ??N, ">">>, V, <<"</", ??N, ">">>]; true -> md_tag(N, V) end).
-define(TAG(N, C, V), if HTMLOutput -> [<<"<", ??N, " class='", C, "'>">>, V, <<"</", ??N, ">">>]; true -> md_tag(N, V) end).
@@ -79,9 +79,9 @@ md_tag(pre, V) ->
md_tag(p, V) ->
[<<"\n\n">>, V, <<"\n">>];
md_tag(h1, V) ->
- [<<"\n## ">>, V, <<"\n">>];
+ [<<"\n\n## ">>, V, <<"\n">>];
md_tag(h2, V) ->
- [<<"\n### ">>, V, <<"\n">>];
+ [<<"\n\n### ">>, V, <<"\n">>];
md_tag(strong, V) ->
[<<"*">>, V, <<"*">>];
md_tag(_, V) ->
@@ -158,7 +158,7 @@ java_call(Name, ArgsDesc, Values, HTMLOutput) ->
Indent, ?ID_L("client"), ?OP_L("."), ?ID_L("setConfig"), ?OP_L("("), ?ID_L("config"), ?OP_L(");"), ?BR, Indent, ?BR,
Indent, ?ID_L("client"), ?OP_L("."), ?ID_L("execute"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "),
java_gen_map(lists:map(fun({A,B})->java_gen(A, B, Indent, HTMLOutput) end, lists:zip(ArgsDesc, Values)), Indent, HTMLOutput),
- ?OP_L(");"), ?BR].
+ ?OP_L(");")].
-define(XML_S(N, V), ?OP_L("<"), ?FIELD_L(??N), ?OP_L(">"), V).
-define(XML_E(N), ?OP_L("</"), ?FIELD_L(??N), ?OP_L(">")).
@@ -360,8 +360,9 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
none ->
[?RAW(io_lib:format("~p", [Result]))];
_ ->
- [?RAW(io_lib:format("~p", [Result])),
- ?TAG_R(p, ResultDesc)]
+ [?TAG(dl, [
+ ?TAG(dt, io_lib:format("~p", [Result])),
+ ?TAG_R(dd, ResultDesc)])]
end,
[?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 8d2d1997..eb06b98f 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -112,7 +112,7 @@ get_env_config() ->
%% @doc Read the ejabberd configuration file.
%% It also includes additional configuration files and replaces macros.
%% This function will crash if finds some error in the configuration file.
-%% @spec (File::string()) -> #state{}.
+%% @spec (File::string()) -> #state{}
read_file(File) ->
read_file(File, [{replace_macros, true},
{include_files, true},
@@ -162,7 +162,7 @@ convert_to_yaml(File, Output) ->
fun({Host, Opts1}) ->
{host_config, [{Host, Opts1}]}
end, HOpts),
- Data = p1_yaml:encode(lists:reverse(NewOpts)),
+ Data = fast_yaml:encode(lists:reverse(NewOpts)),
case Output of
stdout ->
io:format("~s~n", [Data]);
@@ -226,14 +226,14 @@ get_plain_terms_file(File1, Opts) ->
consult(File) ->
case filename:extension(File) of
Ex when (Ex == ".yml") or (Ex == ".yaml") ->
- case p1_yaml:decode_from_file(File, [plain_as_atom]) of
+ case fast_yaml:decode_from_file(File, [plain_as_atom]) of
{ok, []} ->
{ok, []};
{ok, [Document|_]} ->
{ok, parserl(Document)};
{error, Err} ->
Msg1 = "Cannot load " ++ File ++ ": ",
- Msg2 = p1_yaml:format_error(Err),
+ Msg2 = fast_yaml:format_error(Err),
{error, Msg1 ++ Msg2}
end;
_ ->
@@ -408,7 +408,7 @@ maps_to_lists(IMap) ->
end, [], IMap).
merge_configs(Terms, ResMap) ->
- lists:foldl(fun({Name, Val}, Map) when is_list(Val) ->
+ lists:foldl(fun({Name, Val}, Map) when is_list(Val), Name =/= auth_method ->
Old = maps:get(Name, Map, #{}),
New = lists:foldl(fun(SVal, OMap) ->
NVal = if Name == host_config orelse Name == append_host_config ->
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index 2bf20c2e..7a26644f 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -324,7 +324,7 @@ format_args(Args, ArgsFormat) ->
format_arg(Arg, integer) ->
format_arg2(Arg, "~d");
format_arg(Arg, binary) ->
- list_to_binary(format_arg(Arg, string));
+ unicode:characters_to_binary(Arg, utf8);
format_arg("", string) ->
"";
format_arg(Arg, string) ->
@@ -349,7 +349,7 @@ format_result(Atom, {_Name, atom}) ->
format_result(Int, {_Name, integer}) ->
io_lib:format("~p", [Int]);
-format_result(String, {_Name, string}) when is_list(String) ->
+format_result([A|_]=String, {_Name, string}) when is_list(String) and is_integer(A) ->
io_lib:format("~s", [String]);
format_result(Binary, {_Name, string}) when is_binary(Binary) ->
diff --git a/src/ejabberd_frontend_socket.erl b/src/ejabberd_frontend_socket.erl
index 09eeded9..b8e706f2 100644
--- a/src/ejabberd_frontend_socket.erl
+++ b/src/ejabberd_frontend_socket.erl
@@ -148,20 +148,20 @@ init([Module, SockMod, Socket, Opts, Receiver]) ->
receiver = Receiver}}.
handle_call({starttls, TLSOpts}, _From, State) ->
- {ok, TLSSocket} = p1_tls:tcp_to_tls(State#state.socket, TLSOpts),
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(State#state.socket, TLSOpts),
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
Reply = ok,
- {reply, Reply, State#state{socket = TLSSocket, sockmod = p1_tls},
+ {reply, Reply, State#state{socket = TLSSocket, sockmod = fast_tls},
?HIBERNATE_TIMEOUT};
handle_call({starttls, TLSOpts, Data}, _From, State) ->
- {ok, TLSSocket} = p1_tls:tcp_to_tls(State#state.socket, TLSOpts),
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(State#state.socket, TLSOpts),
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
catch (State#state.sockmod):send(
State#state.socket, Data),
Reply = ok,
{reply, Reply,
- State#state{socket = TLSSocket, sockmod = p1_tls},
+ State#state{socket = TLSSocket, sockmod = fast_tls},
?HIBERNATE_TIMEOUT};
handle_call({compress, Data}, _From, State) ->
{ok, ZlibSocket} =
@@ -187,10 +187,10 @@ handle_call(get_sockmod, _From, State) ->
Reply = State#state.sockmod,
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(get_peer_certificate, _From, State) ->
- Reply = p1_tls:get_peer_certificate(State#state.socket),
+ Reply = fast_tls:get_peer_certificate(State#state.socket),
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(get_verify_result, _From, State) ->
- Reply = p1_tls:get_verify_result(State#state.socket),
+ Reply = fast_tls:get_verify_result(State#state.socket),
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(close, _From, State) ->
ejabberd_receiver:close(State#state.receiver),
@@ -236,9 +236,9 @@ check_starttls(SockMod, Socket, Receiver, Opts) ->
(_) -> false
end, Opts),
if TLSEnabled ->
- {ok, TLSSocket} = p1_tls:tcp_to_tls(Socket, TLSOpts),
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(Receiver, TLSSocket),
- {p1_tls, TLSSocket};
+ {fast_tls, TLSSocket};
true ->
{SockMod, Socket}
end.
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index b95f9820..d8d1ddd4 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -117,9 +117,9 @@ init({SockMod, Socket}, Opts) ->
TLSOpts = [verify_none | TLSOpts3],
{SockMod1, Socket1} = if TLSEnabled ->
inet:setopts(Socket, [{recbuf, 8192}]),
- {ok, TLSSocket} = p1_tls:tcp_to_tls(Socket,
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(Socket,
TLSOpts),
- {p1_tls, TLSSocket};
+ {fast_tls, TLSSocket};
true -> {SockMod, Socket}
end,
Captcha = case proplists:get_bool(captcha, Opts) of
@@ -328,8 +328,8 @@ get_transfer_protocol(SockMod, HostPort) ->
{gen_tcp, []} -> {Host, 80, http};
{gen_tcp, [Port]} ->
{Host, jlib:binary_to_integer(Port), http};
- {p1_tls, []} -> {Host, 443, https};
- {p1_tls, [Port]} ->
+ {fast_tls, []} -> {Host, 443, https};
+ {fast_tls, [Port]} ->
{Host, jlib:binary_to_integer(Port), https}
end.
@@ -532,10 +532,10 @@ make_xhtml_output(State, Status, Headers, XHTML) ->
Data = case lists:member(html, Headers) of
true ->
iolist_to_binary([?HTML_DOCTYPE,
- xml:element_to_binary(XHTML)]);
+ fxml:element_to_binary(XHTML)]);
_ ->
iolist_to_binary([?XHTML_DOCTYPE,
- xml:element_to_binary(XHTML)])
+ fxml:element_to_binary(XHTML)])
end,
Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
Headers)
diff --git a/src/ejabberd_http_bind.erl b/src/ejabberd_http_bind.erl
index c4cfdddd..ea8cd792 100644
--- a/src/ejabberd_http_bind.erl
+++ b/src/ejabberd_http_bind.erl
@@ -224,7 +224,7 @@ process_request(Data, IP, HOpts) ->
of
%% No existing session:
{ok, {<<"">>, Rid, Attrs, Payload}} ->
- case xml:get_attr_s(<<"to">>, Attrs) of
+ case fxml:get_attr_s(<<"to">>, Attrs) of
<<"">> ->
?DEBUG("Session not created (Improper addressing)", []),
{200, ?HEADER,
@@ -248,13 +248,13 @@ process_request(Data, IP, HOpts) ->
end;
%% Existing session
{ok, {Sid, Rid, Attrs, Payload1}} ->
- StreamStart = case xml:get_attr_s(<<"xmpp:restart">>,
+ StreamStart = case fxml:get_attr_s(<<"xmpp:restart">>,
Attrs)
of
<<"true">> -> true;
_ -> false
end,
- Payload2 = case xml:get_attr_s(<<"type">>, Attrs) of
+ Payload2 = case fxml:get_attr_s(<<"type">>, Attrs) of
<<"terminate">> ->
Payload1 ++ [{xmlstreamend, <<"stream:stream">>}];
_ -> Payload1
@@ -280,7 +280,7 @@ process_request(Data, IP, HOpts) ->
handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
Payload, PayloadSize, IP) ->
?DEBUG("got pid: ~p", [Pid]),
- Wait = case str:to_integer(xml:get_attr_s(<<"wait">>,
+ Wait = case str:to_integer(fxml:get_attr_s(<<"wait">>,
Attrs))
of
{error, _} -> ?MAX_WAIT;
@@ -289,7 +289,7 @@ handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
true -> CWait
end
end,
- Hold = case str:to_integer(xml:get_attr_s(<<"hold">>,
+ Hold = case str:to_integer(fxml:get_attr_s(<<"hold">>,
Attrs))
of
{error, _} -> (?MAX_REQUESTS) - 1;
@@ -299,7 +299,7 @@ handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
end
end,
Pdelay = case
- str:to_integer(xml:get_attr_s(<<"process-delay">>,
+ str:to_integer(fxml:get_attr_s(<<"process-delay">>,
Attrs))
of
{error, _} -> ?PROCESS_DELAY_DEFAULT;
@@ -312,12 +312,12 @@ handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
?PROCESS_DELAY_MIN])
end,
Version = case catch
- list_to_float(binary_to_list(xml:get_attr_s(<<"ver">>, Attrs)))
+ list_to_float(binary_to_list(fxml:get_attr_s(<<"ver">>, Attrs)))
of
{'EXIT', _} -> 0.0;
V -> V
end,
- XmppVersion = xml:get_attr_s(<<"xmpp:version">>, Attrs),
+ XmppVersion = fxml:get_attr_s(<<"xmpp:version">>, Attrs),
?DEBUG("Create session: ~p", [Sid]),
mnesia:dirty_write(
#http_bind{id = Sid,
@@ -589,8 +589,8 @@ process_http_put(#http_put{rid = Rid, attrs = Attrs,
Request,
StateName, StateData, RidAllow) ->
?DEBUG("Actually processing request: ~p", [Request]),
- Key = xml:get_attr_s(<<"key">>, Attrs),
- NewKey = xml:get_attr_s(<<"newkey">>, Attrs),
+ Key = fxml:get_attr_s(<<"key">>, Attrs),
+ NewKey = fxml:get_attr_s(<<"newkey">>, Attrs),
KeyAllow = case RidAllow of
repeat -> true;
false -> false;
@@ -801,7 +801,7 @@ handle_http_put_error(Reason,
case Reason of
not_exists ->
{200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
+ fxml:element_to_binary(#xmlel{name = <<"body">>,
attrs =
[{<<"xmlns">>, ?NS_HTTP_BIND},
{<<"type">>, <<"terminate">>},
@@ -810,7 +810,7 @@ handle_http_put_error(Reason,
children = []})};
bad_key ->
{200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
+ fxml:element_to_binary(#xmlel{name = <<"body">>,
attrs =
[{<<"xmlns">>, ?NS_HTTP_BIND},
{<<"type">>, <<"terminate">>},
@@ -819,7 +819,7 @@ handle_http_put_error(Reason,
children = []})};
polling_too_frequently ->
{200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
+ fxml:element_to_binary(#xmlel{name = <<"body">>,
attrs =
[{<<"xmlns">>, ?NS_HTTP_BIND},
{<<"type">>, <<"terminate">>},
@@ -855,7 +855,7 @@ rid_allow(OldRid, NewRid, Attrs, Hold, MaxPause) ->
%% We did not miss any packet, we can process it immediately:
NewRid == OldRid + 1 ->
case catch
- jlib:binary_to_integer(xml:get_attr_s(<<"pause">>,
+ jlib:binary_to_integer(fxml:get_attr_s(<<"pause">>,
Attrs))
of
{'EXIT', _} -> {true, 0};
@@ -929,9 +929,9 @@ prepare_outpacket_response(#http_bind{id = Sid,
_Rid, OutPacket, true) ->
case OutPacket of
[{xmlstreamstart, _, OutAttrs} | Els] ->
- AuthID = xml:get_attr_s(<<"id">>, OutAttrs),
- From = xml:get_attr_s(<<"from">>, OutAttrs),
- Version = xml:get_attr_s(<<"version">>, OutAttrs),
+ AuthID = fxml:get_attr_s(<<"id">>, OutAttrs),
+ From = fxml:get_attr_s(<<"from">>, OutAttrs),
+ Version = fxml:get_attr_s(<<"version">>, OutAttrs),
OutEls = case Els of
[] -> [];
[{xmlstreamelement,
@@ -968,7 +968,7 @@ prepare_outpacket_response(#http_bind{id = Sid,
MaxInactivity = get_max_inactivity(To, ?MAX_INACTIVITY),
MaxPause = get_max_pause(To),
{200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
+ fxml:element_to_binary(#xmlel{name = <<"body">>,
attrs =
[{<<"xmlns">>, ?NS_HTTP_BIND},
{<<"sid">>, Sid},
@@ -1032,7 +1032,7 @@ send_outpacket(#http_bind{pid = FsmRef}, OutPacket) ->
TypedEls = lists:foldl(fun ({xmlstreamelement, El},
Acc) ->
Acc ++
- [xml:element_to_binary(check_default_xmlns(El))];
+ [fxml:element_to_binary(check_default_xmlns(El))];
({xmlstreamraw, R}, Acc) ->
Acc ++ [R]
end,
@@ -1067,7 +1067,7 @@ send_outpacket(#http_bind{pid = FsmRef}, OutPacket) ->
|| {xmlstreamelement, OEl} <- StreamTail]
end,
{200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
+ fxml:element_to_binary(#xmlel{name = <<"body">>,
attrs =
[{<<"xmlns">>,
?NS_HTTP_BIND}],
@@ -1114,14 +1114,14 @@ parse_request(Data, PayloadSize, MaxStanzaSize) ->
?DEBUG("--- incoming data --- ~n~s~n --- END "
"--- ",
[Data]),
- case xml_stream:parse_element(Data) of
+ case fxml_stream:parse_element(Data) of
#xmlel{name = <<"body">>, attrs = Attrs,
children = Els} ->
- Xmlns = xml:get_attr_s(<<"xmlns">>, Attrs),
+ Xmlns = fxml:get_attr_s(<<"xmlns">>, Attrs),
if Xmlns /= (?NS_HTTP_BIND) -> {error, bad_request};
true ->
case catch
- jlib:binary_to_integer(xml:get_attr_s(<<"rid">>,
+ jlib:binary_to_integer(fxml:get_attr_s(<<"rid">>,
Attrs))
of
{'EXIT', _} -> {error, bad_request};
@@ -1133,7 +1133,7 @@ parse_request(Data, PayloadSize, MaxStanzaSize) ->
end
end,
Els),
- Sid = xml:get_attr_s(<<"sid">>, Attrs),
+ Sid = fxml:get_attr_s(<<"sid">>, Attrs),
if PayloadSize =< MaxStanzaSize ->
{ok, {Sid, Rid, Attrs, FixedEls}};
true -> {size_limit, Sid}
@@ -1165,7 +1165,7 @@ set_inactivity_timer(_Pause, MaxInactivity) ->
elements_to_string([]) -> [];
elements_to_string([El | Els]) ->
- [xml:element_to_binary(El) | elements_to_string(Els)].
+ [fxml:element_to_binary(El) | elements_to_string(Els)].
%% @spec (To, Default::integer()) -> integer()
%% where To = [] | {Host::string(), Version::string()}
@@ -1188,7 +1188,7 @@ get_max_pause(_) -> ?MAX_PAUSE.
check_default_xmlns(#xmlel{name = Name, attrs = Attrs,
children = Els} =
El) ->
- case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, El) of
<<"">> ->
#xmlel{name = Name,
attrs = [{<<"xmlns">>, ?NS_CLIENT} | Attrs],
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index 3b50e44a..e66cf33a 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -171,11 +171,11 @@ handle_sync_event({send_xml, Packet}, _From, StateName,
{true, {xmlstreamelement, #xmlel{name=Name2} = El2}} ->
El3 = case Name2 of
<<"stream:", _/binary>> ->
- xml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2);
+ fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2);
_ ->
- case xml:get_tag_attr_s(<<"xmlns">>, El2) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, El2) of
<<"">> ->
- xml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2);
+ fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2);
_ ->
El2
end
@@ -186,12 +186,12 @@ handle_sync_event({send_xml, Packet}, _From, StateName,
end,
case Packet2 of
{xmlstreamstart, Name, Attrs3} ->
- B = xml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}),
+ B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}),
WsPid ! {send, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>};
{xmlstreamend, Name} ->
WsPid ! {send, <<"</", Name/binary, ">">>};
{xmlstreamelement, El} ->
- WsPid ! {send, xml:element_to_binary(El)};
+ WsPid ! {send, fxml:element_to_binary(El)};
{xmlstreamraw, Bin} ->
WsPid ! {send, Bin};
{xmlstreamcdata, Bin2} ->
@@ -210,7 +210,7 @@ handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant
when StateName /= stream_end_sent ->
Close = #xmlel{name = <<"close">>,
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]},
- WsPid ! {send, xml:element_to_binary(Close)},
+ WsPid ! {send, fxml:element_to_binary(Close)},
{stop, normal, StateData};
handle_sync_event(close, _From, _StateName, StateData) ->
{stop, normal, StateData}.
@@ -316,9 +316,9 @@ get_human_html_xmlel() ->
parse(#state{rfc_compilant = C} = State, Data) ->
case C of
undefined ->
- P = xml_stream:new(self()),
- P2 = xml_stream:parse(P, Data),
- xml_stream:close(P2),
+ P = fxml_stream:new(self()),
+ P2 = fxml_stream:parse(P, Data),
+ fxml_stream:close(P2),
case parsed_items([]) of
error ->
{State#state{rfc_compilant = true}, <<"parse error">>};
@@ -330,7 +330,7 @@ parse(#state{rfc_compilant = C} = State, Data) ->
parse(State#state{rfc_compilant = false}, Data)
end;
true ->
- El = xml_stream:parse_element(Data),
+ El = fxml_stream:parse_element(Data),
case El of
#xmlel{name = <<"open">>, attrs = Attrs} ->
Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} |
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index 66f530c8..7c30f3b6 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -32,7 +32,7 @@
%% API
-export([start_link/0]).
--export([route/3, route_iq/4, route_iq/5,
+-export([route/3, route_iq/4, route_iq/5, process_iq/3,
process_iq_reply/3, register_iq_handler/4,
register_iq_handler/5, register_iq_response_handler/4,
register_iq_response_handler/5, unregister_iq_handler/2,
@@ -178,6 +178,7 @@ bounce_resource_packet(From, To, Packet) ->
init([]) ->
lists:foreach(fun (Host) ->
ejabberd_router:register_route(Host,
+ Host,
{apply, ?MODULE,
route}),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
@@ -272,7 +273,7 @@ do_route(From, To, Packet) ->
end;
true ->
#xmlel{attrs = Attrs} = Packet,
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index 243864c7..795d4f39 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -50,6 +50,7 @@
%% "ejabberd.log" in current directory.
%% Note: If the directory where to place the ejabberd log file to not exist,
%% it is not created and no log file will be generated.
+%% @spec () -> string()
get_log_path() ->
case ejabberd_config:env_binary_to_list(ejabberd, log_path) of
{ok, Path} ->
@@ -74,68 +75,6 @@ opt_type(log_rate_limit) ->
opt_type(_) ->
[log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit].
-%% Default logger module is LAGER, defined in else clause.
-%% TODO: Remove p1_logger usage and allow using Elixir logger if running in Elixir context.
--ifdef(P1LOGGER).
-
-start() ->
- set(4),
- LogPath = get_log_path(),
- error_logger:add_report_handler(p1_logger_h, LogPath),
- ok.
-
-reopen_log() ->
- %% TODO: Use the Reopen log API for logger_h ?
- p1_logger_h:reopen_log(),
- reopen_sasl_log().
-
-rotate_log() ->
- %% Not implemented.
- ok.
-
-get() ->
- p1_loglevel:get().
-
-set(LogLevel) when LogLevel >=0, LogLevel =< 5 ->
- p1_loglevel:set(LogLevel);
-set(LogLevel) ->
- throw({wrong_loglevel, LogLevel}).
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-reopen_sasl_log() ->
- case application:get_env(sasl,sasl_error_logger) of
- {ok, {file, SASLfile}} ->
- error_logger:delete_report_handler(sasl_report_file_h),
- rotate_sasl_log(SASLfile),
- error_logger:add_report_handler(sasl_report_file_h,
- {SASLfile, get_sasl_error_logger_type()});
- _ -> false
- end,
- ok.
-
-rotate_sasl_log(Filename) ->
- case file:read_file_info(Filename) of
- {ok, _FileInfo} ->
- file:rename(Filename, [Filename, ".0"]),
- ok;
- {error, _Reason} ->
- ok
- end.
-
-%% Function copied from Erlang/OTP lib/sasl/src/sasl.erl which doesn't export it
-get_sasl_error_logger_type () ->
- case application:get_env (sasl, errlog_type) of
- {ok, error} -> error;
- {ok, progress} -> progress;
- {ok, all} -> all;
- {ok, Bad} -> exit ({bad_config, {sasl, {errlog_type, Bad}}});
- _ -> all
- end.
-
--else.
-
get_integer_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, I} when is_integer(I), I>=0 ->
@@ -161,7 +100,33 @@ get_string_env(Name, Default) ->
Default
end.
+%% @spec () -> ok
start() ->
+ StartedApps = application:which_applications(5000),
+ case lists:keyfind(logger, 1, StartedApps) of
+ %% Elixir logger is started. We assume everything is in place
+ %% to use lager to Elixir logger bridge.
+ {logger, _, _} ->
+ error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []),
+ %% Do not start lager, we rely on Elixir Logger
+ do_start_for_logger();
+ _ ->
+ do_start()
+ end.
+
+do_start_for_logger() ->
+ application:load(sasl),
+ application:set_env(sasl, sasl_error_logger, false),
+ application:load(lager),
+ application:set_env(lager, error_logger_redirect, false),
+ application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']),
+ application:set_env(lager, crash_log, false),
+ application:set_env(lager, handlers, [{elixir_logger_backend, [{level, info}]}]),
+ ejabberd:start_app(lager),
+ ok.
+
+%% Start lager
+do_start() ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
@@ -188,10 +153,12 @@ start() ->
ejabberd:start_app(lager),
ok.
+%% @spec () -> ok
reopen_log() ->
%% Lager detects external log rotation automatically.
ok.
+%% @spec () -> ok
rotate_log() ->
lager_crash_log ! rotate,
lists:foreach(
@@ -201,8 +168,9 @@ rotate_log() ->
ok
end, gen_event:which_handlers(lager_event)).
+%% @spec () -> {loglevel(), atom(), string()}
get() ->
- case lager:get_loglevel(lager_console_backend) of
+ case get_lager_loglevel() of
none -> {0, no_log, "No log"};
emergency -> {1, critical, "Critical"};
alert -> {1, critical, "Critical"};
@@ -214,6 +182,7 @@ get() ->
debug -> {5, debug, "Debug"}
end.
+%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()}
set(LogLevel) when is_integer(LogLevel) ->
LagerLogLevel = case LogLevel of
0 -> none;
@@ -224,7 +193,7 @@ set(LogLevel) when is_integer(LogLevel) ->
5 -> debug;
E -> throw({wrong_loglevel, E})
end,
- case lager:get_loglevel(lager_console_backend) of
+ case get_lager_loglevel() of
LagerLogLevel ->
ok;
_ ->
@@ -234,6 +203,8 @@ set(LogLevel) when is_integer(LogLevel) ->
lager:set_loglevel(H, LagerLogLevel);
(lager_console_backend = H) ->
lager:set_loglevel(H, LagerLogLevel);
+ (elixir_logger_backend = H) ->
+ lager:set_loglevel(H, LagerLogLevel);
(_) ->
ok
end, gen_event:which_handlers(lager_event))
@@ -243,4 +214,21 @@ set({_LogLevel, _}) ->
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
{module, lager}.
--endif.
+get_lager_loglevel() ->
+ Handlers = get_lager_handlers(),
+ lists:foldl(fun(lager_console_backend, _Acc) ->
+ lager:get_loglevel(lager_console_backend);
+ (elixir_logger_backend, _Acc) ->
+ lager:get_loglevel(elixir_logger_backend);
+ (_, Acc) ->
+ Acc
+ end,
+ none, Handlers).
+
+get_lager_handlers() ->
+ case catch gen_event:which_handlers(lager_event) of
+ {'EXIT',noproc} ->
+ [];
+ Result ->
+ Result
+ end.
diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl
index a688bb39..1925a2f7 100644
--- a/src/ejabberd_oauth.erl
+++ b/src/ejabberd_oauth.erl
@@ -149,7 +149,7 @@ authenticate_user({User, Server}, {password, Password} = Ctx) ->
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
-verify_resowner_scope({user, User, Server}, Scope, Ctx) ->
+verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
Cmds = ejabberd_commands:get_commands(),
Cmds1 = [sasl_auth | Cmds],
RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1],
@@ -164,7 +164,7 @@ verify_resowner_scope(_, _, _) ->
{error, badscope}.
-associate_access_code(AccessCode, Context, AppContext) ->
+associate_access_code(_AccessCode, _Context, AppContext) ->
%put(?ACCESS_CODE_TABLE, AccessCode, Context),
{ok, AppContext}.
@@ -184,7 +184,7 @@ associate_access_token(AccessToken, Context, AppContext) ->
mnesia:dirty_write(R),
{ok, AppContext}.
-associate_refresh_token(RefreshToken, Context, AppContext) ->
+associate_refresh_token(_RefreshToken, _Context, AppContext) ->
%put(?REFRESH_TOKEN_TABLE, RefreshToken, Context),
{ok, AppContext}.
@@ -303,7 +303,7 @@ process(_Handlers,
process(_Handlers,
#request{method = 'POST', q = Q, lang = _Lang,
path = [_, <<"authorization_token">>]}) ->
- ResponseType = proplists:get_value(<<"response_type">>, Q, <<"">>),
+ _ResponseType = proplists:get_value(<<"response_type">>, Q, <<"">>),
ClientId = proplists:get_value(<<"client_id">>, Q, <<"">>),
RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>),
SScope = proplists:get_value(<<"scope">>, Q, <<"">>),
diff --git a/src/ejabberd_odbc.erl b/src/ejabberd_odbc.erl
index d7b8fa83..f756fdeb 100644
--- a/src/ejabberd_odbc.erl
+++ b/src/ejabberd_odbc.erl
@@ -41,6 +41,7 @@
sql_bloc/2,
escape/1,
escape_like/1,
+ escape_like_arg/1,
to_bool/1,
sqlite_db/1,
sqlite_file/1,
@@ -63,10 +64,12 @@
-include("ejabberd.hrl").
-include("logger.hrl").
+-include("ejabberd_sql_pt.hrl").
-record(state,
{db_ref = self() :: pid(),
db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql,
+ db_version = undefined :: undefined | non_neg_integer(),
start_interval = 0 :: non_neg_integer(),
host = <<"">> :: binary(),
max_pending_requests_len :: non_neg_integer(),
@@ -92,6 +95,8 @@
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
+-define(PREPARE_KEY, ejabberd_odbc_prepare).
+
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -116,11 +121,13 @@ start_link(Host, StartInterval) ->
[Host, StartInterval],
fsm_limit_opts() ++ (?FSMOPTS)).
--type sql_query() :: [sql_query() | binary()].
+-type sql_query() :: [sql_query() | binary()] | #sql_query{} |
+ fun(() -> any()) | fun((atom(), _) -> any()).
-type sql_query_result() :: {updated, non_neg_integer()} |
{error, binary()} |
{selected, [binary()],
- [[binary()]]}.
+ [[binary()]]} |
+ {selected, [any()]}.
-spec sql_query(binary(), sql_query()) -> sql_query_result().
@@ -194,6 +201,13 @@ escape_like($%) -> <<"\\%">>;
escape_like($_) -> <<"\\_">>;
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> odbc_queries:escape(C).
+escape_like_arg(S) when is_binary(S) ->
+ << <<(escape_like_arg(C))/binary>> || <<C>> <= S >>;
+escape_like_arg($%) -> <<"\\%">>;
+escape_like_arg($_) -> <<"\\_">>;
+escape_like_arg($\\) -> <<"\\\\">>;
+escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>.
+
to_bool(<<"t">>) -> true;
to_bool(<<"true">>) -> true;
to_bool(<<"1">>) -> true;
@@ -259,15 +273,16 @@ connecting(connect, #state{host = Host} = State) ->
end,
{_, PendingRequests} = State#state.pending_requests,
case ConnectRes of
- {ok, Ref} ->
- erlang:monitor(process, Ref),
- lists:foreach(fun (Req) ->
- (?GEN_FSM):send_event(self(), Req)
- end,
- queue:to_list(PendingRequests)),
- {next_state, session_established,
- State#state{db_ref = Ref,
- pending_requests = {0, queue:new()}}};
+ {ok, Ref} ->
+ erlang:monitor(process, Ref),
+ lists:foreach(fun (Req) ->
+ (?GEN_FSM):send_event(self(), Req)
+ end,
+ queue:to_list(PendingRequests)),
+ State1 = State#state{db_ref = Ref,
+ pending_requests = {0, queue:new()}},
+ State2 = get_db_version(State1),
+ {next_state, session_established, State2};
{error, Reason} ->
?INFO_MSG("~p connection failed:~n** Reason: ~p~n** "
"Retry after: ~p seconds",
@@ -469,6 +484,71 @@ execute_bloc(F) ->
Res -> {atomic, Res}
end.
+execute_fun(F) when is_function(F, 0) ->
+ F();
+execute_fun(F) when is_function(F, 2) ->
+ State = get(?STATE_KEY),
+ F(State#state.db_type, State#state.db_version).
+
+sql_query_internal([{_, _} | _] = Queries) ->
+ State = get(?STATE_KEY),
+ case select_sql_query(Queries, State) of
+ undefined ->
+ {error, <<"no matching query for the current DBMS found">>};
+ Query ->
+ sql_query_internal(Query)
+ end;
+sql_query_internal(#sql_query{} = Query) ->
+ State = get(?STATE_KEY),
+ Res =
+ try
+ case State#state.db_type of
+ odbc ->
+ generic_sql_query(Query);
+ pgsql ->
+ Key = {?PREPARE_KEY, Query#sql_query.hash},
+ case get(Key) of
+ undefined ->
+ case pgsql_prepare(Query, State) of
+ {ok, _, _, _} ->
+ put(Key, prepared);
+ {error, Error} ->
+ ?ERROR_MSG("PREPARE failed for SQL query "
+ "at ~p: ~p",
+ [Query#sql_query.loc, Error]),
+ put(Key, ignore)
+ end;
+ _ ->
+ ok
+ end,
+ case get(Key) of
+ prepared ->
+ pgsql_execute_sql_query(Query, State);
+ _ ->
+ generic_sql_query(Query)
+ end;
+ mysql ->
+ generic_sql_query(Query);
+ sqlite ->
+ generic_sql_query(Query)
+ end
+ catch
+ Class:Reason ->
+ ST = erlang:get_stacktrace(),
+ ?ERROR_MSG("Internal error while processing SQL query: ~p",
+ [{Class, Reason, ST}]),
+ {error, <<"internal error">>}
+ end,
+ case Res of
+ {error, <<"No SQL-driver information available.">>} ->
+ {updated, 0};
+ _Else -> Res
+ end;
+sql_query_internal(F) when is_function(F) ->
+ case catch execute_fun(F) of
+ {'EXIT', Reason} -> {error, Reason};
+ Res -> Res
+ end;
sql_query_internal(Query) ->
State = get(?STATE_KEY),
?DEBUG("SQL: \"~s\"", [Query]),
@@ -495,6 +575,93 @@ sql_query_internal(Query) ->
_Else -> Res
end.
+select_sql_query(Queries, State) ->
+ select_sql_query(
+ Queries, State#state.db_type, State#state.db_version, undefined).
+
+select_sql_query([], _Type, _Version, undefined) ->
+ undefined;
+select_sql_query([], _Type, _Version, Query) ->
+ Query;
+select_sql_query([{any, Query} | _], _Type, _Version, _) ->
+ Query;
+select_sql_query([{Type, Query} | _], Type, _Version, _) ->
+ Query;
+select_sql_query([{{Type, _Version1}, Query1} | Rest], Type, undefined, _) ->
+ select_sql_query(Rest, Type, undefined, Query1);
+select_sql_query([{{Type, Version1}, Query1} | Rest], Type, Version, Query) ->
+ if
+ Version >= Version1 ->
+ Query1;
+ true ->
+ select_sql_query(Rest, Type, Version, Query)
+ end;
+select_sql_query([{_, _} | Rest], Type, Version, Query) ->
+ select_sql_query(Rest, Type, Version, Query).
+
+generic_sql_query(SQLQuery) ->
+ sql_query_format_res(
+ sql_query_internal(generic_sql_query_format(SQLQuery)),
+ SQLQuery).
+
+generic_sql_query_format(SQLQuery) ->
+ Args = (SQLQuery#sql_query.args)(generic_escape()),
+ (SQLQuery#sql_query.format_query)(Args).
+
+generic_escape() ->
+ #sql_escape{string = fun(X) -> <<"'", (escape(X))/binary, "'">> end,
+ integer = fun(X) -> integer_to_binary(X) end,
+ boolean = fun(true) -> <<"1">>;
+ (false) -> <<"0">>
+ end
+ }.
+
+pgsql_prepare(SQLQuery, State) ->
+ Escape = #sql_escape{_ = fun(X) -> X end},
+ N = length((SQLQuery#sql_query.args)(Escape)),
+ Args = [<<$$, (integer_to_binary(I))/binary>> || I <- lists:seq(1, N)],
+ Query = (SQLQuery#sql_query.format_query)(Args),
+ pgsql:prepare(State#state.db_ref, SQLQuery#sql_query.hash, Query).
+
+pgsql_execute_escape() ->
+ #sql_escape{string = fun(X) -> X end,
+ integer = fun(X) -> [integer_to_binary(X)] end,
+ boolean = fun(true) -> "1";
+ (false) -> "0"
+ end
+ }.
+
+pgsql_execute_sql_query(SQLQuery, State) ->
+ Args = (SQLQuery#sql_query.args)(pgsql_execute_escape()),
+ ExecuteRes =
+ pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args),
+% {T, ExecuteRes} =
+% timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]),
+% io:format("T ~s ~p~n", [SQLQuery#sql_query.hash, T]),
+ Res = pgsql_execute_to_odbc(ExecuteRes),
+ sql_query_format_res(Res, SQLQuery).
+
+
+sql_query_format_res({selected, _, Rows}, SQLQuery) ->
+ Res =
+ lists:flatmap(
+ fun(Row) ->
+ try
+ [(SQLQuery#sql_query.format_res)(Row)]
+ catch
+ Class:Reason ->
+ ST = erlang:get_stacktrace(),
+ ?ERROR_MSG("Error while processing "
+ "SQL query result: ~p~n"
+ "row: ~p",
+ [{Class, Reason, ST}, Row]),
+ []
+ end
+ end, Rows),
+ {selected, Res};
+sql_query_format_res(Res, _SQLQuery) ->
+ Res.
+
%% Generate the OTP callback return tuple depending on the driver result.
abort_on_driver_error({error, <<"query timed out">>} =
Reply,
@@ -606,6 +773,18 @@ pgsql_item_to_odbc(<<"UPDATE ", N/binary>>) ->
pgsql_item_to_odbc({error, Error}) -> {error, Error};
pgsql_item_to_odbc(_) -> {updated, undefined}.
+pgsql_execute_to_odbc({ok, {<<"SELECT", _/binary>>, Rows}}) ->
+ {selected, [], [[Field || {_, Field} <- Row] || Row <- Rows]};
+pgsql_execute_to_odbc({ok, {'INSERT', N}}) ->
+ {updated, N};
+pgsql_execute_to_odbc({ok, {'DELETE', N}}) ->
+ {updated, N};
+pgsql_execute_to_odbc({ok, {'UPDATE', N}}) ->
+ {updated, N};
+pgsql_execute_to_odbc({error, Error}) -> {error, Error};
+pgsql_execute_to_odbc(_) -> {updated, undefined}.
+
+
%% == Native MySQL code
%% part of init/1
@@ -618,7 +797,7 @@ mysql_connect(Server, Port, DB, Username, Password) ->
of
{ok, Ref} ->
p1_mysql_conn:fetch(
- Ref, [<<"set names 'utf8';">>], self()),
+ Ref, [<<"set names 'utf8mb4' collate 'utf8mb4_bin';">>], self()),
{ok, Ref};
Err -> Err
end.
@@ -658,6 +837,24 @@ to_odbc({error, Reason}) when is_list(Reason) ->
to_odbc(Res) ->
Res.
+get_db_version(#state{db_type = pgsql} = State) ->
+ case pgsql:squery(State#state.db_ref,
+ <<"select current_setting('server_version_num')">>) of
+ {ok, [{_, _, [[SVersion]]}]} ->
+ case catch binary_to_integer(SVersion) of
+ Version when is_integer(Version) ->
+ State#state{db_version = Version};
+ Error ->
+ ?WARNING_MSG("error getting pgsql version: ~p", [Error]),
+ State
+ end;
+ Res ->
+ ?WARNING_MSG("error getting pgsql version: ~p", [Res]),
+ State
+ end;
+get_db_version(State) ->
+ State.
+
log(Level, Format, Args) ->
case Level of
debug -> ?DEBUG(Format, Args);
@@ -800,8 +997,17 @@ fsm_limit_opts() ->
_ -> []
end.
+check_error({error, Why} = Err, #sql_query{} = Query) ->
+ ?ERROR_MSG("SQL query '~s' at ~p failed: ~p",
+ [Query#sql_query.hash, Query#sql_query.loc, Why]),
+ Err;
check_error({error, Why} = Err, Query) ->
- ?ERROR_MSG("SQL query '~s' failed: ~p", [Query, Why]),
+ case catch iolist_to_binary(Query) of
+ SQuery when is_binary(SQuery) ->
+ ?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Why]);
+ _ ->
+ ?ERROR_MSG("SQL query ~p failed: ~p", [Query, Why])
+ end,
Err;
check_error(Result, _Query) ->
Result.
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 50c8e323..123189dd 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -64,7 +64,7 @@
-define(NS_PIEFXIS, <<"http://www.xmpp.org/extensions/xep-0227.html#ns">>).
-define(NS_XI, <<"http://www.w3.org/2001/XInclude">>).
--record(state, {xml_stream_state :: xml_stream:xml_stream_state(),
+-record(state, {xml_stream_state :: fxml_stream:xml_stream_state(),
user = <<"">> :: binary(),
server = <<"">> :: binary(),
fd :: file:io_device(),
@@ -80,12 +80,11 @@ import_file(FileName) ->
import_file(FileName, #state{}).
-spec import_file(binary(), state()) -> ok | {error, atom()}.
-
import_file(FileName, State) ->
case file:open(FileName, [read, binary]) of
{ok, Fd} ->
Dir = filename:dirname(FileName),
- XMLStreamState = xml_stream:new(self(), infinity),
+ XMLStreamState = fxml_stream:new(self(), infinity),
Res = process(State#state{xml_stream_state = XMLStreamState,
fd = Fd,
dir = Dir}),
@@ -97,72 +96,14 @@ import_file(FileName, State) ->
{error, Reason}
end.
-%%%==================================
-%%%% Process Elements
-%%%==================================
-%%%% Process Element
-%%%==================================
-%%%% Add user
-%% @spec (El::xmlel(), Domain::string(), User::binary(), Password::binary() | none)
-%% -> ok | {error, ErrorText::string()}
-%% @doc Add a new user to the database.
-%% If user already exists, it will be only updated.
-spec export_server(binary()) -> any().
-
-%% @spec (User::string(), Password::string(), Domain::string())
-%% -> ok | {atomic, exists} | {error, not_allowed}
-%% @doc Create a new user
export_server(Dir) ->
export_hosts(?MYHOSTS, Dir).
-%%%==================================
-%%%% Populate user
-%% @spec (User::string(), Domain::string(), El::xml())
-%% -> ok | {error, not_found}
-%%
-%% @doc Add a new user from a XML file with a roster list.
-%%
-%% Example of a file:
-%% ```
-%% <?xml version='1.0' encoding='UTF-8'?>
-%% <server-data xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'>
-%% <host jid='localhost'>
-%% <user name='juliet' password='s3crEt'>
-%% <query xmlns='jabber:iq:roster'>
-%% <item jid='romeo@montague.net'
-%% name='Romeo'
-%% subscription='both'>
-%% <group>Friends</group>
-%% </item>
-%% </query>
-%% </user>
-%% </host>
-%% </server-data>
-%% '''
-spec export_host(binary(), binary()) -> any().
-
export_host(Dir, Host) ->
export_hosts([Host], Dir).
-%% @spec User = String with the user name
-%% Domain = String with a domain name
-%% El = Sub XML element with vCard tags values
-%% @ret ok | {error, not_found}
-%% @doc Read vcards from the XML and send it to the server
-%%
-%% Example:
-%% ```
-%% <?xml version='1.0' encoding='UTF-8'?>
-%% <server-data xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'>
-%% <host jid='localhost'>
-%% <user name='admin' password='s3crEt'>
-%% <vCard xmlns='vcard-temp'>
-%% <FN>Admin</FN>
-%% </vCard>
-%% </user>
-%% </host>
-%% </server-data>
-%% '''
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -194,11 +135,6 @@ export_hosts(Hosts, Dir) ->
{error, Reason}
end.
-%% @spec User = String with the user name
-%% Domain = String with a domain name
-%% El = Sub XML element with offline messages values
-%% @ret ok | {error, not_found}
-%% @doc Read off-line message from the XML and send it to the server
export_host(Dir, FnH, Host) ->
DFn = make_host_basefilename(Dir, FnH),
case file:open(DFn, [raw, write]) of
@@ -223,11 +159,6 @@ export_host(Dir, FnH, Host) ->
{error, Reason}
end.
-%% @spec User = String with the user name
-%% Domain = String with a domain name
-%% El = Sub XML element with private storage values
-%% @ret ok | {error, not_found}
-%% @doc Private storage parsing
export_users([{User, _S}|Users], Server, Fd) ->
case export_user(User, Server, Fd) of
ok ->
@@ -238,8 +169,6 @@ export_users([{User, _S}|Users], Server, Fd) ->
export_users([], _Server, _Fd) ->
ok.
-%%%==================================
-%%%% Utilities
export_user(User, Server, Fd) ->
Password = ejabberd_auth:get_password_s(User, Server),
LServer = jid:nameprep(Server),
@@ -257,7 +186,7 @@ export_user(User, Server, Fd) ->
get_privacy(User, Server) ++
get_roster(User, Server) ++
get_private(User, Server),
- print(Fd, xml:element_to_binary(
+ print(Fd, fxml:element_to_binary(
#xmlel{name = <<"user">>,
attrs = [{<<"name">>, User},
{<<"password">>, Pass}],
@@ -289,7 +218,6 @@ get_vcard(User, Server) ->
[]
end.
-%%%==================================
get_offline(User, Server) ->
case mod_offline:get_offline_els(User, Server) of
[] ->
@@ -306,7 +234,6 @@ get_offline(User, Server) ->
[#xmlel{name = <<"offline-messages">>, children = NewEls}]
end.
-%%%% Export hosts
get_privacy(User, Server) ->
case mod_privacy:get_user_lists(User, Server) of
{ok, #privacy{default = Default,
@@ -333,7 +260,6 @@ get_privacy(User, Server) ->
[]
end.
-%% @spec (Dir::string(), Hosts::[string()]) -> ok
get_roster(User, Server) ->
JID = jid:make(User, Server, <<>>),
case mod_roster:get_roster(User, Server) of
@@ -387,17 +313,17 @@ get_private(User, Server) ->
process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) ->
case file:read(Fd, ?CHUNK_SIZE) of
{ok, Data} ->
- NewXMLStreamState = xml_stream:parse(XMLStreamState, Data),
+ NewXMLStreamState = fxml_stream:parse(XMLStreamState, Data),
case process_els(State#state{xml_stream_state =
NewXMLStreamState}) of
{ok, NewState} ->
process(NewState);
Err ->
- xml_stream:close(NewXMLStreamState),
+ fxml_stream:close(NewXMLStreamState),
Err
end;
eof ->
- xml_stream:close(XMLStreamState),
+ fxml_stream:close(XMLStreamState),
ok
end.
@@ -415,7 +341,7 @@ process_els(State) ->
end.
process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_PIEFXIS ->
{ok, State};
?NS_PIE ->
@@ -430,7 +356,7 @@ process_el({xmlstreamcdata, _}, State) ->
process_el({xmlstreamelement, #xmlel{name = <<"xi:include">>,
attrs = Attrs}},
#state{dir = Dir, user = <<"">>} = State) ->
- FileName = xml:get_attr_s(<<"href">>, Attrs),
+ FileName = fxml:get_attr_s(<<"href">>, Attrs),
case import_file(filename:join([Dir, FileName]), State) of
ok ->
{ok, State};
@@ -443,7 +369,7 @@ process_el({xmlstreamstart, <<"host">>, Attrs}, State) ->
process_el({xmlstreamelement, #xmlel{name = <<"host">>,
attrs = Attrs,
children = Els}}, State) ->
- JIDS = xml:get_attr_s(<<"jid">>, Attrs),
+ JIDS = fxml:get_attr_s(<<"jid">>, Attrs),
case jid:from_string(JIDS) of
#jid{lserver = S} ->
case lists:member(S, ?MYHOSTS) of
@@ -486,8 +412,8 @@ process_users([], State) ->
process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
#state{server = LServer} = State) ->
- Name = xml:get_attr_s(<<"name">>, Attrs),
- Password = xml:get_attr_s(<<"password">>, Attrs),
+ Name = fxml:get_attr_s(<<"name">>, Attrs),
+ Password = fxml:get_attr_s(<<"password">>, Attrs),
PasswordFormat = ejabberd_config:get_option({auth_password_format, LServer}, fun(X) -> X end, plain),
Pass = case PasswordFormat of
scram ->
@@ -525,7 +451,7 @@ process_user_els([], State) ->
process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El,
State) ->
- case {Name, xml:get_attr_s(<<"xmlns">>, Attrs)} of
+ case {Name, fxml:get_attr_s(<<"xmlns">>, Attrs)} of
{<<"query">>, ?NS_ROSTER} ->
process_roster(El, State);
{<<"query">>, ?NS_PRIVACY} ->
@@ -576,15 +502,13 @@ process_roster(El, State = #state{user = U, server = S}) ->
stop("Failed to write roster: ~p", [Err])
end.
-%%%==================================
-%%%% Export server
process_privacy(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_privacy:process_iq_set(
[], JID, JID, #iq{type = set, sub_el = El}) of
{error, Error} = Err ->
#xmlel{children = Els} = El,
- Name = case xml:remove_cdata(Els) of
+ Name = case fxml:remove_cdata(Els) of
[#xmlel{name = N}] -> N;
_ -> undefined
end,
@@ -603,7 +527,6 @@ process_privacy(El, State = #state{user = U, server = S}) ->
{ok, State}
end.
-%% @spec (Dir::string()) -> ok
process_private(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_private:process_sm_iq(
@@ -614,8 +537,6 @@ process_private(El, State = #state{user = U, server = S}) ->
stop("Failed to write private: ~p", [Err])
end.
-%%%==================================
-%%%% Export host
process_vcard(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_vcard:process_sm_iq(
@@ -626,9 +547,8 @@ process_vcard(El, State = #state{user = U, server = S}) ->
stop("Failed to write vcard: ~p", [Err])
end.
-%% @spec (Dir::string(), Host::string()) -> ok
process_offline_msg(El, State = #state{user = U, server = S}) ->
- FromS = xml:get_attr_s(<<"from">>, El#xmlel.attrs),
+ FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
#jid{} = From ->
To = jid:make(U, S, <<>>),
@@ -643,9 +563,8 @@ process_offline_msg(El, State = #state{user = U, server = S}) ->
stop("Invalid 'from' = ~s", [FromS])
end.
-%% @spec (Dir::string(), Fn::string(), Host::string()) -> ok
process_presence(El, #state{user = U, server = S} = State) ->
- FromS = xml:get_attr_s(<<"from">>, El#xmlel.attrs),
+ FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
#jid{} = From ->
To = jid:make(U, S, <<>>),
diff --git a/src/ejabberd_receiver.erl b/src/ejabberd_receiver.erl
index 78585529..0a33e30e 100644
--- a/src/ejabberd_receiver.erl
+++ b/src/ejabberd_receiver.erl
@@ -48,15 +48,16 @@
-include("logger.hrl").
-record(state,
- {socket :: inet:socket() | p1_tls:tls_socket() | ezlib:zlib_socket(),
- sock_mod = gen_tcp :: gen_tcp | p1_tls | ezlib,
+ {socket :: inet:socket() | fast_tls:tls_socket() | ezlib:zlib_socket(),
+ sock_mod = gen_tcp :: gen_tcp | fast_tls | ezlib,
shaper_state = none :: shaper:shaper(),
c2s_pid :: pid(),
max_stanza_size = infinity :: non_neg_integer() | infinity,
- xml_stream_state :: xml_stream:xml_stream_state(),
+ xml_stream_state :: fxml_stream:xml_stream_state(),
timeout = infinity:: timeout()}).
--define(HIBERNATE_TIMEOUT, 90000).
+-define(HIBERNATE_TIMEOUT, ejabberd_config:get_option(receiver_hibernate, fun(X) when is_integer(X); X == hibernate-> X end, 90000)).
+
-spec start_link(inet:socket(), atom(), shaper:shaper(),
non_neg_integer() | infinity) -> ignore |
@@ -89,7 +90,7 @@ change_shaper(Pid, Shaper) ->
reset_stream(Pid) -> do_call(Pid, reset_stream).
--spec starttls(pid(), p1_tls:tls_socket()) -> ok.
+-spec starttls(pid(), fast_tls:tls_socket()) -> ok.
starttls(Pid, TLSSocket) ->
do_call(Pid, {starttls, TLSSocket}).
@@ -129,8 +130,8 @@ init([Socket, SockMod, Shaper, MaxStanzaSize]) ->
handle_call({starttls, TLSSocket}, _From, State) ->
State1 = reset_parser(State),
NewState = State1#state{socket = TLSSocket,
- sock_mod = p1_tls},
- case p1_tls:recv_data(TLSSocket, <<"">>) of
+ sock_mod = fast_tls},
+ case fast_tls:recv_data(TLSSocket, <<"">>) of
{ok, TLSData} ->
{reply, ok,
process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
@@ -140,6 +141,7 @@ handle_call({starttls, TLSSocket}, _From, State) ->
handle_call({compress, Data}, _From,
#state{socket = Socket, sock_mod = SockMod} =
State) ->
+ ejabberd:start_app(ezlib),
{ok, ZlibSocket} = ezlib:enable_zlib(SockMod,
Socket),
if Data /= undefined -> do_send(State, Data);
@@ -160,7 +162,7 @@ handle_call(reset_stream, _From, State) ->
Reply = ok,
{reply, Reply, NewState, ?HIBERNATE_TIMEOUT};
handle_call({become_controller, C2SPid}, _From, State) ->
- XMLStreamState = xml_stream:new(C2SPid, State#state.max_stanza_size),
+ XMLStreamState = fxml_stream:new(C2SPid, State#state.max_stanza_size),
NewState = State#state{c2s_pid = C2SPid,
xml_stream_state = XMLStreamState},
activate_socket(NewState),
@@ -182,8 +184,8 @@ handle_info({Tag, _TCPSocket, Data},
when (Tag == tcp) or (Tag == ssl) or
(Tag == ejabberd_xml) ->
case SockMod of
- p1_tls ->
- case p1_tls:recv_data(Socket, Data) of
+ fast_tls ->
+ case fast_tls:recv_data(Socket, Data) of
{ok, TLSData} ->
{noreply, process_data(TLSData, State),
?HIBERNATE_TIMEOUT};
@@ -284,7 +286,7 @@ process_data(Data,
undefined ->
XMLStreamState;
_ ->
- xml_stream:parse(XMLStreamState, Data)
+ fxml_stream:parse(XMLStreamState, Data)
end,
{NewShaperState, Pause} = shaper:update(ShaperState, byte_size(Data)),
if
@@ -309,7 +311,7 @@ element_wrapper(Element) -> Element.
close_stream(undefined) -> ok;
close_stream(XMLStreamState) ->
- xml_stream:close(XMLStreamState).
+ fxml_stream:close(XMLStreamState).
reset_parser(#state{xml_stream_state = undefined} = State) ->
State;
@@ -317,14 +319,14 @@ reset_parser(#state{c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize,
xml_stream_state = XMLStreamState}
= State) ->
- NewStreamState = try xml_stream:reset(XMLStreamState)
+ NewStreamState = try fxml_stream:reset(XMLStreamState)
catch error:_ ->
close_stream(XMLStreamState),
case C2SPid of
undefined ->
undefined;
_ ->
- xml_stream:new(C2SPid, MaxStanzaSize)
+ fxml_stream:new(C2SPid, MaxStanzaSize)
end
end,
State#state{xml_stream_state = NewStreamState}.
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index 5ca1262e..e29d6acf 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -36,7 +36,9 @@
route_error/4,
register_route/1,
register_route/2,
+ register_route/3,
register_routes/1,
+ host_of_route/1,
unregister_route/1,
unregister_routes/1,
dirty_get_all_routes/0,
@@ -55,7 +57,7 @@
-type local_hint() :: undefined | integer() | {apply, atom(), atom()}.
--record(route, {domain, pid, local_hint}).
+-record(route, {domain, server_host, pid, local_hint}).
-record(state, {}).
@@ -86,7 +88,7 @@ route(From, To, Packet) ->
route_error(From, To, ErrPacket, OrigPacket) ->
#xmlel{attrs = Attrs} = OrigPacket,
- case <<"error">> == xml:get_attr_s(<<"type">>, Attrs) of
+ case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of
false -> route(From, To, ErrPacket);
true -> ok
end.
@@ -94,19 +96,29 @@ route_error(From, To, ErrPacket, OrigPacket) ->
-spec register_route(binary()) -> term().
register_route(Domain) ->
- register_route(Domain, undefined).
+ ?WARNING_MSG("~s:register_route/1 is deprected, "
+ "use ~s:register_route/2 instead",
+ [?MODULE, ?MODULE]),
+ register_route(Domain, ?MYNAME).
--spec register_route(binary(), local_hint()) -> term().
+-spec register_route(binary(), binary()) -> term().
-register_route(Domain, LocalHint) ->
- case jid:nameprep(Domain) of
- error -> erlang:error({invalid_domain, Domain});
- LDomain ->
+register_route(Domain, ServerHost) ->
+ register_route(Domain, ServerHost, undefined).
+
+-spec register_route(binary(), binary(), local_hint()) -> term().
+
+register_route(Domain, ServerHost, LocalHint) ->
+ case {jid:nameprep(Domain), jid:nameprep(ServerHost)} of
+ {error, _} -> erlang:error({invalid_domain, Domain});
+ {_, error} -> erlang:error({invalid_domain, ServerHost});
+ {LDomain, LServerHost} ->
Pid = self(),
case get_component_number(LDomain) of
undefined ->
F = fun () ->
mnesia:write(#route{domain = LDomain, pid = Pid,
+ server_host = LServerHost,
local_hint = LocalHint})
end,
mnesia:transaction(F);
@@ -115,46 +127,42 @@ register_route(Domain, LocalHint) ->
case mnesia:wread({route, LDomain}) of
[] ->
mnesia:write(#route{domain = LDomain,
+ server_host = LServerHost,
pid = Pid,
local_hint = 1}),
- lists:foreach(fun (I) ->
- mnesia:write(#route{domain
- =
- LDomain,
- pid
- =
- undefined,
- local_hint
- =
- I})
- end,
- lists:seq(2, N));
+ lists:foreach(
+ fun (I) ->
+ mnesia:write(
+ #route{domain = LDomain,
+ pid = undefined,
+ server_host = LServerHost,
+ local_hint = I})
+ end,
+ lists:seq(2, N));
Rs ->
- lists:any(fun (#route{pid = undefined,
- local_hint = I} =
- R) ->
- mnesia:write(#route{domain =
- LDomain,
- pid =
- Pid,
- local_hint
- =
- I}),
- mnesia:delete_object(R),
- true;
- (_) -> false
- end,
- Rs)
+ lists:any(
+ fun (#route{pid = undefined,
+ local_hint = I} = R) ->
+ mnesia:write(
+ #route{domain = LDomain,
+ pid = Pid,
+ server_host = LServerHost,
+ local_hint = I}),
+ mnesia:delete_object(R),
+ true;
+ (_) -> false
+ end,
+ Rs)
end
end,
mnesia:transaction(F)
end
end.
--spec register_routes([binary()]) -> ok.
+-spec register_routes([{binary(), binary()}]) -> ok.
register_routes(Domains) ->
- lists:foreach(fun (Domain) -> register_route(Domain)
+ lists:foreach(fun ({Domain, ServerHost}) -> register_route(Domain, ServerHost)
end,
Domains).
@@ -183,7 +191,9 @@ unregister_route(Domain) ->
of
[R] ->
I = R#route.local_hint,
+ ServerHost = R#route.server_host,
mnesia:write(#route{domain = LDomain,
+ server_host = ServerHost,
pid = undefined,
local_hint = I}),
mnesia:delete_object(R);
@@ -211,6 +221,20 @@ dirty_get_all_routes() ->
dirty_get_all_domains() ->
lists:usort(mnesia:dirty_all_keys(route)).
+-spec host_of_route(binary()) -> binary().
+
+host_of_route(Domain) ->
+ case jid:nameprep(Domain) of
+ error ->
+ erlang:error({invalid_domain, Domain});
+ LDomain ->
+ case mnesia:dirty_read(route, LDomain) of
+ [#route{server_host = ServerHost}|_] ->
+ ServerHost;
+ [] ->
+ erlang:error({unregistered_route, Domain})
+ end
+ end.
%%====================================================================
%% gen_server callbacks
@@ -283,8 +307,11 @@ handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
if is_integer(E#route.local_hint) ->
LDomain = E#route.domain,
I = E#route.local_hint,
+ ServerHost = E#route.server_host,
mnesia:write(#route{domain =
LDomain,
+ server_host =
+ ServerHost,
pid =
undefined,
local_hint =
@@ -394,12 +421,10 @@ get_component_number(LDomain) ->
undefined).
update_tables() ->
- case catch mnesia:table_info(route, attributes) of
- [domain, node, pid] -> mnesia:delete_table(route);
- [domain, pid] -> mnesia:delete_table(route);
- [domain, pid, local_hint] -> ok;
- [domain, pid, local_hint|_] -> mnesia:delete_table(route);
- {'EXIT', _} -> ok
+ try
+ mnesia:transform_table(route, ignore, record_info(fields, route))
+ catch exit:{aborted, {no_exists, _}} ->
+ ok
end,
case lists:member(local_route,
mnesia:system_info(tables))
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
index 2f026b32..0eab4633 100644
--- a/src/ejabberd_s2s.erl
+++ b/src/ejabberd_s2s.erl
@@ -214,7 +214,7 @@ check_peer_certificate(SockMod, Sock, Peer) ->
end
end;
VerifyRes ->
- {error, p1_tls:get_cert_verify_string(VerifyRes, Cert)}
+ {error, fast_tls:get_cert_verify_string(VerifyRes, Cert)}
end;
{error, _Reason} ->
{error, <<"Cannot get peer certificate">>};
@@ -308,7 +308,7 @@ do_route(From, To, Packet) ->
#xmlel{name = Name, attrs = NewAttrs, children = Els}),
ok;
{aborted, _Reason} ->
- case xml:get_tag_attr_s(<<"type">>, Packet) of
+ case fxml:get_tag_attr_s(<<"type">>, Packet) of
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 29dd5e9d..c8d3cd04 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -85,16 +85,16 @@
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(INVALID_NAMESPACE_ERR,
- xml:element_to_binary(?SERR_INVALID_NAMESPACE)).
+ fxml:element_to_binary(?SERR_INVALID_NAMESPACE)).
-define(HOST_UNKNOWN_ERR,
- xml:element_to_binary(?SERR_HOST_UNKNOWN)).
+ fxml:element_to_binary(?SERR_HOST_UNKNOWN)).
-define(INVALID_FROM_ERR,
- xml:element_to_binary(?SERR_INVALID_FROM)).
+ fxml:element_to_binary(?SERR_INVALID_FROM)).
-define(INVALID_XML_ERR,
- xml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
+ fxml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
start(SockData, Opts) ->
supervisor:start_child(ejabberd_s2s_in_sup,
@@ -188,10 +188,10 @@ init([{SockMod, Socket}, Opts]) ->
wait_for_stream({xmlstreamstart, _Name, Attrs},
StateData) ->
- case {xml:get_attr_s(<<"xmlns">>, Attrs),
- xml:get_attr_s(<<"xmlns:db">>, Attrs),
- xml:get_attr_s(<<"to">>, Attrs),
- xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs),
+ fxml:get_attr_s(<<"xmlns:db">>, Attrs),
+ fxml:get_attr_s(<<"to">>, Attrs),
+ fxml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
of
{<<"jabber:server">>, _, Server, true}
when StateData#state.tls and
@@ -199,7 +199,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
send_text(StateData,
?STREAM_HEADER(<<" version='1.0'">>)),
Auth = if StateData#state.tls_enabled ->
- case jid:nameprep(xml:get_attr_s(<<"from">>, Attrs)) of
+ case jid:nameprep(fxml:get_attr_s(<<"from">>, Attrs)) of
From when From /= <<"">>, From /= error ->
{Result, Message} =
ejabberd_s2s:check_peer_certificate(StateData#state.sockmod,
@@ -234,7 +234,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)",
[StateData#state.server, RemoteServer, CertError]),
send_text(StateData,
- <<(xml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>,
+ <<(fxml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>,
CertError)))/binary,
(?STREAM_TRAILER)/binary>>),
{stop, normal, StateData};
@@ -306,7 +306,7 @@ wait_for_feature_request({xmlstreamelement, El},
TLSEnabled = StateData#state.tls_enabled,
SockMod =
(StateData#state.sockmod):get_sockmod(StateData#state.socket),
- case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs), Name} of
{?NS_TLS, <<"starttls">>}
when TLS == true, TLSEnabled == false,
SockMod == gen_tcp ->
@@ -331,7 +331,7 @@ wait_for_feature_request({xmlstreamelement, El},
end,
TLSSocket = (StateData#state.sockmod):starttls(Socket,
TLSOpts,
- xml:element_to_binary(#xmlel{name
+ fxml:element_to_binary(#xmlel{name
=
<<"proceed">>,
attrs
@@ -345,7 +345,7 @@ wait_for_feature_request({xmlstreamelement, El},
StateData#state{socket = TLSSocket, streamid = new_id(),
tls_enabled = true, tls_options = TLSOpts}};
{?NS_SASL, <<"auth">>} when TLSEnabled ->
- Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
+ Mech = fxml:get_attr_s(<<"mechanism">>, Attrs),
case Mech of
<<"EXTERNAL">> when StateData#state.auth_domain /= <<"">> ->
AuthDomain = StateData#state.auth_domain,
@@ -447,9 +447,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
_ ->
NewEl = jlib:remove_attr(<<"xmlns">>, El),
#xmlel{name = Name, attrs = Attrs} = NewEl,
- From_s = xml:get_attr_s(<<"from">>, Attrs),
+ From_s = fxml:get_attr_s(<<"from">>, Attrs),
From = jid:from_string(From_s),
- To_s = xml:get_attr_s(<<"to">>, Attrs),
+ To_s = fxml:get_attr_s(<<"to">>, Attrs),
To = jid:from_string(To_s),
if (To /= error) and (From /= error) ->
LFrom = From#jid.lserver,
@@ -626,7 +626,7 @@ send_text(StateData, Text) ->
Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_binary(El)).
+ send_text(StateData, fxml:element_to_binary(El)).
change_shaper(StateData, Host, JID) ->
Shaper = acl:match_rule(Host, StateData#state.shaper,
@@ -643,15 +643,15 @@ cancel_timer(Timer) ->
is_key_packet(#xmlel{name = Name, attrs = Attrs,
children = Els})
when Name == <<"db:result">> ->
- {key, xml:get_attr_s(<<"to">>, Attrs),
- xml:get_attr_s(<<"from">>, Attrs),
- xml:get_attr_s(<<"id">>, Attrs), xml:get_cdata(Els)};
+ {key, fxml:get_attr_s(<<"to">>, Attrs),
+ fxml:get_attr_s(<<"from">>, Attrs),
+ fxml:get_attr_s(<<"id">>, Attrs), fxml:get_cdata(Els)};
is_key_packet(#xmlel{name = Name, attrs = Attrs,
children = Els})
when Name == <<"db:verify">> ->
- {verify, xml:get_attr_s(<<"to">>, Attrs),
- xml:get_attr_s(<<"from">>, Attrs),
- xml:get_attr_s(<<"id">>, Attrs), xml:get_cdata(Els)};
+ {verify, fxml:get_attr_s(<<"to">>, Attrs),
+ fxml:get_attr_s(<<"from">>, Attrs),
+ fxml:get_attr_s(<<"id">>, Attrs), fxml:get_cdata(Els)};
is_key_packet(_) -> false.
fsm_limit_opts(Opts) ->
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index 69618573..594fbb2c 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -105,13 +105,13 @@
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(INVALID_NAMESPACE_ERR,
- xml:element_to_binary(?SERR_INVALID_NAMESPACE)).
+ fxml:element_to_binary(?SERR_INVALID_NAMESPACE)).
-define(HOST_UNKNOWN_ERR,
- xml:element_to_binary(?SERR_HOST_UNKNOWN)).
+ fxml:element_to_binary(?SERR_HOST_UNKNOWN)).
-define(INVALID_XML_ERR,
- xml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
+ fxml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
-define(SOCKET_DEFAULT_RESULT, {error, badarg}).
@@ -323,15 +323,15 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
true ->
{no_verify, <<"Not verified">>, StateData}
end,
- RemoteStreamID = xml:get_attr_s(<<"id">>, Attrs),
+ RemoteStreamID = fxml:get_attr_s(<<"id">>, Attrs),
NewStateData = StateData0#state{remote_streamid = RemoteStreamID},
- case {xml:get_attr_s(<<"xmlns">>, Attrs),
- xml:get_attr_s(<<"xmlns:db">>, Attrs),
- xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs),
+ fxml:get_attr_s(<<"xmlns:db">>, Attrs),
+ fxml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
of
_ when CertCheckRes == error ->
send_text(NewStateData,
- <<(xml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>,
+ <<(fxml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>,
CertCheckMsg)))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (~s)",
@@ -495,7 +495,7 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
STLSReq} =
Acc) ->
case
- xml:get_attr_s(<<"xmlns">>,
+ fxml:get_attr_s(<<"xmlns">>,
Attrs1)
of
?NS_SASL ->
@@ -508,7 +508,7 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
=
Els2}) ->
case
- xml:get_cdata(Els2)
+ fxml:get_cdata(Els2)
of
<<"EXTERNAL">> ->
true;
@@ -533,13 +533,13 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
_STLSReq} =
Acc) ->
case
- xml:get_attr_s(<<"xmlns">>,
+ fxml:get_attr_s(<<"xmlns">>,
Attrs1)
of
?NS_TLS ->
Req =
case
- xml:get_subtag(El1,
+ fxml:get_subtag(El1,
<<"required">>)
of
#xmlel{} ->
@@ -610,7 +610,7 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
end;
_ ->
send_text(StateData,
- <<(xml:element_to_binary(?SERR_BAD_FORMAT))/binary,
+ <<(fxml:element_to_binary(?SERR_BAD_FORMAT))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad "
"format)",
@@ -637,7 +637,7 @@ wait_for_auth_result({xmlstreamelement, El},
StateData) ->
case El of
#xmlel{name = <<"success">>, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_SASL ->
?DEBUG("auth: ~p",
[{StateData#state.myname, StateData#state.server}]),
@@ -653,7 +653,7 @@ wait_for_auth_result({xmlstreamelement, El},
?FSMTIMEOUT};
_ ->
send_text(StateData,
- <<(xml:element_to_binary(?SERR_BAD_FORMAT))/binary,
+ <<(fxml:element_to_binary(?SERR_BAD_FORMAT))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad "
"format)",
@@ -661,7 +661,7 @@ wait_for_auth_result({xmlstreamelement, El},
{stop, normal, StateData}
end;
#xmlel{name = <<"failure">>, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_SASL ->
?DEBUG("restarted: ~p",
[{StateData#state.myname, StateData#state.server}]),
@@ -670,7 +670,7 @@ wait_for_auth_result({xmlstreamelement, El},
StateData#state{socket = undefined}, ?FSMTIMEOUT};
_ ->
send_text(StateData,
- <<(xml:element_to_binary(?SERR_BAD_FORMAT))/binary,
+ <<(fxml:element_to_binary(?SERR_BAD_FORMAT))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad "
"format)",
@@ -679,7 +679,7 @@ wait_for_auth_result({xmlstreamelement, El},
end;
_ ->
send_text(StateData,
- <<(xml:element_to_binary(?SERR_BAD_FORMAT))/binary,
+ <<(fxml:element_to_binary(?SERR_BAD_FORMAT))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad "
"format)",
@@ -707,7 +707,7 @@ wait_for_starttls_proceed({xmlstreamelement, El},
StateData) ->
case El of
#xmlel{name = <<"proceed">>, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_TLS ->
?DEBUG("starttls: ~p",
[{StateData#state.myname, StateData#state.server}]),
@@ -737,7 +737,7 @@ wait_for_starttls_proceed({xmlstreamelement, El},
?FSMTIMEOUT};
_ ->
send_text(StateData,
- <<(xml:element_to_binary(?SERR_BAD_FORMAT))/binary,
+ <<(fxml:element_to_binary(?SERR_BAD_FORMAT))/binary,
(?STREAM_TRAILER)/binary>>),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad "
"format)",
@@ -985,7 +985,7 @@ send_text(StateData, Text) ->
ejabberd_socket:send(StateData#state.socket, Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_binary(El)).
+ send_text(StateData, fxml:element_to_binary(El)).
send_queue(StateData, Q) ->
case queue:out(Q) of
@@ -997,14 +997,14 @@ send_queue(StateData, Q) ->
%% Bounce a single message (xmlelement)
bounce_element(El, Error) ->
#xmlel{attrs = Attrs} = El,
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
Err = jlib:make_error_reply(El, Error),
- From = jid:from_string(xml:get_tag_attr_s(<<"from">>,
+ From = jid:from_string(fxml:get_tag_attr_s(<<"from">>,
El)),
- To = jid:from_string(xml:get_tag_attr_s(<<"to">>,
+ To = jid:from_string(fxml:get_tag_attr_s(<<"to">>,
El)),
ejabberd_router:route(To, From, Err)
end.
@@ -1070,16 +1070,16 @@ send_db_request(StateData) ->
is_verify_res(#xmlel{name = Name, attrs = Attrs})
when Name == <<"db:result">> ->
- {result, xml:get_attr_s(<<"to">>, Attrs),
- xml:get_attr_s(<<"from">>, Attrs),
- xml:get_attr_s(<<"id">>, Attrs),
- xml:get_attr_s(<<"type">>, Attrs)};
+ {result, fxml:get_attr_s(<<"to">>, Attrs),
+ fxml:get_attr_s(<<"from">>, Attrs),
+ fxml:get_attr_s(<<"id">>, Attrs),
+ fxml:get_attr_s(<<"type">>, Attrs)};
is_verify_res(#xmlel{name = Name, attrs = Attrs})
when Name == <<"db:verify">> ->
- {verify, xml:get_attr_s(<<"to">>, Attrs),
- xml:get_attr_s(<<"from">>, Attrs),
- xml:get_attr_s(<<"id">>, Attrs),
- xml:get_attr_s(<<"type">>, Attrs)};
+ {verify, fxml:get_attr_s(<<"to">>, Attrs),
+ fxml:get_attr_s(<<"from">>, Attrs),
+ fxml:get_attr_s(<<"id">>, Attrs),
+ fxml:get_attr_s(<<"type">>, Attrs)};
is_verify_res(_) -> false.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index a9b771b8..5caae610 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -91,10 +91,10 @@
"m:error></stream:stream>">>).
-define(INVALID_XML_ERR,
- xml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
+ fxml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
-define(INVALID_NS_ERR,
- xml:element_to_binary(?SERR_INVALID_NAMESPACE)).
+ fxml:element_to_binary(?SERR_INVALID_NAMESPACE)).
%%%----------------------------------------------------------------------
%%% API
@@ -166,9 +166,9 @@ init([{SockMod, Socket}, Opts]) ->
wait_for_stream({xmlstreamstart, _Name, Attrs},
StateData) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
<<"jabber:component:accept">> ->
- To = xml:get_attr_s(<<"to">>, Attrs),
+ To = fxml:get_attr_s(<<"to">>, Attrs),
Host = jid:nameprep(To),
if Host == error ->
Header = io_lib:format(?STREAM_HEADER,
@@ -180,7 +180,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
{stop, normal, StateData};
true ->
Header = io_lib:format(?STREAM_HEADER,
- [StateData#state.streamid, xml:crypt(To)]),
+ [StateData#state.streamid, fxml:crypt(To)]),
send_text(StateData, Header),
HostOpts = case dict:is_key(Host, StateData#state.host_opts) of
true ->
@@ -212,7 +212,7 @@ wait_for_stream(closed, StateData) ->
wait_for_handshake({xmlstreamelement, El}, StateData) ->
#xmlel{name = Name, children = Els} = El,
- case {Name, xml:get_cdata(Els)} of
+ case {Name, fxml:get_cdata(Els)} of
{<<"handshake">>, Digest} ->
case dict:find(StateData#state.host, StateData#state.host_opts) of
{ok, Password} ->
@@ -222,7 +222,7 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
send_text(StateData, <<"<handshake/>">>),
lists:foreach(
fun (H) ->
- ejabberd_router:register_route(H),
+ ejabberd_router:register_route(H, ?MYNAME),
?INFO_MSG("Route registered for service ~p~n",
[H])
end, dict:fetch_keys(StateData#state.host_opts)),
@@ -250,7 +250,7 @@ wait_for_handshake(closed, StateData) ->
stream_established({xmlstreamelement, El}, StateData) ->
NewEl = jlib:remove_attr(<<"xmlns">>, El),
#xmlel{name = Name, attrs = Attrs} = NewEl,
- From = xml:get_attr_s(<<"from">>, Attrs),
+ From = fxml:get_attr_s(<<"from">>, Attrs),
FromJID = case StateData#state.check_from of
%% If the admin does not want to check the from field
%% when accept packets from any address.
@@ -269,7 +269,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
_ -> error
end
end,
- To = xml:get_attr_s(<<"to">>, Attrs),
+ To = fxml:get_attr_s(<<"to">>, Attrs),
ToJID = case To of
<<"">> -> error;
_ -> jid:from_string(To)
@@ -356,7 +356,7 @@ handle_info({route, From, To, Packet}, StateName,
Attrs2 =
jlib:replace_from_to_attrs(jid:to_string(From),
jid:to_string(To), Attrs),
- Text = xml:element_to_binary(#xmlel{name = Name,
+ Text = fxml:element_to_binary(#xmlel{name = Name,
attrs = Attrs2, children = Els}),
send_text(StateData, Text);
deny ->
@@ -402,7 +402,7 @@ send_text(StateData, Text) ->
Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_binary(El)).
+ send_text(StateData, fxml:element_to_binary(El)).
new_id() -> randoms:get_string().
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index 3045a417..218e657f 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -35,6 +35,7 @@
-export([start/0,
start_link/0,
route/3,
+ process_iq/3,
open_session/5,
open_session/6,
close_session/4,
@@ -50,6 +51,7 @@
dirty_get_my_sessions_list/0,
get_vh_session_list/1,
get_vh_session_number/1,
+ get_vh_by_backend/1,
register_iq_handler/4,
register_iq_handler/5,
unregister_iq_handler/2,
@@ -133,10 +135,10 @@ open_session(SID, User, Server, Resource, Info) ->
-spec close_session(sid(), binary(), binary(), binary()) -> ok.
close_session(SID, User, Server, Resource) ->
- Mod = get_sm_backend(),
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
+ Mod = get_sm_backend(LServer),
Info = case Mod:delete_session(LUser, LServer, LResource, SID) of
{ok, #session{info = I}} -> I;
{error, notfound} -> []
@@ -172,14 +174,14 @@ disconnect_removed_user(User, Server) ->
get_user_resources(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
Ss = Mod:get_sessions(LUser, LServer),
[element(3, S#session.usr) || S <- clean_session_list(Ss)].
-spec get_user_present_resources(binary(), binary()) -> [tuple()].
get_user_present_resources(LUser, LServer) ->
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
Ss = Mod:get_sessions(LUser, LServer),
[{S#session.priority, element(3, S#session.usr)}
|| S <- clean_session_list(Ss), is_integer(S#session.priority)].
@@ -190,7 +192,7 @@ get_user_ip(User, Server, Resource) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer, LResource) of
[] ->
undefined;
@@ -205,7 +207,7 @@ get_user_info(User, Server, Resource) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer, LResource) of
[] ->
offline;
@@ -255,7 +257,7 @@ get_session_pid(User, Server, Resource) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer, LResource) of
[#session{sid = {_, Pid}}] -> Pid;
_ -> none
@@ -264,33 +266,40 @@ get_session_pid(User, Server, Resource) ->
-spec dirty_get_sessions_list() -> [ljid()].
dirty_get_sessions_list() ->
- Mod = get_sm_backend(),
- [S#session.usr || S <- Mod:get_sessions()].
+ lists:flatmap(
+ fun(Mod) ->
+ [S#session.usr || S <- Mod:get_sessions()]
+ end, get_sm_backends()).
-spec dirty_get_my_sessions_list() -> [#session{}].
dirty_get_my_sessions_list() ->
- Mod = get_sm_backend(),
- [S || S <- Mod:get_sessions(), node(element(2, S#session.sid)) == node()].
+ lists:flatmap(
+ fun(Mod) ->
+ [S || S <- Mod:get_sessions(),
+ node(element(2, S#session.sid)) == node()]
+ end, get_sm_backends()).
-spec get_vh_session_list(binary()) -> [ljid()].
get_vh_session_list(Server) ->
LServer = jid:nameprep(Server),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
[S#session.usr || S <- Mod:get_sessions(LServer)].
-spec get_all_pids() -> [pid()].
get_all_pids() ->
- Mod = get_sm_backend(),
- [element(2, S#session.sid) || S <- Mod:get_sessions()].
+ lists:flatmap(
+ fun(Mod) ->
+ [element(2, S#session.sid) || S <- Mod:get_sessions()]
+ end, get_sm_backends()).
-spec get_vh_session_number(binary()) -> non_neg_integer().
get_vh_session_number(Server) ->
LServer = jid:nameprep(Server),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
length(Mod:get_sessions(LServer)).
register_iq_handler(Host, XMLNS, Module, Fun) ->
@@ -312,8 +321,7 @@ unregister_iq_handler(Host, XMLNS) ->
%%====================================================================
init([]) ->
- Mod = get_sm_backend(),
- Mod:init(),
+ lists:foreach(fun(Mod) -> Mod:init() end, get_sm_backends()),
ets:new(sm_iqtable, [named_table]),
lists:foreach(
fun(Host) ->
@@ -380,7 +388,7 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
LResource = jid:resourceprep(Resource),
US = {LUser, LServer},
USR = {LUser, LServer, LResource},
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
Mod:set_session(#session{sid = SID, usr = USR, us = US,
priority = Priority, info = Info}).
@@ -397,7 +405,7 @@ do_route(From, To, {broadcast, _} = Packet) ->
get_user_resources(To#jid.user, To#jid.server));
_ ->
{U, S, R} = jid:tolower(To),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(S),
case Mod:get_sessions(U, S, R) of
[] ->
?DEBUG("packet dropped~n", []);
@@ -419,10 +427,10 @@ do_route(From, To, #xmlel{} = Packet) ->
<<"">> ->
case Name of
<<"presence">> ->
- {Pass, _Subsc} = case xml:get_attr_s(<<"type">>, Attrs)
+ {Pass, _Subsc} = case fxml:get_attr_s(<<"type">>, Attrs)
of
<<"subscribe">> ->
- Reason = xml:get_path_s(Packet,
+ Reason = fxml:get_path_s(Packet,
[{elem,
<<"status">>},
cdata]),
@@ -483,7 +491,7 @@ do_route(From, To, #xmlel{} = Packet) ->
true -> ok
end;
<<"message">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"chat">> -> route_message(From, To, Packet, chat);
<<"headline">> -> route_message(From, To, Packet, headline);
<<"error">> -> ok;
@@ -498,12 +506,12 @@ do_route(From, To, #xmlel{} = Packet) ->
_ -> ok
end;
_ ->
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer, LResource) of
[] ->
case Name of
<<"message">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"chat">> -> route_message(From, To, Packet, chat);
<<"normal">> -> route_message(From, To, Packet, normal);
<<"">> -> route_message(From, To, Packet, normal);
@@ -514,7 +522,7 @@ do_route(From, To, #xmlel{} = Packet) ->
ejabberd_router:route(To, From, Err)
end;
<<"iq">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
@@ -565,7 +573,7 @@ route_message(From, To, Packet, Type) ->
lists:foreach(fun ({P, R}) when P == Priority;
(P >= 0) and (Type == headline) ->
LResource = jid:resourceprep(R),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer,
LResource) of
[] ->
@@ -647,11 +655,11 @@ get_resource_sessions(User, Server, Resource) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
[S#session.sid || S <- Mod:get_sessions(LUser, LServer, LResource)].
check_max_sessions(LUser, LServer) ->
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
SIDs = [S#session.sid || S <- Mod:get_sessions(LUser, LServer)],
MaxSessions = get_max_user_sessions(LUser, LServer),
if length(SIDs) =< MaxSessions -> ok;
@@ -703,17 +711,17 @@ process_iq(From, To, Packet) ->
-spec force_update_presence({binary(), binary()}) -> any().
force_update_presence({LUser, LServer}) ->
- Mod = get_sm_backend(),
+ Mod = get_sm_backend(LServer),
Ss = Mod:get_sessions(LUser, LServer),
lists:foreach(fun (#session{sid = {_, Pid}}) ->
Pid ! {force_update_presence, LUser, LServer}
end,
Ss).
--spec get_sm_backend() -> module().
+-spec get_sm_backend(binary()) -> module().
-get_sm_backend() ->
- DBType = ejabberd_config:get_option(sm_db_type,
+get_sm_backend(Host) ->
+ DBType = ejabberd_config:get_option({sm_db_type, Host},
fun(mnesia) -> mnesia;
(internal) -> mnesia;
(odbc) -> odbc;
@@ -721,6 +729,19 @@ get_sm_backend() ->
end, mnesia),
list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)).
+-spec get_sm_backends() -> [module()].
+
+get_sm_backends() ->
+ lists:usort([get_sm_backend(Host) || Host <- ?MYHOSTS]).
+
+-spec get_vh_by_backend(module()) -> [binary()].
+
+get_vh_by_backend(Mod) ->
+ lists:filter(
+ fun(Host) ->
+ get_sm_backend(Host) == Mod
+ end, ?MYHOSTS).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% ejabberd commands
diff --git a/src/ejabberd_sm_odbc.erl b/src/ejabberd_sm_odbc.erl
index debeafe9..698763b5 100644
--- a/src/ejabberd_sm_odbc.erl
+++ b/src/ejabberd_sm_odbc.erl
@@ -43,7 +43,7 @@ init() ->
end;
(_, Err) ->
Err
- end, ok, ?MYHOSTS).
+ end, ok, ejabberd_sm:get_vh_by_backend(?MODULE)).
set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R},
priority = Priority, info = Info}) ->
@@ -90,7 +90,7 @@ get_sessions() ->
lists:flatmap(
fun(LServer) ->
get_sessions(LServer)
- end, ?MYHOSTS).
+ end, ejabberd_sm:get_vh_by_backend(?MODULE)).
get_sessions(LServer) ->
case ejabberd_odbc:sql_query(
diff --git a/src/ejabberd_sm_redis.erl b/src/ejabberd_sm_redis.erl
index 637e1364..bf9e0eff 100644
--- a/src/ejabberd_sm_redis.erl
+++ b/src/ejabberd_sm_redis.erl
@@ -107,7 +107,7 @@ get_sessions() ->
lists:flatmap(
fun(LServer) ->
get_sessions(LServer)
- end, ?MYHOSTS).
+ end, ejabberd_sm:get_vh_by_backend(?MODULE)).
-spec get_sessions(binary()) -> [#session{}].
get_sessions(LServer) ->
@@ -204,7 +204,7 @@ clean_table() ->
?ERROR_MSG("failed to clean redis table for "
"server ~s: ~p", [LServer, Err])
end
- end, ?MYHOSTS).
+ end, ejabberd_sm:get_vh_by_backend(?MODULE)).
opt_type(redis_connect_timeout) ->
fun (I) when is_integer(I), I > 0 -> I end;
diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
index 16ffd192..887b4a0f 100644
--- a/src/ejabberd_socket.erl
+++ b/src/ejabberd_socket.erl
@@ -52,10 +52,10 @@
-type sockmod() :: ejabberd_http_bind |
ejabberd_http_ws |
- gen_tcp | p1_tls | ezlib.
+ gen_tcp | fast_tls | ezlib.
-type receiver() :: pid () | atom().
-type socket() :: pid() | inet:socket() |
- p1_tls:tls_socket() |
+ fast_tls:tls_socket() |
ezlib:zlib_socket() |
ejabberd_http_bind:bind_socket().
@@ -145,15 +145,15 @@ connect(Addr, Port, Opts, Timeout) ->
end.
starttls(SocketData, TLSOpts) ->
- {ok, TLSSocket} = p1_tls:tcp_to_tls(SocketData#socket_state.socket, TLSOpts),
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(SocketData#socket_state.socket, TLSOpts),
ejabberd_receiver:starttls(SocketData#socket_state.receiver, TLSSocket),
- SocketData#socket_state{socket = TLSSocket, sockmod = p1_tls}.
+ SocketData#socket_state{socket = TLSSocket, sockmod = fast_tls}.
starttls(SocketData, TLSOpts, Data) ->
- {ok, TLSSocket} = p1_tls:tcp_to_tls(SocketData#socket_state.socket, TLSOpts),
+ {ok, TLSSocket} = fast_tls:tcp_to_tls(SocketData#socket_state.socket, TLSOpts),
ejabberd_receiver:starttls(SocketData#socket_state.receiver, TLSSocket),
send(SocketData, Data),
- SocketData#socket_state{socket = TLSSocket, sockmod = p1_tls}.
+ SocketData#socket_state{socket = TLSSocket, sockmod = fast_tls}.
compress(SocketData) -> compress(SocketData, undefined).
@@ -216,10 +216,10 @@ get_sockmod(SocketData) ->
SocketData#socket_state.sockmod.
get_peer_certificate(SocketData) ->
- p1_tls:get_peer_certificate(SocketData#socket_state.socket).
+ fast_tls:get_peer_certificate(SocketData#socket_state.socket).
get_verify_result(SocketData) ->
- p1_tls:get_verify_result(SocketData#socket_state.socket).
+ fast_tls:get_verify_result(SocketData#socket_state.socket).
close(SocketData) ->
ejabberd_receiver:close(SocketData#socket_state.receiver).
diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl
new file mode 100644
index 00000000..660eac19
--- /dev/null
+++ b/src/ejabberd_sql_pt.erl
@@ -0,0 +1,544 @@
+%%%-------------------------------------------------------------------
+%%% File : ejabberd_sql_pt.erl
+%%% Author : Alexey Shchepin <alexey@process-one.net>
+%%% Description : Parse transform for SQL queries
+%%%
+%%% Created : 20 Jan 2016 by Alexey Shchepin <alexey@process-one.net>
+%%%-------------------------------------------------------------------
+-module(ejabberd_sql_pt).
+
+%% API
+-export([parse_transform/2]).
+
+-export([parse/2]).
+
+-include("ejabberd_sql_pt.hrl").
+
+-record(state, {loc,
+ 'query' = [],
+ params = [],
+ param_pos = 0,
+ args = [],
+ res = [],
+ res_vars = [],
+ res_pos = 0}).
+
+-define(QUERY_RECORD, "sql_query").
+
+-define(ESCAPE_RECORD, "sql_escape").
+-define(ESCAPE_VAR, "__SQLEscape").
+
+-define(MOD, sql__module_).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+parse_transform(AST, _Options) ->
+ %io:format("PT: ~p~nOpts: ~p~n", [AST, Options]),
+ NewAST = top_transform(AST),
+ %io:format("NewPT: ~p~n", [NewAST]),
+ NewAST.
+
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+
+transform(Form) ->
+ case erl_syntax:type(Form) of
+ application ->
+ case erl_syntax_lib:analyze_application(Form) of
+ {?SQL_MARK, 1} ->
+ case erl_syntax:application_arguments(Form) of
+ [Arg] ->
+ case erl_syntax:type(Arg) of
+ string ->
+ S = erl_syntax:string_value(Arg),
+ Pos = erl_syntax:get_pos(Arg),
+ ParseRes = parse(S, Pos),
+ set_pos(make_sql_query(ParseRes), Pos);
+ _ ->
+ throw({error, erl_syntax:get_pos(Form),
+ "?SQL argument must be "
+ "a constant string"})
+ end;
+ _ ->
+ throw({error, erl_syntax:get_pos(Form),
+ "wrong number of ?SQL args"})
+ end;
+ {?SQL_UPSERT_MARK, 2} ->
+ case erl_syntax:application_arguments(Form) of
+ [TableArg, FieldsArg] ->
+ case {erl_syntax:type(TableArg),
+ erl_syntax:is_proper_list(FieldsArg)}of
+ {string, true} ->
+ Table = erl_syntax:string_value(TableArg),
+ ParseRes =
+ parse_upsert(
+ erl_syntax:list_elements(FieldsArg)),
+ Pos = erl_syntax:get_pos(Form),
+ set_pos(
+ make_sql_upsert(Table, ParseRes, Pos),
+ Pos);
+ _ ->
+ throw({error, erl_syntax:get_pos(Form),
+ "?SQL_UPSERT arguments must be "
+ "a constant string and a list"})
+ end;
+ _ ->
+ throw({error, erl_syntax:get_pos(Form),
+ "wrong number of ?SQL_UPSERT args"})
+ end;
+ _ ->
+ Form
+ end;
+ attribute ->
+ case erl_syntax:atom_value(erl_syntax:attribute_name(Form)) of
+ module ->
+ case erl_syntax:attribute_arguments(Form) of
+ [M | _] ->
+ Module = erl_syntax:atom_value(M),
+ %io:format("module ~p~n", [Module]),
+ put(?MOD, Module),
+ Form;
+ _ ->
+ Form
+ end;
+ _ ->
+ Form
+ end;
+ _ ->
+ Form
+ end.
+
+top_transform(Forms) when is_list(Forms) ->
+ lists:map(
+ fun(Form) ->
+ try
+ Form2 = erl_syntax_lib:map(
+ fun(Node) ->
+ %io:format("asd ~p~n", [Node]),
+ transform(Node)
+ end, Form),
+ Form3 = erl_syntax:revert(Form2),
+ Form3
+ catch
+ throw:{error, Line, Error} ->
+ {error, {Line, erl_parse, Error}}
+ end
+ end, Forms).
+
+parse(S, Loc) ->
+ parse1(S, [], #state{loc = Loc}).
+
+parse(S, ParamPos, Loc) ->
+ parse1(S, [], #state{loc = Loc, param_pos = ParamPos}).
+
+parse1([], Acc, State) ->
+ State1 = append_string(lists:reverse(Acc), State),
+ State1#state{'query' = lists:reverse(State1#state.'query'),
+ params = lists:reverse(State1#state.params),
+ args = lists:reverse(State1#state.args),
+ res = lists:reverse(State1#state.res),
+ res_vars = lists:reverse(State1#state.res_vars)
+ };
+parse1([$@, $( | S], Acc, State) ->
+ State1 = append_string(lists:reverse(Acc), State),
+ {Name, Type, S1, State2} = parse_name(S, State1),
+ Var = "__V" ++ integer_to_list(State2#state.res_pos),
+ EVar = erl_syntax:variable(Var),
+ Convert =
+ case Type of
+ integer ->
+ erl_syntax:application(
+ erl_syntax:atom(binary_to_integer),
+ [EVar]);
+ string ->
+ EVar;
+ boolean ->
+ erl_syntax:application(
+ erl_syntax:atom(ejabberd_odbc),
+ erl_syntax:atom(to_bool),
+ [EVar])
+ end,
+ State3 = append_string(Name, State2),
+ State4 = State3#state{res_pos = State3#state.res_pos + 1,
+ res = [Convert | State3#state.res],
+ res_vars = [EVar | State3#state.res_vars]},
+ parse1(S1, [], State4);
+parse1([$%, $( | S], Acc, State) ->
+ State1 = append_string(lists:reverse(Acc), State),
+ {Name, Type, S1, State2} = parse_name(S, State1),
+ Var = State2#state.param_pos,
+ Convert =
+ erl_syntax:application(
+ erl_syntax:record_access(
+ erl_syntax:variable(?ESCAPE_VAR),
+ erl_syntax:atom(?ESCAPE_RECORD),
+ erl_syntax:atom(Type)),
+ [erl_syntax:variable(Name)]),
+ State3 = State2,
+ State4 =
+ State3#state{'query' = [{var, Var} | State3#state.'query'],
+ args = [Convert | State3#state.args],
+ params = [Var | State3#state.params],
+ param_pos = State3#state.param_pos + 1},
+ parse1(S1, [], State4);
+parse1([C | S], Acc, State) ->
+ parse1(S, [C | Acc], State).
+
+append_string([], State) ->
+ State;
+append_string(S, State) ->
+ State#state{query = [{str, S} | State#state.query]}.
+
+parse_name(S, State) ->
+ parse_name(S, [], 0, State).
+
+parse_name([], _Acc, _Depth, State) ->
+ throw({error, State#state.loc,
+ "expected ')', found end of string"});
+parse_name([$), T | S], Acc, 0, State) ->
+ Type =
+ case T of
+ $d -> integer;
+ $s -> string;
+ $b -> boolean;
+ _ ->
+ throw({error, State#state.loc,
+ ["unknown type specifier '", T, "'"]})
+ end,
+ {lists:reverse(Acc), Type, S, State};
+parse_name([$)], _Acc, 0, State) ->
+ throw({error, State#state.loc,
+ "expected type specifier, found end of string"});
+parse_name([$( = C | S], Acc, Depth, State) ->
+ parse_name(S, [C | Acc], Depth + 1, State);
+parse_name([$) = C | S], Acc, Depth, State) ->
+ parse_name(S, [C | Acc], Depth - 1, State);
+parse_name([C | S], Acc, Depth, State) ->
+ parse_name(S, [C | Acc], Depth, State).
+
+
+make_var(V) ->
+ Var = "__V" ++ integer_to_list(V),
+ erl_syntax:variable(Var).
+
+
+make_sql_query(State) ->
+ Hash = erlang:phash2(State#state{loc = undefined}),
+ SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
+ Query = pack_query(State#state.'query'),
+ EQuery =
+ lists:map(
+ fun({str, S}) ->
+ erl_syntax:binary(
+ [erl_syntax:binary_field(
+ erl_syntax:string(S))]);
+ ({var, V}) -> make_var(V)
+ end, Query),
+ erl_syntax:record_expr(
+ erl_syntax:atom(?QUERY_RECORD),
+ [erl_syntax:record_field(
+ erl_syntax:atom(hash),
+ %erl_syntax:abstract(SHash)
+ erl_syntax:binary(
+ [erl_syntax:binary_field(
+ erl_syntax:string(binary_to_list(SHash)))])),
+ erl_syntax:record_field(
+ erl_syntax:atom(args),
+ erl_syntax:fun_expr(
+ [erl_syntax:clause(
+ [erl_syntax:variable(?ESCAPE_VAR)],
+ none,
+ [erl_syntax:list(State#state.args)]
+ )])),
+ erl_syntax:record_field(
+ erl_syntax:atom(format_query),
+ erl_syntax:fun_expr(
+ [erl_syntax:clause(
+ [erl_syntax:list(lists:map(fun make_var/1, State#state.params))],
+ none,
+ [erl_syntax:list(EQuery)]
+ )])),
+ erl_syntax:record_field(
+ erl_syntax:atom(format_res),
+ erl_syntax:fun_expr(
+ [erl_syntax:clause(
+ [erl_syntax:list(State#state.res_vars)],
+ none,
+ [erl_syntax:tuple(State#state.res)]
+ )])),
+ erl_syntax:record_field(
+ erl_syntax:atom(loc),
+ erl_syntax:abstract({get(?MOD), State#state.loc}))
+ ]).
+
+pack_query([]) ->
+ [];
+pack_query([{str, S1}, {str, S2} | Rest]) ->
+ pack_query([{str, S1 ++ S2} | Rest]);
+pack_query([X | Rest]) ->
+ [X | pack_query(Rest)].
+
+
+parse_upsert(Fields) ->
+ {Fs, _} =
+ lists:foldr(
+ fun(F, {Acc, Param}) ->
+ case erl_syntax:type(F) of
+ string ->
+ V = erl_syntax:string_value(F),
+ {_, _, State} = Res =
+ parse_upsert_field(
+ V, Param, erl_syntax:get_pos(F)),
+ {[Res | Acc], State#state.param_pos};
+ _ ->
+ throw({error, erl_syntax:get_pos(F),
+ "?SQL_UPSERT field must be "
+ "a constant string"})
+ end
+ end, {[], 0}, Fields),
+ %io:format("asd ~p~n", [{Fields, Fs}]),
+ Fs.
+
+parse_upsert_field([$! | S], ParamPos, Loc) ->
+ {Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc),
+ {Name, true, ParseState};
+parse_upsert_field(S, ParamPos, Loc) ->
+ {Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc),
+ {Name, false, ParseState}.
+
+parse_upsert_field1([], _Acc, _ParamPos, Loc) ->
+ throw({error, Loc,
+ "?SQL_UPSERT fields must have the "
+ "following form: \"[!]name=value\""});
+parse_upsert_field1([$= | S], Acc, ParamPos, Loc) ->
+ {lists:reverse(Acc), parse(S, ParamPos, Loc)};
+parse_upsert_field1([C | S], Acc, ParamPos, Loc) ->
+ parse_upsert_field1(S, [C | Acc], ParamPos, Loc).
+
+
+make_sql_upsert(Table, ParseRes, Pos) ->
+ check_upsert(ParseRes, Pos),
+ erl_syntax:fun_expr(
+ [erl_syntax:clause(
+ [erl_syntax:atom(pgsql), erl_syntax:variable("__Version")],
+ [erl_syntax:infix_expr(
+ erl_syntax:variable("__Version"),
+ erl_syntax:operator('>='),
+ erl_syntax:integer(90100))],
+ [make_sql_upsert_pgsql901(Table, ParseRes),
+ erl_syntax:atom(ok)]),
+ erl_syntax:clause(
+ [erl_syntax:underscore(), erl_syntax:underscore()],
+ none,
+ [make_sql_upsert_generic(Table, ParseRes)])
+ ]).
+
+make_sql_upsert_generic(Table, ParseRes) ->
+ Update = make_sql_query(make_sql_upsert_update(Table, ParseRes)),
+ Insert = make_sql_query(make_sql_upsert_insert(Table, ParseRes)),
+ InsertBranch =
+ erl_syntax:case_expr(
+ erl_syntax:application(
+ erl_syntax:atom(ejabberd_odbc),
+ erl_syntax:atom(sql_query_t),
+ [Insert]),
+ [erl_syntax:clause(
+ [erl_syntax:abstract({updated, 1})],
+ none,
+ [erl_syntax:atom(ok)]),
+ erl_syntax:clause(
+ [erl_syntax:variable("__UpdateRes")],
+ none,
+ [erl_syntax:variable("__UpdateRes")])]),
+ erl_syntax:case_expr(
+ erl_syntax:application(
+ erl_syntax:atom(ejabberd_odbc),
+ erl_syntax:atom(sql_query_t),
+ [Update]),
+ [erl_syntax:clause(
+ [erl_syntax:abstract({updated, 1})],
+ none,
+ [erl_syntax:atom(ok)]),
+ erl_syntax:clause(
+ [erl_syntax:underscore()],
+ none,
+ [InsertBranch])]).
+
+make_sql_upsert_update(Table, ParseRes) ->
+ WPairs =
+ lists:flatmap(
+ fun({_Field, false, _ST}) ->
+ [];
+ ({Field, true, ST}) ->
+ [ST#state{
+ 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'
+ }]
+ end, ParseRes),
+ Where = join_states(WPairs, " AND "),
+ SPairs =
+ lists:flatmap(
+ fun({_Field, true, _ST}) ->
+ [];
+ ({Field, false, ST}) ->
+ [ST#state{
+ 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'
+ }]
+ end, ParseRes),
+ Set = join_states(SPairs, ", "),
+ State =
+ concat_states(
+ [#state{'query' = [{str, "UPDATE "}, {str, Table}, {str, " SET "}]},
+ Set,
+ #state{'query' = [{str, " WHERE "}]},
+ Where
+ ]),
+ State.
+
+make_sql_upsert_insert(Table, ParseRes) ->
+ Vals =
+ lists:map(
+ fun({_Field, _, ST}) ->
+ ST
+ end, ParseRes),
+ Fields =
+ lists:map(
+ fun({Field, _, _ST}) ->
+ #state{'query' = [{str, Field}]}
+ end, ParseRes),
+ State =
+ concat_states(
+ [#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]},
+ join_states(Fields, ", "),
+ #state{'query' = [{str, ") VALUES ("}]},
+ join_states(Vals, ", "),
+ #state{'query' = [{str, ")"}]}
+ ]),
+ State.
+
+make_sql_upsert_pgsql901(Table, ParseRes) ->
+ Update = make_sql_upsert_update(Table, ParseRes),
+ Vals =
+ lists:map(
+ fun({_Field, _, ST}) ->
+ ST
+ end, ParseRes),
+ Fields =
+ lists:map(
+ fun({Field, _, _ST}) ->
+ #state{'query' = [{str, Field}]}
+ end, ParseRes),
+ Insert =
+ concat_states(
+ [#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]},
+ join_states(Fields, ", "),
+ #state{'query' = [{str, ") SELECT "}]},
+ join_states(Vals, ", "),
+ #state{'query' = [{str, " WHERE NOT EXISTS (SELECT * FROM upsert)"}]}
+ ]),
+ State =
+ concat_states(
+ [#state{'query' = [{str, "WITH upsert AS ("}]},
+ Update,
+ #state{'query' = [{str, " RETURNING *) "}]},
+ Insert
+ ]),
+ Upsert = make_sql_query(State),
+ erl_syntax:application(
+ erl_syntax:atom(ejabberd_odbc),
+ erl_syntax:atom(sql_query_t),
+ [Upsert]).
+
+
+check_upsert(ParseRes, Pos) ->
+ Set =
+ lists:filter(
+ fun({_Field, Match, _ST}) ->
+ not Match
+ end, ParseRes),
+ case Set of
+ [] ->
+ throw({error, Pos,
+ "No ?SQL_UPSERT fields to set, use INSERT instead"});
+ _ ->
+ ok
+ end,
+ ok.
+
+
+concat_states(States) ->
+ lists:foldr(
+ fun(ST11, ST2) ->
+ ST1 = resolve_vars(ST11, ST2),
+ ST1#state{
+ 'query' = ST1#state.'query' ++ ST2#state.'query',
+ params = ST1#state.params ++ ST2#state.params,
+ args = ST1#state.args ++ ST2#state.args,
+ res = ST1#state.res ++ ST2#state.res,
+ res_vars = ST1#state.res_vars ++ ST2#state.res_vars,
+ loc = case ST1#state.loc of
+ undefined -> ST2#state.loc;
+ _ -> ST1#state.loc
+ end
+ }
+ end, #state{}, States).
+
+resolve_vars(ST1, ST2) ->
+ Max = lists:max([0 | ST1#state.params ++ ST2#state.params]),
+ {Map, _} =
+ lists:foldl(
+ fun(Var, {Acc, New}) ->
+ case lists:member(Var, ST2#state.params) of
+ true ->
+ {dict:store(Var, New, Acc), New + 1};
+ false ->
+ {Acc, New}
+ end
+ end, {dict:new(), Max + 1}, ST1#state.params),
+ NewParams =
+ lists:map(
+ fun(Var) ->
+ case dict:find(Var, Map) of
+ {ok, New} ->
+ New;
+ error ->
+ Var
+ end
+ end, ST1#state.params),
+ NewQuery =
+ lists:map(
+ fun({var, Var}) ->
+ case dict:find(Var, Map) of
+ {ok, New} ->
+ {var, New};
+ error ->
+ {var, Var}
+ end;
+ (S) -> S
+ end, ST1#state.'query'),
+ ST1#state{params = NewParams, 'query' = NewQuery}.
+
+
+join_states([], _Sep) ->
+ #state{};
+join_states([H | T], Sep) ->
+ J = [[H] | [[#state{'query' = [{str, Sep}]}, X] || X <- T]],
+ concat_states(lists:append(J)).
+
+
+set_pos(Tree, Pos) ->
+ erl_syntax_lib:map(
+ fun(Node) ->
+ case erl_syntax:get_pos(Node) of
+ 0 -> erl_syntax:set_pos(Node, Pos);
+ _ -> Node
+ end
+ end, Tree).
diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl
index f7ae19cd..3c91117d 100644
--- a/src/ejabberd_stun.erl
+++ b/src/ejabberd_stun.erl
@@ -38,11 +38,11 @@
%%% API
%%%===================================================================
tcp_init(Socket, Opts) ->
- ejabberd:start_app(p1_stun),
+ ejabberd:start_app(stun),
stun:tcp_init(Socket, prepare_turn_opts(Opts)).
udp_init(Socket, Opts) ->
- ejabberd:start_app(p1_stun),
+ ejabberd:start_app(stun),
stun:udp_init(Socket, prepare_turn_opts(Opts)).
udp_recv(Socket, Addr, Port, Packet, Opts) ->
diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl
index 9a0213c1..3f6c0566 100644
--- a/src/ejabberd_system_monitor.erl
+++ b/src/ejabberd_system_monitor.erl
@@ -71,7 +71,7 @@ process_command(From, To, Packet) ->
jid:tolower(jid:remove_resource(From)),
case lists:member(LFrom, get_admin_jids()) of
true ->
- Body = xml:get_path_s(Packet,
+ Body = fxml:get_path_s(Packet,
[{elem, <<"body">>}, cdata]),
spawn(fun () ->
process_flag(priority, high),
@@ -186,18 +186,24 @@ process_large_heap(Pid, Info) ->
"much memory:~n~p~n~s",
[node(), Pid, Info, DetailedInfo])),
From = jid:make(<<"">>, Host, <<"watchdog">>),
+ Hint = [#xmlel{name = <<"no-permanent-store">>,
+ attrs = [{<<"xmlns">>, ?NS_HINTS}]}],
lists:foreach(fun (JID) ->
- send_message(From, jid:make(JID), Body)
+ send_message(From, jid:make(JID), Body, Hint)
end, JIDs).
send_message(From, To, Body) ->
+ send_message(From, To, Body, []).
+
+send_message(From, To, Body, ExtraEls) ->
ejabberd_router:route(From, To,
#xmlel{name = <<"message">>,
attrs = [{<<"type">>, <<"chat">>}],
children =
[#xmlel{name = <<"body">>, attrs = [],
children =
- [{xmlcdata, Body}]}]}).
+ [{xmlcdata, Body}]}
+ | ExtraEls]}).
get_admin_jids() ->
ejabberd_config:get_option(
diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl
index d711f2fc..11d90ab8 100644
--- a/src/ejabberd_web_admin.erl
+++ b/src/ejabberd_web_admin.erl
@@ -383,6 +383,9 @@ css(Host) ->
" background: #f9f9f9;\n"
" font-family: sans-serif;\n"
"}\n"
+ "body {\n"
+ " min-width: 990px;\n"
+ "}\n"
"a {\n"
" text-decoration: none;\n"
" color: #3eaffa;\n"
@@ -461,13 +464,15 @@ css(Host) ->
" font-size: 0.75em;\n"
" text-align: center;\n"
"}\n"
+ "#navigation {\n"
+ " display: inline-block;\n"
+ " vertical-align: top;\n"
+ " width: 30%;\n"
+ "}\n"
"#navigation ul {\n"
- " position: absolute;\n"
- " top: 75px;\n"
- " left: 0;\n"
" padding: 0;\n"
" margin: 0;\n"
- " width: 17em;\n"
+ " width: 90%;\n"
" background: #fff;\n"
"}\n"
"#navigation ul li {\n"
@@ -512,6 +517,9 @@ css(Host) ->
" background: #3eaffa;\n"
" color: #fff;\n"
"}\n"
+ "thead tr td a {\n"
+ " color: #fff;\n"
+ "}\n"
"td.copy {\n"
" text-align: center;\n"
"}\n"
@@ -592,8 +600,10 @@ css(Host) ->
" list-style-type: none;\n"
"}\n"
"#content {\n"
- " padding-left: 19em;\n"
+ " display: inline-block;\n"
+ " vertical-align: top;\n"
" padding-top: 25px;\n"
+ " width: 70%;\n"
"}\n"
"div.guidelink,\n"
"p[dir=ltr] {\n"
@@ -735,7 +745,7 @@ process_admin(Host,
[{{acl, '$1', '$2'}}]}])),
{NumLines, ACLsP} = term_to_paragraph(ACLs, 80),
make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"acl-definition">>, <<"ACL Definition">>))
+ <<"acldefinition">>, <<"ACL Definition">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -771,7 +781,7 @@ process_admin(Host,
[{{acl, {'$1', Host}, '$2'}, [],
[{{acl, '$1', '$2'}}]}])),
make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"acl-definition">>, <<"ACL Definition">>))
+ <<"acldefinition">>, <<"ACL Definition">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -837,7 +847,7 @@ process_admin(Host,
[{{access, '$1', '$2'}}]}]),
{NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80),
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"access-rights">>, <<"Access Rights">>))
+ <<"accessrights">>, <<"Access Rights">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -870,7 +880,7 @@ process_admin(Host,
[{{access, {'$1', Host}, '$2'}, [],
[{{access, '$1', '$2'}}]}]),
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"access-rights">>, <<"Access Rights">>))
+ <<"accessrights">>, <<"Access Rights">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -929,7 +939,7 @@ process_admin(global,
lang = Lang}) ->
Res = list_vhosts(Lang, AJID),
make_xhtml((?H1GL((?T(<<"Virtual Hosts">>)),
- <<"virtual-hosting">>, <<"Virtual Hosting">>))
+ <<"virtualhosting">>, <<"Virtual Hosting">>))
++ Res,
global, Lang, AJID);
process_admin(Host,
@@ -1510,8 +1520,7 @@ get_offlinemsg_length(ModOffline, User, Server) ->
case ModOffline of
none -> <<"disabled">>;
_ ->
- pretty_string_int(ModOffline:get_queue_length(User,
- Server))
+ pretty_string_int(ModOffline:count_offline_messages(User,Server))
end.
get_offlinemsg_module(Server) ->
@@ -2114,7 +2123,7 @@ get_node(global, Node, [<<"ports">>], Query, Lang) ->
[]])),
H1String = <<(?T(<<"Listened Ports at ">>))/binary,
(iolist_to_binary(atom_to_list(Node)))/binary>>,
- (?H1GL(H1String, <<"listening-ports">>, <<"Listening Ports">>))
+ (?H1GL(H1String, <<"listeningports">>, <<"Listening Ports">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -2142,7 +2151,7 @@ get_node(Host, Node, [<<"modules">>], Query, Lang)
NewModules = lists:sort(ejabberd_cluster:call(Node, gen_mod,
loaded_modules_with_opts, [Host])),
H1String = list_to_binary(io_lib:format(?T(<<"Modules at ~p">>), [Node])),
- (?H1GL(H1String, <<"modules-overview">>,
+ (?H1GL(H1String, <<"modulesoverview">>,
<<"Modules Overview">>))
++
case Res of
@@ -2722,14 +2731,14 @@ pretty_print_xml(#xmlel{name = Name, attrs = Attrs,
[{Attr, Val} | RestAttrs] ->
AttrPrefix = [Prefix,
str:copies(<<" ">>, byte_size(Name) + 2)],
- [$\s, Attr, $=, $', xml:crypt(Val) | [$',
+ [$\s, Attr, $=, $', fxml:crypt(Val) | [$',
lists:map(fun ({Attr1,
Val1}) ->
[$\n,
AttrPrefix,
Attr1, $=,
$',
- xml:crypt(Val1),
+ fxml:crypt(Val1),
$']
end,
RestAttrs)]]
@@ -2741,7 +2750,7 @@ pretty_print_xml(#xmlel{name = Name, attrs = Attrs,
end,
Els),
if OnlyCData ->
- [$>, xml:get_cdata(Els), $<, $/, Name, $>, $\n];
+ [$>, fxml:get_cdata(Els), $<, $/, Name, $>, $\n];
true ->
[$>, $\n,
lists:map(fun (E) ->
diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl
index d9a1bafc..0cdd9bac 100644
--- a/src/ejabberd_websocket.erl
+++ b/src/ejabberd_websocket.erl
@@ -373,10 +373,10 @@ process_frame(#frame_info{unprocessed =
process_frame(FrameInfo#frame_info{unprocessed = <<>>},
<<UnprocessedPre/binary, Data/binary>>).
-handle_data(tcp, FrameInfo, Data, Socket, WsHandleLoopPid, p1_tls) ->
- case p1_tls:recv_data(Socket, Data) of
+handle_data(tcp, FrameInfo, Data, Socket, WsHandleLoopPid, fast_tls) ->
+ case fast_tls:recv_data(Socket, Data) of
{ok, NewData} ->
- handle_data_int(FrameInfo, NewData, Socket, WsHandleLoopPid, p1_tls);
+ handle_data_int(FrameInfo, NewData, Socket, WsHandleLoopPid, fast_tls);
{error, Error} ->
{error, Error}
end;
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index 6b25adc4..c7e72d66 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -228,13 +228,13 @@ process(_, #request{method = 'POST', data = Data, opts = Opts}) ->
end,
GetAuth = true,
State = #state{access_commands = AccessCommands, get_auth = GetAuth},
- case xml_stream:parse_element(Data) of
+ case fxml_stream:parse_element(Data) of
{error, _} ->
{400, [],
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"Malformed XML">>}]}};
El ->
- case p1_xmlrpc:decode(El) of
+ case fxmlrpc:decode(El) of
{error, _} = Err ->
?ERROR_MSG("XML-RPC request ~s failed with reason: ~p",
[Data, Err]),
@@ -244,7 +244,7 @@ process(_, #request{method = 'POST', data = Data, opts = Opts}) ->
{ok, RPC} ->
?DEBUG("got XML-RPC request: ~p", [RPC]),
{false, Result} = handler(State, RPC),
- XML = xml:element_to_binary(p1_xmlrpc:encode(Result)),
+ XML = fxml:element_to_binary(fxmlrpc:encode(Result)),
{200, [{<<"Content-Type">>, <<"text/xml">>}],
<<"<?xml version=\"1.0\"?>", XML/binary>>}
end
@@ -491,7 +491,7 @@ format_result(Atom, {Name, atom}) ->
[{Name, iolist_to_binary(atom_to_list(Atom))}]};
format_result(Int, {Name, integer}) ->
{struct, [{Name, Int}]};
-format_result(String, {Name, string}) when is_list(String) ->
+format_result([A|_]=String, {Name, string}) when is_list(String) and is_integer(A) ->
{struct, [{Name, lists:flatten(String)}]};
format_result(Binary, {Name, string}) when is_binary(Binary) ->
{struct, [{Name, binary_to_list(Binary)}]};
diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl
new file mode 100644
index 00000000..c055853f
--- /dev/null
+++ b/src/elixir_logger_backend.erl
@@ -0,0 +1,122 @@
+%%%-------------------------------------------------------------------
+%%% @author Mickael Remond <mremond@process-one.net>
+%%% @doc
+%%% This module bridges lager logs to Elixir Logger.
+%%% @end
+%%% Created : 9 March 2016 by Mickael Remond <mremond@process-one.net>
+%%%
+%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License along
+%%% with this program; if not, write to the Free Software Foundation, Inc.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%-------------------------------------------------------------------
+
+-module(elixir_logger_backend).
+
+-behaviour(gen_event).
+
+-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-record(state, {level = debug}).
+
+init(Opts) ->
+ Level = proplists:get_value(level, Opts, debug),
+ State = #state{level = Level},
+ {ok, State}.
+
+%% @private
+handle_event({log, LagerMsg}, State) ->
+ #{mode := Mode, truncate := Truncate, level := MinLevel, utc_log := UTCLog} = 'Elixir.Logger.Config':'__data__'(),
+ MsgLevel = severity_to_level(lager_msg:severity(LagerMsg)),
+ case {lager_util:is_loggable(LagerMsg, lager_util:level_to_num(State#state.level), ?MODULE),
+ 'Elixir.Logger':compare_levels(MsgLevel, MinLevel)} of
+ {_, lt}->
+ {ok, State};
+ {true, _} ->
+ Metadata = normalize_pid(lager_msg:metadata(LagerMsg)),
+ Message = 'Elixir.Logger.Utils':truncate(lager_msg:message(LagerMsg), Truncate),
+ Timestamp = timestamp(lager_msg:timestamp(LagerMsg), UTCLog),
+ GroupLeader = case proplists:get_value(pid, Metadata, self()) of
+ Pid when is_pid(Pid) ->
+ erlang:process_info(self(), group_leader);
+ _ -> {group_leader, self()}
+ end,
+ notify(Mode, {MsgLevel, GroupLeader, {'Elixir.Logger', Message, Timestamp, Metadata}}),
+ {ok, State};
+ _ ->
+ {ok, State}
+ end;
+handle_event(_Msg, State) ->
+ {ok, State}.
+
+%% @private
+%% TODO Handle loglevels
+handle_call(get_loglevel, State) ->
+ {ok, lager_util:config_to_mask(State#state.level), State};
+handle_call({set_loglevel, Config}, State) ->
+ {ok, ok, State#state{level = Config}}.
+
+%% @private
+handle_info(_Msg, State) ->
+ {ok, State}.
+
+%% @private
+terminate(_Reason, _State) ->
+ ok.
+
+%% @private
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+notify(sync, Msg) ->
+ gen_event:sync_notify('Elixir.Logger', Msg);
+notify(async, Msg) ->
+ gen_event:notify('Elixir.Logger', Msg).
+
+normalize_pid(Metadata) ->
+ case proplists:get_value(pid, Metadata) of
+ Pid when is_pid(Pid) -> Metadata;
+ Pid when is_list(Pid) ->
+ M1 = proplists:delete(pid, Metadata),
+ case catch erlang:list_to_pid(Pid) of
+ {'EXIT', _} ->
+ M1;
+ PidAsPid ->
+ [{pid, PidAsPid}|M1]
+ end;
+ _ ->
+ proplists:delete(pid, Metadata)
+ end.
+
+%% Return timestamp with milliseconds
+timestamp(Time, UTCLog) ->
+ {_, _, Micro} = p1_time_compat:timestamp(),
+ {Date, {Hours, Minutes, Seconds}} =
+ case UTCLog of
+ true -> calendar:now_to_universal_time(Time);
+ false -> calendar:now_to_local_time(Time)
+ end,
+ {Date, {Hours, Minutes, Seconds, Micro div 1000}}.
+
+
+severity_to_level(debug) -> debug;
+severity_to_level(info) -> info;
+severity_to_level(notice) -> info;
+severity_to_level(warning) -> warn;
+severity_to_level(error) -> error;
+severity_to_level(critical) -> error;
+severity_to_level(alert) -> error;
+severity_to_level(emergency) -> error.
diff --git a/src/ext_mod.erl b/src/ext_mod.erl
index 46ece873..91526e71 100644
--- a/src/ext_mod.erl
+++ b/src/ext_mod.erl
@@ -509,12 +509,11 @@ compile(_Module, _Spec, DestDir) ->
filelib:ensure_dir(filename:join(Ebin, ".")),
EjabBin = filename:dirname(code:which(ejabberd)),
EjabInc = filename:join(filename:dirname(EjabBin), "include"),
- XmlHrl = filename:join(EjabInc, "xml.hrl"),
- Logger = [{d, 'P1LOGGER'} || code:is_loaded(lager)==false],
+ XmlHrl = filename:join(EjabInc, "fxml.hrl"),
ExtLib = [{d, 'NO_EXT_LIB'} || filelib:is_file(XmlHrl)],
Options = [{outdir, Ebin}, {i, "include"}, {i, EjabInc},
verbose, report_errors, report_warnings]
- ++ Logger ++ ExtLib,
+ ++ ExtLib,
[file:copy(App, Ebin) || App <- filelib:wildcard("src/*.app")],
Result = [case compile:file(File, Options) of
{ok, _} -> ok;
@@ -589,10 +588,10 @@ rebar_dep({App, _, {git, Url, Ref}}) ->
%% -- YAML spec parser
consult(File) ->
- case p1_yaml:decode_from_file(File, [plain_as_atom]) of
+ case fast_yaml:decode_from_file(File, [plain_as_atom]) of
{ok, []} -> {ok, []};
{ok, [Doc|_]} -> {ok, [format(Spec) || Spec <- Doc]};
- {error, Err} -> {error, p1_yaml:format_error(Err)}
+ {error, Err} -> {error, fast_yaml:format_error(Err)}
end.
format({Key, Val}) when is_binary(Val) ->
diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
index cda8d11c..099387c9 100644
--- a/src/jd2ejd.erl
+++ b/src/jd2ejd.erl
@@ -48,7 +48,7 @@ import_file(File) ->
true ->
case file:read_file(File) of
{ok, Text} ->
- case xml_stream:parse_element(Text) of
+ case fxml_stream:parse_element(Text) of
El when is_record(El, xmlel) ->
case catch process_xdb(User, Server, El) of
{'EXIT', Reason} ->
@@ -113,16 +113,16 @@ process_xdb(User, Server,
xdb_data(_User, _Server, {xmlcdata, _CData}) -> ok;
xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
From = jid:make(User, Server, <<"">>),
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_AUTH ->
- Password = xml:get_tag_cdata(El),
+ Password = fxml:get_tag_cdata(El),
ejabberd_auth:set_password(User, Server, Password),
ok;
?NS_ROSTER ->
catch mod_roster:set_items(User, Server, El), ok;
?NS_LAST ->
- TimeStamp = xml:get_attr_s(<<"last">>, Attrs),
- Status = xml:get_tag_cdata(El),
+ TimeStamp = fxml:get_attr_s(<<"last">>, Attrs),
+ Status = fxml:get_tag_cdata(El),
catch mod_last:store_last_info(User, Server,
jlib:binary_to_integer(TimeStamp),
Status),
@@ -136,7 +136,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
<<"jabber:x:offline">> ->
process_offline(Server, From, El), ok;
XMLNS ->
- case xml:get_attr_s(<<"j_private_flag">>, Attrs) of
+ case fxml:get_attr_s(<<"j_private_flag">>, Attrs) of
<<"1">> ->
catch mod_private:process_sm_iq(From,
jid:make(<<"">>, Server,
@@ -160,7 +160,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
process_offline(Server, To, #xmlel{children = Els}) ->
LServer = jid:nameprep(Server),
lists:foreach(fun (#xmlel{attrs = Attrs} = El) ->
- FromS = xml:get_attr_s(<<"from">>, Attrs),
+ FromS = fxml:get_attr_s(<<"from">>, Attrs),
From = case FromS of
<<"">> ->
jid:make(<<"">>, Server, <<"">>);
diff --git a/src/jlib.erl b/src/jlib.erl
index 61aaf690..8eaebbc8 100644
--- a/src/jlib.erl
+++ b/src/jlib.erl
@@ -92,8 +92,8 @@ make_result_iq_reply(#xmlel{name = Name, attrs = Attrs,
-spec make_result_iq_reply_attrs([attr()]) -> [attr()].
make_result_iq_reply_attrs(Attrs) ->
- To = xml:get_attr(<<"to">>, Attrs),
- From = xml:get_attr(<<"from">>, Attrs),
+ To = fxml:get_attr(<<"to">>, Attrs),
+ From = fxml:get_attr(<<"from">>, Attrs),
Attrs1 = lists:keydelete(<<"to">>, 1, Attrs),
Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1),
Attrs3 = case To of
@@ -133,8 +133,8 @@ make_error_reply(#xmlel{name = Name, attrs = Attrs,
-spec make_error_reply_attrs([attr()]) -> [attr()].
make_error_reply_attrs(Attrs) ->
- To = xml:get_attr(<<"to">>, Attrs),
- From = xml:get_attr(<<"from">>, Attrs),
+ To = fxml:get_attr(<<"to">>, Attrs),
+ From = fxml:get_attr(<<"from">>, Attrs),
Attrs1 = lists:keydelete(<<"to">>, 1, Attrs),
Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1),
Attrs3 = case To of
@@ -159,7 +159,7 @@ make_error_element(Code, Desc) ->
make_correct_from_to_attrs(From, To, Attrs) ->
Attrs1 = lists:keydelete(<<"from">>, 1, Attrs),
- Attrs2 = case xml:get_attr(<<"to">>, Attrs) of
+ Attrs2 = case fxml:get_attr(<<"to">>, Attrs) of
{value, _} -> Attrs1;
_ -> [{<<"to">>, To} | Attrs1]
end,
@@ -299,8 +299,8 @@ jid_replace_resource(JID, Resource) ->
-spec get_iq_namespace(xmlel()) -> binary().
get_iq_namespace(#xmlel{name = <<"iq">>, children = Els}) ->
- case xml:remove_cdata(Els) of
- [#xmlel{attrs = Attrs}] -> xml:get_attr_s(<<"xmlns">>, Attrs);
+ case fxml:remove_cdata(Els) of
+ [#xmlel{attrs = Attrs}] -> fxml:get_attr_s(<<"xmlns">>, Attrs);
_ -> <<"">>
end;
get_iq_namespace(_) -> <<"">>.
@@ -326,9 +326,9 @@ iq_query_or_response_info(El) ->
iq_info_internal(El, any).
iq_info_internal(#xmlel{name = <<"iq">>, attrs = Attrs, children = Els}, Filter) ->
- ID = xml:get_attr_s(<<"id">>, Attrs),
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
- {Type, Class} = case xml:get_attr_s(<<"type">>, Attrs) of
+ ID = fxml:get_attr_s(<<"id">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+ {Type, Class} = case fxml:get_attr_s(<<"type">>, Attrs) of
<<"set">> -> {set, request};
<<"get">> -> {get, request};
<<"result">> -> {result, reply};
@@ -336,15 +336,15 @@ iq_info_internal(#xmlel{name = <<"iq">>, attrs = Attrs, children = Els}, Filter)
_ -> {invalid, invalid}
end,
if Type == invalid -> invalid; Class == request; Filter == any ->
- FilteredEls = xml:remove_cdata(Els),
+ FilteredEls = fxml:remove_cdata(Els),
{XMLNS, SubEl} = case {Class, FilteredEls} of
{request, [#xmlel{attrs = Attrs2}]} ->
- {xml:get_attr_s(<<"xmlns">>, Attrs2), hd(FilteredEls)};
+ {fxml:get_attr_s(<<"xmlns">>, Attrs2), hd(FilteredEls)};
{reply, _} ->
NonErrorEls = [El || #xmlel{name = SubName} = El <- FilteredEls,
SubName /= <<"error">>],
{case NonErrorEls of
- [NonErrorEl] -> xml:get_tag_attr_s(<<"xmlns">>, NonErrorEl);
+ [NonErrorEl] -> fxml:get_tag_attr_s(<<"xmlns">>, NonErrorEl);
_ -> <<"">>
end,
FilteredEls};
@@ -399,7 +399,7 @@ iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) ->
).
parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"submit">> ->
lists:reverse(parse_xdata_fields(Els, []));
<<"form">> -> %% This is a workaround to accept Psi's wrong forms
@@ -418,7 +418,7 @@ parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
parse_xdata_fields([], Res) -> Res;
parse_xdata_fields([#xmlel{name = <<"field">>, attrs = Attrs, children = SubEls}
| Els], Res) ->
- case xml:get_attr_s(<<"var">>, Attrs) of
+ case fxml:get_attr_s(<<"var">>, Attrs) of
<<>> ->
parse_xdata_fields(Els, Res);
Var ->
@@ -437,7 +437,7 @@ parse_xdata_fields([_ | Els], Res) ->
parse_xdata_values([], Res) -> Res;
parse_xdata_values([#xmlel{name = <<"value">>, children = SubEls} | Els], Res) ->
- Val = xml:get_cdata(SubEls),
+ Val = fxml:get_cdata(SubEls),
parse_xdata_values(Els, [Val | Res]);
parse_xdata_values([_ | Els], Res) ->
parse_xdata_values(Els, Res).
@@ -446,7 +446,7 @@ parse_xdata_values([_ | Els], Res) ->
rsm_decode(#iq{sub_el = SubEl}) -> rsm_decode(SubEl);
rsm_decode(#xmlel{} = SubEl) ->
- case xml:get_subtag(SubEl, <<"set">>) of
+ case fxml:get_subtag(SubEl, <<"set">>) of
false -> none;
#xmlel{name = <<"set">>, children = SubEls} ->
lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls)
@@ -455,26 +455,26 @@ rsm_decode(#xmlel{} = SubEl) ->
rsm_parse_element(#xmlel{name = <<"max">>, attrs = []} =
Elem,
RsmIn) ->
- CountStr = xml:get_tag_cdata(Elem),
+ CountStr = fxml:get_tag_cdata(Elem),
{Count, _} = str:to_integer(CountStr),
RsmIn#rsm_in{max = Count};
rsm_parse_element(#xmlel{name = <<"before">>,
attrs = []} =
Elem,
RsmIn) ->
- UID = xml:get_tag_cdata(Elem),
+ UID = fxml:get_tag_cdata(Elem),
RsmIn#rsm_in{direction = before, id = UID};
rsm_parse_element(#xmlel{name = <<"after">>,
attrs = []} =
Elem,
RsmIn) ->
- UID = xml:get_tag_cdata(Elem),
+ UID = fxml:get_tag_cdata(Elem),
RsmIn#rsm_in{direction = aft, id = UID};
rsm_parse_element(#xmlel{name = <<"index">>,
attrs = []} =
Elem,
RsmIn) ->
- IndexStr = xml:get_tag_cdata(Elem),
+ IndexStr = fxml:get_tag_cdata(Elem),
{Index, _} = str:to_integer(IndexStr),
RsmIn#rsm_in{index = Index};
rsm_parse_element(_, RsmIn) -> RsmIn.
@@ -535,7 +535,7 @@ is_standalone_chat_state(#xmlel{name = <<"message">>} = El) ->
<<"paused">>],
Stripped =
lists:foldl(fun(ChatState, AccEl) ->
- xml:remove_subtags(AccEl, ChatState,
+ fxml:remove_subtags(AccEl, ChatState,
{<<"xmlns">>, ?NS_CHATSTATES})
end, El, ChatStates),
case Stripped of
@@ -558,15 +558,15 @@ add_delay_info(El, From, Time) ->
binary()) -> xmlel().
add_delay_info(El, From, Time, Desc) ->
- case xml:get_subtag_with_xmlns(El, <<"delay">>, ?NS_DELAY) of
+ case fxml:get_subtag_with_xmlns(El, <<"delay">>, ?NS_DELAY) of
false ->
%% Add new tag
DelayTag = create_delay_tag(Time, From, Desc),
- xml:append_subtags(El, [DelayTag]);
+ fxml:append_subtags(El, [DelayTag]);
DelayTag ->
%% Update existing tag
NewDelayTag =
- case {xml:get_tag_cdata(DelayTag), Desc} of
+ case {fxml:get_tag_cdata(DelayTag), Desc} of
{<<"">>, <<"">>} ->
DelayTag;
{OldDesc, <<"">>} ->
@@ -582,8 +582,8 @@ add_delay_info(El, From, Time, Desc) ->
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]}
end
end,
- NewEl = xml:remove_subtags(El, <<"delay">>, {<<"xmlns">>, ?NS_DELAY}),
- xml:append_subtags(NewEl, [NewDelayTag])
+ NewEl = fxml:remove_subtags(El, <<"delay">>, {<<"xmlns">>, ?NS_DELAY}),
+ fxml:append_subtags(NewEl, [NewDelayTag])
end.
-spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary())
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index dae775e4..343a1a3a 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -101,89 +101,156 @@ get_commands_spec() ->
desc = "Recompile and reload Erlang source code file",
module = ?MODULE, function = compile,
args = [{file, string}],
- result = {res, rescode}},
+ args_example = ["/home/me/srcs/ejabberd/mod_example.erl"],
+ args_desc = ["Filename of erlang source file to compile"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = get_cookie, tags = [erlang],
desc = "Get the Erlang cookie of this node",
module = ?MODULE, function = get_cookie,
args = [],
- result = {cookie, string}},
+ result = {cookie, string},
+ result_example = "MWTAVMODFELNLSMYXPPD",
+ result_desc = "Erlang cookie used for authentication by ejabberd"},
#ejabberd_commands{name = remove_node, tags = [erlang],
desc = "Remove an ejabberd node from Mnesia clustering config",
module = ?MODULE, function = remove_node,
args = [{node, string}],
- result = {res, rescode}},
-
+ args_example = ["ejabberd@server2"],
+ args_desc = ["Name of erlang node to remove"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = num_active_users, tags = [accounts, stats],
desc = "Get number of users active in the last days",
policy = admin,
module = ?MODULE, function = num_active_users,
args = [{host, binary}, {days, integer}],
- result = {users, integer}},
+ args_example = [<<"myserver.com">>, 3],
+ args_desc = ["Name of host to check", "Number of days to calculate sum"],
+ result = {users, integer},
+ result_example = 123,
+ result_desc = "Number of users active on given server in last n days"},
#ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
desc = "Delete users that didn't log in last days, or that never logged",
module = ?MODULE, function = delete_old_users,
args = [{days, integer}],
- result = {res, restuple}},
+ args_example = [30],
+ args_desc = ["Last login age in days of accounts that should be removed"],
+ result = {res, restuple},
+ result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
+ result_desc = "Result tuple"},
#ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
desc = "Delete users that didn't log in last days in vhost, or that never logged",
module = ?MODULE, function = delete_old_users_vhost,
args = [{host, binary}, {days, integer}],
- result = {res, restuple}},
-
+ args_example = [<<"myserver.com">>, 30],
+ args_desc = ["Server name",
+ "Last login age in days of accounts that should be removed"],
+ result = {res, restuple},
+ result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
+ result_desc = "Result tuple"},
#ejabberd_commands{name = check_account, tags = [accounts],
desc = "Check if an account exists or not",
module = ejabberd_auth, function = is_user_exists,
args = [{user, binary}, {host, binary}],
- result = {res, rescode}},
+ args_example = [<<"peter">>, <<"myserver.com">>],
+ args_desc = ["User name to check", "Server to check"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password, tags = [accounts],
desc = "Check if a password is correct",
module = ejabberd_auth, function = check_password,
args = [{user, binary}, {host, binary}, {password, binary}],
- result = {res, rescode}},
+ args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>],
+ args_desc = ["User name to check", "Server to check", "Password to check"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password_hash, tags = [accounts],
desc = "Check if the password hash is correct",
longdesc = "Allowed hash methods: md5, sha.",
module = ?MODULE, function = check_password_hash,
- args = [{user, binary}, {host, binary}, {passwordhash, string}, {hashmethod, string}],
- result = {res, rescode}},
+ args = [{user, binary}, {host, binary}, {passwordhash, string},
+ {hashmethod, string}],
+ args_example = [<<"peter">>, <<"myserver.com">>,
+ <<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>],
+ args_desc = ["User name to check", "Server to check",
+ "Password's hash value", "Name of hash method"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = change_password, tags = [accounts],
desc = "Change the password of an account",
module = ?MODULE, function = set_password,
args = [{user, binary}, {host, binary}, {newpass, binary}],
- result = {res, rescode}},
+ args_example = [<<"peter">>, <<"myserver.com">>, <<"blank">>],
+ args_desc = ["User name", "Server name",
+ "New password for user"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = ban_account, tags = [accounts],
desc = "Ban an account: kick sessions and set random password",
module = ?MODULE, function = ban_account,
args = [{user, binary}, {host, binary}, {reason, binary}],
- result = {res, rescode}},
-
+ args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
+ args_desc = ["User name to ban", "Server name",
+ "Reason for banning user"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = num_resources, tags = [session],
desc = "Get the number of resources of a user",
module = ?MODULE, function = num_resources,
args = [{user, binary}, {host, binary}],
- result = {resources, integer}},
+ args_example = [<<"peter">>, <<"myserver.com">>],
+ args_desc = ["User name", "Server name"],
+ result = {resources, integer},
+ result_example = 5,
+ result_desc = "Number of active resources for a user"},
#ejabberd_commands{name = resource_num, tags = [session],
desc = "Resource string of a session number",
module = ?MODULE, function = resource_num,
args = [{user, binary}, {host, binary}, {num, integer}],
- result = {resource, string}},
+ args_example = [<<"peter">>, <<"myserver.com">>, 2],
+ args_desc = ["User name", "Server name", "ID of resource to return"],
+ result = {resource, string},
+ result_example = <<"Psi">>,
+ result_desc = "Name of user resource"},
#ejabberd_commands{name = kick_session, tags = [session],
desc = "Kick a user session",
module = ?MODULE, function = kick_session,
args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}],
- result = {res, rescode}},
+ args_example = [<<"peter">>, <<"myserver.com">>, <<"Psi">>,
+ <<"Stuck connection">>],
+ args_desc = ["User name", "Server name", "User's resource",
+ "Reason for closing session"],
+ result = {res, rescode},
+ result_example = ok,
+ result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = status_num_host, tags = [session, stats],
desc = "Number of logged users with this status in host",
policy = admin,
module = ?MODULE, function = status_num,
args = [{host, binary}, {status, binary}],
- result = {users, integer}},
+ args_example = [<<"myserver.com">>, <<"dnd">>],
+ args_desc = ["Server name", "Status type to check"],
+ result = {users, integer},
+ result_example = 23,
+ result_desc = "Number of connected sessions with given status type"},
#ejabberd_commands{name = status_num, tags = [session, stats],
desc = "Number of logged users with this status",
policy = admin,
module = ?MODULE, function = status_num,
args = [{status, binary}],
- result = {users, integer}},
+ args_example = [<<"dnd">>],
+ args_desc = ["Status type to check"],
+ result = {users, integer},
+ result_example = 23,
+ result_desc = "Number of connected sessions with given status type"},
#ejabberd_commands{name = status_list_host, tags = [session],
desc = "List of users logged in host with their statuses",
module = ?MODULE, function = status_list,
@@ -464,7 +531,7 @@ get_commands_spec() ->
tags = [offline],
desc = "Get the number of unread offline messages",
policy = user,
- module = mod_offline, function = get_queue_length,
+ module = mod_offline, function = count_offline_messages,
args = [],
result = {res, integer}},
#ejabberd_commands{name = send_message, tags = [stanza],
@@ -794,7 +861,8 @@ connected_users_info() ->
PI when is_integer(PI) -> PI;
_ -> nil
end,
- {[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, PriorityI, NodeS, Uptime}
+ {binary_to_list(<<U/binary, $@, S/binary, $/, R/binary>>),
+ atom_to_list(Conn), IPS, Port, PriorityI, NodeS, Uptime}
end,
USRIs).
@@ -912,7 +980,7 @@ get_vcard_content(User, Server, Data) ->
[_|_] ->
case get_vcard(Data, A1) of
[false] -> throw(error_no_value_found_in_vcard);
- ElemList -> ?DEBUG("ELS ~p", [ElemList]), [xml:get_tag_cdata(Elem) || Elem <- ElemList]
+ ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList]
end;
[] ->
throw(error_no_vcard_found)
@@ -933,7 +1001,7 @@ get_vcard([Data], A1) ->
get_subtag(A1, Data).
get_subtag(Xmlelement, Name) ->
- [xml:get_subtag(Xmlelement, Name)].
+ [fxml:get_subtag(Xmlelement, Name)].
set_vcard_content(User, Server, Data, SomeContent) ->
ContentList = case SomeContent of
@@ -963,7 +1031,7 @@ set_vcard_content(User, Server, Data, SomeContent) ->
take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) ->
{Taken2, NewEls2} = case lists:keymember(TelType, 2, SubEls) of
- true -> {xml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
+ true -> {fxml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
false -> {Taken, [OldEl | NewEls]}
end,
take_vcard_tel(TelType, OldEls, NewEls2, Taken2);
@@ -1126,7 +1194,7 @@ push_roster_item(LU, LS, R, U, S, Action) ->
ejabberd_sm:route(LJID, LJID, BroadcastEl),
Item = build_roster_item(U, S, Action),
ResIQ = build_iq_roster_push(Item),
- ejabberd_router:route(LJID, LJID, ResIQ).
+ ejabberd_router:route(jid:remove_resource(LJID), LJID, ResIQ).
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
{xmlel, <<"item">>,
@@ -1206,10 +1274,10 @@ private_get(Username, Host, Element, Ns) ->
[{xmlel, <<"query">>,
[{<<"xmlns">>, ?NS_PRIVATE}],
[SubEl]}] = ResIq#iq.sub_el,
- binary_to_list(xml:element_to_binary(SubEl)).
+ binary_to_list(fxml:element_to_binary(SubEl)).
private_set(Username, Host, ElementString) ->
- case xml_stream:parse_element(ElementString) of
+ case fxml_stream:parse_element(ElementString) of
{error, Error} ->
io:format("Error found parsing the element:~n ~p~nError: ~p~n",
[ElementString, Error]),
@@ -1333,7 +1401,7 @@ build_packet(Type, Subject, Body) ->
}.
send_stanza(FromString, ToString, Stanza) ->
- case xml_stream:parse_element(Stanza) of
+ case fxml_stream:parse_element(Stanza) of
{error, Error} ->
{error, Error};
XmlEl ->
@@ -1344,7 +1412,7 @@ send_stanza(FromString, ToString, Stanza) ->
end.
send_stanza_c2s(Username, Host, Resource, Stanza) ->
- case {xml_stream:parse_element(Stanza),
+ case {fxml_stream:parse_element(Stanza),
ejabberd_sm:get_session_pid(Username, Host, Resource)}
of
{{error, Error}, _} ->
@@ -1358,7 +1426,7 @@ send_stanza_c2s(Username, Host, Resource, Stanza) ->
privacy_set(Username, Host, QueryS) ->
From = jid:make(Username, Host, <<"">>),
To = jid:make(<<"">>, Host, <<"">>),
- QueryEl = xml_stream:parse_element(QueryS),
+ QueryEl = fxml_stream:parse_element(QueryS),
StanzaEl = {xmlel, <<"iq">>, [{<<"type">>, <<"set">>}], [QueryEl]},
IQ = jlib:iq_query_info(StanzaEl),
ejabberd_hooks:run_fold(
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index 3d0924a6..d30cf57f 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -845,7 +845,7 @@ announce_motd_update(LServer, Packet) ->
packet = Packet},
motd_schema())};
odbc ->
- XML = ejabberd_odbc:escape(xml:element_to_binary(Packet)),
+ XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet)),
F = fun() ->
odbc_queries:update_t(
<<"motd">>,
@@ -953,7 +953,7 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID, odbc) when LUser /= <<>>
case catch ejabberd_odbc:sql_query(
LServer, [<<"select xml from motd where username='';">>]) of
{selected, [<<"xml">>], [[XML]]} ->
- case xml_stream:parse_element(XML) of
+ case fxml_stream:parse_element(XML) of
{error, _} ->
ok;
Packet ->
@@ -986,8 +986,8 @@ send_motd(_, odbc) ->
get_stored_motd(LServer) ->
case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
{ok, Packet} ->
- {xml:get_subtag_cdata(Packet, <<"subject">>),
- xml:get_subtag_cdata(Packet, <<"body">>)};
+ {fxml:get_subtag_cdata(Packet, <<"subject">>),
+ fxml:get_subtag_cdata(Packet, <<"body">>)};
error ->
{<<>>, <<>>}
end.
@@ -1010,7 +1010,7 @@ get_stored_motd_packet(LServer, odbc) ->
case catch ejabberd_odbc:sql_query(
LServer, [<<"select xml from motd where username='';">>]) of
{selected, [<<"xml">>], [[XML]]} ->
- case xml_stream:parse_element(XML) of
+ case fxml_stream:parse_element(XML) of
{error, _} ->
error;
Packet ->
@@ -1067,7 +1067,7 @@ update_motd_table() ->
fun(#motd{server = S}) -> S end,
fun(#motd{server = S, packet = P} = R) ->
NewS = iolist_to_binary(S),
- NewP = xml:to_xmlel(P),
+ NewP = fxml:to_xmlel(P),
R#motd{server = NewS, packet = NewP}
end);
_ ->
@@ -1105,7 +1105,7 @@ export(_Server) ->
when LServer == Host ->
[[<<"delete from motd where username='';">>],
[<<"insert into motd(username, xml) values ('', '">>,
- ejabberd_odbc:escape(xml:element_to_binary(El)),
+ ejabberd_odbc:escape(fxml:element_to_binary(El)),
<<"');">>]];
(_Host, _R) ->
[]
@@ -1124,7 +1124,7 @@ export(_Server) ->
import(LServer) ->
[{<<"select xml from motd where username='';">>,
fun([XML]) ->
- El = xml_stream:parse_element(XML),
+ El = fxml_stream:parse_element(XML),
#motd{server = LServer, packet = El}
end},
{<<"select username from motd where xml='';">>,
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index 4f9c8273..815884ff 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -76,7 +76,7 @@ process_iq_set(_, From, _To,
sub_el =
#xmlel{name = SubElName, children = SubEls}}) ->
#jid{luser = LUser, lserver = LServer} = From,
- Res = case {SubElName, xml:remove_cdata(SubEls)} of
+ Res = case {SubElName, fxml:remove_cdata(SubEls)} of
{<<"block">>, []} -> {error, ?ERR_BAD_REQUEST};
{<<"block">>, Els} ->
JIDs = parse_blocklist_items(Els, []),
@@ -116,7 +116,7 @@ parse_blocklist_items([#xmlel{name = <<"item">>,
attrs = Attrs}
| Els],
JIDs) ->
- case xml:get_attr(<<"jid">>, Attrs) of
+ case fxml:get_attr(<<"jid">>, Attrs) of
{value, JID1} ->
JID = jid:tolower(jid:from_string(JID1)),
parse_blocklist_items(Els, [JID | JIDs]);
@@ -223,23 +223,18 @@ process_blocklist_block(LUser, LServer, Filter, odbc) ->
Default = case
mod_privacy:sql_get_default_privacy_list_t(LUser)
of
- {selected, [<<"name">>], []} ->
+ {selected, []} ->
Name = <<"Blocked contacts">>,
mod_privacy:sql_add_privacy_list(LUser, Name),
mod_privacy:sql_set_default_privacy_list(LUser,
Name),
Name;
- {selected, [<<"name">>], [[Name]]} -> Name
+ {selected, [{Name}]} -> Name
end,
- {selected, [<<"id">>], [[ID]]} =
+ {selected, [{ID}]} =
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
- case mod_privacy:sql_get_privacy_list_data_by_id_t(ID)
- of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems = [_ | _]} ->
+ case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+ {selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
_ -> List = []
end,
@@ -345,17 +340,12 @@ unblock_by_filter(LUser, LServer, Filter, odbc) ->
F = fun () ->
case mod_privacy:sql_get_default_privacy_list_t(LUser)
of
- {selected, [<<"name">>], []} -> ok;
- {selected, [<<"name">>], [[Default]]} ->
- {selected, [<<"id">>], [[ID]]} =
+ {selected, []} -> ok;
+ {selected, [{Default}]} ->
+ {selected, [{ID}]} =
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
- case mod_privacy:sql_get_privacy_list_data_by_id_t(ID)
- of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems = [_ | _]} ->
+ case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+ {selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1,
RItems),
NewList = Filter(List),
@@ -435,16 +425,12 @@ process_blocklist_get(LUser, LServer, odbc) ->
case catch
mod_privacy:sql_get_default_privacy_list(LUser, LServer)
of
- {selected, [<<"name">>], []} -> [];
- {selected, [<<"name">>], [[Default]]} ->
+ {selected, []} -> [];
+ {selected, [{Default}]} ->
case catch mod_privacy:sql_get_privacy_list_data(LUser,
LServer, Default)
of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems} ->
+ {selected, RItems} ->
lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
{'EXIT', _} -> error
end;
diff --git a/src/mod_caps.erl b/src/mod_caps.erl
index 7d764c4b..0646d381 100644
--- a/src/mod_caps.erl
+++ b/src/mod_caps.erl
@@ -120,12 +120,12 @@ read_caps(Els) -> read_caps(Els, nothing).
read_caps([#xmlel{name = <<"c">>, attrs = Attrs}
| Tail],
Result) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_CAPS ->
- Node = xml:get_attr_s(<<"node">>, Attrs),
- Version = xml:get_attr_s(<<"ver">>, Attrs),
- Hash = xml:get_attr_s(<<"hash">>, Attrs),
- Exts = str:tokens(xml:get_attr_s(<<"ext">>, Attrs),
+ Node = fxml:get_attr_s(<<"node">>, Attrs),
+ Version = fxml:get_attr_s(<<"ver">>, Attrs),
+ Hash = fxml:get_attr_s(<<"hash">>, Attrs),
+ Exts = str:tokens(fxml:get_attr_s(<<"ext">>, Attrs),
<<" ">>),
read_caps(Tail,
#caps{node = Node, hash = Hash, version = Version,
@@ -135,7 +135,7 @@ read_caps([#xmlel{name = <<"c">>, attrs = Attrs}
read_caps([#xmlel{name = <<"x">>, attrs = Attrs}
| Tail],
Result) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MUC_USER -> nothing;
_ -> read_caps(Tail, Result)
end;
@@ -149,7 +149,7 @@ user_send_packet(#xmlel{name = <<"presence">>, attrs = Attrs,
#jid{luser = User, lserver = Server} = From,
#jid{luser = User, lserver = Server,
lresource = <<"">>}) ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
if Type == <<"">>; Type == <<"available">> ->
case read_caps(Els) of
nothing -> ok;
@@ -167,7 +167,7 @@ user_receive_packet(#xmlel{name = <<"presence">>, attrs = Attrs,
_C2SState,
#jid{lserver = Server},
From, _To) ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
IsRemote = not lists:member(From#jid.lserver, ?MYHOSTS),
if IsRemote and
((Type == <<"">>) or (Type == <<"available">>)) ->
@@ -227,7 +227,7 @@ disco_info(Acc, Host, Module, Node, Lang) ->
c2s_presence_in(C2SState,
{From, To, {_, _, Attrs, Els}}) ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
Subscription = ejabberd_c2s:get_subscription(From,
C2SState),
Insert = ((Type == <<"">>) or (Type == <<"available">>))
@@ -434,7 +434,7 @@ feature_response(#iq{type = result,
Features = lists:flatmap(fun (#xmlel{name =
<<"feature">>,
attrs = FAttrs}) ->
- [xml:get_attr_s(<<"var">>, FAttrs)];
+ [fxml:get_attr_s(<<"var">>, FAttrs)];
(_) -> []
end,
Els),
@@ -567,7 +567,7 @@ concat_features(Els) ->
lists:usort(lists:flatmap(fun (#xmlel{name =
<<"feature">>,
attrs = Attrs}) ->
- [[xml:get_attr_s(<<"var">>, Attrs), $<]];
+ [[fxml:get_attr_s(<<"var">>, Attrs), $<]];
(_) -> []
end,
Els)).
@@ -576,11 +576,11 @@ concat_identities(Els) ->
lists:sort(lists:flatmap(fun (#xmlel{name =
<<"identity">>,
attrs = Attrs}) ->
- [[xml:get_attr_s(<<"category">>, Attrs),
- $/, xml:get_attr_s(<<"type">>, Attrs),
+ [[fxml:get_attr_s(<<"category">>, Attrs),
+ $/, fxml:get_attr_s(<<"type">>, Attrs),
$/,
- xml:get_attr_s(<<"xml:lang">>, Attrs),
- $/, xml:get_attr_s(<<"name">>, Attrs),
+ fxml:get_attr_s(<<"xml:lang">>, Attrs),
+ $/, fxml:get_attr_s(<<"name">>, Attrs),
$<]];
(_) -> []
end,
@@ -589,8 +589,8 @@ concat_identities(Els) ->
concat_info(Els) ->
lists:sort(lists:flatmap(fun (#xmlel{name = <<"x">>,
attrs = Attrs, children = Fields}) ->
- case {xml:get_attr_s(<<"xmlns">>, Attrs),
- xml:get_attr_s(<<"type">>, Attrs)}
+ case {fxml:get_attr_s(<<"xmlns">>, Attrs),
+ fxml:get_attr_s(<<"type">>, Attrs)}
of
{?NS_XDATA, <<"result">>} ->
[concat_xdata_fields(Fields)];
@@ -606,10 +606,10 @@ concat_xdata_fields(Fields) ->
attrs = Attrs, children = Els} =
El,
[FormType, VarFields] = Acc) ->
- case xml:get_attr_s(<<"var">>, Attrs) of
+ case fxml:get_attr_s(<<"var">>, Attrs) of
<<"">> -> Acc;
<<"FORM_TYPE">> ->
- [xml:get_subtag_cdata(El,
+ [fxml:get_subtag_cdata(El,
<<"value">>),
VarFields];
Var ->
@@ -622,7 +622,7 @@ concat_xdata_fields(Fields) ->
children
=
VEls}) ->
- [[xml:get_cdata(VEls),
+ [[fxml:get_cdata(VEls),
$<]];
(_) ->
[]
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index a4ca45dd..81cf78a9 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -55,9 +55,9 @@ is_carbon_copy(Packet) ->
is_carbon_copy(Packet, <<"received">>).
is_carbon_copy(Packet, Direction) ->
- case xml:get_subtag(Packet, Direction) of
+ case fxml:get_subtag(Packet, Direction) of
#xmlel{name = Direction, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_CARBONS_2 -> true;
?NS_CARBONS_1 -> true;
_ -> false
@@ -137,8 +137,8 @@ user_receive_packet(Packet, _C2SState, JID, _From, To) ->
% - we also replicate "read" notifications
check_and_forward(JID, To, Packet, Direction)->
case is_chat_message(Packet) andalso
- xml:get_subtag(Packet, <<"private">>) == false andalso
- xml:get_subtag(Packet, <<"no-copy">>) == false of
+ fxml:get_subtag(Packet, <<"private">>) == false andalso
+ fxml:get_subtag(Packet, <<"no-copy">>) == false of
true ->
case is_carbon_copy(Packet) of
false ->
@@ -268,7 +268,7 @@ complete_packet(_From, #xmlel{name = <<"message">>, attrs=OrigAttrs} = Packet, r
Packet#xmlel{attrs = Attrs}.
message_type(#xmlel{attrs = Attrs}) ->
- case xml:get_attr(<<"type">>, Attrs) of
+ case fxml:get_attr(<<"type">>, Attrs) of
{value, Type} -> Type;
false -> <<"normal">>
end.
@@ -282,7 +282,7 @@ is_chat_message(#xmlel{name = <<"message">>} = Packet) ->
is_chat_message(_Packet) -> false.
has_non_empty_body(Packet) ->
- xml:get_subtag_cdata(Packet, <<"body">>) =/= <<"">>.
+ fxml:get_subtag_cdata(Packet, <<"body">>) =/= <<"">>.
%% list {resource, cc_version} with carbons enabled for given user and host
list(User, Server) ->
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index 3c046d77..dfbfc028 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -80,7 +80,7 @@ add_stream_feature(Features, _Host) ->
[Feature | Features].
filter_presence(_Action, #xmlel{name = <<"presence">>, attrs = Attrs}) ->
- case xml:get_attr(<<"type">>, Attrs) of
+ case fxml:get_attr(<<"type">>, Attrs) of
{value, Type} when Type /= <<"unavailable">> ->
?DEBUG("Got important presence stanza", []),
{stop, send};
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 0c7e7526..5e011704 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -350,7 +350,7 @@ adhoc_local_items(Acc, From,
Nodes = recursively_get_local_items(PermLev, LServer,
<<"">>, Server, Lang),
Nodes1 = lists:filter(fun (N) ->
- Nd = xml:get_tag_attr_s(<<"node">>, N),
+ Nd = fxml:get_tag_attr_s(<<"node">>, N),
F = get_local_features([], From, To, Nd,
Lang),
case F of
@@ -379,9 +379,9 @@ recursively_get_local_items(PermLev, LServer, Node,
{error, _Error} -> []
end,
Nodes = lists:flatten(lists:map(fun (N) ->
- S = xml:get_tag_attr_s(<<"jid">>,
+ S = fxml:get_tag_attr_s(<<"jid">>,
N),
- Nd = xml:get_tag_attr_s(<<"node">>,
+ Nd = fxml:get_tag_attr_s(<<"node">>,
N),
if (S /= Server) or
(Nd == <<"">>) ->
diff --git a/src/mod_configure2.erl b/src/mod_configure2.erl
index 3e00a9cf..0acd4a78 100644
--- a/src/mod_configure2.erl
+++ b/src/mod_configure2.erl
@@ -65,7 +65,7 @@ process_local_iq(From, To,
set ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
- %%case xml:get_tag_attr_s("type", SubEl) of
+ %%case fxml:get_tag_attr_s("type", SubEl) of
%% "cancel" ->
%% IQ#iq{type = result,
%% sub_el = [{xmlelement, "query",
@@ -79,7 +79,7 @@ process_local_iq(From, To,
%% _ ->
%% Node =
%% string:tokens(
- %% xml:get_tag_attr_s("node", SubEl),
+ %% fxml:get_tag_attr_s("node", SubEl),
%% "/"),
%% case set_form(Node, Lang, XData) of
%% {result, Res} ->
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 6e0b9c92..734e90d3 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -152,7 +152,7 @@ process_local_iq_items(From, To,
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
Host = To#jid.lserver,
case ejabberd_hooks:run_fold(disco_local_items, Host,
empty, [From, To, Node, Lang])
@@ -180,7 +180,7 @@ process_local_iq_info(From, To,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
Host = To#jid.lserver,
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
Identity = ejabberd_hooks:run_fold(disco_local_identity,
Host, [], [From, To, Node, Lang]),
Info = ejabberd_hooks:run_fold(disco_info, Host, [],
@@ -305,7 +305,7 @@ process_sm_iq_items(From, To,
case is_presence_subscribed(From, To) of
true ->
Host = To#jid.lserver,
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
case ejabberd_hooks:run_fold(disco_sm_items, Host,
empty, [From, To, Node, Lang])
of
@@ -378,10 +378,12 @@ process_sm_iq_info(From, To,
case is_presence_subscribed(From, To) of
true ->
Host = To#jid.lserver,
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
Identity = ejabberd_hooks:run_fold(disco_sm_identity,
Host, [],
[From, To, Node, Lang]),
+ Info = ejabberd_hooks:run_fold(disco_info, Host, [],
+ [From, To, Node, Lang]),
case ejabberd_hooks:run_fold(disco_sm_features, Host,
empty, [From, To, Node, Lang])
of
@@ -397,7 +399,7 @@ process_sm_iq_info(From, To,
[{<<"xmlns">>, ?NS_DISCO_INFO}
| ANode],
children =
- Identity ++
+ Identity ++ Info ++
features_to_xml(Features)}]};
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]}
diff --git a/src/mod_echo.erl b/src/mod_echo.erl
index 3f91f1d0..7184ee4e 100644
--- a/src/mod_echo.erl
+++ b/src/mod_echo.erl
@@ -86,7 +86,7 @@ stop(Host) ->
init([Host, Opts]) ->
MyHost = gen_mod:get_opt_host(Host, Opts,
<<"echo.@HOST@">>),
- ejabberd_router:register_route(MyHost),
+ ejabberd_router:register_route(MyHost, Host),
{ok, #state{host = MyHost}}.
%%--------------------------------------------------------------------
@@ -182,7 +182,7 @@ do_client_version(enabled, From, To) ->
Els = receive
{route, To, From2, IQ} ->
#xmlel{name = <<"query">>, children = List} =
- xml:get_subtag(IQ, <<"query">>),
+ fxml:get_subtag(IQ, <<"query">>),
List
after 5000 -> % Timeout in miliseconds: 5 seconds
[]
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 36d3626b..6c029c43 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -86,7 +86,8 @@
%% Utility functions.
-export([get_proc_name/2,
- expand_home/1]).
+ expand_home/1,
+ expand_host/2]).
-include("ejabberd.hrl").
-include("ejabberd_http.hrl").
@@ -273,6 +274,8 @@ init({ServerHost, Opts}) ->
Thumbnail = gen_mod:get_opt(thumbnail, Opts,
fun(B) when is_boolean(B) -> B end,
true),
+ DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
+ DocRoot2 = expand_host(DocRoot1, ServerHost),
case ServiceURL of
undefined ->
ok;
@@ -289,7 +292,7 @@ init({ServerHost, Opts}) ->
undefined ->
ok;
Mode ->
- file:change_mode(DocRoot, Mode)
+ file:change_mode(DocRoot2, Mode)
end,
case Thumbnail of
true ->
@@ -303,13 +306,13 @@ init({ServerHost, Opts}) ->
false ->
ok
end,
- ejabberd_router:register_route(Host),
+ ejabberd_router:register_route(Host, ServerHost),
{ok, #state{server_host = ServerHost, host = Host, name = Name,
access = Access, max_size = MaxSize,
secret_length = SecretLength, jid_in_url = JIDinURL,
file_mode = FileMode, dir_mode = DirMode,
thumbnail = Thumbnail,
- docroot = expand_home(str:strip(DocRoot, right, $/)),
+ docroot = DocRoot2,
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
service_url = ServiceURL}}.
@@ -509,6 +512,12 @@ expand_home(Subject) ->
Parts = binary:split(Subject, <<"@HOME@">>, [global]),
str:join(Parts, list_to_binary(Home)).
+-spec expand_host(binary(), binary()) -> binary().
+
+expand_host(Subject, Host) ->
+ Parts = binary:split(Subject, <<"@HOST@">>, [global]),
+ str:join(Parts, Host).
+
%%--------------------------------------------------------------------
%% Internal functions.
%%--------------------------------------------------------------------
@@ -526,7 +535,8 @@ process_iq(_From,
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
- children = iq_disco_info(Lang, Name) ++ AddInfo}]};
+ children = iq_disco_info(ServerHost, Lang, Name)
+ ++ AddInfo}]};
process_iq(From,
#iq{type = get, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ,
#state{server_host = ServerHost, access = Access} = State)
@@ -572,12 +582,12 @@ process_iq(_From, invalid, _State) ->
-> {ok, binary(), pos_integer(), binary()} | {error, xmlel()}.
parse_request(#xmlel{name = <<"request">>, attrs = Attrs} = Request, Lang) ->
- case xml:get_attr(<<"xmlns">>, Attrs) of
+ case fxml:get_attr(<<"xmlns">>, Attrs) of
{value, XMLNS} when XMLNS == ?NS_HTTP_UPLOAD;
XMLNS == ?NS_HTTP_UPLOAD_OLD ->
- case {xml:get_subtag_cdata(Request, <<"filename">>),
- xml:get_subtag_cdata(Request, <<"size">>),
- xml:get_subtag_cdata(Request, <<"content-type">>)} of
+ case {fxml:get_subtag_cdata(Request, <<"filename">>),
+ fxml:get_subtag_cdata(Request, <<"size">>),
+ fxml:get_subtag_cdata(Request, <<"content-type">>)} of
{File, SizeStr, ContentType} when byte_size(File) > 0 ->
case catch jlib:binary_to_integer(SizeStr) of
Size when is_integer(Size), Size > 0 ->
@@ -737,20 +747,41 @@ map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
--spec expand_host(binary(), binary()) -> binary().
-
-expand_host(Subject, Host) ->
- Parts = binary:split(Subject, <<"@HOST@">>, [global]),
- str:join(Parts, Host).
-
-spec yield_content_type(binary()) -> binary().
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
yield_content_type(Type) -> Type.
--spec iq_disco_info(binary(), binary()) -> [xmlel()].
-
-iq_disco_info(Lang, Name) ->
+-spec iq_disco_info(binary(), binary(), binary()) -> [xmlel()].
+
+iq_disco_info(Host, Lang, Name) ->
+ Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size,
+ fun(I) when is_integer(I), I > 0 -> I;
+ (infinity) -> infinity
+ end,
+ 104857600) of
+ infinity ->
+ [];
+ MaxSize ->
+ MaxSizeStr = jlib:integer_to_binary(MaxSize),
+ Fields = [#xmlel{name = <<"field">>,
+ attrs = [{<<"type">>, <<"hidden">>},
+ {<<"var">>, <<"FORM_TYPE">>}],
+ children = [#xmlel{name = <<"value">>,
+ children =
+ [{xmlcdata,
+ ?NS_HTTP_UPLOAD}]}]},
+ #xmlel{name = <<"field">>,
+ attrs = [{<<"var">>, <<"max-file-size">>}],
+ children = [#xmlel{name = <<"value">>,
+ children =
+ [{xmlcdata,
+ MaxSizeStr}]}]}],
+ [#xmlel{name = <<"x">>,
+ attrs = [{<<"xmlns">>, ?NS_XDATA},
+ {<<"type">>, <<"result">>}],
+ children = Fields}]
+ end,
[#xmlel{name = <<"identity">>,
attrs = [{<<"category">>, <<"store">>},
{<<"type">>, <<"file">>},
@@ -758,7 +789,7 @@ iq_disco_info(Lang, Name) ->
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD}]},
#xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]}].
+ attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]} | Form].
%% HTTP request handling.
@@ -796,7 +827,7 @@ store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
{ok,
[{<<"Content-Type">>,
<<"text/xml; charset=utf-8">>}],
- xml:element_to_binary(ThumbEl)};
+ fxml:element_to_binary(ThumbEl)};
pass ->
ok
end;
@@ -974,8 +1005,9 @@ remove_user(User, Server) ->
(node) -> node
end,
sha1),
+ DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
UserStr = make_user_string(jid:make(User, Server, <<"">>), JIDinURL),
- UserDir = str:join([expand_home(DocRoot), UserStr], <<$/>>),
+ UserDir = str:join([DocRoot1, UserStr], <<$/>>),
case del_tree(UserDir) of
ok ->
?INFO_MSG("Removed HTTP upload directory of ~s@~s", [User, Server]);
diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl
index db0b4aa4..a5ae0c3c 100644
--- a/src/mod_http_upload_quota.erl
+++ b/src/mod_http_upload_quota.erl
@@ -132,6 +132,7 @@ init({ServerHost, Opts}) ->
fun iolist_to_binary/1,
<<"@HOME@/upload">>),
DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)),
+ DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost),
Timers = if MaxDays == infinity -> [];
true ->
{ok, T1} = timer:send_after(?INITIAL_TIMEOUT, sweep),
@@ -144,7 +145,7 @@ init({ServerHost, Opts}) ->
access_soft_quota = AccessSoftQuota,
access_hard_quota = AccessHardQuota,
max_days = MaxDays,
- docroot = DocRoot2,
+ docroot = DocRoot3,
timers = Timers}}.
-spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}.
diff --git a/src/mod_irc.erl b/src/mod_irc.erl
index 18308895..d5cd0135 100644
--- a/src/mod_irc.erl
+++ b/src/mod_irc.erl
@@ -116,7 +116,7 @@ stop(Host) ->
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
- ejabberd:start_app(p1_iconv),
+ ejabberd:start_app(iconv),
MyHost = gen_mod:get_opt_host(Host, Opts,
<<"irc.@HOST@">>),
case gen_mod:db_type(Host, Opts) of
@@ -133,7 +133,7 @@ init([Host, Opts]) ->
catch ets:new(irc_connection,
[named_table, public,
{keypos, #irc_connection.jid_server_host}]),
- ejabberd_router:register_route(MyHost),
+ ejabberd_router:register_route(MyHost, Host),
{ok,
#state{host = MyHost, server_host = Host,
access = Access}}.
@@ -216,7 +216,7 @@ do_route(Host, ServerHost, Access, From, To, Packet) ->
allow -> do_route1(Host, ServerHost, From, To, Packet);
_ ->
#xmlel{attrs = Attrs} = Packet,
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
ErrText = <<"Access denied by service policy">>,
Err = jlib:make_error_reply(Packet,
?ERRT_FORBIDDEN(Lang, ErrText)),
@@ -234,7 +234,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
#iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS,
sub_el = SubEl, lang = Lang} =
IQ ->
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
[],
[ServerHost, ?MODULE,
@@ -262,7 +262,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
#iq{type = get, xmlns = (?NS_DISCO_ITEMS) = XMLNS,
sub_el = SubEl, lang = Lang} =
IQ ->
- Node = xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
case Node of
<<>> ->
ResIQ = IQ#iq{type = result,
@@ -516,7 +516,7 @@ find_xdata_el1([]) -> false;
find_xdata_el1([#xmlel{name = Name, attrs = Attrs,
children = SubEls}
| Els]) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_XDATA ->
#xmlel{name = Name, attrs = Attrs, children = SubEls};
_ -> find_xdata_el1(Els)
@@ -535,7 +535,7 @@ process_irc_register(ServerHost, Host, From, _To,
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
#xmlel{attrs = Attrs} ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"cancel">> ->
IQ#iq{type = result,
sub_el =
@@ -549,7 +549,7 @@ process_irc_register(ServerHost, Host, From, _To,
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
_ ->
- Node = str:tokens(xml:get_tag_attr_s(<<"node">>,
+ Node = str:tokens(fxml:get_tag_attr_s(<<"node">>,
SubEl),
<<"/">>),
case set_form(ServerHost, Host, From, Node, Lang,
@@ -571,7 +571,7 @@ process_irc_register(ServerHost, Host, From, _To,
end
end;
get ->
- Node = str:tokens(xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = str:tokens(fxml:get_tag_attr_s(<<"node">>, SubEl),
<<"/">>),
case get_form(ServerHost, Host, From, Node, Lang) of
{result, Res} ->
@@ -1368,7 +1368,7 @@ mod_opt_type(_) ->
[access, db_type, default_encoding, host].
extract_ident(Packet) ->
- case xml:get_subtag(Packet, <<"headers">>) of
+ case fxml:get_subtag(Packet, <<"headers">>) of
{xmlel, _Name, _Attrs, Headers} ->
extract_header(<<"X-Irc-Ident">>, Headers);
_ ->
@@ -1376,7 +1376,7 @@ extract_ident(Packet) ->
end.
extract_ip_address(Packet) ->
- case xml:get_subtag(Packet, <<"headers">>) of
+ case fxml:get_subtag(Packet, <<"headers">>) of
{xmlel, _Name, _Attrs, Headers} ->
extract_header(<<"X-Forwarded-For">>, Headers);
_ ->
@@ -1384,7 +1384,7 @@ extract_ip_address(Packet) ->
end.
extract_header(HeaderName, [{xmlel, _Name, _Attrs, [{xmlcdata, Value}]} | Tail]) ->
- case xml:get_attr(<<"name">>, _Attrs) of
+ case fxml:get_attr(<<"name">>, _Attrs) of
{value, HeaderName} ->
binary_to_list(Value);
_ ->
diff --git a/src/mod_irc_connection.erl b/src/mod_irc_connection.erl
index ae709dc2..098c8c28 100644
--- a/src/mod_irc_connection.erl
+++ b/src/mod_irc_connection.erl
@@ -233,7 +233,7 @@ get_password_from_presence(#xmlel{name = <<"presence">>,
case lists:filter(fun (El) ->
case El of
#xmlel{name = <<"x">>, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MUC -> true;
_ -> false
end;
@@ -243,9 +243,9 @@ get_password_from_presence(#xmlel{name = <<"presence">>,
Els)
of
[ElXMUC | _] ->
- case xml:get_subtag(ElXMUC, <<"password">>) of
+ case fxml:get_subtag(ElXMUC, <<"password">>) of
#xmlel{name = <<"password">>} = PasswordTag ->
- {true, xml:get_tag_cdata(PasswordTag)};
+ {true, fxml:get_tag_cdata(PasswordTag)};
_ -> false
end;
_ -> false
@@ -261,7 +261,7 @@ handle_info({route_chan, Channel, Resource,
#xmlel{name = <<"presence">>, attrs = Attrs} =
Presence},
StateName, StateData) ->
- NewStateData = case xml:get_attr_s(<<"type">>, Attrs) of
+ NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
<<"unavailable">> ->
send_stanza_unavailable(Channel, StateData),
S1 = (?SEND((io_lib:format("PART #~s\r\n",
@@ -312,9 +312,9 @@ handle_info({route_chan, Channel, Resource,
handle_info({route_chan, Channel, Resource,
#xmlel{name = <<"message">>, attrs = Attrs} = El},
StateName, StateData) ->
- NewStateData = case xml:get_attr_s(<<"type">>, Attrs) of
+ NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
<<"groupchat">> ->
- case xml:get_path_s(El, [{elem, <<"subject">>}, cdata])
+ case fxml:get_path_s(El, [{elem, <<"subject">>}, cdata])
of
<<"">> ->
ejabberd_router:route(
@@ -325,7 +325,7 @@ handle_info({route_chan, Channel, Resource,
StateData#state.host,
StateData#state.nick),
StateData#state.user, El),
- Body = xml:get_path_s(El,
+ Body = fxml:get_path_s(El,
[{elem, <<"body">>},
cdata]),
case Body of
@@ -386,7 +386,7 @@ handle_info({route_chan, Channel, Resource,
when Type == <<"chat">>;
Type == <<"">>;
Type == <<"normal">> ->
- Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]),
+ Body = fxml:get_path_s(El, [{elem, <<"body">>}, cdata]),
case Body of
<<"/quote ", Rest/binary>> ->
?SEND(<<Rest/binary, "\r\n">>);
@@ -481,9 +481,9 @@ handle_info({route_chan, _Channel, _Resource, _Packet},
handle_info({route_nick, Nick,
#xmlel{name = <<"message">>, attrs = Attrs} = El},
StateName, StateData) ->
- NewStateData = case xml:get_attr_s(<<"type">>, Attrs) of
+ NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
<<"chat">> ->
- Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]),
+ Body = fxml:get_path_s(El, [{elem, <<"body">>}, cdata]),
case Body of
<<"/quote ", Rest/binary>> ->
?SEND(<<Rest/binary, "\r\n">>);
@@ -778,13 +778,13 @@ bounce_messages(Reason) ->
receive
{send_element, El} ->
#xmlel{attrs = Attrs} = El,
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
_ ->
Err = jlib:make_error_reply(El, <<"502">>, Reason),
- From = jid:from_string(xml:get_attr_s(<<"from">>,
+ From = jid:from_string(fxml:get_attr_s(<<"from">>,
Attrs)),
- To = jid:from_string(xml:get_attr_s(<<"to">>,
+ To = jid:from_string(fxml:get_attr_s(<<"to">>,
Attrs)),
ejabberd_router:route(To, From, Err)
end,
@@ -1535,14 +1535,14 @@ iq_admin(StateData, Channel, From, To,
end.
process_iq_admin(StateData, Channel, set, SubEl) ->
- case xml:get_subtag(SubEl, <<"item">>) of
+ case fxml:get_subtag(SubEl, <<"item">>) of
false -> {error, ?ERR_BAD_REQUEST};
ItemEl ->
- Nick = xml:get_tag_attr_s(<<"nick">>, ItemEl),
- Affiliation = xml:get_tag_attr_s(<<"affiliation">>,
+ Nick = fxml:get_tag_attr_s(<<"nick">>, ItemEl),
+ Affiliation = fxml:get_tag_attr_s(<<"affiliation">>,
ItemEl),
- Role = xml:get_tag_attr_s(<<"role">>, ItemEl),
- Reason = xml:get_path_s(ItemEl,
+ Role = fxml:get_tag_attr_s(<<"role">>, ItemEl),
+ Reason = fxml:get_path_s(ItemEl,
[{elem, <<"reason">>}, cdata]),
process_admin(StateData, Channel, Nick, Affiliation,
Role, Reason)
diff --git a/src/mod_last.erl b/src/mod_last.erl
index d76b6049..33f88e02 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -185,18 +185,12 @@ get_last(LUser, LServer, riak) ->
Err
end;
get_last(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_last(LServer, Username) of
- {selected, [<<"seconds">>, <<"state">>], []} ->
- not_found;
- {selected, [<<"seconds">>, <<"state">>],
- [[STimeStamp, Status]]} ->
- case catch jlib:binary_to_integer(STimeStamp) of
- TimeStamp when is_integer(TimeStamp) ->
- {ok, TimeStamp, Status};
- Reason -> {error, {invalid_timestamp, Reason}}
- end;
- Reason -> {error, {invalid_result, Reason}}
+ case catch odbc_queries:get_last(LServer, LUser) of
+ {selected, []} ->
+ not_found;
+ {selected, [{TimeStamp, Status}]} ->
+ {ok, TimeStamp, Status};
+ Reason -> {error, {invalid_result, Reason}}
end.
get_last_iq(IQ, SubEl, LUser, LServer) ->
@@ -260,12 +254,7 @@ store_last_info(LUser, LServer, TimeStamp, Status,
last_activity_schema())};
store_last_info(LUser, LServer, TimeStamp, Status,
odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- Seconds =
- ejabberd_odbc:escape(iolist_to_binary(integer_to_list(TimeStamp))),
- State = ejabberd_odbc:escape(Status),
- odbc_queries:set_last_t(LServer, Username, Seconds,
- State).
+ odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status).
%% @spec (LUser::string(), LServer::string()) ->
%% {ok, TimeStamp::integer(), Status::string()} | not_found
@@ -286,8 +275,7 @@ remove_user(LUser, LServer, mnesia) ->
F = fun () -> mnesia:delete({last_activity, US}) end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_last(LServer, Username);
+ odbc_queries:del_last(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index d6e4e936..38642c0c 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -287,7 +287,7 @@ muc_process_iq(#iq{type = set,
sub_el = #xmlel{name = <<"query">>,
attrs = Attrs} = SubEl} = IQ,
MUCState, From, To) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
muc_process_iq(IQ, MUCState, From, To, get_xdata_fields(SubEl));
_ ->
@@ -297,7 +297,7 @@ muc_process_iq(#iq{type = get,
sub_el = #xmlel{name = <<"query">>,
attrs = Attrs} = SubEl} = IQ,
MUCState, From, To) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MAM_TMP ->
muc_process_iq(IQ, MUCState, From, To, parse_query_v0_2(SubEl));
NS when NS == ?NS_MAM_0; NS == ?NS_MAM_1 ->
@@ -310,8 +310,8 @@ muc_process_iq(IQ, _MUCState, _From, _To) ->
IQ.
get_xdata_fields(SubEl) ->
- case {xml:get_subtag_with_xmlns(SubEl, <<"x">>, ?NS_XDATA),
- xml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
+ case {fxml:get_subtag_with_xmlns(SubEl, <<"x">>, ?NS_XDATA),
+ fxml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
{#xmlel{} = XData, false} ->
jlib:parse_xdata_submit(XData);
{#xmlel{} = XData, #xmlel{}} ->
@@ -407,7 +407,7 @@ delete_old_messages(_TimeStamp, _Type, _Host, _DBType) ->
%%%===================================================================
process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
- NS = case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ NS = case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MAM_0 ->
?NS_MAM_0;
_ ->
@@ -448,7 +448,7 @@ process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
process_iq(#jid{luser = LUser, lserver = LServer},
#jid{lserver = LServer},
#iq{type = set, sub_el = #xmlel{name = <<"prefs">>} = SubEl} = IQ) ->
- try {case xml:get_tag_attr_s(<<"default">>, SubEl) of
+ try {case fxml:get_tag_attr_s(<<"default">>, SubEl) of
<<"always">> -> always;
<<"never">> -> never;
<<"roster">> -> roster
@@ -461,11 +461,13 @@ process_iq(#jid{luser = LUser, lserver = LServer},
(_, {A, N}) ->
{A, N}
end, {[], []}, SubEl#xmlel.children)} of
- {Default, {Always, Never}} ->
- case write_prefs(LUser, LServer, LServer, Default,
- lists:usort(Always), lists:usort(Never)) of
+ {Default, {Always0, Never0}} ->
+ Always = lists:usort(Always0),
+ Never = lists:usort(Never0),
+ case write_prefs(LUser, LServer, LServer, Default, Always, Never) of
ok ->
- IQ#iq{type = result, sub_el = []};
+ NewPrefs = prefs_el(Default, Always, Never, IQ#iq.xmlns),
+ IQ#iq{type = result, sub_el = [NewPrefs]};
_Err ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
@@ -477,21 +479,11 @@ process_iq(#jid{luser = LUser, lserver = LServer},
#jid{lserver = LServer},
#iq{type = get, sub_el = #xmlel{name = <<"prefs">>}} = IQ) ->
Prefs = get_prefs(LUser, LServer),
- Default = jlib:atom_to_binary(Prefs#archive_prefs.default),
- JFun = fun(L) ->
- [#xmlel{name = <<"jid">>,
- children = [{xmlcdata, jid:to_string(J)}]}
- || J <- L]
- end,
- Always = #xmlel{name = <<"always">>,
- children = JFun(Prefs#archive_prefs.always)},
- Never = #xmlel{name = <<"never">>,
- children = JFun(Prefs#archive_prefs.never)},
- IQ#iq{type = result,
- sub_el = [#xmlel{name = <<"prefs">>,
- attrs = [{<<"xmlns">>, IQ#iq.xmlns},
- {<<"default">>, Default}],
- children = [Always, Never]}]};
+ PrefsEl = prefs_el(Prefs#archive_prefs.default,
+ Prefs#archive_prefs.always,
+ Prefs#archive_prefs.never,
+ IQ#iq.xmlns),
+ IQ#iq{type = result, sub_el = [PrefsEl]};
process_iq(_, _, #iq{sub_el = SubEl} = IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
@@ -524,16 +516,13 @@ process_iq(LServer, #jid{luser = LUser} = From, To, IQ, SubEl, Fs, MsgType) ->
{_Start, _End, _With, #rsm_in{index = Index}} when is_integer(Index) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
{Start, End, With, RSM} ->
- NS = xml:get_tag_attr_s(<<"xmlns">>, SubEl),
+ NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
select_and_send(LServer, From, To, Start, End,
With, limit_max(RSM, NS), IQ, MsgType)
end.
-muc_process_iq(#iq{lang = Lang, sub_el = SubEl} = IQ,
- #state{config = #config{members_only = MembersOnly}} = MUCState,
- From, To, Fs) ->
- case not MembersOnly orelse
- mod_muc_room:is_occupant_or_admin(From, MUCState) of
+muc_process_iq(#iq{lang = Lang, sub_el = SubEl} = IQ, MUCState, From, To, Fs) ->
+ case may_enter_room(From, MUCState) of
true ->
LServer = MUCState#state.server_host,
Role = mod_muc_room:get_role(From, MUCState),
@@ -548,13 +537,13 @@ muc_process_iq(#iq{lang = Lang, sub_el = SubEl} = IQ,
parse_query_v0_2(Query) ->
lists:flatmap(
fun (#xmlel{name = <<"start">>} = El) ->
- [{<<"start">>, [xml:get_tag_cdata(El)]}];
+ [{<<"start">>, [fxml:get_tag_cdata(El)]}];
(#xmlel{name = <<"end">>} = El) ->
- [{<<"end">>, [xml:get_tag_cdata(El)]}];
+ [{<<"end">>, [fxml:get_tag_cdata(El)]}];
(#xmlel{name = <<"with">>} = El) ->
- [{<<"with">>, [xml:get_tag_cdata(El)]}];
+ [{<<"with">>, [fxml:get_tag_cdata(El)]}];
(#xmlel{name = <<"withtext">>} = El) ->
- [{<<"withtext">>, [xml:get_tag_cdata(El)]}];
+ [{<<"withtext">>, [fxml:get_tag_cdata(El)]}];
(#xmlel{name = <<"set">>}) ->
[{<<"set">>, Query}];
(_) ->
@@ -562,7 +551,7 @@ parse_query_v0_2(Query) ->
end, Query#xmlel.children).
should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
- case xml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
+ case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
<<"error">> ->
false;
<<"groupchat">> ->
@@ -578,7 +567,7 @@ should_archive(#xmlel{name = <<"message">>} = Pkt, LServer) ->
no_store ->
false;
none ->
- case xml:get_subtag_cdata(Pkt, <<"body">>) of
+ case fxml:get_subtag_cdata(Pkt, <<"body">>) of
<<>> ->
%% Empty body
false;
@@ -596,7 +585,7 @@ strip_my_archived_tag(Pkt, LServer) ->
fun(#xmlel{name = Tag, attrs = Attrs})
when Tag == <<"archived">>; Tag == <<"stanza-id">> ->
case catch jid:nameprep(
- xml:get_attr_s(
+ fxml:get_attr_s(
<<"by">>, Attrs)) of
LServer ->
false;
@@ -612,9 +601,9 @@ strip_x_jid_tags(Pkt) ->
NewEls = lists:filter(
fun(#xmlel{name = <<"x">>} = XEl) ->
not lists:any(fun(ItemEl) ->
- xml:get_tag_attr(<<"jid">>, ItemEl)
+ fxml:get_tag_attr(<<"jid">>, ItemEl)
/= false
- end, xml:get_subtags(XEl, <<"item">>));
+ end, fxml:get_subtags(XEl, <<"item">>));
(_) ->
true
end, Pkt#xmlel.children),
@@ -650,7 +639,7 @@ should_archive_peer(C2SState,
end.
should_archive_muc(Pkt) ->
- case xml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
+ case fxml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
<<"groupchat">> ->
case check_store_hint(Pkt) of
store ->
@@ -658,9 +647,9 @@ should_archive_muc(Pkt) ->
no_store ->
false;
none ->
- case xml:get_subtag_cdata(Pkt, <<"body">>) of
+ case fxml:get_subtag_cdata(Pkt, <<"body">>) of
<<>> ->
- case xml:get_subtag_cdata(Pkt, <<"subject">>) of
+ case fxml:get_subtag_cdata(Pkt, <<"subject">>) of
<<>> ->
false;
_ ->
@@ -688,23 +677,23 @@ check_store_hint(Pkt) ->
end.
has_store_hint(Message) ->
- xml:get_subtag_with_xmlns(Message, <<"store">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Message, <<"store">>, ?NS_HINTS)
/= false.
has_no_store_hint(Message) ->
- xml:get_subtag_with_xmlns(Message, <<"no-store">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Message, <<"no-store">>, ?NS_HINTS)
/= false orelse
- xml:get_subtag_with_xmlns(Message, <<"no-storage">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Message, <<"no-storage">>, ?NS_HINTS)
/= false orelse
- xml:get_subtag_with_xmlns(Message, <<"no-permanent-store">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Message, <<"no-permanent-store">>, ?NS_HINTS)
/= false orelse
- xml:get_subtag_with_xmlns(Message, <<"no-permanent-storage">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Message, <<"no-permanent-storage">>, ?NS_HINTS)
/= false.
is_resent(Pkt, LServer) ->
- case xml:get_subtag_with_xmlns(Pkt, <<"stanza-id">>, ?NS_SID_0) of
+ case fxml:get_subtag_with_xmlns(Pkt, <<"stanza-id">>, ?NS_SID_0) of
#xmlel{attrs = Attrs} ->
- case xml:get_attr(<<"by">>, Attrs) of
+ case fxml:get_attr(<<"by">>, Attrs) of
{value, LServer} ->
true;
_ ->
@@ -714,6 +703,12 @@ is_resent(Pkt, LServer) ->
false
end.
+may_enter_room(From,
+ #state{config = #config{members_only = false}} = MUCState) ->
+ mod_muc_room:get_affiliation(From, MUCState) /= outcast;
+may_enter_room(From, MUCState) ->
+ mod_muc_room:is_occupant_or_admin(From, MUCState).
+
store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
Prefs = get_prefs(LUser, LServer),
case should_archive_peer(C2SState, Prefs, Peer) of
@@ -766,8 +761,8 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, odbc) ->
jid:remove_resource(Peer))),
LPeer = jid:to_string(
jid:tolower(Peer)),
- XML = xml:element_to_binary(Pkt),
- Body = xml:get_subtag_cdata(Pkt, <<"body">>),
+ XML = fxml:element_to_binary(Pkt),
+ Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
case ejabberd_odbc:sql_query(
LServer,
[<<"insert into archive (username, timestamp, "
@@ -871,6 +866,22 @@ get_prefs(LUser, LServer, odbc) ->
error
end.
+prefs_el(Default, Always, Never, NS) ->
+ Default1 = jlib:atom_to_binary(Default),
+ JFun = fun(L) ->
+ [#xmlel{name = <<"jid">>,
+ children = [{xmlcdata, jid:to_string(J)}]}
+ || J <- L]
+ end,
+ Always1 = #xmlel{name = <<"always">>,
+ children = JFun(Always)},
+ Never1 = #xmlel{name = <<"never">>,
+ children = JFun(Never)},
+ #xmlel{name = <<"prefs">>,
+ attrs = [{<<"xmlns">>, NS},
+ {<<"default">>, Default1}],
+ children = [Always1, Never1]}.
+
maybe_activate_mam(LUser, LServer) ->
ActivateOpt = gen_mod:get_module_opt(LServer, ?MODULE,
request_activates_archiving,
@@ -916,12 +927,12 @@ select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType, DBType) -
select_and_start(LServer, From, To, Start, End, With, RSM, MsgType, DBType) ->
case MsgType of
chat ->
- select(LServer, From, Start, End, With, RSM, MsgType, DBType);
+ select(LServer, From, From, Start, End, With, RSM, MsgType, DBType);
{groupchat, _Role, _MUCState} ->
- select(LServer, To, Start, End, With, RSM, MsgType, DBType)
+ select(LServer, From, To, Start, End, With, RSM, MsgType, DBType)
end.
-select(_LServer, JidRequestor, Start, End, _With, RSM,
+select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
{groupchat, _Role, #state{config = #config{mam = false},
history = History}} = MsgType,
_DBType) ->
@@ -941,8 +952,8 @@ select(_LServer, JidRequestor, Start, End, _With, RSM,
peer = undefined,
nick = Nick,
packet = Pkt},
- MsgType,
- JidRequestor)}], I+1};
+ MsgType, JidRequestor, JidArchive)}],
+ I+1};
false ->
{[], I+1}
end
@@ -958,7 +969,8 @@ select(_LServer, JidRequestor, Start, End, _With, RSM,
_ ->
{Msgs, true, L}
end;
-select(_LServer, #jid{luser = LUser, lserver = LServer} = JidRequestor,
+select(_LServer, JidRequestor,
+ #jid{luser = LUser, lserver = LServer} = JidArchive,
Start, End, With, RSM, MsgType, mnesia) ->
MS = make_matchspec(LUser, LServer, Start, End, With),
Msgs = mnesia:dirty_select(archive_msg, MS),
@@ -969,13 +981,13 @@ select(_LServer, #jid{luser = LUser, lserver = LServer} = JidRequestor,
fun(Msg) ->
{Msg#archive_msg.id,
jlib:binary_to_integer(Msg#archive_msg.id),
- msg_to_el(Msg, MsgType, JidRequestor)}
+ msg_to_el(Msg, MsgType, JidRequestor, JidArchive)}
end, FilteredMsgs), IsComplete, Count};
-select(LServer, #jid{luser = LUser} = JidRequestor,
+select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
Start, End, With, RSM, MsgType, {odbc, Host}) ->
User = case MsgType of
chat -> LUser;
- {groupchat, _Role, _MUCState} -> jid:to_string(JidRequestor)
+ {groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
end,
{Query, CountQuery} = make_sql_query(User, LServer,
Start, End, With, RSM),
@@ -1005,7 +1017,7 @@ select(LServer, #jid{luser = LUser} = JidRequestor,
{lists:flatmap(
fun([TS, XML, PeerBin, Kind, Nick]) ->
try
- #xmlel{} = El = xml_stream:parse_element(XML),
+ #xmlel{} = El = fxml_stream:parse_element(XML),
Now = usec_to_now(jlib:binary_to_integer(TS)),
PeerJid = jid:tolower(jid:from_string(PeerBin)),
T = case Kind of
@@ -1019,8 +1031,7 @@ select(LServer, #jid{luser = LUser} = JidRequestor,
type = T,
nick = Nick,
peer = PeerJid},
- MsgType,
- JidRequestor)}]
+ MsgType, JidRequestor, JidArchive)}]
catch _:Err ->
?ERROR_MSG("failed to parse data from SQL: ~p. "
"The data was: "
@@ -1035,31 +1046,44 @@ select(LServer, #jid{luser = LUser} = JidRequestor,
end.
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
- MsgType, #jid{lserver = LServer} = JidRequestor) ->
- Pkt2 = maybe_update_from_to(Pkt1, JidRequestor, Peer, MsgType, Nick),
+ MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
+ Pkt2 = maybe_update_from_to(Pkt1, JidRequestor, JidArchive, Peer, MsgType,
+ Nick),
Pkt3 = #xmlel{name = <<"forwarded">>,
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
- children = [xml:replace_tag_attr(
+ children = [fxml:replace_tag_attr(
<<"xmlns">>, <<"jabber:client">>, Pkt2)]},
jlib:add_delay_info(Pkt3, LServer, TS).
-maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor,
- Peer, {groupchat, Role, _MUCState}, Nick) ->
- Items = case Role of
- moderator when Peer /= undefined ->
+maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor, JidArchive,
+ Peer, {groupchat, Role,
+ #state{config = #config{anonymous = Anon}}},
+ Nick) ->
+ ExposeJID = case {Peer, JidRequestor} of
+ {undefined, _JidRequestor} ->
+ false;
+ {{U, S, _R}, #jid{luser = U, lserver = S}} ->
+ true;
+ {_Peer, _JidRequestor} when not Anon; Role == moderator ->
+ true;
+ {_Peer, _JidRequestor} ->
+ false
+ end,
+ Items = case ExposeJID of
+ true ->
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
children =
[#xmlel{name = <<"item">>,
attrs = [{<<"jid">>,
jid:to_string(Peer)}]}]}];
- _ ->
+ false ->
[]
end,
Pkt1 = Pkt#xmlel{children = Items ++ Els},
- Pkt2 = jlib:replace_from(jid:replace_resource(JidRequestor, Nick), Pkt1),
+ Pkt2 = jlib:replace_from(jid:replace_resource(JidArchive, Nick), Pkt1),
jlib:remove_attr(<<"to">>, Pkt2);
-maybe_update_from_to(Pkt, _JidRequestor, _Peer, chat, _Nick) ->
+maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) ->
Pkt.
is_bare_copy(#jid{luser = U, lserver = S, lresource = R}, To) ->
@@ -1095,8 +1119,8 @@ is_bare_copy(#jid{luser = U, lserver = S, lresource = R}, To) ->
end.
send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
- QID = xml:get_tag_attr_s(<<"queryid">>, SubEl),
- NS = xml:get_tag_attr_s(<<"xmlns">>, SubEl),
+ QID = fxml:get_tag_attr_s(<<"queryid">>, SubEl),
+ NS = fxml:get_tag_attr_s(<<"xmlns">>, SubEl),
QIDAttr = if QID /= <<>> ->
[{<<"queryid">>, QID}];
true ->
@@ -1336,7 +1360,7 @@ datetime_to_now(DateTime, USecs) ->
get_jids(Els) ->
lists:flatmap(
fun(#xmlel{name = <<"jid">>} = El) ->
- J = jid:from_string(xml:get_tag_cdata(El)),
+ J = jid:from_string(fxml:get_tag_cdata(El)),
[jid:tolower(jid:remove_resource(J)),
jid:tolower(J)];
(_) ->
diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl
index 3e64da95..c175fcb8 100644
--- a/src/mod_metrics.erl
+++ b/src/mod_metrics.erl
@@ -39,7 +39,7 @@
s2s_send_packet, s2s_receive_packet,
remove_user, register_user]).
--export([start/2, stop/1, send_metrics/4]).
+-export([start/2, stop/1, send_metrics/4, opt_type/1]).
-export([offline_message_hook/3,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
@@ -123,3 +123,6 @@ send_metrics(Host, Probe, Peer, Port) ->
Error ->
?WARNING_MSG("can not open udp socket to grapherl: ~p", [Error])
end.
+
+opt_type(_) ->
+ [].
diff --git a/src/mod_mix.erl b/src/mod_mix.erl
new file mode 100644
index 00000000..d8cf94ac
--- /dev/null
+++ b/src/mod_mix.erl
@@ -0,0 +1,347 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 2 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(mod_mix).
+
+-behaviour(gen_server).
+-behaviour(gen_mod).
+
+%% API
+-export([start_link/2, start/2, stop/1, process_iq/3,
+ disco_items/5, disco_identity/5, disco_info/5,
+ disco_features/5, mod_opt_type/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-include("logger.hrl").
+-include("jlib.hrl").
+-include("pubsub.hrl").
+
+-define(PROCNAME, ejabberd_mod_mix).
+-define(NODES, [?NS_MIX_NODES_MESSAGES,
+ ?NS_MIX_NODES_PRESENCE,
+ ?NS_MIX_NODES_PARTICIPANTS,
+ ?NS_MIX_NODES_SUBJECT,
+ ?NS_MIX_NODES_CONFIG]).
+
+-record(state, {server_host :: binary(),
+ host :: binary()}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+start_link(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
+
+start(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
+ temporary, 5000, worker, [?MODULE]},
+ supervisor:start_child(ejabberd_sup, ChildSpec).
+
+stop(Host) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ supervisor:terminate_child(ejabberd_sup, Proc),
+ supervisor:delete_child(ejabberd_sup, Proc),
+ ok.
+
+disco_features(_Acc, _From, _To, _Node, _Lang) ->
+ {result, [?NS_MIX_0]}.
+
+disco_items(_Acc, _From, To, _Node, _Lang) when To#jid.luser /= <<"">> ->
+ To_s = jid:to_string(jid:remove_resource(To)),
+ {result, [#xmlel{name = <<"item">>,
+ attrs = [{<<"jid">>, To_s},
+ {<<"node">>, Node}]} || Node <- ?NODES]};
+disco_items(_Acc, _From, _To, _Node, _Lang) ->
+ {result, []}.
+
+disco_identity(Acc, _From, To, _Node, _Lang) when To#jid.luser == <<"">> ->
+ Acc ++ [#xmlel{name = <<"identity">>,
+ attrs =
+ [{<<"category">>, <<"conference">>},
+ {<<"name">>, <<"MIX service">>},
+ {<<"type">>, <<"text">>}]}];
+disco_identity(Acc, _From, _To, _Node, _Lang) ->
+ Acc ++ [#xmlel{name = <<"identity">>,
+ attrs =
+ [{<<"category">>, <<"conference">>},
+ {<<"type">>, <<"mix">>}]}].
+
+disco_info(_Acc, _From, To, _Node, _Lang) when is_atom(To) ->
+ [#xmlel{name = <<"x">>,
+ attrs = [{<<"xmlns">>, ?NS_XDATA},
+ {<<"type">>, <<"result">>}],
+ children = [#xmlel{name = <<"field">>,
+ attrs = [{<<"var">>, <<"FORM_TYPE">>},
+ {<<"type">>, <<"hidden">>}],
+ children = [#xmlel{name = <<"value">>,
+ children = [{xmlcdata,
+ ?NS_MIX_SERVICEINFO_0}]}]}]}];
+disco_info(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+process_iq(From, To,
+ #iq{type = set, sub_el = #xmlel{name = <<"join">>} = SubEl} = IQ) ->
+ Nodes = lists:flatmap(
+ fun(#xmlel{name = <<"subscribe">>, attrs = Attrs}) ->
+ Node = fxml:get_attr_s(<<"node">>, Attrs),
+ case lists:member(Node, ?NODES) of
+ true -> [Node];
+ false -> []
+ end;
+ (_) ->
+ []
+ end, SubEl#xmlel.children),
+ case subscribe_nodes(From, To, Nodes) of
+ {result, _} ->
+ case publish_participant(From, To) of
+ {result, _} ->
+ LFrom_s = jid:to_string(jid:tolower(jid:remove_resource(From))),
+ Subscribe = [#xmlel{name = <<"subscribe">>,
+ attrs = [{<<"node">>, Node}]} || Node <- Nodes],
+ IQ#iq{type = result,
+ sub_el = [#xmlel{name = <<"join">>,
+ attrs = [{<<"jid">>, LFrom_s},
+ {<<"xmlns">>, ?NS_MIX_0}],
+ children = Subscribe}]};
+ {error, Err} ->
+ IQ#iq{type = error, sub_el = [SubEl, Err]}
+ end;
+ {error, Err} ->
+ IQ#iq{type = error, sub_el = [SubEl, Err]}
+ end;
+process_iq(From, To,
+ #iq{type = set, sub_el = #xmlel{name = <<"leave">>} = SubEl} = IQ) ->
+ case delete_participant(From, To) of
+ {result, _} ->
+ case unsubscribe_nodes(From, To, ?NODES) of
+ {result, _} ->
+ IQ#iq{type = result, sub_el = []};
+ {error, Err} ->
+ IQ#iq{type = error, sub_el = [SubEl, Err]}
+ end;
+ {error, Err} ->
+ IQ#iq{type = error, sub_el = [SubEl, Err]}
+ end;
+process_iq(_From, _To, #iq{sub_el = SubEl} = IQ) ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}.
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+init([ServerHost, Opts]) ->
+ Host = gen_mod:get_opt_host(ServerHost, Opts, <<"mix.@HOST@">>),
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
+ one_queue),
+ ConfigTab = gen_mod:get_module_proc(Host, config),
+ ets:new(ConfigTab, [named_table]),
+ ets:insert(ConfigTab, {plugins, [<<"mix">>]}),
+ ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host,
+ ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host,
+ ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_MIX_0, ?MODULE, process_iq, IQDisc),
+ ejabberd_router:register_route(Host, ServerHost),
+ {ok, #state{server_host = ServerHost, host = Host}}.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info({route, From, To, Packet}, State) ->
+ case catch do_route(State, From, To, Packet) of
+ {'EXIT', _} = Err ->
+ try
+ ?ERROR_MSG("failed to route packet ~p from '~s' to '~s': ~p",
+ [Packet, jid:to_string(From), jid:to_string(To), Err]),
+ ErrPkt = jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR),
+ ejabberd_router:route_error(To, From, ErrPkt, Packet)
+ catch _:_ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ {noreply, State};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, #state{host = Host}) ->
+ ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0),
+ ejabberd_router:unregister_route(Host),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+do_route(_State, From, To, #xmlel{name = <<"iq">>} = Packet) ->
+ if To#jid.luser == <<"">> ->
+ ejabberd_local:process_iq(From, To, Packet);
+ true ->
+ ejabberd_sm:process_iq(From, To, Packet)
+ end;
+do_route(_State, From, To, #xmlel{name = <<"presence">>} = Packet)
+ when To#jid.luser /= <<"">> ->
+ case fxml:get_tag_attr_s(<<"type">>, Packet) of
+ <<"unavailable">> ->
+ delete_presence(From, To);
+ _ ->
+ ok
+ end;
+do_route(_State, _From, _To, _Packet) ->
+ ok.
+
+subscribe_nodes(From, To, Nodes) ->
+ LTo = jid:tolower(jid:remove_resource(To)),
+ LFrom = jid:tolower(jid:remove_resource(From)),
+ From_s = jid:to_string(LFrom),
+ lists:foldl(
+ fun(_Node, {error, _} = Err) ->
+ Err;
+ (Node, {result, _}) ->
+ case mod_pubsub:subscribe_node(LTo, Node, From, From_s, []) of
+ {error, _} = Err ->
+ case is_item_not_found(Err) of
+ true ->
+ case mod_pubsub:create_node(
+ LTo, To#jid.lserver, Node, LFrom, <<"mix">>) of
+ {result, _} ->
+ mod_pubsub:subscribe_node(LTo, Node, From, From_s, []);
+ Error ->
+ Error
+ end;
+ false ->
+ Err
+ end;
+ {result, _} = Result ->
+ Result
+ end
+ end, {result, []}, Nodes).
+
+unsubscribe_nodes(From, To, Nodes) ->
+ LTo = jid:tolower(jid:remove_resource(To)),
+ LFrom = jid:tolower(jid:remove_resource(From)),
+ From_s = jid:to_string(LFrom),
+ lists:foldl(
+ fun(_Node, {error, _} = Err) ->
+ Err;
+ (Node, {result, _} = Result) ->
+ case mod_pubsub:unsubscribe_node(LTo, Node, From, From_s, <<"">>) of
+ {error, _} = Err ->
+ case is_not_subscribed(Err) of
+ true -> Result;
+ _ -> Err
+ end;
+ {result, _} = Res ->
+ Res
+ end
+ end, {result, []}, Nodes).
+
+publish_participant(From, To) ->
+ LFrom = jid:tolower(jid:remove_resource(From)),
+ LTo = jid:tolower(jid:remove_resource(To)),
+ Participant = #xmlel{name = <<"participant">>,
+ attrs = [{<<"xmlns">>, ?NS_MIX_0},
+ {<<"jid">>, jid:to_string(LFrom)}]},
+ ItemID = p1_sha:sha(jid:to_string(LFrom)),
+ mod_pubsub:publish_item(
+ LTo, To#jid.lserver, ?NS_MIX_NODES_PARTICIPANTS,
+ From, ItemID, [Participant]).
+
+delete_presence(From, To) ->
+ LFrom = jid:tolower(From),
+ LTo = jid:tolower(jid:remove_resource(To)),
+ case mod_pubsub:get_items(LTo, ?NS_MIX_NODES_PRESENCE) of
+ Items when is_list(Items) ->
+ lists:foreach(
+ fun(#pubsub_item{modification = {_, LJID},
+ itemid = {ItemID, _}}) when LJID == LFrom ->
+ delete_item(From, To, ?NS_MIX_NODES_PRESENCE, ItemID);
+ (_) ->
+ ok
+ end, Items);
+ _ ->
+ ok
+ end.
+
+delete_participant(From, To) ->
+ LFrom = jid:tolower(jid:remove_resource(From)),
+ ItemID = p1_sha:sha(jid:to_string(LFrom)),
+ delete_presence(From, To),
+ delete_item(From, To, ?NS_MIX_NODES_PARTICIPANTS, ItemID).
+
+delete_item(From, To, Node, ItemID) ->
+ LTo = jid:tolower(jid:remove_resource(To)),
+ case mod_pubsub:delete_item(
+ LTo, Node, From, ItemID, true) of
+ {result, _} = Res ->
+ Res;
+ {error, _} = Err ->
+ case is_item_not_found(Err) of
+ true -> {result, []};
+ false -> Err
+ end
+ end.
+
+is_item_not_found({error, ErrEl}) ->
+ case fxml:get_subtag_with_xmlns(
+ ErrEl, <<"item-not-found">>, ?NS_STANZAS) of
+ #xmlel{} -> true;
+ _ -> false
+ end.
+
+is_not_subscribed({error, ErrEl}) ->
+ case fxml:get_subtag_with_xmlns(
+ ErrEl, <<"not-subscribed">>, ?NS_PUBSUB_ERRORS) of
+ #xmlel{} -> true;
+ _ -> false
+ end.
+
+mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
+mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(_) -> [host, iqdisc].
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index a64a0032..0d37a236 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -222,7 +222,7 @@ remove_room_mam(LServer, Host, Name) ->
process_iq_disco_items(Host, From, To,
#iq{lang = Lang} = IQ) ->
Rsm = jlib:rsm_decode(IQ),
- DiscoNode = xml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el),
+ DiscoNode = fxml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el),
Res = IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
@@ -373,7 +373,7 @@ init([Host, Opts]) ->
RoomShaper = gen_mod:get_opt(room_shaper, Opts,
fun(A) when is_atom(A) -> A end,
none),
- ejabberd_router:register_route(MyHost),
+ ejabberd_router:register_route(MyHost, Host),
load_permanent_rooms(MyHost, Host,
{Access, AccessCreate, AccessAdmin, AccessPersistent},
HistorySize, RoomShaper),
@@ -451,7 +451,7 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
From, To, Packet, DefRoomOpts);
_ ->
#xmlel{attrs = Attrs} = Packet,
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
ErrText = <<"Access denied by service policy">>,
Err = jlib:make_error_reply(Packet,
?ERRT_FORBIDDEN(Lang, ErrText)),
@@ -557,18 +557,18 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
_ -> ok
end;
<<"message">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
_ ->
case acl:match_rule(ServerHost, AccessAdmin, From)
of
allow ->
- Msg = xml:get_path_s(Packet,
+ Msg = fxml:get_path_s(Packet,
[{elem, <<"body">>},
cdata]),
broadcast_service_message(Host, Msg);
_ ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
ErrText =
<<"Only service administrators are allowed "
"to send service messages">>,
@@ -581,7 +581,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
<<"presence">> -> ok
end;
_ ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
@@ -593,11 +593,12 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
_ ->
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
[] ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
case {Name, Type} of
{<<"presence">>, <<"">>} ->
case check_user_can_create_room(ServerHost,
- AccessCreate, From, Room) of
+ AccessCreate, From, Room) and
+ check_create_roomid(ServerHost, Room) of
true ->
{ok, Pid} = start_new_room(Host, ServerHost, Access,
Room, HistorySize,
@@ -606,14 +607,14 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
mod_muc_room:route(Pid, From, Nick, Packet),
ok;
false ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
ErrText = <<"Room creation is denied by service policy">>,
Err = jlib:make_error_reply(
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
ejabberd_router:route(To, From, Err)
end;
_ ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
ErrText = <<"Conference room does not exist">>,
Err = jlib:make_error_reply(Packet,
?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
@@ -628,17 +629,22 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
end.
check_user_can_create_room(ServerHost, AccessCreate,
- From, RoomID) ->
+ From, _RoomID) ->
case acl:match_rule(ServerHost, AccessCreate, From) of
- allow ->
- byte_size(RoomID) =<
- gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id,
- fun(infinity) -> infinity;
- (I) when is_integer(I), I>0 -> I
- end, infinity);
+ allow -> true;
_ -> false
end.
+check_create_roomid(ServerHost, RoomID) ->
+ Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id,
+ fun(infinity) -> infinity;
+ (I) when is_integer(I), I>0 -> I
+ end, infinity),
+ Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id,
+ fun iolist_to_binary/1, ""),
+ (byte_size(RoomID) =< Max) and
+ (re:run(RoomID, Regexp, [unicode, {capture, none}]) == match).
+
get_rooms(ServerHost, Host) ->
LServer = jid:nameprep(ServerHost),
get_rooms(LServer, Host,
@@ -1056,12 +1062,12 @@ iq_set_register_info(ServerHost, Host, From, Nick,
process_iq_register_set(ServerHost, Host, From, SubEl,
Lang) ->
#xmlel{children = Els} = SubEl,
- case xml:get_subtag(SubEl, <<"remove">>) of
+ case fxml:get_subtag(SubEl, <<"remove">>) of
false ->
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = <<"x">>} = XEl] ->
- case {xml:get_tag_attr_s(<<"xmlns">>, XEl),
- xml:get_tag_attr_s(<<"type">>, XEl)}
+ case {fxml:get_tag_attr_s(<<"xmlns">>, XEl),
+ fxml:get_tag_attr_s(<<"type">>, XEl)}
of
{?NS_XDATA, <<"cancel">>} -> {result, []};
{?NS_XDATA, <<"submit">>} ->
@@ -1317,6 +1323,8 @@ mod_opt_type(max_room_id) ->
fun (infinity) -> infinity;
(I) when is_integer(I), I > 0 -> I
end;
+mod_opt_type(regexp_room_id) ->
+ fun iolist_to_binary/1;
mod_opt_type(max_room_name) ->
fun (infinity) -> infinity;
(I) when is_integer(I), I > 0 -> I
@@ -1342,7 +1350,7 @@ mod_opt_type(user_presence_shaper) ->
mod_opt_type(_) ->
[access, access_admin, access_create, access_persistent,
db_type, default_room_options, history_size, host,
- max_room_desc, max_room_id, max_room_name,
+ max_room_desc, max_room_id, max_room_name, regexp_room_id,
max_user_conferences, max_users,
max_users_admin_threshold, max_users_presence,
min_message_interval, min_presence_interval,
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index c0875737..7c6e84c4 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -178,7 +178,8 @@ muc_online_rooms(ServerHost) ->
MUCHost = find_host(ServerHost),
Rooms = ets:tab2list(muc_online_room),
lists:foldl(
- fun({_, {Roomname, Host}, _}, Results) ->
+ fun(Room, Results) ->
+ {Roomname, Host} = Room#muc_online_room.name_host,
case MUCHost of
global ->
[<<Roomname/binary, "@", Host/binary>> | Results];
@@ -279,7 +280,7 @@ get_sort_query(Q) ->
get_sort_query2(Q) ->
{value, {_, String}} = lists:keysearch(<<"sort">>, 1, Q),
- Integer = list_to_integer(binary_to_list(String)),
+ Integer = jlib:binary_to_integer(String),
case Integer >= 0 of
true -> {ok, {normal, Integer}};
false -> {ok, {reverse, abs(Integer)}}
@@ -395,7 +396,9 @@ prepare_room_info(Room_info) ->
%% @spec (Name::binary(), Host::binary(), ServerHost::binary()) ->
%% ok | error
%% @doc Create a room immediately with the default options.
-create_room(Name, Host, ServerHost) ->
+create_room(Name1, Host1, ServerHost) ->
+ Name = jid:nodeprep(Name1),
+ Host = jid:nodeprep(Host1),
%% Get the default room options from the muc configuration
DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc,
@@ -471,7 +474,7 @@ destroy_room({N, H, SH}) ->
%% The file encoding must be UTF-8
destroy_rooms_file(Filename) ->
- {ok, F} = file:open(Filename, [read]),
+ {ok, F} = file:open(Filename, [read, binary]),
RJID = read_room(F),
Rooms = read_rooms(F, RJID, []),
file:close(F),
@@ -499,23 +502,16 @@ read_room(F) ->
%% This function is quite rudimentary
%% and may not be accurate
split_roomjid(RoomJID) ->
- [Name, Host] = string:tokens(RoomJID, "@"),
- [_MUC_service_name | ServerHostList] = string:tokens(Host, "."),
- ServerHost = join(ServerHostList, "."),
- {list_to_binary(Name), list_to_binary(Host), list_to_binary(ServerHost)}.
-
-%% This function is copied from string:join/2 in Erlang/OTP R12B-1
-%% Note that string:join/2 is not implemented in Erlang/OTP R11B
-join([H|T], Sep) ->
- H ++ lists:concat([Sep ++ X || X <- T]).
-
+ [Name, Host] = binary:split(RoomJID, <<"@">>),
+ [_MUC_service_name, ServerHost] = binary:split(Host, <<".">>),
+ {Name, Host, ServerHost}.
%%----------------------------
%% Create Rooms in File
%%----------------------------
create_rooms_file(Filename) ->
- {ok, F} = file:open(Filename, [read]),
+ {ok, F} = file:open(Filename, [read, binary]),
RJID = read_room(F),
Rooms = read_rooms(F, RJID, []),
file:close(F),
@@ -692,29 +688,32 @@ send_direct_invitation(RoomName, RoomService, Password, Reason, UsersString) ->
RoomJid = jid:make(RoomName, RoomService, <<"">>),
RoomString = jid:to_string(RoomJid),
XmlEl = build_invitation(Password, Reason, RoomString),
- UsersStrings = get_users_to_invite(RoomJid, binary_to_list(UsersString)),
- [send_direct_invitation(RoomJid, jid:from_string(list_to_binary(UserStrings)), XmlEl)
+ UsersStrings = get_users_to_invite(RoomJid, UsersString),
+ [send_direct_invitation(RoomJid, UserStrings, XmlEl)
|| UserStrings <- UsersStrings],
timer:sleep(1000),
ok.
get_users_to_invite(RoomJid, UsersString) ->
- UsersStrings = string:tokens(UsersString, ":"),
+ UsersStrings = binary:split(UsersString, <<":">>, [global]),
OccupantsTuples = get_room_occupants(RoomJid#jid.luser,
RoomJid#jid.lserver),
OccupantsJids = [jid:from_string(JidString)
|| {JidString, _Nick, _} <- OccupantsTuples],
- lists:filter(
- fun(UserString) ->
- UserJid = jid:from_string(list_to_binary(UserString)),
- %% [{"badlop@localhost/work","badlop","moderator"}]
- lists:all(fun(OccupantJid) ->
- UserJid#jid.luser /= OccupantJid#jid.luser
- orelse UserJid#jid.lserver /= OccupantJid#jid.lserver
- end,
- OccupantsJids)
- end,
- UsersStrings).
+ lists:filtermap(
+ fun(UserString) ->
+ UserJid = jid:from_string(UserString),
+ Val = lists:all(fun(OccupantJid) ->
+ UserJid#jid.luser /= OccupantJid#jid.luser
+ orelse UserJid#jid.lserver /= OccupantJid#jid.lserver
+ end,
+ OccupantsJids),
+ case Val of
+ true -> {true, UserJid};
+ _ -> false
+ end
+ end,
+ UsersStrings).
build_invitation(Password, Reason, RoomString) ->
PasswordAttrList = case Password of
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index 4d8e3965..1f32f6f9 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -193,15 +193,15 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
case has_no_permanent_store_hint(Packet) of
false ->
- case {xml:get_subtag(Packet, <<"subject">>),
- xml:get_subtag(Packet, <<"body">>)}
+ case {fxml:get_subtag(Packet, <<"subject">>),
+ fxml:get_subtag(Packet, <<"body">>)}
of
{false, false} -> ok;
{false, SubEl} ->
- Message = {body, xml:get_tag_cdata(SubEl)},
+ Message = {body, fxml:get_tag_cdata(SubEl)},
add_message_to_log(Nick, Message, Room, Opts, State);
{SubEl, _} ->
- Message = {subject, xml:get_tag_cdata(SubEl)},
+ Message = {subject, fxml:get_tag_cdata(SubEl)},
add_message_to_log(Nick, Message, Room, Opts, State)
end;
true -> ok
@@ -1201,13 +1201,13 @@ fjoin(FileList) ->
list_to_binary(filename:join([binary_to_list(File) || File <- FileList])).
has_no_permanent_store_hint(Packet) ->
- xml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS)
=/= false orelse
- xml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS)
=/= false orelse
- xml:get_subtag_with_xmlns(Packet, <<"no-permanent-store">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-store">>, ?NS_HINTS)
=/= false orelse
- xml:get_subtag_with_xmlns(Packet, <<"no-permanent-storage">>, ?NS_HINTS)
+ fxml:get_subtag_with_xmlns(Packet, <<"no-permanent-storage">>, ?NS_HINTS)
=/= false.
mod_opt_type(access_log) ->
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index d19bc5f3..06fdf325 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -143,12 +143,12 @@ normal_state({route, From, <<"">>,
children = Els} =
Packet},
StateData) ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
case is_user_online(From, StateData) orelse
is_user_allowed_message_nonparticipant(From, StateData)
of
true ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"groupchat">> ->
Activity = get_user_activity(From, StateData),
Now = p1_time_compat:system_time(micro_seconds),
@@ -394,7 +394,7 @@ normal_state({route, From, <<"">>,
{next_state, normal_state, StateData}
end;
_ ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> -> ok;
_ ->
handle_roommessage_from_nonparticipant(Packet, Lang,
@@ -432,7 +432,7 @@ normal_state({route, From, <<"">>,
?NS_MUC_OWNER ->
process_iq_owner(From, Type, Lang, SubEl, StateData);
?NS_DISCO_INFO ->
- case xml:get_attr(<<"node">>, Attrs) of
+ case fxml:get_attr(<<"node">>, Attrs) of
false -> process_iq_disco_info(From, Type, Lang, StateData);
{value, _} -> {error, ?ERR_SERVICE_UNAVAILABLE}
end;
@@ -509,8 +509,8 @@ normal_state({route, From, Nick,
normal_state({route, From, ToNick,
#xmlel{name = <<"message">>, attrs = Attrs} = Packet},
StateData) ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
case decide_fate_message(Type, Packet, From, StateData)
of
{expulse_sender, Reason} ->
@@ -568,7 +568,7 @@ normal_state({route, From, ToNick,
FromNick),
X = #xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_MUC_USER}]},
- PrivMsg = xml:append_subtags(Packet, [X]),
+ PrivMsg = fxml:append_subtags(Packet, [X]),
[ejabberd_router:route(FromNickJID, ToJID, PrivMsg)
|| ToJID <- ToJIDs];
true ->
@@ -607,8 +607,8 @@ normal_state({route, From, ToNick,
normal_state({route, From, ToNick,
#xmlel{name = <<"iq">>, attrs = Attrs} = Packet},
StateData) ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
- StanzaId = xml:get_attr_s(<<"id">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
+ StanzaId = fxml:get_attr_s(<<"id">>, Attrs),
case {(StateData#state.config)#config.allow_query_users,
is_user_online_iq(StanzaId, From, StateData)}
of
@@ -884,7 +884,7 @@ route(Pid, From, ToNick, Packet) ->
process_groupchat_message(From,
#xmlel{name = <<"message">>, attrs = Attrs} = Packet,
StateData) ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
case is_user_online(From, StateData) orelse
is_user_allowed_message_nonparticipant(From, StateData)
of
@@ -936,7 +936,7 @@ process_groupchat_message(From,
drop ->
{next_state, normal_state, StateData};
NewPacket1 ->
- NewPacket = xml:remove_subtags(NewPacket1, <<"nick">>, {<<"xmlns">>, ?NS_NICK}),
+ NewPacket = fxml:remove_subtags(NewPacket1, <<"nick">>, {<<"xmlns">>, ?NS_NICK}),
send_multiple(jid:replace_resource(StateData#state.jid,
FromNick),
StateData#state.server_host,
@@ -1016,7 +1016,7 @@ get_participant_data(From, StateData) ->
process_presence(From, Nick,
#xmlel{name = <<"presence">>, attrs = Attrs0} = Packet0,
StateData) ->
- Type0 = xml:get_attr_s(<<"type">>, Attrs0),
+ Type0 = fxml:get_attr_s(<<"type">>, Attrs0),
IsOnline = is_user_online(From, StateData),
if Type0 == <<"">>;
IsOnline and ((Type0 == <<"unavailable">>) or (Type0 == <<"error">>)) ->
@@ -1029,8 +1029,8 @@ process_presence(From, Nick,
drop ->
{next_state, normal_state, StateData};
#xmlel{attrs = Attrs} = Packet ->
- Type = xml:get_attr_s(<<"type">>, Attrs),
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Type = fxml:get_attr_s(<<"type">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
StateData1 = case Type of
<<"unavailable">> ->
NewPacket = case
@@ -1047,12 +1047,12 @@ process_presence(From, Nick,
{ok, [_, _ | _]} -> ok;
_ -> send_new_presence(From, NewState, StateData)
end,
- Reason = case xml:get_subtag(NewPacket,
+ Reason = case fxml:get_subtag(NewPacket,
<<"status">>)
of
false -> <<"">>;
Status_el ->
- xml:get_tag_cdata(Status_el)
+ fxml:get_tag_cdata(Status_el)
end,
remove_online_user(From, NewState, Reason);
<<"error">> ->
@@ -1087,7 +1087,7 @@ process_presence(From, Nick,
From, Err),
StateData;
{true, _, _} ->
- Lang = xml:get_attr_s(<<"xml:lang">>,
+ Lang = fxml:get_attr_s(<<"xml:lang">>,
Attrs),
ErrText =
<<"That nickname is already in use by another "
@@ -1304,7 +1304,7 @@ get_error_condition(Packet) ->
end.
get_error_condition2(Packet) ->
- #xmlel{children = EEls} = xml:get_subtag(Packet,
+ #xmlel{children = EEls} = fxml:get_subtag(Packet,
<<"error">>),
[Condition] = [Name
|| #xmlel{name = Name,
@@ -1655,7 +1655,7 @@ filter_presence(#xmlel{name = <<"presence">>,
case El of
{xmlcdata, _} -> false;
#xmlel{attrs = Attrs1} ->
- XMLNS = xml:get_attr_s(<<"xmlns">>,
+ XMLNS = fxml:get_attr_s(<<"xmlns">>,
Attrs1),
NS_MUC = ?NS_MUC,
Size = byte_size(NS_MUC),
@@ -1743,11 +1743,11 @@ higher_presence(Pres1, Pres2) ->
Pri1 > Pri2.
get_priority_from_presence(PresencePacket) ->
- case xml:get_subtag(PresencePacket, <<"priority">>) of
+ case fxml:get_subtag(PresencePacket, <<"priority">>) of
false -> 0;
SubEl ->
case catch
- jlib:binary_to_integer(xml:get_tag_cdata(SubEl))
+ jlib:binary_to_integer(fxml:get_tag_cdata(SubEl))
of
P when is_integer(P) -> P;
_ -> 0
@@ -1781,7 +1781,7 @@ nick_collision(User, Nick, StateData) ->
add_new_user(From, Nick,
#xmlel{attrs = Attrs, children = Els} = Packet,
StateData) ->
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
MaxUsers = get_max_users(StateData),
MaxAdminUsers = MaxUsers +
get_max_users_admin_threshold(StateData),
@@ -1856,7 +1856,7 @@ add_new_user(From, Nick,
add_online_user(From, Nick, Role,
StateData)),
send_existing_presences(From, NewState),
- send_new_presence(From, NewState, StateData),
+ send_initial_presence(From, NewState, StateData),
Shift = count_stanza_shift(Nick, Els, NewState),
case send_history(From, Shift, NewState) of
true -> ok;
@@ -1879,7 +1879,7 @@ add_new_user(From, Nick,
From, Err),
StateData;
captcha_required ->
- SID = xml:get_attr_s(<<"id">>, Attrs),
+ SID = fxml:get_attr_s(<<"id">>, Attrs),
RoomJID = StateData#state.jid,
To = jid:replace_resource(RoomJID, Nick),
Limiter = {From#jid.luser, From#jid.lserver},
@@ -1979,11 +1979,11 @@ check_captcha(Affiliation, From, StateData) ->
extract_password([]) -> false;
extract_password([#xmlel{attrs = Attrs} = El | Els]) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MUC ->
- case xml:get_subtag(El, <<"password">>) of
+ case fxml:get_subtag(El, <<"password">>) of
false -> false;
- SubEl -> xml:get_tag_cdata(SubEl)
+ SubEl -> fxml:get_tag_cdata(SubEl)
end;
_ -> extract_password(Els)
end;
@@ -2057,9 +2057,9 @@ calc_shift(MaxSize, Size, Shift, [S | TSizes]) ->
extract_history([], _Type) -> false;
extract_history([#xmlel{attrs = Attrs} = El | Els],
Type) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MUC ->
- AttrVal = xml:get_path_s(El,
+ AttrVal = fxml:get_path_s(El,
[{elem, <<"history">>}, {attr, Type}]),
case Type of
<<"since">> ->
@@ -2090,6 +2090,9 @@ presence_broadcast_allowed(JID, StateData) ->
Role = get_role(JID, StateData),
lists:member(Role, (StateData#state.config)#config.presence_broadcast).
+send_initial_presence(NJID, StateData, OldStateData) ->
+ send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
+
send_update_presence(JID, StateData, OldStateData) ->
send_update_presence(JID, <<"">>, StateData, OldStateData).
@@ -2117,20 +2120,25 @@ send_update_presence1(JID, Reason, StateData, OldStateData) ->
end
end,
lists:foreach(fun (J) ->
- send_new_presence(J, Reason, StateData, OldStateData)
+ send_new_presence1(J, Reason, false, StateData,
+ OldStateData)
end,
LJIDs).
send_new_presence(NJID, StateData, OldStateData) ->
- send_new_presence(NJID, <<"">>, StateData, OldStateData).
+ send_new_presence(NJID, <<"">>, false, StateData, OldStateData).
send_new_presence(NJID, Reason, StateData, OldStateData) ->
+ send_new_presence(NJID, Reason, false, StateData, OldStateData).
+
+send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
case is_room_overcrowded(StateData) of
true -> ok;
- false -> send_new_presence1(NJID, Reason, StateData, OldStateData)
+ false -> send_new_presence1(NJID, Reason, IsInitialPresence, StateData,
+ OldStateData)
end.
-send_new_presence1(NJID, Reason, StateData, OldStateData) ->
+send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
LNJID = jid:tolower(NJID),
#user{nick = Nick} = (?DICT):fetch(LNJID, StateData#state.users),
LJID = find_jid_by_nick(Nick, StateData),
@@ -2187,46 +2195,9 @@ send_new_presence1(NJID, Reason, StateData, OldStateData) ->
children =
[{xmlcdata, Reason}]}]
end,
- Status = case StateData#state.just_created of
- true ->
- [#xmlel{name = <<"status">>,
- attrs =
- [{<<"code">>, <<"201">>}],
- children = []}];
- false -> []
- end,
- Status2 = case
- (StateData#state.config)#config.anonymous
- == false
- andalso NJID == Info#user.jid
- of
- true ->
- [#xmlel{name = <<"status">>,
- attrs =
- [{<<"code">>, <<"100">>}],
- children = []}
- | Status];
- false -> Status
- end,
- Status3 = case NJID == Info#user.jid of
- true ->
- [#xmlel{name = <<"status">>,
- attrs =
- [{<<"code">>, <<"110">>}],
- children = []}
- | Status2];
- false -> Status2
- end,
- Status4 = case (StateData#state.config)#config.logging of
- true ->
- [#xmlel{name = <<"status">>,
- attrs =
- [{<<"code">>, <<"170">>}],
- children = []}
- | Status3];
- false -> Status3
- end,
- Packet = xml:append_subtags(Presence,
+ StatusEls = status_els(IsInitialPresence, NJID, Info,
+ StateData),
+ Packet = fxml:append_subtags(Presence,
[#xmlel{name = <<"x">>,
attrs =
[{<<"xmlns">>,
@@ -2240,7 +2211,7 @@ send_new_presence1(NJID, Reason, StateData, OldStateData) ->
children
=
ItemEls}
- | Status4]}]),
+ | StatusEls]}]),
ejabberd_router:route(jid:replace_resource(StateData#state.jid,
Nick),
Info#user.jid, Packet)
@@ -2289,7 +2260,7 @@ send_existing_presences1(ToJID, StateData) ->
{<<"role">>,
role_to_list(FromRole)}]
end,
- Packet = xml:append_subtags(Presence,
+ Packet = fxml:append_subtags(Presence,
[#xmlel{name =
<<"x">>,
attrs =
@@ -2420,7 +2391,7 @@ send_nick_changing(JID, OldNick, StateData,
<<"303">>}],
children =
[]}|Status110]}]},
- Packet2 = xml:append_subtags(Presence,
+ Packet2 = fxml:append_subtags(Presence,
[#xmlel{name = <<"x">>,
attrs =
[{<<"xmlns">>,
@@ -2450,6 +2421,40 @@ send_nick_changing(JID, OldNick, StateData,
end,
(?DICT):to_list(StateData#state.users)).
+status_els(IsInitialPresence, JID, #user{jid = JID}, StateData) ->
+ Status = case IsInitialPresence of
+ true ->
+ S1 = case StateData#state.just_created of
+ true ->
+ [#xmlel{name = <<"status">>,
+ attrs = [{<<"code">>, <<"201">>}],
+ children = []}];
+ false -> []
+ end,
+ S2 = case (StateData#state.config)#config.anonymous of
+ true -> S1;
+ false ->
+ [#xmlel{name = <<"status">>,
+ attrs = [{<<"code">>, <<"100">>}],
+ children = []} | S1]
+ end,
+ S3 = case (StateData#state.config)#config.logging of
+ true ->
+ [#xmlel{name = <<"status">>,
+ attrs = [{<<"code">>, <<"170">>}],
+ children = []} | S2];
+ false -> S2
+ end,
+ S3;
+ false -> []
+ end,
+ [#xmlel{name = <<"status">>,
+ attrs =
+ [{<<"code">>,
+ <<"110">>}],
+ children = []} | Status];
+status_els(_IsInitialPresence, _JID, _Info, _StateData) -> [].
+
lqueue_new(Max) ->
#lqueue{queue = queue:new(), len = 0, max = Max}.
@@ -2474,7 +2479,7 @@ lqueue_to_list(#lqueue{queue = Q1}) ->
add_message_to_history(FromNick, FromJID, Packet, StateData) ->
- HaveSubject = case xml:get_subtag(Packet, <<"subject">>)
+ HaveSubject = case fxml:get_subtag(Packet, <<"subject">>)
of
false -> false;
_ -> true
@@ -2491,7 +2496,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
Addresses = #xmlel{name = <<"addresses">>,
attrs = [{<<"xmlns">>, ?NS_ADDRESS}],
children = [Address]},
- xml:append_subtags(Packet, [Addresses])
+ fxml:append_subtags(Packet, [Addresses])
end,
TSPacket = jlib:add_delay_info(AddrPacket, StateData#state.jid, TimeStamp),
SPacket =
@@ -2530,9 +2535,9 @@ send_subject(JID, #state{subject_author = Nick} = StateData) ->
Packet).
check_subject(Packet) ->
- case xml:get_subtag(Packet, <<"subject">>) of
+ case fxml:get_subtag(Packet, <<"subject">>) of
false -> false;
- SubjEl -> xml:get_tag_cdata(SubjEl)
+ SubjEl -> fxml:get_tag_cdata(SubjEl)
end.
can_change_subject(Role, StateData) ->
@@ -2549,14 +2554,14 @@ process_iq_admin(From, set, Lang, SubEl, StateData) ->
#xmlel{children = Items} = SubEl,
process_admin_items_set(From, Items, Lang, StateData);
process_iq_admin(From, get, Lang, SubEl, StateData) ->
- case xml:get_subtag(SubEl, <<"item">>) of
+ case fxml:get_subtag(SubEl, <<"item">>) of
false -> {error, ?ERR_BAD_REQUEST};
Item ->
FAffiliation = get_affiliation(From, StateData),
FRole = get_role(From, StateData),
- case xml:get_tag_attr(<<"role">>, Item) of
+ case fxml:get_tag_attr(<<"role">>, Item) of
false ->
- case xml:get_tag_attr(<<"affiliation">>, Item) of
+ case fxml:get_tag_attr(<<"affiliation">>, Item) of
false -> {error, ?ERR_BAD_REQUEST};
{value, StrAffiliation} ->
case catch list_to_affiliation(StrAffiliation) of
@@ -2564,7 +2569,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
SAffiliation ->
if (FAffiliation == owner) or
(FAffiliation == admin) or
- ((FAffiliation == member) and (SAffiliation == member)) ->
+ ((FAffiliation == member) and not
+ (StateData#state.config)#config.anonymous) ->
Items = items_with_affiliation(SAffiliation,
StateData),
{result, Items, StateData};
@@ -2748,7 +2754,7 @@ find_changed_items(UJID, UAffiliation, URole,
[#xmlel{name = <<"item">>, attrs = Attrs} = Item
| Items],
Lang, StateData, Res) ->
- TJID = case xml:get_attr(<<"jid">>, Attrs) of
+ TJID = case fxml:get_attr(<<"jid">>, Attrs) of
{value, S} ->
case jid:from_string(S) of
error ->
@@ -2761,7 +2767,7 @@ find_changed_items(UJID, UAffiliation, URole,
J -> {value, [J]}
end;
_ ->
- case xml:get_attr(<<"nick">>, Attrs) of
+ case fxml:get_attr(<<"nick">>, Attrs) of
{value, N} ->
case find_jids_by_nick(N, StateData) of
false ->
@@ -2781,9 +2787,9 @@ find_changed_items(UJID, UAffiliation, URole,
{value, [JID | _] = JIDs} ->
TAffiliation = get_affiliation(JID, StateData),
TRole = get_role(JID, StateData),
- case xml:get_attr(<<"role">>, Attrs) of
+ case fxml:get_attr(<<"role">>, Attrs) of
false ->
- case xml:get_attr(<<"affiliation">>, Attrs) of
+ case fxml:get_attr(<<"affiliation">>, Attrs) of
false -> {error, ?ERR_BAD_REQUEST};
{value, StrAffiliation} ->
case catch list_to_affiliation(StrAffiliation) of
@@ -2824,7 +2830,7 @@ find_changed_items(UJID, UAffiliation, URole,
Items, Lang, StateData,
Res);
true ->
- Reason = xml:get_path_s(Item,
+ Reason = fxml:get_path_s(Item,
[{elem, <<"reason">>},
cdata]),
MoreRes = [{jid:remove_resource(Jidx),
@@ -2871,7 +2877,7 @@ find_changed_items(UJID, UAffiliation, URole,
find_changed_items(UJID, UAffiliation, URole, Items,
Lang, StateData, Res);
true ->
- Reason = xml:get_path_s(Item,
+ Reason = fxml:get_path_s(Item,
[{elem, <<"reason">>},
cdata]),
MoreRes = [{Jidx, role, SRole, Reason}
@@ -3123,10 +3129,10 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
case FAffiliation of
owner ->
#xmlel{children = Els} = SubEl,
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = <<"x">>} = XEl] ->
- case {xml:get_tag_attr_s(<<"xmlns">>, XEl),
- xml:get_tag_attr_s(<<"type">>, XEl)}
+ case {fxml:get_tag_attr_s(<<"xmlns">>, XEl),
+ fxml:get_tag_attr_s(<<"type">>, XEl)}
of
{?NS_XDATA, <<"cancel">>} -> {result, [], StateData};
{?NS_XDATA, <<"submit">>} ->
@@ -3160,10 +3166,10 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
case FAffiliation of
owner ->
#xmlel{children = Els} = SubEl,
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[] -> get_config(Lang, StateData, From);
[Item] ->
- case xml:get_tag_attr(<<"affiliation">>, Item) of
+ case fxml:get_tag_attr(<<"affiliation">>, Item) of
false -> {error, ?ERR_BAD_REQUEST};
{value, StrAffiliation} ->
case catch list_to_affiliation(StrAffiliation) of
@@ -4219,7 +4225,7 @@ process_iq_captcha(_From, set, _Lang, SubEl,
process_iq_vcard(_From, get, _Lang, _SubEl, StateData) ->
#state{config = #config{vcard = VCardRaw}} = StateData,
- case xml_stream:parse_element(VCardRaw) of
+ case fxml_stream:parse_element(VCardRaw) of
#xmlel{children = VCardEls} ->
{result, VCardEls, StateData};
{error, _} ->
@@ -4228,7 +4234,7 @@ process_iq_vcard(_From, get, _Lang, _SubEl, StateData) ->
process_iq_vcard(From, set, Lang, SubEl, StateData) ->
case get_affiliation(From, StateData) of
owner ->
- VCardRaw = xml:element_to_binary(SubEl),
+ VCardRaw = fxml:element_to_binary(SubEl),
Config = StateData#state.config,
NewConfig = Config#config{vcard = VCardRaw},
change_config(NewConfig, StateData);
@@ -4287,7 +4293,7 @@ is_voice_request(Els) ->
lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
El,
false) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_XDATA ->
case jlib:parse_xdata_submit(El) of
[_ | _] = Fields ->
@@ -4370,7 +4376,7 @@ is_voice_approvement(Els) ->
lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
El,
false) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_XDATA ->
case jlib:parse_xdata_submit(El) of
[_ | _] = Fs ->
@@ -4424,9 +4430,9 @@ is_invitation(Els) ->
lists:foldl(fun (#xmlel{name = <<"x">>, attrs = Attrs} =
El,
false) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_MUC_USER ->
- case xml:get_subtag(El, <<"invite">>) of
+ case fxml:get_subtag(El, <<"invite">>) of
false -> false;
_ -> true
end;
@@ -4442,20 +4448,20 @@ check_invitation(From, Els, Lang, StateData) ->
(StateData#state.config)#config.allow_user_invites
orelse
FAffiliation == admin orelse FAffiliation == owner,
- InviteEl = case xml:remove_cdata(Els) of
+ InviteEl = case fxml:remove_cdata(Els) of
[#xmlel{name = <<"x">>, children = Els1} = XEl] ->
- case xml:get_tag_attr_s(<<"xmlns">>, XEl) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, XEl) of
?NS_MUC_USER -> ok;
_ -> throw({error, ?ERR_BAD_REQUEST})
end,
- case xml:remove_cdata(Els1) of
+ case fxml:remove_cdata(Els1) of
[#xmlel{name = <<"invite">>} = InviteEl1] -> InviteEl1;
_ -> throw({error, ?ERR_BAD_REQUEST})
end;
_ -> throw({error, ?ERR_BAD_REQUEST})
end,
JID = case
- jid:from_string(xml:get_tag_attr_s(<<"to">>,
+ jid:from_string(fxml:get_tag_attr_s(<<"to">>,
InviteEl))
of
error -> throw({error, ?ERR_JID_MALFORMED});
@@ -4464,9 +4470,9 @@ check_invitation(From, Els, Lang, StateData) ->
case CanInvite of
false -> throw({error, ?ERR_NOT_ALLOWED});
true ->
- Reason = xml:get_path_s(InviteEl,
+ Reason = fxml:get_path_s(InviteEl,
[{elem, <<"reason">>}, cdata]),
- ContinueEl = case xml:get_path_s(InviteEl,
+ ContinueEl = case fxml:get_path_s(InviteEl,
[{elem, <<"continue">>}])
of
<<>> -> [];
@@ -4556,10 +4562,10 @@ handle_roommessage_from_nonparticipant(Packet, Lang,
%% because it crashes when the packet is not a decline message.
check_decline_invitation(Packet) ->
#xmlel{name = <<"message">>} = Packet,
- XEl = xml:get_subtag(Packet, <<"x">>),
- (?NS_MUC_USER) = xml:get_tag_attr_s(<<"xmlns">>, XEl),
- DEl = xml:get_subtag(XEl, <<"decline">>),
- ToString = xml:get_tag_attr_s(<<"to">>, DEl),
+ XEl = fxml:get_subtag(Packet, <<"x">>),
+ (?NS_MUC_USER) = fxml:get_tag_attr_s(<<"xmlns">>, XEl),
+ DEl = fxml:get_subtag(XEl, <<"decline">>),
+ ToString = fxml:get_tag_attr_s(<<"to">>, DEl),
ToJID = jid:from_string(ToString),
{true, {Packet, XEl, DEl, ToJID}}.
@@ -4646,7 +4652,7 @@ tab_count_user(JID) ->
end.
element_size(El) ->
- byte_size(xml:element_to_binary(El)).
+ byte_size(fxml:element_to_binary(El)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Multicast
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index 96ebcb6f..83520c0b 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -151,7 +151,7 @@ init([LServerS, Opts]) ->
try_start_loop(),
create_pool(),
ejabberd_router_multicast:register_route(LServerS),
- ejabberd_router:register_route(LServiceS),
+ ejabberd_router:register_route(LServiceS, LServerS),
{ok,
#state{lservice = LServiceS, lserver = LServerS,
access = Access, service_limits = SLimits}}.
@@ -233,7 +233,7 @@ handle_iq(From, To, #xmlel{attrs = Attrs} = Packet, State) ->
ejabberd_router:route(To, From, Err);
reply ->
LServiceS = jts(To),
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"result">> ->
process_iqreply_result(From, LServiceS, Packet, State);
<<"error">> ->
@@ -395,7 +395,7 @@ act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
perform(From, Packet, AAttrs, _,
{route_single, Group}) ->
[route_packet(From, ToUser, Packet, AAttrs,
- Group#group.addresses)
+ Group#group.others, Group#group.addresses)
|| ToUser <- Group#group.dests];
perform(From, Packet, AAttrs, _,
{{route_multicast, JID, RLimits}, Group}) ->
@@ -436,17 +436,17 @@ check_access(LServerS, Access, From) ->
%%%-------------------------
strip_addresses_element(Packet) ->
- case xml:get_subtag(Packet, <<"addresses">>) of
+ case fxml:get_subtag(Packet, <<"addresses">>) of
#xmlel{name = <<"addresses">>, attrs = AAttrs,
children = Addresses} ->
- case xml:get_attr_s(<<"xmlns">>, AAttrs) of
+ case fxml:get_attr_s(<<"xmlns">>, AAttrs) of
?NS_ADDRESS ->
#xmlel{name = Name, attrs = Attrs, children = Els} =
Packet,
Els_stripped = lists:keydelete(<<"addresses">>, 2, Els),
Packet_stripped = #xmlel{name = Name, attrs = Attrs,
children = Els_stripped},
- {ok, Packet_stripped, AAttrs, xml:remove_cdata(Addresses)};
+ {ok, Packet_stripped, AAttrs, fxml:remove_cdata(Addresses)};
_ -> throw(ewxmlns)
end;
_ -> throw(eadsele)
@@ -460,10 +460,10 @@ split_addresses_todeliver(Addresses) ->
lists:partition(fun (XML) ->
case XML of
#xmlel{name = <<"address">>, attrs = Attrs} ->
- case xml:get_attr_s(<<"delivered">>, Attrs) of
+ case fxml:get_attr_s(<<"delivered">>, Attrs) of
<<"true">> -> false;
_ ->
- Type = xml:get_attr_s(<<"type">>,
+ Type = fxml:get_attr_s(<<"type">>,
Attrs),
case Type of
<<"to">> -> true;
@@ -499,10 +499,10 @@ check_limit_dests(SLimits, FromJID, Packet,
convert_dest_record(XMLs) ->
lists:map(fun (XML) ->
- case xml:get_tag_attr_s(<<"jid">>, XML) of
+ case fxml:get_tag_attr_s(<<"jid">>, XML) of
<<"">> -> #dest{jid_string = none, full_xml = XML};
JIDS ->
- Type = xml:get_tag_attr_s(<<"type">>, XML),
+ Type = fxml:get_tag_attr_s(<<"type">>, XML),
JIDJ = stj(JIDS),
#dest{jid_string = JIDS, jid_jid = JIDJ,
type = Type, full_xml = XML}
@@ -525,7 +525,7 @@ split_dests_jid(Dests) ->
Dests).
report_not_jid(From, Packet, Dests) ->
- Dests2 = [xml:element_to_binary(Dest#dest.full_xml)
+ Dests2 = [fxml:element_to_binary(Dest#dest.full_xml)
|| Dest <- Dests],
[route_error(From, From, Packet, jid_malformed,
<<"This service can not process the address: ",
@@ -634,13 +634,13 @@ decide_action_group(Group) ->
%%% Route packet
%%%-------------------------
-route_packet(From, ToDest, Packet, AAttrs, Addresses) ->
+route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
Dests = case ToDest#dest.type of
<<"bcc">> -> [];
_ -> [ToDest]
end,
route_packet2(From, ToDest#dest.jid_string, Dests,
- Packet, AAttrs, Addresses).
+ Packet, AAttrs, {Others, Addresses}).
route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
Addresses, Limits) ->
@@ -666,6 +666,8 @@ route_packet2(From, ToS, Dests, Packet, AAttrs,
ToJID = stj(ToS),
ejabberd_router:route(From, ToJID, Packet2).
+append_dests(_Dests, {Others, Addresses}) ->
+ Addresses++Others;
append_dests([], Addresses) -> Addresses;
append_dests([Dest | Dests], Addresses) ->
append_dests(Dests, [Dest#dest.full_xml | Addresses]).
@@ -734,8 +736,8 @@ process_iqreply_error(From, LServiceS, _Packet) ->
process_iqreply_result(From, LServiceS, Packet, State) ->
#xmlel{name = <<"query">>, attrs = Attrs2,
children = Els2} =
- xml:get_subtag(Packet, <<"query">>),
- case xml:get_attr_s(<<"xmlns">>, Attrs2) of
+ fxml:get_subtag(Packet, <<"query">>),
+ case fxml:get_attr_s(<<"xmlns">>, Attrs2) of
?NS_DISCO_INFO ->
process_discoinfo_result(From, LServiceS, Els2, State);
?NS_DISCO_ITEMS ->
@@ -763,7 +765,7 @@ process_discoinfo_result2(From, FromS, LServiceS, Els,
fun(XML) ->
case XML of
#xmlel{name = <<"feature">>, attrs = Attrs} ->
- (?NS_ADDRESS) == xml:get_attr_s(<<"var">>, Attrs);
+ (?NS_ADDRESS) == fxml:get_attr_s(<<"var">>, Attrs);
_ -> false
end
end,
@@ -807,10 +809,10 @@ get_limits_els(Els) ->
#xmlel{name = <<"x">>, attrs = Attrs,
children = SubEls} ->
case ((?NS_XDATA) ==
- xml:get_attr_s(<<"xmlns">>, Attrs))
+ fxml:get_attr_s(<<"xmlns">>, Attrs))
and
(<<"result">> ==
- xml:get_attr_s(<<"type">>, Attrs))
+ fxml:get_attr_s(<<"type">>, Attrs))
of
true -> get_limits_fields(SubEls) ++ R;
false -> R
@@ -826,11 +828,11 @@ get_limits_fields(Fields) ->
#xmlel{name = <<"field">>,
attrs = Attrs} ->
(<<"FORM_TYPE">> ==
- xml:get_attr_s(<<"var">>,
+ fxml:get_attr_s(<<"var">>,
Attrs))
and
(<<"hidden">> ==
- xml:get_attr_s(<<"type">>,
+ fxml:get_attr_s(<<"type">>,
Attrs));
_ -> false
end
@@ -848,8 +850,8 @@ get_limits_values(Values) ->
children = SubEls} ->
[#xmlel{name = <<"value">>, children = SubElsV}] =
SubEls,
- Number = xml:get_cdata(SubElsV),
- Name = xml:get_attr_s(<<"var">>, Attrs),
+ Number = fxml:get_cdata(SubElsV),
+ Name = fxml:get_attr_s(<<"var">>, Attrs),
[{jlib:binary_to_atom(Name),
jlib:binary_to_integer(Number)}
| R];
@@ -870,7 +872,7 @@ process_discoitems_result(From, LServiceS, Els) ->
fun(XML, Res) ->
case XML of
#xmlel{name = <<"item">>, attrs = Attrs} ->
- SJID = xml:get_attr_s(<<"jid">>, Attrs),
+ SJID = fxml:get_attr_s(<<"jid">>, Attrs),
case jid:from_string(SJID) of
#jid{luser = <<"">>,
lresource = <<"">>} ->
@@ -912,8 +914,9 @@ received_awaiter(JID, Waiter, LServiceS) ->
From = Waiter#waiter.sender,
Packet = Waiter#waiter.packet,
AAttrs = Waiter#waiter.aattrs,
+ Others = Group#group.others,
Addresses = Waiter#waiter.addresses,
- [route_packet(From, ToUser, Packet, AAttrs, Addresses)
+ [route_packet(From, ToUser, Packet, AAttrs, Others, Addresses)
|| ToUser <- Group#group.dests];
true ->
send_query_info(RServer, LServiceS),
@@ -1196,7 +1199,7 @@ to_binary(A) -> list_to_binary(hd(io_lib:format("~p", [A]))).
route_error(From, To, Packet, ErrType, ErrText) ->
#xmlel{attrs = Attrs} = Packet,
- Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
+ Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
Reply = make_reply(ErrType, Lang, ErrText),
Err = jlib:make_error_reply(Packet, Reply),
ejabberd_router:route(From, To, Err).
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 1b1627e8..fa6a961f 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -25,8 +25,11 @@
-module(mod_offline).
+-compile([{parse_transform, ejabberd_sql_pt}]).
+
-author('alexey@process-one.net').
+-protocol({xep, 13, '1.2'}).
-protocol({xep, 22, '1.4'}).
-protocol({xep, 23, '1.3'}).
-protocol({xep, 160, '1.0'}).
@@ -37,15 +40,18 @@
-behaviour(gen_mod).
--export([count_offline_messages/2]).
-
-export([start/2,
start_link/2,
stop/1,
store_packet/3,
+ store_offline_msg/5,
resend_offline_messages/2,
pop_offline_messages/3,
get_sm_features/5,
+ get_sm_identity/5,
+ get_sm_items/5,
+ get_info/5,
+ handle_offline_query/3,
remove_expired_messages/1,
remove_old_messages/2,
remove_user/2,
@@ -53,6 +59,7 @@
import/3,
export/1,
get_queue_length/2,
+ count_offline_messages/2,
get_offline_els/2,
webadmin_page/3,
webadmin_user/4,
@@ -62,6 +69,8 @@
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
+-deprecated({get_queue_length,2}).
+
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -73,6 +82,8 @@
-include("mod_offline.hrl").
+-include("ejabberd_sql_pt.hrl").
+
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
@@ -112,6 +123,8 @@ init([Host, Opts]) ->
update_table();
_ -> ok
end,
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
+ no_queue),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
store_packet, 50),
ejabberd_hooks:add(resend_offline_messages_hook, Host,
@@ -124,12 +137,19 @@ init([Host, Opts]) ->
?MODULE, get_sm_features, 50),
ejabberd_hooks:add(disco_local_features, Host,
?MODULE, get_sm_features, 50),
+ ejabberd_hooks:add(disco_sm_identity, Host,
+ ?MODULE, get_sm_identity, 50),
+ ejabberd_hooks:add(disco_sm_items, Host,
+ ?MODULE, get_sm_items, 50),
+ ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50),
ejabberd_hooks:add(webadmin_page_host, Host,
?MODULE, webadmin_page, 50),
ejabberd_hooks:add(webadmin_user, Host,
?MODULE, webadmin_user, 50),
ejabberd_hooks:add(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE,
+ ?MODULE, handle_offline_query, IQDisc),
AccessMaxOfflineMsgs =
gen_mod:get_opt(access_max_user_messages, Opts,
fun(A) when is_atom(A) -> A end,
@@ -174,17 +194,24 @@ terminate(_Reason, State) ->
?MODULE, remove_user, 50),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50),
+ ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50),
+ ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50),
+ ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 50),
ejabberd_hooks:delete(webadmin_page_host, Host,
?MODULE, webadmin_page, 50),
ejabberd_hooks:delete(webadmin_user, Host,
?MODULE, webadmin_user, 50),
ejabberd_hooks:delete(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE),
ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
+store_offline_msg(Host, US, Msgs, Len, MaxOfflineMsgs) ->
+ DBType = gen_mod:db_type(Host, ?MODULE),
+ store_offline_msg(Host, US, Msgs, Len, MaxOfflineMsgs, DBType).
store_offline_msg(_Host, US, Msgs, Len, MaxOfflineMsgs,
mnesia) ->
@@ -223,7 +250,7 @@ store_offline_msg(Host, {User, _Server}, Msgs, Len, MaxOfflineMsgs, odbc) ->
M#offline_msg.timestamp,
<<"Offline Storage">>),
XML =
- ejabberd_odbc:escape(xml:element_to_binary(NewPacket)),
+ ejabberd_odbc:escape(fxml:element_to_binary(NewPacket)),
odbc_queries:add_spool_sql(Username, XML)
end,
Msgs),
@@ -272,38 +299,229 @@ get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
{result, I} -> I;
_ -> []
end,
- {result, Feats ++ [?NS_FEATURE_MSGOFFLINE]};
+ {result, Feats ++ [?NS_FEATURE_MSGOFFLINE, ?NS_FLEX_OFFLINE]};
get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
%% override all lesser features...
{result, []};
+get_sm_features(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S},
+ ?NS_FLEX_OFFLINE, _Lang) ->
+ {result, [?NS_FLEX_OFFLINE]};
+
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
+get_sm_identity(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S},
+ ?NS_FLEX_OFFLINE, _Lang) ->
+ Identity = #xmlel{name = <<"identity">>,
+ attrs = [{<<"category">>, <<"automation">>},
+ {<<"type">>, <<"message-list">>}]},
+ [Identity];
+get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+get_sm_items(_Acc, #jid{luser = U, lserver = S, lresource = R} = JID,
+ #jid{luser = U, lserver = S},
+ ?NS_FLEX_OFFLINE, _Lang) ->
+ case ejabberd_sm:get_session_pid(U, S, R) of
+ Pid when is_pid(Pid) ->
+ Hdrs = read_message_headers(U, S),
+ BareJID = jid:to_string(jid:remove_resource(JID)),
+ Pid ! dont_ask_offline,
+ {result, lists:map(
+ fun({Node, From, _OfflineMsg}) ->
+ #xmlel{name = <<"item">>,
+ attrs = [{<<"jid">>, BareJID},
+ {<<"node">>, Node},
+ {<<"name">>, From}]}
+ end, Hdrs)};
+ none ->
+ {result, []}
+ end;
+get_sm_items(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
+ #jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
+ N = jlib:integer_to_binary(count_offline_messages(U, S)),
+ case ejabberd_sm:get_session_pid(U, S, R) of
+ Pid when is_pid(Pid) ->
+ Pid ! dont_ask_offline;
+ none ->
+ ok
+ end,
+ [#xmlel{name = <<"x">>,
+ attrs = [{<<"xmlns">>, ?NS_XDATA},
+ {<<"type">>, <<"result">>}],
+ children = [#xmlel{name = <<"field">>,
+ attrs = [{<<"var">>, <<"FORM_TYPE">>},
+ {<<"type">>, <<"hidden">>}],
+ children = [#xmlel{name = <<"value">>,
+ children = [{xmlcdata,
+ ?NS_FLEX_OFFLINE}]}]},
+ #xmlel{name = <<"field">>,
+ attrs = [{<<"var">>, <<"number_of_messages">>}],
+ children = [#xmlel{name = <<"value">>,
+ children = [{xmlcdata, N}]}]}]}];
+get_info(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+handle_offline_query(#jid{luser = U, lserver = S} = From,
+ #jid{luser = U, lserver = S} = _To,
+ #iq{type = Type, sub_el = SubEl} = IQ) ->
+ case Type of
+ get ->
+ case fxml:get_subtag(SubEl, <<"fetch">>) of
+ #xmlel{} ->
+ handle_offline_fetch(From);
+ false ->
+ handle_offline_items_view(From, SubEl)
+ end;
+ set ->
+ case fxml:get_subtag(SubEl, <<"purge">>) of
+ #xmlel{} ->
+ delete_all_msgs(U, S);
+ false ->
+ handle_offline_items_remove(From, SubEl)
+ end
+ end,
+ IQ#iq{type = result, sub_el = []};
+handle_offline_query(_From, _To, #iq{sub_el = SubEl} = IQ) ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}.
+
+handle_offline_items_view(JID, #xmlel{children = Items}) ->
+ {U, S, R} = jid:tolower(JID),
+ lists:foreach(
+ fun(Node) ->
+ case fetch_msg_by_node(JID, Node) of
+ {ok, OfflineMsg} ->
+ case offline_msg_to_route(S, OfflineMsg) of
+ {route, From, To, El} ->
+ NewEl = set_offline_tag(El, Node),
+ case ejabberd_sm:get_session_pid(U, S, R) of
+ Pid when is_pid(Pid) ->
+ Pid ! {route, From, To, NewEl};
+ none ->
+ ok
+ end;
+ error ->
+ ok
+ end;
+ error ->
+ ok
+ end
+ end, get_nodes_from_items(Items, <<"view">>)).
+
+handle_offline_items_remove(JID, #xmlel{children = Items}) ->
+ lists:foreach(
+ fun(Node) ->
+ remove_msg_by_node(JID, Node)
+ end, get_nodes_from_items(Items, <<"remove">>)).
+
+get_nodes_from_items(Items, Action) ->
+ lists:flatmap(
+ fun(#xmlel{name = <<"item">>, attrs = Attrs}) ->
+ case fxml:get_attr_s(<<"action">>, Attrs) of
+ Action ->
+ case fxml:get_attr_s(<<"node">>, Attrs) of
+ <<"">> ->
+ [];
+ TS ->
+ [TS]
+ end;
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end, Items).
+
+set_offline_tag(#xmlel{children = Els} = El, Node) ->
+ OfflineEl = #xmlel{name = <<"offline">>,
+ attrs = [{<<"xmlns">>, ?NS_FLEX_OFFLINE}],
+ children = [#xmlel{name = <<"item">>,
+ attrs = [{<<"node">>, Node}]}]},
+ El#xmlel{children = [OfflineEl|Els]}.
+
+handle_offline_fetch(#jid{luser = U, lserver = S, lresource = R}) ->
+ case ejabberd_sm:get_session_pid(U, S, R) of
+ none ->
+ ok;
+ Pid when is_pid(Pid) ->
+ Pid ! dont_ask_offline,
+ lists:foreach(
+ fun({Node, _, Msg}) ->
+ case offline_msg_to_route(S, Msg) of
+ {route, From, To, El} ->
+ NewEl = set_offline_tag(El, Node),
+ Pid ! {route, From, To, NewEl};
+ error ->
+ ok
+ end
+ end, read_message_headers(U, S))
+ end.
+
+fetch_msg_by_node(To, <<Seq:20/binary, "+", From_s/binary>>) ->
+ case jid:from_string(From_s) of
+ From = #jid{} ->
+ case gen_mod:db_type(To#jid.lserver, ?MODULE) of
+ odbc ->
+ read_message(From, To, Seq, odbc);
+ DBType ->
+ case binary_to_timestamp(Seq) of
+ undefined -> ok;
+ TS -> read_message(From, To, TS, DBType)
+ end
+ end;
+ error ->
+ ok
+ end.
+
+remove_msg_by_node(To, <<Seq:20/binary, "+", From_s/binary>>) ->
+ case jid:from_string(From_s) of
+ From = #jid{} ->
+ case gen_mod:db_type(To#jid.lserver, ?MODULE) of
+ odbc ->
+ remove_message(From, To, Seq, odbc);
+ DBType ->
+ case binary_to_timestamp(Seq) of
+ undefined -> ok;
+ TS -> remove_message(From, To, TS, DBType)
+ end
+ end;
+ error ->
+ ok
+ end.
+
need_to_store(LServer, Packet) ->
- Type = xml:get_tag_attr_s(<<"type">>, Packet),
+ Type = fxml:get_tag_attr_s(<<"type">>, Packet),
if (Type /= <<"error">>) and (Type /= <<"groupchat">>)
and (Type /= <<"headline">>) ->
- case check_store_hint(Packet) of
- store ->
- 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 ->
- xml:get_subtag(Packet, <<"body">>) /= false;
- unless_chat_state ->
- not jlib:is_standalone_chat_state(Packet);
- true ->
- true
- end
+ case has_offline_tag(Packet) of
+ false ->
+ case check_store_hint(Packet) of
+ store ->
+ 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
end;
true ->
false
@@ -342,12 +560,15 @@ check_store_hint(Packet) ->
end.
has_store_hint(Packet) ->
- xml:get_subtag_with_xmlns(Packet, <<"store">>, ?NS_HINTS) =/= false.
+ fxml:get_subtag_with_xmlns(Packet, <<"store">>, ?NS_HINTS) =/= false.
has_no_store_hint(Packet) ->
- xml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS) =/= false
+ fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS) =/= false
orelse
- xml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS) =/= false.
+ fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS) =/= false.
+
+has_offline_tag(Packet) ->
+ fxml:get_subtag_with_xmlns(Packet, <<"offline">>, ?NS_FLEX_OFFLINE) =/= false.
%% Check if the packet has any content about XEP-0022
check_event(From, To, Packet) ->
@@ -356,12 +577,12 @@ check_event(From, To, Packet) ->
case find_x_event(Els) of
false -> true;
El ->
- case xml:get_subtag(El, <<"id">>) of
+ case fxml:get_subtag(El, <<"id">>) of
false ->
- case xml:get_subtag(El, <<"offline">>) of
+ case fxml:get_subtag(El, <<"offline">>) of
false -> true;
_ ->
- ID = case xml:get_tag_attr_s(<<"id">>, Packet) of
+ ID = case fxml:get_tag_attr_s(<<"id">>, Packet) of
<<"">> ->
#xmlel{name = <<"id">>, attrs = [],
children = []};
@@ -398,7 +619,7 @@ find_x_event([]) -> false;
find_x_event([{xmlcdata, _} | Els]) ->
find_x_event(Els);
find_x_event([El | Els]) ->
- case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, El) of
?NS_EVENT -> El;
_ -> find_x_event(Els)
end.
@@ -407,9 +628,9 @@ find_x_expire(_, []) -> never;
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
find_x_expire(TimeStamp, Els);
find_x_expire(TimeStamp, [El | Els]) ->
- case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ case fxml:get_tag_attr_s(<<"xmlns">>, El) of
?NS_EXPIRE ->
- Val = xml:get_tag_attr_s(<<"seconds">>, El),
+ Val = fxml:get_tag_attr_s(<<"seconds">>, El),
case catch jlib:binary_to_integer(Val) of
{'EXIT', _} -> never;
Int when Int > 0 ->
@@ -476,14 +697,11 @@ pop_offline_messages(Ls, LUser, LServer, mnesia) ->
_ -> Ls
end;
pop_offline_messages(Ls, LUser, LServer, odbc) ->
- EUser = ejabberd_odbc:escape(LUser),
- case odbc_queries:get_and_del_spool_msg_t(LServer,
- EUser)
- of
- {atomic, {selected, [<<"username">>, <<"xml">>], Rs}} ->
+ case odbc_queries:get_and_del_spool_msg_t(LServer, LUser) of
+ {atomic, {selected, Rs}} ->
Ls ++
- lists:flatmap(fun ([_, XML]) ->
- case xml_stream:parse_element(XML) of
+ lists:flatmap(fun ({_, XML}) ->
+ case fxml_stream:parse_element(XML) of
{error, _Reason} ->
[];
El ->
@@ -601,8 +819,7 @@ remove_user(LUser, LServer, mnesia) ->
F = fun () -> mnesia:delete({offline_msg, US}) end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_spool_msg(LServer, Username);
+ odbc_queries:del_spool_msg(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(offline_msg,
<<"us">>, {LUser, LServer})}.
@@ -631,7 +848,7 @@ update_table() ->
iolist_to_binary(S)},
from = jid_to_binary(From),
to = jid_to_binary(To),
- packet = xml:to_xmlel(El)}
+ packet = fxml:to_xmlel(El)}
end);
_ ->
?INFO_MSG("Recreating offline_msg table", []),
@@ -646,7 +863,7 @@ discard_warn_sender(Msgs) ->
packet = Packet}) ->
ErrText = <<"Your contact offline message queue is "
"full. The message has been discarded.">>,
- Lang = xml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Err = jlib:make_error_reply(Packet,
?ERRT_RESOURCE_CONSTRAINT(Lang,
ErrText)),
@@ -673,14 +890,14 @@ get_offline_els(LUser, LServer, DBType)
jlib:replace_from_to(From, To, Packet)
end, Msgs);
get_offline_els(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch ejabberd_odbc:sql_query(LServer,
- [<<"select xml from spool where username='">>,
- Username, <<"' order by seq;">>]) of
- {selected, [<<"xml">>], Rs} ->
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(xml)s from spool where "
+ "username=%(LUser)s order by seq")) of
+ {selected, Rs} ->
lists:flatmap(
- fun([XML]) ->
- case xml_stream:parse_element(XML) of
+ fun({XML}) ->
+ case fxml_stream:parse_element(XML) of
#xmlel{} = El ->
case offline_msg_to_route(LServer, El) of
{route, _, _, NewEl} ->
@@ -701,14 +918,131 @@ offline_msg_to_route(LServer, #offline_msg{} = R) ->
jlib:add_delay_info(R#offline_msg.packet, LServer, R#offline_msg.timestamp,
<<"Offline Storage">>)};
offline_msg_to_route(_LServer, #xmlel{} = El) ->
- To = jid:from_string(xml:get_tag_attr_s(<<"to">>, El)),
- From = jid:from_string(xml:get_tag_attr_s(<<"from">>, El)),
+ To = jid:from_string(fxml:get_tag_attr_s(<<"to">>, El)),
+ From = jid:from_string(fxml:get_tag_attr_s(<<"from">>, El)),
if (To /= error) and (From /= error) ->
{route, From, To, El};
true ->
error
end.
+binary_to_timestamp(TS) ->
+ case catch jlib:binary_to_integer(TS) of
+ Int when is_integer(Int) ->
+ Secs = Int div 1000000,
+ USec = Int rem 1000000,
+ MSec = Secs div 1000000,
+ Sec = Secs rem 1000000,
+ {MSec, Sec, USec};
+ _ ->
+ undefined
+ end.
+
+timestamp_to_binary({MS, S, US}) ->
+ format_timestamp(integer_to_list((MS * 1000000 + S) * 1000000 + US)).
+
+format_timestamp(TS) ->
+ iolist_to_binary(io_lib:format("~20..0s", [TS])).
+
+offline_msg_to_header(#offline_msg{from = From, timestamp = Int} = Msg) ->
+ TS = timestamp_to_binary(Int),
+ From_s = jid:to_string(From),
+ {<<TS/binary, "+", From_s/binary>>, From_s, Msg}.
+
+read_message_headers(LUser, LServer) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ read_message_headers(LUser, LServer, DBType).
+
+read_message_headers(LUser, LServer, mnesia) ->
+ Msgs = mnesia:dirty_read({offline_msg, {LUser, LServer}}),
+ Hdrs = lists:map(fun offline_msg_to_header/1, Msgs),
+ lists:keysort(1, Hdrs);
+read_message_headers(LUser, LServer, riak) ->
+ case ejabberd_riak:get_by_index(
+ offline_msg, offline_msg_schema(),
+ <<"us">>, {LUser, LServer}) of
+ {ok, Rs} ->
+ Hdrs = lists:map(fun offline_msg_to_header/1, Rs),
+ lists:keysort(1, Hdrs);
+ _Err ->
+ []
+ end;
+read_message_headers(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch ejabberd_odbc:sql_query(
+ LServer, [<<"select xml, seq from spool where username ='">>,
+ Username, <<"' order by seq;">>]) of
+ {selected, [<<"xml">>, <<"seq">>], Rows} ->
+ Hdrs = lists:flatmap(
+ fun([XML, Seq]) ->
+ try
+ #xmlel{} = El = fxml_stream:parse_element(XML),
+ From = fxml:get_tag_attr_s(<<"from">>, El),
+ #jid{} = jid:from_string(From),
+ TS = format_timestamp(Seq),
+ [{<<TS/binary, "+", From/binary>>, From, El}]
+ catch _:_ -> []
+ end
+ end, Rows),
+ lists:keysort(1, Hdrs);
+ _Err ->
+ []
+ end.
+
+read_message(_From, To, TS, mnesia) ->
+ {U, S, _} = jid:tolower(To),
+ case mnesia:dirty_match_object(
+ offline_msg, #offline_msg{us = {U, S}, timestamp = TS, _ = '_'}) of
+ [Msg|_] ->
+ {ok, Msg};
+ _ ->
+ error
+ end;
+read_message(_From, _To, TS, riak) ->
+ case ejabberd_riak:get(offline_msg, offline_msg_schema(), TS) of
+ {ok, Msg} ->
+ {ok, Msg};
+ _ ->
+ error
+ end;
+read_message(_From, To, Seq, odbc) ->
+ {LUser, LServer, _} = jid:tolower(To),
+ Username = ejabberd_odbc:escape(LUser),
+ SSeq = ejabberd_odbc:escape(Seq),
+ case ejabberd_odbc:sql_query(
+ LServer,
+ [<<"select xml from spool where username='">>, Username,
+ <<"' and seq='">>, SSeq, <<"';">>]) of
+ {selected, [<<"xml">>], [[RawXML]|_]} ->
+ case fxml_stream:parse_element(RawXML) of
+ #xmlel{} = El -> {ok, El};
+ {error, _} -> error
+ end;
+ _ ->
+ error
+ end.
+
+remove_message(_From, To, TS, mnesia) ->
+ {U, S, _} = jid:tolower(To),
+ Msgs = mnesia:dirty_match_object(
+ offline_msg, #offline_msg{us = {U, S}, timestamp = TS, _ = '_'}),
+ lists:foreach(
+ fun(Msg) ->
+ mnesia:dirty_delete_object(Msg)
+ end, Msgs);
+remove_message(_From, _To, TS, riak) ->
+ ejabberd_riak:delete(offline_msg, TS),
+ ok;
+remove_message(_From, To, Seq, odbc) ->
+ {LUser, LServer, _} = jid:tolower(To),
+ Username = ejabberd_odbc:escape(LUser),
+ SSeq = ejabberd_odbc:escape(Seq),
+ ejabberd_odbc:sql_query(
+ LServer,
+ [<<"delete from spool where username='">>, Username,
+ <<"' and seq='">>, SSeq, <<"';">>]),
+ ok.
+
read_all_msgs(LUser, LServer, mnesia) ->
US = {LUser, LServer},
lists:keysort(#offline_msg.timestamp,
@@ -723,20 +1057,20 @@ read_all_msgs(LUser, LServer, riak) ->
[]
end;
read_all_msgs(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch ejabberd_odbc:sql_query(LServer,
- [<<"select xml from spool where username='">>,
- Username, <<"' order by seq;">>])
- of
- {selected, [<<"xml">>], Rs} ->
- lists:flatmap(fun ([XML]) ->
- case xml_stream:parse_element(XML) of
- {error, _Reason} -> [];
- El -> [El]
- end
- end,
- Rs);
- _ -> []
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(xml)s from spool where "
+ "username=%(LUser)s order by seq")) of
+ {selected, Rs} ->
+ lists:flatmap(
+ fun({XML}) ->
+ case fxml_stream:parse_element(XML) of
+ {error, _Reason} -> [];
+ El -> [El]
+ end
+ end,
+ Rs);
+ _ -> []
end.
format_user_queue(Msgs, DBType) when DBType == mnesia; DBType == riak ->
@@ -878,7 +1212,7 @@ user_queue_parse_query(LUser, LServer, Query, odbc) ->
of
{selected, [<<"xml">>, <<"seq">>], Rs} ->
lists:flatmap(fun ([XML, Seq]) ->
- case xml_stream:parse_element(XML)
+ case fxml_stream:parse_element(XML)
of
{error, _Reason} -> [];
El -> [{El, Seq}]
@@ -919,30 +1253,7 @@ us_to_list({User, Server}) ->
jid:to_string({User, Server, <<"">>}).
get_queue_length(LUser, LServer) ->
- get_queue_length(LUser, LServer,
- gen_mod:db_type(LServer, ?MODULE)).
-
-get_queue_length(LUser, LServer, mnesia) ->
- length(mnesia:dirty_read({offline_msg,
- {LUser, LServer}}));
-get_queue_length(LUser, LServer, riak) ->
- case ejabberd_riak:count_by_index(offline_msg,
- <<"us">>, {LUser, LServer}) of
- {ok, N} ->
- N;
- _ ->
- 0
- end;
-get_queue_length(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch ejabberd_odbc:sql_query(LServer,
- [<<"select count(*) from spool where username='">>,
- Username, <<"';">>])
- of
- {selected, [_], [[SCount]]} ->
- jlib:binary_to_integer(SCount);
- _ -> 0
- end.
+ count_offline_messages(LUser, LServer).
get_messages_subset(User, Host, MsgsAll, DBType) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
@@ -984,7 +1295,7 @@ get_messages_subset2(Max, Length, MsgsAll, odbc) ->
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
webadmin_user(Acc, User, Server, Lang) ->
- QueueLen = get_queue_length(jid:nodeprep(User),
+ QueueLen = count_offline_messages(jid:nodeprep(User),
jid:nameprep(Server)),
FQueueLen = [?AC(<<"queue/">>,
(iolist_to_binary(integer_to_list(QueueLen))))],
@@ -1015,8 +1326,7 @@ delete_all_msgs(LUser, LServer, riak) ->
<<"us">>, {LUser, LServer}),
{atomic, Res};
delete_all_msgs(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_spool_msg(LServer, Username),
+ odbc_queries:del_spool_msg(LServer, LUser),
{atomic, ok}.
webadmin_user_parse_query(_, <<"removealloffline">>,
@@ -1052,15 +1362,13 @@ count_offline_messages(LUser, LServer, mnesia) ->
_ -> 0
end;
count_offline_messages(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:count_records_where(LServer,
- <<"spool">>,
- <<"where username='",
- Username/binary, "'">>)
- of
- {selected, [_], [[Res]]} ->
- jlib:binary_to_integer(Res);
- _ -> 0
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(count(*))d from spool "
+ "where username=%(LUser)s")) of
+ {selected, [{Res}]} ->
+ Res;
+ _ -> 0
end;
count_offline_messages(LUser, LServer, riak) ->
case ejabberd_riak:count_by_index(
@@ -1110,7 +1418,7 @@ export(_Server) ->
Packet1 = jlib:replace_from_to(From, To, Packet),
Packet2 = jlib:add_delay_info(Packet1, LServer, TimeStamp,
<<"Offline Storage">>),
- XML = ejabberd_odbc:escape(xml:element_to_binary(Packet2)),
+ XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet2)),
[[<<"delete from spool where username='">>, Username, <<"';">>],
[<<"insert into spool(username, xml) values ('">>,
Username, <<"', '">>, XML, <<"');">>]];
@@ -1121,12 +1429,12 @@ export(_Server) ->
import(LServer) ->
[{<<"select username, xml from spool;">>,
fun([LUser, XML]) ->
- El = #xmlel{} = xml_stream:parse_element(XML),
+ El = #xmlel{} = fxml_stream:parse_element(XML),
From = #jid{} = jid:from_string(
- xml:get_attr_s(<<"from">>, El#xmlel.attrs)),
+ fxml:get_attr_s(<<"from">>, El#xmlel.attrs)),
To = #jid{} = jid:from_string(
- xml:get_attr_s(<<"to">>, El#xmlel.attrs)),
- Stamp = xml:get_path_s(El, [{elem, <<"delay">>},
+ fxml:get_attr_s(<<"to">>, El#xmlel.attrs)),
+ Stamp = fxml:get_path_s(El, [{elem, <<"delay">>},
{attr, <<"stamp">>}]),
TS = case jlib:datetime_string_to_timestamp(Stamp) of
{_, _, _} = Now ->
diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl
index 34fdcdb7..1118b7bb 100644
--- a/src/mod_pres_counter.erl
+++ b/src/mod_pres_counter.erl
@@ -52,7 +52,7 @@ check_packet(_, _User, Server, _PrivacyList,
{From, To, #xmlel{name = Name, attrs = Attrs}}, Dir) ->
case Name of
<<"presence">> ->
- IsSubscription = case xml:get_attr_s(<<"type">>, Attrs)
+ IsSubscription = case fxml:get_attr_s(<<"type">>, Attrs)
of
<<"subscribe">> -> true;
<<"subscribed">> -> true;
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 9c2af037..193befe8 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -35,7 +35,8 @@
process_iq_set/4, process_iq_get/5, get_user_list/3,
check_packet/6, remove_user/2, item_to_raw/1,
raw_to_item/1, is_list_needdb/1, updated_list/3,
- item_to_xml/1, get_user_lists/2, import/3]).
+ item_to_xml/1, get_user_lists/2, import/3,
+ set_privacy_list/1]).
-export([sql_add_privacy_list/2,
sql_get_default_privacy_list/2,
@@ -108,12 +109,12 @@ process_iq_get(_, From, _To, #iq{sub_el = SubEl},
#userlist{name = Active}) ->
#jid{luser = LUser, lserver = LServer} = From,
#xmlel{children = Els} = SubEl,
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[] -> process_lists_get(LUser, LServer, Active);
[#xmlel{name = Name, attrs = Attrs}] ->
case Name of
<<"list">> ->
- ListName = xml:get_attr(<<"name">>, Attrs),
+ ListName = fxml:get_attr(<<"name">>, Attrs),
process_list_get(LUser, LServer, ListName);
_ -> {error, ?ERR_BAD_REQUEST}
end;
@@ -180,16 +181,14 @@ process_lists_get(LUser, LServer, _Active, riak) ->
error
end;
process_lists_get(LUser, LServer, _Active, odbc) ->
- Default = case catch sql_get_default_privacy_list(LUser,
- LServer)
- of
- {selected, [<<"name">>], []} -> none;
- {selected, [<<"name">>], [[DefName]]} -> DefName;
+ Default = case catch sql_get_default_privacy_list(LUser, LServer) of
+ {selected, []} -> none;
+ {selected, [{DefName}]} -> DefName;
_ -> none
end,
case catch sql_get_privacy_list_names(LUser, LServer) of
- {selected, [<<"name">>], Names} ->
- LItems = lists:map(fun ([N]) ->
+ {selected, Names} ->
+ LItems = lists:map(fun ({N}) ->
#xmlel{name = <<"list">>,
attrs = [{<<"name">>, N}],
children = []}
@@ -241,17 +240,11 @@ process_list_get(LUser, LServer, Name, riak) ->
error
end;
process_list_get(LUser, LServer, Name, odbc) ->
- case catch sql_get_privacy_list_id(LUser, LServer, Name)
- of
- {selected, [<<"id">>], []} -> not_found;
- {selected, [<<"id">>], [[ID]]} ->
- case catch sql_get_privacy_list_data_by_id(ID, LServer)
- of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems} ->
+ case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+ {selected, []} -> not_found;
+ {selected, [{ID}]} ->
+ case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+ {selected, RItems} ->
lists:flatmap(fun raw_to_item/1, RItems);
_ -> error
end;
@@ -342,14 +335,14 @@ list_to_action(S) ->
process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
#jid{luser = LUser, lserver = LServer} = From,
#xmlel{children = Els} = SubEl,
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = Name, attrs = Attrs,
children = SubEls}] ->
- ListName = xml:get_attr(<<"name">>, Attrs),
+ ListName = fxml:get_attr(<<"name">>, Attrs),
case Name of
<<"list">> ->
process_list_set(LUser, LServer, ListName,
- xml:remove_cdata(SubEls));
+ fxml:remove_cdata(SubEls));
<<"active">> ->
process_active_set(LUser, LServer, ListName);
<<"default">> ->
@@ -404,9 +397,9 @@ process_default_set(LUser, LServer, {value, Name},
odbc) ->
F = fun () ->
case sql_get_privacy_list_names_t(LUser) of
- {selected, [<<"name">>], []} -> not_found;
- {selected, [<<"name">>], Names} ->
- case lists:member([Name], Names) of
+ {selected, []} -> not_found;
+ {selected, Names} ->
+ case lists:member({Name}, Names) of
true -> sql_set_default_privacy_list(LUser, Name), ok;
false -> not_found
end
@@ -472,17 +465,11 @@ process_active_set(LUser, LServer, Name, riak) ->
error
end;
process_active_set(LUser, LServer, Name, odbc) ->
- case catch sql_get_privacy_list_id(LUser, LServer, Name)
- of
- {selected, [<<"id">>], []} -> error;
- {selected, [<<"id">>], [[ID]]} ->
- case catch sql_get_privacy_list_data_by_id(ID, LServer)
- of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems} ->
+ case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+ {selected, []} -> error;
+ {selected, [{ID}]} ->
+ case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+ {selected, RItems} ->
lists:flatmap(fun raw_to_item/1, RItems);
_ -> error
end;
@@ -519,9 +506,9 @@ remove_privacy_list(LUser, LServer, Name, riak) ->
remove_privacy_list(LUser, LServer, Name, odbc) ->
F = fun () ->
case sql_get_default_privacy_list_t(LUser) of
- {selected, [<<"name">>], []} ->
+ {selected, []} ->
sql_remove_privacy_list(LUser, Name), ok;
- {selected, [<<"name">>], [[Default]]} ->
+ {selected, [{Default}]} ->
if Name == Default -> conflict;
true -> sql_remove_privacy_list(LUser, Name), ok
end
@@ -529,6 +516,35 @@ remove_privacy_list(LUser, LServer, Name, odbc) ->
end,
odbc_queries:sql_transaction(LServer, F).
+set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ set_privacy_list(Privacy, DBType).
+
+set_privacy_list(Privacy, mnesia) ->
+ mnesia:dirty_write(Privacy);
+set_privacy_list(Privacy, riak) ->
+ ejabberd_riak:put(Privacy, privacy_schema());
+set_privacy_list(#privacy{us = {LUser, LServer},
+ default = Default,
+ lists = Lists}, odbc) ->
+ F = fun() ->
+ lists:foreach(
+ fun({Name, List}) ->
+ sql_add_privacy_list(LUser, Name),
+ {selected, [<<"id">>], [[I]]} =
+ sql_get_privacy_list_id_t(LUser, Name),
+ RItems = lists:map(fun item_to_raw/1, List),
+ sql_set_privacy_list(I, RItems),
+ if is_binary(Default) ->
+ sql_set_default_privacy_list(LUser, Default),
+ ok;
+ true ->
+ ok
+ end
+ end, Lists)
+ end,
+ odbc_queries:sql_transaction(LServer, F).
+
set_privacy_list(LUser, LServer, Name, List, mnesia) ->
F = fun () ->
case mnesia:wread({privacy, {LUser, LServer}}) of
@@ -560,12 +576,12 @@ set_privacy_list(LUser, LServer, Name, List, odbc) ->
RItems = lists:map(fun item_to_raw/1, List),
F = fun () ->
ID = case sql_get_privacy_list_id_t(LUser, Name) of
- {selected, [<<"id">>], []} ->
+ {selected, []} ->
sql_add_privacy_list(LUser, Name),
- {selected, [<<"id">>], [[I]]} =
+ {selected, [{I}]} =
sql_get_privacy_list_id_t(LUser, Name),
I;
- {selected, [<<"id">>], [[I]]} -> I
+ {selected, [{I}]} -> I
end,
sql_set_privacy_list(ID, RItems),
ok
@@ -621,10 +637,10 @@ parse_items([#xmlel{name = <<"item">>, attrs = Attrs,
children = SubEls}
| Els],
Res) ->
- Type = xml:get_attr(<<"type">>, Attrs),
- Value = xml:get_attr(<<"value">>, Attrs),
- SAction = xml:get_attr(<<"action">>, Attrs),
- SOrder = xml:get_attr(<<"order">>, Attrs),
+ Type = fxml:get_attr(<<"type">>, Attrs),
+ Value = fxml:get_attr(<<"value">>, Attrs),
+ SAction = fxml:get_attr(<<"action">>, Attrs),
+ SOrder = fxml:get_attr(<<"order">>, Attrs),
Action = case catch list_to_action(element(2, SAction))
of
{'EXIT', _} -> false;
@@ -674,7 +690,7 @@ parse_items([#xmlel{name = <<"item">>, attrs = Attrs,
case I2 of
false -> false;
_ ->
- case parse_matches(I2, xml:remove_cdata(SubEls)) of
+ case parse_matches(I2, fxml:remove_cdata(SubEls)) of
false -> false;
I3 -> parse_items(Els, [I3 | Res])
end
@@ -755,16 +771,11 @@ get_user_list(_, LUser, LServer, riak) ->
get_user_list(_, LUser, LServer, odbc) ->
case catch sql_get_default_privacy_list(LUser, LServer)
of
- {selected, [<<"name">>], []} -> {none, []};
- {selected, [<<"name">>], [[Default]]} ->
+ {selected, []} -> {none, []};
+ {selected, [{Default}]} ->
case catch sql_get_privacy_list_data(LUser, LServer,
- Default)
- of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
- <<"match_all">>, <<"match_iq">>, <<"match_message">>,
- <<"match_presence_in">>, <<"match_presence_out">>],
- RItems} ->
+ Default) of
+ {selected, RItems} ->
{Default, lists:flatmap(fun raw_to_item/1, RItems)};
_ -> {none, []}
end;
@@ -792,26 +803,21 @@ get_user_lists(LUser, LServer, riak) ->
end;
get_user_lists(LUser, LServer, odbc) ->
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
- {selected, [<<"name">>], []} ->
+ {selected, []} ->
none;
- {selected, [<<"name">>], [[DefName]]} ->
+ {selected, [{DefName}]} ->
DefName;
_ ->
none
end,
case catch sql_get_privacy_list_names(LUser, LServer) of
- {selected, [<<"name">>], Names} ->
+ {selected, Names} ->
Lists =
lists:flatmap(
- fun([Name]) ->
+ fun({Name}) ->
case catch sql_get_privacy_list_data(
LUser, LServer, Name) of
- {selected,
- [<<"t">>, <<"value">>, <<"action">>,
- <<"ord">>, <<"match_all">>, <<"match_iq">>,
- <<"match_message">>, <<"match_presence_in">>,
- <<"match_presence_out">>],
- RItems} ->
+ {selected, RItems} ->
[{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
_ ->
[]
@@ -852,7 +858,7 @@ check_packet(_, User, Server,
<<"message">> -> message;
<<"iq">> -> iq;
<<"presence">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
%% notification
<<"">> -> presence;
<<"unavailable">> -> presence;
@@ -964,9 +970,9 @@ updated_list(_, #userlist{name = OldName} = Old,
true -> Old
end.
-raw_to_item([SType, SValue, SAction, SOrder, SMatchAll,
- SMatchIQ, SMatchMessage, SMatchPresenceIn,
- SMatchPresenceOut] = Row) ->
+raw_to_item({SType, SValue, SAction, Order, MatchAll,
+ MatchIQ, MatchMessage, MatchPresenceIn,
+ MatchPresenceOut} = Row) ->
try
{Type, Value} = case SType of
<<"n">> -> {none, none};
@@ -988,12 +994,6 @@ raw_to_item([SType, SValue, SAction, SOrder, SMatchAll,
<<"a">> -> allow;
<<"d">> -> deny
end,
- Order = jlib:binary_to_integer(SOrder),
- MatchAll = ejabberd_odbc:to_bool(SMatchAll),
- MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
- MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
- MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
- MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
[#listitem{type = Type, value = Value, action = Action,
order = Order, match_all = MatchAll, match_iq = MatchIQ,
match_message = MatchMessage,
@@ -1027,58 +1027,29 @@ item_to_raw(#listitem{type = Type, value = Value,
allow -> <<"a">>;
deny -> <<"d">>
end,
- SOrder = iolist_to_binary(integer_to_list(Order)),
- SMatchAll = if MatchAll -> <<"1">>;
- true -> <<"0">>
- end,
- SMatchIQ = if MatchIQ -> <<"1">>;
- true -> <<"0">>
- end,
- SMatchMessage = if MatchMessage -> <<"1">>;
- true -> <<"0">>
- end,
- SMatchPresenceIn = if MatchPresenceIn -> <<"1">>;
- true -> <<"0">>
- end,
- SMatchPresenceOut = if MatchPresenceOut -> <<"1">>;
- true -> <<"0">>
- end,
- [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
- SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
+ {SType, SValue, SAction, Order, MatchAll, MatchIQ,
+ MatchMessage, MatchPresenceIn, MatchPresenceOut}.
sql_get_default_privacy_list(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_default_privacy_list(LServer,
- Username).
+ odbc_queries:get_default_privacy_list(LServer, LUser).
sql_get_default_privacy_list_t(LUser) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_default_privacy_list_t(Username).
+ odbc_queries:get_default_privacy_list_t(LUser).
sql_get_privacy_list_names(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_privacy_list_names(LServer, Username).
+ odbc_queries:get_privacy_list_names(LServer, LUser).
sql_get_privacy_list_names_t(LUser) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_privacy_list_names_t(Username).
+ odbc_queries:get_privacy_list_names_t(LUser).
sql_get_privacy_list_id(LUser, LServer, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_id(LServer, Username,
- SName).
+ odbc_queries:get_privacy_list_id(LServer, LUser, Name).
sql_get_privacy_list_id_t(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_id_t(Username, SName).
+ odbc_queries:get_privacy_list_id_t(LUser, Name).
sql_get_privacy_list_data(LUser, LServer, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_data(LServer, Username,
- SName).
+ odbc_queries:get_privacy_list_data(LServer, LUser, Name).
sql_get_privacy_list_data_t(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
@@ -1092,33 +1063,22 @@ sql_get_privacy_list_data_by_id_t(ID) ->
odbc_queries:get_privacy_list_data_by_id_t(ID).
sql_set_default_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:set_default_privacy_list(Username, SName).
+ odbc_queries:set_default_privacy_list(LUser, Name).
sql_unset_default_privacy_list(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:unset_default_privacy_list(LServer,
- Username).
+ odbc_queries:unset_default_privacy_list(LServer, LUser).
sql_remove_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:remove_privacy_list(Username, SName).
+ odbc_queries:remove_privacy_list(LUser, Name).
sql_add_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:add_privacy_list(Username, SName).
+ odbc_queries:add_privacy_list(LUser, Name).
sql_set_privacy_list(ID, RItems) ->
odbc_queries:set_privacy_list(ID, RItems).
sql_del_privacy_lists(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- Server = ejabberd_odbc:escape(LServer),
- odbc_queries:del_privacy_lists(LServer, Server,
- Username).
+ odbc_queries:del_privacy_lists(LServer, LUser).
update_table() ->
Fields = record_info(fields, privacy),
diff --git a/src/mod_private.erl b/src/mod_private.erl
index a03d83e5..f3dceeaa 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -33,7 +33,7 @@
-export([start/2, stop/1, process_sm_iq/3, import/3,
remove_user/2, get_data/2, export/1, import/1,
- mod_opt_type/1]).
+ mod_opt_type/1, set_data/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -82,19 +82,7 @@ process_sm_iq(#jid{luser = LUser, lserver = LServer},
IQ#iq{type = error,
sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]};
Data ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- F = fun () ->
- lists:foreach(fun (Datum) ->
- set_data(LUser, LServer,
- Datum, DBType)
- end,
- Data)
- end,
- case DBType of
- odbc -> ejabberd_odbc:sql_transaction(LServer, F);
- mnesia -> mnesia:transaction(F);
- riak -> F()
- end,
+ set_data(LUser, LServer, Data),
IQ#iq{type = result, sub_el = []}
end;
_ ->
@@ -137,23 +125,35 @@ filter_xmlels(Xmlels) -> filter_xmlels(Xmlels, []).
filter_xmlels([], Data) -> lists:reverse(Data);
filter_xmlels([#xmlel{attrs = Attrs} = Xmlel | Xmlels],
Data) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
<<"">> -> [];
XmlNS -> filter_xmlels(Xmlels, [{XmlNS, Xmlel} | Data])
end;
filter_xmlels([_ | Xmlels], Data) ->
filter_xmlels(Xmlels, Data).
+set_data(LUser, LServer, Data) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ F = fun () ->
+ lists:foreach(fun (Datum) ->
+ set_data(LUser, LServer,
+ Datum, DBType)
+ end,
+ Data)
+ end,
+ case DBType of
+ odbc -> ejabberd_odbc:sql_transaction(LServer, F);
+ mnesia -> mnesia:transaction(F);
+ riak -> F()
+ end.
+
set_data(LUser, LServer, {XmlNS, Xmlel}, mnesia) ->
mnesia:write(#private_storage{usns =
{LUser, LServer, XmlNS},
xml = Xmlel});
set_data(LUser, LServer, {XMLNS, El}, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- LXMLNS = ejabberd_odbc:escape(XMLNS),
- SData = ejabberd_odbc:escape(xml:element_to_binary(El)),
- odbc_queries:set_private_data(LServer, Username, LXMLNS,
- SData);
+ SData = fxml:element_to_binary(El),
+ odbc_queries:set_private_data(LServer, LUser, XMLNS, SData);
set_data(LUser, LServer, {XMLNS, El}, riak) ->
ejabberd_riak:put(#private_storage{usns = {LUser, LServer, XMLNS},
xml = El},
@@ -181,13 +181,11 @@ get_data(LUser, LServer, mnesia,
end;
get_data(LUser, LServer, odbc, [{XMLNS, El} | Els],
Res) ->
- Username = ejabberd_odbc:escape(LUser),
- LXMLNS = ejabberd_odbc:escape(XMLNS),
case catch odbc_queries:get_private_data(LServer,
- Username, LXMLNS)
+ LUser, XMLNS)
of
- {selected, [<<"data">>], [[SData]]} ->
- case xml_stream:parse_element(SData) of
+ {selected, [{SData}]} ->
+ case fxml_stream:parse_element(SData) of
Data when is_record(Data, xmlel) ->
get_data(LUser, LServer, odbc, Els, [Data | Res])
end;
@@ -214,12 +212,11 @@ get_all_data(LUser, LServer, mnesia) ->
xml = '$1'},
[], ['$1']}]));
get_all_data(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_private_data(LServer, Username) of
- {selected, [<<"namespace">>, <<"data">>], Res} ->
+ case catch odbc_queries:get_private_data(LServer, LUser) of
+ {selected, Res} ->
lists:flatmap(
- fun([_, SData]) ->
- case xml_stream:parse_element(SData) of
+ fun({_, SData}) ->
+ case fxml_stream:parse_element(SData) of
#xmlel{} = El ->
[El];
_ ->
@@ -266,9 +263,7 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_user_private_storage(LServer,
- Username);
+ odbc_queries:del_user_private_storage(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(private_storage,
<<"us">>, {LUser, LServer})}.
@@ -284,7 +279,7 @@ update_table() ->
R#private_storage{usns = {iolist_to_binary(U),
iolist_to_binary(S),
iolist_to_binary(NS)},
- xml = xml:to_xmlel(El)}
+ xml = fxml:to_xmlel(El)}
end);
_ ->
?INFO_MSG("Recreating private_storage table", []),
@@ -299,7 +294,7 @@ export(_Server) ->
Username = ejabberd_odbc:escape(LUser),
LXMLNS = ejabberd_odbc:escape(XMLNS),
SData =
- ejabberd_odbc:escape(xml:element_to_binary(Data)),
+ ejabberd_odbc:escape(fxml:element_to_binary(Data)),
odbc_queries:set_private_data_sql(Username, LXMLNS,
SData);
(_Host, _R) ->
@@ -309,7 +304,7 @@ export(_Server) ->
import(LServer) ->
[{<<"select username, namespace, data from private_storage;">>,
fun([LUser, XMLNS, XML]) ->
- El = #xmlel{} = xml_stream:parse_element(XML),
+ El = #xmlel{} = fxml_stream:parse_element(XML),
#private_storage{usns = {LUser, LServer, XMLNS},
xml = El}
end}].
diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl
index 97d5b00a..8b4644ba 100644
--- a/src/mod_proxy65_service.erl
+++ b/src/mod_proxy65_service.erl
@@ -63,7 +63,7 @@ start_link(Host, Opts) ->
init([Host, Opts]) ->
State = parse_options(Host, Opts),
- ejabberd_router:register_route(State#state.myhost),
+ ejabberd_router:register_route(State#state.myhost, Host),
{ok, State}.
terminate(_Reason, #state{myhost = MyHost}) ->
@@ -175,11 +175,11 @@ process_iq(InitiatorJID,
#state{acl = ACL, serverhost = ServerHost}) ->
case acl:match_rule(ServerHost, ACL, InitiatorJID) of
allow ->
- ActivateEl = xml:get_path_s(SubEl,
+ ActivateEl = fxml:get_path_s(SubEl,
[{elem, <<"activate">>}]),
- SID = xml:get_tag_attr_s(<<"sid">>, SubEl),
+ SID = fxml:get_tag_attr_s(<<"sid">>, SubEl),
case catch
- jid:from_string(xml:get_tag_cdata(ActivateEl))
+ jid:from_string(fxml:get_tag_cdata(ActivateEl))
of
TargetJID
when is_record(TargetJID, jid), SID /= <<"">>,
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 616c6492..6531ed87 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -63,7 +63,7 @@
%% exports for console debug manual use
-export([create_node/5, create_node/7, delete_node/3,
subscribe_node/5, unsubscribe_node/5, publish_item/6,
- delete_item/4, send_items/7, get_items/2, get_item/3,
+ delete_item/4, delete_item/5, send_items/7, get_items/2, get_item/3,
get_cached_item/2, get_configure/5, set_configure/5,
tree_action/3, node_action/4, node_call/4]).
@@ -241,6 +241,7 @@ stop(Host) ->
init([ServerHost, Opts]) ->
?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
+ ejabberd_router:register_route(Host, ServerHost),
Access = gen_mod:get_opt(access_createnode, Opts,
fun(A) when is_atom(A) -> A end, all),
PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts,
@@ -256,22 +257,26 @@ init([ServerHost, Opts]) ->
DefaultNodeCfg = gen_mod:get_opt(default_node_config, Opts,
fun(A) when is_list(A) -> filter_node_options(A) end, []),
pubsub_index:init(Host, ServerHost, Opts),
- ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
mnesia:create_table(pubsub_last_item,
[{ram_copies, [node()]},
{attributes, record_info(fields, pubsub_last_item)}]),
mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {nodetree, NodeTree}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {plugins, Plugins}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {last_item_cache, LastItemCache}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_items_node, MaxItemsNode}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_subscriptions_node, MaxSubsNode}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {default_node_config, DefaultNodeCfg}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {pep_mapping, PepMapping}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
- ets:insert(gen_mod:get_module_proc(ServerHost, config), {access, Access}),
+ lists:foreach(
+ fun(H) ->
+ T = gen_mod:get_module_proc(H, config),
+ ets:new(T, [set, named_table]),
+ ets:insert(T, {nodetree, NodeTree}),
+ ets:insert(T, {plugins, Plugins}),
+ ets:insert(T, {last_item_cache, LastItemCache}),
+ ets:insert(T, {max_items_node, MaxItemsNode}),
+ ets:insert(T, {max_subscriptions_node, MaxSubsNode}),
+ ets:insert(T, {default_node_config, DefaultNodeCfg}),
+ ets:insert(T, {pep_mapping, PepMapping}),
+ ets:insert(T, {ignore_pep_from_offline, PepOffline}),
+ ets:insert(T, {host, Host}),
+ ets:insert(T, {access, Access})
+ end, [Host, ServerHost]),
ejabberd_hooks:add(sm_remove_connection_hook, ServerHost,
?MODULE, on_user_offline, 75),
ejabberd_hooks:add(disco_local_identity, ServerHost,
@@ -309,7 +314,6 @@ init([ServerHost, Opts]) ->
false ->
ok
end,
- ejabberd_router:register_route(Host),
pubsub_migrate:update_node_database(Host, ServerHost),
pubsub_migrate:update_state_database(Host, ServerHost),
pubsub_migrate:update_lastitem_database(Host, ServerHost),
@@ -482,7 +486,7 @@ send_loop(State) ->
-> [xmlel()]
).
disco_local_identity(Acc, _From, To, <<>>, _Lang) ->
- case lists:member(?PEPNODE, plugins(To#jid.lserver)) of
+ case lists:member(?PEPNODE, plugins(host(To#jid.lserver))) of
true ->
[#xmlel{name = <<"identity">>,
attrs = [{<<"category">>, <<"pubsub">>},
@@ -504,7 +508,7 @@ disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
-> [binary(),...]
).
disco_local_features(Acc, _From, To, <<>>, _Lang) ->
- Host = To#jid.lserver,
+ Host = host(To#jid.lserver),
Feats = case Acc of
{result, I} -> I;
_ -> []
@@ -873,7 +877,6 @@ handle_info(_Info, State) ->
%% @private
terminate(_Reason,
#state{host = Host, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) ->
- ejabberd_router:unregister_route(Host),
case lists:member(?PEPNODE, Plugins) of
true ->
ejabberd_hooks:delete(caps_add, ServerHost,
@@ -918,7 +921,8 @@ terminate(_Reason,
Pid ->
Pid ! stop
end,
- terminate_plugins(Host, ServerHost, Plugins, TreePlugin).
+ terminate_plugins(Host, ServerHost, Plugins, TreePlugin),
+ ejabberd_router:unregister_route(Host).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -951,7 +955,7 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
case jlib:iq_query_info(Packet) of
#iq{type = get, xmlns = ?NS_DISCO_INFO, sub_el = SubEl, lang = Lang} = IQ ->
#xmlel{attrs = QAttrs} = SubEl,
- Node = xml:get_attr_s(<<"node">>, QAttrs),
+ Node = fxml:get_attr_s(<<"node">>, QAttrs),
Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
[],
[ServerHost, ?MODULE, <<>>, <<>>]),
@@ -968,7 +972,7 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
ejabberd_router:route(To, From, Res);
#iq{type = get, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} = IQ ->
#xmlel{attrs = QAttrs} = SubEl,
- Node = xml:get_attr_s(<<"node">>, QAttrs),
+ Node = fxml:get_attr_s(<<"node">>, QAttrs),
Res = case iq_disco_items(Host, Node, From, jlib:rsm_decode(IQ)) of
{result, IQRes} ->
jlib:iq_to_xml(IQ#iq{type = result,
@@ -1022,7 +1026,7 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
ok
end;
<<"message">> ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> ->
ok;
_ ->
@@ -1040,7 +1044,7 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
ok
end;
_ ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<"error">> ->
ok;
<<"result">> ->
@@ -1236,7 +1240,7 @@ iq_get_vcard(Lang) ->
).
iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang) ->
- iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, all, plugins(ServerHost)).
+ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, all, plugins(Host)).
-spec(iq_pubsub/8 ::
(
@@ -1255,16 +1259,16 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang) ->
iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
#xmlel{children = SubEls} = SubEl,
- case xml:remove_cdata(SubEls) of
+ case fxml:remove_cdata(SubEls) of
[#xmlel{name = Name, attrs = Attrs, children = Els} | Rest] ->
- Node = xml:get_attr_s(<<"node">>, Attrs),
+ Node = fxml:get_attr_s(<<"node">>, Attrs),
case {IQType, Name} of
{set, <<"create">>} ->
Config = case Rest of
[#xmlel{name = <<"configure">>, children = C}] -> C;
_ -> []
end,
- Type = case xml:get_attr_s(<<"type">>, Attrs) of
+ Type = case fxml:get_attr_s(<<"type">>, Attrs) of
<<>> -> hd(Plugins);
T -> T
end,
@@ -1276,10 +1280,10 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
create_node(Host, ServerHost, Node, From, Type, Access, Config)
end;
{set, <<"publish">>} ->
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = <<"item">>, attrs = ItemAttrs,
children = Payload}] ->
- ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
+ ItemId = fxml:get_attr_s(<<"id">>, ItemAttrs),
publish_item(Host, ServerHost, Node, From, ItemId, Payload, Access);
[] ->
{error,
@@ -1289,14 +1293,14 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)}
end;
{set, <<"retract">>} ->
- ForceNotify = case xml:get_attr_s(<<"notify">>, Attrs) of
+ ForceNotify = case fxml:get_attr_s(<<"notify">>, Attrs) of
<<"1">> -> true;
<<"true">> -> true;
_ -> false
end,
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = <<"item">>, attrs = ItemAttrs}] ->
- ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
+ ItemId = fxml:get_attr_s(<<"id">>, ItemAttrs),
delete_item(Host, Node, From, ItemId, ForceNotify);
_ ->
{error,
@@ -1307,37 +1311,37 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
[#xmlel{name = <<"options">>, children = C}] -> C;
_ -> []
end,
- JID = xml:get_attr_s(<<"jid">>, Attrs),
+ JID = fxml:get_attr_s(<<"jid">>, Attrs),
subscribe_node(Host, Node, From, JID, Config);
{set, <<"unsubscribe">>} ->
- JID = xml:get_attr_s(<<"jid">>, Attrs),
- SubId = xml:get_attr_s(<<"subid">>, Attrs),
+ JID = fxml:get_attr_s(<<"jid">>, Attrs),
+ SubId = fxml:get_attr_s(<<"subid">>, Attrs),
unsubscribe_node(Host, Node, From, JID, SubId);
{get, <<"items">>} ->
- MaxItems = xml:get_attr_s(<<"max_items">>, Attrs),
- SubId = xml:get_attr_s(<<"subid">>, Attrs),
+ MaxItems = fxml:get_attr_s(<<"max_items">>, Attrs),
+ SubId = fxml:get_attr_s(<<"subid">>, Attrs),
ItemIds = lists:foldl(fun
(#xmlel{name = <<"item">>, attrs = ItemAttrs}, Acc) ->
- case xml:get_attr_s(<<"id">>, ItemAttrs) of
+ case fxml:get_attr_s(<<"id">>, ItemAttrs) of
<<>> -> Acc;
ItemId -> [ItemId | Acc]
end;
(_, Acc) ->
Acc
end,
- [], xml:remove_cdata(Els)),
+ [], fxml:remove_cdata(Els)),
get_items(Host, Node, From, SubId, MaxItems, ItemIds, jlib:rsm_decode(SubEl));
{get, <<"subscriptions">>} ->
get_subscriptions(Host, Node, From, Plugins);
{get, <<"affiliations">>} ->
get_affiliations(Host, Node, From, Plugins);
{get, <<"options">>} ->
- SubId = xml:get_attr_s(<<"subid">>, Attrs),
- JID = xml:get_attr_s(<<"jid">>, Attrs),
+ SubId = fxml:get_attr_s(<<"subid">>, Attrs),
+ JID = fxml:get_attr_s(<<"jid">>, Attrs),
get_options(Host, Node, JID, SubId, Lang);
{set, <<"options">>} ->
- SubId = xml:get_attr_s(<<"subid">>, Attrs),
- JID = xml:get_attr_s(<<"jid">>, Attrs),
+ SubId = fxml:get_attr_s(<<"subid">>, Attrs),
+ JID = fxml:get_attr_s(<<"jid">>, Attrs),
set_options(Host, Node, JID, SubId, Els);
_ ->
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
@@ -1362,10 +1366,10 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
).
iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
#xmlel{children = SubEls} = SubEl,
- Action = xml:remove_cdata(SubEls),
+ Action = fxml:remove_cdata(SubEls),
case Action of
[#xmlel{name = Name, attrs = Attrs, children = Els}] ->
- Node = xml:get_attr_s(<<"node">>, Attrs),
+ Node = fxml:get_attr_s(<<"node">>, Attrs),
case {IQType, Name} of
{get, <<"configure">>} ->
get_configure(Host, ServerHost, Node, From, Lang);
@@ -1380,11 +1384,11 @@ iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
{get, <<"subscriptions">>} ->
get_subscriptions(Host, Node, From);
{set, <<"subscriptions">>} ->
- set_subscriptions(Host, Node, From, xml:remove_cdata(Els));
+ set_subscriptions(Host, Node, From, fxml:remove_cdata(Els));
{get, <<"affiliations">>} ->
get_affiliations(Host, Node, From);
{set, <<"affiliations">>} ->
- set_affiliations(Host, Node, From, xml:remove_cdata(Els));
+ set_affiliations(Host, Node, From, fxml:remove_cdata(Els));
_ ->
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
end;
@@ -1597,9 +1601,9 @@ find_authorization_response(Packet) ->
#xmlel{children = Els} = Packet,
XData1 = lists:map(fun
(#xmlel{name = <<"x">>, attrs = XAttrs} = XEl) ->
- case xml:get_attr_s(<<"xmlns">>, XAttrs) of
+ case fxml:get_attr_s(<<"xmlns">>, XAttrs) of
?NS_XDATA ->
- case xml:get_attr_s(<<"type">>, XAttrs) of
+ case fxml:get_attr_s(<<"type">>, XAttrs) of
<<"cancel">> -> none;
_ -> jlib:parse_xdata_submit(XEl)
end;
@@ -1609,7 +1613,7 @@ find_authorization_response(Packet) ->
(_) ->
none
end,
- xml:remove_cdata(Els)),
+ fxml:remove_cdata(Els)),
XData = lists:filter(fun (E) -> E /= none end, XData1),
case XData of
[invalid] ->
@@ -1775,6 +1779,20 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
%%<li>nodetree create_node checks if nodeid already exists</li>
%%<li>node plugin create_node just sets default affiliation/subscription</li>
%%</ul>
+-spec(create_node/5 ::
+ (
+ Host :: mod_pubsub:host(),
+ ServerHost :: binary(),
+ Node :: <<>> | mod_pubsub:nodeId(),
+ Owner :: jid(),
+ Type :: binary())
+ -> {result, [xmlel(),...]}
+ %%%
+ | {error, xmlel()}
+ ).
+create_node(Host, ServerHost, Node, Owner, Type) ->
+ create_node(Host, ServerHost, Node, Owner, Type, all, []).
+
-spec(create_node/7 ::
(
Host :: mod_pubsub:host(),
@@ -1788,8 +1806,6 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
%%%
| {error, xmlel()}
).
-create_node(Host, ServerHost, Node, Owner, Type) ->
- create_node(Host, ServerHost, Node, Owner, Type, all, []).
create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
case lists:member(<<"instant-nodes">>, plugin_features(Host, Type)) of
true ->
@@ -1808,7 +1824,7 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
end;
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Type = select_type(ServerHost, Host, Node, GivenType),
- ParseOptions = case xml:remove_cdata(Configuration) of
+ ParseOptions = case fxml:remove_cdata(Configuration) of
[] ->
{result, node_options(Host, Type)};
[#xmlel{name = <<"x">>} = XEl] ->
@@ -2668,8 +2684,8 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
(El, Acc) ->
case El of
#xmlel{name = <<"affiliation">>, attrs = Attrs} ->
- JID = jid:from_string(xml:get_attr_s(<<"jid">>, Attrs)),
- Affiliation = string_to_affiliation(xml:get_attr_s(<<"affiliation">>, Attrs)),
+ JID = jid:from_string(fxml:get_attr_s(<<"jid">>, Attrs)),
+ Affiliation = string_to_affiliation(fxml:get_attr_s(<<"affiliation">>, Attrs)),
if (JID == error) or (Affiliation == false) -> error;
true -> [{jid:tolower(JID), Affiliation} | Acc]
end
@@ -2998,9 +3014,9 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
(El, Acc) ->
case El of
#xmlel{name = <<"subscription">>, attrs = Attrs} ->
- JID = jid:from_string(xml:get_attr_s(<<"jid">>, Attrs)),
- Sub = string_to_subscription(xml:get_attr_s(<<"subscription">>, Attrs)),
- SubId = xml:get_attr_s(<<"subid">>, Attrs),
+ JID = jid:from_string(fxml:get_attr_s(<<"jid">>, Attrs)),
+ Sub = string_to_subscription(fxml:get_attr_s(<<"subscription">>, Attrs)),
+ SubId = fxml:get_attr_s(<<"subid">>, Attrs),
if (JID == error) or (Sub == false) -> error;
true -> [{jid:tolower(JID), Sub, SubId} | Acc]
end
@@ -3170,17 +3186,15 @@ sub_option_can_deliver(_, _, _) -> true.
presence_can_deliver(_, false) ->
true;
presence_can_deliver({User, Server, Resource}, true) ->
- case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
+ case ejabberd_sm:get_user_present_resources(User, Server) of
[] ->
false;
Ss ->
lists:foldl(fun
(_, true) ->
true;
- ({session, _, _, _, undefined, _}, _Acc) ->
- false;
- ({session, {_, _, R}, _, _, _Priority, _}, _Acc) ->
- case Resource of
+ ({_, R}, _Acc) ->
+ case Resource of
<<>> -> true;
R -> true;
_ -> false
@@ -3294,9 +3308,14 @@ broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payloa
true -> Payload;
false -> []
end,
+ Attrs = case get_option(NodeOptions, itemreply, none) of
+ owner -> itemAttr(ItemId); %% owner not supported
+ publisher -> itemAttr(ItemId, {<<"publisher">>, jid:to_string(From)});
+ none -> itemAttr(ItemId)
+ end,
Stanza = event_stanza(
[#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
- children = [#xmlel{name = <<"item">>, attrs = itemAttr(ItemId),
+ children = [#xmlel{name = <<"item">>, attrs = Attrs,
children = Content}]}]),
broadcast_stanza(Host, From, Node, Nidx, Type,
NodeOptions, SubsByDepth, items, Stanza, true),
@@ -3626,7 +3645,7 @@ get_option(Options, Var, Def) ->
end.
node_options(Host, Type) ->
- case config(serverhost(Host), default_node_config) of
+ case config(Host, default_node_config) of
undefined -> node_plugin_options(Host, Type);
[] -> node_plugin_options(Host, Type);
Config -> Config
@@ -3777,7 +3796,9 @@ get_configure_xfields(_Type, Options, Lang, Groups) ->
?BOOL_CONFIG_FIELD(<<"Only deliver notifications to available users">>,
presence_based_delivery),
?NLIST_CONFIG_FIELD(<<"The collections with which a node is affiliated">>,
- collection)].
+ collection),
+ ?ALIST_CONFIG_FIELD(<<"Whether owners or publisher should receive replies to items">>,
+ itemreply, [none, owner, publisher])].
%%<p>There are several reasons why the node configuration request might fail:</p>
%%<ul>
@@ -3788,9 +3809,9 @@ get_configure_xfields(_Type, Options, Lang, Groups) ->
%%<li>The specified node does not exist.</li>
%%</ul>
set_configure(Host, Node, From, Els, Lang) ->
- case xml:remove_cdata(Els) of
+ case fxml:remove_cdata(Els) of
[#xmlel{name = <<"x">>} = XEl] ->
- case {xml:get_tag_attr_s(<<"xmlns">>, XEl), xml:get_tag_attr_s(<<"type">>, XEl)} of
+ case {fxml:get_tag_attr_s(<<"xmlns">>, XEl), fxml:get_tag_attr_s(<<"type">>, XEl)} of
{?NS_XDATA, <<"cancel">>} ->
{result, []};
{?NS_XDATA, <<"submit">>} ->
@@ -3931,25 +3952,21 @@ set_xoption(Host, [{<<"pubsub#collection">>, Value} | Opts], NewOpts) ->
set_xoption(Host, [{<<"pubsub#node">>, [Value]} | Opts], NewOpts) ->
% NewValue = string_to_node(Value),
?SET_LIST_XOPT(node, Value);
+set_xoption(Host, [{<<"pubsub#itemreply">>, [Val]} | Opts], NewOpts) ->
+ ?SET_ALIST_XOPT(itemreply, Val, [none, owner, publisher]);
set_xoption(Host, [_ | Opts], NewOpts) ->
set_xoption(Host, Opts, NewOpts).
-get_max_items_node({_, ServerHost, _}) ->
- get_max_items_node(ServerHost);
get_max_items_node(Host) ->
- config(serverhost(Host), max_items_node, undefined).
+ config(Host, max_items_node, undefined).
-get_max_subscriptions_node({_, ServerHost, _}) ->
- get_max_subscriptions_node(ServerHost);
get_max_subscriptions_node(Host) ->
- config(serverhost(Host), max_subscriptions_node, undefined).
+ config(Host, max_subscriptions_node, undefined).
%%%% last item cache handling
-is_last_item_cache_enabled({_, ServerHost, _}) ->
- is_last_item_cache_enabled(ServerHost);
is_last_item_cache_enabled(Host) ->
- config(serverhost(Host), last_item_cache, false).
+ config(Host, last_item_cache, false).
set_cached_item({_, ServerHost, _}, Nidx, ItemId, Publisher, Payload) ->
set_cached_item(ServerHost, Nidx, ItemId, Publisher, Payload);
@@ -3999,19 +4016,13 @@ get_cached_item(Host, Nidx) ->
host(ServerHost) ->
config(ServerHost, host, <<"pubsub.", ServerHost/binary>>).
-serverhost({_U, Server, _R})->
- Server;
+serverhost({_U, ServerHost, _R})->
+ serverhost(ServerHost);
serverhost(Host) ->
- case binary:match(Host, <<"pubsub.">>) of
- {0,7} ->
- [_,ServerHost] = binary:split(Host, <<".">>),
- ServerHost;
- _ ->
- Host
- end.
+ ejabberd_router:host_of_route(Host).
tree(Host) ->
- case config(serverhost(Host), nodetree) of
+ case config(Host, nodetree) of
undefined -> tree(Host, ?STDTREE);
Tree -> Tree
end.
@@ -4033,7 +4044,7 @@ plugin(Host, Name) ->
end.
plugins(Host) ->
- case config(serverhost(Host), plugins) of
+ case config(Host, plugins) of
undefined -> [?STDNODE];
[] -> [?STDNODE];
Plugins -> Plugins
@@ -4048,6 +4059,9 @@ subscription_plugin(Host) ->
config(ServerHost, Key) ->
config(ServerHost, Key, undefined).
+
+config({_User, Host, _Resource}, Key, Default) ->
+ config(Host, Key, Default);
config(ServerHost, Key, Default) ->
case catch ets:lookup(gen_mod:get_module_proc(ServerHost, config), Key) of
[{Key, Value}] -> Value;
@@ -4064,14 +4078,14 @@ select_type(ServerHost, Host, Node, Type) ->
_ ->
Type
end,
- ConfiguredTypes = plugins(ServerHost),
+ ConfiguredTypes = plugins(Host),
case lists:member(SelectedType, ConfiguredTypes) of
true -> SelectedType;
false -> hd(ConfiguredTypes)
end.
select_type(ServerHost, Host, Node) ->
- select_type(ServerHost, Host, Node, hd(plugins(ServerHost))).
+ select_type(ServerHost, Host, Node, hd(plugins(Host))).
feature(<<"rsm">>) -> ?NS_RSM;
feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>.
@@ -4273,6 +4287,7 @@ nodeAttr(Node) -> [{<<"node">>, Node}].
itemAttr([]) -> [];
itemAttr(ItemId) -> [{<<"id">>, ItemId}].
+itemAttr(ItemId, From) -> [{<<"id">>, ItemId}, From].
itemsEls(Items) ->
[#xmlel{name = <<"item">>, attrs = itemAttr(ItemId), children = Payload}
diff --git a/src/mod_register.erl b/src/mod_register.erl
index c7bfd963..56c5f720 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -121,9 +121,9 @@ process_iq(From, To,
end,
case Type of
set ->
- UTag = xml:get_subtag(SubEl, <<"username">>),
- PTag = xml:get_subtag(SubEl, <<"password">>),
- RTag = xml:get_subtag(SubEl, <<"remove">>),
+ UTag = fxml:get_subtag(SubEl, <<"username">>),
+ PTag = fxml:get_subtag(SubEl, <<"password">>),
+ RTag = fxml:get_subtag(SubEl, <<"remove">>),
Server = To#jid.lserver,
Access = gen_mod:get_module_opt(Server, ?MODULE, access,
fun(A) when is_atom(A) -> A end,
@@ -132,14 +132,14 @@ process_iq(From, To,
acl:match_rule(Server, Access, From),
if (UTag /= false) and (RTag /= false) and
AllowRemove ->
- User = xml:get_tag_cdata(UTag),
+ User = fxml:get_tag_cdata(UTag),
case From of
#jid{user = User, lserver = Server} ->
ejabberd_auth:remove_user(User, Server),
IQ#iq{type = result, sub_el = []};
_ ->
if PTag /= false ->
- Password = xml:get_tag_cdata(PTag),
+ Password = fxml:get_tag_cdata(PTag),
case ejabberd_auth:remove_user(User, Server,
Password)
of
@@ -185,8 +185,8 @@ process_iq(From, To,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
end;
(UTag /= false) and (PTag /= false) ->
- User = xml:get_tag_cdata(UTag),
- Password = xml:get_tag_cdata(PTag),
+ User = fxml:get_tag_cdata(UTag),
+ Password = fxml:get_tag_cdata(PTag),
try_register_or_set_password(User, Server, Password,
From, IQ, SubEl, Source, Lang,
not IsCaptchaEnabled);
@@ -599,7 +599,7 @@ write_time({{Y, Mo, D}, {H, Mi, S}}) ->
[Y, Mo, D, H, Mi, S]).
process_xdata_submit(El) ->
- case xml:get_subtag(El, <<"x">>) of
+ case fxml:get_subtag(El, <<"x">>) of
false -> error;
Xdata ->
Fields = jlib:parse_xdata_submit(Xdata),
diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl
index 35f52f3b..5b7f950a 100644
--- a/src/mod_register_web.erl
+++ b/src/mod_register_web.erl
@@ -113,8 +113,8 @@ process([<<"new">>],
end;
process([<<"delete">>],
#request{method = 'POST', q = Q, lang = Lang,
- host = Host}) ->
- case form_del_post(Q, Host) of
+ host = _HTTPHost}) ->
+ case form_del_post(Q) of
{atomic, ok} ->
Text = (?T(<<"Your Jabber account was successfully "
"deleted.">>)),
@@ -129,8 +129,8 @@ process([<<"delete">>],
%% should include the host where the POST was sent.
process([<<"change_password">>],
#request{method = 'POST', q = Q, lang = Lang,
- host = Host}) ->
- case form_changepass_post(Q, Host) of
+ host = _HTTPHost}) ->
+ case form_changepass_post(Q) of
{atomic, ok} ->
Text = (?T(<<"The password of your Jabber account "
"was successfully changed.">>)),
@@ -282,9 +282,9 @@ form_new_post(Q) ->
case catch get_register_parameters(Q) of
[Username, Host, Password, Password, Id, Key] ->
form_new_post(Username, Host, Password, {Id, Key});
- [_Username, _Password, _Password2, false, false] ->
+ [_Username, _Host, _Password, _Password2, false, false] ->
{error, passwords_not_identical};
- [_Username, _Password, _Password2, Id, Key] ->
+ [_Username, _Host, _Password, _Password2, Id, Key] ->
ejabberd_captcha:check_captcha(Id, Key),
{error, passwords_not_identical};
_ -> {error, wrong_parameters}
@@ -361,7 +361,8 @@ form_changepass_get(Host, Lang) ->
?INPUTS(<<"text">>, <<"username">>, <<"">>,
<<"20">>)]),
?XE(<<"li">>,
- [?CT(<<"Server:">>), ?C(<<" ">>), ?C(Host)]),
+ [?CT(<<"Server:">>), ?C(<<" ">>),
+ ?INPUTS(<<"text">>, <<"host">>, Host, <<"20">>)]),
?XE(<<"li">>,
[?CT(<<"Old Password:">>), ?C(<<" ">>),
?INPUTS(<<"password">>, <<"passwordold">>, <<"">>,
@@ -386,12 +387,12 @@ form_changepass_get(Host, Lang) ->
%%% Formulary change password POST
%%%----------------------------------------------------------------------
-form_changepass_post(Q, Host) ->
+form_changepass_post(Q) ->
case catch get_changepass_parameters(Q) of
- [Username, PasswordOld, Password, Password] ->
+ [Username, Host, PasswordOld, Password, Password] ->
try_change_password(Username, Host, PasswordOld,
Password);
- [_Username, _PasswordOld, _Password, _Password2] ->
+ [_Username, _Host, _PasswordOld, _Password, _Password2] ->
{error, passwords_not_identical};
_ -> {error, wrong_parameters}
end.
@@ -405,7 +406,7 @@ get_changepass_parameters(Q) ->
{value, {_Key, Value}} = lists:keysearch(Key, 1, Q),
Value
end,
- [<<"username">>, <<"passwordold">>, <<"password">>,
+ [<<"username">>, <<"host">>, <<"passwordold">>, <<"password">>,
<<"password2">>]).
try_change_password(Username, Host, PasswordOld,
@@ -470,7 +471,8 @@ form_del_get(Host, Lang) ->
?INPUTS(<<"text">>, <<"username">>, <<"">>,
<<"20">>)]),
?XE(<<"li">>,
- [?CT(<<"Server:">>), ?C(<<" ">>), ?C(Host)]),
+ [?CT(<<"Server:">>), ?C(<<" ">>),
+ ?INPUTS(<<"text">>, <<"host">>, Host, <<"20">>)]),
?XE(<<"li">>,
[?CT(<<"Password:">>), ?C(<<" ">>),
?INPUTS(<<"password">>, <<"password">>, <<"">>,
@@ -513,9 +515,9 @@ register_account2(Username, Host, Password) ->
%%% Formulary delete POST
%%%----------------------------------------------------------------------
-form_del_post(Q, Host) ->
+form_del_post(Q) ->
case catch get_unregister_parameters(Q) of
- [Username, Password] ->
+ [Username, Host, Password] ->
try_unregister_account(Username, Host, Password);
_ -> {error, wrong_parameters}
end.
@@ -529,7 +531,7 @@ get_unregister_parameters(Q) ->
{value, {_Key, Value}} = lists:keysearch(Key, 1, Q),
Value
end,
- [<<"username">>, <<"password">>]).
+ [<<"username">>, <<"host">>, <<"password">>]).
try_unregister_account(Username, Host, Password) ->
try unregister_account(Username, Host, Password) of
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index adc2210d..997544b1 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -50,7 +50,7 @@
webadmin_user/4, get_versioning_feature/2,
roster_versioning_enabled/1, roster_version/2,
record_to_string/1, groups_to_string/1,
- mod_opt_type/1]).
+ mod_opt_type/1, set_roster/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -203,11 +203,9 @@ read_roster_version(LUser, LServer, mnesia) ->
[] -> error
end;
read_roster_version(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case odbc_queries:get_roster_version(LServer, Username)
- of
- {selected, [<<"version">>], [[Version]]} -> Version;
- {selected, [<<"version">>], []} -> error
+ case odbc_queries:get_roster_version(LServer, LUser) of
+ {selected, [{Version}]} -> Version;
+ {selected, []} -> error
end;
read_roster_version(LServer, LUser, riak) ->
case ejabberd_riak:get(roster_version, roster_version_schema(),
@@ -267,7 +265,7 @@ process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
LServer = From#jid.lserver,
US = {LUser, LServer},
try {ItemsToSend, VersionToSend} = case
- {xml:get_tag_attr(<<"ver">>, SubEl),
+ {fxml:get_tag_attr(<<"ver">>, SubEl),
roster_versioning_enabled(LServer),
roster_version_on_db(LServer)}
of
@@ -369,48 +367,46 @@ get_roster(LUser, LServer, riak) ->
_Err -> []
end;
get_roster(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- Items}
- when is_list(Items) ->
- JIDGroups = case catch
- odbc_queries:get_roster_jid_groups(LServer,
- Username)
- of
- {selected, [<<"jid">>, <<"grp">>], JGrps}
- when is_list(JGrps) ->
- JGrps;
- _ -> []
- end,
- GroupsDict = lists:foldl(fun ([J, G], Acc) ->
- dict:append(J, G, Acc)
- end,
- dict:new(), JIDGroups),
- RItems = lists:flatmap(fun (I) ->
- case raw_to_record(LServer, I) of
- %% Bad JID in database:
- error -> [];
- R ->
- SJID =
- jid:to_string(R#roster.jid),
- Groups = case dict:find(SJID,
- GroupsDict)
- of
- {ok, Gs} -> Gs;
- error -> []
- end,
- [R#roster{groups = Groups}]
- end
- end,
- Items),
- RItems;
- _ -> []
+ case catch odbc_queries:get_roster(LServer, LUser) of
+ {selected, Items} when is_list(Items) ->
+ JIDGroups = case catch odbc_queries:get_roster_jid_groups(
+ LServer, LUser) of
+ {selected, JGrps}
+ when is_list(JGrps) ->
+ JGrps;
+ _ -> []
+ end,
+ GroupsDict = lists:foldl(fun({J, G}, Acc) ->
+ dict:append(J, G, Acc)
+ end,
+ dict:new(), JIDGroups),
+ RItems =
+ lists:flatmap(
+ fun(I) ->
+ case raw_to_record(LServer, I) of
+ %% Bad JID in database:
+ error -> [];
+ R ->
+ SJID = jid:to_string(R#roster.jid),
+ Groups = case dict:find(SJID, GroupsDict) of
+ {ok, Gs} -> Gs;
+ error -> []
+ end,
+ [R#roster{groups = Groups}]
+ end
+ end,
+ Items),
+ RItems;
+ _ -> []
end.
+set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
+ transaction(
+ LServer,
+ fun() ->
+ roster_subscribe_t(LUser, LServer, LJID, Item)
+ end).
+
item_to_xml(Item) ->
Attrs1 = [{<<"jid">>,
jid:to_string(Item#roster.jid)}],
@@ -453,14 +449,8 @@ get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
xs = []}
end;
get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- Res} =
- odbc_queries:get_roster_by_jid(LServer, Username, SJID),
+ {selected, Res} =
+ odbc_queries:get_roster_by_jid(LServer, LUser, jid:to_string(LJID)),
case Res of
[] ->
#roster{usj = {LUser, LServer, LJID},
@@ -509,7 +499,7 @@ process_iq_set(From, To, #iq{sub_el = SubEl, id = Id} = IQ) ->
process_item_set(From, To,
#xmlel{attrs = Attrs, children = Els}, Managed) ->
- JID1 = jid:from_string(xml:get_attr_s(<<"jid">>,
+ JID1 = jid:from_string(fxml:get_attr_s(<<"jid">>,
Attrs)),
#jid{user = User, luser = LUser, lserver = LServer} =
From,
@@ -578,10 +568,10 @@ process_item_els(Item,
| Els]) ->
case Name of
<<"group">> ->
- Groups = [xml:get_cdata(SEls) | Item#roster.groups],
+ Groups = [fxml:get_cdata(SEls) | Item#roster.groups],
process_item_els(Item#roster{groups = Groups}, Els);
_ ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
<<"">> -> process_item_els(Item, Els);
_ ->
XEls = [#xmlel{name = Name, attrs = Attrs,
@@ -655,14 +645,8 @@ get_subscription_lists(_, LUser, LServer, mnesia) ->
_ -> []
end;
get_subscription_lists(_, LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- Items}
- when is_list(Items) ->
+ case catch odbc_queries:get_roster(LServer, LUser) of
+ {selected, Items} when is_list(Items) ->
lists:map(fun(I) -> raw_to_record(LServer, I) end, Items);
_ -> []
end;
@@ -704,12 +688,9 @@ roster_subscribe_t(LUser, LServer, LJID, Item) ->
roster_subscribe_t(_LUser, _LServer, _LJID, Item,
mnesia) ->
mnesia:write(Item);
-roster_subscribe_t(LUser, LServer, LJID, Item, odbc) ->
- ItemVals = record_to_string(Item),
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- odbc_queries:roster_subscribe(LServer, Username, SJID,
- ItemVals);
+roster_subscribe_t(_LUser, _LServer, _LJID, Item, odbc) ->
+ ItemVals = record_to_row(Item),
+ odbc_queries:roster_subscribe(ItemVals);
roster_subscribe_t(LUser, LServer, _LJID, Item, riak) ->
ejabberd_riak:put(Item, roster_schema(),
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
@@ -743,30 +724,18 @@ get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
end;
get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- case odbc_queries:get_roster_by_jid(LServer, Username,
- SJID)
- of
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- [I]} ->
- R = raw_to_record(LServer, I),
- Groups = case odbc_queries:get_roster_groups(LServer,
- Username, SJID)
- of
- {selected, [<<"grp">>], JGrps} when is_list(JGrps) ->
- [JGrp || [JGrp] <- JGrps];
- _ -> []
- end,
- R#roster{groups = Groups};
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- []} ->
+ SJID = jid:to_string(LJID),
+ case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
+ {selected, [I]} ->
+ R = raw_to_record(LServer, I),
+ Groups =
+ case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
+ {selected, JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
+ _ -> []
+ end,
+ R#roster{groups = Groups};
+ {selected, []} ->
#roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer}, jid = LJID}
end;
@@ -988,8 +957,7 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_user_roster_t(LServer, Username),
+ odbc_queries:del_user_roster_t(LServer, LUser),
ok;
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
@@ -1057,11 +1025,10 @@ update_roster_t(_LUser, _LServer, _LJID, Item,
mnesia) ->
mnesia:write(Item);
update_roster_t(LUser, LServer, LJID, Item, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- ItemVals = record_to_string(Item),
- ItemGroups = groups_to_string(Item),
- odbc_queries:update_roster(LServer, Username, SJID, ItemVals,
+ SJID = jid:to_string(LJID),
+ ItemVals = record_to_row(Item),
+ ItemGroups = Item#roster.groups,
+ odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
ItemGroups);
update_roster_t(LUser, LServer, _LJID, Item, riak) ->
ejabberd_riak:put(Item, roster_schema(),
@@ -1074,15 +1041,14 @@ del_roster_t(LUser, LServer, LJID) ->
del_roster_t(LUser, LServer, LJID, mnesia) ->
mnesia:delete({roster, {LUser, LServer, LJID}});
del_roster_t(LUser, LServer, LJID, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- odbc_queries:del_roster(LServer, Username, SJID);
+ SJID = jid:to_string(LJID),
+ odbc_queries:del_roster(LServer, LUser, SJID);
del_roster_t(LUser, LServer, LJID, riak) ->
ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
process_item_set_t(LUser, LServer,
#xmlel{attrs = Attrs, children = Els}) ->
- JID1 = jid:from_string(xml:get_attr_s(<<"jid">>,
+ JID1 = jid:from_string(fxml:get_attr_s(<<"jid">>,
Attrs)),
case JID1 of
error -> ok;
@@ -1177,14 +1143,8 @@ get_in_pending_subscriptions(Ls, User, Server, odbc) ->
JID = jid:make(User, Server, <<"">>),
LUser = JID#jid.luser,
LServer = JID#jid.lserver,
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- Items}
- when is_list(Items) ->
+ case catch odbc_queries:get_roster(LServer, LUser) of
+ {selected, Items} when is_list(Items) ->
Ls ++
lists:map(fun (R) ->
Message = R#roster.askmessage,
@@ -1236,12 +1196,9 @@ read_subscription_and_groups(LUser, LServer, LJID,
end;
read_subscription_and_groups(LUser, LServer, LJID,
odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- case catch odbc_queries:get_subscription(LServer,
- Username, SJID)
- of
- {selected, [<<"subscription">>], [[SSubscription]]} ->
+ SJID = jid:to_string(LJID),
+ case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
+ {selected, [{SSubscription}]} ->
Subscription = case SSubscription of
<<"B">> -> both;
<<"T">> -> to;
@@ -1249,11 +1206,11 @@ read_subscription_and_groups(LUser, LServer, LJID,
_ -> none
end,
Groups = case catch
- odbc_queries:get_rostergroup_by_jid(LServer, Username,
+ odbc_queries:get_rostergroup_by_jid(LServer, LUser,
SJID)
of
- {selected, [<<"grp">>], JGrps} when is_list(JGrps) ->
- [JGrp || [JGrp] <- JGrps];
+ {selected, JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
_ -> []
end,
{Subscription, Groups};
@@ -1290,6 +1247,12 @@ get_jid_info(_, User, Server, JID) ->
raw_to_record(LServer,
[User, SJID, Nick, SSubscription, SAsk, SAskMessage,
_SServer, _SSubscribe, _SType]) ->
+ raw_to_record(LServer,
+ {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
+ _SServer, _SSubscribe, _SType});
+raw_to_record(LServer,
+ {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
+ _SServer, _SSubscribe, _SType}) ->
case jid:from_string(SJID) of
error -> error;
JID ->
@@ -1339,6 +1302,27 @@ record_to_string(#roster{us = {User, _Server},
[Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
<<"N">>, <<"">>, <<"item">>].
+record_to_row(
+ #roster{us = {LUser, _LServer},
+ jid = JID, name = Name, subscription = Subscription,
+ ask = Ask, askmessage = AskMessage}) ->
+ SJID = jid:to_string(jid:tolower(JID)),
+ SSubscription = case Subscription of
+ both -> <<"B">>;
+ to -> <<"T">>;
+ from -> <<"F">>;
+ none -> <<"N">>
+ end,
+ SAsk = case Ask of
+ subscribe -> <<"S">>;
+ unsubscribe -> <<"U">>;
+ both -> <<"B">>;
+ out -> <<"O">>;
+ in -> <<"I">>;
+ none -> <<"N">>
+ end,
+ {LUser, SJID, Name, SSubscription, SAsk, AskMessage}.
+
groups_to_string(#roster{us = {User, _Server},
jid = JID, groups = Groups}) ->
Username = ejabberd_odbc:escape(User),
@@ -1383,7 +1367,7 @@ update_roster_table() ->
groups = [iolist_to_binary(G) || G <- Gs],
askmessage = try iolist_to_binary(Ask)
catch _:_ -> <<"">> end,
- xs = [xml:to_xmlel(X) || X <- Xs]}
+ xs = [fxml:to_xmlel(X) || X <- Xs]}
end);
_ ->
?INFO_MSG("Recreating roster table", []),
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index cde0f66b..212a7d47 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -206,11 +206,11 @@ get_rosteritem_name([ModVcard], U, S) ->
get_rosteritem_name_vcard([]) -> <<"">>;
get_rosteritem_name_vcard([Vcard]) ->
- case xml:get_path_s(Vcard,
+ case fxml:get_path_s(Vcard,
[{elem, <<"NICKNAME">>}, cdata])
of
<<"">> ->
- xml:get_path_s(Vcard, [{elem, <<"FN">>}, cdata]);
+ fxml:get_path_s(Vcard, [{elem, <<"FN">>}, cdata]);
Nickname -> Nickname
end.
@@ -1160,7 +1160,7 @@ list_shared_roster_groups(Host, Query, Lang) ->
[?INPUTT(<<"submit">>, <<"addnew">>,
<<"Add New">>)])])]))])),
(?H1GL((?T(<<"Shared Roster Groups">>)),
- <<"modsharedroster">>, <<"mod_shared_roster">>))
+ <<"mod_shared_roster">>, <<"mod_shared_roster">>))
++
case Res of
ok -> [?XREST(<<"Submitted">>)];
@@ -1254,7 +1254,7 @@ shared_roster_group(Host, Group, Query, Lang) ->
<<"20">>,
list_to_binary(FDisplayedGroups))])])])])),
(?H1GL((?T(<<"Shared Roster Groups">>)),
- <<"modsharedroster">>, <<"mod_shared_roster">>))
+ <<"mod_shared_roster">>, <<"mod_shared_roster">>))
++
[?XC(<<"h2">>, <<(?T(<<"Group ">>))/binary, Group/binary>>)] ++
case Res of
diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl
index a4ac65c1..dd6a7f6d 100644
--- a/src/mod_shared_roster_ldap.erl
+++ b/src/mod_shared_roster_ldap.erl
@@ -3,6 +3,7 @@
%%% Author : Realloc <realloc@realloc.spb.ru>
%%% Marcin Owsiany <marcin@owsiany.pl>
%%% Evgeniy Khramtsov <ekhramtsov@process-one.net>
+%%% Contributor : Mike Kaganski <mikekaganski@hotmail.com>
%%% Description : LDAP shared roster management
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
@@ -26,8 +27,6 @@
%%%-------------------------------------------------------------------
-module(mod_shared_roster_ldap).
--behaviour(ejabberd_config).
-
-behaviour(gen_server).
-behaviour(gen_mod).
@@ -44,19 +43,15 @@
out_subscription/4, mod_opt_type/1, opt_type/1]).
-include("ejabberd.hrl").
--include("logger.hrl").
-include("jlib.hrl").
-include("mod_roster.hrl").
-
-include("eldap.hrl").
+-define(ERROR_MSG(Fmt, Args), error_logger:error_msg(Fmt, Args)).
--define(CACHE_SIZE, 1000).
-
--define(USER_CACHE_VALIDITY, 300).
-
--define(GROUP_CACHE_VALIDITY, 300).
-
--define(LDAP_SEARCH_TIMEOUT, 5).
+-define(CACHE_SIZE, 1).
+-define(CACHE_VALIDITY, 300). %% in seconds
+-define(LDAP_SEARCH_TIMEOUT, 5). %% Timeout for LDAP search queries in seconds
+-define(INVALID_SETTING_MSG, "~s is not properly set! ~s will not function.").
-record(state,
{host = <<"">> :: binary(),
@@ -74,7 +69,6 @@
group_attr = <<"">> :: binary(),
group_desc = <<"">> :: binary(),
user_desc = <<"">> :: binary(),
- user_uid = <<"">> :: binary(),
uid_format = <<"">> :: binary(),
uid_format_re = <<"">> :: binary(),
filter = <<"">> :: binary(),
@@ -82,25 +76,51 @@
rfilter = <<"">> :: binary(),
gfilter = <<"">> :: binary(),
auth_check = true :: boolean(),
- user_cache_size = ?CACHE_SIZE :: non_neg_integer(),
- group_cache_size = ?CACHE_SIZE :: non_neg_integer(),
- user_cache_validity = ?USER_CACHE_VALIDITY :: non_neg_integer(),
- group_cache_validity = ?GROUP_CACHE_VALIDITY :: non_neg_integer()}).
-
+ %% Group data parameters
+ group_base = <<"">> :: binary(),
+ %% - Subgroup of roster filter
+ %% This filter defines which groups are displayed in the shared roster
+ %% Valid values are 'all' or "LDAP filter string" or "LDAP filter string containing %g"
+ shgfilter = <<"">> :: binary(),
+ shg_attr = <<"">> :: binary(),
+ %% - Subgroup of group filter
+ group_is_dn = true :: boolean(),
+ member_attr = <<"">> :: binary(),
+ %% User data parameters
+ member_selection_mode = memberattr_dn :: memberattr_normal | memberattr_dn |
+ group_children,
+ %% Algorithm control parameters
+ subscribe_all = false :: binary(),
+ roster_cache_size = ?CACHE_SIZE :: non_neg_integer(),
+ roster_cache_validity = ?CACHE_VALIDITY :: non_neg_integer()}).
+
+%% If #state.member_selection_mode is memberattr_normal or memberattr_dn,
+%% then members is list of member_attr values;
+%% if #state.member_selection_mode is group_children,
+%% then members is dn of the group (to make it possible to search for its subtree)
-record(group_info, {desc, members}).
+-record(user_info, {us, name}).
+
+-record(shared_roster_item, {us, name, groups}).
+
+% Groups visible to this group
+% grp may be atom 'all' or a group name string.
+% shgrps is a list containing one or more grp
+-record(shg_data, {grp, shgrps}).
+
%%====================================================================
%% API
%%====================================================================
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:start_link({local, Proc}, ?MODULE,
- [Host, Opts], []).
+ [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
- permanent, 1000, worker, [?MODULE]},
+ permanent, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -112,109 +132,92 @@ stop(Host) ->
%% Hooks
%%--------------------------------------------------------------------
get_user_roster(Items, {U, S} = US) ->
- SRUsers = get_user_to_groups_map(US, true),
- {NewItems1, SRUsersRest} = lists:mapfoldl(fun (Item,
- SRUsers1) ->
- {_, _, {U1, S1, _}} =
- Item#roster.usj,
- US1 = {U1, S1},
- case dict:find(US1,
- SRUsers1)
- of
- {ok, _GroupNames} ->
- {Item#roster{subscription
- =
- both,
- ask =
- none},
- dict:erase(US1,
- SRUsers1)};
- error ->
- {Item, SRUsers1}
- end
- end,
- SRUsers, Items),
+ {ok, State} = eldap_utils:get_state(S, ?MODULE),
+ SRUsers = get_shared_roster(State, US),
+ %%?ERROR_MSG("XXXXXX get_user_roster: SRUsers=~p", [SRUsers]),
+ %% If partially subscribed users are also in shared roster,
+ %% show them as totally subscribed:
+ {NewItems1, SRUsersRest} = lists:mapfoldl(
+ fun(Item, SRUsers1) ->
+ {_, _, {U1, S1, _}} = Item#roster.usj,
+ US1 = {U1, S1},
+ case lists:keytake(US1, #shared_roster_item.us, SRUsers1) of
+ %%case dict:find(US1, SRUsers1) of
+ {value, _, SRUsers2} -> {Item#roster{subscription = both, ask = none}, SRUsers2};
+ %%{ok, _GroupNames} -> {Item#roster{subscription = both, ask = none}, dict:erase(US1, SRUsers1)};
+ false -> {Item, SRUsers1}
+ end
+ end,
+ SRUsers, Items),
+ %% Export items in roster format:
SRItems = [#roster{usj = {U, S, {U1, S1, <<"">>}},
- us = US, jid = {U1, S1, <<"">>},
- name = get_user_name(U1, S1), subscription = both,
- ask = none, groups = GroupNames}
- || {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
+ us = US,
+ jid = {U1, S1, <<"">>},
+ name = Name,
+ subscription = both,
+ ask = none,
+ groups = Groups} ||
+ #shared_roster_item{us = {U1, S1}, name = Name, groups = Groups} <- SRUsersRest],
+ %% SRItems = [#roster{usj = {U, S, {U1, S1, <<"">>}},
+ %% us = US, jid = {U1, S1, <<"">>},
+ %% name = get_user_name(U1, S1), subscription = both,
+ %% ask = none, groups = GroupNames}
+ %% || {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
SRItems ++ NewItems1.
%% This function in use to rewrite the roster entries when moving or renaming
%% them in the user contact list.
process_item(RosterItem, _Host) ->
- USFrom = RosterItem#roster.us,
- {User, Server, _Resource} = RosterItem#roster.jid,
- USTo = {User, Server},
- Map = get_user_to_groups_map(USFrom, false),
- case dict:find(USTo, Map) of
- error -> RosterItem;
- {ok, []} -> RosterItem;
- {ok, GroupNames}
- when RosterItem#roster.subscription == remove ->
- RosterItem#roster{subscription = both, ask = none,
- groups = GroupNames};
- _ -> RosterItem#roster{subscription = both, ask = none}
+ {ok, State} = eldap_utils:get_state(_Host, ?MODULE),
+ {User,Server,_Resource} = RosterItem#roster.jid,
+ USTo = {User,Server},
+ SR = get_shared_roster(State, RosterItem#roster.us),
+ case lists:keysearch(USTo, #shared_roster_item.us, SR) of
+ false ->
+ RosterItem;
+ {value, #shared_roster_item{groups = Groups}} when RosterItem#roster.subscription == remove ->
+ %% Roster item cannot be removed:
+ %% We simply reset the original groups:
+ RosterItem#roster{subscription = both, ask = none,
+ groups=Groups};
+ _ ->
+ RosterItem#roster{subscription = both, ask = none}
end.
get_subscription_lists({F, T}, User, Server) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Server),
- US = {LUser, LServer},
- DisplayedGroups = get_user_displayed_groups(US),
- SRUsers = lists:usort(lists:flatmap(fun (Group) ->
- get_group_users(LServer, Group)
- end,
- DisplayedGroups)),
- SRJIDs = [{U1, S1, <<"">>} || {U1, S1} <- SRUsers],
+ U = jid:nodeprep(User),
+ S = jid:nameprep(Server),
+ {ok, State} = eldap_utils:get_state(S, ?MODULE),
+ SRJIDs = get_presense_subscribers(State, {U, S}),
+%?INFO_MSG("SRJIDs: ~p", [SRJIDs]),
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
-get_jid_info({Subscription, Groups}, User, Server,
- JID) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Server),
- US = {LUser, LServer},
+get_jid_info({Subscription, Groups}, User, Server, JID) ->
+ {ok, State} = eldap_utils:get_state(Server, ?MODULE),
{U1, S1, _} = jid:tolower(JID),
US1 = {U1, S1},
- SRUsers = get_user_to_groups_map(US, false),
- case dict:find(US1, SRUsers) of
- {ok, GroupNames} ->
- NewGroups = if Groups == [] -> GroupNames;
- true -> Groups
- end,
- {both, NewGroups};
- error -> {Subscription, Groups}
+ SR = get_shared_roster(State, {User, Server}),
+ case lists:keysearch(US1, #shared_roster_item.us, SR) of
+ false -> {Subscription, Groups};
+ {value, #shared_roster_item{groups = GroupNames}} when Groups == [] -> {both, GroupNames};
+ _ -> {both, Groups}
end.
-in_subscription(Acc, User, Server, JID, Type,
- _Reason) ->
+in_subscription(Acc, User, Server, JID, Type, _Reason) ->
process_subscription(in, User, Server, JID, Type, Acc).
out_subscription(User, Server, JID, Type) ->
- process_subscription(out, User, Server, JID, Type,
- false).
-
-process_subscription(Direction, User, Server, JID,
- _Type, Acc) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Server),
- US = {LUser, LServer},
- {U1, S1, _} =
- jid:tolower(jid:remove_resource(JID)),
+ process_subscription(out, User, Server, JID, Type, false).
+
+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
+ {ok, State} = eldap_utils:get_state(Server, ?MODULE),
+ {U1, S1, _} = jid:tolower(JID),
US1 = {U1, S1},
- DisplayedGroups = get_user_displayed_groups(US),
- SRUsers = lists:usort(lists:flatmap(fun (Group) ->
- get_group_users(LServer, Group)
- end,
- DisplayedGroups)),
- case lists:member(US1, SRUsers) of
- true ->
- case Direction of
- in -> {stop, false};
- out -> stop
- end;
- false -> Acc
+ SR = get_shared_roster(State, {User, Server}),
+ case lists:keysearch(US1, #shared_roster_item.us, SR) of
+ false -> Acc;
+ _ when Direction == in -> {stop, false};
+ _ -> stop
end.
%%====================================================================
@@ -222,267 +225,509 @@ process_subscription(Direction, User, Server, JID,
%%====================================================================
init([Host, Opts]) ->
State = parse_options(Host, Opts),
- cache_tab:new(shared_roster_ldap_user,
- [{max_size, State#state.user_cache_size}, {lru, false},
- {life_time, State#state.user_cache_validity}]),
- cache_tab:new(shared_roster_ldap_group,
- [{max_size, State#state.group_cache_size}, {lru, false},
- {life_time, State#state.group_cache_validity}]),
- ejabberd_hooks:add(roster_get, Host, ?MODULE,
- get_user_roster, 70),
+ if
+ State#state.roster_cache_size > 0 ->
+ cache_tab:new(shared_roster_ldap_sr,
+ [{max_size, State#state.roster_cache_size},
+ {lru, false}, % We don't need LRU algorithm
+ {life_time, State#state.roster_cache_validity}]);
+ true ->
+ false
+ end,
+ ejabberd_hooks:add(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
ejabberd_hooks:add(roster_in_subscription, Host,
- ?MODULE, in_subscription, 30),
+ ?MODULE, in_subscription, 30),
ejabberd_hooks:add(roster_out_subscription, Host,
- ?MODULE, out_subscription, 30),
+ ?MODULE, out_subscription, 30),
ejabberd_hooks:add(roster_get_subscription_lists, Host,
- ?MODULE, get_subscription_lists, 70),
- ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE,
- get_jid_info, 70),
- ejabberd_hooks:add(roster_process_item, Host, ?MODULE,
- process_item, 50),
+ ?MODULE, get_subscription_lists, 70),
+ ejabberd_hooks:add(roster_get_jid_info, Host,
+ ?MODULE, get_jid_info, 70),
+ ejabberd_hooks:add(roster_process_item, Host,
+ ?MODULE, process_item, 50),
eldap_pool:start_link(State#state.eldap_id,
- State#state.servers, State#state.backups,
- State#state.port, State#state.dn,
- State#state.password, State#state.tls_options),
+ State#state.servers,
+ State#state.backups,
+ State#state.port,
+ State#state.dn,
+ State#state.password,
+ State#state.tls_options),
{ok, State}.
handle_call(get_state, _From, State) ->
{reply, {ok, State}, State};
+
handle_call(_Request, _From, State) ->
{reply, {error, badarg}, State}.
-handle_cast(_Msg, State) -> {noreply, State}.
+handle_cast(_Msg, State) ->
+ {noreply, State}.
-handle_info(_Info, State) -> {noreply, State}.
+handle_info(_Info, State) ->
+ {noreply, State}.
terminate(_Reason, State) ->
Host = State#state.host,
- ejabberd_hooks:delete(roster_get, Host, ?MODULE,
- get_user_roster, 70),
+ ejabberd_hooks:delete(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
ejabberd_hooks:delete(roster_in_subscription, Host,
- ?MODULE, in_subscription, 30),
+ ?MODULE, in_subscription, 30),
ejabberd_hooks:delete(roster_out_subscription, Host,
- ?MODULE, out_subscription, 30),
- ejabberd_hooks:delete(roster_get_subscription_lists,
- Host, ?MODULE, get_subscription_lists, 70),
+ ?MODULE, out_subscription, 30),
+ ejabberd_hooks:delete(roster_get_subscription_lists, Host,
+ ?MODULE, get_subscription_lists, 70),
ejabberd_hooks:delete(roster_get_jid_info, Host,
- ?MODULE, get_jid_info, 70),
+ ?MODULE, get_jid_info, 70),
ejabberd_hooks:delete(roster_process_item, Host,
- ?MODULE, process_item, 50).
+ ?MODULE, process_item, 50).
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-get_user_to_groups_map({_, Server} = US, SkipUS) ->
- DisplayedGroups = get_user_displayed_groups(US),
- lists:foldl(fun (Group, Dict1) ->
- GroupName = get_group_name(Server, Group),
- lists:foldl(fun (Contact, Dict) ->
- if SkipUS, Contact == US -> Dict;
- true ->
- dict:append(Contact,
- GroupName, Dict)
- end
- end,
- Dict1, get_group_users(Server, Group))
- end,
- dict:new(), DisplayedGroups).
-
-eldap_search(State, FilterParseArgs, AttributesList) ->
- case apply(eldap_filter, parse, FilterParseArgs) of
- {ok, EldapFilter} ->
- case eldap_pool:search(State#state.eldap_id,
- [{base, State#state.base},
- {filter, EldapFilter},
- {timeout, ?LDAP_SEARCH_TIMEOUT},
- {deref_aliases, State#state.deref_aliases},
- {attributes, AttributesList}])
- of
- #eldap_search_result{entries = Es} ->
- %% A result with entries. Return their list.
- Es;
- _ ->
- %% Something else. Pretend we got no results.
- []
- end;
- _ ->
- %% Filter parsing failed. Pretend we got no results.
- []
+do_eldap_search(PoolName, Opts) ->
+ case eldap_pool:search(PoolName, Opts) of
+ #eldap_search_result{entries = Es} ->
+ %% A result with entries. Return their list.
+ Es;
+ Err ->
+ %% Something else. Pretend we got no results.
+ ?ERROR_MSG("Error searching: ~p ~p", [Err, Opts]),
+ []
+ end.
+
+%% Pass given Filter or FilterTemplate and SubstList to eldap_filter:parse,
+%% and if successful, run LDAP search on the whole subtree of Base, using
+%% resulting filter, retrieving given AttributesList. Return the result entries.
+%% On any error, print an error message and return an empty list of results.
+eldap_search(State, Base, EldapFilter, AttributesList) when is_tuple(EldapFilter) ->
+ do_eldap_search(State#state.eldap_id,
+ [{base, Base},
+%% {scope, wholeSubtree} %% This is the default
+ {filter, EldapFilter},
+ {timeout, ?LDAP_SEARCH_TIMEOUT},
+ {deref_aliases, State#state.deref_aliases},
+ {attributes, AttributesList}]);
+%% Filter is string
+eldap_search(State, Base, Filter, AttributesList) ->
+ eldap_search(State, Base, Filter, [], AttributesList).
+
+eldap_search(State, Base, FilterTemplate, SubstList, AttributesList) ->
+ case apply(eldap_filter, parse, [eldap_filter:do_sub(FilterTemplate, SubstList)]) of
+ {ok, EldapFilter} ->
+ %% Filter parsing succeeded
+ eldap_search(State, Base, EldapFilter, AttributesList);
+ Err ->
+ %% Filter parsing failed. Pretend we got no results.
+ ?ERROR_MSG("Error parsing filter: ~p", [Err]),
+ []
end.
-get_user_displayed_groups({User, Host}) ->
- {ok, State} = eldap_utils:get_state(Host, ?MODULE),
- GroupAttr = State#state.group_attr,
- Entries = eldap_search(State,
- [eldap_filter:do_sub(State#state.rfilter,
- [{<<"%u">>, User}])],
- [GroupAttr]),
- Reply = lists:flatmap(fun (#eldap_entry{attributes =
- Attrs}) ->
- case Attrs of
- [{GroupAttr, ValuesList}] -> ValuesList;
- _ -> []
- end
- end,
- Entries),
- lists:usort(Reply).
-
-get_group_users(Host, Group) ->
- {ok, State} = eldap_utils:get_state(Host, ?MODULE),
- case cache_tab:dirty_lookup(shared_roster_ldap_group,
- {Group, Host},
- fun () -> search_group_info(State, Group) end)
- of
- {ok, #group_info{members = Members}}
- when Members /= undefined ->
- Members;
- _ -> []
+%% The same as above, but gets the Attributes for the specified DN.
+%% Note that this function doesn't honor the State's base DN;
+%% TODO: fix this (create a custom check?)
+eldap_search_dn(State, DN, EldapFilter, AttributesList) when is_tuple(EldapFilter) ->
+ do_eldap_search(State#state.eldap_id,
+ [{scope, baseObject},
+ {base, DN},
+ {filter, EldapFilter},
+ {timeout, ?LDAP_SEARCH_TIMEOUT},
+ {deref_aliases, State#state.deref_aliases},
+ {attributes, AttributesList}]);
+%% Filter is string.
+eldap_search_dn(State, DN, Filter, AttributesList) ->
+ case eldap_filter:parse(Filter) of
+ {ok, EldapFilter} ->
+ %% Filter parsing succeeded
+ eldap_search_dn(State, DN, EldapFilter, AttributesList);
+ Err ->
+ %% Filter parsing failed. Pretend we got no results.
+ ?ERROR_MSG("Error parsing filter: ~p", [Err]),
+ []
end.
-get_group_name(Host, Group) ->
- {ok, State} = eldap_utils:get_state(Host, ?MODULE),
- case cache_tab:dirty_lookup(shared_roster_ldap_group,
- {Group, Host},
- fun () -> search_group_info(State, Group) end)
- of
- {ok, #group_info{desc = GroupName}}
- when GroupName /= undefined ->
- GroupName;
- _ -> Group
+intersection(L1,L2) -> lists:filter(fun(X) -> lists:member(X,L1) end, L2).
+
+filter_roster(Roster, all) -> Roster;
+filter_roster(_, []) -> [];
+filter_roster(Roster, IncludeGroups) when is_list(IncludeGroups) ->
+ lists:foldl(
+ fun(RosterItem, Acc) ->
+ case intersection(IncludeGroups, RosterItem#shared_roster_item.groups) of
+ [] -> Acc;
+ CommonGroups -> [RosterItem#shared_roster_item{groups=CommonGroups} | Acc]
+ end
+ end,
+ [], Roster).
+
+get_user_visible_groups(UserGroups, VisibilityMap) ->
+ lists:foldl(
+ fun(Group, Acc) ->
+ case (lists:keysearch(Group, #shg_data.grp, VisibilityMap)) of
+ {value, #shg_data{shgrps=Gs}} when is_list(Gs) -> Gs ++ Acc;
+ _ -> Acc
+ end
+ end,
+ UserGroups, UserGroups).
+
+%% Returns [#shared_roster_item];
+%% Removes the US from returned data
+%% If State#state.user_groups_only is 'true', then it removes all users that are not in US's groups,
+%% and also removes the groups from the users that the US is not member of.
+get_shared_roster(State, {_, Server} = US) ->
+ case (catch get_full_roster(State, Server)) of
+ {ok, {VisibilityMap, FullRoster}} ->
+ %%?ERROR_MSG("XXXXXX get_shared_roster: VMap=~p FullRoster=~p", [VisibilityMap, FullRoster]),
+ CommonRosterGroups = lists:foldl(
+ fun(_, all) -> all;
+ (#shg_data{grp=all, shgrps=all}, _) -> all;
+ (#shg_data{grp=all, shgrps=Gs}, Acc) when is_list(Gs) -> Gs ++ Acc;
+ (_, Acc) -> Acc
+ end,
+ [], VisibilityMap),
+ case lists:keytake(US, #shared_roster_item.us, FullRoster) of
+ false -> filter_roster(FullRoster, CommonRosterGroups);
+ {value, #shared_roster_item{groups=UserGroups}, Roster2} ->
+ VisibleGroups = case (CommonRosterGroups) of
+ all -> all;
+ CRG -> get_user_visible_groups(UserGroups, VisibilityMap) ++ CRG
+ end,
+ filter_roster(Roster2, VisibleGroups)
+ end;
+ {'EXIT', CatchData} -> ?ERROR_MSG("Error getting shared roster for user ~p: ~p", [US, CatchData]), [];
+ _Unexpected -> []
end.
-get_user_name(User, Host) ->
- {ok, State} = eldap_utils:get_state(Host, ?MODULE),
- case cache_tab:dirty_lookup(shared_roster_ldap_user,
- {User, Host},
- fun () -> search_user_name(State, User) end)
- of
- {ok, UserName} -> UserName;
- error -> User
+%% 1. If user is not a member of shared roster -> no additional subscriptions
+%% 2. Else if ldap_subscribe_all is set AND this user is member of a group published to all ->
+%% add all registered users of this vhost
+%% 3. Else add only those groups this user' groups are published to
+get_presense_subscribers(State, {_, Server} = US) ->
+ case (catch get_full_roster(State, Server)) of
+ {ok, {VisibilityMap, FullRoster}} ->
+ case lists:keytake(US, #shared_roster_item.us, FullRoster) of
+ false -> []; % Case #1
+ {value, #shared_roster_item{groups=UserGroups}, Roster2} ->
+ AllGroups = lists:usort(lists:foldl(
+ fun(#shared_roster_item{groups=Gs}, Acc) -> Gs ++ Acc end,
+ [], FullRoster)),
+ Fun = case (State#state.subscribe_all) of
+ true -> % Possible case 2
+ fun(_, all) -> all;
+ (#shg_data{grp=all, shgrps=all}, _) -> all;
+ (#shg_data{grp=all, shgrps=Gs}, Acc) when is_list(Gs) ->
+ case intersection(Gs, UserGroups) of
+ [] -> Acc;
+ _SomeCommon -> all
+ end;
+ (#shg_data{grp=G, shgrps=Gs}, Acc) when is_list(Gs) ->
+ case intersection(Gs, UserGroups) of
+ [] -> Acc;
+ _SomeCommon -> [G | Acc]
+ end;
+ (_, Acc) -> Acc
+ end;
+ _False -> % Case 3
+ fun(#shg_data{grp=all}, Acc) -> AllGroups ++ Acc;
+ (#shg_data{grp=G, shgrps=Gs}, Acc) when is_list(Gs) ->
+ case intersection(Gs, UserGroups) of
+ [] -> Acc;
+ _SomeCommon -> [G | Acc]
+ end;
+ (_, Acc) -> Acc
+ end
+ end,
+ PublishTo = lists:foldl(Fun, [], VisibilityMap),
+ case (PublishTo) of
+ all ->
+ [{U1, S1, <<"">>} || {U1, S1} <- ejabberd_auth:get_vh_registered_users(Server)];
+ Groups ->
+ [{U1, S1, <<"">>} || #shared_roster_item{us = {U1, S1}} <- filter_roster(Roster2, UserGroups ++ Groups)]
+ end
+ end;
+ {'EXIT', CatchData} -> ?ERROR_MSG("Error getting shared roster for user ~p: ~p", [US, CatchData]), [];
+ _Unexpected -> []
end.
+get_full_roster(State, Server) when State#state.roster_cache_size > 0 ->
+ cache_tab:dirty_lookup(shared_roster_ldap_sr,
+ {Server},
+ fun() -> search_roster_info(State, Server) end);
+get_full_roster(State, Server) ->
+ search_roster_info(State, Server).
+
+search_visible_groups(State, _) when State#state.shgfilter == all ->
+ [{all, all}];
+search_visible_groups(State, _) when State#state.shgfilter == none ->
+ [{all, none}];
+search_visible_groups(State, Groups) ->
+ case (string:str(State#state.shgfilter, "%g")) of
+ 0 -> [{all, search_group_visible_groups(State, "")}];
+ _ -> lists:map(
+ fun(Group) -> {Group, search_group_visible_groups(State, Group)} end,
+ Groups)
+ end.
+
+search_group_visible_groups(State, Group) ->
+ Entries = eldap_search(State, State#state.group_base, State#state.shgfilter, [{<<"%g">>, Group}], [State#state.shg_attr]),
+ lists:usort(lists:flatmap(
+ fun(#eldap_entry{attributes = Attrs}) ->
+ case Attrs of
+ [{_GroupAttr, ValuesList}] ->
+ ValuesList;
+ _ ->
+ []
+ end
+ end, Entries)).
+
+group2name(all, _) -> all;
+group2name(none, _) -> none;
+group2name(Group, GroupNames) ->
+ case (lists:keysearch(Group, 1, GroupNames)) of
+ {value, {_, Name}} -> Name;
+ _ -> false
+ end.
+
+groups2names(all, _) -> all;
+groups2names(none, _) -> none;
+groups2names(GroupList, GroupNames) ->
+ lists:foldl(
+ fun(G, Acc) ->
+ case (group2name(G, GroupNames)) of
+ false -> Acc;
+ Name -> [Name | Acc]
+ end
+ end,
+ [], GroupList).
+
+prep_vis_map(VisGroups, GroupNames) ->
+ lists:foldl(
+ fun({G, Gs}, Acc) ->
+ case (group2name(G, GroupNames)) of
+ false -> Acc;
+ Name -> [#shg_data{grp=Name, shgrps=groups2names(Gs, GroupNames)} | Acc]
+ end
+ end,
+ [], VisGroups).
+
+search_roster_info(State, _Host) ->
+ Entries = eldap_search(State, State#state.group_base, State#state.rfilter, [State#state.group_attr]),
+ AllGroupIds = lists:usort(lists:flatmap(
+ fun(#eldap_entry{attributes = Attrs}) ->
+ case Attrs of
+ [{_GroupAttr, ValuesList}] ->
+ ValuesList;
+ _ ->
+ []
+ end
+ end, Entries)),
+ VisGroups = search_visible_groups(State, AllGroupIds),
+ %%?ERROR_MSG("XXXXXX search_roster_info: VisGroups=~p", [VisGroups]),
+
+ {GroupNames, RosterItems} = case State#state.member_selection_mode of
+ group_children ->
+ {GroupNames0, UsersDict0} = lists:foldl(
+ fun(Group, {GrNAcc, Dict1} = Acc) ->
+ case search_group_info(State, Group) of
+ {ok, #group_info{desc = GroupName, members = GroupDN}} ->
+ {[{Group, GroupName} | GrNAcc], search_users_info(State, GroupDN, GroupName, Dict1)};
+ _ -> Acc %% Error getting group data -> No users!
+ end
+ end,
+ {[], dict:new()}, AllGroupIds),
+
+ {GroupNames0, dict:fold(
+ fun(#user_info{us=US, name=UserName}, Groups, AccIn) ->
+ [#shared_roster_item{us = US, name = UserName, groups = Groups} | AccIn]
+ end,
+ [], UsersDict0)};
+ _ ->
+ {GroupNames1, UsersDict1} = lists:foldl(
+ fun(Group, {GrNAcc, Dict1} = Acc) ->
+ case search_group_info(State, Group) of
+ {ok, #group_info{desc = GroupName, members = Members}} ->
+ {[{Group, GroupName} | GrNAcc], lists:foldl(
+ fun(Member, Dict) -> dict:append(Member, GroupName, Dict) end,
+ Dict1, Members)};
+ _ -> Acc %% Error getting group data -> No users!
+ end
+ end,
+ {[], dict:new()}, AllGroupIds),
+
+ %%?ERROR_MSG("UsersDict1: ~p", [UsersDict1]),
+ %%?ERROR_MSG("GroupNames1: ~p", [GroupNames1]),
+
+ {GroupNames1, dict:fold(
+ fun(Member, Groups, AccIn) ->
+ case search_user_info(State, Member) of
+ {ok, #user_info{us=US, name=UserName}} ->
+ %%?ERROR_MSG("XXXX found user: ~p ~p ~p", [UserName, Groups, US]),
+ [#shared_roster_item{us = US, name = UserName, groups = Groups} | AccIn];
+ _ -> AccIn
+ end
+ end,
+ [], UsersDict1)}
+ end,
+
+ VisibilityMap = prep_vis_map(VisGroups, GroupNames),
+ {ok, {VisibilityMap, RosterItems}}.
+
search_group_info(State, Group) ->
+ AttList = case State#state.member_selection_mode of
+ group_children -> [State#state.group_desc];
+ _ -> [State#state.group_desc, State#state.member_attr]
+ end,
+ SearchResult = case State#state.group_is_dn of
+ true -> eldap_search_dn(State,
+ Group,
+ State#state.gfilter,
+ AttList);
+ _ -> eldap_search(State,
+ State#state.group_base,
+ State#state.gfilter,
+ [{<<"%g">>, Group}],
+ AttList)
+ end,
+ case SearchResult of
+ [] ->
+ error;
+ LDAPEntries ->
+ case State#state.member_selection_mode of
+ group_children ->
+ [#eldap_entry{object_name=Name, attributes=Attrs} | _] = LDAPEntries,
+ {ok, #group_info{desc = eldap_utils:get_ldap_attr(State#state.group_desc, Attrs),
+ members = Name}};
+ _ ->
+ {GroupDesc, MembersLists} = lists:foldl(
+ fun(#eldap_entry{attributes=Attrs}, {DescAcc, MembersAcc}) ->
+ case {eldap_utils:get_ldap_attr(State#state.group_desc, Attrs),
+ lists:keysearch(State#state.member_attr, 1, Attrs)} of
+ {Desc, {value, {GroupMemberAttr, Members}}}
+ when GroupMemberAttr == State#state.member_attr ->
+ {Desc, lists:usort(Members ++ MembersAcc)};
+ _ ->
+ {DescAcc, MembersAcc}
+ end
+ end,
+ {Group, []}, LDAPEntries),
+ {ok, #group_info{desc = GroupDesc,
+ members = lists:usort(MembersLists)}}
+ end
+ end.
+
+%% Takes the attributes from LDAP user search;
+%% returns error or {ok, #user_info}
+construct_user(State, Attrs) ->
Extractor = case State#state.uid_format_re of
- <<"">> ->
- fun (UID) ->
- catch eldap_utils:get_user_part(UID,
- State#state.uid_format)
- end;
- _ ->
- fun (UID) ->
- catch get_user_part_re(UID,
- State#state.uid_format_re)
- end
- end,
+ <<"">> -> fun(UID) ->
+ catch eldap_utils:get_user_part(UID, State#state.uid_format)
+ end;
+ _ -> fun(UID) ->
+ catch get_user_part_re(UID, State#state.uid_format_re)
+ end
+ end,
AuthChecker = case State#state.auth_check of
- true -> fun ejabberd_auth:is_user_exists/2;
- _ -> fun (_U, _S) -> true end
- end,
+ true -> fun ejabberd_auth:is_user_exists/2;
+ _ -> fun(_U, _S) -> true end
+ end,
Host = State#state.host,
- case eldap_search(State,
- [eldap_filter:do_sub(State#state.gfilter,
- [{<<"%g">>, Group}])],
- [State#state.group_attr, State#state.group_desc,
- State#state.uid])
- of
- [] -> error;
- LDAPEntries ->
- {GroupDesc, MembersLists} = lists:foldl(fun
- (#eldap_entry{attributes =
- Attrs},
- {DescAcc, JIDsAcc}) ->
- case
- {eldap_utils:get_ldap_attr(State#state.group_attr,
- Attrs),
- eldap_utils:get_ldap_attr(State#state.group_desc,
- Attrs),
- lists:keysearch(State#state.uid,
- 1,
- Attrs)}
- of
- {ID, Desc,
- {value,
- {GroupMemberAttr,
- Members}}}
- when ID /= <<"">>,
- GroupMemberAttr
- ==
- State#state.uid ->
- JIDs =
- lists:foldl(fun
- ({ok,
- UID},
- L) ->
- PUID =
- jid:nodeprep(UID),
- case
- PUID
- of
- error ->
- L;
- _ ->
- case
- AuthChecker(PUID,
- Host)
- of
- true ->
- [{PUID,
- Host}
- | L];
- _ ->
- L
- end
- end;
- (_,
- L) ->
- L
- end,
- [],
- lists:map(Extractor,
- Members)),
- {Desc,
- [JIDs
- | JIDsAcc]};
- _ ->
- {DescAcc, JIDsAcc}
- end
- end,
- {Group, []}, LDAPEntries),
- {ok,
- #group_info{desc = GroupDesc,
- members = lists:usort(lists:flatten(MembersLists))}}
+
+ case {eldap_utils:get_ldap_attr(State#state.uid, Attrs),
+ eldap_utils:get_ldap_attr(State#state.user_desc, Attrs)} of
+ {UID, Desc} when UID /= "" ->
+ %% By returning "" get_ldap_attr means "not found"
+ case Extractor(UID) of
+ {ok, UID1} ->
+ UID2 = jid:nodeprep(UID1),
+ case UID2 of
+ error -> error;
+ _ ->
+ case AuthChecker(UID2, Host) of
+ true -> {ok, #user_info{us={UID2, Host}, name=Desc}};
+ _ -> error
+ end
+ end;
+ _ -> error
+ end;
+ _ ->
+ error
end.
-search_user_name(State, User) ->
- case eldap_search(State,
- [eldap_filter:do_sub(State#state.ufilter,
- [{<<"%u">>, User}])],
- [State#state.user_desc, State#state.user_uid])
- of
- [#eldap_entry{attributes = Attrs} | _] ->
- case {eldap_utils:get_ldap_attr(State#state.user_uid,
- Attrs),
- eldap_utils:get_ldap_attr(State#state.user_desc, Attrs)}
- of
- {UID, Desc} when UID /= <<"">> -> {ok, Desc};
- _ -> error
- end;
- [] -> error
+%% This function is used when State#state.member_selection_mode is group_children
+%% Returns UsersDict to which the users (#user_info) of this group are added
+%%search_users_info(State, GroupInfo) ->
+search_users_info(State, GroupDN, GroupName, UsersDict) ->
+ SearchResult = eldap_search(State,
+ GroupDN,
+ State#state.ufilter,
+ [State#state.user_desc, State#state.uid]),
+ lists:foldl(
+ fun(#eldap_entry{attributes=Attrs}, Dict1) ->
+ case construct_user(State, Attrs) of
+ {ok, UserInfo} ->
+ dict:append(UserInfo, GroupName, Dict1);
+ _ -> Dict1
+ end
+ end, UsersDict, SearchResult).
+
+%% This function is used when State#state.member_selection_mode is either memberattr_normal or memberattr_dn
+search_user_info(State, User) ->
+ %%?ERROR_MSG("XXX search_user_info: searching for ~p", [User]),
+ SearchResult = case State#state.member_selection_mode of
+ memberattr_dn -> eldap_search_dn(State,
+ User,
+ State#state.ufilter,
+ [State#state.user_desc, State#state.uid]);
+ memberattr_normal -> eldap_search(State,
+ State#state.base,
+ State#state.ufilter,
+ [{<<"%u">>, User}],
+ [State#state.user_desc, State#state.uid])
+ end,
+ case SearchResult of
+ [#eldap_entry{attributes=Attrs}|_] ->
+ construct_user(State, Attrs);
+ [] ->
+ %%?ERROR_MSG("XX not found", []),
+ error
end.
%% Getting User ID part by regex pattern
get_user_part_re(String, Pattern) ->
case catch re:run(String, Pattern) of
- {match, Captured} ->
- {First, Len} = lists:nth(2, Captured),
- Result = str:sub_string(String, First + 1, First + Len),
- {ok, Result};
- _ -> {error, badmatch}
+ {match, Captured} ->
+ {First, Len} = lists:nth(2,Captured),
+ Result = string:sub_string(String, First+1, First+Len),
+ {ok,Result};
+ _ -> {error, badmatch}
end.
+% select(SelectFirst, First, Second) ->
+% case SelectFirst of
+% true -> First;
+% _ -> Second
+% end.
+
+% prepare_filter(Opts, Name, Default, ReturnParsed) ->
+% F = gen_mod:get_opt(Name, Opts, Default),
+% prepare_filter(F, Name, ReturnParsed).
+
+% prepare_filter(F, Name, ReturnParsed) ->
+% case eldap_filter:parse(F) of
+% {ok, EldapFilter} ->
+% case ReturnParsed of
+% true -> EldapFilter;
+% _ -> F
+% end;
+% _ ->
+% ?ERROR_MSG(?INVALID_SETTING_MSG, [atom_to_list(Name), ?MODULE]),
+% []
+% end.
+
parse_options(Host, Opts) ->
Eldap_ID = jlib:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
Cfg = eldap_utils:get_config(Host, Opts),
@@ -516,84 +761,114 @@ parse_options(Host, Opts) ->
(false) -> false;
(true) -> true
end, true),
- UserCacheValidity = gen_mod:get_opt(
- {ldap_user_cache_validity, Host}, Opts,
- fun(I) when is_integer(I), I>0 -> I end,
- ?USER_CACHE_VALIDITY),
- GroupCacheValidity = gen_mod:get_opt(
+ RosterCacheValidity = gen_mod:get_opt(
{ldap_group_cache_validity, Host}, Opts,
fun(I) when is_integer(I), I>0 -> I end,
- ?GROUP_CACHE_VALIDITY),
- UserCacheSize = gen_mod:get_opt(
- {ldap_user_cache_size, Host}, Opts,
- fun(I) when is_integer(I), I>0 -> I end,
- ?CACHE_SIZE),
- GroupCacheSize = gen_mod:get_opt(
- {ldap_group_cache_size, Host}, Opts,
+ ?CACHE_VALIDITY),
+ RosterCacheSize = gen_mod:get_opt(
+ {ldap_roster_cache_size, Host}, Opts,
fun(I) when is_integer(I), I>0 -> I end,
?CACHE_SIZE),
ConfigFilter = gen_mod:get_opt({ldap_filter, Host}, Opts,
- fun check_filter/1, <<"">>),
+ fun check_filter/1, <<"">>),
ConfigUserFilter = gen_mod:get_opt({ldap_ufilter, Host}, Opts,
- fun check_filter/1, <<"">>),
+ fun check_filter/1, <<"">>),
ConfigGroupFilter = gen_mod:get_opt({ldap_gfilter, Host}, Opts,
- fun check_filter/1, <<"">>),
+ fun check_filter/1, <<"">>),
RosterFilter = gen_mod:get_opt({ldap_rfilter, Host}, Opts,
- fun check_filter/1, <<"">>),
+ fun check_filter/1, <<"">>),
SubFilter = <<"(&(", UIDAttr/binary, "=",
- UIDAttrFormat/binary, ")(", GroupAttr/binary, "=%g))">>,
+ UIDAttrFormat/binary, ")(", GroupAttr/binary, "=%g))">>,
UserSubFilter = case ConfigUserFilter of
- <<"">> ->
- eldap_filter:do_sub(SubFilter, [{<<"%g">>, <<"*">>}]);
- UString -> UString
- end,
+ <<"">> ->
+ eldap_filter:do_sub(SubFilter, [{<<"%g">>, <<"*">>}]);
+ UString -> UString
+ end,
GroupSubFilter = case ConfigGroupFilter of
- <<"">> ->
- eldap_filter:do_sub(SubFilter,
- [{<<"%u">>, <<"*">>}]);
- GString -> GString
+ <<"">> ->
+ eldap_filter:do_sub(SubFilter,
+ [{<<"%u">>, <<"*">>}]);
+ GString -> GString
end,
Filter = case ConfigFilter of
- <<"">> -> SubFilter;
- _ ->
- <<"(&", SubFilter/binary, ConfigFilter/binary, ")">>
- end,
+ <<"">> -> SubFilter;
+ _ ->
+ <<"(&", SubFilter/binary, ConfigFilter/binary, ")">>
+ end,
UserFilter = case ConfigFilter of
- <<"">> -> UserSubFilter;
- _ ->
- <<"(&", UserSubFilter/binary, ConfigFilter/binary, ")">>
- end,
+ <<"">> -> UserSubFilter;
+ _ ->
+ <<"(&", UserSubFilter/binary, ConfigFilter/binary, ")">>
+ end,
GroupFilter = case ConfigFilter of
- <<"">> -> GroupSubFilter;
- _ ->
- <<"(&", GroupSubFilter/binary, ConfigFilter/binary,
- ")">>
- end,
+ <<"">> -> GroupSubFilter;
+ _ ->
+ <<"(&", GroupSubFilter/binary, ConfigFilter/binary,
+ ")">>
+ end,
+%%%%%%%%%%%%%
+ GroupBase = gen_mod:get_opt(ldap_group_base, Opts, fun iolist_to_binary/1,
+ Cfg#eldap_config.base),
+ GroupIsDN = gen_mod:get_opt(ldap_group_is_dn, Opts,
+ fun(on) -> true;
+ (off) -> false;
+ (false) -> false;
+ (true) -> true
+ end, true),
+ MemberSelMode = gen_mod:get_opt(ldap_member_selection_mode, Opts,
+ fun(memberattr_normal) -> memberattr_normal;
+ (memberattr_dn) -> memberattr_dn;
+ (group_children) -> group_children;
+ (Invalid) ->
+ ?ERROR_MSG("Invalid ldap_member_selection_mode '~p'. "
+ "Value 'memberattr_normal' will be used instead.",
+ [Invalid])
+ end, memberattr_normal),
+ SubscribeAll = gen_mod:get_opt(ldap_subscribe_all, Opts,
+ fun(on) -> true;
+ (off) -> false;
+ (false) -> false;
+ (true) -> true
+ end, false),
+ % MemberIsDN = (MemberSelMode == member_attr_dn) or (MemberSelMode == group_children),
+ ShGFilter = gen_mod:get_opt(ldap_shgfilter, Opts,
+ fun(all) -> all;
+ (none) -> none;
+ (S) -> check_filter(S)
+ end, all),
+ ShGAttr = gen_mod:get_opt(ldap_shgattr, Opts,
+ fun iolist_to_binary/1,
+ << GroupAttr/binary >>),
+%%%%%%
#state{host = Host, eldap_id = Eldap_ID,
- servers = Cfg#eldap_config.servers,
- backups = Cfg#eldap_config.backups,
+ servers = Cfg#eldap_config.servers,
+ backups = Cfg#eldap_config.backups,
port = Cfg#eldap_config.port,
- tls_options = Cfg#eldap_config.tls_options,
- dn = Cfg#eldap_config.dn,
+ tls_options = Cfg#eldap_config.tls_options,
+ dn = Cfg#eldap_config.dn,
password = Cfg#eldap_config.password,
base = Cfg#eldap_config.base,
deref_aliases = Cfg#eldap_config.deref_aliases,
- uid = UIDAttr,
- group_attr = GroupAttr, group_desc = GroupDesc,
- user_desc = UserDesc, user_uid = UserUID,
- uid_format = UIDAttrFormat,
- uid_format_re = UIDAttrFormatRe, filter = Filter,
- ufilter = UserFilter, rfilter = RosterFilter,
- gfilter = GroupFilter, auth_check = AuthCheck,
- user_cache_size = UserCacheSize,
- user_cache_validity = UserCacheValidity,
- group_cache_size = GroupCacheSize,
- group_cache_validity = GroupCacheValidity}.
+ group_attr = GroupAttr, group_desc = GroupDesc,
+ user_desc = UserDesc, uid = UserUID,
+ uid_format = UIDAttrFormat,
+ uid_format_re = UIDAttrFormatRe, filter = Filter,
+ ufilter = UserFilter, rfilter = RosterFilter,
+ gfilter = GroupFilter, auth_check = AuthCheck,
+ group_base = GroupBase,
+ member_attr = UIDAttr,
+ member_selection_mode = MemberSelMode,
+ group_is_dn = GroupIsDN,
+ shgfilter = ShGFilter,
+ shg_attr = ShGAttr,
+ subscribe_all = SubscribeAll,
+ roster_cache_size = RosterCacheSize,
+ roster_cache_validity = RosterCacheValidity}.
check_filter(F) ->
- NewF = iolist_to_binary(F),
- {ok, _} = eldap_filter:parse(NewF),
- NewF.
+ NewF = iolist_to_binary(F),
+ {ok, _} = eldap_filter:parse(NewF),
+ NewF.
mod_opt_type(deref_aliases) ->
fun (never) -> never;
@@ -661,6 +936,20 @@ mod_opt_type(ldap_user_cache_validity) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(ldap_userdesc) -> fun iolist_to_binary/1;
mod_opt_type(ldap_useruid) -> fun iolist_to_binary/1;
+mod_opt_type(ldap_group_base) -> fun iolist_to_binary/1;
+mod_opt_type(ldap_group_is_dn) -> fun(B) when is_boolean(B) -> B end;
+mod_opt_type(ldap_member_selection_mode) ->
+ fun(memberattr_normal) -> memberattr_normal;
+ (memberattr_dn) -> memberattr_dn;
+ (group_children) -> group_children
+ end;
+mod_opt_type(ldap_subscribe_all) -> fun(B) when is_boolean(B) -> B end;
+mod_opt_type(ldap_shgfilter) ->
+ fun(all) -> all;
+ (none) -> none;
+ (S) -> check_filter(S)
+ end;
+mod_opt_type(ldap_shgattr) -> fun iolist_to_binary/1;
mod_opt_type(_) ->
[ldap_auth_check, ldap_filter, ldap_gfilter,
ldap_group_cache_size, ldap_group_cache_validity,
@@ -672,7 +961,9 @@ mod_opt_type(_) ->
ldap_deref_aliases, ldap_encrypt, ldap_password,
ldap_port, ldap_rootdn, ldap_servers,
ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth,
- ldap_tls_verify].
+ ldap_tls_verify, ldap_group_base, ldap_group_is_dn,
+ ldap_member_selection_mode, ldap_subscribe_all,
+ ldap_shgfilter, ldap_shgattr].
opt_type(ldap_filter) -> fun check_filter/1;
opt_type(ldap_gfilter) -> fun check_filter/1;
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index c9f659d3..0328aec3 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -57,7 +57,7 @@ process_local_iq(_From, To,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
#xmlel{children = Els} = SubEl,
- Node = str:tokens(xml:get_tag_attr_s(<<"node">>, SubEl),
+ Node = str:tokens(fxml:get_tag_attr_s(<<"node">>, SubEl),
<<"/">>),
Names = get_names(Els, []),
case get_local_stats(To#jid.server, Node, Names) of
@@ -76,7 +76,7 @@ get_names([], Res) -> Res;
get_names([#xmlel{name = <<"stat">>, attrs = Attrs}
| Els],
Res) ->
- Name = xml:get_attr_s(<<"name">>, Attrs),
+ Name = fxml:get_attr_s(<<"name">>, Attrs),
case Name of
<<"">> -> get_names(Els, Res);
_ -> get_names(Els, [Name | Res])
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index db19b557..256dc5de 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -25,6 +25,8 @@
-module(mod_vcard).
+-compile([{parse_transform, ejabberd_sql_pt}]).
+
-author('alexey@process-one.net').
-protocol({xep, 54, '1.2'}).
@@ -35,10 +37,11 @@
-export([start/2, init/3, stop/1, get_sm_features/5,
process_local_iq/3, process_sm_iq/3, reindex_vcards/0,
remove_user/2, export/1, import/1, import/3,
- mod_opt_type/1]).
+ mod_opt_type/1, set_vcard/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
+-include("ejabberd_sql_pt.hrl").
-include("jlib.hrl").
@@ -102,7 +105,7 @@ init(Host, ServerHost, Search) ->
case Search of
false -> loop(Host, ServerHost);
_ ->
- ejabberd_router:register_route(Host),
+ ejabberd_router:register_route(Host, ServerHost),
loop(Host, ServerHost)
end.
@@ -212,14 +215,13 @@ get_vcard(LUser, LServer, mnesia) ->
{aborted, _Reason} -> error
end;
get_vcard(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_vcard(LServer, Username) of
- {selected, [<<"vcard">>], [[SVCARD]]} ->
- case xml_stream:parse_element(SVCARD) of
+ case catch odbc_queries:get_vcard(LServer, LUser) of
+ {selected, [{SVCARD}]} ->
+ case fxml_stream:parse_element(SVCARD) of
{error, _Reason} -> error;
VCARD -> [VCARD]
end;
- {selected, [<<"vcard">>], []} -> [];
+ {selected, []} -> [];
_ -> error
end;
get_vcard(LUser, LServer, riak) ->
@@ -233,29 +235,29 @@ get_vcard(LUser, LServer, riak) ->
end.
set_vcard(User, LServer, VCARD) ->
- FN = xml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
- Family = xml:get_path_s(VCARD,
+ FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
+ Family = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
- Given = xml:get_path_s(VCARD,
+ Given = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]),
- Middle = xml:get_path_s(VCARD,
+ Middle = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]),
- Nickname = xml:get_path_s(VCARD,
+ Nickname = fxml:get_path_s(VCARD,
[{elem, <<"NICKNAME">>}, cdata]),
- BDay = xml:get_path_s(VCARD,
+ BDay = fxml:get_path_s(VCARD,
[{elem, <<"BDAY">>}, cdata]),
- CTRY = xml:get_path_s(VCARD,
+ CTRY = fxml:get_path_s(VCARD,
[{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]),
- Locality = xml:get_path_s(VCARD,
+ Locality = fxml:get_path_s(VCARD,
[{elem, <<"ADR">>}, {elem, <<"LOCALITY">>},
cdata]),
- EMail1 = xml:get_path_s(VCARD,
+ EMail1 = fxml:get_path_s(VCARD,
[{elem, <<"EMAIL">>}, {elem, <<"USERID">>}, cdata]),
- EMail2 = xml:get_path_s(VCARD,
+ EMail2 = fxml:get_path_s(VCARD,
[{elem, <<"EMAIL">>}, cdata]),
- OrgName = xml:get_path_s(VCARD,
+ OrgName = fxml:get_path_s(VCARD,
[{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]),
- OrgUnit = xml:get_path_s(VCARD,
+ OrgUnit = fxml:get_path_s(VCARD,
[{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]),
EMail = case EMail1 of
<<"">> -> EMail2;
@@ -336,39 +338,14 @@ set_vcard(User, LServer, VCARD) ->
{<<"orgunit">>, OrgUnit},
{<<"lorgunit">>, LOrgUnit}]}]);
odbc ->
- Username = ejabberd_odbc:escape(User),
- LUsername = ejabberd_odbc:escape(LUser),
- SVCARD =
- ejabberd_odbc:escape(xml:element_to_binary(VCARD)),
- SFN = ejabberd_odbc:escape(FN),
- SLFN = ejabberd_odbc:escape(LFN),
- SFamily = ejabberd_odbc:escape(Family),
- SLFamily = ejabberd_odbc:escape(LFamily),
- SGiven = ejabberd_odbc:escape(Given),
- SLGiven = ejabberd_odbc:escape(LGiven),
- SMiddle = ejabberd_odbc:escape(Middle),
- SLMiddle = ejabberd_odbc:escape(LMiddle),
- SNickname = ejabberd_odbc:escape(Nickname),
- SLNickname = ejabberd_odbc:escape(LNickname),
- SBDay = ejabberd_odbc:escape(BDay),
- SLBDay = ejabberd_odbc:escape(LBDay),
- SCTRY = ejabberd_odbc:escape(CTRY),
- SLCTRY = ejabberd_odbc:escape(LCTRY),
- SLocality = ejabberd_odbc:escape(Locality),
- SLLocality = ejabberd_odbc:escape(LLocality),
- SEMail = ejabberd_odbc:escape(EMail),
- SLEMail = ejabberd_odbc:escape(LEMail),
- SOrgName = ejabberd_odbc:escape(OrgName),
- SLOrgName = ejabberd_odbc:escape(LOrgName),
- SOrgUnit = ejabberd_odbc:escape(OrgUnit),
- SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
- odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY,
- SEMail, SFN, SFamily, SGiven, SLBDay,
- SLCTRY, SLEMail, SLFN, SLFamily,
- SLGiven, SLLocality, SLMiddle,
- SLNickname, SLOrgName, SLOrgUnit,
- SLocality, SMiddle, SNickname, SOrgName,
- SOrgUnit, SVCARD, Username)
+ SVCARD = fxml:element_to_binary(VCARD),
+ odbc_queries:set_vcard(LServer, LUser, BDay, CTRY,
+ EMail, FN, Family, Given, LBDay,
+ LCTRY, LEMail, LFN, LFamily,
+ LGiven, LLocality, LMiddle,
+ LNickname, LOrgName, LOrgUnit,
+ Locality, Middle, Nickname, OrgName,
+ OrgUnit, SVCARD, User)
end,
ejabberd_hooks:run(vcard_set, LServer,
[LUser, LServer, VCARD])
@@ -587,7 +564,7 @@ find_xdata_el1([]) -> false;
find_xdata_el1([#xmlel{name = Name, attrs = Attrs,
children = SubEls}
| Els]) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_XDATA ->
#xmlel{name = Name, attrs = Attrs, children = SubEls};
_ -> find_xdata_el1(Els)
@@ -862,27 +839,27 @@ set_vcard_t(R, _) ->
US = R#vcard.us,
User = US,
VCARD = R#vcard.vcard,
- FN = xml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
- Family = xml:get_path_s(VCARD,
+ FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
+ Family = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
- Given = xml:get_path_s(VCARD,
+ Given = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]),
- Middle = xml:get_path_s(VCARD,
+ Middle = fxml:get_path_s(VCARD,
[{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]),
- Nickname = xml:get_path_s(VCARD,
+ Nickname = fxml:get_path_s(VCARD,
[{elem, <<"NICKNAME">>}, cdata]),
- BDay = xml:get_path_s(VCARD,
+ BDay = fxml:get_path_s(VCARD,
[{elem, <<"BDAY">>}, cdata]),
- CTRY = xml:get_path_s(VCARD,
+ CTRY = fxml:get_path_s(VCARD,
[{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]),
- Locality = xml:get_path_s(VCARD,
+ Locality = fxml:get_path_s(VCARD,
[{elem, <<"ADR">>}, {elem, <<"LOCALITY">>},
cdata]),
- EMail = xml:get_path_s(VCARD,
+ EMail = fxml:get_path_s(VCARD,
[{elem, <<"EMAIL">>}, cdata]),
- OrgName = xml:get_path_s(VCARD,
+ OrgName = fxml:get_path_s(VCARD,
[{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]),
- OrgUnit = xml:get_path_s(VCARD,
+ OrgUnit = fxml:get_path_s(VCARD,
[{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]),
{LUser, _LServer} = US,
LFN = string2lower(FN),
@@ -929,12 +906,14 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- ejabberd_odbc:sql_transaction(LServer,
- [[<<"delete from vcard where username='">>,
- Username, <<"';">>],
- [<<"delete from vcard_search where lusername='">>,
- Username, <<"';">>]]);
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ fun() ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from vcard where username=%(LUser)s")),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from vcard_search where lusername=%(LUser)s"))
+ end);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
@@ -952,7 +931,7 @@ update_vcard_table() ->
fun(#vcard{us = {U, S}, vcard = El} = R) ->
R#vcard{us = {iolist_to_binary(U),
iolist_to_binary(S)},
- vcard = xml:to_xmlel(El)}
+ vcard = fxml:to_xmlel(El)}
end);
_ ->
?INFO_MSG("Recreating vcard table", []),
@@ -991,7 +970,7 @@ export(_Server) ->
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
SVCARD =
- ejabberd_odbc:escape(xml:element_to_binary(VCARD)),
+ ejabberd_odbc:escape(fxml:element_to_binary(VCARD)),
[[<<"delete from vcard where username='">>, Username, <<"';">>],
[<<"insert into vcard(username, vcard) values ('">>,
Username, <<"', '">>, SVCARD, <<"');">>]];
@@ -1064,7 +1043,7 @@ export(_Server) ->
import(LServer) ->
[{<<"select username, vcard from vcard;">>,
fun([LUser, SVCard]) ->
- #xmlel{} = VCARD = xml_stream:parse_element(SVCard),
+ #xmlel{} = VCARD = fxml_stream:parse_element(SVCard),
#vcard{us = {LUser, LServer}, vcard = VCARD}
end},
{<<"select username, lusername, fn, lfn, family, lfamily, "
@@ -1095,29 +1074,29 @@ import(_LServer, mnesia, #vcard{} = VCard) ->
import(_LServer, mnesia, #vcard_search{} = S) ->
mnesia:dirty_write(S);
import(_LServer, riak, #vcard{us = {LUser, _}, vcard = El} = VCard) ->
- FN = xml:get_path_s(El, [{elem, <<"FN">>}, cdata]),
- Family = xml:get_path_s(El,
+ FN = fxml:get_path_s(El, [{elem, <<"FN">>}, cdata]),
+ Family = fxml:get_path_s(El,
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
- Given = xml:get_path_s(El,
+ Given = fxml:get_path_s(El,
[{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]),
- Middle = xml:get_path_s(El,
+ Middle = fxml:get_path_s(El,
[{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]),
- Nickname = xml:get_path_s(El,
+ Nickname = fxml:get_path_s(El,
[{elem, <<"NICKNAME">>}, cdata]),
- BDay = xml:get_path_s(El,
+ BDay = fxml:get_path_s(El,
[{elem, <<"BDAY">>}, cdata]),
- CTRY = xml:get_path_s(El,
+ CTRY = fxml:get_path_s(El,
[{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]),
- Locality = xml:get_path_s(El,
+ Locality = fxml:get_path_s(El,
[{elem, <<"ADR">>}, {elem, <<"LOCALITY">>},
cdata]),
- EMail1 = xml:get_path_s(El,
+ EMail1 = fxml:get_path_s(El,
[{elem, <<"EMAIL">>}, {elem, <<"USERID">>}, cdata]),
- EMail2 = xml:get_path_s(El,
+ EMail2 = fxml:get_path_s(El,
[{elem, <<"EMAIL">>}, cdata]),
- OrgName = xml:get_path_s(El,
+ OrgName = fxml:get_path_s(El,
[{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]),
- OrgUnit = xml:get_path_s(El,
+ OrgUnit = fxml:get_path_s(El,
[{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]),
EMail = case EMail1 of
<<"">> -> EMail2;
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index b24356d8..98aaf936 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -21,7 +21,7 @@
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
-%%%----------------------------------------------------------------------
+%%%---------------------u-------------------------------------------------
-module(mod_vcard_ldap).
@@ -173,7 +173,7 @@ init([Host, Opts]) ->
State#state.password, State#state.tls_options),
case State#state.search of
true ->
- ejabberd_router:register_route(State#state.myhost);
+ ejabberd_router:register_route(State#state.myhost, Host);
_ -> ok
end,
{ok, State}.
@@ -723,7 +723,7 @@ find_xdata_el1([]) -> false;
find_xdata_el1([#xmlel{name = Name, attrs = Attrs,
children = SubEls}
| Els]) ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_XDATA ->
#xmlel{name = Name, attrs = Attrs, children = SubEls};
_ -> find_xdata_el1(Els)
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index 96ee09d8..18fb09a5 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -56,7 +56,7 @@ stop(Host) ->
update_presence(#xmlel{name = <<"presence">>, attrs = Attrs} = Packet,
User, Host) ->
- case xml:get_attr_s(<<"type">>, Attrs) of
+ case fxml:get_attr_s(<<"type">>, Attrs) of
<<>> -> presence_with_xupdate(Packet, User, Host);
_ -> Packet
end;
@@ -64,7 +64,7 @@ update_presence(Packet, _User, _Host) -> Packet.
vcard_set(LUser, LServer, VCARD) ->
US = {LUser, LServer},
- case xml:get_path_s(VCARD,
+ case fxml:get_path_s(VCARD,
[{elem, <<"PHOTO">>}, {elem, <<"BINVAL">>}, cdata])
of
<<>> -> remove_xupdate(LUser, LServer);
diff --git a/src/node_flat_odbc.erl b/src/node_flat_odbc.erl
index 794d3f98..e3c57938 100644
--- a/src/node_flat_odbc.erl
+++ b/src/node_flat_odbc.erl
@@ -824,7 +824,7 @@ set_item(Item) ->
{M, JID} = Item#pubsub_item.modification,
P = encode_jid(JID),
Payload = Item#pubsub_item.payload,
- XML = ejabberd_odbc:escape(str:join([xml:element_to_binary(X) || X<-Payload], <<>>)),
+ XML = ejabberd_odbc:escape(str:join([fxml:element_to_binary(X) || X<-Payload], <<>>)),
S = fun ({T1, T2, T3}) ->
str:join([jlib:i2l(T1, 6), jlib:i2l(T2, 6), jlib:i2l(T3, 6)], <<":">>)
end,
@@ -1041,7 +1041,7 @@ raw_to_item(Nidx, [ItemId, SJID, Creation, Modification, XML]) ->
[T1, T2, T3] = str:tokens(Str, <<":">>),
{jlib:l2i(T1), jlib:l2i(T2), jlib:l2i(T3)}
end,
- Payload = case xml_stream:parse_element(XML) of
+ Payload = case fxml_stream:parse_element(XML) of
{error, _Reason} -> [];
El -> [El]
end,
diff --git a/src/node_mix.erl b/src/node_mix.erl
new file mode 100644
index 00000000..b0410a8c
--- /dev/null
+++ b/src/node_mix.erl
@@ -0,0 +1,167 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(node_mix).
+
+-behaviour(gen_pubsub_node).
+
+%% API
+-export([init/3, terminate/2, options/0, features/0,
+ create_node_permission/6, create_node/2, delete_node/1,
+ purge_node/2, subscribe_node/8, unsubscribe_node/4,
+ publish_item/6, delete_item/4, remove_extra_items/3,
+ get_entity_affiliations/2, get_node_affiliations/1,
+ get_affiliation/2, set_affiliation/3,
+ get_entity_subscriptions/2, get_node_subscriptions/1,
+ get_subscriptions/2, set_subscriptions/4,
+ get_pending_nodes/2, get_states/1, get_state/2,
+ set_state/1, get_items/7, get_items/3, get_item/7,
+ get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+ path_to_node/1]).
+
+-include("pubsub.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(Host, ServerHost, Opts) ->
+ node_flat:init(Host, ServerHost, Opts).
+
+terminate(Host, ServerHost) ->
+ node_flat:terminate(Host, ServerHost).
+
+options() ->
+ [{deliver_payloads, true},
+ {notify_config, false},
+ {notify_delete, false},
+ {notify_retract, true},
+ {purge_offline, false},
+ {persist_items, true},
+ {max_items, ?MAXITEMS},
+ {subscribe, true},
+ {access_model, open},
+ {roster_groups_allowed, []},
+ {publish_model, open},
+ {notification_type, headline},
+ {max_payload_size, ?MAX_PAYLOAD_SIZE},
+ {send_last_published_item, never},
+ {deliver_notifications, true},
+ {broadcast_all_resources, true},
+ {presence_based_delivery, false}].
+
+features() ->
+ [<<"create-nodes">>,
+ <<"delete-nodes">>,
+ <<"delete-items">>,
+ <<"instant-nodes">>,
+ <<"item-ids">>,
+ <<"outcast-affiliation">>,
+ <<"persistent-items">>,
+ <<"publish">>,
+ <<"purge-nodes">>,
+ <<"retract-items">>,
+ <<"retrieve-affiliations">>,
+ <<"retrieve-items">>,
+ <<"retrieve-subscriptions">>,
+ <<"subscribe">>,
+ <<"subscription-notifications">>].
+
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+ node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+ node_flat:create_node(Nidx, Owner).
+
+delete_node(Removed) ->
+ node_flat:delete_node(Removed).
+
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+ SendLast, PresenceSubscription, RosterGroup, Options) ->
+ node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+ PresenceSubscription, RosterGroup, Options).
+
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+ node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
+
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+ node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
+
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+ node_flat:remove_extra_items(Nidx, MaxItems, ItemIds).
+
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+ node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId).
+
+purge_node(Nidx, Owner) ->
+ node_flat:purge_node(Nidx, Owner).
+
+get_entity_affiliations(Host, Owner) ->
+ node_flat:get_entity_affiliations(Host, Owner).
+
+get_node_affiliations(Nidx) ->
+ node_flat:get_node_affiliations(Nidx).
+
+get_affiliation(Nidx, Owner) ->
+ node_flat:get_affiliation(Nidx, Owner).
+
+set_affiliation(Nidx, Owner, Affiliation) ->
+ node_flat:set_affiliation(Nidx, Owner, Affiliation).
+
+get_entity_subscriptions(Host, Owner) ->
+ node_flat:get_entity_subscriptions(Host, Owner).
+
+get_node_subscriptions(Nidx) ->
+ node_flat:get_node_subscriptions(Nidx).
+
+get_subscriptions(Nidx, Owner) ->
+ node_flat:get_subscriptions(Nidx, Owner).
+
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+ node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId).
+
+get_pending_nodes(Host, Owner) ->
+ node_flat:get_pending_nodes(Host, Owner).
+
+get_states(Nidx) ->
+ node_flat:get_states(Nidx).
+
+get_state(Nidx, JID) ->
+ node_flat:get_state(Nidx, JID).
+
+set_state(State) ->
+ node_flat:set_state(State).
+
+get_items(Nidx, From, RSM) ->
+ node_flat:get_items(Nidx, From, RSM).
+
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+ node_flat:get_items(Nidx, JID, AccessModel,
+ PresenceSubscription, RosterGroup, SubId, RSM).
+
+get_item(Nidx, ItemId) ->
+ node_flat:get_item(Nidx, ItemId).
+
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+ node_flat:get_item(Nidx, ItemId, JID, AccessModel,
+ PresenceSubscription, RosterGroup, SubId).
+
+set_item(Item) ->
+ node_flat:set_item(Item).
+
+get_item_name(Host, Node, Id) ->
+ node_flat:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/node_mix_odbc.erl b/src/node_mix_odbc.erl
new file mode 100644
index 00000000..e7cc6883
--- /dev/null
+++ b/src/node_mix_odbc.erl
@@ -0,0 +1,170 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(node_mix_odbc).
+
+-behaviour(gen_pubsub_node).
+
+%% API
+-export([init/3, terminate/2, options/0, features/0,
+ create_node_permission/6, create_node/2, delete_node/1,
+ purge_node/2, subscribe_node/8, unsubscribe_node/4,
+ publish_item/6, delete_item/4, remove_extra_items/3,
+ get_entity_affiliations/2, get_node_affiliations/1,
+ get_affiliation/2, set_affiliation/3,
+ get_entity_subscriptions/2, get_node_subscriptions/1,
+ get_subscriptions/2, set_subscriptions/4,
+ get_pending_nodes/2, get_states/1, get_state/2,
+ set_state/1, get_items/7, get_items/3, get_item/7,
+ get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+ path_to_node/1, get_entity_subscriptions_for_send_last/2]).
+
+-include("pubsub.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(Host, ServerHost, Opts) ->
+ node_flat_odbc:init(Host, ServerHost, Opts).
+
+terminate(Host, ServerHost) ->
+ node_flat_odbc:terminate(Host, ServerHost).
+
+options() ->
+ [{deliver_payloads, true},
+ {notify_config, false},
+ {notify_delete, false},
+ {notify_retract, true},
+ {purge_offline, false},
+ {persist_items, true},
+ {max_items, ?MAXITEMS},
+ {subscribe, true},
+ {access_model, open},
+ {roster_groups_allowed, []},
+ {publish_model, open},
+ {notification_type, headline},
+ {max_payload_size, ?MAX_PAYLOAD_SIZE},
+ {send_last_published_item, never},
+ {deliver_notifications, true},
+ {broadcast_all_resources, true},
+ {presence_based_delivery, false}].
+
+features() ->
+ [<<"create-nodes">>,
+ <<"delete-nodes">>,
+ <<"delete-items">>,
+ <<"instant-nodes">>,
+ <<"item-ids">>,
+ <<"outcast-affiliation">>,
+ <<"persistent-items">>,
+ <<"publish">>,
+ <<"purge-nodes">>,
+ <<"retract-items">>,
+ <<"retrieve-affiliations">>,
+ <<"retrieve-items">>,
+ <<"retrieve-subscriptions">>,
+ <<"subscribe">>,
+ <<"subscription-notifications">>].
+
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+ node_flat_odbc:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+ node_flat_odbc:create_node(Nidx, Owner).
+
+delete_node(Removed) ->
+ node_flat_odbc:delete_node(Removed).
+
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+ SendLast, PresenceSubscription, RosterGroup, Options) ->
+ node_flat_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+ PresenceSubscription, RosterGroup, Options).
+
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+ node_flat_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
+
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+ node_flat_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
+
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+ node_flat_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
+
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+ node_flat_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
+
+purge_node(Nidx, Owner) ->
+ node_flat_odbc:purge_node(Nidx, Owner).
+
+get_entity_affiliations(Host, Owner) ->
+ node_flat_odbc:get_entity_affiliations(Host, Owner).
+
+get_node_affiliations(Nidx) ->
+ node_flat_odbc:get_node_affiliations(Nidx).
+
+get_affiliation(Nidx, Owner) ->
+ node_flat_odbc:get_affiliation(Nidx, Owner).
+
+set_affiliation(Nidx, Owner, Affiliation) ->
+ node_flat_odbc:set_affiliation(Nidx, Owner, Affiliation).
+
+get_entity_subscriptions(Host, Owner) ->
+ node_flat_odbc:get_entity_subscriptions(Host, Owner).
+
+get_node_subscriptions(Nidx) ->
+ node_flat_odbc:get_node_subscriptions(Nidx).
+
+get_subscriptions(Nidx, Owner) ->
+ node_flat_odbc:get_subscriptions(Nidx, Owner).
+
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+ node_flat_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
+
+get_pending_nodes(Host, Owner) ->
+ node_flat_odbc:get_pending_nodes(Host, Owner).
+
+get_states(Nidx) ->
+ node_flat_odbc:get_states(Nidx).
+
+get_state(Nidx, JID) ->
+ node_flat_odbc:get_state(Nidx, JID).
+
+set_state(State) ->
+ node_flat_odbc:set_state(State).
+
+get_items(Nidx, From, RSM) ->
+ node_flat_odbc:get_items(Nidx, From, RSM).
+
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+ node_flat_odbc:get_items(Nidx, JID, AccessModel,
+ PresenceSubscription, RosterGroup, SubId, RSM).
+
+get_item(Nidx, ItemId) ->
+ node_flat_odbc:get_item(Nidx, ItemId).
+
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+ node_flat_odbc:get_item(Nidx, ItemId, JID, AccessModel,
+ PresenceSubscription, RosterGroup, SubId).
+
+set_item(Item) ->
+ node_flat_odbc:set_item(Item).
+
+get_item_name(Host, Node, Id) ->
+ node_flat_odbc:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat_odbc:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat_odbc:path_to_node(Path).
+
+get_entity_subscriptions_for_send_last(Host, Owner) ->
+ node_flat_odbc:get_entity_subscriptions_for_send_last(Host, Owner).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/node_pep.erl b/src/node_pep.erl
index 726146b9..da032539 100644
--- a/src/node_pep.erl
+++ b/src/node_pep.erl
@@ -257,7 +257,7 @@ complain_if_modcaps_disabled(ServerHost) ->
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
- "to be enabled, but it isn't.",
+ "but it does not seems enabled, please check config.",
[ServerHost]);
true -> ok
end.
diff --git a/src/odbc_queries.erl b/src/odbc_queries.erl
index 2f488a0b..c12931c6 100644
--- a/src/odbc_queries.erl
+++ b/src/odbc_queries.erl
@@ -25,6 +25,8 @@
-module(odbc_queries).
+-compile([{parse_transform, ejabberd_sql_pt}]).
+
-behaviour(ejabberd_config).
-author("mremond@process-one.net").
@@ -40,7 +42,7 @@
get_roster_groups/3, del_user_roster_t/2,
get_roster_by_jid/3, get_rostergroup_by_jid/3,
del_roster/3, del_roster_sql/2, update_roster/5,
- update_roster_sql/4, roster_subscribe/4,
+ update_roster_sql/4, roster_subscribe/1,
get_subscription/3, set_private_data/4,
set_private_data_sql/3, get_private_data/3,
get_private_data/2, del_user_private_storage/2,
@@ -54,12 +56,13 @@
set_default_privacy_list/2,
unset_default_privacy_list/2, remove_privacy_list/2,
add_privacy_list/2, set_privacy_list/2,
- del_privacy_lists/3, set_vcard/26, get_vcard/2,
+ del_privacy_lists/2, set_vcard/26, get_vcard/2,
escape/1, count_records_where/3, get_roster_version/2,
set_roster_version/2, opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
+-include("ejabberd_sql_pt.hrl").
%% Almost a copy of string:join/2.
%% We use this version because string:join/2 is relatively
@@ -119,95 +122,92 @@ update(LServer, Table, Fields, Vals, Where) ->
sql_transaction(LServer, F) ->
ejabberd_odbc:sql_transaction(LServer, F).
-get_last(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select seconds, state from last where "
- "username='">>,
- Username, <<"'">>]).
+get_last(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(seconds)d, @(state)s from last"
+ " where username=%(LUser)s")).
-set_last_t(LServer, Username, Seconds, State) ->
- update(LServer, <<"last">>,
- [<<"username">>, <<"seconds">>, <<"state">>],
- [Username, Seconds, State],
- [<<"username='">>, Username, <<"'">>]).
+set_last_t(LServer, LUser, TimeStamp, Status) ->
+ ?SQL_UPSERT(LServer, "last",
+ ["!username=%(LUser)s",
+ "seconds=%(TimeStamp)d",
+ "state=%(Status)s"]).
-del_last(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from last where username='">>, Username,
- <<"'">>]).
+del_last(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from last where username=%(LUser)s")).
-get_password(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select password from users where username='">>,
- Username, <<"';">>]).
+get_password(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(password)s from users where username=%(LUser)s")).
-get_password_scram(LServer, Username) ->
+get_password_scram(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
- [<<"select password, serverkey, salt, iterationcount from users where "
- "username='">>, Username, <<"';">>]).
-
-set_password_t(LServer, Username, Pass) ->
- ejabberd_odbc:sql_transaction(LServer,
- fun () ->
- update_t(<<"users">>,
- [<<"username">>,
- <<"password">>],
- [Username, Pass],
- [<<"username='">>, Username,
- <<"'">>])
- end).
-
-set_password_scram_t(LServer, Username,
+ ?SQL("select @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d"
+ " from users"
+ " where username=%(LUser)s")).
+
+set_password_t(LServer, LUser, Password) ->
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ fun () ->
+ ?SQL_UPSERT_T(
+ "users",
+ ["!username=%(LUser)s",
+ "password=%(Password)s"])
+ end).
+
+set_password_scram_t(LServer, LUser,
StoredKey, ServerKey, Salt, IterationCount) ->
- ejabberd_odbc:sql_transaction(LServer,
- fun () ->
- update_t(<<"users">>,
- [<<"username">>,
- <<"password">>,
- <<"serverkey">>,
- <<"salt">>,
- <<"iterationcount">>],
- [Username, StoredKey,
- ServerKey, Salt,
- IterationCount],
- [<<"username='">>, Username,
- <<"'">>])
- end).
-
-add_user(LServer, Username, Pass) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"insert into users(username, password) "
- "values ('">>,
- Username, <<"', '">>, Pass, <<"');">>]).
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ fun () ->
+ ?SQL_UPSERT_T(
+ "users",
+ ["!username=%(LUser)s",
+ "password=%(StoredKey)s",
+ "serverkey=%(ServerKey)s",
+ "salt=%(Salt)s",
+ "iterationcount=%(IterationCount)d"])
+ end).
+
+add_user(LServer, LUser, Password) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("insert into users(username, password) "
+ "values (%(LUser)s, %(Password)s)")).
-add_user_scram(LServer, Username,
+add_user_scram(LServer, LUser,
StoredKey, ServerKey, Salt, IterationCount) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"insert into users(username, password, serverkey, salt, iterationcount) "
- "values ('">>,
- Username, <<"', '">>, StoredKey, <<"', '">>,
- ServerKey, <<"', '">>,
- Salt, <<"', '">>,
- IterationCount, <<"');">>]).
-
-del_user(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from users where username='">>, Username,
- <<"';">>]).
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("insert into users(username, password, serverkey, salt, "
+ "iterationcount) "
+ "values (%(LUser)s, %(StoredKey)s, %(ServerKey)s,"
+ " %(Salt)s, %(IterationCount)d)")).
-del_user_return_password(_LServer, Username, Pass) ->
+del_user(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from users where username=%(LUser)s")).
+
+del_user_return_password(_LServer, LUser, Password) ->
P =
- ejabberd_odbc:sql_query_t([<<"select password from users where username='">>,
- Username, <<"';">>]),
- ejabberd_odbc:sql_query_t([<<"delete from users where username='">>,
- Username, <<"' and password='">>, Pass,
- <<"';">>]),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(password)s from users where username=%(LUser)s")),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from users"
+ " where username=%(LUser)s and password=%(Password)s")),
P.
list_users(LServer) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select username from users">>]).
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(username)s from users")).
list_users(LServer, [{from, Start}, {to, End}])
when is_integer(Start) and is_integer(End) ->
@@ -222,64 +222,54 @@ list_users(LServer,
{offset, Start - 1}]);
list_users(LServer, [{limit, Limit}, {offset, Offset}])
when is_integer(Limit) and is_integer(Offset) ->
- ejabberd_odbc:sql_query(LServer,
- [list_to_binary(
- io_lib:format(
- "select username from users " ++
- "order by username " ++
- "limit ~w offset ~w",
- [Limit, Offset]))]);
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(username)s from users "
+ "order by username "
+ "limit %(Limit)d offset %(Offset)d"));
list_users(LServer,
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_binary(Prefix) and is_integer(Limit) and
is_integer(Offset) ->
- ejabberd_odbc:sql_query(LServer,
- [list_to_binary(
- io_lib:format(
- "select username from users " ++
- "where username like '~s%' " ++
- "order by username " ++
- "limit ~w offset ~w ",
- [Prefix, Limit, Offset]))]).
+ SPrefix = ejabberd_odbc:escape_like_arg(Prefix),
+ SPrefix2 = <<SPrefix/binary, $%>>,
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(username)s from users "
+ "where username like %(SPrefix2)s "
+ "order by username "
+ "limit %(Limit)d offset %(Offset)d")).
users_number(LServer) ->
- Type = ejabberd_config:get_option({odbc_type, LServer},
- fun(pgsql) -> pgsql;
- (mysql) -> mysql;
- (sqlite) -> sqlite;
- (odbc) -> odbc
- end, odbc),
- case Type of
- pgsql ->
- case
- ejabberd_config:get_option(
- {pgsql_users_number_estimate, LServer},
- fun(V) when is_boolean(V) -> V end,
- false)
- of
- true ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select reltuples from pg_class where "
- "oid = 'users'::regclass::oid">>]);
- _ ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select count(*) from users">>])
+ ejabberd_odbc:sql_query(
+ LServer,
+ fun(pgsql, _) ->
+ case
+ ejabberd_config:get_option(
+ {pgsql_users_number_estimate, LServer},
+ fun(V) when is_boolean(V) -> V end,
+ false) of
+ true ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(reltuples :: bigint)d from pg_class"
+ " where oid = 'users'::regclass::oid"));
+ _ ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(count(*))d from users"))
end;
- _ ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select count(*) from users">>])
- end.
+ (_Type, _) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(count(*))d from users"))
+ end).
users_number(LServer, [{prefix, Prefix}])
when is_binary(Prefix) ->
- ejabberd_odbc:sql_query(LServer,
- [list_to_binary(
- io_lib:fwrite(
- "select count(*) from users " ++
- %% Warning: Escape prefix at higher level to prevent SQL
- %% injection.
- "where username like '~s%'",
- [Prefix]))]);
+ SPrefix = ejabberd_odbc:escape_like_arg(Prefix),
+ SPrefix2 = <<SPrefix/binary, $%>>,
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(count(*))d from users "
+ "where username like %(SPrefix2)s"));
users_number(LServer, []) ->
users_number(LServer).
@@ -291,74 +281,71 @@ add_spool_sql(Username, XML) ->
add_spool(LServer, Queries) ->
ejabberd_odbc:sql_transaction(LServer, Queries).
-get_and_del_spool_msg_t(LServer, Username) ->
+get_and_del_spool_msg_t(LServer, LUser) ->
F = fun () ->
Result =
- ejabberd_odbc:sql_query_t([<<"select username, xml from spool where "
- "username='">>,
- Username,
- <<"' order by seq;">>]),
- ejabberd_odbc:sql_query_t([<<"delete from spool where username='">>,
- Username, <<"';">>]),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(username)s, @(xml)s from spool where "
+ "username=%(LUser)s order by seq;")),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from spool where username=%(LUser)s;")),
Result
end,
ejabberd_odbc:sql_transaction(LServer, F).
-del_spool_msg(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from spool where username='">>, Username,
- <<"';">>]).
+del_spool_msg(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from spool where username=%(LUser)s")).
-get_roster(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select username, jid, nick, subscription, "
- "ask, askmessage, server, subscribe, "
- "type from rosterusers where username='">>,
- Username, <<"'">>]).
+get_roster(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s, "
+ "@(ask)s, @(askmessage)s, @(server)s, @(subscribe)s, "
+ "@(type)s from rosterusers where username=%(LUser)s")).
-get_roster_jid_groups(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select jid, grp from rostergroups where "
- "username='">>,
- Username, <<"'">>]).
-
-get_roster_groups(_LServer, Username, SJID) ->
- ejabberd_odbc:sql_query_t([<<"select grp from rostergroups where username='">>,
- Username, <<"' and jid='">>, SJID, <<"';">>]).
-
-del_user_roster_t(LServer, Username) ->
- ejabberd_odbc:sql_transaction(LServer,
- fun () ->
- ejabberd_odbc:sql_query_t([<<"delete from rosterusers where "
- "username='">>,
- Username,
- <<"';">>]),
- ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
- "username='">>,
- Username,
- <<"';">>])
- end).
-
-get_roster_by_jid(_LServer, Username, SJID) ->
- ejabberd_odbc:sql_query_t([<<"select username, jid, nick, subscription, "
- "ask, askmessage, server, subscribe, "
- "type from rosterusers where username='">>,
- Username, <<"' and jid='">>, SJID, <<"';">>]).
-
-get_rostergroup_by_jid(LServer, Username, SJID) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select grp from rostergroups where username='">>,
- Username, <<"' and jid='">>, SJID, <<"'">>]).
-
-del_roster(_LServer, Username, SJID) ->
- ejabberd_odbc:sql_query_t([<<"delete from rosterusers where "
- "username='">>,
- Username, <<"' and jid='">>, SJID,
- <<"';">>]),
- ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
- "username='">>,
- Username, <<"' and jid='">>, SJID,
- <<"';">>]).
+get_roster_jid_groups(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(jid)s, @(grp)s from rostergroups where "
+ "username=%(LUser)s")).
+
+get_roster_groups(_LServer, LUser, SJID) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(grp)s from rostergroups"
+ " where username=%(LUser)s and jid=%(SJID)s")).
+
+del_user_roster_t(LServer, LUser) ->
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ fun () ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from rosterusers where username=%(LUser)s")),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from rostergroups where username=%(LUser)s"))
+ end).
+
+get_roster_by_jid(_LServer, LUser, SJID) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s,"
+ " @(ask)s, @(askmessage)s, @(server)s, @(subscribe)s,"
+ " @(type)s from rosterusers"
+ " where username=%(LUser)s and jid=%(SJID)s")).
+
+get_rostergroup_by_jid(LServer, LUser, SJID) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(grp)s from rostergroups"
+ " where username=%(LUser)s and jid=%(SJID)s")).
+
+del_roster(_LServer, LUser, SJID) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from rosterusers"
+ " where username=%(LUser)s and jid=%(SJID)s")),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from rostergroups"
+ " where username=%(LUser)s and jid=%(SJID)s")).
del_roster_sql(Username, SJID) ->
[[<<"delete from rosterusers where "
@@ -368,27 +355,19 @@ del_roster_sql(Username, SJID) ->
"username='">>,
Username, <<"' and jid='">>, SJID, <<"';">>]].
-update_roster(_LServer, Username, SJID, ItemVals,
+update_roster(_LServer, LUser, SJID, ItemVals,
ItemGroups) ->
- update_t(<<"rosterusers">>,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- ItemVals,
- [<<"username='">>, Username, <<"' and jid='">>, SJID,
- <<"'">>]),
- ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
- "username='">>,
- Username, <<"' and jid='">>, SJID,
- <<"';">>]),
- lists:foreach(fun (ItemGroup) ->
- ejabberd_odbc:sql_query_t([<<"insert into rostergroups( "
- " username, jid, grp) values ('">>,
- join(ItemGroup,
- <<"', '">>),
- <<"');">>])
- end,
- ItemGroups).
+ roster_subscribe(ItemVals),
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from rostergroups"
+ " where username=%(LUser)s and jid=%(SJID)s")),
+ lists:foreach(
+ fun(ItemGroup) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("insert into rostergroups(username, jid, grp) "
+ "values (%(LUser)s, %(SJID)s, %(ItemGroup)s)"))
+ end,
+ ItemGroups).
update_roster_sql(Username, SJID, ItemVals,
ItemGroups) ->
@@ -410,27 +389,31 @@ update_roster_sql(Username, SJID, ItemVals,
join(ItemGroup, <<"', '">>), <<"');">>]
|| ItemGroup <- ItemGroups].
-roster_subscribe(_LServer, Username, SJID, ItemVals) ->
- update_t(<<"rosterusers">>,
- [<<"username">>, <<"jid">>, <<"nick">>,
- <<"subscription">>, <<"ask">>, <<"askmessage">>,
- <<"server">>, <<"subscribe">>, <<"type">>],
- ItemVals,
- [<<"username='">>, Username, <<"' and jid='">>, SJID,
- <<"'">>]).
-
-get_subscription(LServer, Username, SJID) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select subscription from rosterusers "
- "where username='">>,
- Username, <<"' and jid='">>, SJID, <<"'">>]).
+roster_subscribe({LUser, SJID, Name, SSubscription, SAsk, AskMessage}) ->
+ ?SQL_UPSERT_T(
+ "rosterusers",
+ ["!username=%(LUser)s",
+ "!jid=%(SJID)s",
+ "nick=%(Name)s",
+ "subscription=%(SSubscription)s",
+ "ask=%(SAsk)s",
+ "askmessage=%(AskMessage)s",
+ "server='N'",
+ "subscribe=''",
+ "type='item'"]).
+
+get_subscription(LServer, LUser, SJID) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(subscription)s from rosterusers "
+ "where username=%(LUser)s and jid=%(SJID)s")).
-set_private_data(_LServer, Username, LXMLNS, SData) ->
- update_t(<<"private_storage">>,
- [<<"username">>, <<"namespace">>, <<"data">>],
- [Username, LXMLNS, SData],
- [<<"username='">>, Username, <<"' and namespace='">>,
- LXMLNS, <<"'">>]).
+set_private_data(_LServer, LUser, XMLNS, SData) ->
+ ?SQL_UPSERT_T(
+ "private_storage",
+ ["!username=%(LUser)s",
+ "!namespace=%(XMLNS)s",
+ "data=%(SData)s"]).
set_private_data_sql(Username, LXMLNS, SData) ->
[[<<"delete from private_storage where username='">>,
@@ -440,187 +423,189 @@ set_private_data_sql(Username, LXMLNS, SData) ->
Username, <<"', '">>, LXMLNS, <<"', '">>, SData,
<<"');">>]].
-get_private_data(LServer, Username, LXMLNS) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select data from private_storage where "
- "username='">>,
- Username, <<"' and namespace='">>, LXMLNS,
- <<"';">>]).
+get_private_data(LServer, LUser, XMLNS) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(data)s from private_storage"
+ " where username=%(LUser)s and namespace=%(XMLNS)s")).
-get_private_data(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select namespace, data from private_storage "
- "where username='">>, Username, <<"';">>]).
+get_private_data(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(namespace)s, @(data)s from private_storage"
+ " where username=%(LUser)s")).
-del_user_private_storage(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from private_storage where username='">>,
- Username, <<"';">>]).
-
-set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, SFN,
- SFamily, SGiven, SLBDay, SLCTRY, SLEMail, SLFN,
- SLFamily, SLGiven, SLLocality, SLMiddle, SLNickname,
- SLOrgName, SLOrgUnit, SLocality, SMiddle, SNickname,
- SOrgName, SOrgUnit, SVCARD, Username) ->
- ejabberd_odbc:sql_transaction(LServer,
- fun () ->
- update_t(<<"vcard">>,
- [<<"username">>,
- <<"vcard">>],
- [LUsername, SVCARD],
- [<<"username='">>, LUsername,
- <<"'">>]),
- update_t(<<"vcard_search">>,
- [<<"username">>,
- <<"lusername">>, <<"fn">>,
- <<"lfn">>, <<"family">>,
- <<"lfamily">>, <<"given">>,
- <<"lgiven">>, <<"middle">>,
- <<"lmiddle">>,
- <<"nickname">>,
- <<"lnickname">>, <<"bday">>,
- <<"lbday">>, <<"ctry">>,
- <<"lctry">>, <<"locality">>,
- <<"llocality">>,
- <<"email">>, <<"lemail">>,
- <<"orgname">>,
- <<"lorgname">>,
- <<"orgunit">>,
- <<"lorgunit">>],
- [Username, LUsername, SFN,
- SLFN, SFamily, SLFamily,
- SGiven, SLGiven, SMiddle,
- SLMiddle, SNickname,
- SLNickname, SBDay, SLBDay,
- SCTRY, SLCTRY, SLocality,
- SLLocality, SEMail, SLEMail,
- SOrgName, SLOrgName,
- SOrgUnit, SLOrgUnit],
- [<<"lusername='">>,
- LUsername, <<"'">>])
- end).
-
-get_vcard(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select vcard from vcard where username='">>,
- Username, <<"';">>]).
+del_user_private_storage(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from private_storage"
+ " where username=%(LUser)s")).
+
+set_vcard(LServer, LUser, BDay, CTRY, EMail, FN,
+ Family, Given, LBDay, LCTRY, LEMail, LFN,
+ LFamily, LGiven, LLocality, LMiddle, LNickname,
+ LOrgName, LOrgUnit, Locality, Middle, Nickname,
+ OrgName, OrgUnit, SVCARD, User) ->
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ fun() ->
+ ?SQL_UPSERT(LServer, "vcard",
+ ["!username=%(LUser)s",
+ "vcard=%(SVCARD)s"]),
+ ?SQL_UPSERT(LServer, "vcard_search",
+ ["username=%(User)s",
+ "!lusername=%(LUser)s",
+ "fn=%(FN)s",
+ "lfn=%(LFN)s",
+ "family=%(Family)s",
+ "lfamily=%(LFamily)s",
+ "given=%(Given)s",
+ "lgiven=%(LGiven)s",
+ "middle=%(Middle)s",
+ "lmiddle=%(LMiddle)s",
+ "nickname=%(Nickname)s",
+ "lnickname=%(LNickname)s",
+ "bday=%(BDay)s",
+ "lbday=%(LBDay)s",
+ "ctry=%(CTRY)s",
+ "lctry=%(LCTRY)s",
+ "locality=%(Locality)s",
+ "llocality=%(LLocality)s",
+ "email=%(EMail)s",
+ "lemail=%(LEMail)s",
+ "orgname=%(OrgName)s",
+ "lorgname=%(LOrgName)s",
+ "orgunit=%(OrgUnit)s",
+ "lorgunit=%(LOrgUnit)s"])
+ end).
+
+get_vcard(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(vcard)s from vcard where username=%(LUser)s")).
-get_default_privacy_list(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select name from privacy_default_list "
- "where username='">>,
- Username, <<"';">>]).
+get_default_privacy_list(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(name)s from privacy_default_list "
+ "where username=%(LUser)s")).
-get_default_privacy_list_t(Username) ->
- ejabberd_odbc:sql_query_t([<<"select name from privacy_default_list "
- "where username='">>,
- Username, <<"';">>]).
+get_default_privacy_list_t(LUser) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(name)s from privacy_default_list "
+ "where username=%(LUser)s")).
-get_privacy_list_names(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select name from privacy_list where "
- "username='">>,
- Username, <<"';">>]).
+get_privacy_list_names(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(name)s from privacy_list"
+ " where username=%(LUser)s")).
-get_privacy_list_names_t(Username) ->
- ejabberd_odbc:sql_query_t([<<"select name from privacy_list where "
- "username='">>,
- Username, <<"';">>]).
+get_privacy_list_names_t(LUser) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(name)s from privacy_list"
+ " where username=%(LUser)s")).
-get_privacy_list_id(LServer, Username, SName) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select id from privacy_list where username='">>,
- Username, <<"' and name='">>, SName, <<"';">>]).
+get_privacy_list_id(LServer, LUser, Name) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(id)d from privacy_list"
+ " where username=%(LUser)s and name=%(Name)s")).
-get_privacy_list_id_t(Username, SName) ->
- ejabberd_odbc:sql_query_t([<<"select id from privacy_list where username='">>,
- Username, <<"' and name='">>, SName, <<"';">>]).
+get_privacy_list_id_t(LUser, Name) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(id)d from privacy_list"
+ " where username=%(LUser)s and name=%(Name)s")).
-get_privacy_list_data(LServer, Username, SName) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select t, value, action, ord, match_all, "
- "match_iq, match_message, match_presence_in, "
- "match_presence_out from privacy_list_data "
- "where id = (select id from privacy_list "
- "where username='">>,
- Username, <<"' and name='">>, SName,
- <<"') order by ord;">>]).
-
-get_privacy_list_data_t(Username, SName) ->
- ejabberd_odbc:sql_query_t([<<"select t, value, action, ord, match_all, "
- "match_iq, match_message, match_presence_in, "
- "match_presence_out from privacy_list_data "
- "where id = (select id from privacy_list "
- "where username='">>,
- Username, <<"' and name='">>, SName,
- <<"') order by ord;">>]).
+get_privacy_list_data(LServer, LUser, Name) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
+ "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
+ "@(match_presence_out)b from privacy_list_data "
+ "where id ="
+ " (select id from privacy_list"
+ " where username=%(LUser)s and name=%(Name)s) "
+ "order by ord")).
+
+%% Not used?
+get_privacy_list_data_t(LUser, Name) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
+ "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
+ "@(match_presence_out)b from privacy_list_data "
+ "where id ="
+ " (select id from privacy_list"
+ " where username=%(LUser)s and name=%(Name)s) "
+ "order by ord")).
get_privacy_list_data_by_id(LServer, ID) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select t, value, action, ord, match_all, "
- "match_iq, match_message, match_presence_in, "
- "match_presence_out from privacy_list_data "
- "where id='">>,
- ID, <<"' order by ord;">>]).
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
+ "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
+ "@(match_presence_out)b from privacy_list_data "
+ "where id=%(ID)d order by ord")).
get_privacy_list_data_by_id_t(ID) ->
- ejabberd_odbc:sql_query_t([<<"select t, value, action, ord, match_all, "
- "match_iq, match_message, match_presence_in, "
- "match_presence_out from privacy_list_data "
- "where id='">>,
- ID, <<"' order by ord;">>]).
-
-set_default_privacy_list(Username, SName) ->
- update_t(<<"privacy_default_list">>,
- [<<"username">>, <<"name">>], [Username, SName],
- [<<"username='">>, Username, <<"'">>]).
-
-unset_default_privacy_list(LServer, Username) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from privacy_default_list "
- " where username='">>,
- Username, <<"';">>]).
+ ejabberd_odbc:sql_query_t(
+ ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
+ "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
+ "@(match_presence_out)b from privacy_list_data "
+ "where id=%(ID)d order by ord")).
+
+set_default_privacy_list(LUser, Name) ->
+ ?SQL_UPSERT_T(
+ "privacy_default_list",
+ ["!username=%(LUser)s",
+ "name=%(Name)s"]).
+
+unset_default_privacy_list(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from privacy_default_list"
+ " where username=%(LUser)s")).
-remove_privacy_list(Username, SName) ->
- ejabberd_odbc:sql_query_t([<<"delete from privacy_list where username='">>,
- Username, <<"' and name='">>, SName, <<"';">>]).
+remove_privacy_list(LUser, Name) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from privacy_list where"
+ " username=%(LUser)s and name=%(Name)s")).
-add_privacy_list(Username, SName) ->
- ejabberd_odbc:sql_query_t([<<"insert into privacy_list(username, name) "
- "values ('">>,
- Username, <<"', '">>, SName, <<"');">>]).
+add_privacy_list(LUser, Name) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("insert into privacy_list(username, name) "
+ "values (%(LUser)s, %(Name)s)")).
set_privacy_list(ID, RItems) ->
- ejabberd_odbc:sql_query_t([<<"delete from privacy_list_data where "
- "id='">>,
- ID, <<"';">>]),
- lists:foreach(fun (Items) ->
- ejabberd_odbc:sql_query_t([<<"insert into privacy_list_data(id, t, "
- "value, action, ord, match_all, match_iq, "
- "match_message, match_presence_in, match_prese"
- "nce_out ) values ('">>,
- ID, <<"', '">>,
- join(Items, <<"', '">>),
- <<"');">>])
+ ejabberd_odbc:sql_query_t(
+ ?SQL("delete from privacy_list_data where id=%(ID)d")),
+ lists:foreach(
+ fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
+ MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
+ ejabberd_odbc:sql_query_t(
+ ?SQL("insert into privacy_list_data(id, t, "
+ "value, action, ord, match_all, match_iq, "
+ "match_message, match_presence_in, match_presence_out) "
+ "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
+ " %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
+ " %(MatchMessage)b, %(MatchPresenceIn)b,"
+ " %(MatchPresenceOut)b)"))
end,
RItems).
-del_privacy_lists(LServer, Server, Username) ->
-%% Characters to escape
-%% Count number of records in a table given a where clause
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from privacy_list where username='">>,
- Username, <<"';">>]),
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from privacy_list_data where "
- "value='">>,
- <<Username/binary, "@", Server/binary>>,
- <<"';">>]),
- ejabberd_odbc:sql_query(LServer,
- [<<"delete from privacy_default_list where "
- "username='">>,
- Username, <<"';">>]).
+del_privacy_lists(LServer, LUser) ->
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from privacy_list where username=%(LUser)s")),
+ %US = <<LUser/binary, "@", LServer/binary>>,
+ %ejabberd_odbc:sql_query(
+ % LServer,
+ % ?SQL("delete from privacy_list_data where value=%(US)s")),
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("delete from privacy_default_list where username=%(LUser)s")).
+%% Characters to escape
escape($\000) -> <<"\\0">>;
escape($\n) -> <<"\\n">>;
escape($\t) -> <<"\\t">>;
@@ -631,16 +616,17 @@ escape($") -> <<"\\\"">>;
escape($\\) -> <<"\\\\">>;
escape(C) -> <<C>>.
+%% Count number of records in a table given a where clause
count_records_where(LServer, Table, WhereClause) ->
ejabberd_odbc:sql_query(LServer,
[<<"select count(*) from ">>, Table, <<" ">>,
WhereClause, <<";">>]).
get_roster_version(LServer, LUser) ->
- ejabberd_odbc:sql_query(LServer,
- [<<"select version from roster_version where "
- "username = '">>,
- LUser, <<"'">>]).
+ ejabberd_odbc:sql_query(
+ LServer,
+ ?SQL("select @(version)s from roster_version"
+ " where username = %(LUser)s")).
set_roster_version(LUser, Version) ->
update_t(<<"roster_version">>,
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
new file mode 100644
index 00000000..204cfec2
--- /dev/null
+++ b/src/prosody2ejabberd.erl
@@ -0,0 +1,341 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 20 Jan 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(prosody2ejabberd).
+
+%% API
+-export([from_dir/1]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("logger.hrl").
+-include("mod_roster.hrl").
+-include("mod_offline.hrl").
+-include("mod_privacy.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+from_dir(ProsodyDir) ->
+ case file:list_dir(ProsodyDir) of
+ {ok, HostDirs} ->
+ lists:foreach(
+ fun(HostDir) ->
+ Host = list_to_binary(HostDir),
+ lists:foreach(
+ fun(SubDir) ->
+ Path = filename:join(
+ [ProsodyDir, HostDir, SubDir]),
+ convert_dir(Path, Host, SubDir)
+ end, ["vcard", "accounts", "roster",
+ "private", "config", "offline",
+ "privacy"])
+ end, HostDirs);
+ {error, Why} = Err ->
+ ?ERROR_MSG("failed to list ~s: ~s",
+ [ProsodyDir, file:format_error(Why)]),
+ Err
+ end.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+convert_dir(Path, Host, Type) ->
+ case file:list_dir(Path) of
+ {ok, Files} ->
+ lists:foreach(
+ fun(File) ->
+ FilePath = filename:join(Path, File),
+ case eval_file(FilePath) of
+ {ok, Data} ->
+ Name = iolist_to_binary(filename:rootname(File)),
+ convert_data(Host, Type, Name, Data);
+ Err ->
+ Err
+ end
+ end, Files);
+ {error, enoent} ->
+ ok;
+ {error, Why} = Err ->
+ ?ERROR_MSG("failed to list ~s: ~s",
+ [Path, file:format_error(Why)]),
+ Err
+ end.
+
+eval_file(Path) ->
+ case file:read_file(Path) of
+ {ok, Data} ->
+ State0 = luerl:init(),
+ State1 = luerl:set_table([item],
+ fun([X], State) -> {[X], State} end,
+ State0),
+ NewData = case filename:extension(Path) of
+ ".list" ->
+ <<"return {", Data/binary, "};">>;
+ _ ->
+ Data
+ end,
+ case luerl:eval(NewData, State1) of
+ {ok, _} = Res ->
+ Res;
+ {error, Why} = Err ->
+ ?ERROR_MSG("failed to eval ~s: ~p", [Path, Why]),
+ Err
+ end;
+ {error, Why} = Err ->
+ ?ERROR_MSG("failed to read file ~s: ~s",
+ [Path, file:format_error(Why)]),
+ Err
+ end.
+
+convert_data(Host, "accounts", User, [Data]) ->
+ Password = proplists:get_value(<<"password">>, Data, <<>>),
+ case ejabberd_auth:try_register(User, Host, Password) of
+ {atomic, ok} ->
+ ok;
+ Err ->
+ ?ERROR_MSG("failed to register user ~s@~s: ~p",
+ [User, Host, Err]),
+ Err
+ end;
+convert_data(Host, "roster", User, [Data]) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Host),
+ Rosters =
+ lists:flatmap(
+ fun({<<"pending">>, L}) ->
+ convert_pending_item(LUser, LServer, L);
+ ({S, L}) when is_binary(S) ->
+ convert_roster_item(LUser, LServer, S, L);
+ (_) ->
+ []
+ end, Data),
+ lists:foreach(fun mod_roster:set_roster/1, Rosters);
+convert_data(Host, "private", User, [Data]) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Host),
+ PrivData = lists:flatmap(
+ fun({_TagXMLNS, Raw}) ->
+ case deserialize(Raw) of
+ [El] ->
+ XMLNS = fxml:get_tag_attr_s(<<"xmlns">>, El),
+ [{XMLNS, El}];
+ _ ->
+ []
+ end
+ end, Data),
+ mod_private:set_data(LUser, LServer, PrivData);
+convert_data(Host, "vcard", User, [Data]) ->
+ LServer = jid:nameprep(Host),
+ case deserialize(Data) of
+ [VCard] ->
+ mod_vcard:set_vcard(User, LServer, VCard);
+ _ ->
+ ok
+ end;
+convert_data(_Host, "config", _User, [Data]) ->
+ RoomJID = jid:from_string(proplists:get_value(<<"jid">>, Data, <<"">>)),
+ Config = proplists:get_value(<<"_data">>, Data, []),
+ RoomCfg = convert_room_config(Data),
+ case proplists:get_bool(<<"persistent">>, Config) of
+ true when RoomJID /= error ->
+ mod_muc:store_room(?MYNAME, RoomJID#jid.lserver,
+ RoomJID#jid.luser, RoomCfg);
+ _ ->
+ ok
+ end;
+convert_data(Host, "offline", User, [Data]) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Host),
+ Msgs = lists:flatmap(
+ fun({_, RawXML}) ->
+ case deserialize(RawXML) of
+ [El] -> el_to_offline_msg(LUser, LServer, El);
+ _ -> []
+ end
+ end, Data),
+ mod_offline:store_offline_msg(
+ LServer, {LUser, LServer}, Msgs, length(Msgs), infinity);
+convert_data(Host, "privacy", User, [Data]) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Host),
+ Lists = proplists:get_value(<<"lists">>, Data, []),
+ Priv = #privacy{
+ us = {LUser, LServer},
+ default = proplists:get_value(<<"default">>, Data, none),
+ lists = lists:flatmap(
+ fun({Name, Vals}) ->
+ Items = proplists:get_value(<<"items">>, Vals, []),
+ case lists:map(fun convert_privacy_item/1,
+ Items) of
+ [] -> [];
+ ListItems -> [{Name, ListItems}]
+ end
+ end, Lists)},
+ mod_privacy:set_privacy_list(Priv);
+convert_data(_Host, _Type, _User, _Data) ->
+ ok.
+
+convert_pending_item(LUser, LServer, LuaList) ->
+ lists:flatmap(
+ fun({S, true}) ->
+ case jid:from_string(S) of
+ #jid{} = J ->
+ LJID = jid:tolower(J),
+ [#roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID,
+ ask = in}];
+ error ->
+ []
+ end;
+ (_) ->
+ []
+ end, LuaList).
+
+convert_roster_item(LUser, LServer, JIDstring, LuaList) ->
+ case jid:from_string(JIDstring) of
+ #jid{} = JID ->
+ LJID = jid:tolower(JID),
+ InitR = #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID},
+ Roster =
+ lists:foldl(
+ fun({<<"groups">>, Val}, R) ->
+ Gs = lists:flatmap(
+ fun({G, true}) -> [G];
+ (_) -> []
+ end, Val),
+ R#roster{groups = Gs};
+ ({<<"subscription">>, Sub}, R) ->
+ R#roster{subscription = jlib:binary_to_atom(Sub)};
+ ({<<"ask">>, <<"subscribe">>}, R) ->
+ R#roster{ask = out};
+ ({<<"name">>, Name}, R) ->
+ R#roster{name = Name}
+ end, InitR, LuaList),
+ [Roster];
+ error ->
+ []
+ end.
+
+convert_room_affiliations(Data) ->
+ lists:flatmap(
+ fun({J, Aff}) ->
+ case jid:from_string(J) of
+ #jid{luser = U, lserver = S} ->
+ [{{U, S, <<>>}, jlib:binary_to_atom(Aff)}];
+ error ->
+ []
+ end
+ end, proplists:get_value(<<"_affiliations">>, Data, [])).
+
+convert_room_config(Data) ->
+ Config = proplists:get_value(<<"_data">>, Data, []),
+ Pass = case proplists:get_value(<<"password">>, Config, <<"">>) of
+ <<"">> ->
+ [];
+ Password ->
+ [{password_protected, true},
+ {password, Password}]
+ end,
+ Subj = case jid:from_string(
+ proplists:get_value(
+ <<"subject_from">>, Config, <<"">>)) of
+ #jid{lresource = Nick} when Nick /= <<"">> ->
+ [{subject, proplists:get_value(<<"subject">>, Config, <<"">>)},
+ {subject_author, Nick}];
+ _ ->
+ []
+ end,
+ Anonymous = case proplists:get_value(<<"whois">>, Config, <<"moderators">>) of
+ <<"moderators">> -> true;
+ _ -> false
+ end,
+ [{affiliations, convert_room_affiliations(Data)},
+ {allow_change_subj, proplists:get_bool(<<"changesubject">>, Config)},
+ {description, proplists:get_value(<<"description">>, Config, <<"">>)},
+ {members_only, proplists:get_bool(<<"members_only">>, Config)},
+ {moderated, proplists:get_bool(<<"moderated">>, Config)},
+ {anonymous, Anonymous}] ++ Pass ++ Subj.
+
+convert_privacy_item({_, Item}) ->
+ Action = proplists:get_value(<<"action">>, Item, <<"allow">>),
+ Order = proplists:get_value(<<"order">>, Item, 0),
+ T = jlib:binary_to_atom(proplists:get_value(<<"type">>, Item, <<"none">>)),
+ V = proplists:get_value(<<"value">>, Item, <<"">>),
+ MatchIQ = proplists:get_bool(<<"iq">>, Item),
+ MatchMsg = proplists:get_bool(<<"message">>, Item),
+ MatchPresIn = proplists:get_bool(<<"presence-in">>, Item),
+ MatchPresOut = proplists:get_bool(<<"presence-out">>, Item),
+ MatchAll = if (MatchIQ == false) and (MatchMsg == false) and
+ (MatchPresIn == false) and (MatchPresOut == false) ->
+ true;
+ true ->
+ false
+ end,
+ {Type, Value} = try case T of
+ none -> {T, none};
+ group -> {T, V};
+ jid -> {T, jid:tolower(jid:from_string(V))};
+ subscription -> {T, jlib:binary_to_atom(V)}
+ end
+ catch _:_ ->
+ {none, none}
+ end,
+ #listitem{type = Type,
+ value = Value,
+ action = jlib:binary_to_atom(Action),
+ order = erlang:trunc(Order),
+ match_all = MatchAll,
+ match_iq = MatchIQ,
+ match_message = MatchMsg,
+ match_presence_in = MatchPresIn,
+ match_presence_out = MatchPresOut}.
+
+el_to_offline_msg(LUser, LServer, #xmlel{attrs = Attrs} = El) ->
+ case jlib:datetime_string_to_timestamp(
+ fxml:get_attr_s(<<"stamp">>, Attrs)) of
+ {_, _, _} = TS ->
+ Attrs1 = lists:filter(
+ fun(<<"stamp">>) -> false;
+ (<<"stamp_legacy">>) -> false;
+ (_) -> true
+ end, Attrs),
+ Packet = El#xmlel{attrs = Attrs1},
+ case {jid:from_string(fxml:get_attr_s(<<"from">>, Attrs)),
+ jid:from_string(fxml:get_attr_s(<<"to">>, Attrs))} of
+ {#jid{} = From, #jid{} = To} ->
+ [#offline_msg{
+ us = {LUser, LServer},
+ timestamp = TS,
+ expire = never,
+ from = From,
+ to = To,
+ packet = Packet}];
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end.
+
+deserialize(L) ->
+ deserialize(L, #xmlel{}, []).
+
+deserialize([{<<"attr">>, Attrs}|T], El, Acc) ->
+ deserialize(T, El#xmlel{attrs = Attrs ++ El#xmlel.attrs}, Acc);
+deserialize([{<<"name">>, Name}|T], El, Acc) ->
+ deserialize(T, El#xmlel{name = Name}, Acc);
+deserialize([{_, S}|T], #xmlel{children = Els} = El, Acc) when is_binary(S) ->
+ deserialize(T, El#xmlel{children = [{xmlcdata, S}|Els]}, Acc);
+deserialize([{_, L}|T], #xmlel{children = Els} = El, Acc) when is_list(L) ->
+ deserialize(T, El#xmlel{children = deserialize(L) ++ Els}, Acc);
+deserialize([], El, Acc) ->
+ [El|Acc].
diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl
index 9918a2c3..22c90414 100644
--- a/src/pubsub_subscription.erl
+++ b/src/pubsub_subscription.erl
@@ -126,7 +126,7 @@ get_options_xform(Lang, Options) ->
++ XFields}}.
parse_options_xform(XFields) ->
- case xml:remove_cdata(XFields) of
+ case fxml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->
diff --git a/src/pubsub_subscription_odbc.erl b/src/pubsub_subscription_odbc.erl
index 6791c4ac..149308ad 100644
--- a/src/pubsub_subscription_odbc.erl
+++ b/src/pubsub_subscription_odbc.erl
@@ -151,7 +151,7 @@ get_options_xform(Lang, Options) ->
++ XFields}}.
parse_options_xform(XFields) ->
- case xml:remove_cdata(XFields) of
+ case fxml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) ->