24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-07-19 00:11:01 +02:00

Merge branch '2.2.x' into websockets

This commit is contained in:
Eric Cestari 2010-09-16 14:44:44 +02:00
commit cb54444f00
8 changed files with 231 additions and 84 deletions

View File

@ -1192,6 +1192,12 @@ handle_event({del_rosteritem, IJID}, StateName, StateData) ->
NewStateData = roster_change(IJID, none, StateData),
fsm_next_state(StateName, NewStateData);
handle_event({xmlstreamcdata, _}, StateName, StateData) ->
?DEBUG("cdata ping", []),
NSD1 = change_reception(StateData, true),
NSD2 = start_keepalive_timer(NSD1),
fsm_next_state(StateName, NSD2);
handle_event(_Event, StateName, StateData) ->
fsm_next_state(StateName, StateData).

View File

@ -1 +0,0 @@
-define(MODS, []).

View File

@ -17,7 +17,10 @@
push_notification/8,
enable_offline_notification/5,
disable_notification/3,
receive_offline_packet/3]).
receive_offline_packet/3,
resend_badge/1,
multi_resend_badge/1,
offline_resend_badge/0]).
%% Debug commands
-export([get_token_by_jid/1]).
@ -232,6 +235,50 @@ receive_offline_packet(From, To, Packet) ->
ok
end.
resend_badge(To) ->
Host = To#jid.lserver,
case gen_mod:is_loaded(Host, mod_applepush) of
true ->
case lookup_cache(To) of
false ->
{error, "no cached data for the user"};
{ID, AppID, SendBody, SendFrom} ->
?DEBUG("lookup: ~p~n", [{ID, AppID, SendBody, SendFrom}]),
PushService = get_push_service(Host, To, AppID),
ServiceJID = jlib:make_jid("", PushService, ""),
Offline = ejabberd_hooks:run_fold(
count_offline_messages,
Host,
0,
[To#jid.luser, Host]),
if
Offline == 0 ->
ok;
true ->
Badge = integer_to_list(Offline),
DeviceID = erlang:integer_to_list(ID, 16),
Packet1 =
{xmlelement, "message", [],
[{xmlelement, "push", [{"xmlns", ?NS_P1_PUSH}],
[{xmlelement, "id", [],
[{xmlcdata, DeviceID}]},
{xmlelement, "badge", [],
[{xmlcdata, Badge}]}]}]},
ejabberd_router:route(To, ServiceJID, Packet1)
end
end;
false ->
{error, "mod_applepush is not loaded"}
end.
multi_resend_badge(JIDs) ->
lists:foreach(fun resend_badge/1, JIDs).
offline_resend_badge() ->
USs = mnesia:dirty_all_keys(applepush_cache),
JIDs = lists:map(fun({U, S}) -> jlib:make_jid(U, S, "") end, USs),
multi_resend_badge(JIDs).
lookup_cache(JID) ->
#jid{luser = LUser, lserver = LServer} = JID,
LUS = {LUser, LServer},

View File

@ -198,14 +198,16 @@ handle_info({ssl, Socket, Packet}, State)
<<8, Status, CmdID:32>> when Status /= 0 ->
case dict:find(CmdID, State#state.cmd_cache) of
{ok, {JID, _DeviceID}} ->
From = jlib:make_jid("", State#state.host, ""),
ejabberd_router:route(
From, JID,
{xmlelement, "message", [],
[{xmlelement, "disable",
[{"xmlns", ?NS_P1_PUSH},
{"status", integer_to_list(Status)}],
[]}]});
?ERROR_MSG("PUSH ERROR for ~p: ~p", [JID, Status]),
%From = jlib:make_jid("", State#state.host, ""),
%ejabberd_router:route(
% From, JID,
% {xmlelement, "message", [],
% [{xmlelement, "disable",
% [{"xmlns", ?NS_P1_PUSH},
% {"status", integer_to_list(Status)}],
% []}]});
ok;
error ->
?ERROR_MSG("Unknown cmd ID ~p~n", [CmdID]),
ok
@ -408,8 +410,13 @@ make_payload(State, Msg, Badge, Sound, Sender) ->
Payloads = lists:filter(fun(S) -> S /= "" end,
[AlertPayload, BadgePayload, SoundPayload]),
Payload =
"{\"aps\":{" ++ join(Payloads, ",") ++ "},"
"\"from\":\"" ++ json_escape(Sender) ++ "\"}",
case Sender of
"" ->
"{\"aps\":{" ++ join(Payloads, ",") ++ "}}";
_ ->
"{\"aps\":{" ++ join(Payloads, ",") ++ "},"
"\"from\":\"" ++ json_escape(Sender) ++ "\"}"
end,
PayloadLen = length(Payload),
if
PayloadLen > ?MAX_PAYLOAD_SIZE ->

View File

@ -1,71 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : mod_keepalive.erl
%%% Author : Christophe romain <cromain@process-one.net>
%%% Purpose : Hidden code autoload
%%%
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%----------------------------------------------------------------------
-module(mod_keepalive).
-author('cromain@process-one.net').
-behaviour(gen_mod).
-export([start/2, stop/1, init/1]).
-include("keepalive.hrl").
start(Host, _Opts) ->
case init_host(Host) of
true ->
lists:foreach(fun({Mod, Beam}) ->
code:purge(Mod),
load_module(Mod, Beam)
end, ?MODS);
false ->
ok
end.
stop(_Host) ->
ok.
init(["pack"|Mods]) ->
Code = lists:foldl(fun(Mod, Acc) ->
case file:read_file(Mod++".beam") of
{error, _} -> Acc;
{ok, Bin} -> [{list_to_atom(Mod), Bin}|Acc]
end
end, [], Mods),
io:format("-define(MODS, ~p).", [Code]);
init(_) ->
error.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal module protection
-define(VALID_HOSTS, []). % default is unlimited use
-define(MAX_USERS, 0). % default is unlimited use
init_host(VHost) ->
case ?VALID_HOSTS of
[] -> % unlimited use
true;
ValidList -> % limited use
init_host(VHost, ValidList)
end.
init_host([], _) ->
false;
init_host(VHost, ValidEncryptedList) ->
EncryptedHost = erlang:md5(lists:reverse(VHost)),
case lists:member(EncryptedHost, ValidEncryptedList) of
true ->
case ?MAX_USERS of
0 -> true;
N -> ejabberd_auth:get_vh_registered_users_number(VHost) =< N
end;
false ->
case string:chr(VHost, $.) of
0 -> false;
Pos -> init_host(string:substr(VHost, Pos+1), ValidEncryptedList)
end
end.

View File

@ -0,0 +1,41 @@
-module(pubsub_clean).
-define(TIMEOUT, 1000*600). % 1 minute
-export([start/0, loop/0, subscribed/1, offline/1]).
start() ->
Pid = spawn(?MODULE, loop, []),
register(pubsub_clean, Pid),
Pid.
loop() ->
receive
purge -> purge()
after ?TIMEOUT -> purge()
end,
loop().
purge() ->
{Sessions, Subscriptions} = {mnesia:table_info(session,size),mnesia:table_info(pubsub_state,size)},
if Subscriptions > Sessions + 500 ->
lists:foreach(fun(K) ->
[N]=mnesia:dirty_read({pubsub_node, K}),
I=element(3,N),
lists:foreach(fun(JID) ->
mnesia:dirty_delete({pubsub_state, {JID, I}})
end, offline(subscribed(I)))
end, mnesia:dirty_all_keys(pubsub_node));
true ->
ok
end.
subscribed(NodeId) ->
lists:map(fun(S) ->
element(1,element(2,S))
end, mnesia:dirty_match_object({pubsub_state, {'_',NodeId},'_',none,subscribed})).
offline(Jids) ->
lists:filter(fun({U,S,""}) -> ejabberd_sm:get_user_resources(U,S) == [];
({U,S,R}) -> not lists:member(R,ejabberd_sm:get_user_resources(U,S))
end, Jids).

View File

@ -0,0 +1,112 @@
-module(pubsub_debug).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-compile(export_all).
nodeid(Host, Node) ->
case mnesia:dirty_read({pubsub_node, {Host, Node}}) of
[N] -> nodeid(N);
_ -> 0
end.
nodeid(N) -> N#pubsub_node.id.
nodeids() -> [nodeid(Host, Node) || {Host, Node} <- mnesia:dirty_all_keys(pubsub_node)].
nodeids_by_type(Type) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{type=Type, _='_'})].
nodeids_by_option(Key, Value) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{_='_'}), lists:member({Key, Value}, N#pubsub_node.options)].
nodeids_by_owner(JID) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{_='_'}), lists:member(JID, N#pubsub_node.owners)].
nodes_by_id(I) -> mnesia:dirty_match_object(#pubsub_node{id=I, _='_'}).
state(JID, NodeId) ->
case mnesia:dirty_read({pubsub_state, {JID, NodeId}}) of
[S] -> S;
_ -> undefined
end.
states(NodeId) -> mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, _='_'}).
stateid(S) -> element(1, S#pubsub_state.stateid).
stateids(NodeId) -> [stateid(S) || S <- states(NodeId)].
states_by_jid(JID) -> mnesia:dirty_match_object(#pubsub_state{stateid={JID, '_'}, _='_'}).
item(ItemId, NodeId) ->
case mnesia:dirty_read({pubsub_item, {ItemId, NodeId}}) of
[I] -> I;
_ -> undefined
end.
items(NodeId) -> mnesia:dirty_match_object(#pubsub_item{itemid={'_', NodeId}, _='_'}).
itemid(I) -> element(1, I#pubsub_item.itemid).
itemids(NodeId) -> [itemid(I) || I <- items(NodeId)].
items_by_id(ItemId) -> mnesia:dirty_match_object(#pubsub_item{itemid={ItemId, '_'}, _='_'}).
affiliated(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.affiliation=/=none].
subscribed(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.subscriptions=/=[]].
%subscribed(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.subscription=/=none]. %% old record
owners(NodeId) -> [stateid(S) || S <- mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, affiliation=owner, _='_'})].
orphan_items(NodeId) ->
itemids(NodeId) -- lists:foldl(fun(S, A) -> A++S#pubsub_state.items end, [], mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, _='_'})).
newer_items(NodeId, Seconds) ->
Now = calendar:universal_time(),
Oldest = calendar:seconds_to_daystime(Seconds),
[itemid(I) || I <- items(NodeId), calendar:time_difference(calendar:now_to_universal_time(element(1, I#pubsub_item.modification)), Now) < Oldest].
older_items(NodeId, Seconds) ->
Now = calendar:universal_time(),
Oldest = calendar:seconds_to_daystime(Seconds),
[itemid(I) || I <- items(NodeId), calendar:time_difference(calendar:now_to_universal_time(element(1, I#pubsub_item.modification)), Now) > Oldest].
orphan_nodes() -> [I || I <- nodeids(), owners(I)==[]].
duplicated_nodes() -> L = nodeids(), lists:usort(L -- lists:seq(1, lists:max(L))).
node_options(NodeId) ->
[N] = mnesia:dirty_match_object(#pubsub_node{id=NodeId, _='_'}),
N#pubsub_node.options.
update_node_options(Key, Value, NodeId) ->
[N] = mnesia:dirty_match_object(#pubsub_node{id=NodeId, _='_'}),
NewOptions = lists:keyreplace(Key, 1, N#pubsub_node.options, {Key, Value}),
mnesia:dirty_write(N#pubsub_node{options = NewOptions}).
check() ->
mnesia:transaction(fun() ->
case mnesia:read({pubsub_index, node}) of
[Idx] ->
Free = Idx#pubsub_index.free,
Last = Idx#pubsub_index.last,
Allocated = lists:seq(1, Last) -- Free,
NodeIds = mnesia:foldl(fun(N,A) -> [nodeid(N)|A] end, [], pubsub_node),
StateIds = lists:usort(mnesia:foldl(fun(S,A) -> [element(2, S#pubsub_state.stateid)|A] end, [], pubsub_state)),
ItemIds = lists:usort(mnesia:foldl(fun(I,A) -> [element(2, I#pubsub_item.itemid)|A] end, [], pubsub_item)),
BadNodeIds = NodeIds -- Allocated,
BadStateIds = StateIds -- NodeIds,
BadItemIds = ItemIds -- NodeIds,
Lost = Allocated -- NodeIds,
[{bad_nodes, [N#pubsub_node.nodeid || N <- lists:flatten([mnesia:match_object(#pubsub_node{id=I, _='_'}) || I <- BadNodeIds])]},
{bad_states, lists:foldl(fun(N,A) -> A++[{I,N} || I <- stateids(N)] end, [], BadStateIds)},
{bad_items, lists:foldl(fun(N,A) -> A++[{I,N} || I <- itemids(N)] end, [], BadItemIds)},
{lost_idx, Lost},
{orphaned, [I || I <- NodeIds, owners(I)==[]]},
{duplicated, lists:usort(NodeIds -- lists:seq(1, lists:max(NodeIds)))}];
_ ->
no_index
end
end).
rebuild_index() ->
mnesia:transaction(fun() ->
NodeIds = mnesia:foldl(fun(N,A) -> [nodeid(N)|A] end, [], pubsub_node),
Last = lists:max(NodeIds),
Free = lists:seq(1, Last) -- NodeIds,
mnesia:write(#pubsub_index{index = node, last = Last, free = Free})
end).
pep_subscriptions(LUser, LServer, LResource) ->
case ejabberd_sm:get_session_pid({LUser, LServer, LResource}) of
C2SPid when is_pid(C2SPid) ->
case catch ejabberd_c2s:get_subscribed(C2SPid) of
Contacts when is_list(Contacts) ->
lists:map(fun({U, S, _}) ->
io_lib:format("~s@~s", [U, S])
end, Contacts);
_ ->
[]
end;
_ ->
[]
end.

View File

@ -76,7 +76,13 @@ process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
{UTC, UTC_diff} = jlib:timestamp_to_iso(Now_universal, utc),
Seconds_diff = calendar:datetime_to_gregorian_seconds(Now_local)
- calendar:datetime_to_gregorian_seconds(Now_universal),
{Hd, Md, _} = calendar:seconds_to_time(Seconds_diff),
{Hd, Md, _} = case Seconds_diff >= 0 of
true ->
calendar:seconds_to_time(Seconds_diff);
false ->
{Hd0, Md0, Sd0} = calendar:seconds_to_time(-Seconds_diff),
{-Hd0, Md0, Sd0}
end,
{_, TZO_diff} = jlib:timestamp_to_iso({{0, 0, 0}, {0, 0, 0}}, {Hd, Md}),
IQ#iq{type = result,
sub_el = [{xmlelement, "time",