]> granicus.if.org Git - ejabberd/commitdiff
* src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch
authorAlexey Shchepin <alexey@process-one.net>
Sun, 8 Aug 2004 19:07:55 +0000 (19:07 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Sun, 8 Aug 2004 19:07:55 +0000 (19:07 +0000)
offline messages
* src/mod_offline.erl: Likewise

* src/mod_offline.erl: Added table locking in
remove_old_messages/1

* src/ejabberd_sm.erl: Use offline_message_hook to store offline
messages
* src/mod_offline.erl: Likewise

* src/ejabberd_hooks.erl: Hooks support
* src/ejabberd_sup.erl: Added ejabberd_hooks

* doc/guide.tex: Updated

* src/ejabberd.cfg.example: Updated

* src/ejabberd_c2s.erl: Changed TLS options (thanks to Sergei
Golovan)

SVN Revision: 255

ChangeLog
doc/guide.html
doc/guide.tex
src/ejabberd.cfg.example
src/ejabberd_c2s.erl
src/ejabberd_hooks.erl [new file with mode: 0644]
src/ejabberd_sm.erl
src/ejabberd_sup.erl
src/mod_offline.erl

index 05cd2c43844e9d5a60834fe8935eb09d24ba25a7..e6043d3b91a1819a2e88a3d8331d2ee107cd5f0f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2004-08-08  Alexey Shchepin  <alexey@sevcom.net>
+
+       * src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch
+       offline messages
+       * src/mod_offline.erl: Likewise
+
+       * src/mod_offline.erl: Added table locking in
+       remove_old_messages/1
+
+       * src/ejabberd_sm.erl: Use offline_message_hook to store offline
+       messages
+       * src/mod_offline.erl: Likewise
+
+       * src/ejabberd_hooks.erl: Hooks support
+       * src/ejabberd_sup.erl: Added ejabberd_hooks
+
+       * doc/guide.tex: Updated
+
+       * src/ejabberd.cfg.example: Updated
+
+       * src/ejabberd_c2s.erl: Changed TLS options (thanks to Sergei
+       Golovan)
+
 2004-08-05  Alexey Shchepin  <alexey@sevcom.net>
 
        * src/aclocal.m4: Updated to check for openssl library (thanks to
index 2db534258cab7dac767eda624ff5e149f6b94db2..851bb0384792980d698353c777f4ac75268f87cb 100644 (file)
@@ -142,7 +142,7 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
 The misfeatures of <TT>ejabberd</TT> are:
 <UL><LI>
 No support for virtual domains
-<LI>No support for STARTTLS
+<LI>No support for authentification and STARTTLS in S2S connections
 </UL>
 <!--TOC section Installation-->
 
@@ -164,7 +164,8 @@ To compile <TT>ejabberd</TT>, you will need the following packages:
 GNU Make;
 <LI>GCC;
 <LI>libexpat 1.95 or later;
-<LI>Erlang/OTP R8B or later.
+<LI>Erlang/OTP R8B or later;
+<LI>OpenSSL 0.9.6 or later (optional).
 </UL>
 <!--TOC subsubsection Windows-->
 
@@ -473,8 +474,15 @@ The following options are defined:
  <DT><B><TT>{ip, IPAddress}</TT></B><DD> This option specifies which network interface to
  listen on. For example <CODE>{ip, {192, 168, 1, 1}}</CODE>.
  <DT><B><TT>inet6</TT></B><DD> Set up the socket for IPv6.
+ <DT><B><TT>tls</TT></B><DD> This option specifies that STARTTLS extension is available on
+ connections to this port. You should also set ``<CODE>certfile</CODE>'' option.
+ <DT><B><TT>tls_from_start</TT></B><DD> Among with <TT>tls</TT> this option specifies that
+ traffic on this port will be encrypted using SSL immediately after
+ connecting.
  <DT><B><TT>ssl</TT></B><DD> This option specifies that traffic on this port will be
- encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option.
+ encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option. It
+ is recommended to use <TT>tls</TT> and <TT>tls_from_start</TT> options
+ instead.
  <DT><B><TT>{certfile, Path}</TT></B><DD> Path to a file containing the SSL certificate.
  </DL>
  <DT><B><TT>ejabberd_s2s_in</TT></B><DD> This module serves incoming S2S connections.
@@ -495,7 +503,7 @@ The following additional options are defined for <TT>ejabberd_service</TT>
 <BR>
 The following options are defined:
  <DL COMPACT=compact><DT>
- <B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> .
+ <B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A>
  support. It is available then at <CODE>http://server:port/http-poll/</CODE>.<BR>
 <BR>
 <DT><B><TT>web_admin</TT></B><DD> This option enables web-based interface for <TT>ejabberd</TT>
index 732aec45f4404af92629ce0a00ff27a29df04f19..dd78dbb9853f30695564d078bb7c32dc6a25fbb9 100644 (file)
@@ -126,7 +126,7 @@ The main features of \ejabberd{} are:
 The misfeatures of \ejabberd{} are:
 \begin{itemize}
 \item No support for virtual domains
-\item No support for STARTTLS
+\item No support for authentification and STARTTLS in S2S connections
 \end{itemize}
 
 
@@ -144,7 +144,8 @@ To compile \ejabberd{}, you will need the following packages:
 \item GNU Make;
 \item GCC;
 \item libexpat 1.95 or later;
-\item Erlang/OTP R8B or later.
+\item Erlang/OTP R8B or later;
+\item OpenSSL 0.9.6 or later (optional).
 \end{itemize}
 
 \subsubsection{Windows}
@@ -285,8 +286,6 @@ But in this case \ejabberd{} can start to work slower.
 \subsection{Initial Configuration}
 \label{sec:initconfig}
 
-%\verbatiminput{../src/ejabberd.cfg}
-
 The configuration file is initially loaded the first time \ejabberd{} is
 executed, when it is parsed and stored in a database.  Subsequently the
 configuration is loaded from the database and any commands in the configuration
@@ -477,8 +476,15 @@ Currently these modules are implemented:
     \titem{\{ip, IPAddress\}} This option specifies which network interface to
     listen on. For example \verb|{ip, {192, 168, 1, 1}}|.
     \titem{inet6} Set up the socket for IPv6.
+    \titem{starttls} This option specifies that STARTTLS extension is available
+    on connections to this port.  You should also set ``\verb|certfile|''
+    option.
+    \titem{tls} This option specifies that traffic on this port will be
+    encrypted using SSL immediately after connecting.  You should also set
+    ``\verb|certfile|'' option.
     \titem{ssl} This option specifies that traffic on this port will be
-    encrypted using SSL.  You should also set ``\verb|certfile|'' option.
+    encrypted using SSL.  You should also set ``\verb|certfile|'' option.  It
+    is recommended to use \term{tls} option instead.
     \titem{\{certfile, Path\}} Path to a file containing the SSL certificate.
   \end{description}
   \titem{ejabberd\_s2s\_in} This module serves incoming S2S connections.
@@ -499,7 +505,7 @@ Currently these modules are implemented:
 
   The following options are defined:
   \begin{description}
-    \titem{http\_poll} This option enables \tjepref{0025}{HTTP Polling} .
+    \titem{http\_poll} This option enables \tjepref{0025}{HTTP Polling}
     support.  It is available then at \verb|http://server:port/http-poll/|.
     
     \titem{web\_admin} This option enables web-based interface for \ejabberd{}
index 405f4a2b2d37ec78270aec346744d0b04673dbba..82e1cc43cad8ddf380344880fa84d57e55274922 100644 (file)
 
 % Listened ports:
 {listen,
- [{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper}]},
-  {5223, ejabberd_c2s,     [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
+ [{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper},
+                           starttls, {certfile, "./ssl.pem"}]},
+  {5223, ejabberd_c2s,     [{access, c2s},
+                           tls, {certfile, "./ssl.pem"}]},
+  % Use these two lines instead if TLS support is not compiled
+  %{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper}]},
+  %{5223, ejabberd_c2s,     [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
   {5269, ejabberd_s2s_in,  [{shaper, s2s_shaper}]},
   {5280, ejabberd_http,    [http_poll, web_admin]},
   {8888, ejabberd_service, [{access, all},
index b5b3128dad18d51ca39db0af1aa2588b6dd35bb9..844e7bcfb268452f0421a6de67e82445658706bb 100644 (file)
@@ -80,8 +80,6 @@
 
 -define(INVALID_NS_ERR,
        xml:element_to_string(?SERR_INVALID_NAMESPACE)).
-%-define(INVALID_XML_ERR,
-%      "<stream:error code='400'>Invalid XML</stream:error>").
 -define(INVALID_XML_ERR,
        xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
 
@@ -118,8 +116,9 @@ init([{SockMod, Socket}, Opts]) ->
                 {value, {_, S}} -> S;
                 _ -> none
             end,
-    TLS = lists:member(tls, Opts),
-    TLSEnabled = lists:member(tls_from_start, Opts),
+    StartTLS = lists:member(starttls, Opts),
+    TLSEnabled = lists:member(tls, Opts),
+    TLS = StartTLS orelse TLSEnabled,
     TLSOpts = lists:filter(fun({certfile, _}) -> true;
                              (_) -> false
                           end, Opts),
@@ -1387,9 +1386,8 @@ process_privacy_iq(From, To,
 
 
 resend_offline_messages(StateData) ->
-    case catch mod_offline:pop_offline_messages(StateData#state.user) of
-       {'EXIT', _Reason} ->
-           ok;
+    case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
+                                [StateData#state.user]) of
        Rs when list(Rs) ->
            lists:foreach(
              fun({route, From, To, {xmlelement, Name, Attrs, Els}}) ->
diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl
new file mode 100644 (file)
index 0000000..c00035b
--- /dev/null
@@ -0,0 +1,183 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_hooks.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Manage hooks
+%%% Created :  8 Aug 2004 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id$
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_hooks).
+-author('alexey@sevcom.net').
+
+-behaviour(gen_server).
+
+%% External exports
+-export([start_link/0,
+        add/4,
+        delete/4,
+        run/2,
+        run_fold/3]).
+
+%% gen_server callbacks
+-export([init/1,
+        handle_call/3,
+        handle_cast/2,
+        code_change/3,
+        handle_info/2,
+        terminate/2]).
+
+-include("ejabberd.hrl").
+
+-record(state, {}).
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start_link() ->
+    gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []).
+
+add(Hook, Module, Function, Seq) ->
+    gen_server:call(ejabberd_hooks, {add, Hook, Module, Function, Seq}).
+
+delete(Hook, Module, Function, Seq) ->
+    gen_server:call(ejabberd_hooks, {delete, Hook, Module, Function, Seq}).
+
+run(Hook, Args) ->
+    case ets:lookup(hooks, Hook) of
+       [{_, Ls}] ->
+           run1(Ls, Hook, Args);
+       [] ->
+           ok
+    end.
+
+run_fold(Hook, Val, Args) ->
+    case ets:lookup(hooks, Hook) of
+       [{_, Ls}] ->
+           run_fold1(Ls, Hook, Val, Args);
+       [] ->
+           ok
+    end.
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State}          |
+%%          {ok, State, Timeout} |
+%%          ignore               |
+%%          {stop, Reason}
+%%----------------------------------------------------------------------
+init([]) ->
+    ets:new(hooks, [named_table]),
+    {ok, #state{}}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State}          |
+%%          {reply, Reply, State, Timeout} |
+%%          {noreply, State}               |
+%%          {noreply, State, Timeout}      |
+%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
+%%          {stop, Reason, State}            (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_call({add, Hook, Module, Function, Seq}, From, State) ->
+    Reply = case ets:lookup(hooks, Hook) of
+               [{_, Ls}] ->
+                   El = {Seq, Module, Function},
+                   case lists:member(El, Ls) of
+                       true ->
+                           ok;
+                       false ->
+                           NewLs = lists:merge(Ls, [El]),
+                           ets:insert(hooks, {Hook, NewLs}),
+                           ok
+                   end;
+               [] ->
+                   NewLs = [{Seq, Module, Function}],
+                   ets:insert(hooks, {Hook, NewLs}),
+                   ok
+           end,
+    {reply, Reply, State};
+handle_call({delete, Hook, Module, Function, Seq}, From, State) ->
+    Reply = case ets:lookup(hooks, Hook) of
+               [{_, Ls}] ->
+                   NewLs = lists:delete({Seq, Module, Function}, Ls),
+                   ets:insert(hooks, {Hook, NewLs}),
+                   ok;
+               [] ->
+                   ok
+           end,
+    {reply, Reply, State};
+handle_call(Request, From, State) ->
+    Reply = ok,
+    {reply, Reply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State}          |
+%%          {noreply, State, Timeout} |
+%%          {stop, Reason, State}            (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_cast(Msg, State) ->
+    {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State}          |
+%%          {noreply, State, Timeout} |
+%%          {stop, Reason, State}            (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_info(Info, State) ->
+    {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(Reason, State) ->
+    ok.
+
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+run1([], Hook, Args) ->
+    ok;
+run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
+    case catch apply(Module, Function, Args) of
+       {'EXIT', Reason} ->
+           ?ERROR_MSG("~p~nrunning hook: ~p",
+                      [Reason, {Hook, Args}]),
+           run1(Ls, Hook, Args);
+       stop ->
+           ok;
+       _ ->
+           run1(Ls, Hook, Args)
+    end.
+
+
+run_fold1([], Hook, Val, Args) ->
+    Val;
+run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
+    case catch apply(Module, Function, [Val | Args]) of
+       {'EXIT', Reason} ->
+           ?ERROR_MSG("~p~nrunning hook: ~p",
+                      [Reason, {Hook, Args}]),
+           run_fold1(Ls, Hook, Val, Args);
+       stop ->
+           stopped;
+       {stop, NewVal} ->
+           NewVal;
+       NewVal ->
+           run_fold1(Ls, Hook, NewVal, Args)
+    end.
+
+
+
index 882c61e0ed8931371887a6c362f4277b6c7bd8d7..d4a972518f6fb277d81b003eb9eb8b6f4d1de81b 100644 (file)
@@ -11,6 +11,7 @@
 -vsn('$Revision$ ').
 
 -export([start_link/0, init/0, open_session/2, close_session/2,
+        bounce_offline_message/3,
         get_user_resources/1,
         set_presence/3,
         unset_presence/3,
@@ -44,6 +45,8 @@ init() ->
     mnesia:add_table_index(presence, user),
     mnesia:subscribe(system),
     ets:new(sm_iqtable, [named_table]),
+    ejabberd_hooks:add(offline_message_hook,
+                      ejabberd_sm, bounce_offline_message, 100),
     loop().
 
 loop() ->
@@ -263,18 +266,11 @@ route_message(From, To, Packet) ->
                _ ->
                    case ejabberd_auth:is_user_exists(LUser) of
                        true ->
-                           case catch mod_offline:store_packet(
-                                        From, To, Packet) of
-                               {'EXIT', _} ->
-                                   Err = jlib:make_error_reply(
-                                           Packet, ?ERR_SERVICE_UNAVAILABLE),
-                                   ejabberd_router:route(To, From, Err);
-                               _ ->
-                                   ok
-                           end;
+                           ejabberd_hooks:run(offline_message_hook,
+                                              [From, To, Packet]);
                        _ ->
                            Err = jlib:make_error_reply(
-                                   Packet, ?ERR_ITEM_NOT_FOUND),
+                                   Packet, ?ERR_SERVICE_UNAVAILABLE),
                            ejabberd_router:route(To, From, Err)
                    end
            end;
@@ -291,6 +287,10 @@ route_message(From, To, Packet) ->
            end
     end.
 
+bounce_offline_message(From, To, Packet) ->
+    Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
+    ejabberd_router:route(To, From, Err),
+    stop.
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
index bda9b79a1c7df6848afb03e7a7731ca5cfbd91c7..9a2cc74d55c31a5baac87b81e139c42410a2d659 100644 (file)
@@ -19,36 +19,48 @@ start_link() ->
 
 
 init([]) ->
-    Router = {ejabberd_router,
-             {ejabberd_router, start_link, []},
-             permanent,
-             brutal_kill,
-             worker,
-             [ejabberd_router]},
-    SM = {ejabberd_sm,
-         {ejabberd_sm, start_link, []},
-         permanent,
-         brutal_kill,
-         worker,
-         [ejabberd_sm]},
-    S2S = {ejabberd_s2s,
-          {ejabberd_s2s, start_link, []},
-          permanent,
-          brutal_kill,
-          worker,
-          [ejabberd_s2s]},
-    Local = {ejabberd_local,
-            {ejabberd_local, start_link, []},
-            permanent,
-            brutal_kill,
-            worker,
-            [ejabberd_local]},
-    Listener = {ejabberd_listener,
-               {ejabberd_listener, start_link, []},
-               permanent,
-               infinity,
-               supervisor,
-               [ejabberd_listener]},
+    Hooks =
+       {ejabberd_hooks,
+        {ejabberd_hooks, start_link, []},
+        permanent,
+        brutal_kill,
+        worker,
+        [ejabberd_hooks]},
+    Router =
+       {ejabberd_router,
+        {ejabberd_router, start_link, []},
+        permanent,
+        brutal_kill,
+        worker,
+        [ejabberd_router]},
+    SM =
+       {ejabberd_sm,
+        {ejabberd_sm, start_link, []},
+        permanent,
+        brutal_kill,
+        worker,
+        [ejabberd_sm]},
+    S2S =
+       {ejabberd_s2s,
+        {ejabberd_s2s, start_link, []},
+        permanent,
+        brutal_kill,
+        worker,
+        [ejabberd_s2s]},
+    Local =
+       {ejabberd_local,
+        {ejabberd_local, start_link, []},
+        permanent,
+        brutal_kill,
+        worker,
+        [ejabberd_local]},
+    Listener =
+       {ejabberd_listener,
+        {ejabberd_listener, start_link, []},
+        permanent,
+        infinity,
+        supervisor,
+        [ejabberd_listener]},
     StringPrep =
        {stringprep,
         {stringprep, start_link, []},
@@ -112,7 +124,11 @@ init([]) ->
         supervisor,
         [ejabberd_tmp_sup]},
     {ok, {{one_for_one, 10, 1},
-         [Router, SM, S2S, Local,
+         [Hooks,
+          Router,
+          SM,
+          S2S,
+          Local,
           StringPrep,
           C2SSupervisor,
           S2SInSupervisor,
index 7af72442b063be4d5f9921c70bec7aeffd22c9d1..22e9b85ee13fd89eb08fae4802578eb3c71c595c 100644 (file)
@@ -16,7 +16,7 @@
         stop/0,
         store_packet/3,
         resend_offline_messages/1,
-        pop_offline_messages/1,
+        pop_offline_messages/2,
         remove_old_messages/1,
         remove_user/1]).
 
@@ -31,6 +31,10 @@ start(_) ->
                        [{disc_only_copies, [node()]},
                         {type, bag},
                         {attributes, record_info(fields, offline_msg)}]),
+    ejabberd_hooks:add(offline_message_hook,
+                      ?MODULE, store_packet, 50),
+    ejabberd_hooks:add(resend_offline_messages_hook,
+                      ?MODULE, pop_offline_messages, 50),
     register(?PROCNAME, spawn(?MODULE, init, [])).
 
 init() ->
@@ -61,23 +65,31 @@ receive_all(Msgs) ->
 
 
 stop() ->
+    ejabberd_hooks:delete(offline_message_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) ->
-    true = is_process_alive(whereis(?PROCNAME)),
     Type = xml:get_tag_attr_s("type", Packet),
-    true = Type /= "error" andalso Type /= "groupchat",
-    case check_event(From, To, Packet) of
+    if
+       (Type /= "error") and (Type /= "groupchat") ->
+           case check_event(From, To, Packet) of
+               true ->
+                   #jid{luser = LUser} = To,
+                   TimeStamp = now(),
+                   ?PROCNAME ! #offline_msg{user = LUser,
+                                            timestamp = TimeStamp,
+                                            from = From,
+                                            to = To,
+                                            packet = Packet},
+                   stop;
+               _ ->
+                   ok
+           end;
        true ->
-           #jid{luser = LUser} = To,
-           TimeStamp = now(),
-           ?PROCNAME ! #offline_msg{user = LUser,
-                                    timestamp = TimeStamp,
-                                    from = From,
-                                    to = To,
-                                    packet = Packet};
-       _ ->
            ok
     end.
 
@@ -154,7 +166,7 @@ resend_offline_messages(User) ->
            ok
     end.
 
-pop_offline_messages(User) ->
+pop_offline_messages(Ls, User) ->
     LUser = jlib:nodeprep(User),
     F = fun() ->
                Rs = mnesia:wread({offline_msg, LUser}),
@@ -175,9 +187,9 @@ pop_offline_messages(User) ->
                           calendar:now_to_universal_time(
                             R#offline_msg.timestamp))]}}
              end,
-             lists:keysort(#offline_msg.timestamp, Rs));
+             Ls ++ lists:keysort(#offline_msg.timestamp, Rs));
        _ ->
-           []
+           Ls
     end.
 
 remove_old_messages(Days) ->
@@ -187,6 +199,7 @@ remove_old_messages(Days) ->
     Secs1 = S rem 1000000,
     TimeStamp = {MegaSecs1, Secs1, 0},
     F = fun() ->
+               mnesia:write_lock_table(offline_msg),
                mnesia:foldl(
                  fun(#offline_msg{timestamp = TS} = Rec, _Acc)
                     when TS < TimeStamp ->