Compare commits

...

181 Commits

Author SHA1 Message Date
Christophe Romain 5935b4e104 improve readability of links on github page 2014-12-15 10:52:47 +01:00
Christophe Romain d1f09a29b9 improve README adding feature list 2014-12-15 10:26:57 +01:00
Evgeny Khramtsov df88d9f2e5 Merge pull request #377 from weiss/new-timestamps
Add timestamps to stanzas resent from stream management queue
2014-12-13 09:42:30 +03:00
Holger Weiss 466278fde1 Let jlib use "B" instead of "w" to format integers
As a small optimization, use io:format's "B" control sequence to format
integers.  We don't need to let Erlang figure out the data type if we
already know it.
2014-12-12 23:50:03 +01:00
Holger Weiss 0a19dac4fd Add fractions of seconds to <delay/> timestamps
Include fractions of a second with XEP-0203 <delay/> timestamps, as
specified in XEP-0082.

	Old timestamp: 2014-05-19T11:55:00Z
	New timestamp: 2014-05-19T11:55:00.123Z
2014-12-11 23:11:35 +01:00
Christophe Romain 7e6d310fe4 include static versions of deps in rebar.config/script (thanks to Holger Weiß)(EJAB-1730) 2014-12-11 15:21:58 +01:00
Evgeny Khramtsov ffe3ea8917 Merge pull request #374 from weiss/fix-muc-log-config
mod_muc_log: Fix configuration parsing
2014-12-08 22:56:20 +03:00
Holger Weiss 455039ae69 mod_muc_log: Fix configuration parsing
Fix mod_muc_log's parsing of the "file_permissions" option.

Resolves #373.
2014-12-08 15:51:06 +01:00
Holger Weiss a78a0a65fe Let CSI code add timestamp at later point in time
As a small optimization, make sure we won't add timestamps to presence
stanzas which end up being thrown away by the CSI code.
2014-12-07 16:27:51 +01:00
Holger Weiss ba8f38e2eb XEP-0198: Add timestamp to resent stanzas
When an unacknowledged stanza is resent from the Stream Management
queue, add a timestamp so that the receiving client can display the time
at which the stanza was originally sent.
2014-12-07 16:12:06 +01:00
Holger Weiss 9899935e42 Improve interface for adding timestamps
Provide a simpler interface for adding <delay/> and <x/> timestamps to
stanzas.  This also makes sure that only one <delay/> tag and one <x/>
tag is added to a given stanza.
2014-12-07 15:55:18 +01:00
Evgeny Khramtsov 865509757c Merge pull request #366 from weiss/remove-unused-field
Remove unused field from ejabberd_c2s #state
2014-11-26 02:42:00 +03:00
Holger Weiss 2cb16bc509 Remove unused field from c2s #state 2014-11-26 00:15:19 +01:00
Evgeny Khramtsov 00dfcc1e10 Merge pull request #350 from flygoast/master
Used current working home as base directory to config relative path.
2014-11-25 14:24:37 +03:00
Evgeny Khramtsov 4163626844 Merge pull request #365 from weiss/csi-config
Fix mod_client_state's configuration parsing
2014-11-25 14:21:18 +03:00
Evgeny Khramtsov f60c721f84 Merge pull request #364 from weiss/copy-normal-messages
Also carbon-copy messages of type "normal"
2014-11-25 14:21:06 +03:00
Evgeny Khramtsov e97e56d776 Merge pull request #363 from weiss/drop-pep-errors
Don't route PEP error messages to clients
2014-11-25 14:20:11 +03:00
Evgeny Khramtsov 6b916e7a04 Merge pull request #354 from weiss/pep-privacy
Respect privacy lists for incoming PEP messages
2014-11-25 14:20:01 +03:00
Evgeny Khramtsov 6279c3fd8d Merge pull request #352 from weiss/no-last-pep-duplicates
Don't duplicate last published PEP items
2014-11-25 14:19:53 +03:00
Holger Weiss 6900a41e7d Fix mod_client_state's configuration parsing
Don't log an "invalid value" message when "queue_presence" or
"drop_chat_states" is set to "false".
2014-11-25 12:05:09 +01:00
Holger Weiss a456482e2f Also carbon-copy messages of type "normal"
It makes no sense to restrict carbon-copying to "chat" messages.
XEP-0280 is expected to be updated accordingly.
2014-11-24 22:37:14 +01:00
Holger Weiss 30687c40ef Don't route PEP error messages to clients 2014-11-24 21:19:32 +01:00
Holger Weiss 16311b73c8 Add new hook: c2s_filter_packet_in
The c2s_filter_packet_in hook can be used to modify or drop incoming
packets before they are transmitted to the client.
2014-11-24 20:55:18 +01:00
badlop b85357d280 Merge pull request #223 from kaLaJengkinG/patch-1
Update id.msg
2014-11-24 11:49:26 +01:00
Evgeny Khramtsov 946b64e166 Merge pull request #361 from weiss/parse-ldap-dn-filter
Fix parsing of "ldap_dn_filter" option
2014-11-22 10:36:21 +03:00
Holger Weiss 46d035c142 Fix parsing of "ldap_dn_filter" option 2014-11-22 01:33:23 +01:00
Evgeny Khramtsov 982215d644 Merge pull request #360 from lbanders/master
Fix for ejabbed bug #359 - now strings are formatted correctly.
2014-11-20 13:45:27 +03:00
Leif Bredgaard Honore 5afa1f6ade Fix for ejabbed bug #359 - now strings are formatted correctly. 2014-11-20 14:21:51 +04:00
Holger Weiss c566b1d01e Respect privacy lists for incoming PEP messages 2014-11-18 01:25:12 +01:00
Holger Weiss 84c227e6ae Don't duplicate last published PEP items
When a contact becomes available, usually both the 'caps_update' hook
and the 'presence_probe_hook' are called.  For remote contacts, both
hooks triggered PEP notifications, so each item was sent twice.  Fix
this by ignoring the 'presence_probe_hook' for remote contacts.
2014-11-18 01:13:22 +01:00
Evgeny Khramtsov ab12270837 Merge pull request #351 from weiss/floating-muc-intervals
Support floating point message/presence intervals
2014-11-16 12:35:21 +03:00
Holger Weiss 3b96525550 Support floating point message/presence intervals
Let mod_muc support floating point values for "min_message_interval" and
"min_presence_interval", as documented in the guide.
2014-11-15 22:35:56 +01:00
Gu Feng 62ccf1cf0e Used current working home as base directory to config relative path.
When a config relative path specified, get_absolute_path would not
return an absolute path. The patch fixed it using current working
home as base directory.

Signed-off-by: Gu Feng <flygoast@126.com>
2014-11-16 00:09:52 +08:00
Evgeny Khramtsov d5ecd32cec Merge pull request #345 from weiss/last-pep-items
Fix sending of last published PEP items to newly-available resources
2014-11-14 21:27:24 +03:00
Evgeny Khramtsov e770d3174d Merge pull request #346 from weiss/fix-type
Cosmetic fix: Use correct type for initial c2s #state.user value
2014-11-14 21:22:46 +03:00
Holger Weiss 2446b66016 Use correct type for initial c2s #state.user value 2014-11-14 15:32:48 +01:00
Holger Weiss f69d1ca282 Send last PEP items to remote subscribers
When a remote subscriber becomes available, send him the last published
PEP items, as we do for local subscribers.

However, the current implementation depends on a running ejabberd_c2s
process of the publisher to send items to remote subscribers.  So, for
those, the behavior is always like it is for local subscribers when
"ignore_pep_from_offline" is set to "true".
2014-11-14 01:33:11 +01:00
Holger Weiss 830fdccd21 Don't broadcast last published PEP items
When a client becomes available, don't send the last published PEP items
of all his peers to all his other peers, but only to that client.
2014-11-14 01:03:26 +01:00
Holger Weiss 5cc30c3977 Move routing of last PEP items into new function
This doesn't change the behavior, but avoids some code duplication.
2014-11-14 00:16:13 +01:00
Holger Weiss 8efae1f05b ODBC: Sync last item notifications with mod_pubsub
A while back, mod_pubsub was modified to address EJAB-1456.  However,
the change was only partially applied to mod_pubsub_odbc.  This commit
adds the remaining part.
2014-11-14 00:02:59 +01:00
Evgeny Khramtsov de3e1c3508 Merge pull request #341 from sezuan/fix-kick-user-command
Fix kick_user command
2014-11-10 23:14:34 +03:00
Matthias Rieber 8184326eb9 Fix kick_user command 2014-11-10 20:53:00 +01:00
Evgeny Khramtsov f47a59de2f Merge pull request #340 from weiss/disable-mechanisms
New option: disable_sasl_mechanisms
2014-11-10 11:47:10 +03:00
Holger Weiss ee0ecd2419 New option: disable_sasl_mechanisms
The new "disable_sasl_mechanisms" option allows for restricting the list
of SASL mechanisms offered to the client.

Closes #339.
2014-11-10 01:10:04 +01:00
Evgeny Khramtsov 7138cc5633 Merge pull request #337 from weiss/fix-install-without-json
Fix "make install" without JSON support
2014-11-06 21:11:17 +03:00
Holger Weiss f95f22aea0 Fix "make install" without JSON support
Don't bail out during "make install" when ./configure was called without
"--enable-json".
2014-11-06 17:11:15 +01:00
Evgeny Khramtsov 25e5253f33 Merge pull request #335 from weiss/avoid-carbon-dups
Avoid duplicates of carbon copies
2014-11-05 23:17:45 +03:00
Holger Weiss 41dc1efde4 Avoid duplicates of carbon copies
When multiple resources have the same (highest) priority, the session
manager routes messages sent to their bare JID to each of these
resources.  When another resource has a lower priority but receives
carbon copies, make sure it won't receive multiple copies of such
messages.
2014-11-05 19:04:02 +01:00
Jerome Sautret 1d2efcc168 Add xref test to travis.
Conflicts:
	.travis.yml
2014-10-31 11:24:56 +01:00
Evgeniy Khramtsov dfb21e802e Fix race in CSI test case 2014-10-31 00:26:22 +03:00
Evgeniy Khramtsov 9a0b951855 Add tests for mod_vcard_xupdate 2014-10-30 23:57:15 +03:00
Christophe Romain 7819986ec0 Merge branch 'master' of github.com:processone/ejabberd 2014-10-30 17:44:04 +01:00
Christophe Romain 295681283a don't stop on error if jiffy.so symlink exists #309 2014-10-30 17:43:57 +01:00
Jerome Sautret 5b0d8b7776 Merge branch 'master' of github.com:processone/ejabberd 2014-10-30 17:19:37 +01:00
Jerome Sautret 1d2ef85b33 Clean up rebar script. 2014-10-30 16:51:01 +01:00
Christophe Romain b550f247e7 fix invalid path of jiffy.so on install #309 2014-10-30 15:47:33 +01:00
Jerome Sautret 565f064b15 Fix xref check when odbc driver is not used. 2014-10-30 15:21:42 +01:00
Jerome Sautret 7db4587eeb Ignore riak_object module call for xref check.
It's used in map-reduce function called from riak vm.
2014-10-30 15:21:35 +01:00
Jerome Sautret fad0d867fc Add xref Makefile target.
Conflicts:
	rebar.config.script
2014-10-30 15:21:25 +01:00
Evgeny Khramtsov 56dab7ddbe Merge pull request #328 from flygoast/master
Return an empty <vCard/> element in an IQ-result when no vCard exists.
2014-10-29 16:43:49 +03:00
Evgeniy Khramtsov 74b67fa0dc Add new option: store_empty_body 2014-10-27 14:18:09 +03:00
Evgeniy Khramtsov 067958d705 Merge branch 'master' of github.com:processone/ejabberd 2014-10-27 13:44:59 +03:00
Evgeniy Khramtsov dec1e1f67f Revert "fix mod_offline to store only chat messages with body xml element"
This reverts commit 436f0832c1.
2014-10-27 13:44:46 +03:00
Evgeny Khramtsov 76b9098a25 Merge pull request #330 from weiss/accept-newline
Accept trailing newline characters in Base64 strings
2014-10-25 21:17:56 +04:00
Holger Weiss 2399aba67d Accept trailing whitespace in Base64 strings 2014-10-25 02:05:02 +02:00
Gu Feng 94cdcd7b34 Return an empty <vCard/> element in an IQ-result when no vCard exists.
According to XEP-0054, if no vCard exists, the server MUST return a stanza
error (which SHOULD be <item-not-found/>) or an IQ-result containing an
empty <vCard/> element.

Signed-off-by: Gu Feng <flygoast@126.com>
2014-10-25 00:55:49 +08:00
Evgeniy Khramtsov bf33f74ef8 Get rid of a hyphen in VSN 2014-10-24 18:01:42 +04:00
Evgeny Khramtsov 8cf43cf750 Merge pull request #325 from weiss/auth-after-tls
Don't advertise authentication mechanisms too early
2014-10-23 14:56:15 +04:00
Holger Weiss 2d748115ee Don't advertise auth mechanisms too early
If "starttls_required: true" is specified for c2s connections,
authentication mechanisms shouldn't be offered before negotiating the
TLS connection.
2014-10-23 10:04:14 +02:00
Evgeny Khramtsov 0b22277b11 Merge pull request #321 from weiss/fix-http-request-record
Use 'request' record definition from header file
2014-10-21 12:12:55 +04:00
Evgeny Khramtsov c7d9b46b6f Merge pull request #324 from liudanking/master
fix odbc_keepalive_interval configuration bug
2014-10-21 11:03:16 +04:00
liudan d2edcf1288 fix odbc keepalive interval bug 2014-10-21 10:05:44 +08:00
Evgeniy Khramtsov 160c9d7698 Remove append_host_config from ejabberd.yml.example 2014-10-20 12:59:30 +04:00
Evgeny Khramtsov ecd35f7ba8 Merge pull request #322 from weiss/xep-0198
XEP-0198: Abort immediately on stanza queue overflow
2014-10-17 11:56:25 +04:00
Holger Weiss 0c24e18b5e XEP-0198: Abort immediately on queue overflow
Terminate the ejabberd_c2s process immediately once stanza queue
overflow is detected.  This makes sure the FSM won't process additional
stanzas before terminating if the recipient is flooded.
2014-10-17 01:35:30 +02:00
Holger Weiss 96d6aacede Use 'request' record definition from header file
This fixes a 'badrecord' crash in mod_http_fileserver.
2014-10-16 13:51:13 +02:00
Evgeny Khramtsov adaa067333 Merge pull request #317 from weiss/xep-0198
XEP-0198: Set #state.conn field on session resume
2014-10-13 20:38:15 +04:00
Evgeny Khramtsov 724a31fa13 Merge pull request #318 from weiss/remove-xmlrpc-dep
Remove "xmlrpc" dependency for "make rel"
2014-10-13 20:37:28 +04:00
Holger Weiss 1ccc0d8bcb XEP-0198: Set #state.conn field on session resume 2014-10-12 19:44:35 +02:00
Holger Weiss 3f3f64c217 Remove "xmlrpc" dependency for "make rel"
The "xmlrpc" library isn't used anymore.
2014-10-12 17:23:25 +02:00
Evgeny Khramtsov 97fa57c360 Merge pull request #316 from weiss/really-require-tls
Make sure "starttls_required" can't be bypassed
2014-10-12 11:05:49 +04:00
Holger Weiss 7bdc1151b1 Make sure "starttls_required" can't be bypassed
Don't allow clients to circumvent the "starttls_required" option by
enabling XMPP stream compression.
2014-10-12 02:08:08 +02:00
Evgeniy Khramtsov 4bbf16b21a Fix list unblocking when Riak is used as a backend 2014-10-10 11:38:13 +04:00
Evgeniy Khramtsov d87ca9fb7b Fix format of an XML-RPC response 2014-10-04 12:55:59 +04:00
Evgeniy Khramtsov 7b3209cc7f Switch to P1 implementation of XML-RPC 2014-10-04 12:49:33 +04:00
Evgeniy Khramtsov 1d782db84f Process XML-RPC requests via p1_xml and ejabberd_http 2014-10-04 12:49:12 +04:00
Evgeniy Khramtsov e109f352e3 Make directory creation more robust 2014-10-02 14:21:27 +04:00
Evgeny Khramtsov 6e63ee480e Merge pull request #310 from weiss/ignore-rel-dir
Let Git ignore the "rel/ejabberd" directory
2014-09-30 13:50:32 +04:00
Holger Weiss 90fb19797d Let Git ignore the "rel/ejabberd" directory 2014-09-30 11:35:58 +02:00
Evgeny Khramtsov 415936146b Merge pull request #303 from weiss/no-csi-if-unconfigured
Offer CSI stream feature only if mod_client_state is enabled
2014-09-26 13:16:28 +04:00
Holger Weiss 277e1dc3ff Offer CSI stream feature only if configured
Don't offer the CSI stream feature when mod_client_state isn't actually
configured to filter stanzas.  This makes sure clients won't send CSI
tags that end up being ignored.
2014-09-25 18:28:20 +02:00
Holger Weiss 56175fef1b Add new hook: c2s_post_auth_features
The c2s_post_auth_features hook can be used to extend the list of stream
features offered after authentication.
2014-09-25 18:15:33 +02:00
Evgeny Khramtsov ef89497d3f Merge pull request #307 from weiss/remove-invisible-presence
Remove invisible presence fields from c2s #state
2014-09-25 17:20:45 +04:00
Holger Weiss 7aec0337e1 Remove invisible presence fields from c2s #state
Invisible presence isn't supported anymore, so the corresponding
ejabberd_c2s #state fields were unused.
2014-09-25 00:08:56 +02:00
Evgeny Khramtsov e49cf604e9 Merge pull request #302 from weiss/ldap-deref-aliases
Rename deref_aliases back to ldap_deref_aliases
2014-09-23 13:54:22 +04:00
Holger Weiss 61c8836740 Rename deref_aliases back to ldap_deref_aliases
The "ldap_deref_aliases" option has accidentally been renamed to
"deref_aliases".  Revert that change (but accept both names for a
while), so that the option name now matches the documentation again.
2014-09-23 11:31:44 +02:00
Evgeny Khramtsov 57dec40007 Merge pull request #301 from Iperity/master
Fix init script
2014-09-18 01:17:04 +04:00
Nathan Bruning 29a841d8c7 Fix init script: use getent to allow ejabberd user from external authentication sources (LDAP) 2014-09-17 21:28:50 +02:00
Evgeny Khramtsov c18413c52b Merge pull request #300 from weiss/resend-if-offline
XEP-0198: Support "resend_on_timeout: if_offline"
2014-09-17 09:31:09 +04:00
Holger Weiss 0a9212583d XEP-0198: Support "resend_on_timeout: if_offline"
If "resend_on_timeout" is set to "if_offline", resend unacknowledged
stanzas only if no other resource is online when the session times out.
In other words, allow for sending them to offline storage, but nowhere
else.
2014-09-16 22:42:34 +02:00
Evgeny Khramtsov 19446967fa Merge pull request #299 from sjmackenzie/processone
added p1_utils to reltool.config resolving treap:empty() undefined error
2014-09-15 09:31:27 +04:00
stewart 8d9a9228d9 added p1_utils to reltool.config resolving treap:empty() undefined error 2014-09-15 04:06:07 +00:00
Evgeniy Khramtsov 72fd353988 Avoid generation of excessive records 2014-09-13 22:54:07 +04:00
Evgeny Khramtsov c90786527e Merge pull request #298 from weiss/csi
Add support for XEP-0352: Client State Indication (CSI)
2014-09-12 21:55:10 +04:00
Holger Weiss 1a320baad8 Add tests for Client State Indication support 2014-09-11 18:18:20 +02:00
Holger Weiss b8c98232b8 Support XEP-0352: Client State Indication 2014-09-11 17:44:29 +02:00
Evgeny Khramtsov f723c00762 Merge pull request #297 from weiss/remove-configure-flag
Travis CI: Remove unused configure flag
2014-09-11 19:35:50 +04:00
Holger Weiss 4d59f677a9 Travis CI: Remove unused configure flag
The --disable-http flag no longer exists.
2014-09-10 21:04:43 +02:00
Evgeny Khramtsov 7a48e30523 Merge pull request #296 from weiss/remove-unused-clause
XEP-0198: Remove unused function clause
2014-09-07 07:27:17 +04:00
Evgeny Khramtsov f0887e45b8 Merge pull request #295 from weiss/xep-0198
XEP-0198: Change state immediately when gen_tcp:send/2 returns failure
2014-09-07 07:27:07 +04:00
Holger Weiss 2ca563e328 XEP-0198: Remove unused function clause
In the 'wait_for_resume' state, #state.mgmt_pending_since is always
initialized.  fsm_next_state/2 takes care of that.
2014-09-06 20:39:38 +02:00
Holger Weiss 2e169167d4 XEP-0198: Change state on gen_tcp:send/2 failure
When Stream Management is enabled and a gen_tcp:send/2 call fails, go
into the 'wait_for_resume' state immediately.  This makes sure that
gen_tcp:send/2 won't be called again, which might avoid an Erlang issue
where gen_tcp:send/2 apparently hangs despite 'send_timeout' (and
'send_timeout_close') being set.
2014-09-06 20:34:32 +02:00
Holger Weiss 11b2921971 XEP-0198: Let fsm_next_state/2 check timeout value
Don't force the fsm_next_state/2 caller to check for 'mgmt_timeout = 0'.
2014-09-06 20:29:02 +02:00
Christophe Romain 646b445515 allow guide not to be compiled for install 2014-09-05 19:16:13 +04:00
Evgeniy Khramtsov 50d7046517 Test MUC nick registration 2014-09-03 21:30:44 +04:00
Evgeniy Khramtsov c3eaa29f70 Parse x:data in jabber:register 2014-09-03 21:30:27 +04:00
Evgeniy Khramtsov ac2ba399a9 Make sure x:data form possesses 'type' attribute 2014-09-03 21:28:55 +04:00
Evgeniy Khramtsov fda73c3d16 Fix privacy checks of presence probes 2014-09-01 16:39:02 +04:00
Evgeniy Khramtsov a1ce33ebf8 Automatically remove IPs from ban, add the documentation 2014-08-27 13:25:49 +04:00
Evgeniy Khramtsov 9be9949dab Remove useless -include() 2014-08-27 13:18:29 +04:00
Evgeniy Khramtsov 0f1d95a074 Ban the IP if there are too many failed authentications 2014-08-27 13:18:22 +04:00
Evgeniy Khramtsov 2430e6691b Add mod_fail2ban 2014-08-27 13:17:56 +04:00
Evgeniy Khramtsov bfd028beea Recompile the xmpp_codec using updated XML generator 2014-08-27 12:55:31 +04:00
Evgeny Khramtsov 2cb0f92fe6 Merge pull request #293 from weiss/fix-comment
Fix a comment in ejabberd_hooks
2014-08-27 12:48:58 +04:00
Evgeny Khramtsov 2ae7d0a122 Merge pull request #292 from weiss/fix-xep-reference
Fix an XEP reference in the guide
2014-08-27 12:48:34 +04:00
Holger Weiss f1ad6f017b Fix a comment in ejabberd_hooks 2014-08-26 01:04:15 +02:00
Holger Weiss c658984531 Fix an XEP reference in the guide 2014-08-26 01:01:11 +02:00
Evgeny Khramtsov 191eeed7c9 Merge pull request #287 from vesvalo/master
Fix return value of mod_shared_roster:delete_group.
2014-08-21 16:32:31 +04:00
vesvalo 01a3c1c2e1 Fix return value of mod_shared_roster:delete_group. Current one is not compatible with mod_admin_extra. 2014-08-21 15:13:43 +04:00
Evgeny Khramtsov 8e3a49d369 Merge pull request #286 from vesvalo/fix_pep_odbc_publishing
fix 404 on pep first publishing with odbc
2014-08-21 13:29:55 +04:00
vesvalo c48b7f272b fix 404 on pep first publishing with odbc 2014-08-21 11:23:58 +04:00
Evgeny Khramtsov 4a9417c501 Merge pull request #284 from weiss/infinity-vs-unlimited
Guide: For consistency, use 'infinity' everywhere
2014-08-21 01:26:22 +04:00
Holger Weiss 72049e5323 Guide: For consistency, use 'infinity' everywhere 2014-08-20 20:43:38 +02:00
Evgeny Khramtsov 33e0bf1c19 Merge pull request #283 from weiss/allow-unlimited-ack-queue
Support "max_ack_queue: infinity"
2014-08-20 17:47:04 +04:00
Evgeny Khramtsov 5ed7f10153 Merge pull request #282 from weiss/fix-muc-option-docs
Fix MUC option docs: "infinity", not "infinite"
2014-08-20 17:44:06 +04:00
Holger Weiss 2802b6cee2 Allow for "max_ack_queue: infinity", as documented 2014-08-20 12:53:26 +02:00
Holger Weiss 44828c54fe Fix MUC option docs: "infinity", not "infinite" 2014-08-20 12:26:03 +02:00
Evgeny Khramtsov ae0d31a8c9 Merge pull request #280 from weiss/store-persistent-muc-on-init
Store peristent MUC room during creation
2014-08-19 13:28:15 +04:00
Holger Weiss 7274dafe10 Store persistent MUC room during creation
Make sure persistent rooms are stored to the database.  Without this
change, a room got lost if the 'persistent' flag was handed over to
mod_muc:create_room/5 and the server was then restartet before any
activity took place in that room.
2014-08-19 11:12:51 +02:00
Evgeny Khramtsov bc2e26fecd Merge pull request #277 from weiss/xep-0198
XEP-0198: Create shorter session resume IDs
2014-08-15 19:15:32 +04:00
Holger Weiss 2d4c39cd54 XEP-0198: Create shorter resume IDs
Omit the user and server name from the 'previd' value.
2014-08-15 10:56:59 +02:00
Evgeny Khramtsov 9484b11383 Merge pull request #276 from weiss/xep-0198
XEP-0198: Don't crash if the resume ID is incorrect
2014-08-15 10:49:59 +04:00
Holger Weiss 848e1497d1 XEP-0198: Gracefully handle broken 'previd'
Produce a proper error message instead of crashing when the 'previd'
value of a <resume/> request has an unexpected format.
2014-08-15 01:54:41 +02:00
Holger Weiss 2daf95e93f XEP-0198: Gracefully handle wrong credentials
Produce a proper error message instead of crashing when the JID encoded
in the 'previd' value of a <resume/> request is different from the
authenticated JID.
2014-08-15 01:53:47 +02:00
Evgeny Khramtsov 1b1d9b5a73 Merge pull request #269 from Iperity/master
Fix migration of pubsub nodes. Was deleting and re-creating all nodes on...
2014-08-15 03:20:36 +04:00
Evgeny Khramtsov 5836eb5bc2 Merge pull request #268 from benlangfeld/fix/ldap_filter_dnattributes_new_ejabberd
Set dnAttributes on when it's requested by a filter
2014-08-15 03:19:18 +04:00
Evgeniy Khramtsov 5c88f6423a Fix the deprecation warning to reflect YAML format 2014-08-12 14:26:15 +04:00
Evgeniy Khramtsov 56d61c2784 Do not call functions from ejabberd_riak directly 2014-08-12 14:25:54 +04:00
Evgeny Khramtsov 0917209711 Merge pull request #273 from gamenet/master
odb_queries patch return value of functions update and update_t in case of sql insert
2014-08-08 16:03:03 +04:00
vesvalo 8c22b154c9 Merge pull request #2 from vesvalo/master
fix mod_offline to store only chat messages with body xml element
2014-08-08 14:05:35 +04:00
vesvalo 436f0832c1 fix mod_offline to store only chat messages with body xml element 2014-08-08 13:57:27 +04:00
Nikolay Bondarenko 5d0de39127 Merge pull request #1 from vesvalo/master
Fix odbc update_t and update insert case return value
2014-08-08 13:39:44 +04:00
vesvalo 92f89e3d45 Fix odbc update_t and update insert case return value 2014-08-08 13:26:16 +04:00
Evgeny Khramtsov f91caf7108 Merge pull request #270 from weiss/log-s2s-in-auth
Log authentication method for incoming s2s connections
2014-08-05 16:51:51 +04:00
Holger Weiss 38c016a041 Log auth method for incoming s2s connections
Generate an [info] message that logs whether an incoming s2s connection
is authenticated using the SASL EXTERNAL mechanism or via Server
Dialback.  While at it, also mention whether TLS is enabled.
2014-08-05 14:10:32 +02:00
Nathan Bruning 4f63cb21c2 Fix migration of pubsub nodes. Was deleting and re-creating all nodes on each startup. 2014-08-03 21:03:16 +02:00
Evgeny Khramtsov 2e70c59471 Merge pull request #267 from weiss/replace-echo-calls
doc/Makefile: Replace non-portable echo(1) calls
2014-08-01 19:25:01 +04:00
Alexey Shchepin f00725dffb mod_offline now uses gen_server 2014-07-31 14:26:09 +04:00
Christophe Romain 4205108f30 typo fix on roster subscription (EJAB-1711) 2014-07-31 11:50:22 +02:00
Ben Langfeld 651de2ca8e Set dnAttributes on when it's requested by a filter 2014-07-29 15:22:54 -03:00
Holger Weiss e79290fb56 doc/Makefile: Replace non-portable echo(1) calls
The echo(1) behavior is system-dependent, the printf(1) behavior is not.
2014-07-28 22:54:09 +02:00
Evgeniy Khramtsov db3c469d4d Reorganize mod_announce test in order to avoid race 2014-07-28 13:42:50 +04:00
Evgeny Khramtsov 7d93463d31 Merge pull request #266 from weiss/remove-http
Remove "--enable-http" flag
2014-07-28 11:08:13 +04:00
Evgeny Khramtsov 5d79dff4f3 Merge pull request #265 from weiss/mention-starttls-required
Mention "starttls_required" option in sample configuration file
2014-07-28 11:06:59 +04:00
Evgeny Khramtsov 58fd56e6a2 Merge pull request #264 from weiss/su-without-p
ejabberdctl: Omit su(1)'s "-p" flag
2014-07-28 11:06:27 +04:00
Holger Weiss f1e6365ee1 Remove "--enable-http" flag
Specifying the "--enable-http" flag on the ./configure command line had
no effect.
2014-07-27 12:51:25 +02:00
Holger Weiss 4a02df8b6d Mention "starttls_required" option in ejabberd.yml
Closes #258.
2014-07-27 12:44:34 +02:00
Holger Weiss bee9ffd91e Apply minor improvement to ejabberd.yml comment 2014-07-27 11:54:30 +02:00
Holger Weiss 3e232952ea ejabberdctl: Omit su(1)'s "-p" flag
On Linux, su(1)'s "-p" flag makes sure the following environment
variables are preserved: $HOME, $SHELL, $USER, and $LOGNAME.  The flag
isn't portable, and since we don't set HOME=$SPOOLDIR anymore, there's
no reason to preserve these variables anyway.

Without "-p", we also don't need to set HOME=$INSTALLUSER_HOME, as su(1)
now does that for us.
2014-07-27 11:21:55 +02:00
Evgeniy Khramtsov c0001184fd Merge branch 'master' of github.com:processone/ejabberd 2014-07-27 13:06:46 +04:00
Evgeniy Khramtsov abeaac1c11 Add tests for mod_announce 2014-07-27 13:06:20 +04:00
Evgeny Khramtsov 6427d9398a Merge pull request #263 from weiss/fix-ejabberdctl-issues
Fix ejabberdctl issues
2014-07-24 20:56:32 +04:00
Evgeny Khramtsov 677b358a9a Merge pull request #262 from weiss/update-gitignore
Let Git ignore the "configure" script
2014-07-24 20:55:50 +04:00
Holger Weiss b997c4325a Let Git ignore the "configure" script 2014-07-24 17:50:43 +02:00
Holger Weiss 9c279f2e06 ejabberdctl: Remove outdated comment
The home directory is no longer set to $SPOOL_DIR.
2014-07-24 17:20:09 +02:00
Holger Weiss 46f01b962a ejabberdctl: Create home directory as root
If the $INSTALLUSER is not root, he will usually not have the necessary
permissions to create his home directory.
2014-07-24 17:16:47 +02:00
Evgeny Khramtsov 9db39a5e4c Merge pull request #261 from weiss/no-bash
doc/Makefile: Don't insist on using /bin/bash
2014-07-24 17:44:06 +04:00
Holger Weiss 43000d9ce4 ejabberdctl: Use $INSTALLUSER's home directory
Make sure ejabberdctl uses the $INSTALLUSER's .erlang.cookie file even
if the script was executed by root.
2014-07-24 15:25:28 +02:00
Holger Weiss 33368b7e5c doc/Makefile: Don't insist on using /bin/bash
Fix "make doc" for systems that don't have /bin/bash.  There's no
bash-specific code in doc/Makefile anymore.
2014-07-24 15:15:00 +02:00
Evgeniy Khramtsov a087af7060 Re-generate the XMPP codec using updated xml_gen 2014-07-24 10:35:17 +04:00
bLaDe 48600ae71d Update id.msg 2014-05-24 17:53:39 +07:00
54 changed files with 12347 additions and 8333 deletions
+2
View File
@@ -7,6 +7,7 @@
/Makefile
/config.log
/config.status
/configure
/aclocal.m4
/contrib/extract_translations/extract_translations.beam
/*.cache
@@ -28,6 +29,7 @@
/ejabberd.init
/ejabberdctl.example
/include/XmppAddr.hrl
/rel/ejabberd/
/src/XmppAddr.asn1db
/src/XmppAddr.erl
/src/ejabberd.app.src
+2 -2
View File
@@ -23,8 +23,8 @@ before_script:
script:
- ./autogen.sh
- ./configure --enable-all --disable-http --disable-odbc
- make
- ./configure --enable-all --disable-odbc
- make xref
- ERL_LIBS=$PWD make test
- grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' logs/raw.log
+12 -3
View File
@@ -88,6 +88,10 @@ update:
rm -rf deps/.built
$(REBAR) update-deps && :> deps/.got
xref: all
$(REBAR) skip_deps=true xref
translations:
contrib/extract_translations/prepare-translation.sh -updateall
@@ -157,6 +161,7 @@ install: all
# Binary system libraries
$(INSTALL) -d $(SODIR)
$(INSTALL) -m 644 $(DLLs) $(SODIR)
-[ -f $(SODIR)/jiffy.so ] && (cd $(PRIVDIR); ln -s lib/jiffy.so; true)
#
# Translated strings
$(INSTALL) -d $(MSGSDIR)
@@ -180,12 +185,16 @@ install: all
#
# Documentation
$(INSTALL) -d $(DOCDIR)
$(INSTALL) -m 644 doc/dev.html $(DOCDIR)
$(INSTALL) -m 644 doc/guide.html $(DOCDIR)
$(INSTALL) -m 644 doc/*.png $(DOCDIR)
[ -f doc/dev.html ] \
&& $(INSTALL) -m 644 doc/dev.html $(DOCDIR) \
|| echo "No doc/dev.html was built"
[ -f doc/guide.html ] \
&& $(INSTALL) -m 644 doc/guide.html $(DOCDIR) \
|| echo "No doc/guide.html was built"
[ -f doc/guide.pdf ] \
&& $(INSTALL) -m 644 doc/guide.pdf $(DOCDIR) \
|| echo "No doc/guide.pdf was built"
$(INSTALL) -m 644 doc/*.png $(DOCDIR)
$(INSTALL) -m 644 COPYING $(DOCDIR)
uninstall: uninstall-binary
+100 -7
View File
@@ -1,8 +1,92 @@
ejabberd - High-Performance Enterprise Instant Messaging Server
---------------------------------------------------------------
Ejabberd Community Edition, by ProcessOne
-----------------------------------------
Quickstart guide
================
ejabberd is a distributed, fault-tolerant technology that allows the creation
of large-scale instant messaging applications.
The server can reliably support thousands of simultaneous users on a single
node and has been designed to provide exceptional standards of fault
tolerance.
As an open source technology, based on industry-standards, ejabberd can be
used to build bespoke solutions very cost effectively.
Key Features:
=============
- Cross-platform: ejabberd runs under Microsoft Windows and Unix derived
systems such as Linux, FreeBSD and NetBSD.
- Distributed: You can run ejabberd on a cluster of machines and all of them
will serve the same Jabber domain(s). When you need more capacity you can
simply add a new cheap node to your cluster. Accordingly, you do not need to
buy an expensive high-end machine to support tens of thousands concurrent
users.
- Fault-tolerant: You can deploy an ejabberd cluster so that all the
information required for a properly working service will be replicated
permanently on all nodes. This means that if one of the nodes crashes, the
others will continue working without disruption. In addition, nodes also can
be added or replaced on the fly.
- Administrator Friendly: ejabberd is built on top of the Open Source
Erlang. As a result you do not need to install an external database, an
external web server, amongst others because everything is already included,
and ready to run out of the box. Other administrator benefits include:
Comprehensive documentation.
Straightforward installers for Linux, Mac OS X.
Web Administration.
Shared Roster Groups.
Command line administration tool.
Can integrate with existing authentication mechanisms.
Capability to send announce messages._
- Internationalized: ejabberd leads in internationalization. Hence it is
very well suited in a globalized world. Related features are:
Translated to 25 languages.
Support for IDNA._
- Open Standards: ejabberd is the first Open Source Jabber server claiming
to fully comply to the XMPP standard.
Fully XMPP compliant.
XML-based protocol.
Many protocols supported._
Additional Features:
====================
Moreover, ejabberd comes with a wide range of other state-of-the-art features:
- Modular
Load only the modules you want.
Extend ejabberd with your own custom modules.
- Security
SASL and STARTTLS for c2s and s2s connections.
STARTTLS and Dialback s2s connections.
Web Admin accessible via HTTPS secure access.
- Databases
Internal database for fast deployment (Mnesia).
Native MySQL support.
Native PostgreSQL support.
ODBC data storage support.
Microsoft SQL Server support.
- Authentication
Internal Authentication.
PAM, LDAP and ODBC.
External Authentication script.
- Others
Support for virtual hosting.
Compressing XML streams with Stream Compression (XEP-0138).
Statistics via Statistics Gathering (XEP-0039).
IPv6 support both for c2s and s2s connections.
Multi-User Chat module with support for clustering and HTML logging.
Users Directory based on users vCards.
Publish-Subscribe component with support for Personal Eventing.
Support for web clients: HTTP Polling and HTTP Binding (BOSH).
IRC transport.
Component support: interface with networks such as AIM, ICQ and MSN
Quickstart guide:
=================
0. Requirements
@@ -56,7 +140,16 @@ start and stop ejabberd. For example:
ejabberdctl start
For detailed information please refer to the [ejabberd Installation and
Operation Guide][1].
For detailed information please refer to the ejabberd Installation and
Operation Guide available online and in the doc directory of sources tarball.
Links:
======
- Guide: http://www.process-one.net/docs/ejabberd/guide_en.html
- Official site: https://www.process-one.net/en/ejabberd
- Community site: http://www.ejabberd.im
- Forum: http://www.process-one.net/en/forum
[1]: http://www.process-one.net/docs/ejabberd/guide_en.html
+4 -13
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.53)
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo community` | sed 's/-g.*//' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
REQUIRE_ERLANG_MIN="5.9.1 (Erlang/OTP R15B01)"
REQUIRE_ERLANG_MAX="9.0.0 (No Max)"
@@ -106,10 +106,10 @@ AC_ARG_ENABLE(mssql,
esac],[db_type=generic])
AC_ARG_ENABLE(all,
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-riak --enable-json --enable-iconv --enable-debug --enable-http --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])],
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-riak --enable-json --enable-iconv --enable-debug --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])],
[case "${enableval}" in
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true riak=true json=true iconv=true debug=true http=true lager=true tools=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false riak=false json=false iconv=false debug=false http=false lager=false tools=false ;;
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true riak=true json=true iconv=true debug=true lager=true tools=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false riak=false json=false iconv=false debug=false lager=false tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
esac],[])
@@ -201,14 +201,6 @@ AC_ARG_ENABLE(debug,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;;
esac],[if test "x$debug" = "x"; then debug=true; fi])
AC_ARG_ENABLE(http,
[AC_HELP_STRING([--enable-http], [build external HTTP libraries ('ibrowse' and 'lhttpc', default: no)])],
[case "${enableval}" in
yes) http=true ;;
no) http=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-http) ;;
esac],[if test "x$http" = "x"; then http=false; fi])
AC_ARG_ENABLE(lager,
[AC_HELP_STRING([--enable-lager], [enable lager support (default: yes)])],
[case "${enableval}" in
@@ -250,7 +242,6 @@ AC_SUBST(riak)
AC_SUBST(json)
AC_SUBST(iconv)
AC_SUBST(debug)
AC_SUBST(http)
AC_SUBST(lager)
AC_SUBST(tools)
+10 -10
View File
@@ -1,6 +1,6 @@
# $Id$
SHELL = /bin/bash
SHELL = /bin/sh
CONTRIBUTED_MODULES = ""
#ifeq ($(shell ls mod_http_bind.tex),mod_http_bind.tex)
@@ -11,16 +11,16 @@ CONTRIBUTED_MODULES = ""
all: release pdf html
release:
@echo "Notes for the releaser:"
@echo "* Do not forget to add a link to the release notes in guide.tex"
@echo "* Do not forget to update the version number in ebin/ejabberd.app!"
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
@echo "Press any key to continue"
@printf '%s\n' "Notes for the releaser:"
@printf '%s\n' "* Do not forget to add a link to the release notes in guide.tex"
@printf '%s\n' "* Do not forget to update the version number in ebin/ejabberd.app!"
@printf '%s\n' "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
@printf '%s\n' "Press any key to continue"
##@read foo
@echo "% ejabberd version (automatically generated)." > version.tex
@echo "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../ebin/ejabberd.app`"}" >> version.tex
@echo -n "% Contributed modules (automatically generated)." > contributed_modules.tex
@echo -e "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
@printf '%s\n' "% ejabberd version (automatically generated)." > version.tex
@printf '%s\n' "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../ebin/ejabberd.app`"}" >> version.tex
@printf '%s' "% Contributed modules (automatically generated)." > contributed_modules.tex
@printf '%b\n' "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
html: guide.html dev.html features.html
+80 -9
View File
@@ -66,12 +66,14 @@
\newcommand{\module}[1]{\texttt{#1}}
\newcommand{\modadhoc}{\module{mod\_adhoc}}
\newcommand{\modannounce}{\module{mod\_announce}}
\newcommand{\modclientstate}{\module{mod\_client\_state}}
\newcommand{\modblocking}{\module{mod\_blocking}}
\newcommand{\modcaps}{\module{mod\_caps}}
\newcommand{\modcarboncopy}{\module{mod\_carboncopy}}
\newcommand{\modconfigure}{\module{mod\_configure}}
\newcommand{\moddisco}{\module{mod\_disco}}
\newcommand{\modecho}{\module{mod\_echo}}
\newcommand{\modfailban}{\module{mod\_fail2ban}}
\newcommand{\modhttpbind}{\module{mod\_http\_bind}}
\newcommand{\modhttpfileserver}{\module{mod\_http\_fileserver}}
\newcommand{\modirc}{\module{mod\_irc}}
@@ -1020,7 +1022,7 @@ request_handlers:
/"a"/"b": mod_foo
/"http-bind": mod_http_bind
\end{verbatim}
\titem{resend\_on\_timeout: true|false}
\titem{resend\_on\_timeout: true|false|if\_offline}
If \term{stream\_management} is enabled and this option is set to
\term{true}, any stanzas that weren't acknowledged by the client
will be resent on session timeout. This behavior might often be
@@ -1028,8 +1030,12 @@ request_handlers:
circumstances. For example, a message that was sent to two resources
might get resent to one of them if the other one timed out.
Therefore, the default value for this option is \term{false}, which
tells ejabberd to generate an error message instead. The option can
be specified for \term{ejabberd\_c2s} listeners.
tells ejabberd to generate an error message instead. As an
alternative, the option may be set to \term{if\_offline}. In this
case, unacknowledged stanzas are resent only if no other resource is
online when the session times out. Otherwise, error messages are
generated. The option can be specified for \term{ejabberd\_c2s}
listeners.
\titem{resume\_timeout: Seconds}
This option configures the number of seconds until a session times
out if the connection is lost. During this period of time, a client
@@ -1062,7 +1068,7 @@ request_handlers:
You can define a certificate file for a specific domain using the global option \option{domain\_certfile}.
\titem{stream\_management: true|false}
Setting this option to \term{false} disables ejabberd's support for
\ind{protocols!XEP-0198: Stream Management}. It can be specified for
Stream Management (\xepref{0198}). It can be specified for
\term{ejabberd\_c2s} listeners. The default value is \term{true}.
\titem{timeout: Integer} \ind{options!timeout}
Timeout of the connections, expressed in milliseconds.
@@ -1447,6 +1453,11 @@ The FQDN is used to authenticate some clients that use the DIGEST-MD5 SASL mecha
The option syntax is:
\esyntax{fqdn: undefined|FqdnString|[FqdnString]}
The option \option{disable\_sasl\_mechanisms} specifies a list of SASL
mechanisms that should \emph{not} be offered to the client. The mechanisms can
be listed as lowercase or uppercase strings. The option syntax is:
\esyntax{disable\_sasl\_mechanisms: [Mechanism, ...]}
\makesubsubsection{internalauth}{Internal}
\ind{internal authentication}\ind{Mnesia}
@@ -2023,10 +2034,10 @@ The specific configurable options are:
\titem{turn\_max\_port: Integer}
Together with \option{turn\_min\_port} forms port range to allocate from.
The default is 65535. Implies \term{use\_turn}.
\titem{turn\_max\_allocations: Integer|unlimited}
\titem{turn\_max\_allocations: Integer|infinity}
Maximum number of TURN allocations available from the particular IP address.
The default value is 10. Implies \term{use\_turn}.
\titem{turn\_max\_permissions: Integer|unlimited}
\titem{turn\_max\_permissions: Integer|infinity}
Maximum number of TURN permissions available from the particular IP address.
The default value is 10. Implies \term{use\_turn}.
\titem{auth\_type: user|anonymous}
@@ -2780,9 +2791,11 @@ The following table lists all modules included in \ejabberd{}.
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
\hline \modcarboncopy{} & Message Carbons (\xepref{0280}) & \\
\hline \ahrefloc{modclientstate}{\modclientstate{}} & Filter stanzas for inactive clients & \\
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\
\hline \ahrefloc{modfail2ban}{\modfailban{}} & Bans IPs that show the malicious signs & \\
\hline \ahrefloc{modhttpbind}{\modhttpbind{}} & XMPP over Bosh service (HTTP Binding) & \\
\hline \ahrefloc{modhttpfileserver}{\modhttpfileserver{}} & Small HTTP file server & \\
\hline \ahrefloc{modirc}{\modirc{}} & IRC transport & \\
@@ -2999,6 +3012,38 @@ Note that \modannounce{} can be resource intensive on large
deployments as it can broadcast lot of messages. This module should be
disabled for instances of \ejabberd{} with hundreds of thousands users.
\makesubsection{modclientstate}{\modclientstate{}}
\ind{modules!\modclientstate{}}\ind{Client State Indication}
\ind{protocols!XEP-0352: Client State Indication}
This module allows for queueing or dropping certain types of stanzas
when a client indicates that the user is not actively using the client
at the moment (see \xepref{0352}). This can save bandwidth and
resources.
Options:
\begin{description}
\titem{drop\_chat\_states: true|false} \ind{options!drop\_chat\_states}
Drop most "standalone" Chat State Notifications (as defined in
\xepref{0085}) while a client indicates inactivity. The default value
is \term{false}.
\titem{queue\_presence: true|false} \ind{options!queue\_presence}
While a client is inactive, queue presence stanzas that indicate
(un)availability. The latest queued stanza of each contact is
delivered as soon as the client becomes active again. The default
value is \term{false}.
\end{description}
Example:
\begin{verbatim}
modules:
...
mod_client_state:
drop_chat_states: true
queue_presence: true
...
\end{verbatim}
\makesubsection{moddisco}{\moddisco{}}
\ind{modules!\moddisco{}}
\ind{protocols!XEP-0030: Service Discovery}
@@ -3117,6 +3162,30 @@ modules:
...
\end{verbatim}
\makesubsection{modfail2ban}{\modfailban{}}
\ind{modules!\modfailban{}}\ind{modfail2ban}
The module bans IPs that show the malicious signs. Currently only C2S authentication
failures are detected.
Available options:
\begin{description}
\titem{c2s\_auth\_ban\_lifetime: Seconds} The lifetime of the IP ban caused by too
many C2S authentication failures. The default is 3600, i.e. one hour.
\titem{c2s\_max\_auth\_failures: Integer} The number of C2S authentication failures to
trigger the IP ban. The default is 20.
\end{description}
Example:
\begin{verbatim}
modules:
...
mod_fail2ban:
c2s_auth_block_lifetime: 7200
c2s_max_auth_failures: 50
...
\end{verbatim}
\makesubsection{modhttpbind}{\modhttpbind{}}
\ind{modules!\modhttpbind{}}\ind{modhttpbind}
@@ -3415,15 +3484,15 @@ Module options:
\titem{max\_room\_id: Number} \ind{options!max\_room\_id}
This option defines the maximum number of characters that Room ID
can have when creating a new room.
The default value is to not limit: infinite.
The default value is to not limit: \term{infinity}.
\titem{max\_room\_name: Number} \ind{options!max\_room\_name}
This option defines the maximum number of characters that Room Name
can have when configuring the room.
The default value is to not limit: infinite.
The default value is to not limit: \term{infinity}.
\titem{max\_room\_desc: Number} \ind{options!max\_room\_desc}
This option defines the maximum number of characters that Room Description
can have when configuring the room.
The default value is to not limit: infinite.
The default value is to not limit: \term{infinity}.
\titem{min\_message\_interval: Number} \ind{options!min\_message\_interval}
This option defines the minimum interval between two messages send
by an occupant in seconds. This option is global and valid for all
@@ -3755,6 +3824,8 @@ online again. Thus it is very similar to how email works. Note that
The default value is \term{max\_user\_offline\_messages}.
Then you can define an access rule with a syntax similar to
\term{max\_user\_sessions} (see \ref{configmaxsessions}).
\titem{store\_empty\_body: true|false}\ind{options!store\_empty\_body} Whether or not
to store messages with empty \term{<body/>} element. The default value is \term{true}.
\end{description}
This example allows power users to have as much as 5000 offline messages,
+1 -1
View File
@@ -24,7 +24,7 @@ test -x "$CTL" || {
echo "ERROR: ejabberd not found: $DIR"
exit 1
}
grep ^"$USER": /etc/passwd >/dev/null || {
getent passwd "$USER" >/dev/null || {
echo "ERROR: System user not found: $USER"
exit 2
}
+10 -2
View File
@@ -108,11 +108,16 @@ listen:
##
## If TLS is compiled in and you installed a SSL
## certificate, specify the full path to the
## file and uncomment this line:
## file and uncomment these lines:
##
## certfile: "/path/to/ssl.pem"
## starttls: true
##
## To enforce TLS encryption for client connections,
## use this instead of the "starttls" option:
##
## starttls_required: true
##
## Custom OpenSSL options
##
## protocol_options:
@@ -553,6 +558,9 @@ modules:
mod_blocking: {} # requires mod_privacy
mod_caps: {}
mod_carboncopy: {}
mod_client_state:
drop_chat_states: true
queue_presence: false
mod_configure: {} # requires mod_adhoc
mod_disco: {}
## mod_echo: {}
@@ -638,7 +646,7 @@ modules:
##
## Enable modules with custom options in a specific virtual host
##
## append_host_config:
## host_config:
## "localhost":
## modules:
## mod_echo:
+6 -8
View File
@@ -22,7 +22,12 @@ if [ "$INSTALLUSER" != "" ] ; then
EXEC_CMD="false"
for GID in `id -G`; do
if [ $GID -eq 0 ] ; then
EXEC_CMD="su $INSTALLUSER -p -c"
INSTALLUSER_HOME=$(getent passwd "$INSTALLUSER" | cut -d: -f6)
if [ -n "$INSTALLUSER_HOME" ] && [ ! -d "$INSTALLUSER_HOME" ] ; then
mkdir -p "$INSTALLUSER_HOME"
chown "$INSTALLUSER" "$INSTALLUSER_HOME"
fi
EXEC_CMD="su $INSTALLUSER -c"
fi
done
if [ `id -g` -eq `id -g $INSTALLUSER` ] ; then
@@ -142,14 +147,7 @@ fi
[ -z "$date" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_date '$date'"
[ -z "$EJABBERD_OPTS" ] || EJABBERD_OPTS="-ejabberd ${EJABBERD_OPTS}"
# create the ejabberd home dir with the proper user if doesn't exist
# then change to that directory readable by INSTALLUSER to
# prevent "File operation error: eacces." messages
[ -d $HOME ] || $EXEC_CMD "mkdir -p $HOME"
[ -d $SPOOL_DIR ] || $EXEC_CMD "mkdir -p $SPOOL_DIR"
# then set SPOOL_DIR as ejabberd home directory by changing
# to that directory readable by INSTALLUSER to prevent
# "File operation error: eacces." messages
cd $SPOOL_DIR
# export global variables
+1
View File
@@ -31,5 +31,6 @@
host = <<"">> :: binary(),
port = 5280 :: inet:port_number(),
tp = http, % :: protocol(),
opts = [] :: list(),
headers = [] :: [{atom() | binary(), binary()}]}).
+1
View File
@@ -147,5 +147,6 @@
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
-define(NS_FORWARD, <<"urn:xmpp:forward:0">>).
-define(NS_CLIENT_STATE, <<"urn:xmpp:csi:0">>).
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
+1 -1
View File
@@ -120,7 +120,7 @@
{"has been kicked because of a system shutdown","telah dikick karena sistem shutdown"}.
{"has been kicked because the room has been changed to members-only","telah dikick karena ruangan telah diubah menjadi hanya untuk member"}.
{"has been kicked","telah dikick"}.
{" has set the subject to: ","telah menetapkan topik yaitu:"}.
{" has set the subject to: "," telah menetapkan topik yaitu: "}.
{"Host","Host"}.
{"If you don't see the CAPTCHA image here, visit the web page.","Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web."}.
{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Jika Anda ingin menentukan port yang berbeda, sandi, pengkodean untuk layanan IRC, isi daftar ini dengan nilai-nilai dalam format '{\"server irc \", \"encoding \", port, \"sandi \"}'. Secara default ini menggunakan layanan \"~s \" pengkodean, port ~p, kata sandi kosong."}.
+33 -4
View File
@@ -56,7 +56,7 @@ Deps = [{p1_cache_tab, ".*", {git, "git://github.com/processone/cache_tab"}},
{esip, ".*", {git, "git://github.com/processone/p1_sip"}},
{p1_stun, ".*", {git, "git://github.com/processone/stun"}},
{p1_yaml, ".*", {git, "git://github.com/processone/p1_yaml"}},
{xmlrpc, ".*", {git, "git://github.com/rds13/xmlrpc"}},
{ehyperloglog, ".*", {git, "https://github.com/vaxelfel/eHyperLogLog.git"}},
{p1_utils, ".*", {git, "git://github.com/processone/p1_utils"}}],
ConfigureCmd = fun(Pkg, Flags) ->
@@ -97,9 +97,6 @@ CfgDeps = lists:flatmap(
[{jiffy, ".*", {git, "git://github.com/davisp/jiffy"}}];
({iconv, true}) ->
[{p1_iconv, ".*", {git, "git://github.com/processone/eiconv"}}];
({http, true}) ->
[{ibrowse, ".*", {git, "git://github.com/cmullaparthi/ibrowse"}},
{lhttpc, ".*", {git, "git://github.com/esl/lhttpc"}}];
({lager, true}) ->
[{lager, ".*", {git, "git://github.com/basho/lager"}}];
({lager, false}) ->
@@ -119,6 +116,33 @@ CfgPostHooks = lists:flatmap(
[]
end, Cfg),
CfgXrefs = lists:flatmap(
fun({mysql, false}) ->
["(\".*mysql.*\":_/_)"];
({pgsql, false}) ->
["(\".*pgsql.*\":_/_)"];
({pam, false}) ->
["(\"epam\":_/_)"];
({riak, false}) ->
["(\"riak.*\":_/_)"];
({riak, true}) ->
% used in map-reduce function called from riak vm
["(\"riak_object\":_/_)"];
({json, false}) ->
["(\"jiffy\":_/_)"];
({zlib, false}) ->
["(\"ezlib\":_/_)"];
({http, false}) ->
["(\"lhttpc\":_/_)"];
({iconv, false}) ->
["(\"iconv\":_/_)"];
({odbc, false}) ->
["(\"odbc\":_/_)"];
(_) ->
[]
end, Cfg),
{ok, Cwd} = file:get_cwd(),
Config = [{erl_opts, Macros ++ HiPE ++ DebugInfo ++
@@ -127,6 +151,11 @@ Config = [{erl_opts, Macros ++ HiPE ++ DebugInfo ++
{keep_build_info, true},
{ct_extra_params, "-include "
++ filename:join([Cwd, "tools"])},
{xref_warnings, false},
{xref_checks, []},
{xref_queries,
[{"(XC - UC) || (XU - X - B - "
++ string:join(CfgXrefs, " - ") ++ ")", []}]},
{post_hooks, PostHooks ++ CfgPostHooks},
{deps, Deps ++ CfgDeps}],
%%io:format("ejabberd configuration:~n ~p~n", [Config]),
+1 -2
View File
@@ -28,7 +28,7 @@ ConfiguredOTPApps = lists:flatmap(
OTPApps = RequiredOTPApps ++ ConfiguredOTPApps,
DepRequiredApps = [p1_cache_tab, p1_tls, p1_stringprep, p1_xml, p1_yaml, xmlrpc],
DepRequiredApps = [p1_cache_tab, p1_tls, p1_stringprep, p1_xml, p1_yaml, p1_utils],
DepConfiguredApps = lists:flatmap(
fun({mysql, true}) -> [p1_mysql];
@@ -38,7 +38,6 @@ DepConfiguredApps = lists:flatmap(
({stun, true}) -> [p1_stun];
({json, true}) -> [jiffy];
({iconv, true}) -> [p1_iconv];
({http, true}) -> [ibrowse, lhttpc];
({lager, true}) -> [lager, goldrush];
({lager, false}) -> [p1_logger];
(_) -> []
+25 -3
View File
@@ -93,9 +93,15 @@ start() ->
).
register_mechanism(Mechanism, Module, PasswordType) ->
ets:insert(sasl_mechanism,
#sasl_mechanism{mechanism = Mechanism, module = Module,
password_type = PasswordType}).
case is_disabled(Mechanism) of
false ->
ets:insert(sasl_mechanism,
#sasl_mechanism{mechanism = Mechanism, module = Module,
password_type = PasswordType});
true ->
?DEBUG("SASL mechanism ~p is disabled", [Mechanism]),
true
end.
%%% TODO: use callbacks
%%-include("ejabberd.hrl").
@@ -215,3 +221,19 @@ filter_anonymous(Host, Mechs) ->
true -> Mechs;
false -> Mechs -- [<<"ANONYMOUS">>]
end.
-spec(is_disabled/1 ::
(
Mechanism :: mechanism())
-> boolean()
).
is_disabled(Mechanism) ->
Disabled = ejabberd_config:get_option(
disable_sasl_mechanisms,
fun(V) when is_list(V) ->
lists:map(fun(M) -> str:to_upper(M) end, V);
(V) ->
[str:to_upper(V)]
end, []),
lists:member(Mechanism, Disabled).
+1 -1
View File
@@ -387,7 +387,7 @@ parse_options(Host) ->
[{<<"%u">>, <<"*">>}]),
{DNFilter, DNFilterAttrs} =
eldap_utils:get_opt({ldap_dn_filter, Host}, [],
fun({DNF, DNFA}) ->
fun([{DNF, DNFA}]) ->
NewDNFA = case DNFA of
undefined ->
[];
+335 -186
View File
@@ -45,6 +45,7 @@
set_aux_field/3,
del_aux_field/2,
get_subscription/2,
send_filtered/5,
broadcast/4,
get_subscribed/1,
transform_listen_option/2]).
@@ -94,20 +95,20 @@
tls_options = [],
authenticated = false,
jid,
user = "", server = <<"">>, resource = <<"">>,
user = <<"">>, server = <<"">>, resource = <<"">>,
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
pres_a = ?SETS:new(),
pres_i = ?SETS:new(),
pres_last, pres_pri,
pres_last,
pres_timestamp,
pres_invis = false,
privacy_list = #userlist{},
conn = unknown,
auth_module = unknown,
ip,
aux_fields = [],
csi_state = active,
csi_queue = [],
mgmt_state,
mgmt_xmlns,
mgmt_queue,
@@ -247,6 +248,9 @@ get_subscription(LFrom, StateData) ->
true -> none
end.
send_filtered(FsmRef, Feature, From, To, Packet) ->
FsmRef ! {send_filtered, Feature, From, To, Packet}.
broadcast(FsmRef, Type, From, Packet) ->
FsmRef ! {broadcast, Type, From, Packet}.
@@ -307,41 +311,37 @@ init([{SockMod, Socket}, Opts]) ->
end,
MaxAckQueue = case proplists:get_value(max_ack_queue, Opts) of
Limit when is_integer(Limit), Limit > 0 -> Limit;
infinity -> infinity;
_ -> 500
end,
ResumeTimeout = case proplists:get_value(resume_timeout, Opts) of
Timeout when is_integer(Timeout), Timeout >= 0 -> Timeout;
_ -> 300
end,
ResendOnTimeout = proplists:get_bool(resend_on_timeout, Opts),
ResendOnTimeout = case proplists:get_value(resend_on_timeout, Opts) of
Resend when is_boolean(Resend) -> Resend;
if_offline -> if_offline;
_ -> false
end,
IP = peerip(SockMod, Socket),
%% Check if IP is blacklisted:
case is_ip_blacklisted(IP) of
true ->
?INFO_MSG("Connection attempt from blacklisted "
"IP: ~s (~w)",
[jlib:ip_to_list(IP), IP]),
{stop, normal};
false ->
Socket1 = if TLSEnabled andalso
SockMod /= ejabberd_frontend_socket ->
SockMod:starttls(Socket, TLSOpts);
true -> Socket
end,
SocketMonitor = SockMod:monitor(Socket1),
StateData = #state{socket = Socket1, sockmod = SockMod,
socket_monitor = SocketMonitor,
xml_socket = XMLSocket, zlib = Zlib, tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled, tls_options = TLSOpts,
sid = {now(), self()}, streamid = new_id(),
access = Access, shaper = Shaper, ip = IP,
mgmt_state = StreamMgmtState,
mgmt_max_queue = MaxAckQueue,
mgmt_timeout = ResumeTimeout,
mgmt_resend = ResendOnTimeout},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end.
Socket1 = if TLSEnabled andalso
SockMod /= ejabberd_frontend_socket ->
SockMod:starttls(Socket, TLSOpts);
true -> Socket
end,
SocketMonitor = SockMod:monitor(Socket1),
StateData = #state{socket = Socket1, sockmod = SockMod,
socket_monitor = SocketMonitor,
xml_socket = XMLSocket, zlib = Zlib, tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled, tls_options = TLSOpts,
sid = {now(), self()}, streamid = new_id(),
access = Access, shaper = Shaper, ip = IP,
mgmt_state = StreamMgmtState,
mgmt_max_queue = MaxAckQueue,
mgmt_timeout = ResumeTimeout,
mgmt_resend = ResendOnTimeout},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}.
%% Return list of all available resources of contacts,
get_subscribed(FsmRef) ->
@@ -365,27 +365,31 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
jlib:nameprep(xml:get_attr_s(<<"to">>, Attrs));
S -> S
end,
Lang = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
Lang1 when byte_size(Lang1) =< 35 ->
%% As stated in BCP47, 4.4.1:
%% Protocols or specifications that
%% specify limited buffer sizes for
%% language tags MUST allow for
%% language tags of at least 35 characters.
Lang1;
_ ->
%% Do not store long language tag to
%% avoid possible DoS/flood attacks
<<"">>
end,
IsBlacklistedIP = is_ip_blacklisted(StateData#state.ip, Lang),
case lists:member(Server, ?MYHOSTS) of
true ->
Lang = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
Lang1 when size(Lang1) =< 35 ->
%% As stated in BCP47, 4.4.1:
%% Protocols or specifications that
%% specify limited buffer sizes for
%% language tags MUST allow for
%% language tags of at least 35 characters.
Lang1;
_ ->
%% Do not store long language tag to
%% avoid possible DoS/flood attacks
<<"">>
end,
true when IsBlacklistedIP == false ->
change_shaper(StateData, jlib:make_jid(<<"">>, Server, <<"">>)),
case xml:get_attr_s(<<"version">>, Attrs) of
<<"1.0">> ->
send_header(StateData, Server, <<"1.0">>, DefaultLang),
case StateData#state.authenticated of
false ->
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
SASLState =
cyrsasl:server_new(
<<"jabber">>, Server, <<"">>, [],
@@ -401,12 +405,21 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
ejabberd_auth:check_password_with_authmodule(
U, Server, P, D, DG)
end),
Mechs = lists:map(fun (S) ->
#xmlel{name = <<"mechanism">>,
attrs = [],
children = [{xmlcdata, S}]}
end,
cyrsasl:listmech(Server)),
Mechs =
case TLSEnabled or not TLSRequired of
true ->
Ms = lists:map(fun (S) ->
#xmlel{name = <<"mechanism">>,
attrs = [],
children = [{xmlcdata, S}]}
end,
cyrsasl:listmech(Server)),
[#xmlel{name = <<"mechanisms">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
children = Ms}];
false ->
[]
end,
SockMod =
(StateData#state.sockmod):get_sockmod(
StateData#state.socket),
@@ -424,9 +437,6 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
_ ->
[]
end,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
TLSFeature =
case (TLS == true) andalso
(TLSEnabled == false) andalso
@@ -451,10 +461,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
#xmlel{name = <<"stream:features">>,
attrs = [],
children =
TLSFeature ++ CompressFeature ++
[#xmlel{name = <<"mechanisms">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
children = Mechs}]
TLSFeature ++ CompressFeature ++ Mechs
++
ejabberd_hooks:run_fold(c2s_stream_features,
Server, [], [Server])}),
@@ -491,6 +498,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
++
RosterVersioningFeature ++
StreamManagementFeature ++
ejabberd_hooks:run_fold(c2s_post_auth_features,
Server, [], [Server]) ++
ejabberd_hooks:run_fold(c2s_stream_features,
Server, [], [Server]),
send_element(StateData,
@@ -523,6 +532,15 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
lang = Lang})
end
end;
true ->
IP = StateData#state.ip,
{true, LogReason, ReasonT} = IsBlacklistedIP,
?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s",
[jlib:ip_to_list(IP), LogReason]),
send_header(StateData, Server, <<"">>, DefaultLang),
send_element(StateData, ?POLICY_VIOLATION_ERR(Lang, ReasonT)),
send_trailer(StateData),
{stop, normal, StateData};
_ ->
send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
send_element(StateData, ?HOST_UNKNOWN_ERR),
@@ -622,9 +640,13 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
P, D, DGen)
of
{true, AuthModule} ->
?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p",
[StateData#state.socket,
jlib:jid_to_string(JID), AuthModule]),
?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p from ~s",
[StateData#state.socket,
jlib:jid_to_string(JID), AuthModule,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[true, U, StateData#state.server,
StateData#state.ip]),
Conn = get_conn_type(StateData),
Info = [{ip, StateData#state.ip}, {conn, Conn},
{auth_module, AuthModule}],
@@ -659,12 +681,13 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
privacy_list = PrivList},
fsm_next_state(session_established, NewStateData);
_ ->
IP = peerip(StateData#state.sockmod,
StateData#state.socket),
?INFO_MSG("(~w) Failed legacy authentication for "
"~s from IP ~s",
[StateData#state.socket,
jlib:jid_to_string(JID), jlib:ip_to_list(IP)]),
?INFO_MSG("(~w) Failed legacy authentication for ~s from ~s",
[StateData#state.socket,
jlib:jid_to_string(JID),
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, U, StateData#state.server,
StateData#state.ip]),
Err = jlib:make_error_reply(El, ?ERR_NOT_AUTHORIZED),
send_element(StateData, Err),
fsm_next_state(wait_for_auth, StateData)
@@ -679,9 +702,13 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
fsm_next_state(wait_for_auth, StateData);
true ->
?INFO_MSG("(~w) Forbidden legacy authentication "
"for ~s",
"for ~s from ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
jlib:jid_to_string(JID),
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, U, StateData#state.server,
StateData#state.ip]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err),
fsm_next_state(wait_for_auth, StateData)
@@ -718,7 +745,7 @@ wait_for_feature_request({xmlstreamelement, El},
(StateData#state.sockmod):get_sockmod(StateData#state.socket),
case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
{?NS_SASL, <<"auth">>}
when not ((SockMod == gen_tcp) and TLSRequired) ->
when TLSEnabled or not TLSRequired ->
Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
case cyrsasl:server_start(StateData#state.sasl_state,
@@ -731,8 +758,12 @@ wait_for_feature_request({xmlstreamelement, El},
%AuthModule = xml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p",
[StateData#state.socket, U, AuthModule]),
"by ~p from ~s",
[StateData#state.socket, U, AuthModule,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[true, U, StateData#state.server,
StateData#state.ip]),
send_element(StateData,
#xmlel{name = <<"success">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
@@ -753,10 +784,13 @@ wait_for_feature_request({xmlstreamelement, El},
fsm_next_state(wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState});
{error, Error, Username} ->
IP = peerip(StateData#state.sockmod, StateData#state.socket),
?INFO_MSG("(~w) Failed authentication for ~s@~s from IP ~s",
[StateData#state.socket,
Username, StateData#state.server, jlib:ip_to_list(IP)]),
?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s",
[StateData#state.socket,
Username, StateData#state.server,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, Username, StateData#state.server,
StateData#state.ip]),
send_element(StateData,
#xmlel{name = <<"failure">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
@@ -832,7 +866,7 @@ wait_for_feature_request({xmlstreamelement, El},
end
end;
_ ->
if (SockMod == gen_tcp) and TLSRequired ->
if TLSRequired and not TLSEnabled ->
Lang = StateData#state.lang,
send_element(StateData,
?POLICY_VIOLATION_ERR(Lang,
@@ -877,8 +911,12 @@ wait_for_sasl_response({xmlstreamelement, El},
% AuthModule = xml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, <<>>),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p",
[StateData#state.socket, U, AuthModule]),
"by ~p from ~s",
[StateData#state.socket, U, AuthModule,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[true, U, StateData#state.server,
StateData#state.ip]),
send_element(StateData,
#xmlel{name = <<"success">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
@@ -896,8 +934,12 @@ wait_for_sasl_response({xmlstreamelement, El},
% AuthModule = xml:get_attr_s(auth_module, Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p",
[StateData#state.socket, U, AuthModule]),
"by ~p from ~s",
[StateData#state.socket, U, AuthModule,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[true, U, StateData#state.server,
StateData#state.ip]),
send_element(StateData,
#xmlel{name = <<"success">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
@@ -920,10 +962,13 @@ wait_for_sasl_response({xmlstreamelement, El},
fsm_next_state(wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState});
{error, Error, Username} ->
IP = peerip(StateData#state.sockmod, StateData#state.socket),
?INFO_MSG("(~w) Failed authentication for ~s@~s from IP ~s",
[StateData#state.socket,
Username, StateData#state.server, jlib:ip_to_list(IP)]),
?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s",
[StateData#state.socket,
Username, StateData#state.server,
jlib:ip_to_list(StateData#state.ip)]),
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, Username, StateData#state.server,
StateData#state.ip]),
send_element(StateData,
#xmlel{name = <<"failure">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
@@ -1136,6 +1181,17 @@ wait_for_session(closed, StateData) ->
session_established({xmlstreamelement, #xmlel{name = Name} = El}, StateData)
when ?IS_STREAM_MGMT_TAG(Name) ->
fsm_next_state(session_established, dispatch_stream_mgmt(El, StateData));
session_established({xmlstreamelement,
#xmlel{name = <<"active">>,
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}]}},
StateData) ->
NewStateData = csi_queue_flush(StateData),
fsm_next_state(session_established, NewStateData#state{csi_state = active});
session_established({xmlstreamelement,
#xmlel{name = <<"inactive">>,
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}]}},
StateData) ->
fsm_next_state(session_established, StateData#state{csi_state = inactive});
session_established({xmlstreamelement, El},
StateData) ->
FromJID = StateData#state.jid,
@@ -1167,9 +1223,7 @@ session_established({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
session_established(closed, StateData)
when StateData#state.mgmt_timeout > 0,
StateData#state.mgmt_state == active ->
session_established(closed, #state{mgmt_state = active} = StateData) ->
fsm_next_state(wait_for_resume, StateData);
session_established(closed, StateData) ->
{stop, normal, StateData}.
@@ -1640,12 +1694,23 @@ handle_info({route, From, To,
jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To), NewAttrs),
FixedPacket = #xmlel{name = Name, attrs = Attrs2, children = Els},
SentStateData = send_packet(NewState, FixedPacket),
ejabberd_hooks:run(user_receive_packet,
SentStateData#state.server,
[SentStateData#state.jid, From, To, FixedPacket]),
FinalState =
case ejabberd_hooks:run_fold(c2s_filter_packet_in,
NewState#state.server, FixedPacket,
[NewState#state.jid, From, To])
of
drop ->
NewState;
FinalPacket = #xmlel{} ->
SentState = send_packet(NewState, FinalPacket),
ejabberd_hooks:run(user_receive_packet,
SentState#state.server,
[SentState#state.jid, From, To,
FinalPacket]),
SentState
end,
ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
fsm_next_state(StateName, SentStateData);
fsm_next_state(StateName, FinalState);
true ->
ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
fsm_next_state(StateName, NewState)
@@ -1653,8 +1718,7 @@ handle_info({route, From, To,
handle_info({'DOWN', Monitor, _Type, _Object, _Info},
_StateName, StateData)
when Monitor == StateData#state.socket_monitor ->
if StateData#state.mgmt_timeout > 0,
StateData#state.mgmt_state == active orelse
if StateData#state.mgmt_state == active;
StateData#state.mgmt_state == pending ->
fsm_next_state(wait_for_resume, StateData);
true ->
@@ -1689,6 +1753,32 @@ handle_info({force_update_presence, LUser}, StateName,
_ -> StateData
end,
fsm_next_state(StateName, NewStateData);
handle_info({send_filtered, Feature, From, To, Packet}, StateName, StateData) ->
Drop = ejabberd_hooks:run_fold(c2s_filter_packet, StateData#state.server,
true, [StateData#state.server, StateData,
Feature, To, Packet]),
NewStateData = if Drop ->
?DEBUG("Dropping packet from ~p to ~p",
[jlib:jid_to_string(From),
jlib:jid_to_string(To)]),
StateData;
true ->
FinalPacket = jlib:replace_from_to(From, To, Packet),
case StateData#state.jid of
To ->
case privacy_check_packet(StateData, From, To,
FinalPacket, in) of
deny ->
StateData;
allow ->
send_stanza(StateData, FinalPacket)
end;
_ ->
ejabberd_router:route(From, To, FinalPacket),
StateData
end
end,
fsm_next_state(StateName, NewStateData);
handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
Recipients = ejabberd_hooks:run_fold(
c2s_broadcast_recipients, StateData#state.server,
@@ -1710,11 +1800,10 @@ handle_info(Info, StateName, StateData) ->
%% Purpose: Prepare the state to be printed on error log
%% Returns: State to print
%%----------------------------------------------------------------------
print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
print_state(State = #state{pres_t = T, pres_f = F, pres_a = A}) ->
State#state{pres_t = {pres_t, ?SETS:size(T)},
pres_f = {pres_f, ?SETS:size(F)},
pres_a = {pres_a, ?SETS:size(A)},
pres_i = {pres_i, ?SETS:size(I)}
pres_a = {pres_a, ?SETS:size(A)}
}.
%%----------------------------------------------------------------------
@@ -1750,8 +1839,6 @@ terminate(_Reason, StateName, StateData) ->
<<"Replaced by new connection">>),
presence_broadcast(StateData, From,
StateData#state.pres_a, Packet),
presence_broadcast(StateData, From,
StateData#state.pres_i, Packet),
handle_unacked_stanzas(StateData);
_ ->
?INFO_MSG("(~w) Close session for ~s",
@@ -1759,10 +1846,7 @@ terminate(_Reason, StateName, StateData) ->
jlib:jid_to_string(StateData#state.jid)]),
EmptySet = (?SETS):new(),
case StateData of
#state{pres_last = undefined,
pres_a = EmptySet,
pres_i = EmptySet,
pres_invis = false} ->
#state{pres_last = undefined, pres_a = EmptySet} ->
ejabberd_sm:close_session(StateData#state.sid,
StateData#state.user,
StateData#state.server,
@@ -1778,9 +1862,7 @@ terminate(_Reason, StateName, StateData) ->
StateData#state.resource,
<<"">>),
presence_broadcast(StateData, From,
StateData#state.pres_a, Packet),
presence_broadcast(StateData, From,
StateData#state.pres_i, Packet)
StateData#state.pres_a, Packet)
end,
handle_unacked_stanzas(StateData)
end,
@@ -1812,7 +1894,8 @@ send_text(StateData, Text) when StateData#state.mgmt_state == active ->
?DEBUG("Send XML on stream = ~p", [Text]),
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
{'EXIT', _} ->
(StateData#state.sockmod):close(StateData#state.socket);
(StateData#state.sockmod):close(StateData#state.socket),
error;
_ ->
ok
end;
@@ -1828,27 +1911,30 @@ send_element(StateData, El) when StateData#state.xml_socket ->
send_element(StateData, El) ->
send_text(StateData, xml:element_to_binary(El)).
send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive ->
csi_filter_stanza(StateData, Stanza);
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == pending ->
mgmt_queue_add(StateData, Stanza);
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == active ->
send_stanza_and_ack_req(StateData, Stanza),
mgmt_queue_add(StateData, Stanza);
NewStateData = case send_stanza_and_ack_req(StateData, Stanza) of
ok ->
StateData;
error ->
StateData#state{mgmt_state = pending}
end,
mgmt_queue_add(NewStateData, Stanza);
send_stanza(StateData, Stanza) ->
send_element(StateData, Stanza),
StateData.
send_packet(StateData, Packet) when StateData#state.mgmt_state == active;
StateData#state.mgmt_state == pending ->
send_packet(StateData, Packet) ->
case is_stanza(Packet) of
true ->
send_stanza(StateData, Packet);
false ->
send_element(StateData, Packet),
StateData
end;
send_packet(StateData, Stanza) ->
send_element(StateData, Stanza),
StateData.
end.
send_header(StateData, Server, Version, Lang)
when StateData#state.xml_socket ->
@@ -1957,28 +2043,15 @@ process_presence_probe(From, To, StateData) ->
undefined ->
ok;
_ ->
Cond1 = (not StateData#state.pres_invis)
andalso (?SETS:is_element(LFrom, StateData#state.pres_f)
orelse
((LFrom /= LBFrom) andalso
?SETS:is_element(LBFrom, StateData#state.pres_f)))
andalso (not
(?SETS:is_element(LFrom, StateData#state.pres_i)
orelse
((LFrom /= LBFrom) andalso
?SETS:is_element(LBFrom, StateData#state.pres_i)))),
Cond2 = StateData#state.pres_invis
andalso ?SETS:is_element(LFrom, StateData#state.pres_f)
andalso ?SETS:is_element(LFrom, StateData#state.pres_a),
Cond = ?SETS:is_element(LFrom, StateData#state.pres_f)
orelse
((LFrom /= LBFrom) andalso
?SETS:is_element(LBFrom, StateData#state.pres_f)),
if
Cond1 ->
Timestamp = StateData#state.pres_timestamp,
Packet = xml:append_subtags(
StateData#state.pres_last,
%% To is the one sending the presence (the target of the probe)
[jlib:timestamp_to_xml(Timestamp, utc, To, <<"">>),
%% TODO: Delete the next line once XEP-0091 is Obsolete
jlib:timestamp_to_xml(Timestamp)]),
Cond ->
%% To is the one sending the presence (the probe target)
Packet = jlib:add_delay_info(StateData#state.pres_last, To,
StateData#state.pres_timestamp),
case privacy_check_packet(StateData, To, From, Packet, out) of
deny ->
ok;
@@ -1993,11 +2066,6 @@ process_presence_probe(From, To, StateData) ->
ok
end
end;
Cond2 ->
ejabberd_router:route(To, From,
#xmlel{name = <<"presence">>,
attrs = [],
children = []});
true ->
ok
end
@@ -2035,12 +2103,11 @@ presence_update(From, Packet, StateData) ->
OldPresence -> get_priority_from_presence(OldPresence)
end,
NewPriority = get_priority_from_presence(Packet),
Timestamp = calendar:now_to_universal_time(now()),
update_priority(NewPriority, Packet, StateData),
FromUnavail = (StateData#state.pres_last == undefined),
?DEBUG("from unavail = ~p~n", [FromUnavail]),
NewStateData = StateData#state{pres_last = Packet,
pres_timestamp = Timestamp},
pres_timestamp = now()},
NewState = if FromUnavail ->
ejabberd_hooks:run(user_available_hook,
NewStateData#state.server,
@@ -2173,7 +2240,7 @@ presence_broadcast_first(From, StateData, Packet) ->
[],
StateData#state.pres_t),
PacketProbe = #xmlel{name = <<"presence">>, attrs = [{<<"type">>,<<"probe">>}], children = []},
JIDs2Probe = format_and_check_privacy(From, StateData, Packet, JIDsProbe, out),
JIDs2Probe = format_and_check_privacy(From, StateData, PacketProbe, JIDsProbe, out),
Server = StateData#state.server,
send_multiple(StateData, From, JIDs2Probe, PacketProbe),
{As, JIDs} =
@@ -2429,11 +2496,24 @@ fsm_next_state_gc(StateName, PackedStateData) ->
%% fsm_next_state: Generate the next_state FSM tuple with different
%% timeout, depending on the future state
fsm_next_state(session_established, #state{mgmt_max_queue = exceeded} =
StateData) ->
?WARNING_MSG("ACK queue too long, terminating session for ~s",
[jlib:jid_to_string(StateData#state.jid)]),
Err = ?SERRT_POLICY_VIOLATION(StateData#state.lang,
<<"Too many unacked stanzas">>),
send_element(StateData, Err),
send_trailer(StateData),
{stop, normal, StateData#state{mgmt_resend = false}};
fsm_next_state(session_established, #state{mgmt_state = pending} = StateData) ->
fsm_next_state(wait_for_resume, StateData);
fsm_next_state(session_established, StateData) ->
{next_state, session_established, StateData,
?C2S_HIBERNATE_TIMEOUT};
fsm_next_state(wait_for_resume, StateData)
when StateData#state.mgmt_state /= pending ->
fsm_next_state(wait_for_resume, #state{mgmt_timeout = 0} = StateData) ->
{stop, normal, StateData};
fsm_next_state(wait_for_resume, #state{mgmt_pending_since = undefined} =
StateData) ->
?INFO_MSG("Waiting for resumption of stream for ~s",
[jlib:jid_to_string(StateData#state.jid)]),
{next_state, wait_for_resume,
@@ -2451,11 +2531,6 @@ fsm_next_state(StateName, StateData) ->
fsm_reply(Reply, session_established, StateData) ->
{reply, Reply, session_established, StateData,
?C2S_HIBERNATE_TIMEOUT};
fsm_reply(Reply, wait_for_resume, #state{mgmt_pending_since = undefined} =
StateData) ->
{reply, Reply, wait_for_resume,
StateData#state{mgmt_pending_since = os:timestamp()},
StateData#state.mgmt_timeout};
fsm_reply(Reply, wait_for_resume, StateData) ->
Diff = timer:now_diff(os:timestamp(), StateData#state.mgmt_pending_since),
Timeout = max(StateData#state.mgmt_timeout - Diff div 1000, 1),
@@ -2464,9 +2539,9 @@ fsm_reply(Reply, StateName, StateData) ->
{reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
%% Used by c2s blacklist plugins
is_ip_blacklisted(undefined) -> false;
is_ip_blacklisted({IP, _Port}) ->
ejabberd_hooks:run_fold(check_bl_c2s, false, [IP]).
is_ip_blacklisted(undefined, _Lang) -> false;
is_ip_blacklisted({IP, _Port}, Lang) ->
ejabberd_hooks:run_fold(check_bl_c2s, false, [IP, Lang]).
%% Check from attributes
%% returns invalid-from|NewElement
@@ -2725,15 +2800,20 @@ handle_resume(StateData, Attrs) ->
{<<"h">>, AttrH},
{<<"previd">>, AttrId}],
children = []}),
SendFun = fun(_F, _T, El) -> send_element(NewState, El) end,
SendFun = fun(_F, _T, El, Time) ->
NewEl = add_resent_delay_info(NewState, El, Time),
send_element(NewState, NewEl)
end,
handle_unacked_stanzas(NewState, SendFun),
send_element(NewState,
#xmlel{name = <<"r">>,
attrs = [{<<"xmlns">>, AttrXmlns}],
children = []}),
FlushedState = csi_queue_flush(NewState),
NewStateData = FlushedState#state{csi_state = active},
?INFO_MSG("Resumed session for ~s",
[jlib:jid_to_string(NewState#state.jid)]),
{ok, NewState};
[jlib:jid_to_string(NewStateData#state.jid)]),
{ok, NewStateData};
{error, El, Msg} ->
send_element(StateData, El),
?INFO_MSG("Cannot resume session for ~s@~s: ~s",
@@ -2779,30 +2859,25 @@ mgmt_queue_add(StateData, El) ->
Num ->
Num + 1
end,
NewQueue = queue:in({NewNum, El}, StateData#state.mgmt_queue),
NewQueue = queue:in({NewNum, now(), El}, StateData#state.mgmt_queue),
NewState = StateData#state{mgmt_queue = NewQueue,
mgmt_stanzas_out = NewNum},
check_queue_length(NewState).
mgmt_queue_drop(StateData, NumHandled) ->
NewQueue = jlib:queue_drop_while(fun({N, _Stanza}) -> N =< NumHandled end,
NewQueue = jlib:queue_drop_while(fun({N, _T, _E}) -> N =< NumHandled end,
StateData#state.mgmt_queue),
StateData#state{mgmt_queue = NewQueue}.
check_queue_length(#state{mgmt_max_queue = Limit} = StateData)
when Limit == infinity;
Limit == unlimited ->
Limit == exceeded ->
StateData;
check_queue_length(#state{mgmt_queue = Queue,
mgmt_max_queue = Limit} = StateData) ->
case queue:len(Queue) > Limit of
true ->
?WARNING_MSG("ACK queue too long, terminating session for ~s",
[jlib:jid_to_string(StateData#state.jid)]),
Lang = StateData#state.lang,
Err = ?SERRT_POLICY_VIOLATION(Lang, <<"Too many unacked stanzas">>),
self() ! {kick, queue_overflow, Err},
StateData#state{mgmt_resend = false}; % Don't resend the flood!
StateData#state{mgmt_max_queue = exceeded};
false ->
StateData
end.
@@ -2818,12 +2893,12 @@ handle_unacked_stanzas(StateData, F)
?INFO_MSG("~B stanzas were not acknowledged by ~s",
[N, jlib:jid_to_string(StateData#state.jid)]),
lists:foreach(
fun({_, #xmlel{attrs = Attrs} = El}) ->
fun({_, Time, #xmlel{attrs = Attrs} = El}) ->
From_s = xml:get_attr_s(<<"from">>, Attrs),
From = jlib:string_to_jid(From_s),
To_s = xml:get_attr_s(<<"to">>, Attrs),
To = jlib:string_to_jid(To_s),
F(From, To, El)
F(From, To, El, Time)
end, queue:to_list(Queue))
end;
handle_unacked_stanzas(_StateData, _F) ->
@@ -2832,18 +2907,29 @@ handle_unacked_stanzas(_StateData, _F) ->
handle_unacked_stanzas(StateData)
when StateData#state.mgmt_state == active;
StateData#state.mgmt_state == pending ->
ReRoute = case StateData#state.mgmt_resend of
ResendOnTimeout =
case StateData#state.mgmt_resend of
Resend when is_boolean(Resend) ->
Resend;
if_offline ->
ejabberd_sm:get_user_resources(StateData#state.user,
StateData#state.server) == []
end,
ReRoute = case ResendOnTimeout of
true ->
fun ejabberd_router:route/3;
fun(From, To, El, Time) ->
NewEl = add_resent_delay_info(StateData, El, Time),
ejabberd_router:route(From, To, NewEl)
end;
false ->
fun(From, To, El) ->
fun(From, To, El, _Time) ->
Err =
jlib:make_error_reply(El,
?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err)
end
end,
F = fun(From, To, El) ->
F = fun(From, To, El, Time) ->
%% We'll drop the stanza if it was <forwarded/> by some
%% encapsulating protocol as per XEP-0297. One such protocol is
%% XEP-0280, which says: "When a receiving server attempts to
@@ -2856,7 +2942,7 @@ handle_unacked_stanzas(StateData)
?DEBUG("Dropping forwarded stanza from ~s",
[xml:get_attr_s(<<"from">>, El#xmlel.attrs)]);
false ->
ReRoute(From, To, El)
ReRoute(From, To, El, Time)
end
end,
handle_unacked_stanzas(StateData, F);
@@ -2891,7 +2977,7 @@ is_encapsulated_forward(_El) ->
inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
case jlib:base64_to_term(ResumeID) of
{term, {U, S, R, Time}} ->
{term, {R, Time}} ->
case ejabberd_sm:get_session_pid(U, S, R) of
none ->
{error, <<"Previous session PID not found">>};
@@ -2911,19 +2997,19 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
{auth_module, StateData#state.auth_module}],
ejabberd_sm:open_session(NewSID, U, S, R,
Priority, Info),
{ok, StateData#state{sid = NewSID,
{ok, StateData#state{conn = Conn,
sid = NewSID,
jid = OldStateData#state.jid,
resource = OldStateData#state.resource,
pres_t = OldStateData#state.pres_t,
pres_f = OldStateData#state.pres_f,
pres_a = OldStateData#state.pres_a,
pres_i = OldStateData#state.pres_i,
pres_last = OldStateData#state.pres_last,
pres_pri = OldStateData#state.pres_pri,
pres_timestamp = OldStateData#state.pres_timestamp,
pres_invis = OldStateData#state.pres_invis,
privacy_list = OldStateData#state.privacy_list,
aux_fields = OldStateData#state.aux_fields,
csi_state = OldStateData#state.csi_state,
csi_queue = OldStateData#state.csi_queue,
mgmt_xmlns = OldStateData#state.mgmt_xmlns,
mgmt_queue = OldStateData#state.mgmt_queue,
mgmt_timeout = OldStateData#state.mgmt_timeout,
@@ -2936,7 +3022,7 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
{error, <<"Cannot grab session state">>}
end
end;
error ->
_ ->
{error, <<"Invalid 'previd' value">>}
end.
@@ -2945,11 +3031,74 @@ resume_session({Time, PID}) ->
make_resume_id(StateData) ->
{Time, _} = StateData#state.sid,
ID = {StateData#state.user,
StateData#state.server,
StateData#state.resource,
Time},
jlib:term_to_base64(ID).
jlib:term_to_base64({StateData#state.resource, Time}).
add_resent_delay_info(#state{server = From}, El, Time) ->
jlib:add_delay_info(El, From, Time, <<"Resent">>).
%%%----------------------------------------------------------------------
%%% XEP-0352
%%%----------------------------------------------------------------------
csi_filter_stanza(#state{csi_state = CsiState, jid = JID} = StateData,
Stanza) ->
Action = ejabberd_hooks:run_fold(csi_filter_stanza,
StateData#state.server,
send, [Stanza]),
?DEBUG("Going to ~p stanza for inactive client ~p",
[Action, jlib:jid_to_string(JID)]),
case Action of
queue -> csi_queue_add(StateData, Stanza);
drop -> StateData;
send ->
From = xml:get_tag_attr_s(<<"from">>, Stanza),
StateData1 = csi_queue_send(StateData, From),
StateData2 = send_stanza(StateData1#state{csi_state = active},
Stanza),
StateData2#state{csi_state = CsiState}
end.
csi_queue_add(#state{csi_queue = Queue} = StateData, Stanza) ->
case length(StateData#state.csi_queue) >= csi_max_queue(StateData) of
true -> csi_queue_add(csi_queue_flush(StateData), Stanza);
false ->
From = xml:get_tag_attr_s(<<"from">>, Stanza),
NewQueue = lists:keystore(From, 1, Queue, {From, now(), Stanza}),
StateData#state{csi_queue = NewQueue}
end.
csi_queue_send(#state{csi_queue = Queue, csi_state = CsiState, server = Host} =
StateData, From) ->
case lists:keytake(From, 1, Queue) of
{value, {From, Time, Stanza}, NewQueue} ->
NewStanza = jlib:add_delay_info(Stanza, Host, Time,
<<"Client Inactive">>),
NewStateData = send_stanza(StateData#state{csi_state = active},
NewStanza),
NewStateData#state{csi_queue = NewQueue, csi_state = CsiState};
false -> StateData
end.
csi_queue_flush(#state{csi_queue = Queue, csi_state = CsiState, jid = JID,
server = Host} = StateData) ->
?DEBUG("Flushing CSI queue for ~s", [jlib:jid_to_string(JID)]),
NewStateData =
lists:foldl(fun({_From, Time, Stanza}, AccState) ->
NewStanza =
jlib:add_delay_info(Stanza, Host, Time,
<<"Client Inactive">>),
send_stanza(AccState, NewStanza)
end, StateData#state{csi_state = active}, Queue),
NewStateData#state{csi_queue = [], csi_state = CsiState}.
%% Make sure we won't push too many messages to the XEP-0198 queue when the
%% client becomes 'active' again. Otherwise, the client might not manage to
%% acknowledge the message flood in time. Also, don't let the queue grow to
%% more than 100 stanzas.
csi_max_queue(#state{mgmt_max_queue = infinity}) -> 100;
csi_max_queue(#state{mgmt_max_queue = Max}) when Max > 200 -> 100;
csi_max_queue(#state{mgmt_max_queue = Max}) when Max < 2 -> 1;
csi_max_queue(#state{mgmt_max_queue = Max}) -> Max div 2.
%%%----------------------------------------------------------------------
%%% JID Set memory footprint reduction code
+3 -4
View File
@@ -210,9 +210,8 @@ get_absolute_path(File) ->
absolute ->
File;
relative ->
Config_path = get_ejabberd_config_path(),
Config_dir = filename:dirname(Config_path),
filename:absname_join(Config_dir, File)
{ok, Dir} = file:get_cwd(),
filename:absname_join(Dir, File)
end.
@@ -989,7 +988,7 @@ report_and_stop(Tab, Err) ->
halt(string:substr(ErrTxt, 1, 199)).
emit_deprecation_warning(Module, NewModule, DBType) ->
?WARNING_MSG("Module ~s is deprecated, use {~s, [{db_type, ~s}, ...]}"
?WARNING_MSG("Module ~s is deprecated, use ~s with 'db_type: ~s'"
" instead", [Module, NewModule, DBType]).
emit_deprecation_warning(Module, NewModule) ->
+1 -1
View File
@@ -151,7 +151,7 @@ run(Hook, Host, Args) ->
%% The arguments passed to the function are: [Val | Args].
%% The result of a call is used as Val for the next call.
%% If a call returns 'stop', no more calls are performed and 'stopped' is returned.
%% If a call returns {stopped, NewVal}, no more calls are performed and NewVal is returned.
%% If a call returns {stop, NewVal}, no more calls are performed and NewVal is returned.
run_fold(Hook, Val, Args) ->
run_fold(Hook, global, Val, Args).
+11 -3
View File
@@ -65,6 +65,7 @@
request_tp,
request_headers = [],
end_of_request = false,
options = [],
default_host,
trail = <<>>
}).
@@ -133,6 +134,10 @@ init({SockMod, Socket}, Opts) ->
true -> [{[<<"http-poll">>], ejabberd_http_poll}];
false -> []
end,
XMLRPC = case proplists:get_bool(xmlrpc, Opts) of
true -> [{[], ejabberd_xmlrpc}];
false -> []
end,
DefinedHandlers = gen_mod:get_opt(
request_handlers, Opts,
fun(Hs) ->
@@ -141,7 +146,7 @@ init({SockMod, Socket}, Opts) ->
Mod} || {Path, Mod} <- Hs]
end, []),
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
Admin ++ Bind ++ Poll,
Admin ++ Bind ++ Poll ++ XMLRPC,
?DEBUG("S: ~p~n", [RequestHandlers]),
DefaultHost = gen_mod:get_opt(default_host, Opts, fun(A) -> A end, undefined),
@@ -150,6 +155,7 @@ init({SockMod, Socket}, Opts) ->
State = #state{sockmod = SockMod1,
socket = Socket1,
default_host = DefaultHost,
options = Opts,
request_handlers = RequestHandlers},
receive_headers(State).
@@ -359,7 +365,7 @@ process(Handlers, Request) ->
false -> process(HandlersLeft, Request)
end.
process_request(#state{request_method = Method,
process_request(#state{request_method = Method, options = Options,
request_path = {abs_path, Path}, request_auth = Auth,
request_lang = Lang, request_handlers = RequestHandlers,
request_host = Host, request_port = Port,
@@ -389,6 +395,7 @@ process_request(#state{request_method = Method,
IP = analyze_ip_xff(IPHere, XFF, Host),
Request = #request{method = Method,
path = LPath,
opts = Options,
q = LQuery,
auth = Auth,
lang = Lang,
@@ -413,7 +420,7 @@ process_request(#state{request_method = Method,
make_text_output(State, Status, Headers, Output)
end
end;
process_request(#state{request_method = Method,
process_request(#state{request_method = Method, options = Options,
request_path = {abs_path, Path}, request_auth = Auth,
request_content_length = Len, request_lang = Lang,
sockmod = SockMod, socket = Socket, request_host = Host,
@@ -450,6 +457,7 @@ process_request(#state{request_method = Method,
Request = #request{method = Method,
path = LPath,
q = LQuery,
opts = Options,
auth = Auth,
data = Data,
lang = Lang,
+1 -5
View File
@@ -201,11 +201,7 @@ listen_tcp(PortIP, Module, SockOpts, Port, IPS) ->
catch
_:_ -> []
end,
DeliverAs = case Module of
ejabberd_xmlrpc -> list;
_ -> binary
end,
Res = gen_tcp:listen(Port, [DeliverAs,
Res = gen_tcp:listen(Port, [binary,
{packet, 0},
{active, false},
{reuseaddr, true},
+1 -1
View File
@@ -204,7 +204,7 @@ decode_term(Bin) ->
%%%----------------------------------------------------------------------
init([Host, StartInterval]) ->
case ejabberd_config:get_option(
{keepalive_interval, Host},
{odbc_keepalive_interval, Host},
fun(I) when is_integer(I), I>0 -> I end) of
undefined ->
ok;
+4 -2
View File
@@ -374,8 +374,8 @@ wait_for_feature_request({xmlstreamelement, El},
#xmlel{name = <<"success">>,
attrs = [{<<"xmlns">>, ?NS_SASL}],
children = []}),
?DEBUG("(~w) Accepted s2s authentication for ~s",
[StateData#state.socket, AuthDomain]),
?INFO_MSG("Accepted s2s EXTERNAL authentication for ~s (TLS=~p)",
[AuthDomain, StateData#state.tls_enabled]),
change_shaper(StateData, <<"">>,
jlib:make_jid(<<"">>, AuthDomain, <<"">>)),
{next_state, wait_for_stream,
@@ -515,6 +515,8 @@ stream_established({valid, From, To}, StateData) ->
[{<<"from">>, To}, {<<"to">>, From},
{<<"type">>, <<"valid">>}],
children = []}),
?INFO_MSG("Accepted s2s dialback authentication for ~s (TLS=~p)",
[From, StateData#state.tls_enabled]),
LFrom = jlib:nameprep(From),
LTo = jlib:nameprep(To),
NSD = StateData#state{connections =
+1 -1
View File
@@ -849,7 +849,7 @@ kick_user(User, Server) ->
lists:foreach(
fun(Resource) ->
PID = get_session_pid(User, Server, Resource),
PID ! disconnect
PID ! kick
end, Resources),
length(Resources).
+42 -22
View File
@@ -17,11 +17,12 @@
-author('badlop@process-one.net').
-export([start/2, handler/2, socket_type/0, transform_listen_option/2]).
-export([start/2, handler/2, process/2, socket_type/0,
transform_listen_option/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_http.hrl").
-include("mod_roster.hrl").
-include("jlib.hrl").
@@ -170,12 +171,14 @@
%% -----------------------------
start({gen_tcp = _SockMod, Socket}, Opts) ->
%MaxSessions = gen_mod:get_opt(maxsessions, Opts,
% fun(I) when is_integer(I), I>0 -> I end,
% 10),
Timeout = gen_mod:get_opt(timeout, Opts,
fun(I) when is_integer(I), I>0 -> I end,
5000),
ejabberd_http:start({gen_tcp, Socket}, [{xmlrpc, true}|Opts]).
socket_type() -> raw.
%% -----------------------------
%% HTTP interface
%% -----------------------------
process(_, #request{method = 'POST', data = Data, opts = Opts}) ->
AccessCommandsOpts = gen_mod:get_opt(access_commands, Opts,
fun(L) when is_list(L) -> L end,
[]),
@@ -201,19 +204,36 @@ start({gen_tcp = _SockMod, Socket}, Opts) ->
[?MODULE, Wrong]),
[]
end, AccessCommandsOpts),
GetAuth = case [ACom
|| {Ac, _, _} = ACom <- AccessCommands, Ac /= all]
of
[] -> false;
_ -> true
GetAuth = case [ACom || {Ac, _, _} = ACom <- AccessCommands, Ac /= all] of
[] -> false;
_ -> true
end,
Handler = {?MODULE, handler},
State = #state{access_commands = AccessCommands,
get_auth = GetAuth},
Pid = proc_lib:spawn(xmlrpc_http, handler, [Socket, Timeout, Handler, State]),
{ok, Pid}.
socket_type() -> raw.
State = #state{access_commands = AccessCommands, get_auth = GetAuth},
case xml_stream:parse_element(Data) of
{error, _} ->
{400, [],
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"Malformed XML">>}]}};
El ->
case p1_xmlrpc:decode(El) of
{error, _} = Err ->
?ERROR_MSG("XML-RPC request ~s failed with reason: ~p",
[Data, Err]),
{400, [],
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"Malformed Request">>}]}};
{ok, RPC} ->
?DEBUG("got XML-RPC request: ~p", [RPC]),
{false, Result} = handler(State, RPC),
XML = xml:element_to_binary(p1_xmlrpc:encode(Result)),
{200, [{<<"Content-Type">>, <<"text/xml">>}],
<<"<?xml version=\"1.0\"?>", XML/binary>>}
end
end;
process(_, _) ->
{400, [],
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"400 Bad Request">>}]}}.
%% -----------------------------
%% Access verification
@@ -428,8 +448,8 @@ format_arg({array, Elements}, {list, ElementsDef})
format_arg(Arg, integer) when is_integer(Arg) -> Arg;
format_arg(Arg, binary) when is_list(Arg) -> list_to_binary(Arg);
format_arg(Arg, binary) when is_binary(Arg) -> Arg;
format_arg(Arg, string) when is_list(Arg) -> list_to_binary(Arg);
format_arg(Arg, string) when is_binary(Arg) -> Arg;
format_arg(Arg, string) when is_list(Arg) -> Arg;
format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg);
format_arg(Arg, Format) ->
?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]),
throw({error_formatting_argument, Arg, Format}).
+2 -2
View File
@@ -38,9 +38,9 @@ any -> '$empty': [].
initial -> value: initial('$1').
final -> value: final('$1').
extensible -> xattr ':dn' ':' matchingrule ':=' value: extensible('$6', ['$1', '$4']).
extensible -> xattr ':dn' ':' matchingrule ':=' value: extensible('$6', ['$1', '$4', {dnAttributes, true}]).
extensible -> xattr ':' matchingrule ':=' value: extensible('$5', ['$1', '$3']).
extensible -> xattr ':dn' ':=' value: extensible('$4', ['$1']).
extensible -> xattr ':dn' ':=' value: extensible('$4', ['$1', {dnAttributes, true}]).
extensible -> xattr ':=' value: extensible('$3', ['$1']).
extensible -> ':dn' ':' matchingrule ':=' value: extensible('$5', ['$3']).
extensible -> ':' matchingrule ':=' value: extensible('$4', ['$2']).
+22 -7
View File
@@ -228,13 +228,28 @@ get_config(Host, Opts) ->
Base = get_opt({ldap_base, Host}, Opts,
fun iolist_to_binary/1,
<<"">>),
DerefAliases = get_opt({deref_aliases, Host}, Opts,
fun(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end, never),
#eldap_config{servers = Servers,
OldDerefAliases = get_opt({deref_aliases, Host}, Opts,
fun(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end, unspecified),
DerefAliases =
if OldDerefAliases == unspecified ->
get_opt({ldap_deref_aliases, Host}, Opts,
fun(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end, never);
true ->
?WARNING_MSG("Option 'deref_aliases' is deprecated. "
"The option is still supported "
"but it is better to fix your config: "
"use 'ldap_deref_aliases' instead.", []),
OldDerefAliases
end,
#eldap_config{servers = Servers,
backups = Backups,
tls_options = [{encrypt, Encrypt},
{tls_verify, TLSVerify},
+97 -41
View File
@@ -41,10 +41,11 @@
jid_remove_resource/1, jid_replace_resource/2,
get_iq_namespace/1, iq_query_info/1,
iq_query_or_response_info/1, is_iq_request_type/1,
iq_to_xml/1, parse_xdata_submit/1, timestamp_to_iso/1,
timestamp_to_iso/2, timestamp_to_xml/4,
timestamp_to_xml/1, now_to_utc_string/1,
now_to_local_string/1, datetime_string_to_timestamp/1,
iq_to_xml/1, parse_xdata_submit/1,
add_delay_info/3, add_delay_info/4,
timestamp_to_iso/1, timestamp_to_iso/2,
now_to_utc_string/1, now_to_local_string/1,
datetime_string_to_timestamp/1,
term_to_base64/1, base64_to_term/1,
decode_base64/1, encode_base64/1, ip_to_list/1,
rsm_encode/1, rsm_encode/2, rsm_decode/1,
@@ -600,6 +601,77 @@ rsm_encode_count(Count, Arr) ->
children = [{xmlcdata, i2l(Count)}]}
| Arr].
-spec add_delay_info(xmlel(), erlang:timestamp(), binary()) -> xmlel().
add_delay_info(El, From, Time) ->
add_delay_info(El, From, Time, <<"">>).
-spec add_delay_info(xmlel(), erlang:timestamp(), binary(),
binary()) -> xmlel().
add_delay_info(El, From, Time, Desc) ->
%% TODO: Remove support for <x/>, XEP-0091 is obsolete.
El1 = add_delay_info(El, From, Time, Desc, <<"delay">>, ?NS_DELAY),
El2 = add_delay_info(El1, From, Time, Desc, <<"x">>, ?NS_DELAY91),
El2.
-spec add_delay_info(xmlel(), erlang:timestamp(), binary(), binary(), binary(),
binary()) -> xmlel().
add_delay_info(El, From, Time, Desc, Name, XMLNS) ->
case xml:get_subtag_with_xmlns(El, Name, XMLNS) of
false ->
%% Add new tag
DelayTag = create_delay_tag(Time, From, Desc, XMLNS),
xml:append_subtags(El, [DelayTag]);
DelayTag ->
%% Update existing tag
NewDelayTag =
case {xml:get_tag_cdata(DelayTag), Desc} of
{<<"">>, <<"">>} ->
DelayTag;
{OldDesc, <<"">>} ->
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]};
{<<"">>, NewDesc} ->
DelayTag#xmlel{children = [{xmlcdata, NewDesc}]};
{OldDesc, NewDesc} ->
case binary:match(OldDesc, NewDesc) of
nomatch ->
FinalDesc = <<OldDesc/binary, ", ", NewDesc/binary>>,
DelayTag#xmlel{children = [{xmlcdata, FinalDesc}]};
_ ->
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]}
end
end,
NewEl = xml:remove_subtags(El, Name, {<<"xmlns">>, XMLNS}),
xml:append_subtags(NewEl, [NewDelayTag])
end.
-spec create_delay_tag(erlang:timestamp(), jid() | binary(), binary(),
binary()) -> xmlel() | error.
create_delay_tag(TimeStamp, FromJID, Desc, XMLNS) when is_tuple(FromJID) ->
From = jlib:jid_to_string(FromJID),
{Name, Stamp} = case XMLNS of
?NS_DELAY ->
{<<"delay">>, now_to_utc_string(TimeStamp, 3)};
?NS_DELAY91 ->
DateTime = calendar:now_to_universal_time(TimeStamp),
{<<"x">>, timestamp_to_iso(DateTime)}
end,
Children = case Desc of
<<"">> -> [];
_ -> [{xmlcdata, Desc}]
end,
#xmlel{name = Name,
attrs =
[{<<"xmlns">>, XMLNS}, {<<"from">>, From},
{<<"stamp">>, Stamp}],
children = Children};
create_delay_tag(DateTime, Host, Desc, XMLNS) when is_binary(Host) ->
FromJID = jlib:make_jid(<<"">>, Host, <<"">>),
create_delay_tag(DateTime, FromJID, Desc, XMLNS).
-type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc.
%% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes}
@@ -611,18 +683,18 @@ timestamp_to_iso({{Year, Month, Day},
{Hour, Minute, Second}},
Timezone) ->
Timestamp_string =
lists:flatten(io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w",
lists:flatten(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",
[Year, Month, Day, Hour, Minute, Second])),
Timezone_string = case Timezone of
utc -> "Z";
{Sign, {TZh, TZm}} ->
io_lib:format("~s~2..0w:~2..0w", [Sign, TZh, TZm]);
io_lib:format("~s~2..0B:~2..0B", [Sign, TZh, TZm]);
{TZh, TZm} ->
Sign = case TZh >= 0 of
true -> "+";
false -> "-"
end,
io_lib:format("~s~2..0w:~2..0w",
io_lib:format("~s~2..0B:~2..0B",
[Sign, abs(TZh), TZm])
end,
{iolist_to_binary(Timestamp_string), iolist_to_binary(Timezone_string)}.
@@ -631,46 +703,25 @@ timestamp_to_iso({{Year, Month, Day},
timestamp_to_iso({{Year, Month, Day},
{Hour, Minute, Second}}) ->
iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w",
iolist_to_binary(io_lib:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
[Year, Month, Day, Hour, Minute, Second])).
-spec timestamp_to_xml(calendar:datetime(), tz(), jid(), binary()) -> xmlel().
timestamp_to_xml(DateTime, Timezone, FromJID, Desc) ->
{T_string, Tz_string} = timestamp_to_iso(DateTime,
Timezone),
Text = [{xmlcdata, Desc}],
From = jlib:jid_to_string(FromJID),
%% TODO: Remove this function once XEP-0091 is Obsolete
#xmlel{name = <<"delay">>,
attrs =
[{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From},
{<<"stamp">>, <<T_string/binary, Tz_string/binary>>}],
children = Text}.
-spec timestamp_to_xml(calendar:datetime()) -> xmlel().
timestamp_to_xml({{Year, Month, Day},
{Hour, Minute, Second}}) ->
#xmlel{name = <<"x">>,
attrs =
[{<<"xmlns">>, ?NS_DELAY91},
{<<"stamp">>,
iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w",
[Year, Month, Day, Hour, Minute,
Second]))}],
children = []}.
-spec now_to_utc_string(erlang:timestamp()) -> binary().
now_to_utc_string({MegaSecs, Secs, MicroSecs}) ->
now_to_utc_string({MegaSecs, Secs, MicroSecs}, 6).
-spec now_to_utc_string(erlang:timestamp(), 1..6) -> binary().
now_to_utc_string({MegaSecs, Secs, MicroSecs}, Precision) ->
{{Year, Month, Day}, {Hour, Minute, Second}} =
calendar:now_to_universal_time({MegaSecs, Secs,
MicroSecs}),
list_to_binary(io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6."
".0wZ",
FracOfSec = round(MicroSecs / math:pow(10, 6 - Precision)),
list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B.~*."
".0BZ",
[Year, Month, Day, Hour, Minute, Second,
MicroSecs])).
Precision, FracOfSec])).
-spec now_to_local_string(erlang:timestamp()) -> binary().
@@ -688,8 +739,8 @@ now_to_local_string({MegaSecs, Secs, MicroSecs}) ->
end,
{{Year, Month, Day}, {Hour, Minute, Second}} =
LocalTime,
list_to_binary(io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6."
".0w~s~2..0w:~2..0w",
list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B.~6."
".0B~s~2..0B:~2..0B",
[Year, Month, Day, Hour, Minute, Second,
MicroSecs, Sign, H, M])).
@@ -798,7 +849,12 @@ base64_to_term(Base64) ->
-spec decode_base64(binary()) -> binary().
decode_base64(S) ->
decode_base64_bin(S, <<>>).
case catch binary:last(S) of
C when C == $\n; C == $\s ->
decode_base64(binary:part(S, 0, byte_size(S) - 1));
_ ->
decode_base64_bin(S, <<>>)
end.
take_without_spaces(Bin, Count) ->
take_without_spaces(Bin, Count, <<>>).
+2 -1
View File
@@ -299,7 +299,8 @@ process_blocklist_unblock_all(LUser, LServer, Filter,
NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
case ejabberd_riak:put(P#privacy{lists = NewLists}) of
case ejabberd_riak:put(P#privacy{lists = NewLists},
mod_privacy:privacy_schema()) of
ok ->
{ok, Default, NewList};
Err ->
+21 -1
View File
@@ -48,7 +48,8 @@
%% hook handlers
-export([user_send_packet/3, user_receive_packet/4,
c2s_presence_in/2, c2s_broadcast_recipients/6]).
c2s_presence_in/2, c2s_filter_packet/6,
c2s_broadcast_recipients/6]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -266,6 +267,21 @@ c2s_presence_in(C2SState,
true -> C2SState
end.
c2s_filter_packet(InAcc, Host, C2SState, {pep_message, Feature}, To, _Packet) ->
case ejabberd_c2s:get_aux_field(caps_resources, C2SState) of
{ok, Rs} ->
LTo = jlib:jid_tolower(To),
case gb_trees:lookup(LTo, Rs) of
{value, Caps} ->
Drop = not lists:member(Feature, get_features(Host, Caps)),
{stop, Drop};
none ->
{stop, true}
end;
_ -> InAcc
end;
c2s_filter_packet(Acc, _, _, _, _, _) -> Acc.
c2s_broadcast_recipients(InAcc, Host, C2SState,
{pep_message, Feature}, _From, _Packet) ->
case ejabberd_c2s:get_aux_field(caps_resources,
@@ -317,6 +333,8 @@ init([Host, Opts]) ->
[{max_size, MaxSize}, {life_time, LifeTime}]),
ejabberd_hooks:add(c2s_presence_in, Host, ?MODULE,
c2s_presence_in, 75),
ejabberd_hooks:add(c2s_filter_packet, Host, ?MODULE,
c2s_filter_packet, 75),
ejabberd_hooks:add(c2s_broadcast_recipients, Host,
?MODULE, c2s_broadcast_recipients, 75),
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
@@ -348,6 +366,8 @@ terminate(_Reason, State) ->
Host = State#state.host,
ejabberd_hooks:delete(c2s_presence_in, Host, ?MODULE,
c2s_presence_in, 75),
ejabberd_hooks:delete(c2s_filter_packet, Host, ?MODULE,
c2s_filter_packet, 75),
ejabberd_hooks:delete(c2s_broadcast_recipients, Host,
?MODULE, c2s_broadcast_recipients, 75),
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
+32 -10
View File
@@ -138,8 +138,8 @@ user_receive_packet(JID, _From, To, Packet) ->
% - registered to the user_send_packet hook, to be called only once even for multicast
% - do not support "private" message mode, and do not modify the original packet in any way
% - we also replicate "read" notifications
check_and_forward(JID, To, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Direction)->
case xml:get_attr_s(<<"type">>, Attrs) == <<"chat">> andalso
check_and_forward(JID, To, Packet, Direction)->
case is_chat_or_normal_message(Packet) andalso
xml:get_subtag(Packet, <<"private">>) == false andalso
xml:get_subtag(Packet, <<"no-copy">>) == false of
true ->
@@ -167,6 +167,10 @@ remove_connection(User, Server, Resource, _Status)->
send_copies(JID, To, Packet, Direction)->
{U, S, R} = jlib:jid_tolower(JID),
PrioRes = ejabberd_sm:get_user_present_resources(U, S),
{MaxPrio, MaxRes} = case catch lists:max(PrioRes) of
{Prio, Res} -> {Prio, Res};
_ -> {0, undefined}
end,
IsBareTo = case {Direction, To} of
{received, #jid{lresource = <<>>}} -> true;
@@ -180,15 +184,19 @@ send_copies(JID, To, Packet, Direction)->
end,
%% list of JIDs that should receive a carbon copy of this message (excluding the
%% receiver(s) of the original message
TargetJIDs = if IsBareTo ->
MaxPrio = case catch lists:max(PrioRes) of
{Prio, _Res} -> Prio;
_ -> 0
end,
TargetJIDs = case {IsBareTo, R} of
{true, MaxRes} ->
OrigTo = fun(Res) -> lists:member({MaxPrio, Res}, PrioRes) end,
[ {jlib:make_jid({U, S, CCRes}), CC_Version}
|| {CCRes, CC_Version} <- list(U, S), not OrigTo(CCRes) ];
true ->
{true, _} ->
%% The message was sent to our bare JID, and we currently have
%% multiple resources with the same highest priority, so the session
%% manager routes the message to each of them. We create carbon
%% copies only from one of those resources (the one where R equals
%% MaxRes) in order to avoid duplicates.
[];
{false, _} ->
[ {jlib:make_jid({U, S, CCRes}), CC_Version}
|| {CCRes, CC_Version} <- list(U, S), CCRes /= R ]
%TargetJIDs = lists:delete(JID, [ jlib:make_jid({U, S, CCRes}) || CCRes <- list(U, S) ]),
@@ -207,7 +215,7 @@ send_copies(JID, To, Packet, Direction)->
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_2) ->
#xmlel{name = <<"message">>,
attrs = [{<<"xmlns">>, <<"jabber:client">>},
{<<"type">>, <<"chat">>},
{<<"type">>, message_type(Packet)},
{<<"from">>, jlib:jid_to_string(Sender)},
{<<"to">>, jlib:jid_to_string(Dest)}],
children = [
@@ -223,7 +231,7 @@ build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_2) ->
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_1) ->
#xmlel{name = <<"message">>,
attrs = [{<<"xmlns">>, <<"jabber:client">>},
{<<"type">>, <<"chat">>},
{<<"type">>, message_type(Packet)},
{<<"from">>, jlib:jid_to_string(Sender)},
{<<"to">>, jlib:jid_to_string(Dest)}],
children = [
@@ -264,6 +272,20 @@ complete_packet(_From, #xmlel{name = <<"message">>, attrs=OrigAttrs} = Packet, r
Attrs = lists:keystore(<<"xmlns">>, 1, OrigAttrs, {<<"xmlns">>, <<"jabber:client">>}),
Packet#xmlel{attrs = Attrs}.
message_type(#xmlel{attrs = Attrs}) ->
case xml:get_attr(<<"type">>, Attrs) of
{value, Type} -> Type;
false -> <<"normal">>
end.
is_chat_or_normal_message(#xmlel{name = <<"message">>} = Packet) ->
case message_type(Packet) of
<<"chat">> -> true;
<<"normal">> -> true;
_ -> false
end;
is_chat_or_normal_message(_Packet) -> false.
%% list {resource, cc_version} with carbons enabled for given user and host
list(User, Server)->
mnesia:dirty_select(?TABLE, [{#carboncopy{us = {User, Server}, resource = '$2', version = '$3'}, [], [{{'$2','$3'}}]}]).
+109
View File
@@ -0,0 +1,109 @@
%%%----------------------------------------------------------------------
%%% File : mod_client_state.erl
%%% Author : Holger Weiss
%%% Purpose : Filter stanzas sent to inactive clients (XEP-0352)
%%% Created : 11 Sep 2014 by Holger Weiss
%%%
%%%
%%% ejabberd, Copyright (C) 2014 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-module(mod_client_state).
-author('holger@zedat.fu-berlin.de').
-behavior(gen_mod).
-export([start/2, stop/1, add_stream_feature/2, filter_presence/2,
filter_chat_states/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
start(Host, Opts) ->
QueuePresence = gen_mod:get_opt(queue_presence, Opts,
fun(true) -> true;
(false) -> false
end, false),
DropChatStates = gen_mod:get_opt(drop_chat_states, Opts,
fun(true) -> true;
(false) -> false
end, false),
if QueuePresence; DropChatStates ->
ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE,
add_stream_feature, 50),
if QueuePresence ->
ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
filter_presence, 50);
true -> ok
end,
if DropChatStates ->
ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
filter_chat_states, 50);
true -> ok
end;
true -> ok
end,
ok.
stop(Host) ->
ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
filter_presence, 50),
ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
filter_chat_states, 50),
ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE,
add_stream_feature, 50),
ok.
add_stream_feature(Features, _Host) ->
Feature = #xmlel{name = <<"csi">>,
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}],
children = []},
[Feature | Features].
filter_presence(_Action, #xmlel{name = <<"presence">>, attrs = Attrs}) ->
case xml:get_attr(<<"type">>, Attrs) of
{value, Type} when Type /= <<"unavailable">> ->
?DEBUG("Got important presence stanza", []),
{stop, send};
_ ->
?DEBUG("Got availability presence stanza", []),
{stop, queue}
end;
filter_presence(Action, _Stanza) -> Action.
filter_chat_states(_Action, #xmlel{name = <<"message">>} = Stanza) ->
%% All XEP-0085 chat states except for <gone/>:
ChatStates = [<<"active">>, <<"inactive">>, <<"composing">>, <<"paused">>],
Stripped =
lists:foldl(fun(ChatState, AccStanza) ->
xml:remove_subtags(AccStanza, ChatState,
{<<"xmlns">>, ?NS_CHATSTATES})
end, Stanza, ChatStates),
case Stripped of
#xmlel{children = [#xmlel{name = <<"thread">>}]} ->
?DEBUG("Got standalone chat state notification", []),
{stop, drop};
#xmlel{children = []} ->
?DEBUG("Got standalone chat state notification", []),
{stop, drop};
_ ->
?DEBUG("Got message with chat state notification", []),
{stop, send}
end;
filter_chat_states(Action, _Stanza) -> Action.
+161
View File
@@ -0,0 +1,161 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2014, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 15 Aug 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_fail2ban).
-behaviour(gen_mod).
-behaviour(gen_server).
%% API
-export([start_link/2, start/2, stop/1, c2s_auth_result/4, check_bl_c2s/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").
-define(C2S_AUTH_BAN_LIFETIME, 3600). %% 1 hour
-define(C2S_MAX_AUTH_FAILURES, 20).
-define(CLEAN_INTERVAL, timer:minutes(10)).
-record(state, {host = <<"">> :: binary()}).
%%%===================================================================
%%% API
%%%===================================================================
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
c2s_auth_result(false, _User, LServer, {Addr, _Port}) ->
BanLifetime = gen_mod:get_module_opt(
LServer, ?MODULE, c2s_auth_ban_lifetime,
fun(T) when is_integer(T), T > 0 -> T end,
?C2S_AUTH_BAN_LIFETIME),
MaxFailures = gen_mod:get_module_opt(
LServer, ?MODULE, c2s_max_auth_failures,
fun(I) when is_integer(I), I > 0 -> I end,
?C2S_MAX_AUTH_FAILURES),
UnbanTS = unban_timestamp(BanLifetime),
case ets:lookup(failed_auth, Addr) of
[{Addr, N, _, _}] ->
ets:insert(failed_auth, {Addr, N+1, UnbanTS, MaxFailures});
[] ->
ets:insert(failed_auth, {Addr, 1, UnbanTS, MaxFailures})
end;
c2s_auth_result(true, _User, _Server, _AddrPort) ->
ok.
check_bl_c2s(_Acc, Addr, Lang) ->
case ets:lookup(failed_auth, Addr) of
[{Addr, N, TS, MaxFailures}] when N >= MaxFailures ->
case TS > now() of
true ->
IP = jlib:ip_to_list(Addr),
UnbanDate = format_date(
calendar:now_to_universal_time(TS)),
LogReason = io_lib:fwrite(
"Too many (~p) failed authentications "
"from this IP address (~s). The address "
"will be unblocked at ~s UTC",
[N, IP, UnbanDate]),
ReasonT = io_lib:fwrite(
translate:translate(
Lang,
<<"Too many (~p) failed authentications "
"from this IP address (~s). The address "
"will be unblocked at ~s UTC">>),
[N, IP, UnbanDate]),
{stop, {true, LogReason, ReasonT}};
false ->
ets:delete(failed_auth, Addr),
false
end;
_ ->
false
end.
%%====================================================================
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) ->
catch ets:new(failed_auth, [named_table, public]),
Proc = gen_mod:get_module_proc(Host, ?MODULE),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([Host, _Opts]) ->
ejabberd_hooks:add(c2s_auth_result, Host, ?MODULE, c2s_auth_result, 100),
ejabberd_hooks:add(check_bl_c2s, ?MODULE, check_bl_c2s, 100),
erlang:send_after(?CLEAN_INTERVAL, self(), clean),
{ok, #state{host = Host}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
?ERROR_MSG("got unexpected cast = ~p", [_Msg]),
{noreply, State}.
handle_info(clean, State) ->
?DEBUG("cleaning ~p ETS table", [failed_auth]),
Now = now(),
ets:select_delete(
failed_auth,
ets:fun2ms(fun({_, _, UnbanTS, _}) -> UnbanTS =< Now end)),
erlang:send_after(?CLEAN_INTERVAL, self(), clean),
{noreply, State};
handle_info(_Info, State) ->
?ERROR_MSG("got unexpected info = ~p", [_Info]),
{noreply, State}.
terminate(_Reason, #state{host = Host}) ->
ejabberd_hooks:delete(c2s_auth_result, Host, ?MODULE, c2s_auth_result, 100),
case is_loaded_at_other_hosts(Host) of
true ->
ok;
false ->
ejabberd_hooks:delete(check_bl_c2s, ?MODULE, check_bl_c2s, 100),
ets:delete(failed_auth)
end.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
unban_timestamp(BanLifetime) ->
{MegaSecs, MSecs, USecs} = now(),
UnbanSecs = MegaSecs * 1000000 + MSecs + BanLifetime,
{UnbanSecs div 1000000, UnbanSecs rem 1000000, USecs}.
is_loaded_at_other_hosts(Host) ->
lists:any(
fun(VHost) when VHost == Host ->
false;
(VHost) ->
gen_mod:is_loaded(VHost, ?MODULE)
end, ?MYHOSTS).
format_date({{Year, Month, Day}, {Hour, Minute, Second}}) ->
io_lib:format("~2..0w:~2..0w:~2..0w ~2..0w.~2..0w.~4..0w",
[Hour, Minute, Second, Day, Month, Year]).
+1 -6
View File
@@ -48,17 +48,12 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_http.hrl").
-include("jlib.hrl").
-include_lib("kernel/include/file.hrl").
%%-include("ejabberd_http.hrl").
%% TODO: When ejabberd-modules SVN gets the new ejabberd_http.hrl, delete this code:
-record(request,
{method, path, q = [], us, auth, lang = <<"">>,
data = <<"">>, ip, host, port, tp, headers}).
-record(state,
{host, docroot, accesslog, accesslogfd,
directory_indices, custom_headers, default_content_type,
+13 -4
View File
@@ -37,7 +37,7 @@
-export([update_bl_c2s/0]).
%% Hooks:
-export([is_ip_in_c2s_blacklist/2]).
-export([is_ip_in_c2s_blacklist/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -107,14 +107,23 @@ update_bl_c2s() ->
%% Return: false: IP not blacklisted
%% true: IP is blacklisted
%% IPV4 IP tuple:
is_ip_in_c2s_blacklist(_Val, IP) when is_tuple(IP) ->
is_ip_in_c2s_blacklist(_Val, IP, Lang) when is_tuple(IP) ->
BinaryIP = jlib:ip_to_list(IP),
case ets:lookup(bl_c2s, BinaryIP) of
[] -> %% Not in blacklist
false;
[_] -> {stop, true}
[_] ->
LogReason = io_lib:fwrite(
"This IP address is blacklisted in ~s",
[?BLC2S]),
ReasonT = io_lib:fwrite(
translate:translate(
Lang,
<<"This IP address is blacklisted in ~s">>),
[?BLC2S]),
{stop, {true, LogReason, ReasonT}}
end;
is_ip_in_c2s_blacklist(_Val, _IP) -> false.
is_ip_in_c2s_blacklist(_Val, _IP, _Lang) -> false.
%% TODO:
%% - For now, we do not kick user already logged on a given IP after
+2 -1
View File
@@ -914,7 +914,8 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
<<"You need a client that supports x:data "
"to register the nickname">>)}]},
#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA}],
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"form">>}],
children =
[#xmlel{name = <<"title">>, attrs = [],
children =
+8 -11
View File
@@ -146,7 +146,13 @@ init([Host, Opts]) ->
(plaintext) -> plaintext
end, html),
FilePermissions = gen_mod:get_opt(file_permissions, Opts,
fun({A, B}) -> {A, B}
fun(SubOpts) ->
F = fun({mode, Mode}, {_M, G}) ->
{Mode, G};
({group, Group}, {M, _G}) ->
{M, Group}
end,
lists:foldl(F, {644, 33}, SubOpts)
end, {644, 33}),
CSSFile = gen_mod:get_opt(cssfile, Opts,
fun iolist_to_binary/1,
@@ -571,16 +577,7 @@ get_dateweek(Date, Lang) ->
end).
make_dir_rec(Dir) ->
DirS = binary_to_list(Dir),
case file:read_file_info(DirS) of
{ok, _} -> ok;
{error, enoent} ->
DirL = [list_to_binary(F) || F <- filename:split(DirS)],
DirR = lists:sublist(DirL, length(DirL) - 1),
make_dir_rec(fjoin(DirR)),
file:make_dir(DirS),
file:change_mode(DirS, 8#00755) % -rwxr-xr-x
end.
filelib:ensure_dir(<<Dir/binary, $/>>).
%% {ok, F1}=file:open("valid-xhtml10.png", [read]).
%% {ok, F1b}=file:read(F1, 1000000).
+23 -17
View File
@@ -127,6 +127,13 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, D
just_created = true,
room_shaper = Shaper}),
State1 = set_opts(DefRoomOpts, State),
if (State1#state.config)#config.persistent ->
mod_muc:store_room(State1#state.server_host,
State1#state.host,
State1#state.room,
make_opts(State1));
true -> ok
end,
?INFO_MSG("Created MUC room ~s@~s by ~s",
[Room, Host, jlib:jid_to_string(Creator)]),
add_to_log(room_existence, created, State1),
@@ -167,7 +174,7 @@ normal_state({route, From, <<"">>,
Now = now_to_usec(now()),
MinMessageInterval =
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_message_interval, fun(MMI) when is_integer(MMI) -> MMI end, 0)
mod_muc, min_message_interval, fun(MMI) when is_number(MMI) -> MMI end, 0)
* 1000000),
Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} =
@@ -1510,15 +1517,17 @@ get_user_activity(JID, StateData) ->
store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_message_interval,
fun(I) when is_integer(I), I>=0 -> I end,
0),
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_message_interval,
fun(I) when is_number(I), I>=0 -> I end,
0)
* 1000),
MinPresenceInterval =
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_presence_interval,
fun(I) when is_integer(I), I>=0 -> I end,
0),
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_presence_interval,
fun(I) when is_number(I), I>=0 -> I end,
0)
* 1000),
Key = jlib:jid_tolower(JID),
Now = now_to_usec(now()),
Activity1 = clean_treap(StateData#state.activity,
@@ -1549,8 +1558,8 @@ store_user_activity(JID, UserActivity, StateData) ->
100000),
Delay = lists:max([MessageShaperInterval,
PresenceShaperInterval,
MinMessageInterval * 1000,
MinPresenceInterval * 1000])
MinMessageInterval,
MinPresenceInterval])
* 1000,
Priority = {1, -(Now + Delay)},
StateData#state{activity =
@@ -2429,24 +2438,21 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
false -> false;
_ -> true
end,
TimeStamp = calendar:now_to_universal_time(now()),
TimeStamp = now(),
SenderJid = case
(StateData#state.config)#config.anonymous
of
true -> StateData#state.jid;
false -> FromJID
end,
TSPacket = xml:append_subtags(Packet,
[jlib:timestamp_to_xml(TimeStamp, utc,
SenderJid, <<"">>),
jlib:timestamp_to_xml(TimeStamp)]),
TSPacket = jlib:add_delay_info(Packet, SenderJid, TimeStamp),
SPacket =
jlib:replace_from_to(jlib:jid_replace_resource(StateData#state.jid,
FromNick),
StateData#state.jid, TSPacket),
Size = element_size(SPacket),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject,
TimeStamp, Size},
calendar:now_to_universal_time(TimeStamp), Size},
StateData#state.history),
add_to_log(text, {FromNick, Packet}, StateData),
StateData#state{history = Q1}.
+124 -99
View File
@@ -26,13 +26,15 @@
-module(mod_offline).
-author('alexey@process-one.net').
-define(GEN_SERVER, p1_server).
-behaviour(?GEN_SERVER).
-behaviour(gen_mod).
-export([count_offline_messages/2]).
-export([start/2,
loop/2,
start_link/2,
stop/1,
store_packet/3,
resend_offline_messages/2,
@@ -50,6 +52,10 @@
webadmin_user/4,
webadmin_user_parse_query/5]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -67,6 +73,10 @@
to = #jid{} :: jid() | '_',
packet = #xmlel{} :: xmlel() | '_'}).
-record(state,
{host = <<"">> :: binary(),
access_max_offline_messages}).
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
@@ -74,7 +84,29 @@
%% default value for the maximum number of user messages
-define(MAX_USER_MESSAGES, infinity).
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
?GEN_SERVER:start_link({local, Proc}, ?MODULE,
[Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
?GEN_SERVER:call(Proc, stop),
supervisor:delete_child(ejabberd_sup, Proc),
ok.
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
case gen_mod:db_type(Opts) of
mnesia ->
mnesia:create_table(offline_msg,
@@ -102,22 +134,57 @@ start(Host, Opts) ->
ejabberd_hooks:add(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, fun(A) -> A end, max_user_offline_messages),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
{ok,
#state{host = Host,
access_max_offline_messages = AccessMaxOfflineMsgs}}.
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(#offline_msg{us = UserServer} = Msg, State) ->
#state{host = Host,
access_max_offline_messages = AccessMaxOfflineMsgs} = State,
DBType = gen_mod:db_type(Host, ?MODULE),
Msgs = receive_all(UserServer, [Msg], DBType),
Len = length(Msgs),
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
UserServer, Host),
store_offline_msg(Host, UserServer, Msgs, Len, MaxOfflineMsgs, DBType),
{noreply, State};
handle_info(_Info, State) ->
?ERROR_MSG("got unexpected info: ~p", [_Info]),
{noreply, State}.
terminate(_Reason, State) ->
Host = State#state.host,
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, store_packet, 50),
ejabberd_hooks:delete(resend_offline_messages_hook,
Host, ?MODULE, pop_offline_messages, 50),
ejabberd_hooks:delete(remove_user, Host, ?MODULE,
remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, Host,
?MODULE, remove_user, 50),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(webadmin_page_host, Host,
?MODULE, webadmin_page, 50),
ejabberd_hooks:delete(webadmin_user, Host,
?MODULE, webadmin_user, 50),
ejabberd_hooks:delete(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
loop(Host, AccessMaxOfflineMsgs) ->
receive
#offline_msg{us = UserServer} = Msg ->
DBType = gen_mod:db_type(Host, ?MODULE),
Msgs = receive_all(UserServer, [Msg], DBType),
Len = length(Msgs),
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
UserServer, Host),
store_offline_msg(Host, UserServer, Msgs, Len, MaxOfflineMsgs, DBType),
loop(Host, AccessMaxOfflineMsgs);
_ ->
loop(Host, AccessMaxOfflineMsgs)
end.
store_offline_msg(_Host, US, Msgs, Len, MaxOfflineMsgs,
mnesia) ->
@@ -148,26 +215,15 @@ store_offline_msg(Host, {User, _Server}, Msgs, Len, MaxOfflineMsgs, odbc) ->
ejabberd_odbc:escape((M#offline_msg.to)#jid.luser),
From = M#offline_msg.from,
To = M#offline_msg.to,
#xmlel{name = Name, attrs = Attrs,
children = Els} =
M#offline_msg.packet,
Attrs2 =
jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
Packet = #xmlel{name = Name,
attrs = Attrs2,
children =
Els ++
[jlib:timestamp_to_xml(calendar:now_to_universal_time(M#offline_msg.timestamp),
utc,
jlib:make_jid(<<"">>,
Host,
<<"">>),
<<"Offline Storage">>),
jlib:timestamp_to_xml(calendar:now_to_universal_time(M#offline_msg.timestamp))]},
Packet =
jlib:replace_from_to(From, To,
M#offline_msg.packet),
NewPacket =
jlib:add_delay_info(Packet, Host,
M#offline_msg.timestamp,
<<"Offline Storage">>),
XML =
ejabberd_odbc:escape(xml:element_to_binary(Packet)),
ejabberd_odbc:escape(xml:element_to_binary(NewPacket)),
odbc_queries:add_spool_sql(Username, XML)
end,
Msgs),
@@ -212,28 +268,7 @@ receive_all(US, Msgs, DBType) ->
end
end.
stop(Host) ->
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, store_packet, 50),
ejabberd_hooks:delete(resend_offline_messages_hook,
Host, ?MODULE, pop_offline_messages, 50),
ejabberd_hooks:delete(remove_user, Host, ?MODULE,
remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, Host,
?MODULE, remove_user, 50),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(webadmin_page_host, Host,
?MODULE, webadmin_page, 50),
ejabberd_hooks:delete(webadmin_user, Host,
?MODULE, webadmin_user, 50),
ejabberd_hooks:delete(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
exit(whereis(Proc), stop),
{wait, Proc}.
get_sm_features(Acc, _From, _To, "", _Lang) ->
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
@@ -247,11 +282,26 @@ get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
store_packet(From, To, Packet) ->
need_to_store(LServer, Packet) ->
Type = xml:get_tag_attr_s(<<"type">>, Packet),
if (Type /= <<"error">>) and (Type /= <<"groupchat">>)
and (Type /= <<"headline">>) ->
and (Type /= <<"headline">>) ->
case gen_mod:get_module_opt(
LServer, ?MODULE, store_empty_body,
fun(V) when is_boolean(V) -> V end,
true) of
false ->
xml:get_subtag(Packet, <<"body">>) /= false;
true ->
true
end;
true ->
false
end.
store_packet(From, To, Packet) ->
case need_to_store(To#jid.lserver, Packet) of
true ->
case has_no_storage_hint(Packet) of
false ->
case check_event(From, To, Packet) of
@@ -269,7 +319,7 @@ store_packet(From, To, Packet) ->
end;
_ -> ok
end;
true -> ok
false -> ok
end.
has_no_storage_hint(Packet) ->
@@ -371,15 +421,12 @@ resend_offline_messages(User, Server) ->
case mnesia:transaction(F) of
{atomic, Rs} ->
lists:foreach(fun (R) ->
#xmlel{name = Name, attrs = Attrs,
children = Els} =
R#offline_msg.packet,
ejabberd_sm !
{route, R#offline_msg.from, R#offline_msg.to,
#xmlel{name = Name, attrs = Attrs,
children =
Els ++
[jlib:timestamp_to_xml(calendar:now_to_universal_time(R#offline_msg.timestamp))]}}
jlib:add_delay_info(R#offline_msg.packet,
LServer,
R#offline_msg.timestamp,
<<"Offline Storage">>)}
end,
lists:keysort(#offline_msg.timestamp, Rs));
_ -> ok
@@ -625,19 +672,9 @@ get_offline_els(LUser, LServer, odbc) ->
end.
offline_msg_to_route(LServer, #offline_msg{} = R) ->
El = #xmlel{children = Els} = R#offline_msg.packet,
{route, R#offline_msg.from, R#offline_msg.to,
El#xmlel{children =
Els ++
[jlib:timestamp_to_xml(
calendar:now_to_universal_time(
R#offline_msg.timestamp),
utc,
jlib:make_jid(<<"">>, LServer, <<"">>),
<<"Offline Storage">>),
jlib:timestamp_to_xml(
calendar:now_to_universal_time(
R#offline_msg.timestamp))]}};
jlib:add_delay_info(R#offline_msg.packet, LServer, R#offline_msg.timestamp,
<<"Offline Storage">>)};
offline_msg_to_route(_LServer, #xmlel{} = El) ->
To = jlib:string_to_jid(xml:get_tag_attr_s(<<"to">>, El)),
From = jlib:string_to_jid(xml:get_tag_attr_s(<<"from">>, El)),
@@ -1048,26 +1085,14 @@ export(_Server) ->
packet = Packet})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
#xmlel{name = Name, attrs = Attrs, children = Els} =
Packet,
Attrs2 =
jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
NewPacket = #xmlel{name = Name, attrs = Attrs2,
children =
Els ++
[jlib:timestamp_to_xml(
calendar:now_to_universal_time(TimeStamp),
utc,
jlib:make_jid(<<"">>,
LServer,
<<"">>),
<<"Offline Storage">>),
jlib:timestamp_to_xml(
calendar:now_to_universal_time(TimeStamp))]},
Packet1 =
jlib:replace_from_to(jlib:jid_to_string(From),
jlib:jid_to_string(To), Packet),
Packet2 =
jlib:add_delay_info(Packet1, LServer, TimeStamp,
<<"Offline Storage">>),
XML =
ejabberd_odbc:escape(xml:element_to_binary(NewPacket)),
ejabberd_odbc:escape(xml:element_to_binary(Packet2)),
[[<<"delete from spool where username='">>, Username, <<"';">>],
[<<"insert into spool(username, xml) values ('">>,
Username, <<"', '">>, XML, <<"');">>]];
+81 -50
View File
@@ -74,7 +74,8 @@
on_user_offline/3, remove_user/2,
disco_local_identity/5, disco_local_features/5,
disco_local_items/5, disco_sm_identity/5,
disco_sm_features/5, disco_sm_items/5]).
disco_sm_features/5, disco_sm_items/5,
drop_pep_error/4]).
%% exported iq handlers
-export([iq_sm/3]).
@@ -344,6 +345,8 @@ init([ServerHost, Opts]) ->
?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE,
disco_sm_items, 75),
ejabberd_hooks:add(c2s_filter_packet_in, ServerHost, ?MODULE,
drop_pep_error, 75),
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
@@ -690,9 +693,9 @@ update_node_database(Host, ServerHost) ->
end,
mnesia:transaction(fun () ->
case catch mnesia:first(pubsub_node) of
{_, L} when is_binary(L) ->
{_, L} when is_list(L) ->
lists:foreach(fun ({H, N})
when is_binary(N) ->
when is_list(N) ->
[Node] =
mnesia:read({pubsub_node,
{H,
@@ -1179,8 +1182,12 @@ presence_probe(#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, _Pid)
%% ignore presence_probe from my other ressources
%% to not get duplicated last items
ok;
presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = Host} = JID, _Pid) ->
presence(Host, {presence, U, S, [R], JID}).
presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = S} = JID, _Pid) ->
presence(S, {presence, U, S, [R], JID});
presence_probe(_Host, _JID, _Pid) ->
%% ignore presence_probe from remote contacts,
%% those are handled via caps_update
ok.
presence(ServerHost, Presence) ->
SendLoop = case
@@ -1278,6 +1285,33 @@ unsubscribe_user(Entity, Owner) ->
plugins(Host))
end).
%% -------
%% packet receive hook handling function
%%
drop_pep_error(#xmlel{name = <<"message">>, attrs = Attrs} = Packet, _JID, From,
#jid{lresource = <<"">>} = To) ->
case xml:get_attr_s(<<"type">>, Attrs) of
<<"error">> ->
case xml:get_subtag(Packet, <<"event">>) of
#xmlel{attrs = EventAttrs} ->
case xml:get_attr_s(<<"xmlns">>, EventAttrs) of
?NS_PUBSUB_EVENT ->
?DEBUG("Dropping PEP error message from ~s to ~s",
[jlib:jid_to_string(From),
jlib:jid_to_string(To)]),
drop;
_ ->
Packet
end;
false ->
Packet
end;
_ ->
Packet
end;
drop_pep_error(Acc, _JID, _From, _To) -> Acc.
%% -------
%% user remove hook handling function
%%
@@ -1418,6 +1452,8 @@ terminate(_Reason,
?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost,
?MODULE, disco_sm_items, 75),
ejabberd_hooks:delete(c2s_filter_packet_in, ServerHost,
?MODULE, drop_pep_error, 75),
gen_iq_handler:remove_iq_handler(ejabberd_sm,
ServerHost, ?NS_PUBSUB),
gen_iq_handler:remove_iq_handler(ejabberd_sm,
@@ -3312,7 +3348,7 @@ get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
%% Number = last | integer()
%% @doc <p>Resend the items of a node to the user.</p>
%% @todo use cache-last-item feature
send_items(Host, Node, NodeId, Type, {U, S, R} = LJID, last) ->
send_items(Host, Node, NodeId, Type, LJID, last) ->
case get_cached_item(Host, NodeId) of
undefined ->
send_items(Host, Node, NodeId, Type, LJID, 1);
@@ -3325,24 +3361,9 @@ send_items(Host, Node, NodeId, Type, {U, S, R} = LJID, last) ->
children =
itemsEls([LastItem])}],
ModifNow, ModifUSR),
case is_tuple(Host) of
false ->
ejabberd_router:route(service_jid(Host),
jlib:make_jid(LJID), Stanza);
true ->
case ejabberd_sm:get_session_pid(U, S, R) of
C2SPid when is_pid(C2SPid) ->
ejabberd_c2s:broadcast(C2SPid,
{pep_message,
<<((Node))/binary, "+notify">>},
_Sender = service_jid(Host),
Stanza);
_ -> ok
end
end
dispatch_items(Host, LJID, Node, Stanza)
end;
send_items(Host, Node, NodeId, Type, {U, S, R} = LJID,
Number) ->
send_items(Host, Node, NodeId, Type, LJID, Number) ->
ToSend = case node_action(Host, Type, get_items,
[NodeId, LJID])
of
@@ -3370,22 +3391,38 @@ send_items(Host, Node, NodeId, Type, {U, S, R} = LJID,
attrs = nodeAttr(Node),
children = itemsEls(ToSend)}])
end,
case {is_tuple(Host), Stanza} of
{_, undefined} ->
ok;
{false, _} ->
ejabberd_router:route(service_jid(Host),
jlib:make_jid(LJID), Stanza);
{true, _} ->
case ejabberd_sm:get_session_pid(U, S, R) of
C2SPid when is_pid(C2SPid) ->
ejabberd_c2s:broadcast(C2SPid,
{pep_message,
<<((Node))/binary, "+notify">>},
_Sender = service_jid(Host), Stanza);
_ -> ok
end
end.
dispatch_items(Host, LJID, Node, Stanza).
-spec(dispatch_items/4 ::
(
From :: mod_pubsub:host(),
To :: jid(),
Node :: mod_pubsub:nodeId(),
Stanza :: xmlel() | undefined)
-> any()
).
dispatch_items(_From, _To, _Node, _Stanza = undefined) -> ok;
dispatch_items({FromU, FromS, FromR} = From, {ToU, ToS, ToR} = To, Node,
Stanza) ->
C2SPid = case ejabberd_sm:get_session_pid(ToU, ToS, ToR) of
ToPid when is_pid(ToPid) -> ToPid;
_ ->
R = user_resource(FromU, FromS, FromR),
case ejabberd_sm:get_session_pid(FromU, FromS, R) of
FromPid when is_pid(FromPid) -> FromPid;
_ -> undefined
end
end,
if C2SPid == undefined -> ok;
true ->
ejabberd_c2s:send_filtered(C2SPid,
{pep_message, <<Node/binary, "+notify">>},
service_jid(From), jlib:make_jid(To),
Stanza)
end;
dispatch_items(From, To, _Node, Stanza) ->
ejabberd_router:route(service_jid(From), jlib:make_jid(To), Stanza).
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
%% Host = host()
@@ -4238,21 +4275,15 @@ payload_xmlelements([_ | Tail], Count) ->
%% @spec (Els) -> stanza()
%% Els = [xmlelement()]
%% @doc <p>Build pubsub event stanza</p>
event_stanza(Els) -> event_stanza_withmoreels(Els, []).
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
DateTime = calendar:now_to_datetime(ModifNow),
MoreEls = [jlib:timestamp_to_xml(DateTime, utc,
ModifUSR, <<"">>)],
event_stanza_withmoreels(Els, MoreEls).
event_stanza_withmoreels(Els, MoreEls) ->
event_stanza(Els) ->
#xmlel{name = <<"message">>, attrs = [],
children =
[#xmlel{name = <<"event">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
children = Els}
| MoreEls]}.
children = Els}]}.
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow).
%%%%%% broadcast functions
+79 -33
View File
@@ -74,7 +74,8 @@
on_user_offline/3, remove_user/2,
disco_local_identity/5, disco_local_features/5,
disco_local_items/5, disco_sm_identity/5,
disco_sm_features/5, disco_sm_items/5]).
disco_sm_features/5, disco_sm_items/5,
drop_pep_error/4]).
%% exported iq handlers
-export([iq_sm/3]).
@@ -344,6 +345,8 @@ init([ServerHost, Opts]) ->
?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE,
disco_sm_items, 75),
ejabberd_hooks:add(c2s_filter_packet_in, ServerHost, ?MODULE,
drop_pep_error, 75),
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
@@ -830,8 +833,12 @@ presence_probe(#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, _Pid)
%% ignore presence_probe from my other ressources
%% to not get duplicated last items
ok;
presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = Host} = JID, _Pid) ->
presence(Host, {presence, U, S, [R], JID}).
presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = S} = JID, _Pid) ->
presence(S, {presence, U, S, [R], JID});
presence_probe(_Host, _JID, _Pid) ->
%% ignore presence_probe from remote contacts,
%% those are handled via caps_update
ok.
presence(ServerHost, Presence) ->
SendLoop = case
@@ -929,6 +936,33 @@ unsubscribe_user(Entity, Owner) ->
plugins(Host))
end).
%% -------
%% packet receive hook handling function
%%
drop_pep_error(#xmlel{name = <<"message">>, attrs = Attrs} = Packet, _JID, From,
#jid{lresource = <<"">>} = To) ->
case xml:get_attr_s(<<"type">>, Attrs) of
<<"error">> ->
case xml:get_subtag(Packet, <<"event">>) of
#xmlel{attrs = EventAttrs} ->
case xml:get_attr_s(<<"xmlns">>, EventAttrs) of
?NS_PUBSUB_EVENT ->
?DEBUG("Dropping PEP error message from ~s to ~s",
[jlib:jid_to_string(From),
jlib:jid_to_string(To)]),
drop;
_ ->
Packet
end;
false ->
Packet
end;
_ ->
Packet
end;
drop_pep_error(Acc, _JID, _From, _To) -> Acc.
%% -------
%% user remove hook handling function
%%
@@ -1069,6 +1103,8 @@ terminate(_Reason,
?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost,
?MODULE, disco_sm_items, 75),
ejabberd_hooks:delete(c2s_filter_packet_in, ServerHost,
?MODULE, drop_pep_error, 75),
gen_iq_handler:remove_iq_handler(ejabberd_sm,
ServerHost, ?NS_PUBSUB),
gen_iq_handler:remove_iq_handler(ejabberd_sm,
@@ -2315,7 +2351,7 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
{result, Reply};
{result, {NodeId, _SubsByDepth, Result}} ->
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
{result, Result};
{result, Reply};
Error ->
%% in case we change transaction to sync_dirty...
%% node_call(Type, delete_node, [Host, Node]),
@@ -3011,8 +3047,8 @@ send_items(Host, Node, NodeId, Type, LJID, last) ->
itemsEls([LastItem])}],
ModifNow, ModifUSR)
end,
ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza);
send_items(Host, Node, NodeId, Type, {U, S, R} = LJID, Number) ->
dispatch_items(Host, LJID, Node, Stanza);
send_items(Host, Node, NodeId, Type, LJID, Number) ->
ToSend = case node_action(Host, Type, get_items,
[NodeId, LJID])
of
@@ -3040,22 +3076,38 @@ send_items(Host, Node, NodeId, Type, {U, S, R} = LJID, Number) ->
attrs = nodeAttr(Node),
children = itemsEls(ToSend)}])
end,
case {is_tuple(Host), Stanza} of
{_, undefined} ->
ok;
{false, _} ->
ejabberd_router:route(service_jid(Host),
jlib:make_jid(LJID), Stanza);
{true, _} ->
case ejabberd_sm:get_session_pid(U, S, R) of
C2SPid when is_pid(C2SPid) ->
ejabberd_c2s:broadcast(C2SPid,
{pep_message,
<<((Node))/binary, "+notify">>},
_Sender = service_jid(Host), Stanza);
_ -> ok
end
end.
dispatch_items(Host, LJID, Node, Stanza).
-spec(dispatch_items/4 ::
(
From :: mod_pubsub:host(),
To :: jid(),
Node :: mod_pubsub:nodeId(),
Stanza :: xmlel() | undefined)
-> any()
).
dispatch_items(_From, _To, _Node, _Stanza = undefined) -> ok;
dispatch_items({FromU, FromS, FromR} = From, {ToU, ToS, ToR} = To, Node,
Stanza) ->
C2SPid = case ejabberd_sm:get_session_pid(ToU, ToS, ToR) of
ToPid when is_pid(ToPid) -> ToPid;
_ ->
R = user_resource(FromU, FromS, FromR),
case ejabberd_sm:get_session_pid(FromU, FromS, R) of
FromPid when is_pid(FromPid) -> FromPid;
_ -> undefined
end
end,
if C2SPid == undefined -> ok;
true ->
ejabberd_c2s:send_filtered(C2SPid,
{pep_message, <<Node/binary, "+notify">>},
service_jid(From), jlib:make_jid(To),
Stanza)
end;
dispatch_items(From, To, _Node, Stanza) ->
ejabberd_router:route(service_jid(From), jlib:make_jid(To), Stanza).
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
%% Host = host()
@@ -3873,21 +3925,15 @@ payload_xmlelements([_ | Tail], Count) ->
%% @spec (Els) -> stanza()
%% Els = [xmlelement()]
%% @doc <p>Build pubsub event stanza</p>
event_stanza(Els) -> event_stanza_withmoreels(Els, []).
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
DateTime = calendar:now_to_datetime(ModifNow),
MoreEls = [jlib:timestamp_to_xml(DateTime, utc,
ModifUSR, <<"">>)],
event_stanza_withmoreels(Els, MoreEls).
event_stanza_withmoreels(Els, MoreEls) ->
event_stanza(Els) ->
#xmlel{name = <<"message">>, attrs = [],
children =
[#xmlel{name = <<"event">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
children = Els}
| MoreEls]}.
children = Els}]}.
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow).
%%%%%% broadcast functions
+1 -1
View File
@@ -1238,7 +1238,7 @@ read_subscription_and_groups(LUser, LServer, LJID,
case catch odbc_queries:get_subscription(LServer,
Username, SJID)
of
{selected, [<<"subscription">>], [{SSubscription}]} ->
{selected, [<<"subscription">>], [[SSubscription]]} ->
Subscription = case SSubscription of
<<"B">> -> both;
<<"T">> -> to;
+4 -1
View File
@@ -501,7 +501,10 @@ delete_group(Host, Group, odbc) ->
ejabberd_odbc:sql_query_t([<<"delete from sr_user where grp='">>,
SGroup, <<"';">>])
end,
ejabberd_odbc:sql_transaction(Host, F).
case ejabberd_odbc:sql_transaction(Host, F) of
{atomic,{updated,_}} -> {atomic, ok};
Res -> Res
end.
get_group_opts(Host, Group) ->
get_group_opts(Host, Group,
+5
View File
@@ -186,6 +186,11 @@ process_sm_iq(From, To,
error ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
[] ->
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"vCard">>,
attrs = [{<<"xmlns">>, ?NS_VCARD}],
children = []}]};
Els -> IQ#iq{type = result, sub_el = Els}
end
end.
+12 -4
View File
@@ -97,10 +97,14 @@ update_t(Table, Fields, Vals, Where) ->
of
{updated, 1} -> ok;
_ ->
ejabberd_odbc:sql_query_t([<<"insert into ">>, Table,
Res = ejabberd_odbc:sql_query_t([<<"insert into ">>, Table,
<<"(">>, join(Fields, <<", ">>),
<<") values ('">>, join(Vals, <<"', '">>),
<<"');">>])
<<"');">>]),
case Res of
{updated,1} -> ok;
_ -> Res
end
end.
update(LServer, Table, Fields, Vals, Where) ->
@@ -115,10 +119,14 @@ update(LServer, Table, Fields, Vals, Where) ->
of
{updated, 1} -> ok;
_ ->
ejabberd_odbc:sql_query(LServer,
Res = ejabberd_odbc:sql_query(LServer,
[<<"insert into ">>, Table, <<"(">>,
join(Fields, <<", ">>), <<") values ('">>,
join(Vals, <<"', '">>), <<"');">>])
join(Vals, <<"', '">>), <<"');">>]),
case Res of
{updated,1} -> ok;
_ -> Res
end
end.
%% F can be either a fun or a list of queries
+208 -39
View File
@@ -199,6 +199,8 @@ db_tests(riak) ->
blocking,
vcard,
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
@@ -206,6 +208,10 @@ db_tests(riak) ->
[offline_master, offline_slave]},
{test_muc, [parallel],
[muc_master, muc_slave]},
{test_announce, [sequence],
[announce_master, announce_slave]},
{test_vcard_xupdate, [parallel],
[vcard_xupdate_master, vcard_xupdate_slave]},
{test_roster_remove, [parallel],
[roster_remove_master,
roster_remove_slave]}];
@@ -224,6 +230,8 @@ db_tests(mnesia) ->
vcard,
pubsub,
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
@@ -231,8 +239,14 @@ db_tests(mnesia) ->
[offline_master, offline_slave]},
{test_carbons, [parallel],
[carbons_master, carbons_slave]},
{test_client_state, [parallel],
[client_state_master, client_state_slave]},
{test_muc, [parallel],
[muc_master, muc_slave]},
{test_announce, [sequence],
[announce_master, announce_slave]},
{test_vcard_xupdate, [parallel],
[vcard_xupdate_master, vcard_xupdate_slave]},
{test_roster_remove, [parallel],
[roster_remove_master,
roster_remove_slave]}];
@@ -252,6 +266,8 @@ db_tests(_) ->
vcard,
pubsub,
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
@@ -259,6 +275,10 @@ db_tests(_) ->
[offline_master, offline_slave]},
{test_muc, [parallel],
[muc_master, muc_slave]},
{test_announce, [sequence],
[announce_master, announce_slave]},
{test_vcard_xupdate, [parallel],
[vcard_xupdate_master, vcard_xupdate_slave]},
{test_roster_remove, [parallel],
[roster_remove_master,
roster_remove_slave]}].
@@ -513,16 +533,16 @@ sm(Config) ->
Msg = #message{to = ServerJID, body = [#text{data = <<"body">>}]},
true = ?config(sm, Config),
%% Enable the session management with resumption enabled
send(Config, #sm_enable{resume = true}),
send(Config, #sm_enable{resume = true, xmlns = ?NS_STREAM_MGMT_3}),
#sm_enabled{id = ID, resume = true} = recv(),
%% Initial request; 'h' should be 0.
send(Config, #sm_r{}),
send(Config, #sm_r{xmlns = ?NS_STREAM_MGMT_3}),
#sm_a{h = 0} = recv(),
%% sending two messages and requesting again; 'h' should be 3.
send(Config, Msg),
send(Config, Msg),
send(Config, Msg),
send(Config, #sm_r{}),
send(Config, #sm_r{xmlns = ?NS_STREAM_MGMT_3}),
#sm_a{h = 3} = recv(),
close_socket(Config),
{save_config, set_opt(sm_previd, ID, Config)}.
@@ -537,11 +557,11 @@ sm_resume(Config) ->
Msg = #message{from = ServerJID, to = MyJID, body = [Txt]},
%% Route message. The message should be queued by the C2S process.
ejabberd_router:route(ServerJID, MyJID, xmpp_codec:encode(Msg)),
send(Config, #sm_resume{previd = ID, h = 0}),
send(Config, #sm_resume{previd = ID, h = 0, xmlns = ?NS_STREAM_MGMT_3}),
#sm_resumed{previd = ID, h = 3} = recv(),
#message{from = ServerJID, to = MyJID, body = [Txt]} = recv(),
#sm_r{} = recv(),
send(Config, #sm_a{h = 1}),
send(Config, #sm_a{h = 1, xmlns = ?NS_STREAM_MGMT_3}),
disconnect(Config).
private(Config) ->
@@ -708,6 +728,42 @@ vcard_get(Config) ->
send_recv(Config, #iq{type = get, sub_els = [#vcard{}]}),
disconnect(Config).
vcard_xupdate_master(Config) ->
Img = <<137, "PNG\r\n", 26, $\n>>,
ImgHash = p1_sha:sha(Img),
MyJID = my_jid(Config),
Peer = ?config(slave, Config),
wait_for_slave(Config),
send(Config, #presence{}),
?recv2(#presence{from = MyJID, type = undefined},
#presence{from = Peer, type = undefined}),
VCard = #vcard{photo = #vcard_photo{type = <<"image/png">>, binval = Img}},
I1 = send(Config, #iq{type = set, sub_els = [VCard]}),
?recv2(#iq{type = result, sub_els = [], id = I1},
#presence{from = MyJID, type = undefined,
sub_els = [#vcard_xupdate{photo = ImgHash}]}),
I2 = send(Config, #iq{type = set, sub_els = [#vcard{}]}),
?recv3(#iq{type = result, sub_els = [], id = I2},
#presence{from = MyJID, type = undefined,
sub_els = [#vcard_xupdate{photo = undefined}]},
#presence{from = Peer, type = unavailable}),
disconnect(Config).
vcard_xupdate_slave(Config) ->
Img = <<137, "PNG\r\n", 26, $\n>>,
ImgHash = p1_sha:sha(Img),
MyJID = my_jid(Config),
Peer = ?config(master, Config),
send(Config, #presence{}),
#presence{from = MyJID, type = undefined} = recv(),
wait_for_master(Config),
#presence{from = Peer, type = undefined} = recv(),
#presence{from = Peer, type = undefined,
sub_els = [#vcard_xupdate{photo = ImgHash}]} = recv(),
#presence{from = Peer, type = undefined,
sub_els = [#vcard_xupdate{photo = undefined}]} = recv(),
disconnect(Config).
stats(Config) ->
#iq{type = result, sub_els = [#stats{stat = Stats}]} =
send_recv(Config, #iq{type = get, sub_els = [#stats{}],
@@ -745,7 +801,7 @@ pubsub(Config) ->
node = Node,
jid = my_jid(Config)}}]}),
?recv2(
#message{sub_els = [#pubsub_event{}, #delay{}]},
#message{sub_els = [#pubsub_event{}, #delay{}, #legacy_delay{}]},
#iq{type = result, id = I1}),
%% Get subscriptions
true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
@@ -1004,7 +1060,8 @@ muc_master(Config) ->
%% As this is the newly created room, we receive only the 2nd stanza.
#presence{
from = MyNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
status_codes = Codes,
items = [#muc_item{role = moderator,
jid = MyJID,
@@ -1073,7 +1130,8 @@ muc_master(Config) ->
[#muc_invite{to = PeerJID}]}]}),
%% Peer is joining
#presence{from = PeerNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
items = [#muc_item{role = visitor,
jid = PeerJID,
affiliation = none}]}]} = recv(),
@@ -1106,7 +1164,8 @@ muc_master(Config) ->
fields = ReplyVoiceReqFs}]}),
%% Peer is becoming a participant
#presence{from = PeerNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
items = [#muc_item{role = participant,
jid = PeerJID,
affiliation = none}]}]} = recv(),
@@ -1124,7 +1183,8 @@ muc_master(Config) ->
affiliation = member}]}]}),
%% Peer became a member
#presence{from = PeerNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
items = [#muc_item{affiliation = member,
jid = PeerJID,
role = participant}]}]} = recv(),
@@ -1141,7 +1201,7 @@ muc_master(Config) ->
role = none}]}]}),
%% Got notification the peer is kicked
%% 307 -> Inform user that he or she has been kicked from the room
#presence{from = PeerNickJID,
#presence{from = PeerNickJID, type = unavailable,
sub_els = [#muc_user{
status_codes = [307],
items = [#muc_item{affiliation = member,
@@ -1199,14 +1259,16 @@ muc_slave(Config) ->
%% First presence is from the participant, i.e. from the peer
#presence{
from = PeerNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
status_codes = [],
items = [#muc_item{role = moderator,
affiliation = owner}]}]} = recv(),
%% The next is the self-presence (code 110 means it)
#presence{
from = MyNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
status_codes = [110],
items = [#muc_item{role = visitor,
affiliation = none}]}]} = recv(),
@@ -1237,7 +1299,8 @@ muc_slave(Config) ->
send(Config, #message{to = Room, sub_els = [VoiceReq]}),
%% Becoming a participant
#presence{from = MyNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
items = [#muc_item{role = participant,
affiliation = none}]}]} = recv(),
%% Sending private message to the peer
@@ -1245,7 +1308,8 @@ muc_slave(Config) ->
body = [#text{data = Subject}]}),
%% Becoming a member
#presence{from = MyNickJID,
sub_els = [#muc_user{
sub_els = [#vcard_xupdate{},
#muc_user{
items = [#muc_item{role = participant,
affiliation = member}]}]} = recv(),
%% Retrieving a member list
@@ -1273,6 +1337,91 @@ muc_slave(Config) ->
role = none}]}]} = recv(),
disconnect(Config).
muc_register_nick(Config, MUC, PrevNick, Nick) ->
{Registered, PrevNickVals} = if PrevNick /= <<"">> ->
{true, [PrevNick]};
true ->
{false, []}
end,
%% Request register form
#iq{type = result,
sub_els = [#register{registered = Registered,
xdata = #xdata{type = form,
fields = FsWithoutNick}}]} =
send_recv(Config, #iq{type = get, to = MUC,
sub_els = [#register{}]}),
%% Check if 'nick' field presents
#xdata_field{type = 'text-single',
var = <<"nick">>,
values = PrevNickVals} =
lists:keyfind(<<"nick">>, #xdata_field.var, FsWithoutNick),
X = #xdata{type = submit,
fields = [#xdata_field{var = <<"nick">>, values = [Nick]}]},
%% Submitting form
#iq{type = result, sub_els = [_|_]} =
send_recv(Config, #iq{type = set, to = MUC,
sub_els = [#register{xdata = X}]}),
%% Check if the nick was registered
#iq{type = result,
sub_els = [#register{registered = true,
xdata = #xdata{type = form,
fields = FsWithNick}}]} =
send_recv(Config, #iq{type = get, to = MUC,
sub_els = [#register{}]}),
#xdata_field{type = 'text-single', var = <<"nick">>,
values = [Nick]} =
lists:keyfind(<<"nick">>, #xdata_field.var, FsWithNick).
muc_register_master(Config) ->
MUC = muc_jid(Config),
%% Register nick "master1"
muc_register_nick(Config, MUC, <<"">>, <<"master1">>),
%% Unregister nick "master1" via jabber:register
#iq{type = result, sub_els = [_|_]} =
send_recv(Config, #iq{type = set, to = MUC,
sub_els = [#register{remove = true}]}),
%% Register nick "master2"
muc_register_nick(Config, MUC, <<"">>, <<"master2">>),
%% Now register nick "master"
muc_register_nick(Config, MUC, <<"master2">>, <<"master">>),
disconnect(Config).
muc_register_slave(Config) ->
MUC = muc_jid(Config),
%% Trying to register occupied nick "master"
X = #xdata{type = submit,
fields = [#xdata_field{var = <<"nick">>,
values = [<<"master">>]}]},
#iq{type = error} =
send_recv(Config, #iq{type = set, to = MUC,
sub_els = [#register{xdata = X}]}),
disconnect(Config).
announce_master(Config) ->
MyJID = my_jid(Config),
ServerJID = server_jid(Config),
MotdJID = jlib:jid_replace_resource(ServerJID, <<"announce/motd">>),
MotdText = #text{data = <<"motd">>},
send(Config, #presence{}),
#presence{from = MyJID} = recv(),
%% Set message of the day
send(Config, #message{to = MotdJID, body = [MotdText]}),
%% Receive this message back
#message{from = ServerJID, body = [MotdText]} = recv(),
disconnect(Config).
announce_slave(Config) ->
MyJID = my_jid(Config),
ServerJID = server_jid(Config),
MotdDelJID = jlib:jid_replace_resource(ServerJID, <<"announce/motd/delete">>),
MotdText = #text{data = <<"motd">>},
send(Config, #presence{}),
?recv2(#presence{from = MyJID},
#message{from = ServerJID, body = [MotdText]}),
%% Delete message of the day
send(Config, #message{to = MotdDelJID}),
disconnect(Config).
offline_master(Config) ->
Peer = ?config(slave, Config),
LPeer = jlib:jid_remove_resource(Peer),
@@ -1408,6 +1557,43 @@ carbons_slave(Config) ->
#presence{from = Peer, type = unavailable} = recv(),
disconnect(Config).
client_state_master(Config) ->
Peer = ?config(slave, Config),
Presence = #presence{to = Peer},
Message = #message{to = Peer, thread = <<"1">>,
sub_els = [#chatstate{type = active}]},
wait_for_slave(Config),
%% Should be queued (but see below):
send(Config, Presence),
%% Should be sent immediately, together with the previous presence:
send(Config, Message#message{body = [#text{data = <<"body">>}]}),
%% Should be dropped:
send(Config, Message),
%% Should be queued (but see below):
send(Config, Presence),
%% Should replace the previous presence in the queue:
send(Config, Presence#presence{type = unavailable}),
wait_for_slave(Config),
%% Should be sent immediately, as the client is active again.
send(Config, Message),
disconnect(Config).
client_state_slave(Config) ->
true = ?config(csi, Config),
Peer = ?config(master, Config),
send(Config, #csi{type = inactive}),
wait_for_master(Config),
#presence{from = Peer, sub_els = [#vcard_xupdate{}|_]} = recv(),
#message{from = Peer, thread = <<"1">>, sub_els = [#chatstate{type = active}],
body = [#text{data = <<"body">>}]} = recv(),
wait_for_master(Config),
send(Config, #csi{type = active}),
?recv2(#presence{from = Peer, type = unavailable,
sub_els = [#delay{}, #legacy_delay{}]},
#message{from = Peer, thread = <<"1">>,
sub_els = [#chatstate{type = active}]}),
disconnect(Config).
%%%===================================================================
%%% Aux functions
%%%===================================================================
@@ -1520,29 +1706,12 @@ split(Data) ->
clear_riak_tables(Config) ->
User = ?config(user, Config),
Server = ?config(server, Config),
Slave = jlib:make_jid(<<"test_slave">>, Server, <<>>),
Master = jlib:make_jid(<<"test_master">>, Server, <<>>),
Room = muc_room_jid(Config),
{U, S, _} = jlib:jid_tolower(jlib:make_jid(User, Server, <<>>)),
{USlave, SSlave, _} = LSlave = jlib:jid_tolower(Slave),
{UMaster, SMaster, _} = LMaster = jlib:jid_tolower(Master),
{URoom, SRoom, _} = jlib:jid_tolower(jlib:jid_remove_resource(Room)),
US = {U, S},
USSlave = {USlave, SSlave},
USMaster = {UMaster, SMaster},
USRoom = {URoom, SRoom},
ok = ejabberd_riak:delete(roster, {USlave, SSlave, LMaster}),
ok = ejabberd_riak:delete(roster, {UMaster, SMaster, LSlave}),
ok = ejabberd_riak:delete(passwd, US),
ok = ejabberd_riak:delete(passwd, USSlave),
ok = ejabberd_riak:delete(passwd, USMaster),
ok = ejabberd_riak:delete(roster_version, USSlave),
ok = ejabberd_riak:delete(roster_version, USMaster),
ok = ejabberd_riak:delete(last_activity, US),
ok = ejabberd_riak:delete(last_activity, USSlave),
ok = ejabberd_riak:delete(last_activity, USMaster),
ok = ejabberd_riak:delete(vcard, US),
ok = ejabberd_riak:delete(privacy, US),
ok = ejabberd_riak:delete(private_storage, {U, S, <<"storage:bookmarks">>}),
ok = ejabberd_riak:delete(muc_room, USRoom),
{URoom, SRoom, _} = jlib:jid_tolower(Room),
ejabberd_auth:remove_user(User, Server),
ejabberd_auth:remove_user(<<"test_slave">>, Server),
ejabberd_auth:remove_user(<<"test_master">>, Server),
mod_muc:forget_room(Server, URoom, SRoom),
ejabberd_riak:delete(muc_registered, {{<<"test_slave">>, Server}, SRoom}),
ejabberd_riak:delete(muc_registered, {{<<"test_master">>, Server}, SRoom}),
Config.
+15
View File
@@ -11,6 +11,7 @@ host_config:
modules:
mod_announce:
db_type: odbc
access: local
mod_blocking:
db_type: odbc
mod_caps:
@@ -39,6 +40,8 @@ host_config:
db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
db_type: odbc
mod_adhoc: []
mod_configure: []
mod_disco: []
@@ -64,6 +67,7 @@ Welcome to this XMPP server."
modules:
mod_announce:
db_type: odbc
access: local
mod_blocking:
db_type: odbc
mod_caps:
@@ -92,6 +96,8 @@ Welcome to this XMPP server."
db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
db_type: odbc
mod_adhoc: []
mod_configure: []
mod_disco: []
@@ -110,6 +116,7 @@ Welcome to this XMPP server."
modules:
mod_announce:
db_type: internal
access: local
mod_blocking:
db_type: internal
mod_caps:
@@ -138,8 +145,13 @@ Welcome to this XMPP server."
db_type: internal
mod_vcard:
db_type: internal
mod_vcard_xupdate:
db_type: internal
mod_carboncopy:
db_type: internal
mod_client_state:
drop_chat_states: true
queue_presence: true
mod_adhoc: []
mod_configure: []
mod_disco: []
@@ -158,6 +170,7 @@ Welcome to this XMPP server."
modules:
mod_announce:
db_type: riak
access: local
mod_blocking:
db_type: riak
mod_caps:
@@ -178,6 +191,8 @@ Welcome to this XMPP server."
db_type: riak
mod_vcard:
db_type: riak
mod_vcard_xupdate:
db_type: riak
mod_adhoc: []
mod_configure: []
mod_disco: []
+2
View File
@@ -155,6 +155,8 @@ wait_auth_SASL_result(Config) ->
lists:foldl(
fun(#feature_sm{}, ConfigAcc) ->
set_opt(sm, true, ConfigAcc);
(#feature_csi{}, ConfigAcc) ->
set_opt(csi, true, ConfigAcc);
(_, ConfigAcc) ->
ConfigAcc
end, Config, Fs);
+78
View File
@@ -0,0 +1,78 @@
#!/bin/sh
set -e
set -u
export PATH="/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:$PATH"
deps_dir='deps'
rebar_script='rebar.config.script'
temp_file=$(mktemp "$rebar_script.XXXXXX")
trap 'rm -f $temp_file' EXIT INT TERM
die()
{
echo >&2 "FATAL: $@."
exit 1
}
get_dep_list()
{
sed -n \
'/.*{ *\([^,]*\),[^,]*, *{git, *"\([^"]*\)".*/ {
s//\1,\2/
p
}' "$rebar_script"
}
get_dep_name()
{
printf '%s' "${1%%,*}"
}
get_dep_url()
{
printf '%s' "${1#*,}"
}
get_dep_rev()
{
dep_name=$(get_dep_name "$1")
dep_dir="$deps_dir/$dep_name"
test -d "$dep_dir" || clone_repo "$dep"
cd "$dep_dir"
printf '%s' "$(git rev-parse --verify HEAD)"
cd "$OLDPWD"
}
clone_repo()
{
dep_name=$(get_dep_name "$1")
dep_url=$(get_dep_url "$1")
cd "$deps_dir"
git clone -q "$dep_url" "$dep_name"
cd "$OLDPWD"
}
edit_rebar_script()
{
dep_name=$(get_dep_name "$1")
dep_url=$(get_dep_url "$1")
dep_rev=$(get_dep_rev "$1")
echo "Using revision $dep_rev of $dep_name"
sed "s|\"$dep_url\"[^}]*}|\"$dep_url\", \"$dep_rev\"}|" \
"$rebar_script" >"$temp_file"
mv "$temp_file" "$rebar_script"
}
test -e "$rebar_script" || die 'Please change to ejabberd source directory'
test -d "$deps_dir" || mkdir -p "$deps_dir"
for dep in $(get_dep_list)
do
edit_rebar_script "$dep"
done
+10410 -7651
View File
File diff suppressed because it is too large Load Diff
+60 -43
View File
@@ -1,16 +1,24 @@
%% Created automatically by XML generator (xml_gen.erl)
%% Source: xmpp_codec.spec
-record(chatstate, {type :: active | composing | gone | inactive | paused}).
-record(csi, {type :: active | inactive}).
-record(feature_register, {}).
-record(sasl_success, {text :: any()}).
-record(text, {lang :: binary(),
data :: binary()}).
-record(streamhost, {jid :: any(),
host :: binary(),
port = 1080 :: non_neg_integer()}).
-record(sm_resume, {h :: non_neg_integer(),
previd :: binary()}).
previd :: binary(),
xmlns :: binary()}).
-record(carbons_enable, {}).
@@ -37,18 +45,21 @@
from :: any(),
to :: any()}).
-record(sm_a, {h :: non_neg_integer()}).
-record(sm_a, {h :: non_neg_integer(),
xmlns :: binary()}).
-record(starttls_proceed, {}).
-record(sm_resumed, {h :: non_neg_integer(),
previd :: binary()}).
previd :: binary(),
xmlns :: binary()}).
-record(forwarded, {delay :: #delay{},
sub_els = [] :: [any()]}).
-record(sm_enable, {max :: non_neg_integer(),
resume = false :: any()}).
resume = false :: any(),
xmlns :: binary()}).
-record(starttls_failure, {}).
@@ -60,7 +71,7 @@
-record(p1_ack, {}).
-record(feature_sm, {}).
-record(feature_sm, {xmlns :: binary()}).
-record(pubsub_item, {id :: binary(),
xml_els = [] :: [any()]}).
@@ -81,7 +92,10 @@
node :: binary(),
publisher :: binary()}).
-record(sm_r, {}).
-record(sm_r, {xmlns :: binary()}).
-record(muc_actor, {jid :: any(),
nick :: binary()}).
-record(stat, {name :: binary(),
units :: binary(),
@@ -102,7 +116,8 @@
-record(sm_enabled, {id :: binary(),
location :: binary(),
max :: non_neg_integer(),
resume = false :: any()}).
resume = false :: any(),
xmlns :: binary()}).
-record(pubsub_event_items, {node :: binary(),
retract = [] :: [binary()],
@@ -120,6 +135,8 @@
-record(p1_push, {}).
-record(feature_csi, {xmlns :: binary()}).
-record(legacy_delay, {stamp :: binary(),
from :: any()}).
@@ -150,8 +167,15 @@
subid :: binary(),
type :: 'none' | 'pending' | 'subscribed' | 'unconfigured'}).
-record(muc_actor, {jid :: any(),
nick :: binary()}).
-record(muc_item, {actor :: #muc_actor{},
continue :: binary(),
reason :: binary(),
affiliation :: 'admin' | 'member' | 'none' | 'outcast' | 'owner',
role :: 'moderator' | 'none' | 'participant' | 'visitor',
jid :: any(),
nick :: binary()}).
-record(muc_admin, {items = [] :: [#muc_item{}]}).
-record(shim, {headers = [] :: [{binary(),'undefined' | binary()}]}).
@@ -192,9 +216,6 @@
notify = false :: any(),
items = [] :: [#pubsub_item{}]}).
-record(text, {lang :: binary(),
data :: binary()}).
-record(vcard_geo, {lat :: binary(),
lon :: binary()}).
@@ -224,14 +245,6 @@
-record(bind, {jid :: any(),
resource :: any()}).
-record(muc_item, {actor :: #muc_actor{},
continue :: binary(),
reason :: binary(),
affiliation :: 'admin' | 'member' | 'none' | 'outcast' | 'owner',
role :: 'moderator' | 'none' | 'participant' | 'visitor',
jid :: any(),
nick :: binary()}).
-record(muc_user, {decline :: #muc_decline{},
destroy :: #muc_user_destroy{},
invites = [] :: [#muc_invite{}],
@@ -239,7 +252,7 @@
status_codes = [] :: [pos_integer()],
password :: binary()}).
-record(muc_admin, {items = [] :: [#muc_item{}]}).
-record(vcard_xupdate, {photo :: binary()}).
-record(carbons_disable, {}).
@@ -288,27 +301,6 @@
nick :: binary(),
password :: binary()}).
-record(register, {registered = false :: boolean(),
remove = false :: boolean(),
instructions :: binary(),
username :: 'none' | binary(),
nick :: 'none' | binary(),
password :: 'none' | binary(),
name :: 'none' | binary(),
first :: 'none' | binary(),
last :: 'none' | binary(),
email :: 'none' | binary(),
address :: 'none' | binary(),
city :: 'none' | binary(),
state :: 'none' | binary(),
zip :: 'none' | binary(),
phone :: 'none' | binary(),
url :: 'none' | binary(),
date :: 'none' | binary(),
misc :: 'none' | binary(),
text :: 'none' | binary(),
key :: 'none' | binary()}).
-record(bookmark_url, {name :: binary(),
url :: binary()}).
@@ -371,6 +363,28 @@
items :: #pubsub_items{},
retract :: #pubsub_retract{}}).
-record(register, {registered = false :: boolean(),
remove = false :: boolean(),
instructions :: binary(),
username :: 'none' | binary(),
nick :: 'none' | binary(),
password :: 'none' | binary(),
name :: 'none' | binary(),
first :: 'none' | binary(),
last :: 'none' | binary(),
email :: 'none' | binary(),
address :: 'none' | binary(),
city :: 'none' | binary(),
state :: 'none' | binary(),
zip :: 'none' | binary(),
phone :: 'none' | binary(),
url :: 'none' | binary(),
date :: 'none' | binary(),
misc :: 'none' | binary(),
text :: 'none' | binary(),
key :: 'none' | binary(),
xdata :: #xdata{}}).
-record(disco_info, {node :: binary(),
identities = [] :: [#identity{}],
features = [] :: [binary()],
@@ -378,7 +392,8 @@
-record(sasl_mechanisms, {list = [] :: [binary()]}).
-record(sm_failed, {reason :: atom() | #gone{} | #redirect{}}).
-record(sm_failed, {reason :: atom() | #gone{} | #redirect{},
xmlns :: binary()}).
-record(error, {type :: 'auth' | 'cancel' | 'continue' | 'modify' | 'wait',
by :: binary(),
@@ -467,3 +482,5 @@
-record(time, {tzo :: any(),
utc :: any()}).
+86 -19
View File
@@ -944,8 +944,10 @@
'$username', '$nick', '$password', '$name',
'$first', '$last', '$email', '$address',
'$city', '$state', '$zip', '$phone', '$url',
'$date', '$misc', '$text', '$key'},
refs = [#ref{name = register_registered, min = 0, max = 1,
'$date', '$misc', '$text', '$key', '$xdata'},
refs = [#ref{name = xdata, min = 0, max = 1,
label = '$xdata'},
#ref{name = register_registered, min = 0, max = 1,
default = false, label = '$registered'},
#ref{name = register_remove, min = 0, max = 1,
default = false, label = '$remove'},
@@ -1484,6 +1486,18 @@
label = '$categories'},
#ref{name = vcard_CLASS, min = 0, max = 1, label = '$class'}]}).
-xml(vcard_xupdate_photo,
#elem{name = <<"photo">>,
xmlns = <<"vcard-temp:x:update">>,
result = '$cdata'}).
-xml(vcard_xupdate,
#elem{name = <<"x">>,
xmlns = <<"vcard-temp:x:update">>,
result = {vcard_xupdate, '$photo'},
refs = [#ref{name = vcard_xupdate_photo, min = 0, max = 1,
label = '$photo'}]}).
-xml(xdata_field_required,
#elem{name = <<"required">>,
xmlns = <<"jabber:x:data">>,
@@ -1755,6 +1769,33 @@
result = {shim, '$headers'},
refs = [#ref{name = shim_header, label = '$headers'}]}).
-record(chatstate, {type :: active | composing | gone | inactive | paused}).
-xml(chatstate_active,
#elem{name = <<"active">>,
xmlns = <<"http://jabber.org/protocol/chatstates">>,
result = {chatstate, active}}).
-xml(chatstate_composing,
#elem{name = <<"composing">>,
xmlns = <<"http://jabber.org/protocol/chatstates">>,
result = {chatstate, composing}}).
-xml(chatstate_gone,
#elem{name = <<"gone">>,
xmlns = <<"http://jabber.org/protocol/chatstates">>,
result = {chatstate, gone}}).
-xml(chatstate_inactive,
#elem{name = <<"inactive">>,
xmlns = <<"http://jabber.org/protocol/chatstates">>,
result = {chatstate, inactive}}).
-xml(chatstate_paused,
#elem{name = <<"paused">>,
xmlns = <<"http://jabber.org/protocol/chatstates">>,
result = {chatstate, paused}}).
-xml(delay,
#elem{name = <<"delay">>,
xmlns = <<"urn:xmpp:delay">>,
@@ -2068,18 +2109,38 @@
refs = [#ref{name = forwarded, min = 1,
max = 1, label = '$forwarded'}]}).
-xml(feature_csi,
#elem{name = <<"csi">>,
xmlns = <<"urn:xmpp:csi:0">>,
result = {feature_csi, '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}]}).
-record(csi, {type :: active | inactive}).
-xml(csi_active,
#elem{name = <<"active">>,
xmlns = <<"urn:xmpp:csi:0">>,
result = {csi, active}}).
-xml(csi_inactive,
#elem{name = <<"inactive">>,
xmlns = <<"urn:xmpp:csi:0">>,
result = {csi, inactive}}).
-xml(feature_sm,
#elem{name = <<"sm">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {feature_sm}}).
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {feature_sm, '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}]}).
-xml(sm_enable,
#elem{name = <<"enable">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_enable, '$max', '$resume'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_enable, '$max', '$resume', '$xmlns'},
attrs = [#attr{name = <<"max">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"xmlns">>},
#attr{name = <<"resume">>,
default = false,
dec = {dec_bool, []},
@@ -2087,10 +2148,11 @@
-xml(sm_enabled,
#elem{name = <<"enabled">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_enabled, '$id', '$location', '$max', '$resume'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_enabled, '$id', '$location', '$max', '$resume', '$xmlns'},
attrs = [#attr{name = <<"id">>},
#attr{name = <<"location">>},
#attr{name = <<"xmlns">>},
#attr{name = <<"max">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
@@ -2101,44 +2163,49 @@
-xml(sm_resume,
#elem{name = <<"resume">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_resume, '$h', '$previd'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_resume, '$h', '$previd', '$xmlns'},
attrs = [#attr{name = <<"h">>,
required = true,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"xmlns">>},
#attr{name = <<"previd">>,
required = true}]}).
-xml(sm_resumed,
#elem{name = <<"resumed">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_resumed, '$h', '$previd'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_resumed, '$h', '$previd', '$xmlns'},
attrs = [#attr{name = <<"h">>,
required = true,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"xmlns">>},
#attr{name = <<"previd">>,
required = true}]}).
-xml(sm_r,
#elem{name = <<"r">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_r}}).
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_r, '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}]}).
-xml(sm_a,
#elem{name = <<"a">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_a, '$h'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_a, '$h', '$xmlns'},
attrs = [#attr{name = <<"h">>,
required = true,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}}]}).
enc = {enc_int, []}},
#attr{name = <<"xmlns">>}]}).
-xml(sm_failed,
#elem{name = <<"failed">>,
xmlns = <<"urn:xmpp:sm:3">>,
result = {sm_failed, '$reason'},
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
result = {sm_failed, '$reason', '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}],
refs = [#ref{name = error_bad_request,
min = 0, max = 1, label = '$reason'},
#ref{name = error_conflict,
-1
View File
@@ -26,7 +26,6 @@
{zlib, @zlib@}.
{riak, @riak@}.
{json, @json@}.
{http, @http@}.
{lager, @lager@}.
{iconv, @iconv@}.