mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-26 17:38:45 +01:00
Merge branch '2.2.x-applepush' into 2.2.x
Without applepush Conflicts: src/ejabberd_c2s.erl src/ejabberd_c2s.hrl
This commit is contained in:
commit
2ab31cb613
File diff suppressed because it is too large
Load Diff
@ -61,4 +61,27 @@
|
|||||||
fsm_limit_opts,
|
fsm_limit_opts,
|
||||||
lang,
|
lang,
|
||||||
debug=false,
|
debug=false,
|
||||||
flash_connection = false}).
|
flash_connection = false,
|
||||||
|
reception = true,
|
||||||
|
standby = false,
|
||||||
|
queue = queue:new(),
|
||||||
|
queue_len = 0,
|
||||||
|
pres_queue = gb_trees:empty(),
|
||||||
|
keepalive_timer,
|
||||||
|
keepalive_timeout,
|
||||||
|
oor_timeout,
|
||||||
|
oor_status = "",
|
||||||
|
oor_show = "",
|
||||||
|
oor_notification,
|
||||||
|
oor_send_body = all,
|
||||||
|
oor_send_groupchat = false,
|
||||||
|
oor_send_from = jid,
|
||||||
|
oor_appid = "",
|
||||||
|
oor_unread = 0,
|
||||||
|
oor_unread_users = ?SETS:new(),
|
||||||
|
oor_unread_client = 0,
|
||||||
|
oor_offline = false,
|
||||||
|
ack_enabled = false,
|
||||||
|
ack_counter = 0,
|
||||||
|
ack_queue = queue:new(),
|
||||||
|
ack_timer}).
|
||||||
|
@ -30,19 +30,18 @@
|
|||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
loop/1,
|
init/1,
|
||||||
stop/1,
|
stop/1,
|
||||||
store_packet/3,
|
store_packet/3,
|
||||||
resend_offline_messages/2,
|
resend_offline_messages/2,
|
||||||
pop_offline_messages/3,
|
pop_offline_messages/3,
|
||||||
get_sm_features/5,
|
|
||||||
remove_expired_messages/0,
|
remove_expired_messages/0,
|
||||||
remove_old_messages/1,
|
remove_old_messages/1,
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
get_queue_length/2,
|
|
||||||
webadmin_page/3,
|
webadmin_page/3,
|
||||||
webadmin_user/4,
|
webadmin_user/4,
|
||||||
webadmin_user_parse_query/5]).
|
webadmin_user_parse_query/5,
|
||||||
|
count_offline_messages/3]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
@ -54,9 +53,6 @@
|
|||||||
-define(PROCNAME, ejabberd_offline).
|
-define(PROCNAME, ejabberd_offline).
|
||||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||||
|
|
||||||
%% default value for the maximum number of user messages
|
|
||||||
-define(MAX_USER_MESSAGES, infinity).
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
mnesia:create_table(offline_msg,
|
mnesia:create_table(offline_msg,
|
||||||
[{disc_only_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
@ -71,28 +67,30 @@ start(Host, Opts) ->
|
|||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:add(anonymous_purge_hook, Host,
|
ejabberd_hooks:add(anonymous_purge_hook, Host,
|
||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:add(disco_sm_features, Host,
|
|
||||||
?MODULE, get_sm_features, 50),
|
|
||||||
ejabberd_hooks:add(disco_local_features, Host,
|
|
||||||
?MODULE, get_sm_features, 50),
|
|
||||||
ejabberd_hooks:add(webadmin_page_host, Host,
|
ejabberd_hooks:add(webadmin_page_host, Host,
|
||||||
?MODULE, webadmin_page, 50),
|
?MODULE, webadmin_page, 50),
|
||||||
ejabberd_hooks:add(webadmin_user, Host,
|
ejabberd_hooks:add(webadmin_user, Host,
|
||||||
?MODULE, webadmin_user, 50),
|
?MODULE, webadmin_user, 50),
|
||||||
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
||||||
?MODULE, webadmin_user_parse_query, 50),
|
?MODULE, webadmin_user_parse_query, 50),
|
||||||
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
|
ejabberd_hooks:add(count_offline_messages, Host,
|
||||||
|
?MODULE, count_offline_messages, 50),
|
||||||
|
MaxOfflineMsgs = gen_mod:get_opt(user_max_messages, Opts, infinity),
|
||||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
|
spawn(?MODULE, init, [MaxOfflineMsgs])).
|
||||||
|
|
||||||
loop(AccessMaxOfflineMsgs) ->
|
%% MaxOfflineMsgs is either infinity of integer > 0
|
||||||
|
init(infinity) ->
|
||||||
|
loop(infinity);
|
||||||
|
init(MaxOfflineMsgs)
|
||||||
|
when is_integer(MaxOfflineMsgs), MaxOfflineMsgs > 0 ->
|
||||||
|
loop(MaxOfflineMsgs).
|
||||||
|
|
||||||
|
loop(MaxOfflineMsgs) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{us=US} = Msg ->
|
#offline_msg{us=US} = Msg ->
|
||||||
Msgs = receive_all(US, [Msg]),
|
Msgs = receive_all(US, [Msg]),
|
||||||
Len = length(Msgs),
|
Len = length(Msgs),
|
||||||
{User, Host} = US,
|
|
||||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
|
||||||
User, Host),
|
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
%% Only count messages if needed:
|
%% Only count messages if needed:
|
||||||
Count = if MaxOfflineMsgs =/= infinity ->
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
@ -118,18 +116,9 @@ loop(AccessMaxOfflineMsgs) ->
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F),
|
mnesia:transaction(F),
|
||||||
loop(AccessMaxOfflineMsgs);
|
loop(MaxOfflineMsgs);
|
||||||
_ ->
|
_ ->
|
||||||
loop(AccessMaxOfflineMsgs)
|
loop(MaxOfflineMsgs)
|
||||||
end.
|
|
||||||
|
|
||||||
%% Function copied from ejabberd_sm.erl:
|
|
||||||
get_max_user_messages(AccessRule, LUser, Host) ->
|
|
||||||
case acl:match_rule(
|
|
||||||
Host, AccessRule, jlib:make_jid(LUser, Host, "")) of
|
|
||||||
Max when is_integer(Max) -> Max;
|
|
||||||
infinity -> infinity;
|
|
||||||
_ -> ?MAX_USER_MESSAGES
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
receive_all(US, Msgs) ->
|
receive_all(US, Msgs) ->
|
||||||
@ -150,8 +139,6 @@ stop(Host) ->
|
|||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:delete(anonymous_purge_hook, Host,
|
ejabberd_hooks:delete(anonymous_purge_hook, Host,
|
||||||
?MODULE, remove_user, 50),
|
?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(webadmin_page_host, Host,
|
ejabberd_hooks:delete(webadmin_page_host, Host,
|
||||||
?MODULE, webadmin_page, 50),
|
?MODULE, webadmin_page, 50),
|
||||||
ejabberd_hooks:delete(webadmin_user, Host,
|
ejabberd_hooks:delete(webadmin_user, Host,
|
||||||
@ -162,27 +149,12 @@ stop(Host) ->
|
|||||||
exit(whereis(Proc), stop),
|
exit(whereis(Proc), stop),
|
||||||
{wait, Proc}.
|
{wait, Proc}.
|
||||||
|
|
||||||
get_sm_features(Acc, _From, _To, "", _Lang) ->
|
|
||||||
Feats = case Acc of
|
|
||||||
{result, I} -> I;
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
{result, Feats ++ [?NS_FEATURE_MSGOFFLINE]};
|
|
||||||
|
|
||||||
get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
|
|
||||||
%% override all lesser features...
|
|
||||||
{result, []};
|
|
||||||
|
|
||||||
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
|
|
||||||
store_packet(From, To, Packet) ->
|
store_packet(From, To, Packet) ->
|
||||||
Type = xml:get_tag_attr_s("type", Packet),
|
Type = xml:get_tag_attr_s("type", Packet),
|
||||||
if
|
if
|
||||||
(Type /= "error") and (Type /= "groupchat") and
|
(Type /= "error") and (Type /= "groupchat") and
|
||||||
(Type /= "headline") ->
|
(Type /= "headline") ->
|
||||||
case check_event_chatstates(From, To, Packet) of
|
case check_event(From, To, Packet) of
|
||||||
true ->
|
true ->
|
||||||
#jid{luser = LUser, lserver = LServer} = To,
|
#jid{luser = LUser, lserver = LServer} = To,
|
||||||
TimeStamp = now(),
|
TimeStamp = now(),
|
||||||
@ -203,22 +175,12 @@ store_packet(From, To, Packet) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Check if the packet has any content about XEP-0022 or XEP-0085
|
check_event(From, To, Packet) ->
|
||||||
check_event_chatstates(From, To, Packet) ->
|
|
||||||
{xmlelement, Name, Attrs, Els} = Packet,
|
{xmlelement, Name, Attrs, Els} = Packet,
|
||||||
case find_x_event_chatstates(Els, {false, false, false}) of
|
case find_x_event(Els) of
|
||||||
%% There wasn't any x:event or chatstates subelements
|
false ->
|
||||||
{false, false, _} ->
|
|
||||||
true;
|
true;
|
||||||
%% There a chatstates subelement and other stuff, but no x:event
|
El ->
|
||||||
{false, CEl, true} when CEl /= false ->
|
|
||||||
true;
|
|
||||||
%% There was only a subelement: a chatstates
|
|
||||||
{false, CEl, false} when CEl /= false ->
|
|
||||||
%% Don't allow offline storage
|
|
||||||
false;
|
|
||||||
%% There was an x:event element, and maybe also other stuff
|
|
||||||
{El, _, _} when El /= false ->
|
|
||||||
case xml:get_subtag(El, "id") of
|
case xml:get_subtag(El, "id") of
|
||||||
false ->
|
false ->
|
||||||
case xml:get_subtag(El, "offline") of
|
case xml:get_subtag(El, "offline") of
|
||||||
@ -246,19 +208,16 @@ check_event_chatstates(From, To, Packet) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Check if the packet has subelements about XEP-0022, XEP-0085 or other
|
find_x_event([]) ->
|
||||||
find_x_event_chatstates([], Res) ->
|
false;
|
||||||
Res;
|
find_x_event([{xmlcdata, _} | Els]) ->
|
||||||
find_x_event_chatstates([{xmlcdata, _} | Els], Res) ->
|
find_x_event(Els);
|
||||||
find_x_event_chatstates(Els, Res);
|
find_x_event([El | Els]) ->
|
||||||
find_x_event_chatstates([El | Els], {A, B, C}) ->
|
|
||||||
case xml:get_tag_attr_s("xmlns", El) of
|
case xml:get_tag_attr_s("xmlns", El) of
|
||||||
?NS_EVENT ->
|
?NS_EVENT ->
|
||||||
find_x_event_chatstates(Els, {El, B, C});
|
El;
|
||||||
?NS_CHATSTATES ->
|
|
||||||
find_x_event_chatstates(Els, {A, El, C});
|
|
||||||
_ ->
|
_ ->
|
||||||
find_x_event_chatstates(Els, {A, B, true})
|
find_x_event(Els)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
find_x_expire(_, []) ->
|
find_x_expire(_, []) ->
|
||||||
@ -307,13 +266,6 @@ resend_offline_messages(User, Server) ->
|
|||||||
{xmlelement, Name, Attrs,
|
{xmlelement, Name, Attrs,
|
||||||
Els ++
|
Els ++
|
||||||
[jlib:timestamp_to_xml(
|
[jlib:timestamp_to_xml(
|
||||||
calendar:now_to_universal_time(
|
|
||||||
R#offline_msg.timestamp),
|
|
||||||
utc,
|
|
||||||
jlib:make_jid("", Server, ""),
|
|
||||||
"Offline Storage"),
|
|
||||||
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
|
||||||
jlib:timestamp_to_xml(
|
|
||||||
calendar:now_to_universal_time(
|
calendar:now_to_universal_time(
|
||||||
R#offline_msg.timestamp))]}}
|
R#offline_msg.timestamp))]}}
|
||||||
end,
|
end,
|
||||||
@ -343,13 +295,6 @@ pop_offline_messages(Ls, User, Server) ->
|
|||||||
{xmlelement, Name, Attrs,
|
{xmlelement, Name, Attrs,
|
||||||
Els ++
|
Els ++
|
||||||
[jlib:timestamp_to_xml(
|
[jlib:timestamp_to_xml(
|
||||||
calendar:now_to_universal_time(
|
|
||||||
R#offline_msg.timestamp),
|
|
||||||
utc,
|
|
||||||
jlib:make_jid("", Server, ""),
|
|
||||||
"Offline Storage"),
|
|
||||||
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
|
||||||
jlib:timestamp_to_xml(
|
|
||||||
calendar:now_to_universal_time(
|
calendar:now_to_universal_time(
|
||||||
R#offline_msg.timestamp))]}}
|
R#offline_msg.timestamp))]}}
|
||||||
end,
|
end,
|
||||||
@ -367,7 +312,6 @@ pop_offline_messages(Ls, User, Server) ->
|
|||||||
Ls
|
Ls
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
remove_expired_messages() ->
|
remove_expired_messages() ->
|
||||||
TimeStamp = now(),
|
TimeStamp = now(),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
@ -530,9 +474,8 @@ webadmin_page(Acc, _, _) -> Acc.
|
|||||||
user_queue(User, Server, Query, Lang) ->
|
user_queue(User, Server, Query, Lang) ->
|
||||||
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
|
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
|
||||||
Res = user_queue_parse_query(US, Query),
|
Res = user_queue_parse_query(US, Query),
|
||||||
MsgsAll = lists:keysort(#offline_msg.timestamp,
|
Msgs = lists:keysort(#offline_msg.timestamp,
|
||||||
mnesia:dirty_read({offline_msg, US})),
|
mnesia:dirty_read({offline_msg, US})),
|
||||||
Msgs = get_messages_subset(User, Server, MsgsAll),
|
|
||||||
FMsgs =
|
FMsgs =
|
||||||
lists:map(
|
lists:map(
|
||||||
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
||||||
@ -614,32 +557,9 @@ user_queue_parse_query(US, Query) ->
|
|||||||
us_to_list({User, Server}) ->
|
us_to_list({User, Server}) ->
|
||||||
jlib:jid_to_string({User, Server, ""}).
|
jlib:jid_to_string({User, Server, ""}).
|
||||||
|
|
||||||
get_queue_length(User, Server) ->
|
|
||||||
length(mnesia:dirty_read({offline_msg, {User, Server}})).
|
|
||||||
|
|
||||||
get_messages_subset(User, Host, MsgsAll) ->
|
|
||||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
|
||||||
max_user_offline_messages),
|
|
||||||
MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
|
|
||||||
Number when is_integer(Number) -> Number;
|
|
||||||
_ -> 100
|
|
||||||
end,
|
|
||||||
Length = length(MsgsAll),
|
|
||||||
get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
|
|
||||||
|
|
||||||
get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
|
|
||||||
MsgsAll;
|
|
||||||
get_messages_subset2(Max, Length, MsgsAll) ->
|
|
||||||
FirstN = Max,
|
|
||||||
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
|
|
||||||
MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
|
|
||||||
NoJID = jlib:make_jid("...", "...", ""),
|
|
||||||
IntermediateMsg = #offline_msg{timestamp = now(), from = NoJID, to = NoJID,
|
|
||||||
packet = {xmlelement, "...", [], []}},
|
|
||||||
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
|
||||||
|
|
||||||
webadmin_user(Acc, User, Server, Lang) ->
|
webadmin_user(Acc, User, Server, Lang) ->
|
||||||
QueueLen = get_queue_length(jlib:nodeprep(User), jlib:nameprep(Server)),
|
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
|
||||||
|
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
|
||||||
FQueueLen = [?AC("queue/",
|
FQueueLen = [?AC("queue/",
|
||||||
integer_to_list(QueueLen))],
|
integer_to_list(QueueLen))],
|
||||||
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
||||||
@ -663,3 +583,23 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
|||||||
end;
|
end;
|
||||||
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
|
|
||||||
|
%% ------------------------------------------------
|
||||||
|
%% mod_offline: number of messages quota management
|
||||||
|
|
||||||
|
count_offline_messages(_Acc, User, Server) ->
|
||||||
|
LUser = jlib:nodeprep(User),
|
||||||
|
LServer = jlib:nameprep(Server),
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
p1_mnesia:count_records(
|
||||||
|
offline_msg,
|
||||||
|
#offline_msg{us=US, _='_'})
|
||||||
|
end,
|
||||||
|
N = case catch mnesia:async_dirty(F) of
|
||||||
|
I when is_integer(I) -> I;
|
||||||
|
_ -> 0
|
||||||
|
end,
|
||||||
|
{stop, N}.
|
||||||
|
|
||||||
|
@ -32,16 +32,15 @@
|
|||||||
-export([count_offline_messages/2]).
|
-export([count_offline_messages/2]).
|
||||||
|
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
loop/2,
|
init/2,
|
||||||
stop/1,
|
stop/1,
|
||||||
store_packet/3,
|
store_packet/3,
|
||||||
pop_offline_messages/3,
|
pop_offline_messages/3,
|
||||||
get_sm_features/5,
|
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
get_queue_length/2,
|
|
||||||
webadmin_page/3,
|
webadmin_page/3,
|
||||||
webadmin_user/4,
|
webadmin_user/4,
|
||||||
webadmin_user_parse_query/5]).
|
webadmin_user_parse_query/5,
|
||||||
|
count_offline_messages/3]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
@ -53,9 +52,6 @@
|
|||||||
-define(PROCNAME, ejabberd_offline).
|
-define(PROCNAME, ejabberd_offline).
|
||||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||||
|
|
||||||
%% default value for the maximum number of user messages
|
|
||||||
-define(MAX_USER_MESSAGES, infinity).
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
ejabberd_hooks:add(offline_message_hook, Host,
|
ejabberd_hooks:add(offline_message_hook, Host,
|
||||||
?MODULE, store_packet, 50),
|
?MODULE, store_packet, 50),
|
||||||
@ -65,27 +61,30 @@ start(Host, Opts) ->
|
|||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:add(anonymous_purge_hook, Host,
|
ejabberd_hooks:add(anonymous_purge_hook, Host,
|
||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:add(disco_sm_features, Host,
|
|
||||||
?MODULE, get_sm_features, 50),
|
|
||||||
ejabberd_hooks:add(disco_local_features, Host,
|
|
||||||
?MODULE, get_sm_features, 50),
|
|
||||||
ejabberd_hooks:add(webadmin_page_host, Host,
|
ejabberd_hooks:add(webadmin_page_host, Host,
|
||||||
?MODULE, webadmin_page, 50),
|
?MODULE, webadmin_page, 50),
|
||||||
ejabberd_hooks:add(webadmin_user, Host,
|
ejabberd_hooks:add(webadmin_user, Host,
|
||||||
?MODULE, webadmin_user, 50),
|
?MODULE, webadmin_user, 50),
|
||||||
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
||||||
?MODULE, webadmin_user_parse_query, 50),
|
?MODULE, webadmin_user_parse_query, 50),
|
||||||
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
|
ejabberd_hooks:add(count_offline_messages, Host,
|
||||||
|
?MODULE, count_offline_messages, 50),
|
||||||
|
MaxOfflineMsgs = gen_mod:get_opt(user_max_messages, Opts, infinity),
|
||||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
|
spawn(?MODULE, init, [Host, MaxOfflineMsgs])).
|
||||||
|
|
||||||
loop(Host, AccessMaxOfflineMsgs) ->
|
%% MaxOfflineMsgs is either infinity of integer > 0
|
||||||
|
init(Host, infinity) ->
|
||||||
|
loop(Host, infinity);
|
||||||
|
init(Host, MaxOfflineMsgs)
|
||||||
|
when is_integer(MaxOfflineMsgs), MaxOfflineMsgs > 0 ->
|
||||||
|
loop(Host, MaxOfflineMsgs).
|
||||||
|
|
||||||
|
loop(Host, MaxOfflineMsgs) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{user = User} = Msg ->
|
#offline_msg{user = User} = Msg ->
|
||||||
Msgs = receive_all(User, [Msg]),
|
Msgs = receive_all(User, [Msg]),
|
||||||
Len = length(Msgs),
|
Len = length(Msgs),
|
||||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
|
||||||
User, Host),
|
|
||||||
|
|
||||||
%% Only count existing messages if needed:
|
%% Only count existing messages if needed:
|
||||||
Count = if MaxOfflineMsgs =/= infinity ->
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
@ -112,18 +111,12 @@ loop(Host, AccessMaxOfflineMsgs) ->
|
|||||||
Packet = {xmlelement, Name, Attrs2,
|
Packet = {xmlelement, Name, Attrs2,
|
||||||
Els ++
|
Els ++
|
||||||
[jlib:timestamp_to_xml(
|
[jlib:timestamp_to_xml(
|
||||||
calendar:now_to_universal_time(
|
|
||||||
M#offline_msg.timestamp),
|
|
||||||
utc,
|
|
||||||
jlib:make_jid("", Host, ""),
|
|
||||||
"Offline Storage"),
|
|
||||||
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
|
||||||
jlib:timestamp_to_xml(
|
|
||||||
calendar:now_to_universal_time(
|
calendar:now_to_universal_time(
|
||||||
M#offline_msg.timestamp))]},
|
M#offline_msg.timestamp))]},
|
||||||
XML =
|
XML =
|
||||||
ejabberd_odbc:escape(
|
ejabberd_odbc:escape(
|
||||||
xml:element_to_binary(Packet)),
|
lists:flatten(
|
||||||
|
xml:element_to_string(Packet))),
|
||||||
odbc_queries:add_spool_sql(Username, XML)
|
odbc_queries:add_spool_sql(Username, XML)
|
||||||
end, Msgs),
|
end, Msgs),
|
||||||
case catch odbc_queries:add_spool(Host, Query) of
|
case catch odbc_queries:add_spool(Host, Query) of
|
||||||
@ -135,18 +128,9 @@ loop(Host, AccessMaxOfflineMsgs) ->
|
|||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
loop(Host, AccessMaxOfflineMsgs);
|
loop(Host, MaxOfflineMsgs);
|
||||||
_ ->
|
_ ->
|
||||||
loop(Host, AccessMaxOfflineMsgs)
|
loop(Host, MaxOfflineMsgs)
|
||||||
end.
|
|
||||||
|
|
||||||
%% Function copied from ejabberd_sm.erl:
|
|
||||||
get_max_user_messages(AccessRule, LUser, Host) ->
|
|
||||||
case acl:match_rule(
|
|
||||||
Host, AccessRule, jlib:make_jid(LUser, Host, "")) of
|
|
||||||
Max when is_integer(Max) -> Max;
|
|
||||||
infinity -> infinity;
|
|
||||||
_ -> ?MAX_USER_MESSAGES
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
receive_all(Username, Msgs) ->
|
receive_all(Username, Msgs) ->
|
||||||
@ -167,8 +151,6 @@ stop(Host) ->
|
|||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
ejabberd_hooks:delete(anonymous_purge_hook, Host,
|
ejabberd_hooks:delete(anonymous_purge_hook, Host,
|
||||||
?MODULE, remove_user, 50),
|
?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(webadmin_page_host, Host,
|
ejabberd_hooks:delete(webadmin_page_host, Host,
|
||||||
?MODULE, webadmin_page, 50),
|
?MODULE, webadmin_page, 50),
|
||||||
ejabberd_hooks:delete(webadmin_user, Host,
|
ejabberd_hooks:delete(webadmin_user, Host,
|
||||||
@ -179,27 +161,12 @@ stop(Host) ->
|
|||||||
exit(whereis(Proc), stop),
|
exit(whereis(Proc), stop),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
get_sm_features(Acc, _From, _To, "", _Lang) ->
|
|
||||||
Feats = case Acc of
|
|
||||||
{result, I} -> I;
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
{result, Feats ++ [?NS_FEATURE_MSGOFFLINE]};
|
|
||||||
|
|
||||||
get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
|
|
||||||
%% override all lesser features...
|
|
||||||
{result, []};
|
|
||||||
|
|
||||||
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
|
|
||||||
store_packet(From, To, Packet) ->
|
store_packet(From, To, Packet) ->
|
||||||
Type = xml:get_tag_attr_s("type", Packet),
|
Type = xml:get_tag_attr_s("type", Packet),
|
||||||
if
|
if
|
||||||
(Type /= "error") and (Type /= "groupchat") and
|
(Type /= "error") and (Type /= "groupchat") and
|
||||||
(Type /= "headline") ->
|
(Type /= "headline") ->
|
||||||
case check_event_chatstates(From, To, Packet) of
|
case check_event(From, To, Packet) of
|
||||||
true ->
|
true ->
|
||||||
#jid{luser = LUser} = To,
|
#jid{luser = LUser} = To,
|
||||||
TimeStamp = now(),
|
TimeStamp = now(),
|
||||||
@ -220,22 +187,12 @@ store_packet(From, To, Packet) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Check if the packet has any content about XEP-0022 or XEP-0085
|
check_event(From, To, Packet) ->
|
||||||
check_event_chatstates(From, To, Packet) ->
|
|
||||||
{xmlelement, Name, Attrs, Els} = Packet,
|
{xmlelement, Name, Attrs, Els} = Packet,
|
||||||
case find_x_event_chatstates(Els, {false, false, false}) of
|
case find_x_event(Els) of
|
||||||
%% There wasn't any x:event or chatstates subelements
|
false ->
|
||||||
{false, false, _} ->
|
|
||||||
true;
|
true;
|
||||||
%% There a chatstates subelement and other stuff, but no x:event
|
El ->
|
||||||
{false, CEl, true} when CEl /= false ->
|
|
||||||
true;
|
|
||||||
%% There was only a subelement: a chatstates
|
|
||||||
{false, CEl, false} when CEl /= false ->
|
|
||||||
%% Don't allow offline storage
|
|
||||||
false;
|
|
||||||
%% There was an x:event element, and maybe also other stuff
|
|
||||||
{El, _, _} when El /= false ->
|
|
||||||
case xml:get_subtag(El, "id") of
|
case xml:get_subtag(El, "id") of
|
||||||
false ->
|
false ->
|
||||||
case xml:get_subtag(El, "offline") of
|
case xml:get_subtag(El, "offline") of
|
||||||
@ -263,19 +220,16 @@ check_event_chatstates(From, To, Packet) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Check if the packet has subelements about XEP-0022, XEP-0085 or other
|
find_x_event([]) ->
|
||||||
find_x_event_chatstates([], Res) ->
|
false;
|
||||||
Res;
|
find_x_event([{xmlcdata, _} | Els]) ->
|
||||||
find_x_event_chatstates([{xmlcdata, _} | Els], Res) ->
|
find_x_event(Els);
|
||||||
find_x_event_chatstates(Els, Res);
|
find_x_event([El | Els]) ->
|
||||||
find_x_event_chatstates([El | Els], {A, B, C}) ->
|
|
||||||
case xml:get_tag_attr_s("xmlns", El) of
|
case xml:get_tag_attr_s("xmlns", El) of
|
||||||
?NS_EVENT ->
|
?NS_EVENT ->
|
||||||
find_x_event_chatstates(Els, {El, B, C});
|
El;
|
||||||
?NS_CHATSTATES ->
|
|
||||||
find_x_event_chatstates(Els, {A, El, C});
|
|
||||||
_ ->
|
_ ->
|
||||||
find_x_event_chatstates(Els, {A, B, true})
|
find_x_event(Els)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
find_x_expire(_, []) ->
|
find_x_expire(_, []) ->
|
||||||
@ -375,7 +329,7 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
Username = ejabberd_odbc:escape(LUser),
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
Res = user_queue_parse_query(Username, LServer, Query),
|
Res = user_queue_parse_query(Username, LServer, Query),
|
||||||
MsgsAll = case catch ejabberd_odbc:sql_query(
|
Msgs = case catch ejabberd_odbc:sql_query(
|
||||||
LServer,
|
LServer,
|
||||||
["select username, xml from spool"
|
["select username, xml from spool"
|
||||||
" where username='", Username, "'"
|
" where username='", Username, "'"
|
||||||
@ -393,7 +347,6 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
Msgs = get_messages_subset(User, Server, MsgsAll),
|
|
||||||
FMsgs =
|
FMsgs =
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
|
fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
|
||||||
@ -480,8 +433,11 @@ user_queue_parse_query(Username, LServer, Query) ->
|
|||||||
us_to_list({User, Server}) ->
|
us_to_list({User, Server}) ->
|
||||||
jlib:jid_to_string({User, Server, ""}).
|
jlib:jid_to_string({User, Server, ""}).
|
||||||
|
|
||||||
get_queue_length(Username, LServer) ->
|
webadmin_user(Acc, User, Server, Lang) ->
|
||||||
case catch ejabberd_odbc:sql_query(
|
LUser = jlib:nodeprep(User),
|
||||||
|
LServer = jlib:nameprep(Server),
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
QueueLen = case catch ejabberd_odbc:sql_query(
|
||||||
LServer,
|
LServer,
|
||||||
["select count(*) from spool"
|
["select count(*) from spool"
|
||||||
" where username='", Username, "';"]) of
|
" where username='", Username, "';"]) of
|
||||||
@ -489,32 +445,7 @@ get_queue_length(Username, LServer) ->
|
|||||||
SCount;
|
SCount;
|
||||||
_ ->
|
_ ->
|
||||||
0
|
0
|
||||||
end.
|
|
||||||
|
|
||||||
get_messages_subset(User, Host, MsgsAll) ->
|
|
||||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
|
||||||
max_user_offline_messages),
|
|
||||||
MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
|
|
||||||
Number when is_integer(Number) -> Number;
|
|
||||||
_ -> 100
|
|
||||||
end,
|
end,
|
||||||
Length = length(MsgsAll),
|
|
||||||
get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
|
|
||||||
|
|
||||||
get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
|
|
||||||
MsgsAll;
|
|
||||||
get_messages_subset2(Max, Length, MsgsAll) ->
|
|
||||||
FirstN = Max,
|
|
||||||
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
|
|
||||||
MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
|
|
||||||
IntermediateMsg = {xmlelement, "...", [], []},
|
|
||||||
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
|
||||||
|
|
||||||
webadmin_user(Acc, User, Server, Lang) ->
|
|
||||||
LUser = jlib:nodeprep(User),
|
|
||||||
LServer = jlib:nameprep(Server),
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
QueueLen = get_queue_length(Username, LServer),
|
|
||||||
FQueueLen = [?AC("queue/", QueueLen)],
|
FQueueLen = [?AC("queue/", QueueLen)],
|
||||||
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
||||||
|
|
||||||
@ -546,3 +477,30 @@ count_offline_messages(LUser, LServer) ->
|
|||||||
_ ->
|
_ ->
|
||||||
0
|
0
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
count_offline_messages(_Acc, User, Server) ->
|
||||||
|
LUser = jlib:nodeprep(User),
|
||||||
|
LServer = jlib:nameprep(Server),
|
||||||
|
Num = case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
["select xml from spool"
|
||||||
|
" where username='", LUser, "';"]) of
|
||||||
|
{selected, ["xml"], Rs} ->
|
||||||
|
lists:foldl(
|
||||||
|
fun({XML}, Acc) ->
|
||||||
|
case xml_stream:parse_element(XML) of
|
||||||
|
{error, _Reason} ->
|
||||||
|
Acc;
|
||||||
|
El ->
|
||||||
|
case xml:get_subtag(El, "body") of
|
||||||
|
false ->
|
||||||
|
Acc;
|
||||||
|
_ ->
|
||||||
|
Acc + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, 0, Rs);
|
||||||
|
_ ->
|
||||||
|
0
|
||||||
|
end,
|
||||||
|
{stop, Num}.
|
||||||
|
26
src/xml.erl
26
src/xml.erl
@ -31,6 +31,7 @@
|
|||||||
element_to_binary/1,
|
element_to_binary/1,
|
||||||
crypt/1, make_text_node/1,
|
crypt/1, make_text_node/1,
|
||||||
remove_cdata/1,
|
remove_cdata/1,
|
||||||
|
remove_subtags/3,
|
||||||
get_cdata/1, get_tag_cdata/1,
|
get_cdata/1, get_tag_cdata/1,
|
||||||
get_attr/2, get_attr_s/2,
|
get_attr/2, get_attr_s/2,
|
||||||
get_tag_attr/2, get_tag_attr_s/2,
|
get_tag_attr/2, get_tag_attr_s/2,
|
||||||
@ -186,6 +187,31 @@ remove_cdata_p(_) -> false.
|
|||||||
|
|
||||||
remove_cdata(L) -> [E || E <- L, remove_cdata_p(E)].
|
remove_cdata(L) -> [E || E <- L, remove_cdata_p(E)].
|
||||||
|
|
||||||
|
%% TODO: Make more generic.
|
||||||
|
%% For now only support all parameters:
|
||||||
|
%% xml:remove_subtags({xmlelement,"message", [{"id","81be72"}],[{xmlelement,"on-sender-server",[{"xmlns","urn:xmpp:receipts"},{"server","text-one.com"}], []}]}, "on-sender-server", {"xmlns","urn:xmpp:receipts"}).
|
||||||
|
remove_subtags({xmlelement, TagName, TagAttrs, Els}, Name, Attr) ->
|
||||||
|
{xmlelement, TagName, TagAttrs, remove_subtags1(Els, [], Name, Attr)}.
|
||||||
|
|
||||||
|
remove_subtags1([], NewEls, _Name, _Attr) ->
|
||||||
|
lists:reverse(NewEls);
|
||||||
|
remove_subtags1([El | Els], NewEls, Name, {AttrName, AttrValue} = Attr) ->
|
||||||
|
case El of
|
||||||
|
{xmlelement, Name, Attrs, _} ->
|
||||||
|
case get_attr(AttrName, Attrs) of
|
||||||
|
false ->
|
||||||
|
remove_subtags1(Els, [El|NewEls], Name, Attr);
|
||||||
|
{value, AttrValue} ->
|
||||||
|
remove_subtags1(Els, NewEls, Name, Attr);
|
||||||
|
_ ->
|
||||||
|
remove_subtags1(Els, [El|NewEls], Name, Attr)
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
remove_subtags1(Els, [El|NewEls], Name, Attr)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
get_cdata(L) ->
|
get_cdata(L) ->
|
||||||
binary_to_list(list_to_binary(get_cdata(L, ""))).
|
binary_to_list(list_to_binary(get_cdata(L, ""))).
|
||||||
|
|
||||||
|
@ -76,6 +76,9 @@ process_data(CallbackPid, Stack, Data) ->
|
|||||||
{?XML_CDATA, CData} ->
|
{?XML_CDATA, CData} ->
|
||||||
case Stack of
|
case Stack of
|
||||||
[El] ->
|
[El] ->
|
||||||
|
catch gen_fsm:send_all_state_event(
|
||||||
|
CallbackPid,
|
||||||
|
{xmlstreamcdata, CData}),
|
||||||
[El];
|
[El];
|
||||||
%% Merge CDATA nodes if they are contiguous
|
%% Merge CDATA nodes if they are contiguous
|
||||||
%% This does not change the semantic: the split in
|
%% This does not change the semantic: the split in
|
||||||
@ -89,7 +92,8 @@ process_data(CallbackPid, Stack, Data) ->
|
|||||||
[{xmlelement, Name, Attrs, Els} | Tail] ->
|
[{xmlelement, Name, Attrs, Els} | Tail] ->
|
||||||
[{xmlelement, Name, Attrs, [{xmlcdata, CData} | Els]} |
|
[{xmlelement, Name, Attrs, [{xmlcdata, CData} | Els]} |
|
||||||
Tail];
|
Tail];
|
||||||
[] -> []
|
[] ->
|
||||||
|
[]
|
||||||
end;
|
end;
|
||||||
{?XML_ERROR, Err} ->
|
{?XML_ERROR, Err} ->
|
||||||
catch gen_fsm:send_event(CallbackPid, {xmlstreamerror, Err})
|
catch gen_fsm:send_event(CallbackPid, {xmlstreamerror, Err})
|
||||||
|
Loading…
Reference in New Issue
Block a user