summaryrefslogtreecommitdiff
path: root/src/mod_offline_odbc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_offline_odbc.erl')
-rw-r--r--src/mod_offline_odbc.erl242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl
new file mode 100644
index 00000000..abc06b47
--- /dev/null
+++ b/src/mod_offline_odbc.erl
@@ -0,0 +1,242 @@
+%%%----------------------------------------------------------------------
+%%% File : mod_offline_odbc.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(mod_offline_odbc).
+-author('alexey@sevcom.net').
+
+-behaviour(gen_mod).
+
+-export([start/1,
+ init/0,
+ stop/0,
+ store_packet/3,
+ pop_offline_messages/2,
+ remove_user/1]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+
+-record(offline_msg, {user, timestamp, expire, from, to, packet}).
+
+-define(PROCNAME, ejabberd_offline).
+-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
+
+start(_) ->
+ % TODO: remove
+ ejabberd_odbc:start(),
+ ejabberd_hooks:add(offline_message_hook,
+ ?MODULE, store_packet, 50),
+ ejabberd_hooks:add(offline_subscription_hook,
+ ?MODULE, store_packet, 50),
+ ejabberd_hooks:add(resend_offline_messages_hook,
+ ?MODULE, pop_offline_messages, 50),
+ register(?PROCNAME, spawn(?MODULE, init, [])).
+
+init() ->
+ loop().
+
+loop() ->
+ receive
+ #offline_msg{} = Msg ->
+ Msgs = receive_all([Msg]),
+ % TODO
+ Query = lists:map(
+ fun(M) ->
+ Username =
+ ejabberd_odbc:escape(
+ (M#offline_msg.to)#jid.luser),
+ From = M#offline_msg.from,
+ To = M#offline_msg.to,
+ {xmlelement, Name, Attrs, Els} =
+ M#offline_msg.packet,
+ Attrs2 = jlib:replace_from_to_attrs(
+ jlib:jid_to_string(From),
+ jlib:jid_to_string(To),
+ Attrs),
+ Packet = {xmlelement, Name, Attrs2,
+ Els ++
+ [jlib:timestamp_to_xml(
+ calendar:now_to_universal_time(
+ M#offline_msg.timestamp))]},
+ XML =
+ ejabberd_odbc:escape(
+ lists:flatten(
+ xml:element_to_string(Packet))),
+ ["insert into spool(username, xml) "
+ "values ('", Username, "', '",
+ XML,
+ "');"]
+ end, Msgs),
+ case catch ejabberd_odbc:sql_query(
+ ["begin; ", Query, " commit"]) of
+ {'EXIT', Reason} ->
+ ?ERROR_MSG("~p~n", [Reason]);
+ _ ->
+ ok
+ end,
+ loop();
+ _ ->
+ loop()
+ end.
+
+receive_all(Msgs) ->
+ receive
+ #offline_msg{} = Msg ->
+ receive_all([Msg | Msgs])
+ after 0 ->
+ Msgs
+ end.
+
+
+stop() ->
+ ejabberd_hooks:delete(offline_message_hook,
+ ?MODULE, store_packet, 50),
+ ejabberd_hooks:delete(offline_subscription_hook,
+ ?MODULE, store_packet, 50),
+ ejabberd_hooks:delete(resend_offline_messages_hook,
+ ?MODULE, pop_offline_messages, 50),
+ exit(whereis(?PROCNAME), stop),
+ ok.
+
+store_packet(From, To, Packet) ->
+ Type = xml:get_tag_attr_s("type", Packet),
+ if
+ (Type /= "error") and (Type /= "groupchat") ->
+ case check_event(From, To, Packet) of
+ true ->
+ #jid{luser = LUser} = To,
+ TimeStamp = now(),
+ {xmlelement, _Name, _Attrs, Els} = Packet,
+ Expire = find_x_expire(TimeStamp, Els),
+ ?PROCNAME ! #offline_msg{user = LUser,
+ timestamp = TimeStamp,
+ expire = Expire,
+ from = From,
+ to = To,
+ packet = Packet},
+ stop;
+ _ ->
+ ok
+ end;
+ true ->
+ ok
+ end.
+
+check_event(From, To, Packet) ->
+ {xmlelement, Name, Attrs, Els} = Packet,
+ case find_x_event(Els) of
+ false ->
+ true;
+ El ->
+ case xml:get_subtag(El, "id") of
+ false ->
+ case xml:get_subtag(El, "offline") of
+ false ->
+ true;
+ _ ->
+ ID = case xml:get_tag_attr_s("id", Packet) of
+ "" ->
+ {xmlelement, "id", [], []};
+ S ->
+ {xmlelement, "id", [],
+ [{xmlcdata, S}]}
+ end,
+ ejabberd_router:route(
+ To, From, {xmlelement, Name, Attrs,
+ [{xmlelement, "x",
+ [{"xmlns", ?NS_EVENT}],
+ [ID,
+ {xmlelement, "offline", [], []}]}]
+ }),
+ true
+ end;
+ _ ->
+ false
+ end
+ end.
+
+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
+ ?NS_EVENT ->
+ El;
+ _ ->
+ find_x_event(Els)
+ end.
+
+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
+ ?NS_EXPIRE ->
+ case xml:get_tag_attr_s("seconds", El) of
+ Val ->
+ case catch list_to_integer(Val) of
+ {'EXIT', _} ->
+ never;
+ Int when Int > 0 ->
+ {MegaSecs, Secs, MicroSecs} = TimeStamp,
+ S = MegaSecs * 1000000 + Secs + Int,
+ MegaSecs1 = S div 1000000,
+ Secs1 = S rem 1000000,
+ {MegaSecs1, Secs1, MicroSecs};
+ _ ->
+ never
+ end;
+ _ ->
+ never
+ end;
+ _ ->
+ find_x_expire(TimeStamp, Els)
+ end.
+
+
+pop_offline_messages(Ls, User) ->
+ LUser = jlib:nodeprep(User),
+ EUser = ejabberd_odbc:escape(LUser),
+ case ejabberd_odbc:sql_query(
+ ["begin;"
+ "select * from spool where username='", EUser, "';"
+ "delete from spool where username='", EUser, "';"
+ "commit"]) of
+ {selected, ["username","xml"], Rs} ->
+ Ls ++ lists:flatmap(
+ fun({_, XML}) ->
+ case xml_stream:parse_element(XML) of
+ {error, _Reason} ->
+ [];
+ El ->
+ To = jlib:string_to_jid(
+ xml:get_tag_attr_s("to", El)),
+ From = jlib:string_to_jid(
+ xml:get_tag_attr_s("from", El)),
+ if
+ (To /= error) and
+ (From /= error) ->
+ [{route, From, To, El}];
+ true ->
+ []
+ end
+ end
+ end, Rs);
+ _ ->
+ Ls
+ end.
+
+
+remove_user(User) ->
+ LUser = jlib:nodeprep(User),
+ Username = ejabberd_odbc:escape(LUser),
+ ejabberd_odbc:sql_query(
+ ["delete from spool where username='", Username, "'"]).
+