mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Merge from trunk (r1734 to r1752).
Note: this merge doesn't include the following revisions because it was made by previous commits: r1737, r1740, r1745, r1747, r1748. Additionnaly, any deprecated content was removed from jlib.hrl. This leaves only the new RSM records. Warning: Ejabberd may be broken until the merge is completly finished. PR: EJABP-1 SVN Revision: 1824
This commit is contained in:
parent
04f6a2be37
commit
2cc2039ad8
136
ChangeLog
136
ChangeLog
@ -1,11 +1,22 @@
|
|||||||
2009-01-15 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-16 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
|
||||||
|
|
||||||
|
Merge from trunk (r1734 to r1752).
|
||||||
|
|
||||||
|
Note: this merge doesn't include the following revisions because it
|
||||||
|
was made by previous commits:
|
||||||
|
r1737, r1740, r1745, r1747, r1748.
|
||||||
|
|
||||||
|
* src/jlib.hrl: Any deprecated content was removed from jlib.hrl. This
|
||||||
|
leaves only the new RSM records.
|
||||||
|
|
||||||
|
2009-01-15 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_muc/mod_muc.erl, src/mod_muc/mod_muc_room.erl:
|
* src/mod_muc/mod_muc.erl, src/mod_muc/mod_muc_room.erl:
|
||||||
Store registered nicknames, rooms and domains as binary().
|
Store registered nicknames, rooms and domains as binary().
|
||||||
Use document_to_iolist/1 and iolist_size/1 instead of
|
Use document_to_iolist/1 and iolist_size/1 instead of
|
||||||
document_to_list/1.
|
document_to_list/1.
|
||||||
|
|
||||||
2009-01-11 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-11 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/mod_pubsub.erl: Fix typo, initial update
|
* src/mod_pubsub/mod_pubsub.erl: Fix typo, initial update
|
||||||
to new hooks API (using binaries). mod_pubsub is still
|
to new hooks API (using binaries). mod_pubsub is still
|
||||||
@ -21,42 +32,42 @@
|
|||||||
permissions (thanks to Andy Skelton)(EJAB-840)
|
permissions (thanks to Andy Skelton)(EJAB-840)
|
||||||
* src/mod_pubsub/node_default.erl: Likewise
|
* src/mod_pubsub/node_default.erl: Likewise
|
||||||
|
|
||||||
2009-01-10 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-10 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_vcard_odbc.erl: Fix bug in user search.
|
* src/mod_vcard_odbc.erl: Fix bug in user search.
|
||||||
|
|
||||||
* src/mod_vcard_ldap.erl, src/mod_vcard.erl, src/mod_configure.erl,
|
* src/mod_vcard_ldap.erl, src/mod_vcard.erl, src/mod_configure.erl,
|
||||||
src/ejabberd_sm.erl, src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl,
|
src/ejabberd_sm.erl, src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl,
|
||||||
src/ejabberd_local.erl, src/mod_privacy.erl, src/mod_adhoc.erl,
|
src/ejabberd_local.erl, src/mod_privacy.erl, src/mod_adhoc.erl,
|
||||||
src/mod_pubsub/mod_pubsub.erl, src/mod_vcard_odbc.erl, src/mod_stats.erl,
|
src/mod_pubsub/mod_pubsub.erl, src/mod_vcard_odbc.erl,
|
||||||
src/mod_last.erl, src/mod_private.erl, src/mod_roster.erl,
|
src/mod_stats.erl, src/mod_last.erl, src/mod_private.erl,
|
||||||
src/mod_disco.erl, src/mod_private_odbc.erl, src/mod_configure2.erl,
|
src/mod_roster.erl, src/mod_disco.erl, src/mod_private_odbc.erl,
|
||||||
src/mod_roster_odbc.erl, src/mod_register.erl, src/mod_version.erl,
|
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/mod_register.erl,
|
||||||
src/mod_caps.erl, src/mod_last_odbc.erl, src/mod_time.erl: Update
|
src/mod_version.erl, src/mod_caps.erl, src/mod_last_odbc.erl,
|
||||||
gen_iq_handler API, require the 'Host' argument to be in binary() format.
|
src/mod_time.erl: Update gen_iq_handler API, require the 'Host'
|
||||||
|
argument to be in binary() format.
|
||||||
|
|
||||||
2009-01-10 Christophe Romain <christophe.romain@process-one.net>
|
2009-01-10 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/node_default.erl: fix unsubscription of full jid
|
* src/mod_pubsub/node_default.erl: fix unsubscription of full jid
|
||||||
subscribed node (thanks to Andy Skelton)(EJAB-839)
|
subscribed node (thanks to Andy Skelton)(EJAB-839)
|
||||||
|
|
||||||
2009-01-09 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-09 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl
|
* src/mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl
|
||||||
src/mod_offline_odbc.erl, src/mod_irc/mod_irc_connection.erl,
|
src/mod_offline_odbc.erl, src/mod_irc/mod_irc_connection.erl,
|
||||||
src/mod_irc/mod_irc.erl, src/ejabberd_c2s.erl, src/ejabberd_local.erl,
|
src/mod_irc/mod_irc.erl, src/ejabberd_c2s.erl, src/ejabberd_local.erl,
|
||||||
src/mod_pubsub/mod_pubsub.erl, src/ejabberd_s2s.erl, src/mod_roster.erl,
|
src/mod_pubsub/mod_pubsub.erl, src/ejabberd_s2s.erl,
|
||||||
src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl, src/mod_offline.erl,
|
src/mod_roster.erl, src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl,
|
||||||
src/translate.erl: Adapt to new exmpp API where get_id/1, get_lang/1,
|
src/mod_offline.erl, src/translate.erl: Adapt to new exmpp API where
|
||||||
get_initiating_entity/1, get_receiving_entity/1 and get_type/1
|
get_id/1, get_lang/1, get_initiating_entity/1, get_receiving_entity/1
|
||||||
returns binary().
|
and get_type/1 return binary().
|
||||||
|
|
||||||
* src/mod_pubsub/node_default.erl: Fix typo in variable name.
|
* src/mod_pubsub/node_default.erl: Fix typo in variable name.
|
||||||
|
|
||||||
* src/ejabberd_c2s.erl: Fix bug in handle_info/3 when dealing with
|
* src/ejabberd_c2s.erl: Fix bug in handle_info/3 when dealing with
|
||||||
VCARD requests: convert to IQ struct before invoking gen_iq_handler.
|
VCARD requests: convert to IQ struct before invoking gen_iq_handler.
|
||||||
|
|
||||||
|
|
||||||
2009-01-08 Christophe Romain <christophe.romain@process-one.net>
|
2009-01-08 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/mod_pubsub.erl: completely support subscription using
|
* src/mod_pubsub/mod_pubsub.erl: completely support subscription using
|
||||||
@ -77,7 +88,8 @@
|
|||||||
* src/mod_pubsub/node_default.erl: Likewise
|
* src/mod_pubsub/node_default.erl: Likewise
|
||||||
* src/mod_pubsub/node_club.erl: Likewise
|
* src/mod_pubsub/node_club.erl: Likewise
|
||||||
|
|
||||||
2009-01-08 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-08 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_vcard_ldap.erl, src/mod_muc/mod_muc.erl, src/mod_roster.hrl,
|
* src/mod_vcard_ldap.erl, src/mod_muc/mod_muc.erl, src/mod_roster.hrl,
|
||||||
src/mod_offline_odbc.erl, src/ejabberd_s2s_in.erl, src/adhoc.erl,
|
src/mod_offline_odbc.erl, src/ejabberd_s2s_in.erl, src/adhoc.erl,
|
||||||
src/mod_configure.erl, src/mod_irc/mod_irc_connection.erl,
|
src/mod_configure.erl, src/mod_irc/mod_irc_connection.erl,
|
||||||
@ -86,26 +98,29 @@
|
|||||||
src/mod_privacy.erl, src/mod_adhoc.erl, src/mod_pubsub/mod_pubsub.erl,
|
src/mod_privacy.erl, src/mod_adhoc.erl, src/mod_pubsub/mod_pubsub.erl,
|
||||||
src/mod_vcard_odbc.erl, src/mod_stats.erl, src/mod_last.erl,
|
src/mod_vcard_odbc.erl, src/mod_stats.erl, src/mod_last.erl,
|
||||||
src/mod_roster.erl, src/ejabberd_service.erl, src/mod_disco.erl,
|
src/mod_roster.erl, src/ejabberd_service.erl, src/mod_disco.erl,
|
||||||
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl,
|
src/mod_configure2.erl, src/mod_roster_odbc.erl,
|
||||||
src/mod_last_odbc.erl: XML attributes as binary(). Change Node argument
|
src/ejabberd_s2s_out.erl, src/mod_last_odbc.erl: XML attributes as
|
||||||
to binary in the following hooks: disco_local_items, disco_local_features,
|
binary(). Change Node argument to binary in the following hooks:
|
||||||
disco_local_identity, disco_sm_items and disco_sm_identity.
|
disco_local_items, disco_local_features, disco_local_identity,
|
||||||
|
disco_sm_items and disco_sm_identity.
|
||||||
|
|
||||||
2009-01-05 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-05 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_roster.erl: Fix typo.
|
* src/mod_roster.erl: Fix typo.
|
||||||
|
|
||||||
2009-01-03 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-01-03 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
* src/mod_pubsub_node_default.erl: Fix typo
|
|
||||||
|
|
||||||
* src/mod_vcard.erl, src/mod_vcard_ldap.erl,src/ ejabberd_hooks.erl,
|
* src/mod_pubsub_node_default.erl: Fix typo.
|
||||||
mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl,
|
|
||||||
|
* src/mod_vcard.erl, src/mod_vcard_ldap.erl, src/ejabberd_hooks.erl,
|
||||||
|
src/mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl,
|
||||||
src/mod_muc/mod_muc_log.erl, src/mod_shared_roster.erl,
|
src/mod_muc/mod_muc_log.erl, src/mod_shared_roster.erl,
|
||||||
src/ejabberd_auth_odbc.erl, src/mod_offline_odbc.erl,
|
src/ejabberd_auth_odbc.erl, src/mod_offline_odbc.erl,
|
||||||
src/ejabberd_system_monitor.erl, src/ejabberd_s2s_in.erl,
|
src/ejabberd_system_monitor.erl, src/ejabberd_s2s_in.erl,
|
||||||
src/mod_configure.erl, src/ejabberd_receiver.erl, src/mod_irc/mod_irc.erl,
|
src/mod_configure.erl, src/ejabberd_receiver.erl,
|
||||||
src/ejabberd_sm.erl, src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl,
|
src/mod_irc/mod_irc.erl, src/ejabberd_sm.erl,
|
||||||
src/mod_announce.erl, src/ejabberd_local.erl, src/mod_privacy.erl,
|
src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl, src/mod_announce.erl,
|
||||||
|
src/ejabberd_local.erl, src/mod_privacy.erl,
|
||||||
src/ejabberd_auth_internal.erl, src/mod_adhoc.erl, src/mod_echo.erl,
|
src/ejabberd_auth_internal.erl, src/mod_adhoc.erl, src/mod_echo.erl,
|
||||||
src/jlib.erl, src/mod_vcard_odbc.erl, src/ejabberd_s2s.erl,
|
src/jlib.erl, src/mod_vcard_odbc.erl, src/ejabberd_s2s.erl,
|
||||||
src/mod_stats.erl, src/ejabberd_router.erl, src/mod_last.erl,
|
src/mod_stats.erl, src/ejabberd_router.erl, src/mod_last.erl,
|
||||||
@ -113,13 +128,11 @@
|
|||||||
src/mod_disco.erl, src/mod_private_odbc.erl, src/mod_service_log.erl,
|
src/mod_disco.erl, src/mod_private_odbc.erl, src/mod_service_log.erl,
|
||||||
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/mod_offline.erl,
|
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/mod_offline.erl,
|
||||||
src/mod_register.erl, src/mod_version.erl, src/mod_caps.erl,
|
src/mod_register.erl, src/mod_version.erl, src/mod_caps.erl,
|
||||||
src/mod_last_odbc.erl: Use exmpp API to access JID fields. Keep
|
src/mod_last_odbc.erl: Use exmpp API to access JID fields. Keep #jid
|
||||||
#jid fields in binary format when possible. Change all 'user' and
|
fields in binary format when possible. Change all 'user' and 'server'
|
||||||
'server' arguments in all hooks to binary. Change internal tables of
|
arguments in all hooks to binary. Change internal tables of
|
||||||
ejabberd_sm, ejabberd_router, ejabberd_hooks, mod_last, mod_roster
|
ejabberd_sm, ejabberd_router, ejabberd_hooks, mod_last, mod_roster to
|
||||||
to use binary() storage.
|
use binary() storage.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2009-01-03 Christophe Romain <christophe.romain@process-one.net>
|
2009-01-03 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
@ -142,12 +155,61 @@
|
|||||||
* src/mod_pubsub/mod_pubsub.erl: Added "access-whitelist" and
|
* src/mod_pubsub/mod_pubsub.erl: Added "access-whitelist" and
|
||||||
"member-affiliation" features (thanks to Andy Skelton)(EJAB-780)
|
"member-affiliation" features (thanks to Andy Skelton)(EJAB-780)
|
||||||
|
|
||||||
|
2008-12-23 Badlop <badlop@process-one.net>
|
||||||
|
|
||||||
|
* src/acl.erl: New ACL: shared_group (thanks to Maxim Ryazanov)
|
||||||
|
* doc/guide.tex: Likewise
|
||||||
|
|
||||||
|
* src/mod_shared_roster.erl: Push new group members when
|
||||||
|
registered or manually added to group: EJAB-730 EJAB-731 EJAB-732
|
||||||
|
EJAB-767 EJAB-794. When user is added to group, push it to other
|
||||||
|
members, and other members to it. When user is removed from group,
|
||||||
|
push deletion to other members, and other members to it. When user
|
||||||
|
is registered, push him to members of group @all@. When user is
|
||||||
|
deleted, push deletion to members of group @all@. Document several
|
||||||
|
functions in mod_shared_roster.
|
||||||
|
|
||||||
|
* src/ejabberd_auth.erl: Rename hook user_registered to
|
||||||
|
register_user, for name consistency with the widely used hook
|
||||||
|
remove_user. Run hook register_user in ejabberd_auth, so it's run
|
||||||
|
when account is created with any method. Run hook remove_user in
|
||||||
|
ejabberd_auth, so it's run when account is deleted with any
|
||||||
|
method.
|
||||||
|
* src/ejabberd_auth_internal.erl: Likewise
|
||||||
|
* src/ejabberd_auth_ldap.erl: Likewise
|
||||||
|
* src/ejabberd_auth_odbc.erl: Likewise
|
||||||
|
* src/ejabberd_auth_pam.erl: Likewise
|
||||||
|
* src/mod_register.erl: Likewise
|
||||||
|
|
||||||
|
* src/jlib.erl: Implementation of XEP-0059 Result Set
|
||||||
|
Management (thanks to Eric Cestari)(EJAB-807)
|
||||||
|
* src/jlib.hrl: Likewise
|
||||||
|
* src/mod_muc/mod_muc.erl: Likewise
|
||||||
|
|
||||||
2008-12-23 Christophe Romain <christophe.romain@process-one.net>
|
2008-12-23 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
|
* src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
|
||||||
external contacts (EJAB-825)
|
external contacts (EJAB-825)
|
||||||
* src/mod_caps.erl: Likewise
|
* src/mod_caps.erl: Likewise
|
||||||
|
|
||||||
|
2008-12-23 Badlop <badlop@process-one.net>
|
||||||
|
|
||||||
|
* src/mod_last.erl: Implement workaround for uptime statistic in
|
||||||
|
32 bit machines, so it can show uptime greater than 50
|
||||||
|
days (EJAB-610)
|
||||||
|
* src/mod_last_odbc.erl: Likewise
|
||||||
|
* src/ejabberd_config.erl: Store start time in local_config table
|
||||||
|
|
||||||
|
* src/cyrsasl_digest.erl: Check digest-uri in SASL digest
|
||||||
|
authentication (thanks to Paul Guyot)(EJAB-569)
|
||||||
|
|
||||||
|
* src/odbc/odbc_queries.erl: Fix removal of private_storage of an
|
||||||
|
account when the account is removed
|
||||||
|
|
||||||
|
* src/mod_privacy.erl: Remove privacy lists of an account when the
|
||||||
|
account is removed (EJAB-720)
|
||||||
|
* src/mod_privacy_odbc.erl: Likewise
|
||||||
|
|
||||||
2008-12-19 Christophe Romain <christophe.romain@process-one.net>
|
2008-12-19 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/mod_pubsub.erl: Fix send_last_published_item issue
|
* src/mod_pubsub/mod_pubsub.erl: Fix send_last_published_item issue
|
||||||
@ -158,7 +220,7 @@
|
|||||||
* src/mod_pubsub/mod_pubsub.erl: Check option of the nodetree instead
|
* src/mod_pubsub/mod_pubsub.erl: Check option of the nodetree instead
|
||||||
of checking configuration (thanks to Eric Cestari)(EJAB-737)
|
of checking configuration (thanks to Eric Cestari)(EJAB-737)
|
||||||
|
|
||||||
2008-12-17 Pablo Polvorin <pablo.polvorin@process-one.net>
|
2008-12-17 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||||
|
|
||||||
* src/mod_muc/mod_muc_room.erl: Fix bug in MUC invite.
|
* src/mod_muc/mod_muc_room.erl: Fix bug in MUC invite.
|
||||||
|
|
||||||
|
@ -1239,6 +1239,14 @@ declarations of ACLs in the configuration file have the following syntax:
|
|||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
{acl, mucklres, {resource, "muckl"}}.
|
{acl, mucklres, {resource, "muckl"}}.
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\titem{\{shared\_group, <groupname>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host. Example:
|
||||||
|
\begin{verbatim}
|
||||||
|
{acl, techgroupmembers, {shared_group, "techteam"}}.
|
||||||
|
\end{verbatim}
|
||||||
|
\titem{\{shared\_group, <groupname>, <server>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host \term{<server>}. Example:
|
||||||
|
\begin{verbatim}
|
||||||
|
{acl, techgroupmembers, {shared_group, "techteam", "example.org"}}.
|
||||||
|
\end{verbatim}
|
||||||
\titem{\{user\_regexp, <regexp>\}} Matches any local user with a name that
|
\titem{\{user\_regexp, <regexp>\}} Matches any local user with a name that
|
||||||
matches \term{<regexp>} on local virtual hosts. Example:
|
matches \term{<regexp>} on local virtual hosts. Example:
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
|
@ -180,6 +180,10 @@ match_acl(ACL, JID, Host) ->
|
|||||||
((Host == global) andalso
|
((Host == global) andalso
|
||||||
lists:member(Server, ?MYHOSTS)))
|
lists:member(Server, ?MYHOSTS)))
|
||||||
andalso is_regexp_match(User, UR);
|
andalso is_regexp_match(User, UR);
|
||||||
|
{shared_group, G} ->
|
||||||
|
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
|
||||||
|
{shared_group, G, H} ->
|
||||||
|
mod_shared_roster:is_user_in_group({User, Server}, G, H);
|
||||||
{user_regexp, UR, S} ->
|
{user_regexp, UR, S} ->
|
||||||
(S == Server) andalso
|
(S == Server) andalso
|
||||||
is_regexp_match(User, UR);
|
is_regexp_match(User, UR);
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
-behaviour(cyrsasl).
|
-behaviour(cyrsasl).
|
||||||
|
|
||||||
-record(state, {step, nonce, username, authzid, get_password, auth_module}).
|
-record(state, {step, nonce, username, authzid, get_password, auth_module,
|
||||||
|
host}).
|
||||||
|
|
||||||
start(_Opts) ->
|
start(_Opts) ->
|
||||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
|
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
|
||||||
@ -26,9 +27,10 @@ start(_Opts) ->
|
|||||||
stop() ->
|
stop() ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
mech_new(_Host, GetPassword, _CheckPassword) ->
|
mech_new(Host, GetPassword, _CheckPassword) ->
|
||||||
{ok, #state{step = 1,
|
{ok, #state{step = 1,
|
||||||
nonce = randoms:get_string(),
|
nonce = randoms:get_string(),
|
||||||
|
host = Host,
|
||||||
get_password = GetPassword}}.
|
get_password = GetPassword}}.
|
||||||
|
|
||||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||||
@ -41,27 +43,35 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
|||||||
bad ->
|
bad ->
|
||||||
{error, 'bad-protocol'};
|
{error, 'bad-protocol'};
|
||||||
KeyVals ->
|
KeyVals ->
|
||||||
|
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
|
||||||
UserName = xml:get_attr_s("username", KeyVals),
|
UserName = xml:get_attr_s("username", KeyVals),
|
||||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
case is_digesturi_valid(DigestURI, State#state.host) of
|
||||||
case (State#state.get_password)(UserName) of
|
false ->
|
||||||
{false, _} ->
|
?DEBUG("User login not authorized because digest-uri "
|
||||||
|
"seems invalid: ~p", [DigestURI]),
|
||||||
{error, 'not-authorized', UserName};
|
{error, 'not-authorized', UserName};
|
||||||
{Passwd, AuthModule} ->
|
true ->
|
||||||
Response = response(KeyVals, UserName, Passwd,
|
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||||
Nonce, AuthzId, "AUTHENTICATE"),
|
case (State#state.get_password)(UserName) of
|
||||||
case xml:get_attr_s("response", KeyVals) of
|
{false, _} ->
|
||||||
Response ->
|
{error, 'not-authorized', UserName};
|
||||||
RspAuth = response(KeyVals,
|
{Passwd, AuthModule} ->
|
||||||
UserName, Passwd,
|
Response = response(KeyVals, UserName, Passwd,
|
||||||
Nonce, AuthzId, ""),
|
Nonce, AuthzId, "AUTHENTICATE"),
|
||||||
{continue,
|
case xml:get_attr_s("response", KeyVals) of
|
||||||
"rspauth=" ++ RspAuth,
|
Response ->
|
||||||
State#state{step = 5,
|
RspAuth = response(KeyVals,
|
||||||
auth_module = AuthModule,
|
UserName, Passwd,
|
||||||
username = UserName,
|
Nonce, AuthzId, ""),
|
||||||
authzid = AuthzId}};
|
{continue,
|
||||||
_ ->
|
"rspauth=" ++ RspAuth,
|
||||||
{error, 'not-authorized', UserName}
|
State#state{step = 5,
|
||||||
|
auth_module = AuthModule,
|
||||||
|
username = UserName,
|
||||||
|
authzid = AuthzId}};
|
||||||
|
_ ->
|
||||||
|
{error, 'not-authorized', UserName}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
@ -75,7 +85,6 @@ mech_step(A, B) ->
|
|||||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||||
{error, 'bad-protocol'}.
|
{error, 'bad-protocol'}.
|
||||||
|
|
||||||
|
|
||||||
parse(S) ->
|
parse(S) ->
|
||||||
parse1(S, "", []).
|
parse1(S, "", []).
|
||||||
|
|
||||||
@ -118,6 +127,23 @@ parse4([], Key, Val, Ts) ->
|
|||||||
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
|
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
|
||||||
|
|
||||||
|
|
||||||
|
%% @doc Check if the digest-uri is valid.
|
||||||
|
%% RFC-2831 allows to provide the IP address in Host,
|
||||||
|
%% however ejabberd doesn't allow that.
|
||||||
|
%% If the service (for example jabber.example.org)
|
||||||
|
%% is provided by several hosts (being one of them server3.example.org),
|
||||||
|
%% then digest-uri can be like xmpp/server3.example.org/jabber.example.org
|
||||||
|
%% In that case, ejabberd only checks the service name, not the host.
|
||||||
|
is_digesturi_valid(DigestURICase, JabberHost) ->
|
||||||
|
DigestURI = stringprep:tolower(DigestURICase),
|
||||||
|
case catch string:tokens(DigestURI, "/") of
|
||||||
|
["xmpp", Host] when Host == JabberHost ->
|
||||||
|
true;
|
||||||
|
["xmpp", _Host, ServName] when ServName == JabberHost ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,6 +139,7 @@ set_password(User, Server, Password) ->
|
|||||||
Res
|
Res
|
||||||
end, {error, not_allowed}, auth_modules(Server)).
|
end, {error, not_allowed}, auth_modules(Server)).
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed}
|
||||||
try_register(_User, _Server, "") ->
|
try_register(_User, _Server, "") ->
|
||||||
%% We do not allow empty password
|
%% We do not allow empty password
|
||||||
{error, not_allowed};
|
{error, not_allowed};
|
||||||
@ -149,12 +150,19 @@ try_register(User, Server, Password) ->
|
|||||||
false ->
|
false ->
|
||||||
case lists:member(exmpp_stringprep:nameprep(Server), ?MYHOSTS) of
|
case lists:member(exmpp_stringprep:nameprep(Server), ?MYHOSTS) of
|
||||||
true ->
|
true ->
|
||||||
lists:foldl(
|
Res = lists:foldl(
|
||||||
fun(_M, {atomic, ok} = Res) ->
|
fun(_M, {atomic, ok} = Res) ->
|
||||||
Res;
|
Res;
|
||||||
(M, _) ->
|
(M, _) ->
|
||||||
M:try_register(User, Server, Password)
|
M:try_register(User, Server, Password)
|
||||||
end, {error, not_allowed}, auth_modules(Server));
|
end, {error, not_allowed}, auth_modules(Server)),
|
||||||
|
case Res of
|
||||||
|
{atomic, ok} ->
|
||||||
|
ejabberd_hooks:run(register_user, Server,
|
||||||
|
[User, Server]),
|
||||||
|
{atomic, ok};
|
||||||
|
_ -> Res
|
||||||
|
end;
|
||||||
false ->
|
false ->
|
||||||
{error, not_allowed}
|
{error, not_allowed}
|
||||||
end
|
end
|
||||||
@ -251,17 +259,37 @@ is_user_exists_in_other_modules(Module, User, Server) ->
|
|||||||
M:is_user_exists(User, Server)
|
M:is_user_exists(User, Server)
|
||||||
end, auth_modules(Server)--[Module]).
|
end, auth_modules(Server)--[Module]).
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> ok | error | {error, not_allowed}
|
||||||
|
%% Remove user.
|
||||||
|
%% Note: it may return ok even if there was some problem removing the user.
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
lists:foreach(
|
R = lists:foreach(
|
||||||
fun(M) ->
|
fun(M) ->
|
||||||
M:remove_user(User, Server)
|
M:remove_user(User, Server)
|
||||||
end, auth_modules(Server)).
|
end, auth_modules(Server)),
|
||||||
|
case R of
|
||||||
|
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
R.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error
|
||||||
|
%% Try to remove user if the provided password is correct.
|
||||||
|
%% The removal is attempted in each auth method provided:
|
||||||
|
%% when one returns 'ok' the loop stops;
|
||||||
|
%% if no method returns 'ok' then it returns the error message indicated by the last method attempted.
|
||||||
remove_user(User, Server, Password) ->
|
remove_user(User, Server, Password) ->
|
||||||
lists:foreach(
|
R = lists:foldl(
|
||||||
fun(M) ->
|
fun(_M, ok = Res) ->
|
||||||
|
Res;
|
||||||
|
(M, _) ->
|
||||||
M:remove_user(User, Server, Password)
|
M:remove_user(User, Server, Password)
|
||||||
end, auth_modules(Server)).
|
end, error, auth_modules(Server)),
|
||||||
|
case R of
|
||||||
|
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
R.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
@ -112,6 +112,7 @@ set_password(User, Server, Password) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
|
||||||
try_register(User, Server, Password) ->
|
try_register(User, Server, Password) ->
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
@ -252,6 +253,9 @@ is_user_exists(User, Server) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> ok
|
||||||
|
%% Remove user.
|
||||||
|
%% Note: it returns ok even if there was some problem removing the user.
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
@ -261,14 +265,14 @@ remove_user(User, Server) ->
|
|||||||
mnesia:delete({passwd, US})
|
mnesia:delete({passwd, US})
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F),
|
mnesia:transaction(F),
|
||||||
ejabberd_hooks:run(remove_user,
|
ok
|
||||||
list_to_binary(LServer),
|
|
||||||
[list_to_binary(User), list_to_binary(Server)])
|
|
||||||
catch
|
catch
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
|
||||||
|
%% Remove user if the provided password is correct.
|
||||||
remove_user(User, Server, Password) ->
|
remove_user(User, Server, Password) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
@ -287,9 +291,6 @@ remove_user(User, Server, Password) ->
|
|||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
ejabberd_hooks:run(remove_user,
|
|
||||||
list_to_binary(LServer),
|
|
||||||
[list_to_binary(User), list_to_binary(Server)]),
|
|
||||||
ok;
|
ok;
|
||||||
{atomic, Res} ->
|
{atomic, Res} ->
|
||||||
Res;
|
Res;
|
||||||
|
@ -153,6 +153,7 @@ check_password(User, Server, Password, _StreamID, _Digest) ->
|
|||||||
set_password(_User, _Server, _Password) ->
|
set_password(_User, _Server, _Password) ->
|
||||||
{error, not_allowed}.
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> {error, not_allowed}
|
||||||
try_register(_User, _Server, _Password) ->
|
try_register(_User, _Server, _Password) ->
|
||||||
{error, not_allowed}.
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ set_password(User, Server, Password) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
|
||||||
try_register(User, Server, Password) ->
|
try_register(User, Server, Password) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
@ -225,19 +226,23 @@ is_user_exists(User, Server) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> ok | error
|
||||||
|
%% Remove user.
|
||||||
|
%% Note: it may return ok even if there was some problem removing the user.
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
catch odbc_queries:del_user(LServer, Username),
|
catch odbc_queries:del_user(LServer, Username),
|
||||||
ejabberd_hooks:run(remove_user, list_to_binary(LServer),
|
ok
|
||||||
[list_to_binary(User), list_to_binary(Server)])
|
|
||||||
catch
|
catch
|
||||||
_ ->
|
_ ->
|
||||||
error
|
error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
|
||||||
|
%% Remove user if the provided password is correct.
|
||||||
remove_user(User, Server, Password) ->
|
remove_user(User, Server, Password) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
@ -249,8 +254,6 @@ remove_user(User, Server, Password) ->
|
|||||||
LServer, Username, Pass),
|
LServer, Username, Pass),
|
||||||
case Result of
|
case Result of
|
||||||
{selected, ["password"], [{Password}]} ->
|
{selected, ["password"], [{Password}]} ->
|
||||||
ejabberd_hooks:run(remove_user, list_to_binary(LServer),
|
|
||||||
[list_to_binary(User), list_to_binary(Server)]),
|
|
||||||
ok;
|
ok;
|
||||||
{selected, ["password"], []} ->
|
{selected, ["password"], []} ->
|
||||||
not_exists;
|
not_exists;
|
||||||
|
@ -91,7 +91,7 @@ remove_user(_User, _Server) ->
|
|||||||
{error, not_allowed}.
|
{error, not_allowed}.
|
||||||
|
|
||||||
remove_user(_User, _Server, _Password) ->
|
remove_user(_User, _Server, _Password) ->
|
||||||
{error, not_allowed}.
|
not_allowed.
|
||||||
|
|
||||||
plain_password_required() ->
|
plain_password_required() ->
|
||||||
true.
|
true.
|
||||||
|
@ -55,7 +55,10 @@ start() ->
|
|||||||
{attributes, record_info(fields, local_config)}]),
|
{attributes, record_info(fields, local_config)}]),
|
||||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||||
Config = get_ejabberd_config_path(),
|
Config = get_ejabberd_config_path(),
|
||||||
load_file(Config).
|
load_file(Config),
|
||||||
|
%% This start time is used by mod_last:
|
||||||
|
add_local_option(node_start, now()),
|
||||||
|
ok.
|
||||||
|
|
||||||
%% @doc Get the filename of the ejabberd configuration file.
|
%% @doc Get the filename of the ejabberd configuration file.
|
||||||
%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
|
%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
|
||||||
@ -76,7 +79,7 @@ get_ejabberd_config_path() ->
|
|||||||
|
|
||||||
%% @doc Load the ejabberd configuration file.
|
%% @doc Load the ejabberd configuration file.
|
||||||
%% It also includes additional configuration files and replaces macros.
|
%% It also includes additional configuration files and replaces macros.
|
||||||
%% @spec (File::string()) -> [term()]
|
%% @spec (File::string()) -> ok
|
||||||
load_file(File) ->
|
load_file(File) ->
|
||||||
Terms = get_plain_terms_file(File),
|
Terms = get_plain_terms_file(File),
|
||||||
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||||
|
69
src/jlib.erl
69
src/jlib.erl
@ -36,6 +36,9 @@
|
|||||||
decode_base64/1,
|
decode_base64/1,
|
||||||
encode_base64/1,
|
encode_base64/1,
|
||||||
ip_to_list/1,
|
ip_to_list/1,
|
||||||
|
rsm_encode/1,
|
||||||
|
rsm_encode/2,
|
||||||
|
rsm_decode/1,
|
||||||
from_old_jid/1,
|
from_old_jid/1,
|
||||||
short_jid/1,
|
short_jid/1,
|
||||||
short_bare_jid/1,
|
short_bare_jid/1,
|
||||||
@ -44,6 +47,8 @@
|
|||||||
|
|
||||||
-include_lib("exmpp/include/exmpp.hrl").
|
-include_lib("exmpp/include/exmpp.hrl").
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
|
||||||
parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
|
parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
|
||||||
case exmpp_xml:get_attribute_from_list(Attrs, 'type', "") of
|
case exmpp_xml:get_attribute_from_list(Attrs, 'type', "") of
|
||||||
@ -75,6 +80,70 @@ parse_xdata_values([#xmlel{name = 'value', children = SubEls} | Els], Res) ->
|
|||||||
parse_xdata_values([_ | Els], Res) ->
|
parse_xdata_values([_ | Els], Res) ->
|
||||||
parse_xdata_values(Els, Res).
|
parse_xdata_values(Els, Res).
|
||||||
|
|
||||||
|
rsm_decode(#iq{payload=SubEl})->
|
||||||
|
rsm_decode(SubEl);
|
||||||
|
rsm_decode(#xmlel{}=SubEl)->
|
||||||
|
case exmpp_xml:get_element(SubEl, 'set') of
|
||||||
|
undefined ->
|
||||||
|
none;
|
||||||
|
#xmlelement{name = 'set', children = SubEls}->
|
||||||
|
lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls)
|
||||||
|
end.
|
||||||
|
|
||||||
|
rsm_parse_element(#xmlel{name = 'max'}=Elem, RsmIn)->
|
||||||
|
CountStr = exmpp_xml:get_cdata_as_list(Elem),
|
||||||
|
{Count, _} = string:to_integer(CountStr),
|
||||||
|
RsmIn#rsm_in{max=Count};
|
||||||
|
|
||||||
|
rsm_parse_element(#xmlel{name = 'before'}=Elem, RsmIn)->
|
||||||
|
UID = exmpp_xml:get_cdata_as_list(Elem),
|
||||||
|
RsmIn#rsm_in{direction=before, id=UID};
|
||||||
|
|
||||||
|
rsm_parse_element(#xmlel{name = 'after'}=Elem, RsmIn)->
|
||||||
|
UID = exmpp_xml:get_cdata_as_list(Elem),
|
||||||
|
RsmIn#rsm_in{direction=aft, id=UID};
|
||||||
|
|
||||||
|
rsm_parse_element(#xmlel{name = 'index'}=Elem, RsmIn)->
|
||||||
|
IndexStr = exmpp_xml:get_cdata_as_list(Elem),
|
||||||
|
{Index, _} = string:to_integer(IndexStr),
|
||||||
|
RsmIn#rsm_in{index=Index};
|
||||||
|
|
||||||
|
|
||||||
|
rsm_parse_element(_, RsmIn)->
|
||||||
|
RsmIn.
|
||||||
|
|
||||||
|
rsm_encode(#iq{payload=SubEl}=IQ_Rec,RsmOut)->
|
||||||
|
Set = #xmlel{ns = ?NS_RSM, name = 'set', children =
|
||||||
|
lists:reverse(rsm_encode_out(RsmOut))},
|
||||||
|
New = exmpp_xml:prepend_child(SubEl, Set),
|
||||||
|
IQ_Rec#iq{payload=New}.
|
||||||
|
|
||||||
|
rsm_encode(none)->
|
||||||
|
[];
|
||||||
|
rsm_encode(RsmOut)->
|
||||||
|
[#xmlel{ns = ?NS_RSM, name = 'set', children = lists:reverse(rsm_encode_out(RsmOut))}].
|
||||||
|
rsm_encode_out(#rsm_out{count=Count, index=Index, first=First, last=Last})->
|
||||||
|
El = rsm_encode_first(First, Index, []),
|
||||||
|
El2 = rsm_encode_last(Last,El),
|
||||||
|
rsm_encode_count(Count, El2).
|
||||||
|
|
||||||
|
rsm_encode_first(undefined, undefined, Arr) ->
|
||||||
|
Arr;
|
||||||
|
rsm_encode_first(First, undefined, Arr) ->
|
||||||
|
[#xmlel{ns = ?NS_RSM, name = 'first', children = [#xmlcdata{cdata = list_to_binary(First)}]}|Arr];
|
||||||
|
rsm_encode_first(First, Index, Arr) ->
|
||||||
|
[#xmlel{ns = ?NS_RSM, name = 'first', attrs = [#xmlattr{name = 'index', value = i2b(Index)}], children = [#xmlcdata{cdata = list_to_binary(First)}]}|Arr].
|
||||||
|
|
||||||
|
rsm_encode_last(undefined, Arr) -> Arr;
|
||||||
|
rsm_encode_last(Last, Arr) ->
|
||||||
|
[#xmlel{ns = ?NS_RSM, name = 'last', children = [#xmlcdata{cdata = list_to_binary(Last)}]}|Arr].
|
||||||
|
|
||||||
|
rsm_encode_count(undefined, Arr)-> Arr;
|
||||||
|
rsm_encode_count(Count, Arr)->
|
||||||
|
[#xmlel{ns = ?NS_RSM, name = 'count', children = [#xmlcdata{cdata = i2b(Count)}]} | Arr].
|
||||||
|
|
||||||
|
i2b(I) when is_integer(I) -> list_to_binary(integer_to_list(I));
|
||||||
|
i2b(L) when is_list(L) -> list_to_binary(L).
|
||||||
|
|
||||||
timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
||||||
lists:flatten(
|
lists:flatten(
|
||||||
|
289
src/jlib.hrl
289
src/jlib.hrl
@ -19,290 +19,5 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
-record(rsm_in, {max, direction, id, index}).
|
||||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
-record(rsm_out, {count, index, first, last}).
|
||||||
-define(NS_VCARD, "vcard-temp").
|
|
||||||
-define(NS_AUTH, "jabber:iq:auth").
|
|
||||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
|
||||||
-define(NS_REGISTER, "jabber:iq:register").
|
|
||||||
-define(NS_SEARCH, "jabber:iq:search").
|
|
||||||
-define(NS_ROSTER, "jabber:iq:roster").
|
|
||||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
|
||||||
-define(NS_PRIVATE, "jabber:iq:private").
|
|
||||||
-define(NS_VERSION, "jabber:iq:version").
|
|
||||||
-define(NS_TIME, "jabber:iq:time").
|
|
||||||
-define(NS_LAST, "jabber:iq:last").
|
|
||||||
-define(NS_XDATA, "jabber:x:data").
|
|
||||||
-define(NS_IQDATA, "jabber:iq:data").
|
|
||||||
-define(NS_DELAY, "jabber:x:delay").
|
|
||||||
-define(NS_EXPIRE, "jabber:x:expire").
|
|
||||||
-define(NS_EVENT, "jabber:x:event").
|
|
||||||
-define(NS_XCONFERENCE, "jabber:x:conference").
|
|
||||||
-define(NS_STATS, "http://jabber.org/protocol/stats").
|
|
||||||
-define(NS_MUC, "http://jabber.org/protocol/muc").
|
|
||||||
-define(NS_MUC_USER, "http://jabber.org/protocol/muc#user").
|
|
||||||
-define(NS_MUC_ADMIN, "http://jabber.org/protocol/muc#admin").
|
|
||||||
-define(NS_MUC_OWNER, "http://jabber.org/protocol/muc#owner").
|
|
||||||
-define(NS_PUBSUB, "http://jabber.org/protocol/pubsub").
|
|
||||||
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
|
|
||||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
|
||||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
|
||||||
-define(NS_PUBSUB_ERRORS,"http://jabber.org/protocol/pubsub#errors").
|
|
||||||
-define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config").
|
|
||||||
-define(NS_PUBSUB_SUB_AUTH, "http://jabber.org/protocol/pubsub#subscribe_authorization").
|
|
||||||
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
|
|
||||||
-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams").
|
|
||||||
-define(NS_ADMIN, "http://jabber.org/protocol/admin").
|
|
||||||
|
|
||||||
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
|
|
||||||
|
|
||||||
-define(NS_STREAM, "http://etherx.jabber.org/streams").
|
|
||||||
|
|
||||||
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
|
|
||||||
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
|
|
||||||
|
|
||||||
-define(NS_TLS, "urn:ietf:params:xml:ns:xmpp-tls").
|
|
||||||
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
|
|
||||||
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
|
|
||||||
-define(NS_BIND, "urn:ietf:params:xml:ns:xmpp-bind").
|
|
||||||
|
|
||||||
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
|
|
||||||
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
|
|
||||||
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
|
|
||||||
|
|
||||||
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
|
|
||||||
|
|
||||||
-define(NS_CAPS, "http://jabber.org/protocol/caps").
|
|
||||||
|
|
||||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
|
||||||
-define(STANZA_ERROR(Code, Type, Condition),
|
|
||||||
{xmlelement, "error",
|
|
||||||
[{"code", Code}, {"type", Type}],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
|
||||||
|
|
||||||
-define(ERR_BAD_REQUEST,
|
|
||||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
|
||||||
-define(ERR_CONFLICT,
|
|
||||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
|
||||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
|
||||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
|
||||||
-define(ERR_FORBIDDEN,
|
|
||||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
|
||||||
-define(ERR_GONE,
|
|
||||||
?STANZA_ERROR("302", "modify", "gone")).
|
|
||||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
|
||||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
|
||||||
-define(ERR_ITEM_NOT_FOUND,
|
|
||||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
|
||||||
-define(ERR_JID_MALFORMED,
|
|
||||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
|
||||||
-define(ERR_NOT_ACCEPTABLE,
|
|
||||||
?STANZA_ERROR("406", "modify", "not-acceptable")).
|
|
||||||
-define(ERR_NOT_ALLOWED,
|
|
||||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
|
||||||
-define(ERR_NOT_AUTHORIZED,
|
|
||||||
?STANZA_ERROR("401", "auth", "not-authorized")).
|
|
||||||
-define(ERR_PAYMENT_REQUIRED,
|
|
||||||
?STANZA_ERROR("402", "auth", "payment-required")).
|
|
||||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
|
||||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
|
||||||
-define(ERR_REDIRECT,
|
|
||||||
?STANZA_ERROR("302", "modify", "redirect")).
|
|
||||||
-define(ERR_REGISTRATION_REQUIRED,
|
|
||||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
|
||||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
|
||||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
|
||||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
|
||||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
|
||||||
-define(ERR_RESOURCE_CONSTRAINT,
|
|
||||||
?STANZA_ERROR("500", "wait", "resource-constraint")).
|
|
||||||
-define(ERR_SERVICE_UNAVAILABLE,
|
|
||||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
|
||||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
|
||||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
|
||||||
-define(ERR_UNEXPECTED_REQUEST,
|
|
||||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
|
||||||
%-define(ERR_,
|
|
||||||
% ?STANZA_ERROR("", "", "")).
|
|
||||||
|
|
||||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang, Text),
|
|
||||||
{xmlelement, "error",
|
|
||||||
[{"code", Code}, {"type", Type}],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []},
|
|
||||||
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
|
||||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
|
||||||
|
|
||||||
-define(ERRT_BAD_REQUEST(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "modify", "bad-request", Lang, Text)).
|
|
||||||
-define(ERRT_CONFLICT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("409", "cancel", "conflict", Lang, Text)).
|
|
||||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("501", "cancel", "feature-not-implemented", Lang, Text)).
|
|
||||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
|
||||||
?STANZA_ERRORT("403", "auth", "forbidden", Lang, Text)).
|
|
||||||
-define(ERRT_GONE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("302", "modify", "gone", Lang, Text)).
|
|
||||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
|
||||||
?STANZA_ERRORT("500", "wait", "internal-server-error", Lang, Text)).
|
|
||||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "cancel", "item-not-found", Lang, Text)).
|
|
||||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "modify", "jid-malformed", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("406", "modify", "not-acceptable", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("405", "cancel", "not-allowed", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("401", "auth", "not-authorized", Lang, Text)).
|
|
||||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("402", "auth", "payment-required", Lang, Text)).
|
|
||||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "wait", "recipient-unavailable", Lang, Text)).
|
|
||||||
-define(ERRT_REDIRECT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("302", "modify", "redirect", Lang, Text)).
|
|
||||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("407", "auth", "registration-required", Lang, Text)).
|
|
||||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "cancel", "remote-server-not-found", Lang, Text)).
|
|
||||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("504", "wait", "remote-server-timeout", Lang, Text)).
|
|
||||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("500", "wait", "resource-constraint", Lang, Text)).
|
|
||||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("503", "cancel", "service-unavailable", Lang, Text)).
|
|
||||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("407", "auth", "subscription-required", Lang, Text)).
|
|
||||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
|
||||||
|
|
||||||
% Auth stanza errors
|
|
||||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
|
||||||
?ERRT_NOT_ACCEPTABLE(Lang, "No resource provided")).
|
|
||||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
|
||||||
?ERRT_NOT_ACCEPTABLE(Lang, "Illegal resource format")).
|
|
||||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
|
||||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
|
||||||
|
|
||||||
|
|
||||||
-define(STREAM_ERROR(Condition),
|
|
||||||
{xmlelement, "stream:error",
|
|
||||||
[],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
|
||||||
|
|
||||||
-define(SERR_BAD_FORMAT,
|
|
||||||
?STREAM_ERROR("bad-format")).
|
|
||||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
|
||||||
?STREAM_ERROR("bad-namespace-prefix")).
|
|
||||||
-define(SERR_CONFLICT,
|
|
||||||
?STREAM_ERROR("conflict")).
|
|
||||||
-define(SERR_CONNECTION_TIMEOUT,
|
|
||||||
?STREAM_ERROR("connection-timeout")).
|
|
||||||
-define(SERR_HOST_GONE,
|
|
||||||
?STREAM_ERROR("host-gone")).
|
|
||||||
-define(SERR_HOST_UNKNOWN,
|
|
||||||
?STREAM_ERROR("host-unknown")).
|
|
||||||
-define(SERR_IMPROPER_ADDRESSING,
|
|
||||||
?STREAM_ERROR("improper-addressing")).
|
|
||||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
|
||||||
?STREAM_ERROR("internal-server-error")).
|
|
||||||
-define(SERR_INVALID_FROM,
|
|
||||||
?STREAM_ERROR("invalid-from")).
|
|
||||||
-define(SERR_INVALID_ID,
|
|
||||||
?STREAM_ERROR("invalid-id")).
|
|
||||||
-define(SERR_INVALID_NAMESPACE,
|
|
||||||
?STREAM_ERROR("invalid-namespace")).
|
|
||||||
-define(SERR_INVALID_XML,
|
|
||||||
?STREAM_ERROR("invalid-xml")).
|
|
||||||
-define(SERR_NOT_AUTHORIZED,
|
|
||||||
?STREAM_ERROR("not-authorized")).
|
|
||||||
-define(SERR_POLICY_VIOLATION,
|
|
||||||
?STREAM_ERROR("policy-violation")).
|
|
||||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
|
||||||
?STREAM_ERROR("remote-connection-failed")).
|
|
||||||
-define(SERR_RESOURSE_CONSTRAINT,
|
|
||||||
?STREAM_ERROR("resource-constraint")).
|
|
||||||
-define(SERR_RESTRICTED_XML,
|
|
||||||
?STREAM_ERROR("restricted-xml")).
|
|
||||||
% TODO: include hostname or IP
|
|
||||||
-define(SERR_SEE_OTHER_HOST,
|
|
||||||
?STREAM_ERROR("see-other-host")).
|
|
||||||
-define(SERR_SYSTEM_SHUTDOWN,
|
|
||||||
?STREAM_ERROR("system-shutdown")).
|
|
||||||
-define(SERR_UNSUPPORTED_ENCODING,
|
|
||||||
?STREAM_ERROR("unsupported-encoding")).
|
|
||||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
|
||||||
?STREAM_ERROR("unsupported-stanza-type")).
|
|
||||||
-define(SERR_UNSUPPORTED_VERSION,
|
|
||||||
?STREAM_ERROR("unsupported-version")).
|
|
||||||
-define(SERR_XML_NOT_WELL_FORMED,
|
|
||||||
?STREAM_ERROR("xml-not-well-formed")).
|
|
||||||
%-define(SERR_,
|
|
||||||
% ?STREAM_ERROR("")).
|
|
||||||
|
|
||||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
|
||||||
{xmlelement, "stream:error",
|
|
||||||
[],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
|
||||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
|
||||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
|
||||||
|
|
||||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
|
||||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
|
||||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
|
||||||
-define(SERRT_CONFLICT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
|
||||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
|
||||||
-define(SERRT_HOST_GONE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
|
||||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
|
||||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
|
||||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
|
||||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
|
||||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
|
||||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_ID(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_XML(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
|
||||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
|
||||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
|
||||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
|
||||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
|
||||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
|
||||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
|
||||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
|
||||||
% TODO: include hostname or IP
|
|
||||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
|
||||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
|
||||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
|
||||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
|
||||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
|
||||||
%-define(SERRT_(Lang, Text),
|
|
||||||
% ?STREAM_ERRORT("", Lang, Text)).
|
|
||||||
|
|
||||||
|
|
||||||
-record(jid, {user, server, resource,
|
|
||||||
luser, lserver, lresource}).
|
|
||||||
|
|
||||||
-record(iq, {id = "",
|
|
||||||
type,
|
|
||||||
xmlns = "",
|
|
||||||
lang = "",
|
|
||||||
sub_el}).
|
|
||||||
|
|
||||||
|
@ -71,14 +71,36 @@ stop(Host) ->
|
|||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
||||||
|
|
||||||
|
%%%
|
||||||
|
%%% Uptime of ejabberd node
|
||||||
|
%%%
|
||||||
|
|
||||||
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
||||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
Sec = get_node_uptime(),
|
||||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
||||||
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
||||||
exmpp_iq:result(IQ_Rec, Response);
|
exmpp_iq:result(IQ_Rec, Response);
|
||||||
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
||||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||||
|
|
||||||
|
%% @spec () -> integer()
|
||||||
|
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
||||||
|
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
||||||
|
get_node_uptime() ->
|
||||||
|
case ejabberd_config:get_local_option(node_start) of
|
||||||
|
{_, _, _} = StartNow ->
|
||||||
|
now_to_seconds(now()) - now_to_seconds(StartNow);
|
||||||
|
_undefined ->
|
||||||
|
trunc(element(1, erlang:statistics(wall_clock))/1000)
|
||||||
|
end.
|
||||||
|
|
||||||
|
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||||
|
MegaSecs * 1000000 + Secs.
|
||||||
|
|
||||||
|
|
||||||
|
%%%
|
||||||
|
%%% Serve queries about user last online
|
||||||
|
%%%
|
||||||
|
|
||||||
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
||||||
{Subscription, _Groups} =
|
{Subscription, _Groups} =
|
||||||
@ -117,8 +139,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
|||||||
[] ->
|
[] ->
|
||||||
exmpp_iq:error(IQ_Rec, 'service-unavailable');
|
exmpp_iq:error(IQ_Rec, 'service-unavailable');
|
||||||
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
||||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
TimeStamp2 = now_to_seconds(now()),
|
||||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
|
||||||
Sec = TimeStamp2 - TimeStamp,
|
Sec = TimeStamp2 - TimeStamp,
|
||||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
||||||
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
||||||
@ -129,8 +150,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
|||||||
|
|
||||||
|
|
||||||
on_presence_update(User, Server, _Resource, Status) ->
|
on_presence_update(User, Server, _Resource, Status) ->
|
||||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
TimeStamp = now_to_seconds(now()),
|
||||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
|
||||||
store_last_info(User, Server, TimeStamp, Status).
|
store_last_info(User, Server, TimeStamp, Status).
|
||||||
|
|
||||||
store_last_info(User, Server, TimeStamp, Status)
|
store_last_info(User, Server, TimeStamp, Status)
|
||||||
@ -148,7 +168,8 @@ store_last_info(User, Server, TimeStamp, Status)
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Returns: {ok, Timestamp, Status} | not_found
|
%% @spec (LUser::string(), LServer::string() ->
|
||||||
|
%% {ok, Timestamp::integer(), Status::string()} | not_found
|
||||||
get_last_info(LUser, LServer) when is_binary(LUser), is_binary(LServer) ->
|
get_last_info(LUser, LServer) when is_binary(LUser), is_binary(LServer) ->
|
||||||
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
|
@ -64,14 +64,36 @@ stop(Host) ->
|
|||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
||||||
|
|
||||||
|
%%%
|
||||||
|
%%% Uptime of ejabberd node
|
||||||
|
%%%
|
||||||
|
|
||||||
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
||||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
Sec = get_node_uptime(),
|
||||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
||||||
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
||||||
exmpp_iq:result(IQ_Rec, Response);
|
exmpp_iq:result(IQ_Rec, Response);
|
||||||
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
||||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||||
|
|
||||||
|
%% @spec () -> integer()
|
||||||
|
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
||||||
|
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
||||||
|
get_node_uptime() ->
|
||||||
|
case ejabberd_config:get_local_option(node_start) of
|
||||||
|
{_, _, _} = StartNow ->
|
||||||
|
now_to_seconds(now()) - now_to_seconds(StartNow);
|
||||||
|
_undefined ->
|
||||||
|
trunc(element(1, erlang:statistics(wall_clock))/1000)
|
||||||
|
end.
|
||||||
|
|
||||||
|
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||||
|
MegaSecs * 1000000 + Secs.
|
||||||
|
|
||||||
|
|
||||||
|
%%%
|
||||||
|
%%% Serve queries about user last online
|
||||||
|
%%%
|
||||||
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
||||||
User = exmpp_jid:lnode_as_list(To),
|
User = exmpp_jid:lnode_as_list(To),
|
||||||
Server = exmpp_jid:ldomain_as_list(To),
|
Server = exmpp_jid:ldomain_as_list(To),
|
||||||
@ -112,8 +134,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
|||||||
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
|
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
|
||||||
case catch list_to_integer(STimeStamp) of
|
case catch list_to_integer(STimeStamp) of
|
||||||
TimeStamp when is_integer(TimeStamp) ->
|
TimeStamp when is_integer(TimeStamp) ->
|
||||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
TimeStamp2 = now_to_seconds(now()),
|
||||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
|
||||||
Sec = TimeStamp2 - TimeStamp,
|
Sec = TimeStamp2 - TimeStamp,
|
||||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
||||||
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
||||||
@ -127,8 +148,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
on_presence_update(User, Server, _Resource, Status) ->
|
on_presence_update(User, Server, _Resource, Status) ->
|
||||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
TimeStamp = now_to_seconds(now()),
|
||||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
|
||||||
store_last_info(User, Server, TimeStamp, Status).
|
store_last_info(User, Server, TimeStamp, Status).
|
||||||
|
|
||||||
store_last_info(User, Server, TimeStamp, Status)
|
store_last_info(User, Server, TimeStamp, Status)
|
||||||
@ -148,7 +168,8 @@ store_last_info(User, Server, TimeStamp, Status)
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Returns: {ok, Timestamp, Status} | not_found
|
%% @spec (LUser::string(), LServer::string() ->
|
||||||
|
%% {ok, Timestamp::integer(), Status::string()} | not_found
|
||||||
get_last_info(LUser, LServer) ->
|
get_last_info(LUser, LServer) ->
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
case catch odbc_queries:get_last(LServer, Username) of
|
case catch odbc_queries:get_last(LServer, Username) of
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
-include_lib("exmpp/include/exmpp.hrl").
|
-include_lib("exmpp/include/exmpp.hrl").
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
|
||||||
-record(muc_room, {name_host, opts}).
|
-record(muc_room, {name_host, opts}).
|
||||||
@ -125,11 +126,11 @@ forget_room(Host, Name) when is_binary(Host), is_binary(Name) ->
|
|||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F).
|
||||||
|
|
||||||
process_iq_disco_items(Host, From, To, #iq{} = IQ) when is_binary(Host) ->
|
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) when is_binary(Host) ->
|
||||||
Lang = exmpp_stanza:get_lang(IQ),
|
Rsm = jlib:rsm_decode(IQ),
|
||||||
Res = exmpp_iq:result(IQ, #xmlel{ns = ?NS_DISCO_ITEMS,
|
Res = exmpp_iq:result(IQ, #xmlel{ns = ?NS_DISCO_ITEMS,
|
||||||
name = 'query',
|
name = 'query',
|
||||||
children = iq_disco_items(Host, From, Lang)}),
|
children = iq_disco_items(Host, From, Lang, Rsm)}),
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
From,
|
From,
|
||||||
exmpp_iq:iq_to_xmlel(Res)).
|
exmpp_iq:iq_to_xmlel(Res)).
|
||||||
@ -513,13 +514,15 @@ iq_disco_info(Lang) ->
|
|||||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||||
[#xmlattr{name = 'var',
|
[#xmlattr{name = 'var',
|
||||||
value = ?NS_INBAND_REGISTER_s}]},
|
value = ?NS_INBAND_REGISTER_s}]},
|
||||||
|
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||||
|
[#xmlattr{name = 'var',
|
||||||
|
value = ?NS_RSM_s}]},
|
||||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||||
[#xmlattr{name = 'var',
|
[#xmlattr{name = 'var',
|
||||||
value = ?NS_VCARD_s}]}].
|
value = ?NS_VCARD_s}]}].
|
||||||
|
|
||||||
|
|
||||||
|
iq_disco_items(Host, From, Lang, none) when is_binary(Host) ->
|
||||||
iq_disco_items(Host, From, Lang) when is_binary(Host) ->
|
|
||||||
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||||
case catch gen_fsm:sync_send_all_state_event(
|
case catch gen_fsm:sync_send_all_state_event(
|
||||||
Pid, {get_disco_item, From, Lang}, 100) of
|
Pid, {get_disco_item, From, Lang}, 100) of
|
||||||
@ -535,7 +538,72 @@ iq_disco_items(Host, From, Lang) when is_binary(Host) ->
|
|||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end, get_vh_rooms(Host)).
|
end, get_vh_rooms(Host));
|
||||||
|
|
||||||
|
iq_disco_items(Host, From, Lang, Rsm) ->
|
||||||
|
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
||||||
|
RsmOut = jlib:rsm_encode(RsmO),
|
||||||
|
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||||
|
case catch gen_fsm:sync_send_all_state_event(
|
||||||
|
Pid, {get_disco_item, From, Lang}, 100) of
|
||||||
|
{item, Desc} ->
|
||||||
|
flush(),
|
||||||
|
{true,
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", jlib:jid_to_string({Name, Host, ""})},
|
||||||
|
{"name", Desc}], []}};
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end, Rooms) ++ RsmOut.
|
||||||
|
|
||||||
|
get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
|
||||||
|
AllRooms = lists:sort(get_vh_rooms(Host)),
|
||||||
|
Count = erlang:length(AllRooms),
|
||||||
|
Guard = case Direction of
|
||||||
|
_ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
|
||||||
|
aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
|
||||||
|
before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
|
||||||
|
_ -> [{'==', {element, 2, '$1'}, Host}]
|
||||||
|
end,
|
||||||
|
L = lists:sort(
|
||||||
|
mnesia:dirty_select(muc_online_room,
|
||||||
|
[{#muc_online_room{name_host = '$1', _ = '_'},
|
||||||
|
Guard,
|
||||||
|
['$_']}])),
|
||||||
|
L2 = if
|
||||||
|
Index == undefined andalso Direction == before ->
|
||||||
|
lists:reverse(lists:sublist(lists:reverse(L), 1, M));
|
||||||
|
Index == undefined ->
|
||||||
|
lists:sublist(L, 1, M);
|
||||||
|
Index > Count orelse Index < 0 ->
|
||||||
|
[];
|
||||||
|
true ->
|
||||||
|
lists:sublist(L, Index+1, M)
|
||||||
|
end,
|
||||||
|
if
|
||||||
|
L2 == [] ->
|
||||||
|
{L2, #rsm_out{count=Count}};
|
||||||
|
true ->
|
||||||
|
H = hd(L2),
|
||||||
|
NewIndex = get_room_pos(H, AllRooms),
|
||||||
|
T=lists:last(L2),
|
||||||
|
{F, _}=H#muc_online_room.name_host,
|
||||||
|
{Last, _}=T#muc_online_room.name_host,
|
||||||
|
{L2, #rsm_out{first=F, last=Last, count=Count, index=NewIndex}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Return the position of desired room in the list of rooms.
|
||||||
|
%% The room must exist in the list. The count starts in 0.
|
||||||
|
%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
|
||||||
|
get_room_pos(Desired, Rooms) ->
|
||||||
|
get_room_pos(Desired, Rooms, 0).
|
||||||
|
get_room_pos(Desired, [HeadRoom | _], HeadPosition)
|
||||||
|
when (Desired#muc_online_room.name_host ==
|
||||||
|
HeadRoom#muc_online_room.name_host) ->
|
||||||
|
HeadPosition;
|
||||||
|
get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
|
||||||
|
get_room_pos(Desired, Rooms, HeadPosition + 1).
|
||||||
|
|
||||||
flush() ->
|
flush() ->
|
||||||
receive
|
receive
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
process_iq_get/5,
|
process_iq_get/5,
|
||||||
get_user_list/3,
|
get_user_list/3,
|
||||||
check_packet/6,
|
check_packet/6,
|
||||||
|
remove_user/2,
|
||||||
updated_list/3]).
|
updated_list/3]).
|
||||||
|
|
||||||
-include_lib("exmpp/include/exmpp.hrl").
|
-include_lib("exmpp/include/exmpp.hrl").
|
||||||
@ -59,6 +60,8 @@ start(Host, Opts) ->
|
|||||||
?MODULE, check_packet, 50),
|
?MODULE, check_packet, 50),
|
||||||
ejabberd_hooks:add(privacy_updated_list, HostB,
|
ejabberd_hooks:add(privacy_updated_list, HostB,
|
||||||
?MODULE, updated_list, 50),
|
?MODULE, updated_list, 50),
|
||||||
|
ejabberd_hooks:add(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
||||||
?MODULE, process_iq, IQDisc).
|
?MODULE, process_iq, IQDisc).
|
||||||
|
|
||||||
@ -74,6 +77,8 @@ stop(Host) ->
|
|||||||
?MODULE, check_packet, 50),
|
?MODULE, check_packet, 50),
|
||||||
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
||||||
?MODULE, updated_list, 50),
|
?MODULE, updated_list, 50),
|
||||||
|
ejabberd_hooks:delete(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
||||||
|
|
||||||
process_iq(_From, _To, IQ_Rec) ->
|
process_iq(_From, _To, IQ_Rec) ->
|
||||||
@ -667,6 +672,16 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
remove_user(User, Server) ->
|
||||||
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:delete({privacy,
|
||||||
|
{LUser, LServer}})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
|
||||||
updated_list(_,
|
updated_list(_,
|
||||||
#userlist{name = OldName} = Old,
|
#userlist{name = OldName} = Old,
|
||||||
#userlist{name = NewName} = New) ->
|
#userlist{name = NewName} = New) ->
|
||||||
@ -678,7 +693,6 @@ updated_list(_,
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
update_table() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, privacy),
|
Fields = record_info(fields, privacy),
|
||||||
case mnesia:table_info(privacy, attributes) of
|
case mnesia:table_info(privacy, attributes) of
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
process_iq_get/5,
|
process_iq_get/5,
|
||||||
get_user_list/3,
|
get_user_list/3,
|
||||||
check_packet/6,
|
check_packet/6,
|
||||||
|
remove_user/2,
|
||||||
updated_list/3]).
|
updated_list/3]).
|
||||||
|
|
||||||
-include_lib("exmpp/include/exmpp.hrl").
|
-include_lib("exmpp/include/exmpp.hrl").
|
||||||
@ -55,6 +56,8 @@ start(Host, Opts) ->
|
|||||||
?MODULE, check_packet, 50),
|
?MODULE, check_packet, 50),
|
||||||
ejabberd_hooks:add(privacy_updated_list, HostB,
|
ejabberd_hooks:add(privacy_updated_list, HostB,
|
||||||
?MODULE, updated_list, 50),
|
?MODULE, updated_list, 50),
|
||||||
|
ejabberd_hooks:add(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
||||||
?MODULE, process_iq, IQDisc).
|
?MODULE, process_iq, IQDisc).
|
||||||
|
|
||||||
@ -70,6 +73,8 @@ stop(Host) ->
|
|||||||
?MODULE, check_packet, 50),
|
?MODULE, check_packet, 50),
|
||||||
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
||||||
?MODULE, updated_list, 50),
|
?MODULE, updated_list, 50),
|
||||||
|
ejabberd_hooks:delete(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
||||||
|
|
||||||
process_iq(_From, _To, IQ_Rec) ->
|
process_iq(_From, _To, IQ_Rec) ->
|
||||||
@ -666,6 +671,12 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
remove_user(User, Server) ->
|
||||||
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
|
sql_del_privacy_lists(LUser, LServer).
|
||||||
|
|
||||||
|
|
||||||
updated_list(_,
|
updated_list(_,
|
||||||
#userlist{name = OldName} = Old,
|
#userlist{name = OldName} = Old,
|
||||||
#userlist{name = NewName} = New) ->
|
#userlist{name = NewName} = New) ->
|
||||||
@ -880,3 +891,16 @@ sql_set_privacy_list(ID, RItems) ->
|
|||||||
") "
|
") "
|
||||||
"values ('", ID, "', ", Items, ");"])
|
"values ('", ID, "', ", Items, ");"])
|
||||||
end, RItems).
|
end, RItems).
|
||||||
|
|
||||||
|
sql_del_privacy_lists(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
Server = ejabberd_odbc:escape(LServer),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
["delete from privacy_list where username='", Username, "';"]),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
["delete from privacy_list_data where value='", Username++"@"++Server, "';"]),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
["delete from privacy_default_list where username='", Username, "';"]).
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
-author(__TO_BE_DEFINED__).
|
-author(__TO_BE_DEFINED__).
|
||||||
|
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("jlib.hrl").
|
|
||||||
|
|
||||||
-behaviour(gen_pubsub_node).
|
-behaviour(gen_pubsub_node).
|
||||||
|
|
||||||
|
@ -215,8 +215,6 @@ try_register(User, Server, Password, Source, Lang) ->
|
|||||||
true ->
|
true ->
|
||||||
case ejabberd_auth:try_register(User, Server, Password) of
|
case ejabberd_auth:try_register(User, Server, Password) of
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
ejabberd_hooks:run(user_registered, exmpp_jid:domain(JID),
|
|
||||||
[exmpp_jid:node(JID), exmpp_jid:domain(JID)]),
|
|
||||||
send_welcome_message(JID),
|
send_welcome_message(JID),
|
||||||
send_registration_notifications(JID),
|
send_registration_notifications(JID),
|
||||||
ok;
|
ok;
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
process_item/2,
|
process_item/2,
|
||||||
in_subscription/6,
|
in_subscription/6,
|
||||||
out_subscription/4,
|
out_subscription/4,
|
||||||
user_registered/2,
|
register_user/2,
|
||||||
|
remove_user/2,
|
||||||
list_groups/1,
|
list_groups/1,
|
||||||
create_group/2,
|
create_group/2,
|
||||||
create_group/3,
|
create_group/3,
|
||||||
@ -46,6 +47,7 @@
|
|||||||
set_group_opts/3,
|
set_group_opts/3,
|
||||||
get_group_users/2,
|
get_group_users/2,
|
||||||
get_group_explicit_users/2,
|
get_group_explicit_users/2,
|
||||||
|
is_user_in_group/3,
|
||||||
add_user_to_group/3,
|
add_user_to_group/3,
|
||||||
remove_user_from_group/3]).
|
remove_user_from_group/3]).
|
||||||
|
|
||||||
@ -85,8 +87,10 @@ start(Host, _Opts) ->
|
|||||||
?MODULE, get_jid_info, 70),
|
?MODULE, get_jid_info, 70),
|
||||||
ejabberd_hooks:add(roster_process_item, HostB,
|
ejabberd_hooks:add(roster_process_item, HostB,
|
||||||
?MODULE, process_item, 50),
|
?MODULE, process_item, 50),
|
||||||
ejabberd_hooks:add(user_registered, HostB,
|
ejabberd_hooks:add(register_user, HostB,
|
||||||
?MODULE, user_registered, 50).
|
?MODULE, register_user, 50),
|
||||||
|
ejabberd_hooks:add(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50).
|
||||||
%%ejabberd_hooks:add(remove_user, HostB,
|
%%ejabberd_hooks:add(remove_user, HostB,
|
||||||
%% ?MODULE, remove_user, 50),
|
%% ?MODULE, remove_user, 50),
|
||||||
|
|
||||||
@ -108,8 +112,10 @@ stop(Host) ->
|
|||||||
?MODULE, get_jid_info, 70),
|
?MODULE, get_jid_info, 70),
|
||||||
ejabberd_hooks:delete(roster_process_item, HostB,
|
ejabberd_hooks:delete(roster_process_item, HostB,
|
||||||
?MODULE, process_item, 50),
|
?MODULE, process_item, 50),
|
||||||
ejabberd_hooks:delete(user_registered, HostB,
|
ejabberd_hooks:delete(register_user, HostB,
|
||||||
?MODULE, user_registered, 50).
|
?MODULE, register_user, 50),
|
||||||
|
ejabberd_hooks:delete(remove_user, HostB,
|
||||||
|
?MODULE, remove_user, 50).
|
||||||
%%ejabberd_hooks:delete(remove_user, HostB,
|
%%ejabberd_hooks:delete(remove_user, HostB,
|
||||||
%% ?MODULE, remove_user, 50),
|
%% ?MODULE, remove_user, 50),
|
||||||
|
|
||||||
@ -443,6 +449,7 @@ get_group_users(_User, Host, Group, GroupOpts) ->
|
|||||||
[]
|
[]
|
||||||
end ++ get_group_explicit_users(Host, Group).
|
end ++ get_group_explicit_users(Host, Group).
|
||||||
|
|
||||||
|
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
|
||||||
get_group_explicit_users(Host, Group) ->
|
get_group_explicit_users(Host, Group) ->
|
||||||
Read = (catch mnesia:dirty_index_read(
|
Read = (catch mnesia:dirty_index_read(
|
||||||
sr_user,
|
sr_user,
|
||||||
@ -458,6 +465,7 @@ get_group_explicit_users(Host, Group) ->
|
|||||||
get_group_name(Host, Group) ->
|
get_group_name(Host, Group) ->
|
||||||
get_group_opt(Host, Group, name, Group).
|
get_group_opt(Host, Group, name, Group).
|
||||||
|
|
||||||
|
%% Get list of names of groups that have @all@ in the memberlist
|
||||||
get_special_users_groups(Host) ->
|
get_special_users_groups(Host) ->
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun(Group) ->
|
fun(Group) ->
|
||||||
@ -465,6 +473,8 @@ get_special_users_groups(Host) ->
|
|||||||
end,
|
end,
|
||||||
list_groups(Host)).
|
list_groups(Host)).
|
||||||
|
|
||||||
|
%% Given two lists of groupnames and their options,
|
||||||
|
%% return the list of displayed groups to the second list
|
||||||
displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
||||||
DisplayedGroups =
|
DisplayedGroups =
|
||||||
lists:usort(
|
lists:usort(
|
||||||
@ -476,6 +486,9 @@ displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
|||||||
[G || G <- DisplayedGroups,
|
[G || G <- DisplayedGroups,
|
||||||
not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
|
not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
|
||||||
|
|
||||||
|
%% Given a list of group names with options,
|
||||||
|
%% for those that have @all@ in memberlist,
|
||||||
|
%% get the list of groups displayed
|
||||||
get_special_displayed_groups(GroupsOpts) ->
|
get_special_displayed_groups(GroupsOpts) ->
|
||||||
Groups = lists:filter(
|
Groups = lists:filter(
|
||||||
fun({_Group, Opts}) ->
|
fun({_Group, Opts}) ->
|
||||||
@ -483,6 +496,9 @@ get_special_displayed_groups(GroupsOpts) ->
|
|||||||
end, GroupsOpts),
|
end, GroupsOpts),
|
||||||
displayed_groups(GroupsOpts, Groups).
|
displayed_groups(GroupsOpts, Groups).
|
||||||
|
|
||||||
|
%% Given a username and server, and a list of group names with options,
|
||||||
|
%% for the list of groups of that server that user is member
|
||||||
|
%% get the list of groups displayed
|
||||||
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||||
Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
|
Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
|
||||||
Rs when is_list(Rs) ->
|
Rs when is_list(Rs) ->
|
||||||
@ -493,6 +509,7 @@ get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
|||||||
end,
|
end,
|
||||||
displayed_groups(GroupsOpts, Groups).
|
displayed_groups(GroupsOpts, Groups).
|
||||||
|
|
||||||
|
%% @doc Get the list of groups that are displayed to this user
|
||||||
get_user_displayed_groups(US) ->
|
get_user_displayed_groups(US) ->
|
||||||
Host = element(2, US),
|
Host = element(2, US),
|
||||||
DisplayedGroups1 =
|
DisplayedGroups1 =
|
||||||
@ -515,22 +532,60 @@ is_user_in_group({_U, S} = US, Group, Host) ->
|
|||||||
_ -> true
|
_ -> true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
||||||
add_user_to_group(Host, US, Group) ->
|
add_user_to_group(Host, US, Group) ->
|
||||||
|
{LUser, LServer} = US,
|
||||||
|
%% Push this new user to members of groups where this group is displayed
|
||||||
|
push_user_to_displayed(LUser, LServer, Group, both),
|
||||||
|
%% Push members of groups that are displayed to this group
|
||||||
|
push_displayed_to_user(LUser, LServer, Group, Host, both),
|
||||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(R)
|
mnesia:write(R)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||||
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
|
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||||
|
DisplayedGroups = proplists:get_value(displayed_groups, GroupOpts, []),
|
||||||
|
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
|
||||||
|
|
||||||
remove_user_from_group(Host, US, Group) ->
|
remove_user_from_group(Host, US, Group) ->
|
||||||
GroupHost = {Group, Host},
|
GroupHost = {Group, Host},
|
||||||
R = #sr_user{us = US, group_host = GroupHost},
|
R = #sr_user{us = US, group_host = GroupHost},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete_object(R)
|
mnesia:delete_object(R)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
Result = mnesia:transaction(F),
|
||||||
|
{LUser, LServer} = US,
|
||||||
|
%% Push removal of the old user to members of groups where the group that this user was members was displayed
|
||||||
|
push_user_to_displayed(LUser, LServer, Group, remove),
|
||||||
|
%% Push removal of members of groups that where displayed to the group which this user has left
|
||||||
|
push_displayed_to_user(LUser, LServer, Group, Host, remove),
|
||||||
|
Result.
|
||||||
|
|
||||||
user_registered(User, Server) ->
|
push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||||
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
|
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||||
|
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||||
|
Members = get_group_users(Host, Group),
|
||||||
|
lists:foreach(
|
||||||
|
fun({U, S}) ->
|
||||||
|
push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
|
||||||
|
end, Members).
|
||||||
|
|
||||||
|
register_user(User, Server) ->
|
||||||
|
%% Get list of groups where this user is member
|
||||||
|
Groups = get_user_groups({User, Server}),
|
||||||
|
%% Push this user to members of groups where is displayed a group which this user is member
|
||||||
|
[push_user_to_displayed(User, Server, Group, both) || Group <- Groups].
|
||||||
|
|
||||||
|
remove_user(User, Server) ->
|
||||||
|
push_user_to_members(User, Server, remove).
|
||||||
|
|
||||||
|
push_user_to_members(User, Server, Subscription) ->
|
||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
@ -543,14 +598,7 @@ user_registered(User, Server) ->
|
|||||||
GroupName = proplists:get_value(name, GroupOpts, Group),
|
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({U, S}) ->
|
fun({U, S}) ->
|
||||||
Item = #roster{usj = {U, S, {LUser, LServer, undefined}},
|
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
|
||||||
us = {U, S},
|
|
||||||
jid = {LUser, LServer, undefined},
|
|
||||||
name = "",
|
|
||||||
subscription = both,
|
|
||||||
ask = none,
|
|
||||||
groups = [GroupName]},
|
|
||||||
push_item(U, S, exmpp_jid:make_bare_jid(S), Item)
|
|
||||||
end, get_group_users(LUser, LServer, Group, GroupOpts))
|
end, get_group_users(LUser, LServer, Group, GroupOpts))
|
||||||
end, lists:usort(SpecialGroups++UserGroups))
|
end, lists:usort(SpecialGroups++UserGroups))
|
||||||
catch
|
catch
|
||||||
@ -558,6 +606,27 @@ user_registered(User, Server) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
push_user_to_displayed(LUser, LServer, Group, Subscription) ->
|
||||||
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
|
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||||
|
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||||
|
DisplayedToGroupsOpts = displayed_to_groups(Group, LServer),
|
||||||
|
[push_user_to_group(LUser, LServer, GroupD, GroupName, Subscription) || {GroupD, _Opts} <- DisplayedToGroupsOpts].
|
||||||
|
|
||||||
|
push_user_to_group(LUser, LServer, Group, GroupName, Subscription) ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({U, S}) ->
|
||||||
|
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
|
||||||
|
end, get_group_users(LServer, Group)).
|
||||||
|
|
||||||
|
%% Get list of groups to which this group is displayed
|
||||||
|
displayed_to_groups(GroupName, LServer) ->
|
||||||
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
|
lists:filter(
|
||||||
|
fun({_Group, Opts}) ->
|
||||||
|
lists:member(GroupName, proplists:get_value(displayed_groups, Opts, []))
|
||||||
|
end, GroupsOpts).
|
||||||
|
|
||||||
push_item(_User, _Server, _From, none) ->
|
push_item(_User, _Server, _From, none) ->
|
||||||
ok;
|
ok;
|
||||||
push_item(User, Server, From, Item) ->
|
push_item(User, Server, From, Item) ->
|
||||||
@ -580,6 +649,16 @@ push_item(User, Server, From, Item) ->
|
|||||||
ejabberd_router:route(JID, JID, Stanza)
|
ejabberd_router:route(JID, JID, Stanza)
|
||||||
end, ejabberd_sm:get_user_resources(list_to_binary(User), list_to_binary(Server))).
|
end, ejabberd_sm:get_user_resources(list_to_binary(User), list_to_binary(Server))).
|
||||||
|
|
||||||
|
push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) ->
|
||||||
|
Item = #roster{usj = {User, Server, {ContactU, ContactS, ""}},
|
||||||
|
us = {User, Server},
|
||||||
|
jid = {ContactU, ContactS, ""},
|
||||||
|
name = "",
|
||||||
|
subscription = Subscription,
|
||||||
|
ask = none,
|
||||||
|
groups = [GroupName]},
|
||||||
|
push_item(User, Server, jlib:make_jid("", Server, ""), Item).
|
||||||
|
|
||||||
item_to_xml(Item) ->
|
item_to_xml(Item) ->
|
||||||
{U, S, R} = Item#roster.jid,
|
{U, S, R} = Item#roster.jid,
|
||||||
Attrs1 = exmpp_xml:set_attribute_in_list([],
|
Attrs1 = exmpp_xml:set_attribute_in_list([],
|
||||||
|
@ -376,7 +376,7 @@ get_private_data(LServer, Username, LXMLNS) ->
|
|||||||
"namespace='", LXMLNS, "';"]).
|
"namespace='", LXMLNS, "';"]).
|
||||||
|
|
||||||
del_user_private_storage(LServer, Username) ->
|
del_user_private_storage(LServer, Username) ->
|
||||||
ejabberd_odbc:sql_transaction(
|
ejabberd_odbc:sql_query(
|
||||||
LServer,
|
LServer,
|
||||||
["delete from private_storage where username='", Username, "';"]).
|
["delete from private_storage where username='", Username, "';"]).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user