From d77a936b322220a67e703525ff585660ea8da670 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 12 Aug 2004 22:34:19 +0000 Subject: [PATCH] * 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) SVN Revision: 256 --- ChangeLog | 19 +++++ doc/guide.html | 107 ++++++++++++++--------- doc/guide.tex | 26 ++++++ src/cyrsasl_digest.erl | 4 +- src/ejabberd.cfg.example | 1 + src/ejabberd_c2s.erl | 48 +++++++++-- src/ejabberd_hooks.erl | 2 +- src/ejabberd_local.erl | 33 +++----- src/mod_announce.erl | 178 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 342 insertions(+), 76 deletions(-) create mode 100644 src/mod_announce.erl diff --git a/ChangeLog b/ChangeLog index e6043d3b9..56c36a727 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2004-08-12 Alexey Shchepin + + * 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 * src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch diff --git a/doc/guide.html b/doc/guide.html index 851bb0384..1d0927ac8 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -85,24 +85,25 @@ A.1.1  Option iqdisc
  • A.1.2  Option host -
  • A.2  mod_configure -
  • A.3  mod_disco -
  • A.4  mod_echo -
  • A.5  mod_irc -
  • A.6  mod_last -
  • A.7  mod_muc -
  • A.8  mod_offline -
  • A.9  mod_privacy -
  • A.10  mod_private -
  • A.11  mod_pubsub -
  • A.12  mod_register -
  • A.13  mod_roster -
  • A.14  mod_stats -
  • A.15  mod_time -
  • A.16  mod_vcard -
  • A.17  mod_version +
  • A.2  mod_announce +
  • A.3  mod_configure +
  • A.4  mod_disco +
  • A.5  mod_echo +
  • A.6  mod_irc +
  • A.7  mod_last +
  • A.8  mod_muc +
  • A.9  mod_offline +
  • A.10  mod_privacy +
  • A.11  mod_private +
  • A.12  mod_pubsub +
  • A.13  mod_register +
  • A.14  mod_roster +
  • A.15  mod_stats +
  • A.16  mod_time +
  • A.17  mod_vcard +
  • A.18  mod_version -
  • B  I18n/L10n +
  • B  I18n/L10n @@ -474,15 +475,15 @@ The following options are defined:
    {ip, IPAddress}
    This option specifies which network interface to listen on. For example {ip, {192, 168, 1, 1}}.
    inet6
    Set up the socket for IPv6. -
    tls
    This option specifies that STARTTLS extension is available on - connections to this port. You should also set ``certfile'' option. -
    tls_from_start
    Among with tls this option specifies that - traffic on this port will be encrypted using SSL immediately after - connecting. +
    starttls
    This option specifies that STARTTLS extension is available + on connections to this port. You should also set ``certfile'' + option. +
    tls
    This option specifies that traffic on this port will be + encrypted using SSL immediately after connecting. You should also set + ``certfile'' option.
    ssl
    This option specifies that traffic on this port will be encrypted using SSL. You should also set ``certfile'' option. It - is recommended to use tls and tls_from_start options - instead. + is recommended to use tls option instead.
    {certfile, Path}
    Path to a file containing the SSL certificate.
    ejabberd_s2s_in
    This module serves incoming S2S connections. @@ -818,9 +819,33 @@ Example: ... ]}. + + +

    A.2  mod_announce

    + + +This module adds support for broadcast announce messages and MOTD.
    +
    +Options: +
    +access
    Specifies who is allowed to send announce messages +and set MOTD (default value is none). +
    +Example: +
    +  % Only admins can send announcement messages:
    +  {access, announce, [{allow, admin}]}.
    +
    +  {modules,
    +   [
    +    ...
    +    {mod_announce, [{access, announce}]},
    +    ...
    +   ]}.
    +
    -

    A.2  mod_configure

    +

    A.3  mod_configure

    Options: @@ -830,7 +855,7 @@ discipline (see A.1.1). -

    A.3  mod_disco

    +

    A.4  mod_disco

    This module adds support for JEP-0030 (Service Discovery).
    @@ -855,7 +880,7 @@ Example: -

    A.4  mod_echo

    +

    A.5  mod_echo

    This module acts as a service and simply returns to sender any Jabber packet. Module may be @@ -869,7 +894,7 @@ then prefix echo. is added to main ejabberd hostname. -

    A.5  mod_irc

    +

    A.6  mod_irc

    This module implements IRC transport.
    @@ -892,7 +917,7 @@ Example: -

    A.6  mod_last

    +

    A.7  mod_last

    This module adds support for JEP-0012 (Last Activity)
    @@ -904,7 +929,7 @@ discipline (see A.1.1). -

    A.7  mod_muc

    +

    A.8  mod_muc

    This module implements JEP-0045 (Multi-User Chat) service.
    @@ -939,14 +964,14 @@ Example: -

    A.8  mod_offline

    +

    A.9  mod_offline

    This module implements offline message storage.

    -

    A.9  mod_privacy

    +

    A.10  mod_privacy

    This module implements Privacy Rules as defined in XMPP IM @@ -959,7 +984,7 @@ discipline (see A.1.1). -

    A.10  mod_private

    +

    A.11  mod_private

    This module adds support of JEP-0049 (Private XML Storage).
    @@ -971,7 +996,7 @@ discipline (see A.1.1). -

    A.11  mod_pubsub

    +

    A.12  mod_pubsub

    This module implements JEP-0060 (Publish-Subscribe Service).
    @@ -996,7 +1021,7 @@ Example: -

    A.12  mod_register

    +

    A.13  mod_register

    This module adds support for JEP-0077 (In-Band Registration).
    @@ -1029,7 +1054,7 @@ Example: -

    A.13  mod_roster

    +

    A.14  mod_roster

    This module implements roster management.
    @@ -1041,7 +1066,7 @@ discipline (see A.1.1). -

    A.14  mod_stats

    +

    A.15  mod_stats

    This module adds support for JEP-0039 (Statistics Gathering).
    @@ -1055,7 +1080,7 @@ TBD about access.

    -

    A.15  mod_time

    +

    A.16  mod_time

    This module answers UTC time on jabber:iq:time queries.
    @@ -1067,7 +1092,7 @@ discipline (see A.1.1). -

    A.16  mod_vcard

    +

    A.17  mod_vcard

    This module implements simple Jabber User Directory (based on user vCards) @@ -1095,7 +1120,7 @@ Example: -

    A.17  mod_version

    +

    A.18  mod_version

    This module answers ejabberd version on jabber:iq:version queries.
    @@ -1107,7 +1132,7 @@ discipline (see A.1.1). -

    B  I18n/L10n

    +

    B  I18n/L10n

    All built-in modules support xml:lang attribute inside IQ queries. diff --git a/doc/guide.tex b/doc/guide.tex index dd78dbb98..5e12c70db 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -34,6 +34,7 @@ \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}} @@ -812,6 +813,31 @@ Example: \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} diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index ae94c181f..2e5e94e5a 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -51,7 +51,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) -> 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"), @@ -66,7 +66,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) -> username = UserName, authzid = AuthzId}}; _ -> - {error, "bad-auth"} + {error, "not-authorized"} end end end; diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index 82e1cc43c..253752f53 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -121,6 +121,7 @@ {mod_stats, []}, {mod_vcard, []}, {mod_offline, []}, + {mod_announce, [{access, announce}]}, {mod_echo, [{host, "echo.localhost"}]}, {mod_private, []}, {mod_irc, []}, diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 844e7bcfb..d8021606f 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -673,12 +673,17 @@ session_established({xmlstreamelement, El}, StateData) -> 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; _ -> @@ -882,8 +887,11 @@ handle_info({route, From, To, Packet}, StateName, 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} @@ -1102,6 +1110,8 @@ presence_update(From, Packet, StateData) -> 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, @@ -1385,17 +1395,37 @@ process_privacy_iq(From, To, 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. diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index c00035b41..d316fae89 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -55,7 +55,7 @@ run_fold(Hook, Val, Args) -> [{_, Ls}] -> run_fold1(Ls, Hook, Val, Args); [] -> - ok + Val end. %%%---------------------------------------------------------------------- diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 446ec292b..5da4cfaa1 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -15,7 +15,8 @@ -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"). @@ -32,6 +33,8 @@ init() -> 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}). @@ -104,14 +107,8 @@ do_route(State, From, To, Packet) -> "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} @@ -159,17 +156,7 @@ unregister_iq_handler(XMLNS) -> 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. diff --git a/src/mod_announce.erl b/src/mod_announce.erl new file mode 100644 index 000000000..29ac65e87 --- /dev/null +++ b/src/mod_announce.erl @@ -0,0 +1,178 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_announce.erl +%%% Author : Alexey Shchepin +%%% Purpose : Manage announce messages +%%% Created : 11 Aug 2003 by Alexey Shchepin +%%% 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. +