+2004-08-12 Alexey Shchepin <alexey@sevcom.net>
+
+ * src/ejabberd_c2s.erl: Bugfix in resend_offline_messages/1
+
+ * src/mod_announce.erl: New module to manage announce messages
+ (thanks to Sergei Golovan)
+
+ * src/ejabberd_local.erl: Moved processing of announce messages to
+ mod_announce (thanks to Sergei Golovan)
+
+ * src/ejabberd_c2s.erl: Added several hooks
+
+ * src/ejabberd_hooks.erl: Fixed run_fold (thanks to Sergei
+ Golovan)
+
+ * src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
+
+ * doc/guide.tex: Updated (thanks to Sergei Golovan)
+
2004-08-08 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch
<A HREF="#htoc30">A.1.1 Option <TT>iqdisc</TT></A>
<LI><A HREF="#htoc31">A.1.2 Option <TT>host</TT></A>
</UL>
-<LI><A HREF="#htoc32">A.2 <TT>mod_configure</TT></A>
-<LI><A HREF="#htoc33">A.3 <TT>mod_disco</TT></A>
-<LI><A HREF="#htoc34">A.4 <TT>mod_echo</TT></A>
-<LI><A HREF="#htoc35">A.5 <TT>mod_irc</TT></A>
-<LI><A HREF="#htoc36">A.6 <TT>mod_last</TT></A>
-<LI><A HREF="#htoc37">A.7 <TT>mod_muc</TT></A>
-<LI><A HREF="#htoc38">A.8 <TT>mod_offline</TT></A>
-<LI><A HREF="#htoc39">A.9 <TT>mod_privacy</TT></A>
-<LI><A HREF="#htoc40">A.10 <TT>mod_private</TT></A>
-<LI><A HREF="#htoc41">A.11 <TT>mod_pubsub</TT></A>
-<LI><A HREF="#htoc42">A.12 <TT>mod_register</TT></A>
-<LI><A HREF="#htoc43">A.13 <TT>mod_roster</TT></A>
-<LI><A HREF="#htoc44">A.14 <TT>mod_stats</TT></A>
-<LI><A HREF="#htoc45">A.15 <TT>mod_time</TT></A>
-<LI><A HREF="#htoc46">A.16 <TT>mod_vcard</TT></A>
-<LI><A HREF="#htoc47">A.17 <TT>mod_version</TT></A>
+<LI><A HREF="#htoc32">A.2 <TT>mod_announce</TT></A>
+<LI><A HREF="#htoc33">A.3 <TT>mod_configure</TT></A>
+<LI><A HREF="#htoc34">A.4 <TT>mod_disco</TT></A>
+<LI><A HREF="#htoc35">A.5 <TT>mod_echo</TT></A>
+<LI><A HREF="#htoc36">A.6 <TT>mod_irc</TT></A>
+<LI><A HREF="#htoc37">A.7 <TT>mod_last</TT></A>
+<LI><A HREF="#htoc38">A.8 <TT>mod_muc</TT></A>
+<LI><A HREF="#htoc39">A.9 <TT>mod_offline</TT></A>
+<LI><A HREF="#htoc40">A.10 <TT>mod_privacy</TT></A>
+<LI><A HREF="#htoc41">A.11 <TT>mod_private</TT></A>
+<LI><A HREF="#htoc42">A.12 <TT>mod_pubsub</TT></A>
+<LI><A HREF="#htoc43">A.13 <TT>mod_register</TT></A>
+<LI><A HREF="#htoc44">A.14 <TT>mod_roster</TT></A>
+<LI><A HREF="#htoc45">A.15 <TT>mod_stats</TT></A>
+<LI><A HREF="#htoc46">A.16 <TT>mod_time</TT></A>
+<LI><A HREF="#htoc47">A.17 <TT>mod_vcard</TT></A>
+<LI><A HREF="#htoc48">A.18 <TT>mod_version</TT></A>
</UL>
-<LI><A HREF="#htoc48">B I18n/L10n</A>
+<LI><A HREF="#htoc49">B I18n/L10n</A>
</UL>
<!--TOC section Introduction-->
<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>starttls</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</TT></B><DD> This option specifies that traffic on this port will be
+ encrypted using SSL immediately after connecting. You should also set
+ ``<CODE>certfile</CODE>'' option.
<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. It
- is recommended to use <TT>tls</TT> and <TT>tls_from_start</TT> options
- instead.
+ is recommended to use <TT>tls</TT> option 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.
...
]}.
</PRE>
+<!--TOC subsection <TT>mod_announce</TT>-->
+
+<H3><A NAME="htoc32">A.2</A> <TT>mod_announce</TT></H3><!--SEC END -->
+
+<A NAME="sec:modannounce"></A>
+This module adds support for broadcast announce messages and MOTD.<BR>
+<BR>
+Options:
+<DL COMPACT=compact><DT>
+<B><TT>access</TT></B><DD> Specifies who is allowed to send announce messages
+and set MOTD (default value is <TT>none</TT>).
+</DL>
+Example:
+<PRE>
+ % Only admins can send announcement messages:
+ {access, announce, [{allow, admin}]}.
+
+ {modules,
+ [
+ ...
+ {mod_announce, [{access, announce}]},
+ ...
+ ]}.
+</PRE>
<!--TOC subsection <TT>mod_configure</TT>-->
-<H3><A NAME="htoc32">A.2</A> <TT>mod_configure</TT></H3><!--SEC END -->
+<H3><A NAME="htoc33">A.3</A> <TT>mod_configure</TT></H3><!--SEC END -->
<A NAME="sec:modconfigure"></A>
Options:
</DL>
<!--TOC subsection <TT>mod_disco</TT>-->
-<H3><A NAME="htoc33">A.3</A> <TT>mod_disco</TT></H3><!--SEC END -->
+<H3><A NAME="htoc34">A.4</A> <TT>mod_disco</TT></H3><!--SEC END -->
<A NAME="sec:moddisco"></A>
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A> (Service Discovery).<BR>
</PRE>
<!--TOC subsection <TT>mod_echo</TT>-->
-<H3><A NAME="htoc34">A.4</A> <TT>mod_echo</TT></H3><!--SEC END -->
+<H3><A NAME="htoc35">A.5</A> <TT>mod_echo</TT></H3><!--SEC END -->
<A NAME="sec:modecho"></A>
This module acts as a service and simply returns to sender any Jabber packet. Module may be
</DL>
<!--TOC subsection <TT>mod_irc</TT>-->
-<H3><A NAME="htoc35">A.5</A> <TT>mod_irc</TT></H3><!--SEC END -->
+<H3><A NAME="htoc36">A.6</A> <TT>mod_irc</TT></H3><!--SEC END -->
<A NAME="sec:modirc"></A>
This module implements IRC transport.<BR>
</PRE>
<!--TOC subsection <TT>mod_last</TT>-->
-<H3><A NAME="htoc36">A.6</A> <TT>mod_last</TT></H3><!--SEC END -->
+<H3><A NAME="htoc37">A.7</A> <TT>mod_last</TT></H3><!--SEC END -->
<A NAME="sec:modlast"></A>
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0012.html">JEP-0012</A> (Last Activity)<BR>
</DL>
<!--TOC subsection <TT>mod_muc</TT>-->
-<H3><A NAME="htoc37">A.7</A> <TT>mod_muc</TT></H3><!--SEC END -->
+<H3><A NAME="htoc38">A.8</A> <TT>mod_muc</TT></H3><!--SEC END -->
<A NAME="sec:modmuc"></A>
This module implements <A HREF="http://www.jabber.org/jeps/jep-0045.html">JEP-0045</A> (Multi-User Chat) service.<BR>
</PRE>
<!--TOC subsection <TT>mod_offline</TT>-->
-<H3><A NAME="htoc38">A.8</A> <TT>mod_offline</TT></H3><!--SEC END -->
+<H3><A NAME="htoc39">A.9</A> <TT>mod_offline</TT></H3><!--SEC END -->
<A NAME="sec:modoffline"></A>
This module implements offline message storage.<BR>
<BR>
<!--TOC subsection <TT>mod_privacy</TT>-->
-<H3><A NAME="htoc39">A.9</A> <TT>mod_privacy</TT></H3><!--SEC END -->
+<H3><A NAME="htoc40">A.10</A> <TT>mod_privacy</TT></H3><!--SEC END -->
<A NAME="sec:modprivacy"></A>
This module implements Privacy Rules as defined in XMPP IM
</DL>
<!--TOC subsection <TT>mod_private</TT>-->
-<H3><A NAME="htoc40">A.10</A> <TT>mod_private</TT></H3><!--SEC END -->
+<H3><A NAME="htoc41">A.11</A> <TT>mod_private</TT></H3><!--SEC END -->
<A NAME="sec:modprivate"></A>
This module adds support of <A HREF="http://www.jabber.org/jeps/jep-0049.html">JEP-0049</A> (Private XML Storage).<BR>
</DL>
<!--TOC subsection <TT>mod_pubsub</TT>-->
-<H3><A NAME="htoc41">A.11</A> <TT>mod_pubsub</TT></H3><!--SEC END -->
+<H3><A NAME="htoc42">A.12</A> <TT>mod_pubsub</TT></H3><!--SEC END -->
<A NAME="sec:modpubsub"></A>
This module implements <A HREF="http://www.jabber.org/jeps/jep-0060.html">JEP-0060</A> (Publish-Subscribe Service).<BR>
</PRE>
<!--TOC subsection <TT>mod_register</TT>-->
-<H3><A NAME="htoc42">A.12</A> <TT>mod_register</TT></H3><!--SEC END -->
+<H3><A NAME="htoc43">A.13</A> <TT>mod_register</TT></H3><!--SEC END -->
<A NAME="sec:modregister"></A>
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0077.html">JEP-0077</A> (In-Band Registration).<BR>
</PRE>
<!--TOC subsection <TT>mod_roster</TT>-->
-<H3><A NAME="htoc43">A.13</A> <TT>mod_roster</TT></H3><!--SEC END -->
+<H3><A NAME="htoc44">A.14</A> <TT>mod_roster</TT></H3><!--SEC END -->
<A NAME="sec:modroster"></A>
This module implements roster management.<BR>
</DL>
<!--TOC subsection <TT>mod_stats</TT>-->
-<H3><A NAME="htoc44">A.14</A> <TT>mod_stats</TT></H3><!--SEC END -->
+<H3><A NAME="htoc45">A.15</A> <TT>mod_stats</TT></H3><!--SEC END -->
<A NAME="sec:modstats"></A>
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A> (Statistics Gathering).<BR>
<BR>
<!--TOC subsection <TT>mod_time</TT>-->
-<H3><A NAME="htoc45">A.15</A> <TT>mod_time</TT></H3><!--SEC END -->
+<H3><A NAME="htoc46">A.16</A> <TT>mod_time</TT></H3><!--SEC END -->
<A NAME="sec:modtime"></A>
This module answers UTC time on <TT>jabber:iq:time</TT> queries.<BR>
</DL>
<!--TOC subsection <TT>mod_vcard</TT>-->
-<H3><A NAME="htoc46">A.16</A> <TT>mod_vcard</TT></H3><!--SEC END -->
+<H3><A NAME="htoc47">A.17</A> <TT>mod_vcard</TT></H3><!--SEC END -->
<A NAME="sec:modvcard"></A>
This module implements simple Jabber User Directory (based on user vCards)
</PRE>
<!--TOC subsection <TT>mod_version</TT>-->
-<H3><A NAME="htoc47">A.17</A> <TT>mod_version</TT></H3><!--SEC END -->
+<H3><A NAME="htoc48">A.18</A> <TT>mod_version</TT></H3><!--SEC END -->
<A NAME="sec:modversion"></A>
This module answers <TT>ejabberd</TT> version on <TT>jabber:iq:version</TT> queries.<BR>
</DL>
<!--TOC section I18n/L10n-->
-<H2><A NAME="htoc48">B</A> I18n/L10n</H2><!--SEC END -->
+<H2><A NAME="htoc49">B</A> I18n/L10n</H2><!--SEC END -->
<A NAME="sec:i18nl10n"></A>
All built-in modules support <TT>xml:lang</TT> attribute inside IQ queries.
\newcommand{\Jabber}{Jabber}
\newcommand{\module}[1]{\texttt{#1}}
+\newcommand{\modannounce}{\module{mod\_announce}}
\newcommand{\modconfigure}{\module{mod\_configure}}
\newcommand{\moddisco}{\module{mod\_disco}}
\newcommand{\modirc}{\module{mod\_irc}}
\end{verbatim}
+\subsection{\modannounce{}}
+\label{sec:modannounce}
+
+This module adds support for broadcast announce messages and MOTD.
+
+Options:
+\begin{description}
+\titem{access} Specifies who is allowed to send announce messages
+and set MOTD (default value is \term{none}).
+\end{description}
+
+Example:
+\begin{verbatim}
+ % Only admins can send announcement messages:
+ {access, announce, [{allow, admin}]}.
+
+ {modules,
+ [
+ ...
+ {mod_announce, [{access, announce}]},
+ ...
+ ]}.
+\end{verbatim}
+
+
\subsection{\modconfigure{}}
\label{sec:modconfigure}
AuthzId = xml:get_attr_s("authzid", KeyVals),
case ejabberd_auth:get_password(UserName) of
false ->
- {error, "no-user"};
+ {error, "not-authorized"};
Passwd ->
Response = response(KeyVals, UserName, Passwd,
Nonce, AuthzId, "AUTHENTICATE"),
username = UserName,
authzid = AuthzId}};
_ ->
- {error, "bad-auth"}
+ {error, "not-authorized"}
end
end
end;
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
+ {mod_announce, [{access, announce}]},
{mod_echo, [{host, "echo.localhost"}]},
{mod_private, []},
{mod_irc, []},
process_privacy_iq(
FromJID, ToJID, IQ, StateData);
_ ->
+ ejabberd_hooks:run(
+ user_send_packet,
+ [FromJID, ToJID, NewEl]),
ejabberd_router:route(
FromJID, ToJID, NewEl),
StateData
end
end;
"message" ->
+ ejabberd_hooks:run(user_send_packet,
+ [FromJID, ToJID, NewEl]),
ejabberd_router:route(FromJID, ToJID, NewEl),
StateData;
_ ->
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To),
NewAttrs),
- Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
+ FixedPacket = {xmlelement, Name, Attrs2, Els},
+ Text = xml:element_to_string(FixedPacket),
send_text(StateData, Text),
+ ejabberd_hooks:run(user_receive_packet,
+ [StateData#state.jid, From, To, FixedPacket]),
{next_state, StateName, NewState};
true ->
{next_state, StateName, NewState}
NewState =
if
FromUnavail ->
+ ejabberd_hooks:run(user_available_hook,
+ [StateData#state.jid]),
resend_offline_messages(StateData),
presence_broadcast_first(
From, StateData#state{pres_last = Packet,
NewStateData.
-resend_offline_messages(StateData) ->
+resend_offline_messages(#state{user = User,
+ privacy_list = PrivList} = StateData) ->
case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
- [StateData#state.user]) of
+ [User]) of
Rs when list(Rs) ->
lists:foreach(
- fun({route, From, To, {xmlelement, Name, Attrs, Els}}) ->
- Attrs2 = jlib:replace_from_to_attrs(
- jlib:jid_to_string(From),
- jlib:jid_to_string(To),
- Attrs),
- send_element(StateData, {xmlelement, Name, Attrs2, Els})
+ fun({route,
+ From, To, {xmlelement, Name, Attrs, Els} = Packet}) ->
+ Pass = case catch mod_privacy:check_packet(
+ User,
+ PrivList,
+ {From, To, Packet},
+ in) of
+ {'EXIT', _Reason} ->
+ true;
+ allow ->
+ true;
+ deny ->
+ false
+ end,
+ if
+ Pass ->
+ Attrs2 = jlib:replace_from_to_attrs(
+ jlib:jid_to_string(From),
+ jlib:jid_to_string(To),
+ Attrs),
+ send_element(StateData,
+ {xmlelement, Name, Attrs2, Els});
+ true ->
+ ok
+ end
end, Rs)
end.
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
- ok
+ Val
end.
%%%----------------------------------------------------------------------
-export([register_iq_handler/3,
register_iq_handler/4,
unregister_iq_handler/1,
- refresh_iq_handlers/0
+ refresh_iq_handlers/0,
+ bounce_resource_packet/3
]).
-include("ejabberd.hrl").
MyDomain = ?MYNAME,
ejabberd_router:register_route(MyDomain),
catch ets:new(local_iqtable, [named_table, public]),
+ ejabberd_hooks:add(local_send_to_resource_hook,
+ ?MODULE, bounce_resource_packet, 100),
loop(#state{mydomain = MyDomain,
iqtable = local_iqtable}).
"error" -> ok;
"result" -> ok;
_ ->
- case {Res, Name} of
- {"announce/online", "message"} ->
- announce_online(From, To, Packet);
- _ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_ITEM_NOT_FOUND),
- ejabberd_router:route(To, From, Err)
- end
+ ejabberd_hooks:run(local_send_to_resource_hook,
+ [From, To, Packet])
end;
_ ->
ejabberd_sm ! {route, From, To, Packet}
refresh_iq_handlers() ->
ejabberd_local ! refresh_iq_handlers.
-announce_online(From, To, Packet) ->
- case acl:match_rule(announce, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
- ejabberd_router:route(To, From, Err);
- allow ->
- Local = jlib:make_jid("", ?MYNAME, ""),
- lists:foreach(
- fun({U, R}) ->
- Dest = jlib:make_jid(U, ?MYNAME, R),
- ejabberd_router:route(Local, Dest, Packet)
- end, ejabberd_sm:dirty_get_sessions_list())
- end.
-
+bounce_resource_packet(From, To, Packet) ->
+ Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
+ ejabberd_router:route(To, From, Err),
+ stop.
--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : mod_announce.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Manage announce messages
+%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(mod_announce).
+-author('alexey@sevcom.net').
+
+-behaviour(gen_mod).
+
+-export([start/1,
+ init/0,
+ stop/0,
+ announce/3,
+ send_motd/1]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+
+-record(motd, {id, packet}).
+-record(motd_users, {luser, dummy = []}).
+
+-define(PROCNAME, ejabberd_announce).
+
+start(_) ->
+ mnesia:create_table(motd, [{disc_copies, [node()]},
+ {attributes, record_info(fields, motd)}]),
+ mnesia:create_table(motd_users, [{disc_copies, [node()]},
+ {attributes, record_info(fields, motd_users)}]),
+ ejabberd_hooks:add(local_send_to_resource_hook,
+ ?MODULE, announce, 50),
+ ejabberd_hooks:add(user_available_hook,
+ ?MODULE, send_motd, 50),
+ register(?PROCNAME, proc_lib:spawn(?MODULE, init, [])).
+
+init() ->
+ loop().
+
+loop() ->
+ receive
+ {announce_online, From, To, Packet} ->
+ announce_online(From, To, Packet),
+ loop();
+ {announce_motd, From, To, Packet} ->
+ announce_motd(From, To, Packet),
+ loop();
+ {announce_motd_update, From, To, Packet} ->
+ announce_motd_update(From, To, Packet),
+ loop();
+ {announce_motd_delete, From, To, Packet} ->
+ announce_motd_delete(From, To, Packet),
+ loop();
+ _ ->
+ loop()
+ end.
+
+stop() ->
+ ejabberd_hooks:delete(local_send_to_resource_hook,
+ ?MODULE, announce, 50),
+ ejabberd_hooks:delete(sm_register_connection_hook,
+ ?MODULE, send_motd, 50),
+ exit(whereis(?PROCNAME), stop),
+ ok.
+
+announce(From, To, Packet) ->
+ case To of
+ #jid{luser = "", lresource = Res} ->
+ {xmlelement, Name, _Attrs, _Els} = Packet,
+ case {Res, Name} of
+ {"announce/online", "message"} ->
+ ?PROCNAME ! {announce_online, From, To, Packet},
+ stop;
+ {"announce/motd", "message"} ->
+ ?PROCNAME ! {announce_motd, From, To, Packet},
+ stop;
+ {"announce/motd/update", "message"} ->
+ ?PROCNAME ! {announce_motd_update, From, To, Packet},
+ stop;
+ {"announce/motd/delete", "message"} ->
+ ?PROCNAME ! {announce_motd_delete, From, To, Packet},
+ stop;
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+announce_online(From, To, Packet) ->
+ Access = gen_mod:get_module_opt(?MODULE, access, none),
+ case acl:match_rule(Access, From) of
+ deny ->
+ Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ allow ->
+ announce_online1(ejabberd_sm:dirty_get_sessions_list(), Packet)
+ end.
+
+announce_online1(Sessions, Packet) ->
+ Server = ?MYNAME,
+ Local = jlib:make_jid("", Server, ""),
+ lists:foreach(
+ fun({U, R}) ->
+ Dest = jlib:make_jid(U, Server, R),
+ ejabberd_router:route(Local, Dest, Packet)
+ end, Sessions).
+
+announce_motd(From, To, Packet) ->
+ Access = gen_mod:get_module_opt(?MODULE, access, none),
+ case acl:match_rule(Access, From) of
+ deny ->
+ Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ allow ->
+ announce_motd_update(Packet),
+ Sessions = ejabberd_sm:dirty_get_sessions_list(),
+ announce_online1(Sessions, Packet),
+ F = fun() ->
+ lists:foreach(
+ fun({U, _R}) ->
+ mnesia:write(#motd_users{luser = U})
+ end, Sessions)
+ end,
+ mnesia:transaction(F)
+ end.
+
+announce_motd_update(From, To, Packet) ->
+ Access = gen_mod:get_module_opt(?MODULE, access, none),
+ case acl:match_rule(Access, From) of
+ deny ->
+ Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ allow ->
+ announce_motd_update(Packet)
+ end.
+
+announce_motd_update(Packet) ->
+ announce_motd_delete(),
+ F = fun() ->
+ mnesia:write(#motd{id = motd, packet = Packet})
+ end,
+ mnesia:transaction(F).
+
+announce_motd_delete(From, To, Packet) ->
+ Access = gen_mod:get_module_opt(?MODULE, access, none),
+ case acl:match_rule(Access, From) of
+ deny ->
+ Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ allow ->
+ announce_motd_delete()
+ end.
+
+announce_motd_delete() ->
+ mnesia:clear_table(motd),
+ mnesia:clear_table(motd_users).
+
+send_motd(#jid{luser = LUser} = JID) ->
+ case catch mnesia:dirty_read({motd, motd}) of
+ [#motd{packet = Packet}] ->
+ case catch mnesia:dirty_read({motd_users, LUser}) of
+ [#motd_users{}] ->
+ ok;
+ _ ->
+ Local = jlib:make_jid("", ?MYNAME, ""),
+ ejabberd_router:route(Local, JID, Packet),
+ F = fun() ->
+ mnesia:write(#motd_users{luser = LUser})
+ end,
+ mnesia:transaction(F)
+ end;
+ _ ->
+ ok
+ end.
+