Compare commits

..

226 Commits

Author SHA1 Message Date
Christophe Romain 0bd4d1aade Update mix.exs version 2018-04-25 12:30:36 +02:00
Paweł Chmielowski d49aa429ca Update deps 2018-04-25 10:45:18 +02:00
Christophe Romain 316a19d600 Merge pull request #2399 from 4z3/ejabberdctl-fix-parser
ejabberdctl: fix parameters parsing
2018-04-25 10:39:19 +02:00
tv c43037887a ejabberdctl: fix parameter parsing 2018-04-25 00:41:30 +02:00
Holger Weiss 538e0d4844 misc: Catch all Base64 decoding errors 2018-04-24 18:29:10 +02:00
Holger Weiss f3795e9d03 mod_http_upload: Add MIME type for M4A files 2018-04-24 18:16:16 +02:00
Christophe Romain 3df919244c PubSub purge_node must use a transaction (#2231) 2018-04-24 15:58:56 +02:00
Christophe Romain 67773c5174 Merge branch 'master' of github.com:processone/ejabberd 2018-04-24 14:44:58 +02:00
Christophe Romain 61dee97738 Pubsub creation/modification use varchar (#2397) 2018-04-24 14:44:52 +02:00
Evgeniy Khramtsov 6774418a7f Introduce new mod_muc option: access_register
The option is an ACL rule defining who is able to register
nicknames within the conference service. The default is `all`
(for backward compatibility).
2018-04-24 12:29:59 +03:00
Evgeniy Khramtsov ad6fcc7865 Get rid of useless memory/disk usage warnings 2018-04-24 12:12:48 +03:00
Evgeniy Khramtsov ca28faa51a Fix get_affiliation/2 2018-04-24 12:07:10 +03:00
Paweł Chmielowski 5b730cdbf2 Use httpc directly instead of using p1_http wrapper 2018-04-23 17:40:44 +02:00
Paweł Chmielowski 9ed0357760 Use correct headers in rest calls 2018-04-23 12:29:56 +02:00
Evgeniy Khramtsov 06ce884aa8 Add stubs for affiliation-specific backend callbacks 2018-04-23 11:35:43 +03:00
Paweł Chmielowski 3fc0eb4f5b Use correct db backend for remove_mam_for_user_with_peer 2018-04-20 14:06:23 +02:00
Paweł Chmielowski 3bfa683586 Fix mnesia call in mam archive management function 2018-04-20 13:36:54 +02:00
Paweł Chmielowski 5be49cc0fa Add commands for cleaning up mam archive 2018-04-20 13:27:46 +02:00
Christophe Romain 42c029d5f7 Fix type of rest:url/2 2018-04-19 13:21:33 +02:00
Christophe Romain a567abcfdf Fix deprecated call injected by 265c7b62 2018-04-18 14:16:56 +02:00
Christophe Romain 265c7b62c7 Add flexibility on rest url config 2018-04-18 13:16:08 +02:00
Holger Weiss 332567693c mod_push_keepalive: Reset timeout on messages only
Some mobile apps might only be notified on actual chat messages with
a body, so don't let mod_push_keepalive reset the stream management
timeout on other types of traffic.
2018-04-17 00:27:07 +02:00
Holger Weiss de7dc4affa mod_push: Optionally include message sender/body
Add 'include_sender' and 'include_body' options.  If one or both of them
are set to 'true', a urn:xmpp:push:summary form with the enabled
field(s) is included in push notifications that are generated for
messages with a body.

The 'include_body' option can instead be set to a static text.  In this
case, the specified text will be included in place of the actual message
body.  This can be useful to signal the push service whether the
notification was triggered by a message with body (as opposed to other
types of traffic) without leaking actual message contents.
2018-04-16 23:18:03 +02:00
Holger Weiss 48c5ab59f1 mod_http_upload*: Remove empty lines after specs
Remove blank lines following function specifications in mod_http_upload
and mod_http_upload_quota for consistency with other modules.
2018-04-16 18:22:54 +02:00
Holger Weiss b2855d63a7 mod_http_upload*: Add function specifications 2018-04-16 18:17:28 +02:00
Holger Weiss 0282cf64a0 mod_push: Add function specification 2018-04-16 18:14:07 +02:00
Holger Weiss e5cb9dad40 mod_push: Add/adjust debug messages 2018-04-16 18:12:46 +02:00
Evgeniy Khramtsov ec819b4002 Update MUC MAM tests 2018-04-16 16:10:44 +03:00
Evgeniy Khramtsov acc162f4f4 Carefully validate options list 2018-04-16 15:48:06 +03:00
Evgeniy Khramtsov b8505f3e78 Don't crash on invalid module's sub-options
Fixes #2387
2018-04-16 11:06:57 +03:00
Holger Weiss 8a71e2e4f7 mod_push: Don't notify on stream errors
If a pending stream management session is closed with a stream error,
this is usually due to the client opening a new stream that conflicts
with the old one.  Don't generate a push notification in this situation.
2018-04-16 01:08:56 +02:00
Evgeniy Khramtsov a5284229cb Merge branch 'muc-self-presence' 2018-04-14 18:32:12 +03:00
Evgeniy Khramtsov d0f36537fb Clear fast_tls cache on configuration reload 2018-04-13 11:10:20 +03:00
Holger Weiss 3cf4fbc7b0 mod_roster: Use 'lserver' for configuration lookup 2018-04-13 00:12:07 +02:00
Paweł Chmielowski fe4b1a492c Fix notification payload generated by pubsub 2018-04-12 18:02:32 +02:00
Paweł Chmielowski c3b4b4ce4f Pass access option from websocket to c2s
This fixes issue #2223
2018-04-12 17:42:59 +02:00
Paweł Chmielowski 95244c3b6f Fix csi tests 2018-04-12 17:08:27 +02:00
a-iv 89d91b609a New schema support for tests. (#2355) 2018-04-12 15:42:43 +02:00
Christophe Romain d28064518b Improve pubsub#itemreply implementation (#2325) 2018-04-12 15:38:12 +02:00
Evgeniy Khramtsov 7627575856 Update the xmpp dependency to support 'parent' attribute
Fixes #2375
2018-04-11 09:34:06 +03:00
Christophe Romain 99444f2d0e Fix illegal match on previous commit 2018-04-10 15:02:03 +02:00
Christophe Romain 4c0f87b2ff Improve fix for #2288, don't mask errors on get_item 2018-04-10 14:47:18 +02:00
Holger Weiss 54363f8476 gen_mod: Support global module processes 2018-04-04 18:25:19 +02:00
Holger Weiss 094f586811 gen_mod: Remove frontend process support
ejabberd doesn't support frontend processes anymore.
2018-04-04 18:22:59 +02:00
Paweł Chmielowski 45a3c7e0ce Improve mod_multicast 2018-04-04 12:06:35 +02:00
Holger Weiss e2652ce02f mod_http_upload: Accept characters of any script
Accept all alphanumeric characters of any script in user and file names
rather than replacing non-ASCII characters with underscores.  However,
non-alphanumeric characters are still replaced, except for "." and "-".

Closes #2346.
2018-04-03 21:00:15 +02:00
Holger Weiss df651d893e Remove old hex conversion functions
Depend on list_to_integer/2 and integer_to_list/2 being available.
2018-04-03 00:21:33 +02:00
Holger Weiss a2e1f5c882 Move ejabberd_http:url_encode/1 to 'misc' module 2018-04-03 00:12:43 +02:00
Badlop 7f5796fe31 Fix Code format when logging a MUC room kick/ban 2018-04-02 13:51:19 +02:00
Holger Weiss 5f1191b9f5 mod_client_state: Add 'csi_activity' hook
Closes #2358.
2018-04-01 17:13:04 +02:00
Evgeny Khramtsov 0041a11c4a Merge pull request #2357 from Pouriya-Jahanbakhsh/component-send-packet-hook
feat: add hook for sending packet from component
2018-03-30 21:51:03 +03:00
Pouriya Jahanbakhsh e17a16a300 fix: run 'component_send_packet' hook in global mode 2018-03-30 23:19:33 +04:30
Pouriya Jahanbakhsh 7b3d26992b feat: add hook for sending packet from component
New hook 'component_send_packet' added.
Callback function must accept one argument {Pkt, ComponentState} and should yield 'drop' or {NewPkt, NewComponentState}.
2018-03-30 21:31:30 +04:30
Evgeniy Khramtsov 9373ad20ca Don't produce a crash dump during intentional exit
Also halt faster without relying on timeouts for buffers flushing
2018-03-29 12:14:31 +03:00
Evgeniy Khramtsov b283cfa6f2 Remove unused variable 2018-03-29 10:34:09 +03:00
Evgeny Khramtsov a84771fd14 Merge pull request #2351 from rom1dep/config-order_c2s-direct
config: move section about direct-tls for c2s just under regular c2s config
2018-03-29 08:32:13 +03:00
Romain DEP. 2bb6782bee config: move section about direct-tls for c2s just under regular c2s config (to ease parameters comparison) 2018-03-28 23:17:43 +02:00
Mickael Remond ae151927ae Add support for PATCH http method
This is needed to improve out APIs.
2018-03-28 17:34:47 +02:00
Paweł Chmielowski dfbdffad44 Fix process_discoitems_result in mod_multicast 2018-03-28 11:23:28 +02:00
Paweł Chmielowski d71bc73271 Update eimp 2018-03-26 16:18:29 +02:00
Evgeniy Khramtsov ea9c3fd8f7 Fix returning value from mod_vcard_ldap's search() callback
Fixes #2335
2018-03-25 10:53:46 +03:00
Christophe Romain f3b3bffec0 Get rid of 'fs' package dependency in mix 2018-03-23 14:48:17 +01:00
Evgeniy Khramtsov f39dbe6e49 Get rid of 'fs' package dependency
Certificates auto-reloading will be fixed later.
For now to reload certificates call `reload-config` ejabberd command.
2018-03-23 16:40:26 +03:00
Evgeniy Khramtsov 75450a62b3 Clarify the statement about mod_http_upload thumbnails 2018-03-23 16:19:13 +03:00
Evgeniy Khramtsov a15039638b Force node config for bookmarks 2018-03-23 16:16:27 +03:00
Evgeniy Khramtsov 50de427570 Set empty least as a default for force_node_config 2018-03-23 16:09:18 +03:00
Evgeniy Khramtsov 55604b2d97 Move force_node_config defaults into ejabberd.yml.example 2018-03-23 16:08:12 +03:00
Paweł Chmielowski 92bc5dc85b Update mix deps 2018-03-23 13:27:58 +01:00
Christophe Romain baf2473688 Update mix.exs version 2018-03-23 12:56:17 +01:00
Evgeniy Khramtsov f5bab5d6c4 Fetch 'registration_watchers' option from the correct module
Fixes #2337
2018-03-23 09:42:55 +03:00
Paweł Chmielowski b23be02dfe Update deps 2018-03-22 16:15:19 +01:00
Christophe Romain 0bda169a5a Remove items of unregistered user (#2129) 2018-03-22 11:59:24 +01:00
Paweł Chmielowski 96c183c04b Accept atoms in api_permission command lists and commands with numbers in them 2018-03-21 12:53:46 +01:00
Badlop b293e99aee Add option --enable-group=xxx (thanks to andreabenini)(#1429) 2018-03-20 23:39:07 +01:00
Badlop b4b3ff50d6 If mod_last is disabled, return error instead of crashing (#2330) 2018-03-20 22:18:43 +01:00
Evgeniy Khramtsov 8962397cf3 Report meaningful error when luerl is not available 2018-03-19 20:09:35 +03:00
Evgeniy Khramtsov b1ecd8ac01 Set -protocol() directive for mod_avatar 2018-03-19 19:23:15 +03:00
Evgeniy Khramtsov 4f1d7c4b66 Avoid logging IP addresses in mod_register when it's not desired
Fixes #2326
2018-03-19 18:23:52 +03:00
Evgeniy Khramtsov bb20e5f3fa Apply some dirty hacks for mod_shared_roster
Probably this fixes #1846
2018-03-19 12:32:43 +03:00
Evgeniy Khramtsov e1e7986918 Hardcode ACL rules used by ejabberd_web_admin 2018-03-19 12:29:07 +03:00
Christophe Romain 7ba6fae67c Improve result of XEP-0060 §6.5.9.12 (#2288)
Moving get_item result control to keep item-not-found on transaction
error when node does not exists
2018-03-17 10:59:34 +01:00
Christophe Romain 7beb19b01e Fix result of XEP-0060 §6.5.9.12 (#2288) 2018-03-16 17:48:42 +01:00
Christophe Romain a84dd0f627 Add transient notification condition (#2267) 2018-03-16 17:26:51 +01:00
Christophe Romain d8f9219b4f Refactor publish_item conditions (#2267) 2018-03-16 17:17:53 +01:00
Evgeniy Khramtsov ddc29d42de Fulfill all requirements of XEP-0398 v0.2.0
These include:
- Avoid rewriting vcard:x:update tags with empty <photo/> element
- Advertise "urn:xmpp:pep-vcard-conversion:0" feature
2018-03-16 12:10:57 +03:00
Alexey Shchepin e15595df64 Add 'new_sql_schema' config option, --enable-new-sql-schema now sets its default value to true (#2239) 2018-03-15 17:55:05 +03:00
Evgeniy Khramtsov 99b41146b1 Get rid of catch-all in mod_pubsub 2018-03-13 22:10:58 +03:00
Evgeniy Khramtsov 6b079c0ab3 Preserve modules order
When modules for some virtual host are about to be started,
they are topologically sorted to preserve dependencies order.
We now keep this order for stop/reload functions to work properly.
2018-03-13 18:18:53 +03:00
badlop c5aea779b4 Merge pull request #2314 from oxpa/mod_admin_extra_hashes
allow using hashes from "crypto" applications in mod_admin_extra
2018-03-12 15:20:53 +01:00
Badlop be33c93344 Return errors in admin#add-user command response stanza (#2321) 2018-03-12 13:33:44 +01:00
Evgeniy Khramtsov 2785f1dfd2 Introduce force_node_config for mod_pubsub
The option can be used to override configuration options of a
particular PubSub node. Example:

mod_pubsub:
  ...
  force_node_config:
    "eu.siacs.conversations.axolotl.*":
      access_model: whitelist
    "*":
      persist_items: true

Fixes #2276
2018-03-11 16:54:35 +03:00
Evgeniy Khramtsov b179874ec6 Add mod_fail2ban to the example config 2018-03-10 21:41:55 +03:00
Evgeniy Khramtsov c3eeb8624b Strip duplicates from module's options 2018-03-07 17:46:16 +03:00
Holger Weiss bc808ffcde mod_stream_mgmt: Clean up on timed out resumption
During resumption, make sure the old process and the corresponding
session entry are disposed also in the case where the call that queries
the old process times out.
2018-03-06 21:03:31 +01:00
Evgeniy Khramtsov d9bf5a6865 Don't forget to remove mgmt_force_enqueue flag 2018-03-06 16:18:03 +03:00
Holger Weiss 67fe5d38a7 mod_push_keepalive: Preserve timeout on resumption
Don't forget to carry over the original XEP-0198 resumption timeout
value while resuming.
2018-03-05 01:10:12 +01:00
Evgeniy Khramtsov 63dba3fd64 Merge branch 'master' into muc-self-presence 2018-03-03 21:09:27 +03:00
Evgeniy Khramtsov da1a5036fe Revert "Support for default values in default_room_opts"
This reverts commit 5054a9933f.
The commit is plain wrong: similar options are not required to
be in pair.
2018-03-03 21:08:05 +03:00
Evgeniy Khramtsov 0d3637d18f Simplify ejabberd_sup code 2018-03-03 18:05:12 +03:00
oxpa f2a3118ecc allow using hashes from "crypto" applications in mod_admin_extra 2018-03-02 04:10:30 -09:00
Badlop dbf1cabdcd Fix: mod_offline:store_offline_msg/1 expects a message, not list (#2312) 2018-03-02 11:44:10 +01:00
Badlop ad0fd1eac1 Simplify result of get_room_affiliation command (#2301) 2018-03-01 19:45:16 +01:00
badlop 3003307e60 Merge pull request #2301 from tsaqova/ejabberd_commands_get_room_affiliation
add ejabberd_command to get affiliation of a user in MUC room
2018-03-01 19:37:55 +01:00
Evgeniy Khramtsov 76f827ac83 Increase log level for DIGEST-MD5 FQDN
SASL DIGEST-MD5 anyway deprecated anyway, so not point in
logging this. This is now logged in `debug` mode.
2018-02-28 21:43:43 +03:00
Evgeny Khramtsov b5138a8ddb Merge pull request #2311 from nosnilmot/validate-listen-opts
Validate additional listen opts
2018-02-28 19:37:57 +03:00
Stu Tomlinson da81590fef Validate additional listen opts
The options "inet", "inet6" and "backlog" are valid listen options, but are
currently logged as errors (even though they do work):

2018-02-28 16:08:44.141 [error] <0.338.0>@ejabberd_listener:validate_module_option:630 unknown listen option 'backlog' for 'ejabberd_c2s' will be likely ignored, available options are: access, shaper, certfile, ciphers, dhfile, cafile, client_cafile, protocol_options, tls, tls_compression, starttls, starttls_required, tls_verify, zlib, max_fsm_queue

This adds the necessary validators so they are correctly recognized.
2018-02-28 16:14:35 +00:00
Yusro Tsaqova 8a41cfc0f5 add ejabberd_command to get affiliation of a user in MUC room 2018-02-28 20:22:27 +07:00
Badlop 5054a9933f Support for default values in default_room_opts 2018-02-26 17:22:37 +01:00
Paweł Chmielowski 5912c573ea Use length on fields in mysql indexes 2018-02-26 09:43:04 +01:00
Paweł Chmielowski b2095ebcfe Simplify code for splitting auth string in cyrsasl
This may fix problem from issue #2296
2018-02-26 09:36:56 +01:00
Evgeny Khramtsov 0a67cdfb16 Merge pull request #2304 from weiss/run-auth-callbacks-earlier
Run SASL result callbacks earlier
2018-02-26 09:04:32 +03:00
Holger Weiss c2235860ab xmpp_stream_in: Run auth result callbacks earlier
Call Mod:handle_auth_success/4 and Mod:handle_auth_failure/4 before
sending the SASL response rather than afterwards.  This way, callbacks
can send a custom response and disconnect.
2018-02-26 00:06:35 +01:00
Holger Weiss 22e43ebd8a mod_stream_mgmt: Cope with exit during resumption
Don't crash if the old process exits while it is queried for the session
state.
2018-02-24 21:50:54 +01:00
Evgeny Khramtsov 35be7d2718 Merge pull request #2299 from weiss/add-username-to-indexes
Add username to peer indexes for MAM lookups
2018-02-24 07:41:44 +03:00
Holger Weiss f7566bd00e sql/*: Add username to peer indexes
The username is available for all MAM queries in question, and adding it
to the indexes can improve the lookup performance significantly.
2018-02-24 00:50:20 +01:00
Evgeny Khramtsov 5bf753fd2d Merge pull request #2297 from af8a524db1/move_make_rand_string
Move make_rand_string() to 'randoms' module
2018-02-23 20:35:37 +03:00
Marc Schink c1e5ae5308 Move make_rand_string() to 'randoms' module 2018-02-23 18:32:34 +01:00
Holger Weiss ea87bdfbe5 mod_carboncopy: Apply cosmetic change
The xmpp:has_subtag/2 function returns a boolen() value, so it can be
used with the 'not' operator.
2018-02-22 00:46:47 +01:00
Holger Weiss 7a1ed065fe mod_carboncopy: Copy outgoing MUC PMs
Incoming MUC PMs aren't carbon-copied, as the MUC service usually forks
them.  However, don't suppress copying of outgoing PMs, where no such
forking takes place.
2018-02-22 00:40:09 +01:00
Evgeniy Khramtsov ec0f0f7c72 Move some log messages to debug level 2018-02-21 17:12:50 +03:00
Evgeniy Khramtsov 0acc69e303 Use nicks in disco#items or disco#info report 2018-02-21 10:25:15 +03:00
Evgeniy Khramtsov 4bf4193d55 Add 'negotiation_timeout' to the known options list 2018-02-20 19:47:50 +03:00
Evgeniy Khramtsov d625e24029 Introduce 'negotiation_timeout'
The option can be used to specify a period (in seconds) for a stream
negotiation to complete. If the timer fires, the stream is considered
as failed and the underlying connection gets closed. This is a global
option (you cannot set it per domain) and the default is 30 seconds.
2018-02-20 11:38:00 +03:00
Holger Weiss a875195940 mod_admin_extra: Fix srg_get_info with '@all@'
Don't let the srg_get_info command crash if the roster group has '@all@'
or the '@online@' users as members.
2018-02-20 00:44:47 +01:00
Evgeniy Khramtsov 06c480106f Don't emit validator's warning if the module is not found 2018-02-19 22:07:09 +03:00
Evgeniy Khramtsov e070e6bccb Replace ?MYLANG with connection's language wherever possible 2018-02-19 21:47:20 +03:00
Evgeniy Khramtsov de49e7631f Push blocking related IQs from bare JID
Fixes #2287
2018-02-18 18:00:20 +03:00
Evgeniy Khramtsov 25abf8b634 Don't inject node name inside "id" attribute
Fixes #2284
2018-02-18 11:54:40 +03:00
Evgeniy Khramtsov ff06bdf144 Don't ask other nodes to invalidate cache when the key is not updated 2018-02-18 09:02:23 +03:00
Evgeniy Khramtsov d5afc767e6 Fix 'badmatch' crash
The crash was introduced in 4b012a99d2
2018-02-17 20:06:50 +03:00
Evgeniy Khramtsov 5704a980c5 Introduce 'access' option for mod_block_stranger
The option is supposed to be used when `allow_local_users`
and `allow_transports` are not enough. It's an ACL where `deny`
means the message will be rejected (or a CAPTCHA would be
generated for a presence), and `allow` means the sender is
whitelisted and the stanza will pass through.

The default value is `none`, which means nothing is whitelisted.
2018-02-17 18:53:35 +03:00
Evgeniy Khramtsov cffdb06b66 Cache 'isuser' queries to external auth program 2018-02-16 20:50:22 +03:00
Evgeniy Khramtsov f5d208441d Improve example extauth script 2018-02-16 20:34:09 +03:00
Paweł Chmielowski 71a856deaa Handle gracefully that mnesia is already loaded when starting tests 2018-02-16 16:49:48 +01:00
Christophe Romain 7e1df0752a Export helper function 2018-02-16 09:53:38 +01:00
Christophe Romain f0ccdebf7f Export helper functions 2018-02-16 09:52:29 +01:00
Evgeniy Khramtsov 32e5a3255d Export aux functions from mod_muc_room 2018-02-16 08:28:33 +03:00
Evgeniy Khramtsov c102a45fac Rename some keys and functions for clarity 2018-02-15 15:50:20 +03:00
Evgeniy Khramtsov 52ded14b7f Update incoming stanzas counter on invalid XML 2018-02-15 15:42:55 +03:00
Evgeniy Khramtsov e5ba7c3f3c Better solution for a previous fix 2018-02-15 10:48:59 +03:00
Evgeniy Khramtsov 51aa9d98a7 Don't forget to add invalid XML responses to sending queue 2018-02-15 10:18:06 +03:00
Evgeniy Khramtsov a65500b6aa Fix external components unregistration 2018-02-14 13:09:27 +03:00
Evgeniy Khramtsov 032f796292 Introduce option 'global_routes' for ejabberd_service
The option emulates legacy behaviour which registers all routes
defined in `hosts` on a component connected. This behaviour
is considered harmful in the case when it's desired to multiplex
different components on the same port, so, to disable it,
set `global_routes` to `false`. The default value is `true`,
e.g. legacy behaviour is emulated: the only reason for this is
to maintain backward compatibility with existing deployments.
2018-02-14 11:53:52 +03:00
Evgeniy Khramtsov 516f4d03a1 Fix indentation 2018-02-14 11:42:43 +03:00
Paweł Chmielowski 60a8623929 Change formatting of commands markdown documentation 2018-02-13 16:27:39 +01:00
Christophe Romain 9dbdeba6c1 Add case on create_room, avoid useless call 2018-02-13 11:08:13 +01:00
Evgeniy Khramtsov 4632f5520f Really run use_cache/1 and cache_nodes/1 callbacks for mod_mam 2018-02-13 11:38:41 +03:00
Evgeniy Khramtsov ffe02c46e4 Let a MUC room to route presences from its bare JID
The goal for this is to provide entity capabilities (XEP-0115) and
vCard-based avatar hash (XEP-0153)
2018-02-12 17:37:36 +03:00
Evgeny Khramtsov 42794ce4e4 Merge pull request #2274 from Torxen/feature/fixed-return-value-mod_muc
Issue: mod_muc.erl expect wrong return value
2018-02-12 17:36:47 +03:00
Tobias Koch 9188a7b838 Dialyzer checks always failed because the return value of the function 'get_subscribed_rooms' in 'mod_muc_sql' is different to the defined value in 'mod_muc'. Fix was to update the return value in 'mod_muc.erl'. 2018-02-12 15:12:46 +01:00
Evgeniy Khramtsov 66fc1bf3b6 Remove 'iqdisc' option
Since we got rid of all bottle-neck processes and we have
a connection pool for every database, the option is no longer
needed and in fact is detrimental: in practice what you get
is just a bunch of overloaded processes in the IQ handlers pool
no matter how much you increase the `iqdisc` value.

Given that there are close to zero operators understanding
the meaning of the option and, hence, not using it all,
it's not simply deprecated but completely removed.

The commit also deprecates the following functions:
- gen_iq_handler:add_iq_handler/6
- gen_iq_handler:handle/5
- gen_iq_handler:iqdisc/1
2018-02-11 12:54:15 +03:00
Evgeniy Khramtsov 11a58f8dff Bump xmpp version 2018-02-11 09:45:54 +03:00
Evgeniy Khramtsov 97f913b8d9 Merge branch 'master' of github.com:processone/ejabberd 2018-02-10 11:36:48 +03:00
Evgeniy Khramtsov 6c1a1bd000 Rely on use_cache/1 callback in mod_caps 2018-02-10 11:36:39 +03:00
Holger Weiss 9fb2253aa9 mod_stream_mgmt: Abort connection on count error
If the client acknowledged more stanzas than the server sent, close the
connection with a stream error rather than hiding client bugs by silently
adjusting the server's count.
2018-02-10 00:06:19 +01:00
Evgeniy Khramtsov 672c2f75d3 Introduce option 'validate_stream'
If set to `true`, all incoming XML packets are fully validated
against known schemas. If an error occurs, the packet will be bounced
with the corresponding error reason. The default value is `false`.
The option might be useful to protect client software from sofisticated
bugs related to XML validation as well as for client developers
who want to catch validation errors at early stage of development.

Note that the option might have slight performance impact, so use it
with care on loaded machines.
2018-02-09 18:12:50 +03:00
Evgeniy Khramtsov 5c85106a41 Fix type spec 2018-02-07 22:20:12 +03:00
Badlop c2911222e4 Commands markdown also lack arguments when policy=user 2018-02-07 16:39:18 +01:00
Evgeniy Khramtsov 232b66b0f4 Introduce 'rate_limit' option of mod_avatar
The option controls how many avatars a user can upload per minute.
The option takes positive integer values. The default is 10.
Note that the option only takes effect when an avatar is about
to convert to a different format, i.e. it implies that `convert`
option is configured.
2018-02-05 23:12:36 +03:00
Evgeniy Khramtsov 3327da72a6 Merge branch 'master' of github.com:processone/ejabberd 2018-02-05 15:13:23 +03:00
Evgeniy Khramtsov c65dcfeda7 Export is_online/1 function 2018-02-05 15:13:13 +03:00
Jérôme Sautret 411d1711a0 Merge pull request #2266 from mieciu/master
Fix MySQL ERROR 1170 (42000) 'server_host'
2018-02-05 13:04:25 +01:00
phejman bef7d1ba66 Fix MySQL error server_host used in key specification without a key length 2018-02-05 12:33:59 +01:00
Evgeniy Khramtsov c990abf222 Improve log message when module startup has failed 2018-02-02 13:16:14 +03:00
Paweł Chmielowski 983aaac765 Another try to fix travis 2018-01-31 17:54:32 +01:00
Paweł Chmielowski 0f25e59143 Properly escape string in travis config 2018-01-31 17:10:05 +01:00
Paweł Chmielowski 2a77805072 Print something when doing tests in travis, as not doing that get us killed 2018-01-31 17:03:18 +01:00
Paweł Chmielowski b86402f3e7 Don't run tests in verbose mode on travis 2018-01-31 16:10:09 +01:00
Paweł Chmielowski f6ebbe4c78 Fix compilation ordering in mix by s/-behavior/-behaviour/ 2018-01-31 14:57:43 +01:00
Paweł Chmielowski 485f8e48e5 Fix mod_roster start invocation in elixir tests 2018-01-31 09:34:54 +01:00
Paweł Chmielowski 51f652a1e1 Fix elixir tests 2018-01-30 19:45:12 +01:00
Paweł Chmielowski fc3c605945 Use correct default when getting list of modules from config 2018-01-30 19:44:27 +01:00
Paweł Chmielowski 69de43d5ee Remove line left from debugging 2018-01-30 18:37:17 +01:00
Paweł Chmielowski ad4ffce788 Start ejabberd_config earlier 2018-01-30 18:24:47 +01:00
Paweł Chmielowski cd82a9d534 Disable riak in travis 2018-01-30 15:31:07 +01:00
Paweł Chmielowski 3b646cc2ec Run tests only on backends enabled by configure 2018-01-30 15:30:37 +01:00
Paweł Chmielowski bb58307190 Match all possible values in mod_privilege checks, or we get crashes 2018-01-30 13:10:22 +01:00
Paweł Chmielowski 53870c854e Fix exception in mod_privilege:process_presence_in
This should fix problem reported in issue #2248
2018-01-30 12:52:38 +01:00
Paweł Chmielowski b69fb5aae0 Bump sqlite deps version 2018-01-30 12:51:38 +01:00
Paweł Chmielowski ddf6076328 Fix elixir tests 2018-01-29 18:28:02 +01:00
Paweł Chmielowski d2974cf48a Fix processing of ldap_memberattr_format_re option
This makes sure that "" value is handled as before options processing
overhaul.

This fixed ldap shared roster testcase
2018-01-29 17:25:31 +01:00
Paweł Chmielowski dc601610b6 Don't return undefined from ejabberd_config:get_myhosts() 2018-01-29 11:05:59 +01:00
Paweł Chmielowski 719dfe12f6 Use named functions instead of carrying self as argument 2018-01-29 10:02:20 +01:00
Paweł Chmielowski c47366ba97 Restore original return value to ejabberd_config:add_option 2018-01-29 09:46:11 +01:00
Holger Weiss 1b26c8d214 ejabberdctl: Omit redundant erl option
The "-detached" flag implies "-noinput".
2018-01-29 01:16:20 +01:00
Holger Weiss f31782a252 mod_caps: Only store CAPS if contact is subscribed
If a user is subscribed to a contact but not vice versa, don't store the
contact's CAPS.  This makes sure no PEP items are leaked to the contact.
2018-01-29 01:07:38 +01:00
Holger Weiss d0af61f488 mod_caps: Fix indentation 2018-01-29 00:02:15 +01:00
Evgeniy Khramtsov 795efb2ee1 Improve logging of idle s2s connections 2018-01-28 11:10:22 +03:00
Evgeniy Khramtsov 6e5439db5c Find and fix typos using 'codespell' 2018-01-27 19:35:38 +03:00
Evgeniy Khramtsov 5d582080be Fix a typo in call to create_captcha()
Thanks to Paul Schaub for spotting this
2018-01-26 22:56:49 +03:00
Evgeniy Khramtsov 4b012a99d2 Introduce option 'captcha' for mod_block_strangers
When the option is set to `true`, the module will generate
CAPTCHA challenges for incoming subscription requests. The option
also implies that option `drop` is set to `true`. Note that
the module won't generate CAPTCHA challenges for messages: they
will still be rejected if `drop` is set to `true`.

Fixes #2246
2018-01-26 15:02:06 +03:00
Paweł Chmielowski 7e561dd20a Fix more tests 2018-01-25 18:43:34 +01:00
Paweł Chmielowski 2afdde84ea Adopt tests to changes in xmpp 2018-01-25 18:24:05 +01:00
Evgeniy Khramtsov 2269d290d8 Switch to newer fast_xml and xmpp and update record fields in the code 2018-01-25 20:02:47 +03:00
Paweł Chmielowski 56d4224e08 Handle ignore from xmpp_socket callback start
This should help with issue #2244
2018-01-24 17:32:24 +01:00
Holger Weiss 666da60cba Reduce log level for unexpected XML input
Log a warning rather than an error when, for example, a client sends a
stanza before opening the stream.
2018-01-24 12:04:52 +01:00
Holger Weiss e8f1de8785 mod_block_strangers: Bounce groupchat to bare JID
If a blocked message is of type 'groupchat', address the error message
to the bare JID (rather than sending it as MUC PM).
2018-01-24 11:49:31 +01:00
Evgeniy Khramtsov 1f6c0022dd Fix a typo in mod_caps 2018-01-23 23:20:10 +03:00
Paweł Chmielowski 6cdb7b4468 Fix tag used in luerl dependency 2018-01-23 09:37:46 +01:00
Evgeniy Khramtsov a917f4d451 Merge branch 'mod-default-options' 2018-01-23 11:00:00 +03:00
Evgeniy Khramtsov ba2b650464 Introduce new gen_mod callback: mod_options/1
The callback is supposed to provide known options and their default
values, as long as the documentation. Passing default values into
get_mod functions is now deprecated: all defaults should be provided
by the Mod:mod_options/1 callback.
2018-01-23 10:54:52 +03:00
Christophe Romain 121d12f4d1 Move luerl as optional tools dependency 2018-01-22 17:42:27 +01:00
Christophe Romain 7abd13974b Remove deps override on mix 2018-01-22 17:42:02 +01:00
Holger Weiss ae86af9399 Merge remote-tracking branch 'processone/pr/2238'
* processone/pr/2238:
  Fix typo /this/it/
2018-01-21 15:05:05 +01:00
Licaon_Kter a0c8cac1b6 Fix typo /this/it/ 2018-01-21 13:44:30 +00:00
Holger Weiss 818ff5a263 Update 'pubsub_node' column name for DB export
The 'type' column has been renamed in commit
44700d91ba.
2018-01-19 13:10:14 +01:00
Evgeniy Khramtsov c0ef054f6f Do not try to start ezlib application too frequently
This may overload Erlang applicaton controller
2018-01-16 18:06:31 +03:00
Evgeniy Khramtsov d35a8805b0 Return debug message back 2018-01-16 17:57:21 +03:00
Christophe Romain 1daa7ef785 Sync mix and rebar for eimp dep 2018-01-16 11:45:29 +01:00
Evgeniy Khramtsov 7b0fa7e6e2 Improve validation of 'convert' option 2018-01-16 01:06:20 +03:00
Christophe Romain 8bdccc25ab Include acme deps in release, fixes f2c3fe8ac 2018-01-15 22:21:24 +01:00
Paweł Chmielowski 59b7c89944 Use correct name of new rebar command 2018-01-15 15:21:04 +01:00
Paweł Chmielowski a7639fd4ad Call configure on deps in separate rebar command that compile
This way we rebar.config.script can use vars detected by configure
2018-01-15 12:31:47 +01:00
Paweł Chmielowski 99f8e58eaf Use override syntax that rebar3 accepts 2018-01-15 11:56:20 +01:00
Evgeniy Khramtsov d3aab2ea18 Get rid of a call to misc:have_eimp() 2018-01-15 13:14:51 +03:00
Evgeniy Khramtsov b970c88941 Merge branch 'master' of github.com:processone/ejabberd 2018-01-15 13:00:19 +03:00
Evgeniy Khramtsov 0f86559d83 Always build eimp dependency
Even if no suitable C graphics libraries are detected
at compile time, the package is still usable because it
provides `eimp:get_type/1` which is used by mod_avatar.
2018-01-15 12:54:57 +03:00
Paweł Chmielowski be592c9272 Don't report errors when trying to remove not existing files in make install 2018-01-15 10:38:36 +01:00
Paweł Chmielowski 3df78d3a8f Copy sh_to_ask function from xmerl_regexp.erl
This way we don't need to include xmerl application in our docker container
2018-01-15 10:31:26 +01:00
Evgeny Khramtsov d2427c98c3 Merge pull request #2221 from licaon-kter/patch-1
Fix example config macro names
2018-01-14 09:54:22 +03:00
Licaon_Kter 84819ba0fe Fix example config macro names
These were missed in https://github.com/processone/ejabberd/commit/c26b56679e06ef8b88734030de11c6b885f9bb78#diff-19e0c10699732f76181f70cfbec95d38 I guess.
2018-01-13 21:38:01 +00:00
Evgeniy Khramtsov f66a004821 Improve logging of failed s2s EXTERNAL authentication 2018-01-13 13:01:40 +03:00
Paweł Chmielowski e1da673502 Fix detection of rebar3
Looks like sometimes rebar3 report version that can't be split to 3 fields
by '.', we need only first one so don't match the rest
2018-01-12 17:12:46 +01:00
Paweł Chmielowski 7d58b7a100 Improve resolving of system deps
This version is able to search for know alternative names of deps and
helps with running tests when using system deps
2018-01-12 16:16:12 +01:00
Christophe Romain f2c3fe8ac6 Add mix dependencies for acme support 2018-01-12 12:28:31 +01:00
Evgeniy Khramtsov 1c1b12fc64 Note about direct-tls connections in example config 2018-01-12 10:27:33 +03:00
161 changed files with 4530 additions and 3988 deletions
+6 -16
View File
@@ -6,7 +6,6 @@ otp_release:
- 19.2
services:
- riak
- redis-server
before_install:
@@ -31,16 +30,6 @@ install:
before_script:
# Ulimit: See Travis-CI issue report: https://github.com/travis-ci/travis-ci/issues/3328
- echo 'ulimit -n 4096' > riak
- sudo mv riak /etc/default/riak
- mkdir "$PWD/ebin"
- sed 's/^storage_backend.*/storage_backend = leveldb/' /etc/riak/riak.conf > riak.conf
- sudo mv riak.conf /etc/riak/riak.conf
- echo "[{riak_kv, [{add_paths, [\"$PWD/ebin/\"]}]}]." > advanced.config
- sudo mv advanced.config /etc/riak/advanced.config
- sudo service riak restart
- sudo riak-admin wait-for-service riak_kv 'riak@127.0.0.1'
- sudo riak-admin test
- mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';"
- mysql -u root -e "CREATE DATABASE ejabberd_test;"
- mysql -u root -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';"
@@ -50,23 +39,24 @@ before_script:
script:
- ./autogen.sh
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc --disable-riak
- make
- make install
- make install -s
- make xref
- sed -i -e 's/ct:pal/ct:log/' test/suite.erl
- ln -sf ../sql priv/
- echo "" >> rebar.config
- echo '{ct_extra_params, "-verbosity 20"}.' >> rebar.config
- escript ./rebar skip_deps=true ct -v
- grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' logs/raw.log
- grep -q 'TEST COMPLETE,.* 0 failed' logs/raw.log
after_script:
- find logs -name suite.log -exec cat '{}' ';'
after_failure:
- find logs -name exunit.log -exec cat '{}' ';'
# Try checking Riak database logs
- tail -n 100000 /var/log/riak/*.log
- find logs -name ejabberd.log -exec cat '{}' ';'
- find logs -name suite.log -exec cat '{}' ';' | awk 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}
after_success:
- coveralls-merge erlang.json
+8 -2
View File
@@ -85,6 +85,11 @@ else
CHOWN_OUTPUT=&1
INIT_USER=$(INSTALLUSER)
endif
# if no group was enabled, don't set privileges or ownership
INSTALLGROUP=@INSTALLGROUP@
ifneq ($(INSTALLGROUP),)
G_USER=-g $(INSTALLGROUP)
endif
all: deps src
@@ -97,6 +102,7 @@ deps/.got:
$(REBAR) get-deps && :> deps/.got
deps/.built: deps/.got
$(REBAR) configure-deps
$(REBAR) compile && :> deps/.built
src: deps/.built
@@ -149,7 +155,7 @@ define DEP_VERSION_template
DEP_$(1)_VERSION:=$(shell $(SED) -e '/vsn/!d;s/.*, *"/$(1)-/;s/".*//' $(2) 2>/dev/null)
endef
DELETE_TARGET_SO=$(if $(subst X.soX,,X$(suffix $(1))X),,rm $(call TO_DEST,$(1));)
DELETE_TARGET_SO=$(if $(subst X.soX,,X$(suffix $(1))X),,rm -f $(call TO_DEST,$(1));)
$(foreach DEP,$(DEPS),$(eval $(call DEP_VERSION_template,$(DEP),deps/$(DEP)/ebin/$(DEP).app)))
$(eval $(call DEP_VERSION_template,ejabberd,ebin/ejabberd.app))
@@ -159,7 +165,7 @@ $(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $(call DELETE_TARGET_SO
endef
define COPY_BINARY_template
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; rm $(call TO_DEST,$(1)); $$(INSTALL) -m 755 $$(O_USER) $(1) $(call TO_DEST,$(1))
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; rm -f $(call TO_DEST,$(1)); $$(INSTALL) -m 755 $$(O_USER) $(1) $(call TO_DEST,$(1))
endef
$(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,$(file))))
+25 -9
View File
@@ -244,14 +244,6 @@ AC_ARG_ENABLE(sip,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-sip) ;;
esac],[if test "x$sip" = "x"; then sip=false; fi])
AC_ARG_ENABLE(graphics,
[AC_HELP_STRING([--enable-graphics], [enable support for graphic images manipulation (default: yes)])],
[case "${enableval}" in
yes) graphics=true ;;
no) graphics=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-graphics) ;;
esac],[if test "x$graphics" = "x"; then graphics=true; fi])
AC_CONFIG_FILES([Makefile
vars.config
src/ejabberd.app.src])
@@ -269,6 +261,19 @@ if test "$ENABLEUSER" != ""; then
echo "allow this system user to start ejabberd: $ENABLEUSER"
AC_SUBST([INSTALLUSER], [$ENABLEUSER])
fi
ENABLEGROUP=""
AC_ARG_ENABLE(group,
[AS_HELP_STRING([--enable-group[[[[=GROUP]]]]], [allow this system group to start ejabberd (default: no)])],
[case "${enableval}" in
yes) ENABLEGROUP=`groups |head -n 1` ;;
no) ENABLEGROUP="" ;;
*) ENABLEGROUP=$enableval
esac],
[])
if test "$ENABLEGROUP" != ""; then
echo "allow this system group to start ejabberd: $ENABLEGROUP"
AC_SUBST([INSTALLGROUP], [$ENABLEGROUP])
fi
ERLANG_DEPRECATED_TYPES_CHECK
@@ -279,6 +284,17 @@ if test "$sqlite" = "true"; then
fi
fi
enabled_backends=""
for backend in odbc mysql pgsql sqlite riak redis; do
if eval test x\${$backend} = xtrue; then
if test "x$enabled_backends" = "x"; then
enabled_backends=$backend
else
enabled_backends="$enabled_backends, $backend"
fi
fi
done
AC_SUBST(hipe)
AC_SUBST(roster_gateway_workaround)
AC_SUBST(new_sql_schema)
@@ -297,12 +313,12 @@ AC_SUBST(iconv)
AC_SUBST(stun)
AC_SUBST(sip)
AC_SUBST(debug)
AC_SUBST(graphics)
AC_SUBST(tools)
AC_SUBST(latest_deps)
AC_SUBST(system_deps)
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(enabled_backends)
AC_OUTPUT
+34 -8
View File
@@ -169,6 +169,20 @@ listen:
max_stanza_size: 65536
shaper: c2s_shaper
access: c2s
##
## Direct-TLS for C2S (XEP-0368). A good practice is to forward
## traffic from port 443 to this port, possibly multiplexing it
## with HTTP using e.g. sslh [https://wiki.xmpp.org/web/Tech_pages/XEP-0368],
## so modern clients can bypass restrictive firewalls (in airports, hotels, etc.).
##
## -
## port: 5223
## ip: "::"
## module: ejabberd_c2s
## tls: true
## max_stanza_size: 65536
## shaper: c2s_shaper
## access: c2s
-
port: 5269
ip: "::"
@@ -185,6 +199,7 @@ listen:
web_admin: true
## register: true
captcha: true
##
## ejabberd_service: Interact with external components (transports, ...)
##
@@ -242,9 +257,9 @@ listen:
## request_handlers:
## "": mod_http_upload
## tls: true
## protocol_options: 'TLSOPTS'
## dhfile: 'DHFILE'
## ciphers: 'CIPHERS'
## protocol_options: 'TLS_OPTIONS'
## dhfile: 'DH_FILE'
## ciphers: 'TLS_CIPHERS'
## Disabling digest-md5 SASL authentication. digest-md5 requires plain-text
## password storage (see auth_password_format option).
@@ -430,6 +445,11 @@ auth_method: internal
##
## sql_keepalive_interval: undefined
##
## Use the new SQL schema
##
## new_sql_schema: true
###. ===============
###' TRAFFIC SHAPERS
@@ -722,7 +742,7 @@ modules:
## mod_http_upload:
## # docroot: "@HOME@/upload"
## put_url: "https://@HOST@:5444"
## thumbnail: false # otherwise needs the identify command from ImageMagick installed
## thumbnail: false # otherwise needs ejabberd to be compiled with libgd support
## mod_http_upload_quota:
## max_days: 30
mod_last: {}
@@ -761,6 +781,14 @@ modules:
- "flat"
- "hometree"
- "pep" # pep requires mod_caps
force_node_config:
## Avoid using OMEMO by default because it
## introduces a lot of hard-to-track problems
"eu.siacs.conversations.axolotl.*":
access_model: whitelist
## Avoid buggy clients to make their bookmarks public
"storage:bookmarks":
access_model: whitelist
mod_push: {}
mod_push_keepalive: {}
## mod_register:
@@ -803,10 +831,7 @@ modules:
mod_vcard:
search: false
mod_vcard_xupdate: {}
## Convert all avatars posted by Android clients from WebP to JPEG
## mod_avatar: # this module needs compile option --enable-graphics
## convert:
## webp: jpeg
mod_avatar: {}
mod_version: {}
mod_stream_mgmt: {}
## Non-SASL Authentication (XEP-0078) is now disabled by default
@@ -820,6 +845,7 @@ modules:
## and check your accessibility at https://check.messaging.one/
mod_s2s_dialback: {}
mod_http_api: {}
mod_fail2ban: {}
##
## Enable modules with custom options in a specific virtual host
+11 -13
View File
@@ -27,7 +27,7 @@ case $(id -un) in
EXEC_CMD="as_install_user"
else
EXEC_CMD="as_current_user"
echo "WARNING: This is not recommended to run ejabberd as root" >&2
echo "WARNING: It is not recommended to run ejabberd as root" >&2
fi
;;
*)
@@ -41,19 +41,17 @@ case $(id -un) in
esac
# parse command line parameters
for arg; do
case $arg in
-n|--node) ERLANG_NODE_ARG=$2; shift;;
-s|--spool) SPOOL_DIR=$2; shift;;
-l|--logs) LOGS_DIR=$2; shift;;
-f|--config) EJABBERD_CONFIG_PATH=$2; shift;;
-c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift;;
-d|--config-dir) ETC_DIR=$2; shift;;
-t|--no-timeout) NO_TIMEOUT="--no-timeout";;
--) :;;
while [ $# -gt 0 ]; do
case $1 in
-n|--node) ERLANG_NODE_ARG=$2; shift 2;;
-s|--spool) SPOOL_DIR=$2; shift 2;;
-l|--logs) LOGS_DIR=$2; shift 2;;
-f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;;
-c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;;
-d|--config-dir) ETC_DIR=$2; shift 2;;
-t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;;
*) break;;
esac
shift
done
# define ejabberd variables if not already defined from the command line
@@ -263,7 +261,7 @@ cd "$SPOOL_DIR" || {
case $1 in
start)
check_start
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -detached
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
;;
foreground)
check_start
+1 -1
View File
@@ -46,6 +46,6 @@
buf :: binary(),
http_opts = [] :: list()}).
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE' | 'PATCH'.
-type protocol() :: http | https.
-type http_request() :: #request{}.
+1
View File
@@ -63,6 +63,7 @@
max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none,
logging = false :: boolean(),
vcard = <<"">> :: binary(),
vcard_xupdate = undefined :: undefined | external | binary(),
captcha_whitelist = (?SETS):empty() :: ?TGB_SET,
mam = false :: boolean(),
pubsub = <<"">> :: binary()
+10 -9
View File
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
def project do
[app: :ejabberd,
version: "18.1.0",
version: "18.4.0",
description: description(),
elixir: "~> 1.4",
elixirc_paths: ["lib"],
@@ -28,7 +28,8 @@ defmodule Ejabberd.Mixfile do
applications: [:ssl, :os_mon],
included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab,
:fast_tls, :stringprep, :fast_xml, :xmpp,
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2, :fs]
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2,
:eimp, :base64url, :jose]
++ cond_apps()]
end
@@ -73,7 +74,9 @@ defmodule Ejabberd.Mixfile do
{:p1_oauth2, "~> 0.6.1"},
{:distillery, "~> 1.0"},
{:ex_doc, ">= 0.0.0", only: :dev},
{:fs, "~> 3.4"}]
{:eimp, "~> 1.0"},
{:base64url, "~> 0.0.1"},
{:jose, "~> 1.8"}]
++ cond_deps()
end
@@ -89,13 +92,12 @@ defmodule Ejabberd.Mixfile do
for {:true, dep} <- [{config(:sqlite), {:sqlite3, "~> 1.1"}},
{config(:riak), {:riakc, "~> 2.4"}},
{config(:redis), {:eredis, "~> 1.0"}},
{config(:zlib), {:ezlib, github: "processone/ezlib", tag: "1.0.3", override: true, manager: :rebar}},
{config(:zlib), {:ezlib, "~> 1.0"}},
{config(:iconv), {:iconv, "~> 1.0"}},
{config(:pam), {:epam, "~> 1.0"}},
{config(:tools), {:luerl, github: "rvirding/luerl", tag: "v0.2"}},
{config(:tools), {:luerl, "~> 0.3.1"}},
{config(:tools), {:meck, "~> 0.8.4"}},
{config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}},
{config(:graphics), {:eimp, github: "processone/eimp", tag: "1.0.1"}}], do:
{config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}}], do:
dep
end
@@ -105,8 +107,7 @@ defmodule Ejabberd.Mixfile do
{config(:pgsql), :p1_pgsql},
{config(:sqlite), :sqlite3},
{config(:zlib), :ezlib},
{config(:iconv), :iconv},
{config(:graphics), :eimp}], do:
{config(:iconv), :iconv}], do:
app
end
+22 -19
View File
@@ -1,32 +1,35 @@
%{"cache_tab": {:hex, :cache_tab, "1.0.12", "a06a4ffbd4da8469791ba941512a6a45ed8c11865b4606a368e21b332da3638a", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
%{
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
"cache_tab": {:hex, :cache_tab, "1.0.13", "e09857af9b7ba89428227d3801256852cb0d51a4de47e4edcb160d96cc96f8eb", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
"eimp": {:git, "https://github.com/processone/eimp.git", "23796118176be98195db9f831f17dde74d1553e1", [tag: "1.0.1"]},
"epam": {:hex, :epam, "1.0.3", "3adcc148cdbaaa2bbe15dd661f0d74284e5749a815b4e480dbf94e8e023361b9", [:rebar3], [], "hexpm"},
"eimp": {:hex, :eimp, "1.0.3", "e40108d622d672cf6003d279d98fc46a98df182dbe8756857896ffd28883090d", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"epam": {:hex, :epam, "1.0.4", "2a5e40cbf9b2cf41df515782894c3b33c81b8ad32fff2fc847c3f725071dfaed", [:rebar3], [], "hexpm"},
"eredis": {:hex, :eredis, "1.1.0", "8d8d74496f35216679b97726b75fb1c8715e99dd7f3ef9f9824a2264c3e0aac0", [:rebar3], [], "hexpm"},
"esip": {:hex, :esip, "1.0.21", "711c704337d434db6d7c70bd0da868aaacd91b252c0bb63b4580e6c896164f1f", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"ezlib": {:git, "https://github.com/processone/ezlib.git", "ec6491d788436bb096022843e6ec7f58d2973ae3", [tag: "1.0.3"]},
"fast_tls": {:hex, :fast_tls, "1.0.20", "edd241961ab20b71ec1e9f75a2a2c043128ff117adf3efd42e6cec94f1937539", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"fast_xml": {:hex, :fast_xml, "1.1.28", "31ce5cf44d20e900e1a499009f886ff74b589324d532ed0ed7a432e4f498beb1", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"fast_yaml": {:hex, :fast_yaml, "1.0.12", "ee8527d388255cf7a24fc1e6cb2d09dca4e506966dd9d86e61d3d90f236a3e2e", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"fs": {:hex, :fs, "3.4.0", "6d18575c250b415b3cad559e6f97a4c822516c7bc2c10bfbb2493a8f230f5132", [:rebar3], [], "hexpm"},
"esip": {:hex, :esip, "1.0.22", "3e387312614762fb84d3f77ba4f17650faf52510482521300b3d98ecdcbec21d", [:rebar3], [{:fast_tls, "1.0.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"ezlib": {:hex, :ezlib, "1.0.4", "2434e4bb549cb060d5ac02261ba48fbe1a69b2ae4e1bf7485a3b27b3f3ec618d", [:rebar3], [], "hexpm"},
"fast_tls": {:hex, :fast_tls, "1.0.21", "7005fe030c0472643314c9c31e7627bb296dcb96a9ce0b5dd8ccb34273f4c1ff", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"fast_xml": {:hex, :fast_xml, "1.1.29", "c6356d28f0f76ffefc68b5eb65916f0b8ca513bab71db8ad95bd8577c47e30e2", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"fast_yaml": {:hex, :fast_yaml, "1.0.13", "adcb8db20bb96d4e56b63b48c75d47ca15a6bd409da0200ffbd32db382131e22", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"},
"hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"},
"iconv": {:hex, :iconv, "1.0.6", "3b424a80039059767f1037dc6a49ff07c2f88df14068c16dc938c4f377a77b4c", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"iconv": {:hex, :iconv, "1.0.7", "f81eb6b8c977b1fd078515937fdce64292d64c6102353fbbfe57db580f4689d1", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [:rebar3], [], "hexpm"},
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
"luerl": {:git, "https://github.com/rvirding/luerl.git", "f7b2cc0ab6fa4245ebeda0169fc994aff0628bf9", [tag: "v0.2"]},
"luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"},
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
"moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]},
"p1_mysql": {:hex, :p1_mysql, "1.0.4", "7b9d7957a9d031813a0e6bcea5a7f5e91b54db805a92709a445cf75cf934bc1d", [:rebar3], [], "hexpm"},
"p1_mysql": {:hex, :p1_mysql, "1.0.5", "2a9644d27050a6aa9e7eb70a0620043f93655212b15f3620dc12f2fbd1a8c43a", [:rebar3], [], "hexpm"},
"p1_oauth2": {:hex, :p1_oauth2, "0.6.2", "cc381038920e3d34ef32aa10ba7eb637bdff38a946748c4fd99329ff484a3889", [:rebar3], [], "hexpm"},
"p1_pgsql": {:hex, :p1_pgsql, "1.1.4", "eadbbddee8d52145694bf86bdfe8c1ae8353a55e152410146b8c2711756d6041", [:rebar3], [], "hexpm"},
"p1_utils": {:hex, :p1_utils, "1.0.10", "a6d6927114bac79cf6468a10824125492034af7071adc6ed5ebc4ddb443845d4", [:rebar3], [], "hexpm"},
"p1_pgsql": {:hex, :p1_pgsql, "1.1.5", "1e1bef6e6d906e10552a608b9fe5ef39b3099caf0f44c07d3d9e18ac4dee34d1", [:rebar3], [], "hexpm"},
"p1_utils": {:hex, :p1_utils, "1.0.11", "a471f80644d4b464fa67572affddda7e95df5d4b099624b8907f5726e8a1769c", [:rebar3], [], "hexpm"},
"riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"},
"riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"},
"samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]},
"sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], [], "hexpm"},
"stringprep": {:hex, :stringprep, "1.0.10", "552d784eb60652220fce9131f8bb0ebc62fdffd6482c4f08f2e7d61300227c28", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"stun": {:hex, :stun, "1.0.20", "6b156fa11606bebb6086d02cb2f6532c84effb59c95ba93d0e2d8e2510970253", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"xmpp": {:hex, :xmpp, "1.1.19", "ca0a89c567e972d119204b1296ffe58ad5d3237738950ae2c61043fbaf5e150e", [:rebar3], [{:fast_xml, "1.1.28", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.10", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"}}
"sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"},
"stringprep": {:hex, :stringprep, "1.0.11", "002e6972ab36c35f3dd88c11725014e62608c45a00399c083681879973fa8026", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"stun": {:hex, :stun, "1.0.21", "087fb20497081927690ef9d70b5bd6f9f4bea256ad758c500842722c0b6bb6ab", [:rebar3], [{:fast_tls, "1.0.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
"xmpp": {:hex, :xmpp, "1.1.20", "33ddcc698518061f5051b98a6f731eef9342799f0c276a9debdfffe85c32fe6d", [:rebar3], [{:fast_xml, "1.1.29", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.11", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
}
+5
View File
@@ -0,0 +1,5 @@
-module(configure_deps).
-export(['configure-deps'/2]).
'configure-deps'(Config, Vals) ->
{ok, Config}.
+1 -1
View File
@@ -8,7 +8,7 @@ override_opts(override, Config, Opts) ->
override_opts(add, Config, Opts) ->
lists:foldl(fun({Opt, Value}, Conf) ->
V = rebar_config:get_local(Conf, Opt, []),
rebar_config:set(Conf, Opt, [Value | V])
rebar_config:set(Conf, Opt, V ++ Value)
end, Config, Opts).
preprocess(Config, _Dirs) ->
+22 -22
View File
@@ -20,33 +20,31 @@
{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager",
{tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.10"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.12"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.20"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.10"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.28"}}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.19"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.12"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.11"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.13"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.22"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.11"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.30"}}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.21"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.14"}}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.3"}}},
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
{fs, ".*", {git, "https://github.com/synrc/fs", "bed9467"}},
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.20"}}}},
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.21"}}}},
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.5"}}},
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.22"}}}},
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.23"}}}},
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
{tag, "1.0.4"}}}},
{tag, "1.0.5"}}}},
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
{tag, "1.1.4"}}}},
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
{tag, "1.1.5"}}}},
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
{tag, "1.1.6"}}}},
{if_var_true, pam, {epam, ".*", {git, "https://github.com/processone/epam",
{tag, "1.0.3"}}}},
{tag, "1.0.4"}}}},
{if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib",
{tag, "1.0.3"}}}},
{tag, "1.0.4"}}}},
{if_var_true, riak, {riakc, ".*", {git, "https://github.com/processone/riak-erlang-client",
{tag, {if_version_above, "19", "develop", "2.5.3"}}}}},
{if_var_true, graphics, {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.2"}}}},
%% Elixir support, needed to run tests
{if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
{tag, {if_version_above, "17", "v1.4.4", "v1.1.1"}}}}},
@@ -54,7 +52,9 @@
{if_not_rebar3, {if_var_true, elixir, {rebar_elixir_plugin, ".*",
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}},
{if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv",
{tag, "1.0.6"}}}},
{tag, "1.0.7"}}}},
{if_var_true, tools, {luerl, ".*", {git, "https://github.com/rvirding/luerl",
{tag, "v0.3"}}}},
{if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck",
{tag, "0.8.4"}}}},
{if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka",
@@ -93,7 +93,6 @@
{if_var_true, debug, debug_info},
{if_var_true, sip, {d, 'SIP'}},
{if_var_true, stun, {d, 'STUN'}},
{if_var_true, graphics, {d, 'GRAPHICS'}},
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
{if_var_match, db_type, mssql, {d, 'mssql'}},
{if_var_true, elixir, {d, 'ELIXIR_ENABLED'}},
@@ -112,7 +111,7 @@
{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}]}}.
{if_not_rebar3, {plugins, [
deps_erl_opts, override_deps_versions, override_opts,
deps_erl_opts, override_deps_versions, override_opts, configure_deps,
{if_var_true, elixir, rebar_elixir_compiler},
{if_var_true, elixir, rebar_exunit}
]}}.
@@ -154,15 +153,16 @@
{if_version_above, "17", {cover_enabled, true}}.
{cover_export_enabled, true}.
{recursive_cmds, ['configure-deps']}.
{post_hook_configure, [{"fast_tls", []},
{"stringprep", []},
{"fast_yaml", []},
{"eimp", []},
{if_var_true, sip, {"esip", []}},
{"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]},
{if_var_true, pam, {"epam", []}},
{if_var_true, zlib, {"ezlib", []}},
{if_var_true, graphics, {"eimp", []}},
{if_var_true, iconv, {"iconv", []}}]}.
{port_env, [{"CFLAGS", "-g -O2 -Wall"}]}.
+68 -49
View File
@@ -17,7 +17,6 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of
{ok, Terms} ->
Terms;
@@ -30,7 +29,7 @@ Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])
{ldflags, LDFlags} = lists:keyfind(ldflags, 1, Vars),
{system_deps, SystemDeps} = lists:keyfind(system_deps, 1, Vars),
GetCfg0 = fun(F, Cfg, [Key | Tail], Default) ->
GetCfg = fun GetCfg(Cfg, [Key | Tail], Default) ->
Val = case lists:keyfind(Key, 1, Cfg) of
{Key, V1} -> V1;
false -> Default
@@ -39,10 +38,10 @@ GetCfg0 = fun(F, Cfg, [Key | Tail], Default) ->
[] ->
Val;
_ ->
F(F, Val, Tail, Default)
GetCfg(Val, Tail, Default)
end
end,
ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) ->
ModCfg = fun ModCfg(Cfg, [Key | Tail], Op, Default) ->
{OldVal, PartCfg} = case lists:keytake(Key, 1, Cfg) of
{value, {_, V1}, V2} -> {V1, V2};
false -> {if Tail == [] -> Default; true -> [] end, Cfg}
@@ -51,26 +50,26 @@ ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) ->
[] ->
[{Key, Op(OldVal)} | PartCfg];
_ ->
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
[{Key, ModCfg(OldVal, Tail, Op, Default)} | PartCfg]
end
end,
FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) ->
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail);
(F, Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) ->
SourceVal = GetCfg0(GetCfg0, Cfg, SourcePath, SourceDefault),
FilterConfig = fun FilterConfig(Cfg, [{Path, true, ModFun, Default} | Tail]) ->
FilterConfig(ModCfg(Cfg, Path, ModFun, Default), Tail);
FilterConfig(Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) ->
SourceVal = GetCfg(Cfg, SourcePath, SourceDefault),
ModFun2 = fun(V) -> ModFun(V, SourceVal) end,
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun2, Default), Tail);
(F, Cfg, [_ | Tail]) ->
F(F, Cfg, Tail);
(_, Cfg, []) ->
FilterConfig(ModCfg(Cfg, Path, ModFun2, Default), Tail);
FilterConfig(Cfg, [_ | Tail]) ->
FilterConfig(Cfg, Tail);
FilterConfig(Cfg, []) ->
Cfg
end,
IsRebar3 = case application:get_key(rebar, vsn) of
{ok, VSN} ->
[VSN1 | _] = string:tokens(VSN, "-"),
[Maj, _Min, _Patch] = string:tokens(VSN1, "."),
[Maj|_] = string:tokens(VSN1, "."),
(list_to_integer(Maj) >= 3);
undefined ->
lists:keymember(mix, 1, application:loaded_applications())
@@ -79,15 +78,15 @@ IsRebar3 = case application:get_key(rebar, vsn) of
SysVer = erlang:system_info(otp_release),
ProcessSingleVar = fun(F, Var, Tail) ->
case F(F, [Var], []) of
case F([Var], []) of
[] -> Tail;
[Val] -> [Val | Tail]
end
end,
ProcessVars = fun(_F, [], Acc) ->
ProcessVars = fun F([], Acc) ->
lists:reverse(Acc);
(F, [{Type, Ver, Value} | Tail], Acc) when
F([{Type, Ver, Value} | Tail], Acc) when
Type == if_version_above orelse
Type == if_version_below ->
SysVer = erlang:system_info(otp_release),
@@ -97,11 +96,11 @@ ProcessVars = fun(_F, [], Acc) ->
SysVer < Ver
end,
if Include ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
true ->
F(F, Tail, Acc)
F(Tail, Acc)
end;
(F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
F([{Type, Ver, Value, ElseValue} | Tail], Acc) when
Type == if_version_above orelse
Type == if_version_below ->
Include = if Type == if_version_above ->
@@ -110,53 +109,53 @@ ProcessVars = fun(_F, [], Acc) ->
SysVer < Ver
end,
if Include ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
true ->
F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
F(Tail, ProcessSingleVar(F, ElseValue, Acc))
end;
(F, [{Type, Var, Value} | Tail], Acc) when
F([{Type, Var, Value} | Tail], Acc) when
Type == if_var_true orelse
Type == if_var_false ->
Flag = Type == if_var_true,
case proplists:get_bool(Var, Vars) of
V when V == Flag ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
_ ->
F(F, Tail, Acc)
F(Tail, Acc)
end;
(F, [{Type, Value} | Tail], Acc) when
F([{Type, Value} | Tail], Acc) when
Type == if_rebar3 orelse
Type == if_not_rebar3 ->
Flag = Type == if_rebar3,
case IsRebar3 == Flag of
true ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
_ ->
F(F, Tail, Acc)
F(Tail, Acc)
end;
(F, [{Type, Var, Match, Value} | Tail], Acc) when
F([{Type, Var, Match, Value} | Tail], Acc) when
Type == if_var_match orelse
Type == if_var_no_match ->
case proplists:get_value(Var, Vars) of
V when V == Match ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
_ ->
F(F, Tail, Acc)
F(Tail, Acc)
end;
(F, [{if_have_fun, MFA, Value} | Tail], Acc) ->
F([{if_have_fun, MFA, Value} | Tail], Acc) ->
{Mod, Fun, Arity} = MFA,
code:ensure_loaded(Mod),
case erlang:function_exported(Mod, Fun, Arity) of
true ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
F(Tail, ProcessSingleVar(F, Value, Acc));
false ->
F(F, Tail, Acc)
F(Tail, Acc)
end;
(F, [Other1 | Tail1], Acc) ->
F(F, Tail1, [F(F, Other1, []) | Acc]);
(F, Val, Acc) when is_tuple(Val) ->
list_to_tuple(F(F, tuple_to_list(Val), Acc));
(_F, Other2, _Acc) ->
F([Other1 | Tail1], Acc) ->
F(Tail1, [F(Other1, []) | Acc]);
F(Val, Acc) when is_tuple(Val) ->
list_to_tuple(F(tuple_to_list(Val), Acc));
F(Other2, _Acc) ->
Other2
end,
@@ -205,15 +204,35 @@ fun(DepsList) ->
end, DepsList)
end,
DepAlts = fun("esip") -> ["esip", "p1_sip"];
("xmpp") -> ["xmpp", "p1_xmpp"];
("fast_xml") -> ["fast_xml", "p1_xml"];
(Val) -> [Val]
end,
LibDirInt = fun F([Dep|Rest], Suffix) ->
case code:lib_dir(Dep) of
{error, _} ->
F(Rest, Suffix);
V -> V ++ Suffix
end;
F([], _) ->
error
end,
LibDir = fun(Name, Suffix) ->
LibDirInt(DepAlts(Name), Suffix)
end,
GlobalDepsFilter =
fun(Deps) ->
DepNames = lists:map(fun({DepName, _, _}) -> DepName;
({DepName, _}) -> DepName
end, Deps),
lists:filtermap(fun(Dep) ->
case code:lib_dir(Dep) of
{error, _} ->
{true, "Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps."};
case LibDir(atom_to_list(Dep), "") of
error ->
exit("Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps.");
_ ->
false
end
@@ -233,9 +252,9 @@ ResolveDepPath = case {SystemDeps, IsRebar3} of
{true, _} ->
fun("deps/" ++ Rest) ->
Slash = string:str(Rest, "/"),
case code:lib_dir(string:sub_string(Rest, 1, Slash -1)) of
{error, _} -> Rest;
V -> V ++ string:sub_string(Rest, Slash)
case LibDir(string:sub_string(Rest, 1, Slash -1), string:sub_string(Rest, Slash)) of
error -> Rest;
V -> V
end;
(Path) ->
Path
@@ -258,7 +277,7 @@ ResolveDepPath = case {SystemDeps, IsRebar3} of
CtParams = fun(CompileOpts) ->
["-ct_hooks cth_surefire ",
lists:map(fun({i, IncPath}) ->
[" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])]
[" -include ", filename:absname(ResolveDepPath(IncPath), Cwd)]
end, CompileOpts),
TestConfig]
end,
@@ -279,8 +298,8 @@ GenDepsConfigure =
fun(Hooks) ->
lists:map(fun({Pkg, Flags}) ->
DepPath = ResolveDepPath("deps/" ++ Pkg ++ "/"),
{add, list_to_atom(Pkg), [{pre_hooks, {'compile',
lists:flatten(GenDepConfigureLine(DepPath, Flags))}}]}
Line = lists:flatten(GenDepConfigureLine(DepPath, Flags)),
{add, list_to_atom(Pkg), [{pre_hooks, [{'compile', Line}, {'configure-deps', Line}]}]}
end, Hooks)
end,
@@ -353,7 +372,7 @@ Rules = [
],
Config = [{plugin_dir, filename:join([filename:dirname(SCRIPT),"plugins"])}]++
FilterConfig(FilterConfig, ProcessVars(ProcessVars, CONFIG, []), Rules),
FilterConfig(ProcessVars(CONFIG, []), Rules),
%io:format("ejabberd configuration:~n ~p~n", [Config]),
+4 -4
View File
@@ -109,9 +109,9 @@ CREATE TABLE archive (
);
CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp);
CREATE INDEX i_archive_sh_username_peer ON archive (server_host, username, peer);
CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, username, bare_peer);
CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp);
CREATE INDEX i_archive_sh_peer ON archive (server_host, peer);
CREATE INDEX i_archive_sh_bare_peer ON archive (server_host, bare_peer);
CREATE TABLE archive_prefs (
username text NOT NULL,
@@ -259,8 +259,8 @@ CREATE TABLE pubsub_item (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL DEFAULT ''
);
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
+4 -4
View File
@@ -98,9 +98,9 @@ CREATE TABLE archive (
);
CREATE INDEX i_username_timestamp ON archive(username, timestamp);
CREATE INDEX i_archive_username_peer ON archive (username, peer);
CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer);
CREATE INDEX i_timestamp ON archive(timestamp);
CREATE INDEX i_peer ON archive(peer);
CREATE INDEX i_bare_peer ON archive(bare_peer);
CREATE TABLE archive_prefs (
username text NOT NULL PRIMARY KEY,
@@ -236,8 +236,8 @@ CREATE TABLE pubsub_item (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL DEFAULT ''
);
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
+8 -8
View File
@@ -41,15 +41,15 @@ CREATE TABLE [dbo].[archive] (
CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [archive_username_peer] ON [archive] (username, peer)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [archive_username_bare_peer] ON [archive] (username, bare_peer)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [archive_timestamp] ON [archive] (timestamp)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [archive_peer] ON [archive] (peer)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE INDEX [archive_bare_peer] ON [archive] (bare_peer)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
CREATE TABLE [dbo].[archive_prefs] (
[username] [varchar] (250) NOT NULL,
[def] [text] NOT NULL,
@@ -220,8 +220,8 @@ CREATE TABLE [dbo].[pubsub_item] (
[nodeid] [bigint] NULL,
[itemid] [varchar] (255) NOT NULL,
[publisher] [text] NOT NULL,
[creation] [text] NOT NULL,
[modification] [varchar] (255) NOT NULL,
[creation] [varchar] (32) NOT NULL,
[modification] [varchar] (32) NOT NULL,
[payload] [text] NOT NULL DEFAULT ''
) TEXTIMAGE_ON [PRIMARY];
+7 -7
View File
@@ -113,10 +113,10 @@ CREATE TABLE archive (
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE FULLTEXT INDEX i_text ON archive(txt);
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username,timestamp);
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username(191), timestamp);
CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username(191), peer(191));
CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username(191), bare_peer(191));
CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp);
CREATE INDEX i_archive_sh_peer USING BTREE ON archive(server_host(191), peer);
CREATE INDEX i_archive_sh_bare_peer USING BTREE ON archive(server_host(191), bare_peer);
CREATE TABLE archive_prefs (
username varchar(191) NOT NULL,
@@ -274,8 +274,8 @@ CREATE TABLE pubsub_item (
nodeid bigint,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
@@ -422,7 +422,7 @@ CREATE TABLE carboncopy (
PRIMARY KEY (server_host(191), username(191), resource(191))
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host, username(75));
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host(191), username(75));
CREATE TABLE proxy65 (
sid text NOT NULL,
@@ -446,4 +446,4 @@ CREATE TABLE push_session (
PRIMARY KEY (server_host(191), username(191), timestamp)
);
CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username(191), service(191), node(191));
CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host(191), username(191), service(191), node(191));
+5 -5
View File
@@ -102,10 +102,10 @@ CREATE TABLE archive (
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE FULLTEXT INDEX i_text ON archive(txt);
CREATE INDEX i_username_timestamp USING BTREE ON archive(username,timestamp);
CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), timestamp);
CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(191));
CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), bare_peer(191));
CREATE INDEX i_timestamp USING BTREE ON archive(timestamp);
CREATE INDEX i_peer USING BTREE ON archive(peer);
CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer);
CREATE TABLE archive_prefs (
username varchar(191) NOT NULL PRIMARY KEY,
@@ -251,8 +251,8 @@ CREATE TABLE pubsub_item (
nodeid bigint,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
+8 -9
View File
@@ -61,15 +61,14 @@
-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT;
-- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
-- DROP INDEX i_username;
-- DROP INDEX i_username_timestamp;
-- DROP INDEX i_username_peer;
-- DROP INDEX i_username_bare_peer;
-- DROP INDEX i_timestamp;
-- DROP INDEX i_peer;
-- DROP INDEX i_bare_peer;
-- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
-- CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
-- CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
-- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
-- CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
-- CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
-- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT;
-- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
@@ -265,9 +264,9 @@ CREATE TABLE archive (
);
CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
CREATE TABLE archive_prefs (
username text NOT NULL,
@@ -429,8 +428,8 @@ CREATE TABLE pubsub_item (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL DEFAULT ''
);
CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid);
+4 -4
View File
@@ -102,9 +102,9 @@ CREATE TABLE archive (
);
CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp);
CREATE INDEX i_username_peer ON archive USING btree (username, peer);
CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare_peer);
CREATE INDEX i_timestamp ON archive USING btree (timestamp);
CREATE INDEX i_peer ON archive USING btree (peer);
CREATE INDEX i_bare_peer ON archive USING btree (bare_peer);
CREATE TABLE archive_prefs (
username text NOT NULL PRIMARY KEY,
@@ -254,8 +254,8 @@ CREATE TABLE pubsub_item (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
itemid text NOT NULL,
publisher text NOT NULL,
creation text NOT NULL,
modification text NOT NULL,
creation varchar(32) NOT NULL,
modification varchar(32) NOT NULL,
payload text NOT NULL DEFAULT ''
);
CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid);
+1 -1
View File
@@ -100,7 +100,7 @@ solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, Key) ->
ets_put_key_authorization(Tkn, KeyAuthz),
{ok, Chal#challenge.uri, KeyAuthz};
solve_challenge1(Challenge, _Key) ->
?ERROR_MSG("Unkown Challenge Type: ~p", [Challenge]),
?ERROR_MSG("Unknown Challenge Type: ~p", [Challenge]),
{error, unknown_challenge}.
+2 -2
View File
@@ -59,8 +59,8 @@
start(_Opts) ->
Fqdn = get_local_fqdn(),
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
[Fqdn]),
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
[Fqdn]),
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
digest).
+2 -9
View File
@@ -81,15 +81,8 @@ prepare(ClientIn) ->
_ -> error
end.
parse(S) -> parse1(binary_to_list(S), "", []).
parse1([0 | Cs], S, T) ->
parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
%parse1([], [], T) ->
% lists:reverse(T);
parse1([], S, T) ->
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
parse(S) ->
binary:split(S, <<0>>, [global]).
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
+1 -1
View File
@@ -6,7 +6,7 @@
{modules, []},
{registered, []},
{applications, [kernel, stdlib]},
{env, []},
{env, [{enabled_backends, [@enabled_backends@]}]},
{mod, {ejabberd_app, []}}]}.
+8 -3
View File
@@ -25,6 +25,7 @@
-module(ejabberd).
-author('alexey@process-one.net').
-compile({no_auto_import, [{halt, 0}]}).
-protocol({xep, 4, '2.9'}).
-protocol({xep, 86, '1.0'}).
@@ -36,7 +37,7 @@
-protocol({xep, 243, '1.0'}).
-protocol({xep, 270, '1.0'}).
-export([start/0, stop/0, start_app/1, start_app/2,
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
get_pid_file/0, check_app/1, module_name/1]).
-include("logger.hrl").
@@ -49,6 +50,11 @@ stop() ->
application:stop(ejabberd).
%%ejabberd_cover:stop().
halt() ->
application:stop(lager),
application:stop(sasl),
erlang:halt(1, [{flush, true}]).
%% @spec () -> false | string()
get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of
@@ -131,8 +137,7 @@ exit_or_halt(Reason, StartFlag) ->
?CRITICAL_MSG(Reason, []),
if StartFlag ->
%% Wait for the critical message is written in the console/log
timer:sleep(1000),
halt(string:substr(lists:flatten(Reason), 1, 199));
halt();
true ->
erlang:error(application_start_failed)
end.
+7 -3
View File
@@ -29,7 +29,7 @@
-include("logger.hrl").
-behaviour(gen_server).
-behavior(ejabberd_config).
-behaviour(ejabberd_config).
%% API
-export([start_link/0,
@@ -393,7 +393,7 @@ parse_who(Name, Defs, ParseOauth) when is_list(Defs) ->
{oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest}
end;
scope ->
report_error(<<"Oauth rule can't be embeded inside other oauth rule in 'who' section for api_permission '~s'">>,
report_error(<<"Oauth rule can't be embedded inside other oauth rule in 'who' section for api_permission '~s'">>,
[Name])
end;
({scope, ScopeList}) ->
@@ -492,6 +492,8 @@ parse_single_what(Binary) when is_binary(Binary) ->
_ ->
{error, <<"Invalid value">>}
end;
parse_single_what(Atom) when is_atom(Atom) ->
parse_single_what(atom_to_binary(Atom, latin1));
parse_single_what(_) ->
{error, <<"Invalid value">>}.
@@ -502,7 +504,9 @@ is_valid_command_name(Val) ->
is_valid_command_name2(<<>>) ->
true;
is_valid_command_name2(<<K:8, Rest/binary>>) when K >= $a andalso K =< $z orelse K == $_ ->
is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
orelse (K >= $0 andalso K =< $9)
orelse K == $_ ->
is_valid_command_name2(Rest);
is_valid_command_name2(_) ->
false.
+1 -1
View File
@@ -1,6 +1,6 @@
-module (ejabberd_acme).
-behaviour(gen_server).
-behavior(ejabberd_config).
-behaviour(ejabberd_config).
%% ejabberdctl commands
-export([get_certificates/1,
+2 -11
View File
@@ -62,8 +62,7 @@ start(normal, _Args) ->
{ok, SupPid};
Err ->
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
timer:sleep(1000),
halt("Refer to ejabberd log files to diagnose the problem")
ejabberd:halt()
end;
start(_, _) ->
{error, badarg}.
@@ -150,7 +149,7 @@ start_apps() ->
ejabberd:start_app(fast_tls),
ejabberd:start_app(xmpp),
ejabberd:start_app(cache_tab),
start_eimp().
ejabberd:start_app(eimp).
setup_if_elixir_conf_used() ->
case ejabberd_config:is_using_elixir_config() of
@@ -174,11 +173,3 @@ start_elixir_application() ->
_ ->
ok
end.
-ifdef(GRAPHICS).
start_eimp() ->
ejabberd:start_app(eimp).
-else.
start_eimp() ->
ok.
-endif.
+19 -3
View File
@@ -526,7 +526,10 @@ db_get_password(User, Server, Mod) ->
UseCache = use_cache(Mod, Server),
case erlang:function_exported(Mod, get_password, 2) of
false when UseCache ->
ets_cache:lookup(?AUTH_CACHE, {User, Server});
case ets_cache:lookup(?AUTH_CACHE, {User, Server}) of
{ok, exists} -> error;
Other -> Other
end;
false ->
error;
true when UseCache ->
@@ -544,7 +547,20 @@ db_user_exists(User, Server, Mod) ->
error ->
case Mod:store_type(Server) of
external ->
Mod:user_exists(User, Server);
case ets_cache:lookup(
?AUTH_CACHE, {User, Server},
fun() ->
case Mod:user_exists(User, Server) of
true -> {ok, exists};
false -> error;
{error, _} = Err -> Err
end
end) of
{ok, _} ->
true;
error ->
false
end;
_ ->
false
end
@@ -568,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
false ->
error
end
end, cache_nodes(Mod, Server)) of
end) of
{ok, _} ->
true;
error ->
+8 -15
View File
@@ -71,16 +71,12 @@
-define(NS_HTTP_BIND,
<<"http://jabber.org/protocol/httpbind">>).
-define(DEFAULT_MAXPAUSE, 120).
-define(DEFAULT_WAIT, 300).
-define(DEFAULT_HOLD, 1).
-define(DEFAULT_POLLING, 2).
-define(DEFAULT_INACTIVITY, 30).
-define(MAX_SHAPED_REQUESTS_QUEUE_LEN, 1000).
-define(SEND_TIMEOUT, 15000).
@@ -102,7 +98,7 @@
inactivity_timer :: reference() | undefined,
wait_timer :: reference() | undefined,
wait_timeout = ?DEFAULT_WAIT :: timeout(),
inactivity_timeout = ?DEFAULT_INACTIVITY :: timeout(),
inactivity_timeout :: timeout(),
prev_rid = 0 :: non_neg_integer(),
prev_key = <<"">> :: binary(),
prev_poll :: erlang:timestamp() | undefined,
@@ -294,7 +290,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
XMPPVer = get_attr('xmpp:version', Attrs),
XMPPDomain = get_attr(to, Attrs),
{InBuf, Opts} = case gen_mod:get_module_opt(
XMPPDomain, mod_bosh, prebind, false) of
XMPPDomain, mod_bosh, prebind) of
true ->
JID = make_random_jid(XMPPDomain),
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
@@ -306,10 +302,8 @@ init([#body{attrs = Attrs}, IP, SID]) ->
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
[{receiver, self()}|Opts]),
Inactivity = gen_mod:get_module_opt(XMPPDomain,
mod_bosh, max_inactivity,
?DEFAULT_INACTIVITY),
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat,
unlimited),
mod_bosh, max_inactivity),
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
State = #state{host = XMPPDomain, sid = SID, ip = IP,
xmpp_ver = XMPPVer, el_ibuf = InBuf,
@@ -354,8 +348,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
true -> {undefined, []}
end,
MaxPause = gen_mod:get_module_opt(State#state.host,
mod_bosh, max_pause,
?DEFAULT_MAXPAUSE),
mod_bosh, max_pause),
Resp = #body{attrs =
[{sid, State#state.sid}, {wait, Wait},
{ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING},
@@ -746,9 +739,10 @@ bounce_receivers(State, Reason) ->
State, Receivers ++ ShapedReceivers).
bounce_els_from_obuf(State) ->
Opts = ejabberd_config:codec_options(State#state.host),
p1_queue:foreach(
fun({xmlstreamelement, El}) ->
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
try xmpp:decode(El, ?NS_CLIENT, Opts) of
Pkt when ?is_stanza(Pkt) ->
case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of
{#jid{}, #jid{}} ->
@@ -1028,8 +1022,7 @@ buf_new(Host) ->
buf_new(Host, Limit) ->
QueueType = gen_mod:get_module_opt(
Host, mod_bosh, queue_type,
ejabberd_config:default_queue_type(Host)),
Host, mod_bosh, queue_type),
p1_queue:new(QueueType, Limit).
buf_in(Xs, Buf) ->
+32 -23
View File
@@ -109,7 +109,7 @@ set_presence(Ref, Pres) ->
resend_presence(Pid) ->
resend_presence(Pid, undefined).
-spec resend_presence(pid(), jid() | undefined) -> ok.
-spec resend_presence(pid(), jid() | undefined) -> boolean().
resend_presence(Pid, To) ->
route(Pid, {resend_presence, To}).
@@ -418,8 +418,10 @@ handle_stream_start(StreamStart, #{lserver := LServer} = State) ->
send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
true ->
State1 = change_shaper(State),
Opts = ejabberd_config:codec_options(LServer),
State2 = State1#{codec_options => Opts},
ejabberd_hooks:run_fold(
c2s_stream_started, LServer, State1, [StreamStart])
c2s_stream_started, LServer, State2, [StreamStart])
end.
handle_stream_end(Reason, #{lserver := LServer} = State) ->
@@ -517,6 +519,7 @@ init([State, Opts]) ->
TLSRequired = proplists:get_bool(starttls_required, Opts),
TLSVerify = proplists:get_bool(tls_verify, Opts),
Zlib = proplists:get_bool(zlib, Opts),
Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
tls_required => TLSRequired,
tls_enabled => TLSEnabled,
@@ -528,7 +531,8 @@ init([State, Opts]) ->
lserver => ?MYNAME,
access => Access,
shaper => Shaper},
ejabberd_hooks:run_fold(c2s_init, {ok, State1}, [Opts]).
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]).
handle_call(get_presence, From, #{jid := JID} = State) ->
Pres = case maps:get(pres_last, State, error) of
@@ -643,12 +647,12 @@ route_probe_reply(_, _) ->
ok.
-spec process_presence_out(state(), presence()) -> state().
process_presence_out(#{user := User, server := Server, lserver := LServer,
jid := JID, lang := Lang, pres_a := PresA} = State,
process_presence_out(#{lserver := LServer, jid := JID,
lang := Lang, pres_a := PresA} = State,
#presence{from = From, to = To, type = Type} = Pres) ->
if Type == subscribe; Type == subscribed;
Type == unsubscribe; Type == unsubscribed ->
Access = gen_mod:get_module_opt(LServer, mod_roster, access, all),
Access = gen_mod:get_module_opt(LServer, mod_roster, access),
MyBareJID = jid:remove_resource(JID),
case acl:match_rule(LServer, Access, MyBareJID) of
deny ->
@@ -656,9 +660,7 @@ process_presence_out(#{user := User, server := Server, lserver := LServer,
AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
send_error(State, Pres, AccessErr);
allow ->
ejabberd_hooks:run(roster_out_subscription,
LServer,
[User, Server, To, Type])
ejabberd_hooks:run(roster_out_subscription, LServer, [Pres])
end;
true -> ok
end,
@@ -839,9 +841,9 @@ route_multiple(#{lserver := LServer}, JIDs, Pkt) ->
ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt).
get_subscription(#jid{luser = LUser, lserver = LServer}, JID) ->
{Subscription, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, LServer, {none, []},
[LUser, LServer, JID]),
{Subscription, _, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, LServer, {none, none, []},
[LUser, LServer, JID]),
Subscription.
-spec resource_conflict_action(binary(), binary(), binary()) ->
@@ -997,6 +999,9 @@ opt_type(_) ->
(max_stanza_size) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((timeout()) -> timeout());
(stream_management) -> fun((boolean()) -> boolean());
(inet) -> fun((boolean()) -> boolean());
(inet6) -> fun((boolean()) -> boolean());
(backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
@@ -1025,20 +1030,24 @@ listen_opt_type(max_stanza_size) ->
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
%% The following hack should be removed in future releases: it is intended
%% for backward compatibility with ejabberd 17.01 or older
listen_opt_type(stream_management) ->
?WARNING_MSG("listening option 'stream_management' is deprecated: "
"use mod_stream_mgmt module", []),
?ERROR_MSG("listening option 'stream_management' is ignored: "
"use mod_stream_mgmt module", []),
fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(O) ->
case mod_stream_mgmt:mod_opt_type(O) of
L when is_list(L) ->
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
case lists:keyfind(O, 1, StreamOpts) of
false ->
[access, shaper, certfile, ciphers, dhfile, cafile,
protocol_options, tls, tls_compression, starttls,
starttls_required, tls_verify, zlib, max_fsm_queue];
VFun ->
?WARNING_MSG("listening option '~s' is deprecated: use '~s' "
"option from mod_stream_mgmt module", [O, O]),
VFun
starttls_required, tls_verify, zlib, max_fsm_queue,
backlog, inet, inet6];
_ ->
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
"option from mod_stream_mgmt module", [O, O]),
mod_stream_mgmt:mod_opt_type(O)
end.
+112 -39
View File
@@ -41,7 +41,8 @@
-export([create_captcha/6, build_captcha_html/2,
check_captcha/2, process_reply/1, process/2,
is_feature_available/0, create_captcha_x/5,
opt_type/1]).
opt_type/1, host_up/1, host_down/1,
config_reloaded/0, process_iq/1]).
-include("xmpp.hrl").
-include("ejabberd.hrl").
@@ -53,7 +54,8 @@
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
-record(state, {limits = treap:empty() :: treap:treap()}).
-record(state, {limits = treap:empty() :: treap:treap(),
enabled = false :: boolean()}).
-record(captcha, {id :: binary(),
pid :: pid() | undefined,
@@ -214,6 +216,25 @@ process_reply(#xcaptcha{xdata = #xdata{} = X}) ->
process_reply(_) ->
{error, malformed}.
process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) ->
case process_reply(El) of
ok ->
xmpp:make_iq_result(IQ);
{error, malformed} ->
Txt = <<"Incorrect CAPTCHA submit">>,
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
{error, _} ->
Txt = <<"The CAPTCHA verification has failed">>,
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang))
end;
process_iq(#iq{type = get, lang = Lang} = IQ) ->
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
process_iq(#iq{lang = Lang} = IQ) ->
?INFO_MSG("IQ = ~p", [IQ]),
Txt = <<"No module is handling this query">>,
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
process(_Handlers,
#request{method = 'GET', lang = Lang,
path = [_, Id]}) ->
@@ -261,12 +282,29 @@ process(_Handlers,
process(_Handlers, _Request) ->
ejabberd_web:error(not_found).
host_up(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA,
?MODULE, process_iq).
host_down(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA).
config_reloaded() ->
gen_server:call(?MODULE, config_reloaded, timer:minutes(1)).
init([]) ->
mnesia:delete_table(captcha),
ets:new(captcha,
[named_table, public, {keypos, #captcha.id}]),
check_captcha_setup(),
{ok, #state{}}.
ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
case check_captcha_setup() of
true ->
register_handlers(),
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
{ok, #state{enabled = true}};
false ->
{ok, #state{enabled = false}};
{error, Reason} ->
{stop, Reason}
end.
handle_call({is_limited, Limiter, RateLimit}, _From,
State) ->
@@ -285,6 +323,23 @@ handle_call({is_limited, Limiter, RateLimit}, _From,
Limits),
{reply, false, State#state{limits = NewLimits}}
end;
handle_call(config_reloaded, _From, #state{enabled = Enabled} = State) ->
State1 = case is_feature_available() of
true when not Enabled ->
case check_captcha_setup() of
true ->
register_handlers(),
State#state{enabled = true};
_ ->
State
end;
false when Enabled ->
unregister_handlers(),
State#state{enabled = false};
_ ->
State
end,
{reply, ok, State1};
handle_call(_Request, _From, State) ->
{reply, bad_request, State}.
@@ -293,17 +348,29 @@ handle_cast(_Msg, State) -> {noreply, State}.
handle_info({remove_id, Id}, State) ->
?DEBUG("captcha ~p timed out", [Id]),
case ets:lookup(captcha, Id) of
[#captcha{args = Args, pid = Pid}] ->
if is_pid(Pid) -> Pid ! {captcha_failed, Args};
true -> ok
end,
ets:delete(captcha, Id);
_ -> ok
[#captcha{args = Args, pid = Pid}] ->
callback(captcha_failed, Pid, Args),
ets:delete(captcha, Id);
_ -> ok
end,
{noreply, State};
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
terminate(_Reason, #state{enabled = Enabled}) ->
if Enabled -> unregister_handlers();
true -> ok
end,
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50).
register_handlers() ->
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:add(host_down, ?MODULE, host_down, 50),
lists:foreach(fun host_up/1, ?MYHOSTS).
unregister_handlers() ->
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50),
lists:foreach(fun host_down/1, ?MYHOSTS).
code_change(_OldVsn, State, _Extra) -> {ok, State}.
@@ -467,16 +534,18 @@ is_feature_available() ->
check_captcha_setup() ->
case is_feature_available() of
true ->
case create_image() of
{ok, _, _, _} -> ok;
_Err ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
"but it can't generate images.",
[]),
throw({error, captcha_cmd_enabled_but_fails})
end;
false -> ok
true ->
case create_image() of
{ok, _, _, _} ->
true;
Err ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
"but it can't generate images.",
[]),
Err
end;
false ->
false
end.
lookup_captcha(Id) ->
@@ -491,22 +560,18 @@ lookup_captcha(Id) ->
check_captcha(Id, ProvidedKey) ->
case ets:lookup(captcha, Id) of
[#captcha{pid = Pid, args = Args, key = ValidKey,
tref = Tref}] ->
ets:delete(captcha, Id),
erlang:cancel_timer(Tref),
if ValidKey == ProvidedKey ->
if is_pid(Pid) -> Pid ! {captcha_succeed, Args};
true -> ok
end,
captcha_valid;
true ->
if is_pid(Pid) -> Pid ! {captcha_failed, Args};
true -> ok
end,
captcha_non_valid
end;
_ -> captcha_not_found
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
ets:delete(captcha, Id),
erlang:cancel_timer(Tref),
if ValidKey == ProvidedKey ->
callback(captcha_succeed, Pid, Args),
captcha_valid;
true ->
callback(captcha_failed, Pid, Args),
captcha_non_valid
end;
_ ->
captcha_not_found
end.
clean_treap(Treap, CleanPriority) ->
@@ -520,6 +585,14 @@ clean_treap(Treap, CleanPriority) ->
end
end.
-spec callback(captcha_succeed | captcha_failed, pid(), term()) -> any().
callback(Result, _Pid, F) when is_function(F) ->
F(Result);
callback(Result, Pid, Args) when is_pid(Pid) ->
Pid ! {Result, Args};
callback(_, _, _) ->
ok.
now_priority() ->
-p1_time_compat:system_time(micro_seconds).
+4 -4
View File
@@ -259,9 +259,9 @@ get_commands_spec() ->
"documentation should be stored",
"Regexp matching names of commands or modules "
"that will be included inside generated document",
"Comma separated list of languages (choosen from java, perl, xmlrpc, json)"
"Comma separated list of languages (chosen from java, perl, xmlrpc, json)"
"that will have example invocation include in markdown document"],
result_desc = "0 if command failed, 1 when succedded",
result_desc = "0 if command failed, 1 when succeeded",
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
result_example = ok},
#ejabberd_commands{name = gen_markdown_doc_for_commands, tags = [documentation],
@@ -273,9 +273,9 @@ get_commands_spec() ->
"documentation should be stored",
"Regexp matching names of commands or modules "
"that will be included inside generated document",
"Comma separated list of languages (choosen from java, perl, xmlrpc, json)"
"Comma separated list of languages (chosen from java, perl, xmlrpc, json)"
"that will have example invocation include in markdown document"],
result_desc = "0 if command failed, 1 when succedded",
result_desc = "0 if command failed, 1 when succeeded",
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
result_example = ok}].
+11 -10
View File
@@ -363,10 +363,6 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
args=Args, args_desc=ArgsDesc,
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
try
LDesc = case LongDesc of
"" -> Desc;
_ -> LongDesc
end,
ArgsText = case ArgsDesc of
none ->
[?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput)
@@ -393,11 +389,15 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
end
end,
[?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
?TAG(p, ?RAW(LDesc)),
?TAG(h2, <<"Arguments:">>), ArgsText,
?TAG(h2, <<"Result:">>), ResultText,
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
[?TAG(h1, atom_to_list(Name)),
?TAG(p, ?RAW(Desc)),
case LongDesc of
"" -> [];
_ -> ?TAG(p, ?RAW(LongDesc))
end,
?TAG(h2, <<"Arguments:">>), ArgsText,
?TAG(h2, <<"Result:">>), ResultText,
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
catch
_:Ex ->
throw(iolist_to_binary(io_lib:format(
@@ -458,9 +458,10 @@ generate_md_output(File, RegExp, Languages) ->
Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) ->
N1 =< N2
end, Cmds2),
Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3],
Langs = binary:split(Languages, <<",">>, [global]),
Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>,
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds3),
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4),
{ok, Fh} = file:open(File, [write]),
io:format(Fh, "~s~s", [Header, Out]),
file:close(Fh),
+65 -64
View File
@@ -28,7 +28,6 @@
-export([start/0, load_file/1, reload_file/0, read_file/1,
get_option/1, get_option/2, add_option/2, has_option/1,
get_vh_by_auth_method/1,
get_version/0, get_myhosts/0, get_mylang/0, get_lang/1,
get_ejabberd_config_path/0, is_using_elixir_config/0,
prepare_opt_val/4, transform_options/1, collect_options/1,
@@ -37,7 +36,8 @@
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1]).
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
codec_options/1, get_plain_terms_file/2, negotiation_timeout/0]).
-export([start/2]).
@@ -71,8 +71,10 @@
start() ->
ConfigFile = get_ejabberd_config_path(),
?INFO_MSG("Loading configuration from ~s", [ConfigFile]),
p1_options:start_link(ejabberd_options),
p1_options:start_link(ejabberd_db_modules),
catch ets:new(ejabberd_options,
[named_table, public, {read_concurrency, true}]),
catch ets:new(ejabberd_db_modules,
[named_table, public, {read_concurrency, true}]),
State1 = load_file(ConfigFile),
UnixTime = p1_time_compat:system_time(seconds),
SharedKey = case erlang:get_cookie() of
@@ -105,9 +107,21 @@ hosts_to_start(State) ->
%% At the moment, these functions are mainly used to setup unit tests.
-spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok.
start(Hosts, Opts) ->
p1_options:start_link(ejabberd_options),
p1_options:start_link(ejabberd_db_modules),
set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})),
catch ets:new(ejabberd_options,
[named_table, public, {read_concurrency, true}]),
catch ets:new(ejabberd_db_modules,
[named_table, public, {read_concurrency, true}]),
UnixTime = p1_time_compat:system_time(seconds),
SharedKey = case erlang:get_cookie() of
nocookie ->
str:sha(randoms:get_string());
Cookie ->
str:sha(misc:atom_to_binary(Cookie))
end,
State1 = #state{opts = Opts},
State2 = set_option({node_start, global}, UnixTime, State1),
State3 = set_option({shared_key, global}, SharedKey, State2),
set_opts(set_hosts_in_options(Hosts, State3)),
ok.
%% @doc Get the filename of the ejabberd configuration file.
@@ -441,8 +455,7 @@ get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(D
exit_or_halt(ExitText) ->
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
[] ->
timer:sleep(1000),
halt(string:substr(ExitText, 1, 199));
ejabberd:halt();
[_] ->
exit(ExitText)
end.
@@ -758,17 +771,12 @@ append_option({Opt, Host}, Val, State) ->
set_opts(State) ->
Opts = State#state.opts,
ets:select_delete(ejabberd_options,
ets:fun2ms(
fun({{node_start, _}, _}) -> false;
({{shared_key, _}, _}) -> false;
(_) -> true
end)),
lists:foreach(
fun(#local_config{key = {Opt, Host}, value = Val}) ->
p1_options:insert(ejabberd_options, Opt, Host, Val)
end, Opts),
p1_options:compile(ejabberd_options),
ets:insert(
ejabberd_options,
lists:map(
fun(#local_config{key = Key, value = Val}) ->
{Key, Val}
end, Opts)),
set_log_level().
set_log_level() ->
@@ -784,8 +792,8 @@ add_local_option(Opt, Val) ->
add_option(Opt, Val) when is_atom(Opt) ->
add_option({Opt, global}, Val);
add_option({Opt, Host}, Val) ->
p1_options:insert(ejabberd_options, Opt, Host, Val),
p1_options:compile(ejabberd_options).
ets:insert(ejabberd_options, {{Opt, Host}, Val}),
ok.
-spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
@@ -862,13 +870,12 @@ get_option(Opt, Default) ->
"format. This is likely a bug", [Opt]),
{undefined, global}
end,
case ejabberd_options:is_known(Key) of
true ->
case ejabberd_options:Key(Host) of
{ok, Val} -> Val;
undefined -> Default
try ets:lookup_element(ejabberd_options, {Key, Host}, 2)
catch _:badarg when Host /= global ->
try ets:lookup_element(ejabberd_options, {Key, global}, 2)
catch _:badarg -> Default
end;
false ->
_:badarg ->
Default
end.
@@ -878,8 +885,8 @@ has_option(Opt) ->
init_module_db_table(Modules) ->
%% Dirty hack for mod_pubsub
p1_options:insert(ejabberd_db_modules, mod_pubsub, mnesia, true),
p1_options:insert(ejabberd_db_modules, mod_pubsub, sql, true),
ets:insert(ejabberd_db_modules, {{mod_pubsub, mnesia}, true}),
ets:insert(ejabberd_db_modules, {{mod_pubsub, sql}, true}),
lists:foreach(
fun(M) ->
case re:split(atom_to_list(M), "_", [{return, list}]) of
@@ -891,14 +898,13 @@ init_module_db_table(Modules) ->
BareMod = list_to_atom(string:join(lists:reverse(T), "_")),
case is_behaviour(BareMod, M) of
true ->
p1_options:insert(ejabberd_db_modules,
BareMod, Suffix, true);
ets:insert(ejabberd_db_modules,
{{BareMod, Suffix}, true});
false ->
ok
end
end
end, Modules),
p1_options:compile(ejabberd_db_modules).
end, Modules).
is_behaviour(Behav, Mod) ->
try Mod:module_info(attributes) of
@@ -920,20 +926,20 @@ is_behaviour(Behav, Mod) ->
v_db(Mod, internal) -> v_db(Mod, mnesia);
v_db(Mod, odbc) -> v_db(Mod, sql);
v_db(Mod, Type) ->
case ejabberd_db_modules:is_known(Mod) of
true ->
case ejabberd_db_modules:Mod(Type) of
{ok, _} -> Type;
_ -> erlang:error(badarg)
end;
false ->
erlang:error(badarg)
case ets:member(ejabberd_db_modules, {Mod, Type}) of
true -> Type;
false -> erlang:error(badarg)
end.
-spec v_dbs(module()) -> [atom()].
v_dbs(Mod) ->
ejabberd_db_modules:get_scope(Mod).
ets:select(
ejabberd_db_modules,
ets:fun2ms(
fun({{M, Type}, _}) when M == Mod ->
Type
end)).
-spec v_dbs_mods(module()) -> [module()].
@@ -1039,26 +1045,6 @@ validate_opts(#state{opts = Opts} = State, ModOpts) ->
end, Opts),
State#state{opts = NewOpts}.
-spec get_vh_by_auth_method(atom()) -> [binary()].
%% Return the list of hosts with a given auth method
get_vh_by_auth_method(AuthMethod) ->
Hosts = ejabberd_options:get_scope(auth_method),
get_vh_by_auth_method(AuthMethod, Hosts, []).
get_vh_by_auth_method(Method, [Host|Hosts], Result) ->
Methods = get_option({auth_method, Host}, []),
case lists:member(Method, Methods) of
true when Host == global ->
get_myhosts();
true ->
get_vh_by_auth_method(Method, Hosts, [Host|Result]);
false ->
get_vh_by_auth_method(Method, Hosts, Result)
end;
get_vh_by_auth_method(_, [], Result) ->
Result.
%% @spec (Path::string()) -> true | false
is_file_readable(Path) ->
case file:read_file_info(Path) of
@@ -1428,15 +1414,19 @@ opt_type(cache_life_time) ->
(infinity) -> infinity;
(unlimited) -> infinity
end;
opt_type(negotiation_timeout) ->
fun(T) when T > 0 -> T end;
opt_type(shared_key) ->
fun iolist_to_binary/1;
opt_type(node_start) ->
fun(I) when is_integer(I), I>=0 -> I end;
opt_type(validate_stream) ->
fun(B) when is_boolean(B) -> B end;
opt_type(_) ->
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
default_db, default_ram_db, queue_type, queue_dir, loglevel,
use_cache, cache_size, cache_missed, cache_life_time,
shared_key, node_start].
shared_key, node_start, validate_stream, negotiation_timeout].
-spec may_hide_data(any()) -> any().
may_hide_data(Data) ->
@@ -1483,3 +1473,14 @@ cache_missed(Host) ->
%% NOTE: the integer value returned is in *seconds*
cache_life_time(Host) ->
get_option({cache_life_time, Host}, 3600).
-spec codec_options(binary() | global) -> [xmpp:decode_option()].
codec_options(Host) ->
case get_option({validate_stream, Host}, false) of
true -> [];
false -> [ignore_els]
end.
-spec negotiation_timeout() -> pos_integer().
negotiation_timeout() ->
timer:seconds(get_option(negotiation_timeout, 30)).
+2 -2
View File
@@ -133,7 +133,7 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%-----------------------------
%% ejabberdctl Command managment
%% ejabberdctl Command management
%%-----------------------------
register_commands(CmdDescs, Module, Function) ->
@@ -866,7 +866,7 @@ print(Format, Args) ->
io:format(lists:flatten(Format), Args).
%%-----------------------------
%% Command managment
%% Command management
%%-----------------------------
%%+++
-44
View File
@@ -1,44 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @doc
%%% This is a stub module which will be replaced during
%%% configuration load via p1_options:compile/1
%%% The only purpose of this file is to shut up xref/dialyzer
%%% @end
%%% Created : 27 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2018 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(ejabberd_db_modules).
%% API
-export([is_known/1, get_scope/1]).
%%%===================================================================
%%% API
%%%===================================================================
is_known(_) ->
false.
get_scope(_) ->
[].
%%%===================================================================
%%% Internal functions
%%%===================================================================
+8 -55
View File
@@ -31,7 +31,7 @@
%% External exports
-export([start/2, start_link/2, become_controller/1,
socket_type/0, receive_headers/1, url_encode/1,
socket_type/0, receive_headers/1,
transform_listen_option/2, listen_opt_type/1]).
-export([init/2, opt_type/1]).
@@ -651,7 +651,7 @@ parse_lang(Langs) ->
end.
% Code below is taken (with some modifications) from the yaws webserver, which
% is distributed under the folowing license:
% is distributed under the following license:
%
% This software (the yaws webserver) is free software.
% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
@@ -679,7 +679,7 @@ url_decode_q_split(<<>>, Ack) ->
path_decode(Path) -> path_decode(Path, <<>>).
path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
Hex = hex_to_integer([Hi, Lo]),
Hex = list_to_integer([Hi, Lo], 16),
if Hex == 0 -> exit(badurl);
true -> ok
end,
@@ -716,22 +716,6 @@ expand_custom_headers(Headers) ->
{K, misc:expand_keyword(<<"@VERSION@">>, V, ?VERSION)}
end, Headers).
%% hex_to_integer
hex_to_integer(Hex) ->
case catch list_to_integer(Hex, 16) of
{'EXIT', _} -> old_hex_to_integer(Hex);
X -> X
end.
old_hex_to_integer(Hex) ->
DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
(H) when H >= $A, H =< $F -> H - $A + 10;
(H) when H >= $0, H =< $9 -> H - $0
end,
lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0,
Hex).
code_to_phrase(100) -> <<"Continue">>;
code_to_phrase(101) -> <<"Switching Protocols ">>;
code_to_phrase(200) -> <<"OK">>;
@@ -802,7 +786,7 @@ parse_urlencoded(S) ->
parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur,
State) ->
Hex = hex_to_integer([Hi, Lo]),
Hex = list_to_integer([Hi, Lo], 16),
parse_urlencoded(Tail, Last, <<Cur/binary, Hex>>, State);
parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) ->
[{Cur, <<"">>} | parse_urlencoded(Tail,
@@ -822,41 +806,6 @@ parse_urlencoded(<<>>, Last, Cur, _State) ->
[{Last, Cur}];
parse_urlencoded(undefined, _, _, _) -> [].
url_encode(A) ->
url_encode(A, <<>>).
url_encode(<<H:8, T/binary>>, Acc) when
(H >= $a andalso H =< $z) orelse
(H >= $A andalso H =< $Z) orelse
(H >= $0 andalso H =< $9) orelse
H == $_ orelse
H == $. orelse
H == $- orelse
H == $/ orelse
H == $: ->
url_encode(T, <<Acc/binary, H>>);
url_encode(<<H:8, T/binary>>, Acc) ->
case integer_to_hex(H) of
[X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
[X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
end;
url_encode(<<>>, Acc) ->
Acc.
integer_to_hex(I) ->
case catch erlang:integer_to_list(I, 16) of
{'EXIT', _} -> old_integer_to_hex(I);
Int -> Int
end.
old_integer_to_hex(I) when I < 10 -> integer_to_list(I);
old_integer_to_hex(I) when I < 16 -> [I - 10 + $A];
old_integer_to_hex(I) when I >= 16 ->
N = trunc(I / 16),
old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
% The following code is mostly taken from yaws_ssl.erl
toupper(C) when C >= $a andalso C =< $z -> C - 32;
@@ -994,6 +943,10 @@ listen_opt_type(default_host) ->
fun(A) -> A end;
listen_opt_type(custom_headers) ->
fun expand_custom_headers/1;
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
%% TODO
fun(A) -> A end.
+1
View File
@@ -134,6 +134,7 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
({resume_timeout, _}) -> true;
({max_resume_timeout, _}) -> true;
({resend_on_timeout, _}) -> true;
({access, _}) -> true;
(_) -> false
end, HOpts),
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
+2 -2
View File
@@ -144,7 +144,7 @@ noreply(#state{expire = Expire} = State) ->
-spec encode_id(non_neg_integer(), binary()) -> binary().
encode_id(Expire, Rnd) ->
ExpireBin = integer_to_binary(Expire),
Node = atom_to_binary(node(), utf8),
Node = ejabberd_cluster:node_id(),
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, Node/binary>>),
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
@@ -155,7 +155,7 @@ decode_id(<<"rr-", ID/binary>>) ->
[Rnd, Rest] = binary:split(Tail, <<"-">>),
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, NodeBin/binary>>),
Node = erlang:binary_to_existing_atom(NodeBin, utf8),
Node = ejabberd_cluster:get_node_by_id(NodeBin),
Expire = binary_to_integer(ExpireBin),
{ok, Expire, Rnd, Node}
catch _:{badmatch, _} ->
+19 -3
View File
@@ -107,7 +107,7 @@ init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
{reuseaddr, true} |
SockOpts]) of
{ok, Socket} ->
%% Inform my parent that this port was opened succesfully
%% Inform my parent that this port was opened successfully
proc_lib:init_ack({ok, self()}),
application:ensure_started(ejabberd),
start_module_sup(Port, Module),
@@ -133,7 +133,7 @@ init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS),
%% Inform my parent that this port was opened succesfully
%% Inform my parent that this port was opened successfully
proc_lib:init_ack({ok, self()}),
application:ensure_started(ejabberd),
start_module_sup(Port, Module),
@@ -572,10 +572,18 @@ transform_options({listen, LOpts}, Opts) ->
transform_options(Opt, Opts) ->
[Opt|Opts].
known_listen_options(Module) ->
try Module:listen_options() of
Opts -> [element(1, Opt) || Opt <- Opts]
catch _:undef ->
Module:listen_opt_type('')
end.
-spec validate_module_options(module(), [{atom(), any()}]) -> [{atom(), any()}].
validate_module_options(Module, Opts) ->
try Module:listen_opt_type('') of
try known_listen_options(Module) of
_ ->
maybe_start_zlib(Opts),
lists:filtermap(
fun({Opt, Val}) ->
case validate_module_option(Module, Opt, Val) of
@@ -664,5 +672,13 @@ all_zero_ip(Opts) ->
false -> {0,0,0,0}
end.
maybe_start_zlib(Opts) ->
case proplists:get_bool(zlib, Opts) of
true ->
ejabberd:start_app(ezlib);
false ->
ok
end.
opt_type(listen) -> fun validate_cfg/1;
opt_type(_) -> [listen].
+8 -15
View File
@@ -34,7 +34,7 @@
-export([route/1, process_iq/1,
get_features/1,
register_iq_handler/5,
register_iq_handler/4,
unregister_iq_handler/2,
bounce_resource_packet/1,
host_up/1, host_down/1]).
@@ -78,8 +78,8 @@ process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
XMLNS = xmpp:get_ns(El),
Host = To#jid.lserver,
case ets:lookup(?IQTABLE, {Host, XMLNS}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:handle(Host, Module, Function, Opts, Packet);
[{_, Module, Function}] ->
gen_iq_handler:handle(Host, Module, Function, Packet);
[] ->
Txt = <<"No module is handling this query">>,
Err = xmpp:err_service_unavailable(Txt, Lang),
@@ -112,11 +112,10 @@ route_iq(IQ, Fun) ->
route_iq(IQ, Fun, Timeout) ->
ejabberd_router:route_iq(IQ, Fun, undefined, Timeout).
-spec register_iq_handler(binary(), binary(), module(), function(),
gen_iq_handler:opts()) -> ok.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
-spec register_iq_handler(binary(), binary(), module(), function()) -> ok.
register_iq_handler(Host, XMLNS, Module, Fun) ->
gen_server:cast(?MODULE,
{register_iq_handler, Host, XMLNS, Module, Fun, Opts}).
{register_iq_handler, Host, XMLNS, Module, Fun}).
-spec unregister_iq_handler(binary(), binary()) -> ok.
unregister_iq_handler(Host, XMLNS) ->
@@ -160,19 +159,13 @@ init([]) ->
handle_call(_Request, _From, State) ->
Reply = ok, {reply, Reply, State}.
handle_cast({register_iq_handler, Host, XMLNS, Module,
Function, Opts},
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
State) ->
ets:insert(?IQTABLE,
{{Host, XMLNS}, Module, Function, Opts}),
{{Host, XMLNS}, Module, Function}),
{noreply, State};
handle_cast({unregister_iq_handler, Host, XMLNS},
State) ->
case ets:lookup(?IQTABLE, {Host, XMLNS}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ -> ok
end,
ets:delete(?IQTABLE, {Host, XMLNS}),
{noreply, State};
handle_cast(_Msg, State) -> {noreply, State}.
-44
View File
@@ -1,44 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @doc
%%% This is a stub module which will be replaced during
%%% configuration load via p1_options:compile/1
%%% The only purpose of this file is to shut up xref/dialyzer
%%% @end
%%% Created : 16 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2018 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(ejabberd_options).
%% API
-export([is_known/1, get_scope/1]).
%%%===================================================================
%%% API
%%%===================================================================
is_known(_) ->
false.
get_scope(_) ->
[].
%%%===================================================================
%%% Internal functions
%%%===================================================================
+1 -1
View File
@@ -277,7 +277,7 @@ get_roster(User, Server) ->
get_private(User, Server) ->
case mod_private:get_data(User, Server) of
[_|_] = Els ->
[xmpp:encode(#private{xml_els = Els})];
[xmpp:encode(#private{sub_els = Els})];
_ ->
[]
end.
+2 -52
View File
@@ -37,7 +37,6 @@
-include("logger.hrl").
-record(state, {validate = true :: boolean(),
notify = false :: boolean(),
paths = [] :: [file:filename()],
certs = #{} :: map(),
graph :: digraph:graph(),
@@ -173,6 +172,7 @@ config_reloaded() ->
true -> init_cache();
false -> delete_cache()
end,
fast_tls:clear_cache(),
gen_server:call(?MODULE, config_reloaded, 60000).
opt_type(ca_path) ->
@@ -197,7 +197,6 @@ opt_type(_) ->
%%% gen_server callbacks
%%%===================================================================
init([]) ->
Notify = start_fs(),
process_flag(trap_exit, true),
ets:new(?MODULE, [named_table, public]),
ejabberd_hooks:add(route_registered, ?MODULE, route_registered, 50),
@@ -214,7 +213,7 @@ init([]) ->
end,
G = digraph:new([acyclic]),
init_cache(),
State = #state{validate = Validate, notify = Notify, graph = G},
State = #state{validate = Validate, graph = G},
case filelib:ensure_dir(filename:join(certs_dir(), "foo")) of
ok ->
clean_dir(certs_dir()),
@@ -279,20 +278,6 @@ handle_call(_Request, _From, State) ->
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({_, {fs, file_event}, {File, Events}}, State) ->
?DEBUG("got FS events for ~s: ~p", [File, Events]),
Path = iolist_to_binary(File),
case lists:member(modified, Events) of
true ->
case lists:member(Path, State#state.paths) of
true ->
handle_cast(config_reloaded, State);
false ->
{noreply, State}
end;
false ->
{noreply, State}
end;
handle_info(_Info, State) ->
?WARNING_MSG("unexpected info: ~p", [_Info]),
{noreply, State}.
@@ -419,7 +404,6 @@ build_chain_and_check(State) ->
?DEBUG("Validating certificates", []),
Errors = validate(CertPaths, State#state.validate),
?DEBUG("Subscribing to file events", []),
subscribe(State),
lists:foreach(
fun({Cert, Why}) ->
Path = maps:get(Cert, State#state.certs),
@@ -854,40 +838,6 @@ short_name_hash(_) ->
"".
-endif.
-spec subscribe(state()) -> ok.
subscribe(#state{notify = true} = State) ->
lists:foreach(
fun(Path) ->
Dir = filename:dirname(Path),
Name = list_to_atom(integer_to_list(erlang:phash2(Dir))),
case fs:start_link(Name, Dir) of
{ok, _} ->
?DEBUG("Subscribed to FS events from ~s", [Dir]),
fs:subscribe(Name);
{error, _} ->
ok
end
end, State#state.paths);
subscribe(_) ->
ok.
-spec start_fs() -> boolean().
start_fs() ->
application:load(fs),
application:set_env(fs, backwards_compatible, false),
case application:ensure_all_started(fs) of
{ok, _} -> true;
{error, {already_loaded, _}} -> true;
{error, Reason} ->
?ERROR_MSG("Failed to load 'fs' Erlang application: ~p; "
"certificates change detection will be disabled. "
"You should now manually run `ejabberdctl "
"reload_config` whenever certificates are changed "
"on disc",
[Reason]),
false
end.
wildcard(Path) when is_binary(Path) ->
wildcard(binary_to_list(Path));
wildcard(Path) ->
+61 -7
View File
@@ -85,11 +85,65 @@ greplace(String, Regexp, New) ->
A -> A
end.
-spec sh_to_awk(binary()) -> binary().
sh_to_awk(ShRegExp) ->
case exec({xmerl_regexp, sh_to_awk, [binary_to_list(ShRegExp)]},
{regexp, sh_to_awk, [binary_to_list(ShRegExp)]})
of
A -> iolist_to_binary(A)
end.
%% This code was copied and adapted from xmerl_regexp.erl
-spec sh_to_awk(binary()) -> binary().
sh_to_awk(Sh) ->
iolist_to_binary([<<"^(">>, sh_to_awk_1(Sh)]). %Fix the beginning
sh_to_awk_1(<<"*", Sh/binary>>) -> %This matches any string
[<<".*">>, sh_to_awk_1(Sh)];
sh_to_awk_1(<<"?", Sh/binary>>) -> %This matches any character
[$., sh_to_awk_1(Sh)];
sh_to_awk_1(<<"[^]", Sh/binary>>) -> %This takes careful handling
[<<"\\^">>, sh_to_awk_1(Sh)];
%% Must move '^' to end.
sh_to_awk_1(<<"[^", Sh/binary>>) ->
[$[, sh_to_awk_2(Sh, true)];
sh_to_awk_1(<<"[!", Sh/binary>>) ->
[<<"[^">>, sh_to_awk_2(Sh, false)];
sh_to_awk_1(<<"[", Sh/binary>>) ->
[$[, sh_to_awk_2(Sh, false)];
sh_to_awk_1(<<C:8, Sh/binary>>) -> %% Unspecialise everything else which is not an escape character.
case sh_special_char(C) of
true -> [$\\,C|sh_to_awk_1(Sh)];
false -> [C|sh_to_awk_1(Sh)]
end;
sh_to_awk_1(<<>>) ->
<<")$">>. %Fix the end
sh_to_awk_2(<<"]", Sh/binary>>, UpArrow) ->
[$]|sh_to_awk_3(Sh, UpArrow)];
sh_to_awk_2(Sh, UpArrow) ->
sh_to_awk_3(Sh, UpArrow).
sh_to_awk_3(<<"]", Sh/binary>>, true) ->
[<<"^]">>, sh_to_awk_1(Sh)];
sh_to_awk_3(<<"]", Sh/binary>>, false) ->
[$]|sh_to_awk_1(Sh)];
sh_to_awk_3(<<C:8, Sh/binary>>, UpArrow) ->
[C|sh_to_awk_3(Sh, UpArrow)];
sh_to_awk_3(<<>>, true) ->
[$^|sh_to_awk_1([])];
sh_to_awk_3(<<>>, false) ->
sh_to_awk_1([]).
%% -type sh_special_char(char()) -> bool().
%% Test if a character is a special character.
sh_special_char($|) -> true;
sh_special_char($*) -> true;
sh_special_char($+) -> true;
sh_special_char($?) -> true;
sh_special_char($() -> true;
sh_special_char($)) -> true;
sh_special_char($\\) -> true;
sh_special_char($^) -> true;
sh_special_char($$) -> true;
sh_special_char($.) -> true;
sh_special_char($[) -> true;
sh_special_char($]) -> true;
sh_special_char($") -> true;
sh_special_char(_C) -> false.
+13 -3
View File
@@ -169,7 +169,8 @@ handle_stream_start(_StreamStart, #{lserver := LServer} = State) ->
send(State, xmpp:serr_host_unknown());
true ->
ServerHost = ejabberd_router:host_of_route(LServer),
State#{server_host => ServerHost}
Opts = ejabberd_config:codec_options(LServer),
State#{server_host => ServerHost, codec_options => Opts}
end.
handle_stream_end(Reason, #{server_host := LServer} = State) ->
@@ -258,6 +259,7 @@ init([State, Opts]) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
auth_domains => sets:new(),
xmlns => ?NS_SERVER,
@@ -267,7 +269,8 @@ init([State, Opts]) ->
server_host => ?MYNAME,
established => false,
shaper => Shaper},
ejabberd_hooks:run_fold(s2s_in_init, {ok, State1}, [Opts]).
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
ejabberd_hooks:run_fold(s2s_in_init, {ok, State2}, [Opts]).
handle_call(Request, From, #{server_host := LServer} = State) ->
ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]).
@@ -355,6 +358,9 @@ change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
(supervisor) -> fun((boolean()) -> boolean());
(max_stanza_type) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
(inet) -> fun((boolean()) -> boolean());
(inet6) -> fun((boolean()) -> boolean());
(backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
listen_opt_type(certfile = Opt) ->
@@ -378,6 +384,10 @@ listen_opt_type(max_stanza_size) ->
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
tls_compression, tls, max_fsm_queue].
tls_compression, tls, max_fsm_queue, backlog, inet, inet6].
+7 -3
View File
@@ -254,10 +254,12 @@ handle_recv(El, Pkt, #{server_host := ServerHost} = State) ->
handle_send(El, Pkt, #{server_host := ServerHost} = State) ->
ejabberd_hooks:run_fold(s2s_out_handle_send, ServerHost, State, [El, Pkt]).
handle_timeout(#{on_route := Action} = State) ->
handle_timeout(#{on_route := Action, lang := Lang} = State) ->
case Action of
bounce -> stop(State);
_ -> send(State, xmpp:serr_connection_timeout())
_ ->
Txt = <<"Idle connection">>,
send(State, xmpp:serr_connection_timeout(Txt, Lang))
end.
init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
@@ -268,15 +270,17 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
{_, N} -> N;
false -> unlimited
end,
Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{on_route => queue,
queue => p1_queue:new(QueueType, QueueLimit),
xmlns => ?NS_SERVER,
lang => ?MYLANG,
server_host => ServerHost,
shaper => none},
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
[LServer, RServer]),
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State1}, [Opts]).
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State2}, [Opts]).
handle_call(Request, From, #{server_host := ServerHost} = State) ->
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
+72 -36
View File
@@ -100,38 +100,43 @@ init([State, Opts]) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
Timeout = ejabberd_config:negotiation_timeout(),
State1 = xmpp_stream_in:change_shaper(State, Shaper),
State2 = State1#{access => Access,
xmlns => ?NS_COMPONENT,
lang => ?MYLANG,
server => ?MYNAME,
host_opts => dict:from_list(HostOpts1),
stream_version => undefined,
tls_options => TLSOpts,
check_from => CheckFrom},
ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]).
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
State3 = State2#{access => Access,
xmlns => ?NS_COMPONENT,
lang => ?MYLANG,
server => ?MYNAME,
host_opts => dict:from_list(HostOpts1),
stream_version => undefined,
tls_options => TLSOpts,
global_routes => GlobalRoutes,
check_from => CheckFrom},
ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]).
handle_stream_start(_StreamStart,
#{remote_server := RemoteServer,
lang := Lang,
host_opts := HostOpts} = State) ->
case ejabberd_router:is_my_host(RemoteServer) of
true ->
true ->
Txt = <<"Unable to register route on existing local domain">>,
xmpp_stream_in:send(State, xmpp:serr_conflict(Txt, Lang));
false ->
false ->
NewHostOpts = case dict:is_key(RemoteServer, HostOpts) of
true ->
HostOpts;
false ->
case dict:find(global, HostOpts) of
{ok, GlobalPass} ->
{ok, GlobalPass} ->
dict:from_list([{RemoteServer, GlobalPass}]);
error ->
error ->
HostOpts
end
end,
State#{host_opts => NewHostOpts}
end
end,
CodecOpts = ejabberd_config:codec_options(global),
State#{host_opts => NewHostOpts, codec_options => CodecOpts}
end.
get_password_fun(#{remote_server := RemoteServer,
@@ -139,7 +144,7 @@ get_password_fun(#{remote_server := RemoteServer,
host_opts := HostOpts}) ->
fun(_) ->
case dict:find(RemoteServer, HostOpts) of
{ok, Password} ->
{ok, Password} ->
{Password, undefined};
error ->
?INFO_MSG("(~s) Domain ~s is unconfigured for "
@@ -152,16 +157,22 @@ get_password_fun(#{remote_server := RemoteServer,
handle_auth_success(_, Mech, _,
#{remote_server := RemoteServer, host_opts := HostOpts,
socket := Socket, ip := IP} = State) ->
socket := Socket, ip := IP,
global_routes := GlobalRoutes} = State) ->
?INFO_MSG("(~s) Accepted external component ~s authentication "
"for ~s from ~s",
[xmpp_socket:pp(Socket), Mech, RemoteServer,
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
lists:foreach(
fun (H) ->
ejabberd_router:register_route(H, ?MYNAME),
ejabberd_hooks:run(component_connected, [H])
end, dict:fetch_keys(HostOpts)),
Routes = if GlobalRoutes ->
dict:fetch_keys(HostOpts);
true ->
[RemoteServer]
end,
lists:foreach(
fun(H) ->
ejabberd_router:register_route(H, ?MYNAME),
ejabberd_hooks:run(component_connected, [H])
end, Routes),
State.
handle_auth_failure(_, Mech, Reason,
@@ -179,11 +190,17 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
Pkt = xmpp:put_meta(Pkt0, ip, IP),
From = xmpp:get_from(Pkt),
case check_from(From, State) of
true ->
ejabberd_router:route(Pkt),
State;
false ->
Txt = <<"Improper domain part of 'from' attribute">>,
true ->
{Pkt2, State2} = ejabberd_hooks:run_fold(component_send_packet, {Pkt, State}, []),
case Pkt2 of
drop ->
ok;
_ ->
ejabberd_router:route(Pkt2)
end,
State2;
false ->
Txt = <<"Improper domain part of 'from' attribute">>,
Err = xmpp:serr_invalid_from(Txt, Lang),
xmpp_stream_in:send(State, Err)
end;
@@ -192,7 +209,7 @@ handle_authenticated_packet(_Pkt, State) ->
handle_info({route, Packet}, #{access := Access} = State) ->
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
allow ->
allow ->
xmpp_stream_in:send(State, Packet);
deny ->
Lang = xmpp:get_lang(Packet),
@@ -204,17 +221,27 @@ handle_info(Info, State) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
State.
terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) ->
terminate(Reason, #{stream_state := StreamState,
host_opts := HostOpts,
remote_server := RemoteServer,
global_routes := GlobalRoutes}) ->
case StreamState of
established ->
Routes = if GlobalRoutes ->
dict:fetch_keys(HostOpts);
true ->
[RemoteServer]
end,
lists:foreach(
fun(H) ->
ejabberd_router:unregister_route(H),
ejabberd_router:unregister_route(H),
ejabberd_hooks:run(component_disconnected, [H, Reason])
end, dict:fetch_keys(HostOpts));
end, Routes);
_ ->
ok
end.
ok
end;
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -267,9 +294,12 @@ transform_listen_option(Opt, Opts) ->
(check_from) -> fun((boolean()) -> boolean());
(password) -> fun((boolean()) -> boolean());
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
[{binary(), binary() | undefined}]);
[{binary(), binary() | undefined}]);
(max_stanza_type) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
(inet) -> fun((boolean()) -> boolean());
(inet6) -> fun((boolean()) -> boolean());
(backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
@@ -298,6 +328,8 @@ listen_opt_type(hosts) ->
{iolist_to_binary(Host), Password}
end, HostOpts)
end;
listen_opt_type(global_routes) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(max_stanza_size) ->
fun(I) when is_integer(I) -> I;
(unlimited) -> infinity;
@@ -305,7 +337,11 @@ listen_opt_type(max_stanza_size) ->
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
protocol_options, tls_compression, password, hosts, check_from,
max_fsm_queue].
max_fsm_queue, global_routes, backlog, inet, inet6].
+17 -28
View File
@@ -43,7 +43,7 @@
open_session/5,
open_session/6,
close_session/4,
check_in_subscription/6,
check_in_subscription/2,
bounce_offline_message/1,
disconnect_removed_user/2,
get_user_resources/2,
@@ -58,7 +58,7 @@
get_vh_session_list/1,
get_vh_session_number/1,
get_vh_by_backend/1,
register_iq_handler/5,
register_iq_handler/4,
unregister_iq_handler/2,
force_update_presence/1,
connected_users/0,
@@ -80,7 +80,8 @@
host_down/1,
make_sid/0,
clean_cache/1,
config_reloaded/0
config_reloaded/0,
is_online/1
]).
-export([init/1, handle_call/3, handle_cast/2,
@@ -183,10 +184,9 @@ close_session(SID, User, Server, Resource) ->
ejabberd_hooks:run(sm_remove_connection_hook,
JID#jid.lserver, [SID, JID, Info]).
-spec check_in_subscription(boolean(), binary(), binary(), jid(),
subscribe | subscribed | unsubscribe | unsubscribed,
binary()) -> boolean() | {stop, false}.
check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
-spec check_in_subscription(boolean(), presence()) -> boolean() | {stop, false}.
check_in_subscription(Acc, #presence{to = To}) ->
#jid{user = User, server = Server} = To,
case ejabberd_auth:user_exists(User, Server) of
true -> Acc;
false -> {stop, false}
@@ -397,11 +397,11 @@ get_vh_session_number(Server) ->
Mod = get_sm_backend(LServer),
length(online(get_sessions(Mod, LServer))).
-spec register_iq_handler(binary(), binary(), atom(), atom(), list()) -> ok.
-spec register_iq_handler(binary(), binary(), atom(), atom()) -> ok.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
register_iq_handler(Host, XMLNS, Module, Fun) ->
?GEN_SERVER:cast(?MODULE,
{register_iq_handler, Host, XMLNS, Module, Fun, Opts}).
{register_iq_handler, Host, XMLNS, Module, Fun}).
-spec unregister_iq_handler(binary(), binary()) -> ok.
@@ -448,19 +448,13 @@ init([]) ->
handle_call(_Request, _From, State) ->
Reply = ok, {reply, Reply, State}.
handle_cast({register_iq_handler, Host, XMLNS, Module,
Function, Opts},
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
State) ->
ets:insert(sm_iqtable,
{{Host, XMLNS}, Module, Function, Opts}),
{{Host, XMLNS}, Module, Function}),
{noreply, State};
handle_cast({unregister_iq_handler, Host, XMLNS},
State) ->
case ets:lookup(sm_iqtable, {Host, XMLNS}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ -> ok
end,
ets:delete(sm_iqtable, {Host, XMLNS}),
{noreply, State};
handle_cast(_Msg, State) -> {noreply, State}.
@@ -627,19 +621,14 @@ do_route(To, Term) ->
end.
-spec do_route(stanza()) -> any().
do_route(#presence{from = From, to = To, type = T, status = Status} = Packet)
do_route(#presence{to = To, type = T} = Packet)
when T == subscribe; T == subscribed; T == unsubscribe; T == unsubscribed ->
?DEBUG("processing subscription:~n~s", [xmpp:pp(Packet)]),
#jid{user = User, server = Server,
luser = LUser, lserver = LServer} = To,
Reason = if T == subscribe -> xmpp:get_text(Status);
true -> <<"">>
end,
#jid{luser = LUser, lserver = LServer} = To,
case is_privacy_allow(Packet) andalso
ejabberd_hooks:run_fold(
roster_in_subscription,
LServer, false,
[User, Server, From, T, Reason]) of
LServer, false, [Packet]) of
true ->
Mod = get_sm_backend(LServer),
lists:foreach(
@@ -867,8 +856,8 @@ process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
XMLNS = xmpp:get_ns(El),
Host = To#jid.lserver,
case ets:lookup(sm_iqtable, {Host, XMLNS}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:handle(Host, Module, Function, Opts, Packet);
[{_, Module, Function}] ->
gen_iq_handler:handle(Host, Module, Function, Packet);
[] ->
Txt = <<"No module is handling this query">>,
Err = xmpp:err_service_unavailable(Txt, Lang),
+13 -1
View File
@@ -39,6 +39,7 @@
sql_bloc/2,
abort/1,
restart/1,
use_new_schema/0,
sql_query_to_iolist/1,
escape/1,
standard_escape/1,
@@ -95,6 +96,12 @@
-define(PREPARE_KEY, ejabberd_sql_prepare).
-ifdef(NEW_SQL_SCHEMA).
-define(USE_NEW_SCHEMA_DEFAULT, true).
-else.
-define(USE_NEW_SCHEMA_DEFAULT, false).
-endif.
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -272,6 +279,9 @@ sqlite_file(Host) ->
binary_to_list(File)
end.
use_new_schema() ->
ejabberd_config:get_option(new_sql_schema, ?USE_NEW_SCHEMA_DEFAULT).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -1133,9 +1143,11 @@ opt_type(sql_connect_timeout) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(sql_queue_type) ->
fun(ram) -> ram; (file) -> file end;
opt_type(new_sql_schema) -> fun(B) when is_boolean(B) -> B end;
opt_type(_) ->
[sql_database, sql_keepalive_interval,
sql_password, sql_port, sql_server,
sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile,
sql_ssl_cafile, sql_queue_type, sql_query_timeout,
sql_connect_timeout].
sql_connect_timeout,
new_sql_schema].
+105 -90
View File
@@ -28,7 +28,7 @@
%% API
-export([parse_transform/2, format_error/1]).
-export([parse/2]).
%-export([parse/2]).
-include("ejabberd_sql_pt.hrl").
@@ -41,7 +41,8 @@
res_vars = [],
res_pos = 0,
server_host_used = false,
used_vars = []}).
used_vars = [],
use_new_schema}).
-define(QUERY_RECORD, "sql_query").
@@ -88,26 +89,7 @@ transform(Form) ->
[Arg] ->
case erl_syntax:type(Arg) of
string ->
S = erl_syntax:string_value(Arg),
Pos = erl_syntax:get_pos(Arg),
ParseRes = parse(S, Pos),
UnusedVars =
case ParseRes#state.server_host_used of
{true, SHVar} ->
case ?USE_NEW_SCHEMA of
true -> [];
false -> [SHVar]
end;
false ->
add_warning(
Pos, no_server_host),
[]
end,
set_pos(
add_unused_vars(
make_sql_query(ParseRes),
UnusedVars),
Pos);
transform_sql(Arg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL argument must be "
@@ -123,26 +105,7 @@ transform(Form) ->
case {erl_syntax:type(TableArg),
erl_syntax:is_proper_list(FieldsArg)}of
{string, true} ->
Table = erl_syntax:string_value(TableArg),
ParseRes =
parse_upsert(
erl_syntax:list_elements(FieldsArg)),
Pos = erl_syntax:get_pos(Form),
case lists:keymember(
"server_host", 1, ParseRes) of
true ->
ok;
false ->
add_warning(Pos, no_server_host)
end,
{ParseRes2, UnusedVars} =
filter_upsert_sh(Table, ParseRes),
set_pos(
add_unused_vars(
make_sql_upsert(Table, ParseRes2, Pos),
UnusedVars
),
Pos);
transform_upsert(Form, TableArg, FieldsArg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL_UPSERT arguments must be "
@@ -158,26 +121,7 @@ transform(Form) ->
case {erl_syntax:type(TableArg),
erl_syntax:is_proper_list(FieldsArg)}of
{string, true} ->
Table = erl_syntax:string_value(TableArg),
ParseRes =
parse_insert(
erl_syntax:list_elements(FieldsArg)),
Pos = erl_syntax:get_pos(Form),
case lists:keymember(
"server_host", 1, ParseRes) of
true ->
ok;
false ->
add_warning(Pos, no_server_host)
end,
{ParseRes2, UnusedVars} =
filter_upsert_sh(Table, ParseRes),
set_pos(
add_unused_vars(
make_sql_insert(Table, ParseRes2),
UnusedVars
),
Pos);
transform_insert(Form, TableArg, FieldsArg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL_INSERT arguments must be "
@@ -226,11 +170,81 @@ top_transform(Forms) when is_list(Forms) ->
end
end, Forms).
parse(S, Loc) ->
parse1(S, [], #state{loc = Loc}).
transform_sql(Arg) ->
S = erl_syntax:string_value(Arg),
Pos = erl_syntax:get_pos(Arg),
ParseRes = parse(S, Pos, true),
ParseResOld = parse(S, Pos, false),
case ParseRes#state.server_host_used of
{true, _SHVar} ->
ok;
false ->
add_warning(
Pos, no_server_host),
[]
end,
set_pos(
make_schema_check(
make_sql_query(ParseRes),
make_sql_query(ParseResOld)
),
Pos).
parse(S, ParamPos, Loc) ->
parse1(S, [], #state{loc = Loc, param_pos = ParamPos}).
transform_upsert(Form, TableArg, FieldsArg) ->
Table = erl_syntax:string_value(TableArg),
ParseRes =
parse_upsert(
erl_syntax:list_elements(FieldsArg)),
Pos = erl_syntax:get_pos(Form),
case lists:keymember(
"server_host", 1, ParseRes) of
true ->
ok;
false ->
add_warning(Pos, no_server_host)
end,
ParseResOld =
filter_upsert_sh(Table, ParseRes),
set_pos(
make_schema_check(
make_sql_upsert(Table, ParseRes, Pos),
make_sql_upsert(Table, ParseResOld, Pos)
),
Pos).
transform_insert(Form, TableArg, FieldsArg) ->
Table = erl_syntax:string_value(TableArg),
ParseRes =
parse_insert(
erl_syntax:list_elements(FieldsArg)),
Pos = erl_syntax:get_pos(Form),
case lists:keymember(
"server_host", 1, ParseRes) of
true ->
ok;
false ->
add_warning(Pos, no_server_host)
end,
ParseResOld =
filter_upsert_sh(Table, ParseRes),
set_pos(
make_schema_check(
make_sql_insert(Table, ParseRes),
make_sql_insert(Table, ParseResOld)
),
Pos).
parse(S, Loc, UseNewSchema) ->
parse1(S, [],
#state{loc = Loc,
use_new_schema = UseNewSchema}).
parse(S, ParamPos, Loc, UseNewSchema) ->
parse1(S, [],
#state{loc = Loc,
param_pos = ParamPos,
use_new_schema = UseNewSchema}).
parse1([], Acc, State) ->
State1 = append_string(lists:reverse(Acc), State),
@@ -274,7 +288,7 @@ parse1([$%, $( | S], Acc, State) ->
State3 =
State2#state{server_host_used = {true, Name},
used_vars = [Name | State2#state.used_vars]},
case ?USE_NEW_SCHEMA of
case State#state.use_new_schema of
true ->
Convert =
erl_syntax:application(
@@ -350,7 +364,7 @@ make_var(V) ->
make_sql_query(State) ->
Hash = erlang:phash2(State#state{loc = undefined}),
Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}),
SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
Query = pack_query(State#state.'query'),
EQuery =
@@ -442,7 +456,7 @@ parse_upsert_field1([], _Acc, _ParamPos, Loc) ->
"?SQL_UPSERT fields must have the "
"following form: \"[!-]name=value\""});
parse_upsert_field1([$= | S], Acc, ParamPos, Loc) ->
{lists:reverse(Acc), parse(S, ParamPos, Loc)};
{lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
parse_upsert_field1([C | S], Acc, ParamPos, Loc) ->
parse_upsert_field1(S, [C | Acc], ParamPos, Loc).
@@ -632,7 +646,7 @@ parse_insert_field1([], _Acc, _ParamPos, Loc) ->
"?SQL_INSERT fields must have the "
"following form: \"name=value\""});
parse_insert_field1([$= | S], Acc, ParamPos, Loc) ->
{lists:reverse(Acc), parse(S, ParamPos, Loc)};
{lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
parse_insert_field1(S, [C | Acc], ParamPos, Loc).
@@ -640,6 +654,23 @@ parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
make_sql_insert(Table, ParseRes) ->
make_sql_query(make_sql_upsert_insert(Table, ParseRes)).
make_schema_check(Tree, Tree) ->
Tree;
make_schema_check(New, Old) ->
erl_syntax:case_expr(
erl_syntax:application(
erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(use_new_schema),
[]),
[erl_syntax:clause(
[erl_syntax:abstract(true)],
none,
[New]),
erl_syntax:clause(
[erl_syntax:abstract(false)],
none,
[Old])]).
concat_states(States) ->
lists:foldr(
@@ -711,26 +742,10 @@ set_pos(Tree, Pos) ->
end, Tree).
filter_upsert_sh(Table, ParseRes) ->
case ?USE_NEW_SCHEMA of
true ->
{ParseRes, []};
false ->
lists:foldr(
fun({Field, _Match, ST} = P, {Acc, Vars}) ->
if
Field /= "server_host" orelse Table == "route" ->
{[P | Acc], Vars};
true ->
{Acc, ST#state.used_vars ++ Vars}
end
end, {[], []}, ParseRes)
end.
add_unused_vars(Tree, []) ->
Tree;
add_unused_vars(Tree, Vars) ->
erl_syntax:block_expr(
lists:map(fun erl_syntax:variable/1, Vars) ++ [Tree]).
lists:filter(
fun({Field, _Match, _ST}) ->
Field /= "server_host" orelse Table == "route"
end, ParseRes).
-ifdef(ENABLE_PT_WARNINGS).
+3 -1
View File
@@ -172,8 +172,10 @@ listen_opt_type(turn_max_permissions) ->
end;
listen_opt_type(server_name) ->
fun iolist_to_binary/1;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
turn_max_port, turn_max_allocations, turn_max_permissions,
server_name].
server_name, backlog].
-endif.
+50 -151
View File
@@ -30,159 +30,58 @@
-export([start_link/0, init/1]).
-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
Hooks =
{ejabberd_hooks,
{ejabberd_hooks, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_hooks]},
Cluster = {ejabberd_cluster,
{ejabberd_cluster, start_link, []},
permanent,
5000,
worker,
[ejabberd_cluster]},
S2S =
{ejabberd_s2s,
{ejabberd_s2s, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_s2s]},
Captcha =
{ejabberd_captcha,
{ejabberd_captcha, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_captcha]},
Listener =
{ejabberd_listener,
{ejabberd_listener, start_link, []},
permanent,
infinity,
supervisor,
[ejabberd_listener]},
S2SInSupervisor =
{ejabberd_s2s_in_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_s2s_in_sup, ejabberd_s2s_in]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
S2SOutSupervisor =
{ejabberd_s2s_out_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_s2s_out_sup, ejabberd_s2s_out]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
ServiceSupervisor =
{ejabberd_service_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_service_sup, ejabberd_service]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
IQSupervisor =
{ejabberd_iq_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_iq_sup, gen_iq_handler]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
BackendSupervisor = {ejabberd_backend_sup,
{ejabberd_backend_sup, start_link, []},
permanent, infinity, supervisor,
[ejabberd_backend_sup]},
ACL = {acl, {acl, start_link, []},
permanent, 5000, worker, [acl]},
Shaper = {shaper, {shaper, start_link, []},
permanent, 5000, worker, [shaper]},
SQLSupervisor = {ejabberd_rdbms,
{ejabberd_rdbms, start_link, []},
permanent, infinity, supervisor, [ejabberd_rdbms]},
RiakSupervisor = {ejabberd_riak_sup,
{ejabberd_riak_sup, start_link, []},
permanent, infinity, supervisor, [ejabberd_riak_sup]},
RedisSupervisor = {ejabberd_redis_sup,
{ejabberd_redis_sup, start_link, []},
permanent, infinity, supervisor, [ejabberd_redis_sup]},
Router = {ejabberd_router, {ejabberd_router, start_link, []},
permanent, 5000, worker, [ejabberd_router]},
RouterMulticast = {ejabberd_router_multicast,
{ejabberd_router_multicast, start_link, []},
permanent, 5000, worker, [ejabberd_router_multicast]},
Local = {ejabberd_local, {ejabberd_local, start_link, []},
permanent, 5000, worker, [ejabberd_local]},
SM = {ejabberd_sm, {ejabberd_sm, start_link, []},
permanent, 5000, worker, [ejabberd_sm]},
GenModSupervisor = {ejabberd_gen_mod_sup, {gen_mod, start_link, []},
permanent, infinity, supervisor, [gen_mod]},
ExtMod = {ext_mod, {ext_mod, start_link, []},
permanent, 5000, worker, [ext_mod]},
Auth = {ejabberd_auth, {ejabberd_auth, start_link, []},
permanent, 5000, worker, [ejabberd_auth]},
OAuth = {ejabberd_oauth, {ejabberd_oauth, start_link, []},
permanent, 5000, worker, [ejabberd_oauth]},
Translation = {translate, {translate, start_link, []},
permanent, 5000, worker, [translate]},
AccessPerms = {ejabberd_access_permissions,
{ejabberd_access_permissions, start_link, []},
permanent, 5000, worker, [ejabberd_access_permissions]},
Ctl = {ejabberd_ctl, {ejabberd_ctl, start_link, []},
permanent, 5000, worker, [ejabberd_ctl]},
Commands = {ejabberd_commands, {ejabberd_commands, start_link, []},
permanent, 5000, worker, [ejabberd_commands]},
Admin = {ejabberd_admin, {ejabberd_admin, start_link, []},
permanent, 5000, worker, [ejabberd_admin]},
CyrSASL = {cyrsasl, {cyrsasl, start_link, []},
permanent, 5000, worker, [cyrsasl]},
PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []},
permanent, 5000, worker, [ejabberd_pkix]},
ACME = {ejabberd_acme, {ejabberd_acme, start_link, []},
permanent, 5000, worker, [ejabberd_acme]},
IQ = {ejabberd_iq, {ejabberd_iq, start_link, []},
permanent, 5000, worker, [ejabberd_iq]},
{ok, {{one_for_one, 10, 1},
[Hooks,
Cluster,
CyrSASL,
Translation,
AccessPerms,
Ctl,
Commands,
Admin,
PKIX,
ACME,
Listener,
S2S,
Captcha,
S2SInSupervisor,
S2SOutSupervisor,
ServiceSupervisor,
IQSupervisor,
ACL,
Shaper,
BackendSupervisor,
SQLSupervisor,
RiakSupervisor,
RedisSupervisor,
IQ,
Router,
RouterMulticast,
Local,
SM,
ExtMod,
GenModSupervisor,
Auth,
OAuth]}}.
[worker(ejabberd_hooks),
worker(ejabberd_cluster),
worker(cyrsasl),
worker(translate),
worker(ejabberd_access_permissions),
worker(ejabberd_ctl),
worker(ejabberd_commands),
worker(ejabberd_admin),
worker(ejabberd_pkix),
worker(ejabberd_acme),
supervisor(ejabberd_listener),
worker(ejabberd_s2s),
simple_supervisor(ejabberd_s2s_in),
simple_supervisor(ejabberd_s2s_out),
simple_supervisor(ejabberd_service),
worker(acl),
worker(shaper),
supervisor(ejabberd_backend_sup),
supervisor(ejabberd_rdbms),
supervisor(ejabberd_riak_sup),
supervisor(ejabberd_redis_sup),
worker(ejabberd_iq),
worker(ejabberd_router),
worker(ejabberd_router_multicast),
worker(ejabberd_local),
worker(ejabberd_sm),
worker(ejabberd_captcha),
worker(ext_mod),
supervisor(ejabberd_gen_mod_sup, gen_mod),
worker(ejabberd_auth),
worker(ejabberd_oauth)]}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
worker(Mod) ->
{Mod, {Mod, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [Mod]}.
supervisor(Mod) ->
supervisor(Mod, Mod).
supervisor(Name, Mod) ->
{Name, {Mod, start_link, []}, permanent, infinity, supervisor, [Mod]}.
simple_supervisor(Mod) ->
Name = list_to_atom(atom_to_list(Mod) ++ "_sup"),
{Name, {ejabberd_tmp_sup, start_link, [Name, Mod]},
permanent, infinity, supervisor, [ejabberd_tmp_sup]}.
+1 -15
View File
@@ -42,7 +42,6 @@
%%-include("logger.hrl").
-define(CHECK_INTERVAL, timer:seconds(30)).
-define(DISK_FULL_THRES, 0.99).
-record(state, {tref :: reference(),
mref :: reference()}).
@@ -67,8 +66,7 @@ start() ->
application:set_env(os_mon, start_cpu_sup, false),
application:set_env(os_mon, start_os_sup, false),
application:set_env(os_mon, start_memsup, true),
application:set_env(os_mon, start_disksup, true),
application:set_env(os_mon, disk_almost_full_threshold, ?DISK_FULL_THRES),
application:set_env(os_mon, start_disksup, false),
ejabberd:start_app(os_mon).
excluded_apps() ->
@@ -81,16 +79,10 @@ init([]) ->
{ok, #state{}}.
handle_event({set_alarm, {system_memory_high_watermark, _}}, State) ->
error_logger:warning_msg(
"More than 80% of OS memory is allocated, "
"starting OOM watchdog", []),
handle_overload(State),
{ok, restart_timer(State)};
handle_event({clear_alarm, system_memory_high_watermark}, State) ->
cancel_timer(State#state.tref),
error_logger:info_msg(
"Memory consumption is back to normal, "
"stopping OOM watchdog", []),
{ok, State#state{tref = undefined}};
handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
case proc_stat(Pid, get_app_pids()) of
@@ -105,12 +97,6 @@ handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
end;
handle_event({clear_alarm, process_memory_high_watermark}, State) ->
{ok, State};
handle_event({set_alarm, {{disk_almost_full, MountPoint}, _}}, State) ->
error_logger:warning_msg("Disk is almost full on ~p", [MountPoint]),
{ok, State};
handle_event({clear_alarm, {disk_almost_full, MountPoint}}, State) ->
error_logger:info_msg("Disk usage is back to normal on ~p", [MountPoint]),
{ok, State};
handle_event(Event, State) ->
error_logger:warning_msg("unexpected event: ~p", [Event]),
{ok, State}.
+8 -20
View File
@@ -74,27 +74,15 @@ get_acl_rule([<<"vhosts">>], _) ->
%% The pages of a vhost are only accesible if the user is admin of that vhost:
get_acl_rule([<<"server">>, VHost | _RPath], Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, configure),
ACR = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access_readonly, webadmin_view),
{VHost, [AC, ACR]};
{VHost, [configure, webadmin_view]};
get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, configure),
{VHost, [AC]};
{VHost, [configure]};
%% Default rule: only global admins can access any other random page
get_acl_rule(_RPath, Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, configure),
ACR = gen_mod:get_module_opt(global, ejabberd_web_admin,
access_readonly, webadmin_view),
{global, [AC, ACR]};
{global, [configure, webadmin_view]};
get_acl_rule(_RPath, 'POST') ->
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, configure),
{global, [AC]}.
{global, [configure]}.
%%%==================================
%%%% Menu Items Access
@@ -275,7 +263,7 @@ get_auth_account(HostOfRule, AccessRule, User, Server,
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true ->
case acl:any_rules_allowed(HostOfRule, AccessRule,
jid:make(User, Server))
jid:make(User, Server))
of
false -> {unauthorized, <<"unprivileged-account">>};
true -> {ok, {User, Server}}
@@ -1252,7 +1240,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
?XE(<<"tr">>,
[?XE(<<"td">>,
[?AC((URLFunc({user, Prefix,
ejabberd_http:url_encode(User),
misc:url_encode(User),
Server})),
(us_to_list(US)))]),
?XE(<<"td">>, FQueueLen),
@@ -1274,7 +1262,7 @@ get_offlinemsg_module(Server) ->
end.
get_lastactivity_menuitem_list(Server) ->
case gen_mod:db_type(Server, mod_last) of
case gen_mod:get_module_opt(Server, mod_last, db_type) of
mnesia -> [{<<"last-activity">>, <<"Last Activity">>}];
_ -> []
end.
@@ -1331,7 +1319,7 @@ list_online_users(Host, _Lang) ->
SUsers = lists:usort(Users),
lists:flatmap(fun ({_S, U} = SU) ->
[?AC(<<"../user/",
(ejabberd_http:url_encode(U))/binary, "/">>,
(misc:url_encode(U))/binary, "/">>,
(su_to_list(SU))),
?BR]
end,
+5 -1
View File
@@ -581,5 +581,9 @@ listen_opt_type(maxsessions) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(timeout) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(backlog) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[access_commands, maxsessions, timeout].
[access_commands, maxsessions, timeout, backlog, inet, inet6].
+16 -5
View File
@@ -1046,8 +1046,6 @@ polish([], Res, Ref) -> {Res, Ref}.
%%-----------------------------------------------------------------------
connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts),
?INFO_MSG("LDAP connection on ~s:~p",
[Host, S#eldap.port]),
Opts = if S#eldap.tls == tls ->
[{packet, asn1}, {active, true}, {keepalive, true},
binary
@@ -1056,6 +1054,8 @@ connect_bind(S) ->
[{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary]
end,
?DEBUG("Connecting to LDAP server at ~s:~p with options ~p",
[Host, S#eldap.port, Opts]),
HostS = binary_to_list(Host),
SocketData = case S#eldap.tls of
tls ->
@@ -1080,9 +1080,8 @@ connect_bind(S) ->
{ok, connecting, NewS#eldap{host = Host}}
end;
{error, Reason} ->
?ERROR_MSG("LDAP connection failed:~n** Server: "
"~s:~p~n** Reason: ~p~n** Socket options: ~p",
[Host, S#eldap.port, Reason, Opts]),
?ERROR_MSG("LDAP connection to ~s:~b failed: ~s",
[Host, S#eldap.port, format_error(SockMod, Reason)]),
NewS = close_and_retry(S),
{ok, connecting, NewS#eldap{host = Host}}
end.
@@ -1121,3 +1120,15 @@ bump_id(#eldap{id = Id})
when Id > (?MAX_TRANSACTION_ID) ->
?MIN_TRANSACTION_ID;
bump_id(#eldap{id = Id}) -> Id + 1.
format_error(SockMod, Reason) ->
Txt = case SockMod of
ssl -> ssl:format_error(Reason);
gen_tcp -> inet:format_error(Reason)
end,
case Txt of
"unknown POSIX error" ->
lists:flatten(io_lib:format("~p", [Reason]));
_ ->
Txt
end.
+1 -1
View File
@@ -64,7 +64,7 @@ parse(L) ->
%%% RFC2254_Filter = RegExp = Value = string(),
%%% N = integer().
%%%
%%% Description: The same as parse/1, but substitutes N or all occurences
%%% Description: The same as parse/1, but substitutes N or all occurrences
%%% of RegExp with Value *after* parsing.
%%%
%%% Example:
+45 -10
View File
@@ -31,7 +31,8 @@
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
get_ldap_attr/2, get_user_part/2, make_filter/2,
get_state/2, case_insensitive_match/2, get_config/2,
decode_octet_string/3, uids_domain_subst/2, opt_type/1]).
decode_octet_string/3, uids_domain_subst/2, opt_type/1,
options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -180,12 +181,16 @@ get_config(Host, Opts) ->
TLSCertFile = get_opt(ldap_tls_certfile, Host, Opts),
TLSCAFile = get_opt(ldap_tls_cacertfile, Host, Opts),
TLSDepth = get_opt(ldap_tls_depth, Host, Opts),
Port = get_opt(ldap_port, Host, Opts,
Port = case get_opt(ldap_port, Host, Opts) of
undefined ->
case Encrypt of
tls -> ?LDAPS_PORT;
starttls -> ?LDAP_PORT;
_ -> ?LDAP_PORT
end),
end;
P ->
P
end,
RootDN = get_opt(ldap_rootdn, Host, Opts, <<"">>),
Password = get_opt(ldap_password, Host, Opts, <<"">>),
Base = get_opt(ldap_base, Host, Opts, <<"">>),
@@ -348,7 +353,12 @@ collect_parts_bit([],Acc,Uacc) ->
(ldap_uids) -> fun((uids()) -> uids());
(atom()) -> [atom()].
opt_type(deref_aliases) ->
opt_type(ldap_deref_aliases);
fun(unspecified) -> unspecified;
(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end;
opt_type(ldap_backups) ->
fun (L) -> [iolist_to_binary(H) || H <- L] end;
opt_type(ldap_base) -> fun iolist_to_binary/1;
@@ -365,25 +375,33 @@ opt_type(ldap_encrypt) ->
end;
opt_type(ldap_password) -> fun iolist_to_binary/1;
opt_type(ldap_port) ->
fun (I) when is_integer(I), I > 0 -> I end;
fun(undefined) -> undefined;
(I) when is_integer(I), I > 0 -> I
end;
opt_type(ldap_rootdn) -> fun iolist_to_binary/1;
opt_type(ldap_servers) ->
fun (L) -> [iolist_to_binary(H) || H <- L] end;
opt_type(ldap_tls_certfile) ->
fun(S) ->
binary_to_list(ejabberd_pkix:try_certfile(S))
fun(undefined) -> undefined;
(S) -> binary_to_list(ejabberd_pkix:try_certfile(S))
end;
opt_type(ldap_tls_cacertfile) ->
fun(S) -> binary_to_list(misc:try_read_file(S)) end;
fun(undefined) -> undefined;
(S) -> binary_to_list(misc:try_read_file(S))
end;
opt_type(ldap_tls_depth) ->
fun (I) when is_integer(I), I >= 0 -> I end;
fun(undefined) -> undefined;
(I) when is_integer(I), I >= 0 -> I
end;
opt_type(ldap_tls_verify) ->
fun (hard) -> hard;
(soft) -> soft;
(false) -> false
end;
opt_type(ldap_filter) ->
fun check_filter/1;
fun(<<"">>) -> <<"">>;
(F) -> check_filter(F)
end;
opt_type(ldap_uids) ->
fun (Us) ->
lists:map(fun ({U, P}) ->
@@ -399,3 +417,20 @@ opt_type(_) ->
ldap_port, ldap_rootdn, ldap_servers, ldap_filter,
ldap_tls_certfile, ldap_tls_cacertfile, ldap_tls_depth,
ldap_tls_verify].
options(_) ->
[{deref_aliases, unspecified},
{ldap_backups, []},
{ldap_base, <<"">>},
{ldap_uids, [{<<"uid">>, <<"%u">>}]},
{ldap_deref_aliases, never},
{ldap_encrypt, none},
{ldap_password, <<"">>},
{ldap_port, undefined},
{ldap_rootdn, <<"">>},
{ldap_servers, [<<"localhost">>]},
{ldap_filter, <<"">>},
{ldap_tls_certfile, undefined},
{ldap_tls_cacertfile, undefined},
{ldap_tls_depth, undefined},
{ldap_tls_verify, false}].
+13 -13
View File
@@ -56,7 +56,8 @@ init([]) ->
process_flag(trap_exit, true),
[code:add_patha(module_ebin_dir(Module))
|| {Module, _} <- installed()],
p1_http:start(),
application:start(inets),
inets:start(httpc, [{profile, ext_mod}]),
ejabberd_commands:register_commands(get_commands_spec()),
{ok, #state{}}.
@@ -313,23 +314,22 @@ check(Package) when is_binary(Package) ->
%% -- archives and variables functions
geturl(Url) ->
geturl(Url, []).
geturl(Url, UsrOpts) ->
geturl(Url, [], UsrOpts).
geturl(Url, Hdrs, UsrOpts) ->
Host = case getenv("PROXY_SERVER", "", ":") of
[H, Port] -> [{proxy_host, H}, {proxy_port, list_to_integer(Port)}];
[H] -> [{proxy_host, H}, {proxy_port, 8080}];
_ -> []
case getenv("PROXY_SERVER", "", ":") of
[H, Port] ->
httpc:set_options([{proxy, {{H, list_to_integer(Port)}, []}}], ext_mod);
[H] ->
httpc:set_options([{proxy, {{H, 8080}, []}}], ext_mod);
_ ->
ok
end,
User = case getenv("PROXY_USER", "", [4]) of
[U, Pass] -> [{proxy_user, U}, {proxy_password, Pass}];
[U, Pass] -> [{proxy_auth, {U, Pass}}];
_ -> []
end,
case p1_http:request(get, Url, Hdrs, [], Host++User++UsrOpts++[{version, "HTTP/1.0"}]) of
{ok, 200, Headers, Response} ->
case httpc:request(get, {Url, []}, User, [{body_format, binary}], ext_mod) of
{ok, {{_, 200, _}, Headers, Response}} ->
{ok, Headers, Response};
{ok, Code, _Headers, Response} ->
{ok, {{_, Code, _}, _Headers, Response}} ->
{error, {Code, Response}};
{error, Reason} ->
{error, Reason}
+26 -118
View File
@@ -27,110 +27,38 @@
-author('alexey@process-one.net').
-ifndef(GEN_SERVER).
-define(GEN_SERVER, gen_server).
-endif.
-behaviour(?GEN_SERVER).
-behaviour(ejabberd_config).
%% API
-export([start_link/3, add_iq_handler/6,
remove_iq_handler/3, stop_iq_handler/3, handle/5,
-export([add_iq_handler/5, remove_iq_handler/3, handle/4,
process_iq/4, check_type/1, transform_module_options/1,
opt_type/1, iqdisc/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
opt_type/1]).
%% Deprecated functions
-export([add_iq_handler/6, handle/5, iqdisc/1]).
-deprecated([{add_iq_handler, 6}, {handle, 5}, {iqdisc, 1}]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
-record(state, {host, module, function}).
-type component() :: ejabberd_sm | ejabberd_local.
-type type() :: no_queue | one_queue | pos_integer() | parallel.
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
-export_type([opts/0, type/0]).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Module, Function) ->
?GEN_SERVER:start_link(?MODULE, [Host, Module, Function],
[]).
-spec add_iq_handler(module(), binary(), binary(), module(), atom(), type()) -> ok.
add_iq_handler(Component, Host, NS, Module, Function,
Type) ->
case Type of
no_queue ->
Component:register_iq_handler(Host, NS, Module,
Function, no_queue);
one_queue ->
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
[Host, Module, Function]),
Component:register_iq_handler(Host, NS, Module,
Function, {one_queue, Pid});
N when is_integer(N) ->
Pids = lists:map(fun (_) ->
{ok, Pid} =
supervisor:start_child(ejabberd_iq_sup,
[Host, Module,
Function]),
Pid
end,
lists:seq(1, N)),
Component:register_iq_handler(Host, NS, Module,
Function, {queues, Pids});
parallel ->
Component:register_iq_handler(Host, NS, Module,
Function, parallel)
end.
-spec add_iq_handler(module(), binary(), binary(), module(), atom()) -> ok.
add_iq_handler(Component, Host, NS, Module, Function) ->
Component:register_iq_handler(Host, NS, Module, Function).
-spec remove_iq_handler(component(), binary(), binary()) -> ok.
remove_iq_handler(Component, Host, NS) ->
Component:unregister_iq_handler(Host, NS).
-spec stop_iq_handler(atom(), atom(), [pid()]) -> any().
stop_iq_handler(_Module, _Function, Opts) ->
case Opts of
{one_queue, Pid} -> ?GEN_SERVER:call(Pid, stop);
{queues, Pids} ->
lists:foreach(fun (Pid) ->
catch ?GEN_SERVER:call(Pid, stop)
end,
Pids);
_ -> ok
end.
-spec handle(binary(), atom(), atom(), opts(), iq()) -> any().
handle(Host, Module, Function, Opts, IQ) ->
case Opts of
no_queue ->
process_iq(Host, Module, Function, IQ);
{one_queue, Pid} ->
Pid ! {process_iq, IQ};
{queues, Pids} ->
Pid = lists:nth(erlang:phash(p1_time_compat:unique_integer(),
length(Pids)), Pids),
Pid ! {process_iq, IQ};
parallel ->
spawn(?MODULE, process_iq, [Host, Module, Function, IQ]);
_ -> todo
end.
-spec handle(binary(), atom(), atom(), iq()) -> any().
handle(Host, Module, Function, IQ) ->
process_iq(Host, Module, Function, IQ).
-spec process_iq(binary(), atom(), atom(), iq()) -> any().
process_iq(_Host, Module, Function, IQ) ->
try
ResIQ = case erlang:function_exported(Module, Function, 1) of
@@ -178,15 +106,14 @@ process_iq(Module, Function, From, To, IQ) ->
To, From)
end.
-spec check_type(type()) -> type().
-spec check_type(any()) -> no_queue.
check_type(_Type) ->
?WARNING_MSG("Option 'iqdisc' is deprecated and has no effect anymore", []),
no_queue.
check_type(no_queue) -> no_queue;
check_type(one_queue) -> one_queue;
check_type(N) when is_integer(N), N>0 -> N;
check_type(parallel) -> parallel.
iqdisc(Host) ->
ejabberd_config:get_option({iqdisc, Host}, no_queue).
-spec iqdisc(binary() | global) -> no_queue.
iqdisc(_Host) ->
no_queue.
-spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}].
@@ -198,37 +125,18 @@ transform_module_options(Opts) ->
Opt
end, Opts).
-spec opt_type(iqdisc) -> fun((type()) -> type());
-spec opt_type(iqdisc) -> fun((any()) -> no_queue);
(atom()) -> [atom()].
opt_type(iqdisc) -> fun check_type/1;
opt_type(_) -> [iqdisc].
%%====================================================================
%% gen_server callbacks
%% Deprecated API
%%====================================================================
-spec add_iq_handler(module(), binary(), binary(), module(), atom(), any()) -> ok.
add_iq_handler(Component, Host, NS, Module, Function, _Type) ->
add_iq_handler(Component, Host, NS, Module, Function).
init([Host, Module, Function]) ->
{ok,
#state{host = Host, module = Module,
function = Function}}.
handle_call(stop, _From, State) ->
Reply = ok, {stop, normal, Reply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info({process_iq, IQ},
#state{host = Host, module = Module,
function = Function} =
State) ->
process_iq(Host, Module, Function, IQ),
{noreply, State};
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-spec handle(binary(), atom(), atom(), any(), iq()) -> any().
handle(Host, Module, Function, _Opts, IQ) ->
handle(Host, Module, Function, IQ).
+368 -174
View File
@@ -32,20 +32,29 @@
-export([init/1, start_link/0, start_child/3, start_child/4,
stop_child/1, stop_child/2, config_reloaded/0]).
-export([start_module/2, start_module/3,
stop_module/2, stop_module_keep_config/2,
get_opt/2, get_opt/3, get_opt_host/3,
get_opt_hosts/3, opt_type/1, is_equal_opt/4,
get_module_opt/3, get_module_opt/4, get_module_opt_host/3,
-export([start_module/2, stop_module/2, stop_module_keep_config/2,
get_opt/2, get_opt_hosts/2, opt_type/1, is_equal_opt/3,
get_module_opt/3, get_module_opt_host/3,
loaded_modules/1, loaded_modules_with_opts/1,
get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2,
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
db_mod/2, db_mod/3, ram_db_mod/2, ram_db_mod/3,
db_type/2, db_type/3, ram_db_type/2, ram_db_type/3]).
is_db_configured/2]).
%% Deprecated functions
-export([get_opt/4, get_module_opt/5]).
-deprecated([{get_opt, 4}, {get_module_opt, 5}]).
-export([get_opt/3, get_opt/4, get_module_opt/4, get_module_opt/5,
get_opt_host/3, get_opt_hosts/3, db_type/2, db_type/3,
ram_db_type/2, ram_db_type/3]).
-deprecated([{get_opt, 3},
{get_opt, 4},
{get_opt_host, 3},
{get_opt_hosts, 3},
{get_module_opt, 4},
{get_module_opt, 5},
{db_type, 2},
{db_type, 3},
{ram_db_type, 2},
{ram_db_type, 3}]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -53,7 +62,8 @@
-record(ejabberd_module,
{module_host = {undefined, <<"">>} :: {atom(), binary()},
opts = [] :: opts() | '_' | '$2'}).
opts = [] :: opts() | '_' | '$2',
order = 0 :: integer()}).
-type opts() :: [{atom(), any()}].
-type db_type() :: atom().
@@ -62,9 +72,10 @@
-callback stop(binary()) -> any().
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
-callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
-callback mod_options(binary()) -> opts().
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
-optional_callbacks([reload/3]).
-optional_callbacks([mod_opt_type/1, reload/3]).
-export_type([opts/0]).
-export_type([db_type/0]).
@@ -122,7 +133,7 @@ start_modules() ->
end, ?MYHOSTS).
get_modules_options(Host) ->
ejabberd_config:get_option({modules, Host}, []).
sort_modules(Host, ejabberd_config:get_option({modules, Host}, [])).
sort_modules(Host, ModOpts) ->
G = digraph:new([acyclic]),
@@ -135,15 +146,15 @@ sort_modules(Host, ModOpts) ->
case lists:keyfind(DepMod, 1, ModOpts) of
false when Type == hard ->
ErrTxt = io_lib:format(
"failed to load module '~s' "
"Failed to load module '~s' "
"because it depends on module '~s' "
"which is not found in the config",
[Mod, DepMod]),
?ERROR_MSG(ErrTxt, []),
digraph:del_vertex(G, Mod),
maybe_halt_ejabberd(ErrTxt);
maybe_halt_ejabberd();
false when Type == soft ->
?WARNING_MSG("module '~s' is recommended for "
?WARNING_MSG("Module '~s' is recommended for "
"module '~s' but is not found in "
"the config",
[DepMod, Mod]);
@@ -151,7 +162,7 @@ sort_modules(Host, ModOpts) ->
digraph:add_vertex(G, DepMod, DepOpts),
case digraph:add_edge(G, DepMod, Mod) of
{error, {bad_edge, Path}} ->
?WARNING_MSG("cyclic dependency detected "
?WARNING_MSG("Cyclic dependency detected "
"between modules: ~p",
[Path]);
_ ->
@@ -160,17 +171,21 @@ sort_modules(Host, ModOpts) ->
end
end, Deps)
end, ModOpts),
Result = [digraph:vertex(G, V) || V <- digraph_utils:topsort(G)],
{Result, _} = lists:mapfoldl(
fun(V, Order) ->
{M, O} = digraph:vertex(G, V),
{{M, O, Order}, Order+1}
end, 1, digraph_utils:topsort(G)),
digraph:delete(G),
Result.
-spec start_modules(binary()) -> ok.
start_modules(Host) ->
Modules = sort_modules(Host, get_modules_options(Host)),
Modules = get_modules_options(Host),
lists:foreach(
fun({Module, Opts}) ->
start_module(Host, Module, Opts)
fun({Module, Opts, Order}) ->
start_module(Host, Module, Opts, Order)
end, Modules).
-spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}.
@@ -178,51 +193,64 @@ start_modules(Host) ->
start_module(Host, Module) ->
Modules = get_modules_options(Host),
case lists:keyfind(Module, 1, Modules) of
{_, Opts} ->
start_module(Host, Module, Opts);
{_, Opts, Order} ->
start_module(Host, Module, Opts, Order);
false ->
{error, not_found_in_config}
end.
-spec start_module(binary(), atom(), opts()) -> ok | {ok, pid()}.
start_module(Host, Module, Opts) ->
start_module(Host, Module, Opts, true).
-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}.
start_module(Host, Module, Opts, Order) ->
start_module(Host, Module, Opts, Order, true).
-spec start_module(binary(), atom(), opts(), boolean()) -> ok | {ok, pid()}.
start_module(Host, Module, Opts0, NeedValidation) ->
?DEBUG("loading ~s at ~s", [Module, Host]),
Opts = if NeedValidation ->
validate_opts(Host, Module, Opts0);
true ->
Opts0
end,
store_options(Host, Module, Opts),
try case Module:start(Host, Opts) of
ok -> ok;
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
Err -> erlang:error(Err)
end
catch Class:Reason ->
ets:delete(ejabberd_modules, {Module, Host}),
ErrorText =
io_lib:format("Problem starting the module ~s for host "
"~s ~n options: ~p~n ~p: ~p~n~p",
[Module, Host, Opts, Class, Reason,
erlang:get_stacktrace()]),
?CRITICAL_MSG(ErrorText, []),
maybe_halt_ejabberd(ErrorText),
erlang:raise(Class, Reason, erlang:get_stacktrace())
-spec start_module(binary(), atom(), opts(), integer(), boolean()) -> ok | {ok, pid()}.
start_module(Host, Module, Opts0, Order, NeedValidation) ->
?DEBUG("Loading ~s at ~s", [Module, Host]),
Res = if NeedValidation ->
validate_opts(Host, Module, Opts0);
true ->
{ok, Opts0}
end,
case Res of
{ok, Opts} ->
store_options(Host, Module, Opts, Order),
try case Module:start(Host, Opts) of
ok -> ok;
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
Err -> erlang:error(Err)
end
catch Class:Reason ->
ets:delete(ejabberd_modules, {Module, Host}),
ErrorText =
case Reason == undef andalso
code:ensure_loaded(Module) /= {module, Module} of
true ->
io_lib:format("Failed to load unknown module "
"~s for host ~s: make sure "
"there is no typo and ~s.beam "
"exists inside either ~s or ~s "
"directory",
[Module, Host, Module,
filename:dirname(code:which(?MODULE)),
ext_mod:modules_dir()]);
false ->
io_lib:format("Problem starting the module ~s for host "
"~s ~n options: ~p~n ~p: ~p~n~p",
[Module, Host, Opts, Class, Reason,
erlang:get_stacktrace()])
end,
?CRITICAL_MSG(ErrorText, []),
maybe_halt_ejabberd(),
erlang:raise(Class, Reason, erlang:get_stacktrace())
end;
{error, _ErrorText} ->
maybe_halt_ejabberd()
end.
-spec reload_modules(binary()) -> ok.
reload_modules(Host) ->
NewMods = ejabberd_config:get_option({modules, Host}, []),
OldMods = ets:select(
ejabberd_modules,
ets:fun2ms(
fun(#ejabberd_module{module_host = {M, H}, opts = O})
when H == Host -> {M, O}
end)),
NewMods = get_modules_options(Host),
OldMods = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
fun({Mod, _Opts}) ->
case lists:keymember(Mod, 1, NewMods) of
@@ -233,10 +261,10 @@ reload_modules(Host) ->
end
end, OldMods),
lists:foreach(
fun({Mod, Opts}) ->
fun({Mod, Opts, Order}) ->
case lists:keymember(Mod, 1, OldMods) of
false ->
start_module(Host, Mod, Opts);
start_module(Host, Mod, Opts, Order);
true ->
ok
end
@@ -244,24 +272,26 @@ reload_modules(Host) ->
lists:foreach(
fun({Mod, OldOpts}) ->
case lists:keyfind(Mod, 1, NewMods) of
{_, NewOpts0} ->
{_, NewOpts0, Order} ->
case validate_opts(Host, Mod, NewOpts0) of
OldOpts ->
{ok, OldOpts} ->
ok;
NewOpts ->
reload_module(Host, Mod, NewOpts, OldOpts)
{ok, NewOpts} ->
reload_module(Host, Mod, NewOpts, OldOpts, Order);
{error, _} ->
ok
end;
_ ->
ok
end
end, OldMods).
-spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}.
reload_module(Host, Module, NewOpts, OldOpts) ->
-spec reload_module(binary(), module(), opts(), opts(), integer()) -> ok | {ok, pid()}.
reload_module(Host, Module, NewOpts, OldOpts, Order) ->
case erlang:function_exported(Module, reload, 3) of
true ->
?DEBUG("reloading ~s at ~s", [Module, Host]),
store_options(Host, Module, NewOpts),
?DEBUG("Reloading ~s at ~s", [Module, Host]),
store_options(Host, Module, NewOpts, Order),
try case Module:reload(Host, NewOpts, OldOpts) of
ok -> ok;
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
@@ -269,33 +299,32 @@ reload_module(Host, Module, NewOpts, OldOpts) ->
end
catch Class:Reason ->
StackTrace = erlang:get_stacktrace(),
?CRITICAL_MSG("failed to reload module ~s at ~s:~n"
?CRITICAL_MSG("Failed to reload module ~s at ~s:~n"
"** Reason = ~p",
[Module, Host,
{Class, {Reason, StackTrace}}]),
erlang:raise(Class, Reason, StackTrace)
end;
false ->
?WARNING_MSG("module ~s doesn't support reloading "
?WARNING_MSG("Module ~s doesn't support reloading "
"and will be restarted", [Module]),
stop_module(Host, Module),
start_module(Host, Module, NewOpts, false)
start_module(Host, Module, NewOpts, Order, false)
end.
-spec store_options(binary(), module(), opts()) -> true.
store_options(Host, Module, Opts) ->
-spec store_options(binary(), module(), opts(), integer()) -> true.
store_options(Host, Module, Opts, Order) ->
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts}).
opts = Opts, order = Order}).
maybe_halt_ejabberd(ErrorText) ->
maybe_halt_ejabberd() ->
case is_app_running(ejabberd) of
false ->
?CRITICAL_MSG("ejabberd initialization was aborted "
"because a module start failed.",
[]),
timer:sleep(3000),
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199));
ejabberd:halt();
true ->
ok
end.
@@ -316,7 +345,7 @@ stop_modules() ->
-spec stop_modules(binary()) -> ok.
stop_modules(Host) ->
Modules = get_modules_options(Host),
Modules = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
fun({Module, _Args}) ->
stop_module_keep_config(Host, Module)
@@ -325,7 +354,6 @@ stop_modules(Host) ->
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
stop_module(Host, Module) ->
?DEBUG("stopping ~s at ~s", [Module, Host]),
case stop_module_keep_config(Host, Module) of
error -> error;
ok -> ok
@@ -334,6 +362,7 @@ stop_module(Host, Module) ->
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
stop_module_keep_config(Host, Module) ->
?DEBUG("Stopping ~s at ~s", [Module, Host]),
case catch Module:stop(Host) of
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), error;
{wait, ProcList} when is_list(ProcList) ->
@@ -367,21 +396,19 @@ wait_for_stop1(MonitorReference) ->
-type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
-spec get_opt(atom() | {atom(), binary() | global}, opts()) -> any().
-spec get_opt(atom(), opts()) -> any().
get_opt(Opt, Opts) ->
get_opt(Opt, Opts, undefined).
case lists:keyfind(Opt, 1, Opts) of
{_, Val} -> Val;
false ->
?DEBUG("Attempt to read unspecified option ~s", [Opt]),
undefined
end.
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun() | any()) -> any().
-spec get_opt(atom(), opts(), check_fun() | any()) -> any().
get_opt(Opt, Opts, F) when is_function(F) ->
get_opt(Opt, Opts, undefined);
get_opt({Opt, Host}, Opts, Default) ->
case lists:keyfind(Opt, 1, Opts) of
false ->
ejabberd_config:get_option({Opt, Host}, Default);
{_, Val} ->
Val
end;
get_opt(Opt, Opts, Default) ->
case lists:keyfind(Opt, 1, Opts) of
false ->
@@ -442,125 +469,255 @@ get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
-spec get_opt_hosts(binary(), opts()) -> [binary()].
get_opt_hosts(Host, Opts) ->
get_opt_hosts(Host, Opts, undefined).
-spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
get_opt_hosts(Host, Opts, Default) ->
Vals = case get_opt(host, Opts, undefined) of
undefined ->
case get_opt(hosts, Opts, []) of
[] -> [Default];
L -> L
end;
Val ->
[Val]
Vals = case get_opt(hosts, Opts) of
L when L == [] orelse L == undefined ->
case get_opt(host, Opts) of
undefined -> [Default];
H -> [H]
end;
L ->
L
end,
[ejabberd_regexp:greplace(V, <<"@HOST@">>, Host) || V <- Vals].
-spec get_validators(binary(), module(), opts()) -> dict:dict() | undef.
get_validators(Host, Module, Opts) ->
try Module:mod_opt_type('') of
L ->
SubMods1 = case lists:member(db_type, L) of
true -> [db_mod(Host, Opts, Module)];
false -> []
end,
SubMods2 = case lists:member(ram_db_type, L) of
true -> [ram_db_mod(Host, Opts, Module)];
false -> []
end,
lists:foldl(
fun(Mod, D) ->
try Mod:mod_opt_type('') of
Os ->
lists:foldl(
fun({Opt, SubOpt} = O, Acc) ->
SubF = Mod:mod_opt_type(O),
F = case Mod:mod_opt_type(Opt) of
F1 when is_function(F1) ->
F1;
_ ->
fun(X) -> X end
end,
dict:append_list(
Opt, [F, {SubOpt, [SubF]}], Acc);
(O, Acc) ->
F = Mod:mod_opt_type(O),
dict:store(O, [F], Acc)
end, D, Os)
catch _:undef ->
D
end
end, dict:new(), [Module|SubMods1 ++ SubMods2])
catch _:undef ->
?WARNING_MSG("module '~s' doesn't export mod_opt_type/1",
[Module]),
undef
-spec get_validators(binary(), {module(), [module()]}) -> list() | undef.
get_validators(Host, {Module, SubMods}) ->
Validators =
dict:to_list(
lists:foldl(
fun(Mod, D) ->
try list_known_opts(Host, Mod) of
Os ->
lists:foldl(
fun({Opt, SubOpt} = O, Acc) ->
SubF = Mod:mod_opt_type(O),
F = try Mod:mod_opt_type(Opt)
catch _:_ -> fun(X) -> X end
end,
dict:append_list(
Opt, [F, {SubOpt, [SubF]}], Acc);
(O, Acc) ->
F = Mod:mod_opt_type(O),
dict:store(O, [F], Acc)
end, D, Os)
catch _:undef ->
D
end
end, dict:new(), [Module|SubMods])),
case Validators of
[] ->
case have_validators(Module) of
false ->
case code:ensure_loaded(Module) of
{module, _} ->
?WARNING_MSG("Third-party module '~s' doesn't export "
"options validator; consider to upgrade "
"the module", [Module]);
_ ->
%% Silently ignore this, the error will be
%% generated later
ok
end,
undef;
true ->
[]
end;
_ ->
Validators
end.
-spec validate_opts(binary(), module(), opts()) -> opts().
validate_opts(Host, Module, Opts) ->
case get_validators(Host, Module, Opts) of
undef ->
Opts;
Validators ->
validate_opts(Host, Module, Opts, dict:to_list(Validators))
-spec have_validators(module()) -> boolean().
have_validators(Module) ->
erlang:function_exported(Module, mod_options, 1)
orelse erlang:function_exported(Module, mod_opt_type, 1).
-spec validate_opts(binary(), module(), opts()) -> {ok, opts()} | {error, string()}.
validate_opts(Host, Module, Opts0) ->
SubMods = get_submodules(Host, Module, Opts0),
DefaultOpts = lists:flatmap(
fun(M) ->
try M:mod_options(Host)
catch _:undef -> []
end
end, [Module|SubMods]),
Required = lists:filter(fun is_atom/1, DefaultOpts),
try
Opts = merge_opts(Opts0, DefaultOpts, Module),
{ok, case get_validators(Host, {Module, SubMods}) of
undef ->
Opts;
Validators ->
Opts1 = validate_opts(Host, Module, Opts, Required, Validators),
remove_duplicated_opts(Opts1)
end}
catch _:{missing_required_option, Opt} ->
ErrTxt = io_lib:format("Module '~s' is missing required option '~s'",
[Module, Opt]),
?ERROR_MSG(ErrTxt, []),
{error, ErrTxt}
end.
validate_opts(Host, Module, Opts, Validators) when is_list(Opts) ->
validate_opts(Host, Module, Opts, Required, Validators) when is_list(Opts) ->
lists:flatmap(
fun({Opt, Val}) when is_atom(Opt) ->
case lists:keyfind(Opt, 1, Validators) of
{_, L} ->
case lists:partition(fun is_function/1, L) of
{[VFun|_], []} ->
validate_opt(Module, Opt, Val, VFun);
validate_opt(Module, Opt, Val, Required, VFun);
{[VFun|_], SubValidators} ->
try validate_opts(Host, Module, Val, SubValidators) of
try validate_opts(Host, Module, Val, Required, SubValidators) of
SubOpts ->
validate_opt(Module, Opt, SubOpts, VFun)
validate_opt(Module, Opt, SubOpts, Required, VFun)
catch _:bad_option ->
?ERROR_MSG("ignoring invalid value '~p' for "
?ERROR_MSG("Ignoring invalid value '~p' for "
"option '~s' of module '~s'",
[Val, Opt, Module]),
fail_if_option_is_required(Opt, Required),
[]
end
end;
false ->
case Validators of
[] ->
?ERROR_MSG("unknown option '~s' for module '~s' "
"will be likely ignored because the "
"module doesn't have any options",
?ERROR_MSG("Ignoring unknown option '~s' of '~s':"
" the module doesn't have any options",
[Opt, Module]);
_ ->
?ERROR_MSG("unknown option '~s' for module '~s' will be"
" likely ignored, available options are: ~s",
?ERROR_MSG("Ignoring unknown option '~s' of '~s',"
" available options are: ~s",
[Opt, Module,
misc:join_atoms([K || {K, _} <- Validators],
<<", ">>)])
misc:join_atoms(
[K || {K, _} <- Validators],
<<", ">>)])
end,
[{Opt, Val}]
[]
end;
(_) ->
erlang:error(bad_option)
end, Opts);
validate_opts(_, _, _, _) ->
validate_opts(_, _, _, _, _) ->
erlang:error(bad_option).
-spec validate_opt(module(), atom(), any(),
-spec validate_opt(module(), atom(), any(), [atom()],
[{atom(), check_fun(), any()}]) -> [{atom(), any()}].
validate_opt(Module, Opt, Val, VFun) ->
validate_opt(Module, Opt, Val, Required, VFun) ->
try VFun(Val) of
NewVal -> [{Opt, NewVal}]
catch {invalid_syntax, Error} ->
?ERROR_MSG("ignoring invalid value '~p' for "
?ERROR_MSG("Ignoring invalid value '~p' for "
"option '~s' of module '~s': ~s",
[Val, Opt, Module, Error]),
fail_if_option_is_required(Opt, Required),
[];
_:_ ->
?ERROR_MSG("ignoring invalid value '~p' for "
?ERROR_MSG("Ignoring invalid value '~p' for "
"option '~s' of module '~s'",
[Val, Opt, Module]),
fail_if_option_is_required(Opt, Required),
[]
end.
-spec fail_if_option_is_required(atom(), [atom()]) -> ok | no_return().
fail_if_option_is_required(Opt, Required) ->
case lists:member(Opt, Required) of
true -> erlang:error({missing_required_option, Opt});
false -> ok
end.
-spec list_known_opts(binary(), module()) -> [atom() | {atom(), atom()}].
list_known_opts(Host, Module) ->
try Module:mod_options(Host) of
DefaultOpts ->
lists:flatmap(
fun({Opt, [{A, _}|_] = Vals}) when is_atom(A) ->
[{Opt, Val} || {Val, _} <- Vals];
({Opt, _}) -> [Opt];
(Opt) -> [Opt]
end, DefaultOpts)
catch _:undef ->
Module:mod_opt_type('')
end.
-spec merge_opts(opts(), opts(), module()) -> opts().
merge_opts(Opts, DefaultOpts, Module) ->
Result =
lists:foldr(
fun({Opt, Default}, Acc) ->
case lists:keyfind(Opt, 1, Opts) of
{_, Val} ->
case Default of
[{A, _}|_] when is_atom(A) andalso is_list(Val) ->
case is_opt_list(Val) of
true ->
[{Opt, merge_opts(Val, Default, Module)}|Acc];
false ->
?ERROR_MSG(
"Ignoring invalid value '~p' for "
"option '~s' of module '~s'",
[Val, Opt, Module]),
[{Opt, Default}|Acc]
end;
Val ->
[{Opt, Default}|Acc];
_ ->
[{Opt, Val}, {Opt, Default}|Acc]
end;
_ ->
[{Opt, Default}|Acc]
end;
(Opt, Acc) ->
case lists:keyfind(Opt, 1, Opts) of
{_, Val} ->
[{Opt, Val}|Acc];
false ->
erlang:error({missing_required_option, Opt})
end
end, [], DefaultOpts),
lists:foldl(
fun({Opt, Val}, Acc) ->
case lists:keymember(Opt, 1, Result) of
true -> Acc;
false -> [{Opt, Val}|Acc]
end
end, Result, Opts).
remove_duplicated_opts([{Opt, Val}, {Opt, _Default}|Opts]) ->
[{Opt, Val}|remove_duplicated_opts(Opts)];
remove_duplicated_opts([{Opt, [{SubOpt, _}|_] = SubOpts}|Opts])
when is_atom(SubOpt) ->
[{Opt, remove_duplicated_opts(SubOpts)}|remove_duplicated_opts(Opts)];
remove_duplicated_opts([OptVal|Opts]) ->
[OptVal|remove_duplicated_opts(Opts)];
remove_duplicated_opts([]) ->
[].
-spec get_submodules(binary(), module(), opts()) -> [module()].
get_submodules(Host, Module, Opts) ->
try Module:mod_options(Host) of
DefaultOpts ->
Mod1 = case lists:keyfind(db_type, 1, DefaultOpts) of
{_, T1} ->
DBType = proplists:get_value(db_type, Opts, T1),
[db_mod(DBType, Module)];
false ->
[]
end,
Mod2 = case lists:keyfind(ram_db_type, 1, DefaultOpts) of
{_, T2} ->
RamDBType = proplists:get_value(ram_db_type, Opts, T2),
[ram_db_mod(RamDBType, Module)];
false ->
[]
end,
Mod1 ++ Mod2
catch _:undef ->
[]
end.
@@ -630,20 +787,45 @@ ram_db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
ram_db_mod(Host, Opts, Module) when is_list(Opts) ->
ram_db_mod(ram_db_type(Host, Opts, Module), Module).
is_db_configured(Type, Host) ->
lists:any(
fun(#ejabberd_module{module_host = {_, H}, opts = Opts})
when H == Host orelse Host == global ->
case lists:keyfind(db_type, 1, Opts) of
{_, Type} -> true;
_ ->
case lists:keyfind(ram_db_type, 1, Opts) of
{_, Type} -> true;
_ -> false
end
end;
(_) ->
false
end, ets:tab2list(ejabberd_modules)).
-spec loaded_modules(binary()) -> [atom()].
loaded_modules(Host) ->
ets:select(ejabberd_modules,
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
[], ['$1']}]).
Mods = ets:select(
ejabberd_modules,
ets:fun2ms(
fun(#ejabberd_module{module_host = {Mod, H},
order = Order}) when H == Host ->
{Mod, Order}
end)),
[Mod || {Mod, _} <- lists:keysort(2, Mods)].
-spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}].
loaded_modules_with_opts(Host) ->
ets:select(ejabberd_modules,
[{#ejabberd_module{_ = '_', module_host = {'$1', Host},
opts = '$2'},
[], [{{'$1', '$2'}}]}]).
Mods = ets:select(
ejabberd_modules,
ets:fun2ms(
fun(#ejabberd_module{module_host = {Mod, H}, opts = Opts,
order = Order}) when H == Host ->
{Mod, Opts, Order}
end)),
[{Mod, Opts} || {Mod, Opts, _} <- lists:keysort(3, Mods)].
-spec get_hosts(opts(), binary()) -> [binary()].
@@ -660,9 +842,9 @@ get_hosts(Opts, Prefix) ->
Hosts
end.
-spec get_module_proc(binary(), {frontend, atom()} | atom()) -> atom().
get_module_proc(Host, {frontend, Base}) ->
get_module_proc(<<"frontend_", Host/binary>>, Base);
-spec get_module_proc(binary() | global, atom()) -> atom().
get_module_proc(global, Base) ->
get_module_proc(<<"global">>, Base);
get_module_proc(Host, Base) ->
binary_to_atom(
<<(erlang:atom_to_binary(Base, latin1))/binary, "_", Host/binary>>,
@@ -689,23 +871,35 @@ config_reloaded() ->
reload_modules(Host)
end, ?MYHOSTS).
-spec is_equal_opt(atom(), opts(), opts(), any()) ->
-spec is_equal_opt(atom(), opts(), opts()) ->
true | {false, any(), any()}.
is_equal_opt(Opt, NewOpts, OldOpts, Default) ->
NewVal = get_opt(Opt, NewOpts, Default),
OldVal = get_opt(Opt, OldOpts, Default),
is_equal_opt(Opt, NewOpts, OldOpts) ->
NewVal = get_opt(Opt, NewOpts),
OldVal = get_opt(Opt, OldOpts),
if NewVal /= OldVal ->
{false, NewVal, OldVal};
true ->
true
end.
-spec is_opt_list(term()) -> boolean().
is_opt_list([]) ->
true;
is_opt_list(L) when is_list(L) ->
lists:all(
fun({Opt, _Val}) -> is_atom(Opt);
(_) -> false
end, L);
is_opt_list(_) ->
false.
-spec opt_type(modules) -> fun(([{atom(), list()}]) -> [{atom(), list()}]);
(atom()) -> [atom()].
opt_type(modules) ->
fun(Mods) ->
lists:map(
fun({M, A}) when is_atom(M), is_list(A) ->
fun({M, A}) when is_atom(M) ->
true = is_opt_list(A),
{M, A}
end, Mods)
end;
+26 -9
View File
@@ -29,11 +29,11 @@
%% API
-export([tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
hex_to_bin/1, hex_to_base64/1, expand_keyword/3,
hex_to_bin/1, hex_to_base64/1, url_encode/1, expand_keyword/3,
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
compile_exprs/2, join_atoms/2, try_read_file/1, have_eimp/0,
compile_exprs/2, join_atoms/2, try_read_file/1,
css_dir/0, img_dir/0, js_dir/0, read_css/1, read_img/1, read_js/1]).
%% Deprecated functions
@@ -65,7 +65,7 @@ term_to_base64(Term) ->
base64_to_term(Base64) ->
try binary_to_term(base64:decode(Base64), [safe]) of
Term -> {term, Term}
catch _:badarg ->
catch _:_ ->
error
end.
@@ -105,6 +105,10 @@ hex_to_bin([H1, H2 | T], Acc) ->
hex_to_base64(Hex) ->
base64:encode(hex_to_bin(Hex)).
-spec url_encode(binary()) -> binary().
url_encode(A) ->
url_encode(A, <<>>).
-spec expand_keyword(binary(), binary(), binary()) -> binary().
expand_keyword(Keyword, Input, Replacement) ->
Parts = binary:split(Input, Keyword, [global]),
@@ -214,12 +218,6 @@ try_read_file(Path) ->
erlang:error(badarg)
end.
-ifdef(GRAPHICS).
have_eimp() -> true.
-else.
have_eimp() -> false.
-endif.
-spec css_dir() -> file:filename().
css_dir() ->
case os:getenv("EJABBERD_CSS_PATH") of
@@ -268,6 +266,25 @@ read_js(File) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec url_encode(binary(), binary()) -> binary().
url_encode(<<H:8, T/binary>>, Acc) when
(H >= $a andalso H =< $z) orelse
(H >= $A andalso H =< $Z) orelse
(H >= $0 andalso H =< $9) orelse
H == $_ orelse
H == $. orelse
H == $- orelse
H == $/ orelse
H == $: ->
url_encode(T, <<Acc/binary, H>>);
url_encode(<<H:8, T/binary>>, Acc) ->
case integer_to_list(H, 16) of
[X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
[X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
end;
url_encode(<<>>, Acc) ->
Acc.
-spec set_node_id(string(), binary()) -> pid().
set_node_id(PidStr, NodeBin) ->
ExtPidStr = erlang:pid_to_list(
+13 -23
View File
@@ -35,19 +35,18 @@
process_sm_iq/1, get_local_commands/5,
get_local_identity/5, get_local_features/5,
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
ping_item/4, ping_command/4, mod_opt_type/1, depends/2]).
ping_item/4, ping_command/4, mod_opt_type/1, depends/2,
mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
start(Host, _Opts) ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_COMMANDS, ?MODULE, process_local_iq,
IQDisc),
?NS_COMMANDS, ?MODULE, process_local_iq),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_COMMANDS, ?MODULE, process_sm_iq, IQDisc),
?NS_COMMANDS, ?MODULE, process_sm_iq),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE,
get_local_identity, 99),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
@@ -87,16 +86,8 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host,
?NS_COMMANDS).
reload(Host, NewOpts, OldOpts) ->
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS,
?MODULE, process_sm_iq, IQDisc);
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
%-------------------------------------------------------------------------
@@ -104,8 +95,7 @@ get_local_commands(Acc, _From,
#jid{server = Server, lserver = LServer} = _To, <<"">>,
Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE,
report_commands_node,
false),
report_commands_node),
case Display of
false -> Acc;
_ ->
@@ -133,8 +123,7 @@ get_local_commands(Acc, _From, _To, _Node, _Lang) ->
get_sm_commands(Acc, _From,
#jid{lserver = LServer} = To, <<"">>, Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE,
report_commands_node,
false),
report_commands_node),
case Display of
false -> Acc;
_ ->
@@ -281,7 +270,8 @@ ping_command(Acc, _From, _To, _Request) -> Acc.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(report_commands_node) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) -> [iqdisc, report_commands_node].
fun (B) when is_boolean(B) -> B end.
mod_options(_Host) ->
[{report_commands_node, false}].
+30 -27
View File
@@ -30,7 +30,7 @@
-include("logger.hrl").
-export([start/2, stop/1, reload/3, mod_opt_type/1,
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
% Commands API
@@ -59,6 +59,7 @@
add_rosteritem/7, delete_rosteritem/4,
process_rosteritems/5, get_roster/2, push_roster/3,
push_roster_all/1, push_alltoall/2,
push_roster_item/5, build_roster_item/3,
% Private storage
private_get/4, private_set/3,
@@ -224,7 +225,7 @@ get_commands_spec() ->
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password_hash, tags = [accounts],
desc = "Check if the password hash is correct",
longdesc = "Allowed hash methods: md5, sha.",
longdesc = "Allows hash methods from crypto application",
module = ?MODULE, function = check_password_hash,
args = [{user, binary}, {host, binary}, {passwordhash, binary},
{hashmethod, binary}],
@@ -785,24 +786,23 @@ get_cookie() ->
restart_module(Host, Module) when is_binary(Module) ->
restart_module(Host, misc:binary_to_atom(Module));
restart_module(Host, Module) when is_atom(Module) ->
List = gen_mod:loaded_modules_with_opts(Host),
case proplists:get_value(Module, List) of
undefined ->
case gen_mod:is_loaded(Host, Module) of
false ->
% not a running module, force code reload anyway
code:purge(Module),
code:delete(Module),
code:load_file(Module),
1;
Opts ->
true ->
gen_mod:stop_module(Host, Module),
case code:soft_purge(Module) of
true ->
code:delete(Module),
code:load_file(Module),
gen_mod:start_module(Host, Module, Opts),
gen_mod:start_module(Host, Module),
0;
false ->
gen_mod:start_module(Host, Module, Opts),
gen_mod:start_module(Host, Module),
2
end
end.
@@ -821,13 +821,15 @@ check_password(User, Host, Password) ->
%% Copied some code from ejabberd_commands.erl
check_password_hash(User, Host, PasswordHash, HashMethod) ->
AccountPass = ejabberd_auth:get_password_s(User, Host),
AccountPassHash = case {AccountPass, HashMethod} of
Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
proplists:get_value(hashs, crypto:supports())),
MethodAllowed = lists:member(HashMethod, Methods),
AccountPassHash = case {AccountPass, MethodAllowed} of
{A, _} when is_tuple(A) -> scrammed;
{_, <<"md5">>} -> get_md5(AccountPass);
{_, <<"sha">>} -> get_sha(AccountPass);
{_, Method} ->
{_, true} -> get_hash(AccountPass, HashMethod);
{_, false} ->
?ERROR_MSG("check_password_hash called "
"with hash method: ~p", [Method]),
"with hash method: ~p", [HashMethod]),
undefined
end,
case AccountPassHash of
@@ -838,15 +840,14 @@ check_password_hash(User, Host, PasswordHash, HashMethod) ->
PasswordHash -> ok;
_ -> false
end.
get_md5(AccountPass) ->
get_hash(AccountPass, Method) ->
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|| X <- binary_to_list(erlang:md5(AccountPass))]).
get_sha(AccountPass) ->
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|| X <- binary_to_list(crypto:hash(sha, AccountPass))]).
|| X <- binary_to_list(
crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
num_active_users(Host, Days) ->
DB_Type = gen_mod:db_type(Host, mod_last),
DB_Type = gen_mod:get_module_opt(Host, mod_last, db_type),
list_last_activity(Host, true, Days, DB_Type).
%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
@@ -1025,7 +1026,7 @@ get_status_list(Host, Status_required) ->
Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
Fhost = case Host of
<<"all">> ->
%% All hosts are requested, so dont filter at all
%% All hosts are requested, so don't filter at all
fun(_, _) -> true end;
_ ->
%% Filter the list, only Host is interesting
@@ -1461,7 +1462,7 @@ private_get(Username, Host, Element, Ns) ->
ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
[{Ns, ElementXml}]),
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{xml_els = Els}))).
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
private_set(Username, Host, ElementString) ->
case fxml_stream:parse_element(ElementString) of
@@ -1506,11 +1507,12 @@ srg_get_info(Group, Host) ->
Os when is_list(Os) -> Os;
error -> []
end,
[{misc:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
btl([]) -> [];
btl([B|L]) -> [btl(B)|btl(L)];
btl(B) -> binary_to_list(B).
to_list([]) -> [];
to_list([H|T]) -> [to_list(H)|to_list(T)];
to_list(E) when is_atom(E) -> atom_to_list(E);
to_list(E) -> binary_to_list(E).
srg_get_members(Group, Host) ->
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
@@ -1549,7 +1551,8 @@ send_stanza(FromString, ToString, Stanza) ->
#xmlel{} = El = fxml_stream:parse_element(Stanza),
From = jid:decode(FromString),
To = jid:decode(ToString),
Pkt = xmpp:decode(El, ?NS_CLIENT, [ignore_els]),
CodecOpts = ejabberd_config:codec_options(From#jid.lserver),
Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
catch _:{xmpp_codec, Why} ->
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
@@ -1761,4 +1764,4 @@ is_glob_match(String, <<"!", Glob/binary>>) ->
is_glob_match(String, Glob) ->
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
mod_opt_type(_) -> [].
mod_options(_) -> [].
+2 -2
View File
@@ -28,7 +28,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, mod_opt_type/1,
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
% Commands API
@@ -362,4 +362,4 @@ sql_query(Host, Query) ->
ok
end.
mod_opt_type(_) -> [].
mod_options(_) -> [].
+22 -24
View File
@@ -36,7 +36,7 @@
import_start/2, import/5, announce/1, send_motd/1, disco_identity/5,
disco_features/5, disco_items/5, depends/2,
send_announcement_to_all/3, announce_commands/4,
announce_items/4, mod_opt_type/1, clean_cache/1]).
announce_items/4, mod_opt_type/1, mod_options/1, clean_cache/1]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
-export([announce_all/1,
@@ -715,7 +715,8 @@ send_motd({#presence{type = available},
Mod = gen_mod:db_mod(LServer, ?MODULE),
case get_motd(Mod, LServer) of
{ok, Packet} ->
try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
CodecOpts = ejabberd_config:codec_options(LServer),
try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
Msg ->
case is_motd_user(Mod, LUser, LServer) of
false ->
@@ -806,7 +807,8 @@ get_stored_motd(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
case get_motd(Mod, LServer) of
{ok, Packet} ->
try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
CodecOpts = ejabberd_config:codec_options(LServer),
try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
#message{body = Body, subject = Subject} ->
{xmpp:get_text(Subject), xmpp:get_text(Body)}
catch _:{xmpp_codec, Why} ->
@@ -834,7 +836,7 @@ send_announcement_to_all(Host, SubjectS, BodyS) ->
-spec get_access(global | binary()) -> atom().
get_access(Host) ->
gen_mod:get_module_opt(Host, ?MODULE, access, none).
gen_mod:get_module_opt(Host, ?MODULE, access).
-spec add_store_hint(stanza()) -> stanza().
add_store_hint(El) ->
@@ -850,23 +852,17 @@ route_forbidden_error(Packet) ->
init_cache(Mod, Host, Opts) ->
case use_cache(Mod, Host) of
true ->
CacheOpts = cache_opts(Host, Opts),
CacheOpts = cache_opts(Opts),
ets_cache:new(?MOTD_CACHE, CacheOpts);
false ->
ets_cache:delete(?MOTD_CACHE)
end.
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(
cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(
cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(
cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
cache_opts(Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts),
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -876,10 +872,7 @@ cache_opts(Host, Opts) ->
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(Host);
false ->
gen_mod:get_module_opt(
Host, ?MODULE, use_cache,
ejabberd_config:use_cache(Host))
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -918,7 +911,12 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
(infinity) -> infinity
end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[access, db_type, cache_life_time, cache_size,
use_cache, cache_missed].
fun (B) when is_boolean(B) -> B end.
mod_options(Host) ->
[{access, none},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
+69 -51
View File
@@ -23,10 +23,13 @@
-module(mod_avatar).
-behaviour(gen_mod).
-protocol({xep, 398, '0.2.0'}).
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]).
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
%% Hooks
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1]).
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1,
get_sm_features/5]).
-include("xmpp.hrl").
-include("logger.hrl").
@@ -38,27 +41,22 @@
%%% API
%%%===================================================================
start(Host, _Opts) ->
case misc:have_eimp() of
true ->
ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE,
pubsub_publish_item, 50),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
vcard_iq_convert, 30),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
vcard_iq_publish, 100);
false ->
?CRITICAL_MSG("ejabberd is built without "
"graphics support: reconfigure it with "
"--enable-graphics or disable '~s'",
[?MODULE]),
{error, graphics_not_compiled}
end.
ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE,
pubsub_publish_item, 50),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
vcard_iq_convert, 30),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
vcard_iq_publish, 100),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
get_sm_features, 50).
stop(Host) ->
ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE,
pubsub_publish_item, 50),
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30),
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100).
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE,
get_sm_features, 50).
reload(_Host, _NewOpts, _OldOpts) ->
ok.
@@ -153,6 +151,20 @@ vcard_iq_publish(#iq{sub_els = [#vcard_temp{
vcard_iq_publish(Acc) ->
Acc.
-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
jid(), jid(), binary(), binary()) ->
{error, stanza_error()} | empty | {result, [binary()]}.
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
{result, [?NS_DISCO_INFO, ?NS_PEP_VCARD_CONVERSION_0 |
case Acc of
{result, Features} -> Features;
empty -> []
end]};
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -325,7 +337,10 @@ convert_avatar(LUser, LServer, Data, Rules) ->
true ->
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
[LUser, LServer, Type, NewType]),
case eimp:convert(Data, NewType) of
RateLimit = gen_mod:get_module_opt(LServer, ?MODULE, rate_limit),
Opts = [{limit_by, {LUser, LServer}},
{rate_limit, RateLimit}],
case eimp:convert(Data, NewType, Opts) of
{ok, NewData} ->
{ok, encode_mime_type(NewType), NewData};
{error, Reason} = Err ->
@@ -384,7 +399,7 @@ stop_with_error(Lang, Reason) ->
-spec get_converting_rules(binary()) -> convert_rules().
get_converting_rules(LServer) ->
gen_mod:get_module_opt(LServer, ?MODULE, convert, []).
gen_mod:get_module_opt(LServer, ?MODULE, convert).
-spec get_type(binary()) -> eimp:img_type() | unknown.
get_type(Data) ->
@@ -416,35 +431,38 @@ decode_mime_type(MimeType) ->
encode_mime_type(Type) ->
<<"image/", (atom_to_binary(Type, latin1))/binary>>.
mod_opt_type({convert, png}) ->
fun(jpeg) -> jpeg;
(webp) -> webp;
(gif) -> gif
-spec fail(atom()) -> no_return().
fail(Format) ->
FormatS = case Format of
webp -> "WebP";
png -> "PNG";
jpeg -> "JPEG";
gif -> "GIF";
_ -> ""
end,
if FormatS /= "" ->
?WARNING_MSG("ejabberd is not compiled with ~s support", [FormatS]);
true ->
ok
end,
erlang:error(badarg).
mod_opt_type({convert, From}) ->
fun(To) when is_atom(To), To /= From ->
case eimp:is_supported(From) orelse From == default of
false ->
fail(From);
true ->
case eimp:is_supported(To) orelse To == undefined of
false -> fail(To);
true -> To
end
end
end;
mod_opt_type({convert, webp}) ->
fun(jpeg) -> jpeg;
(png) -> png;
(gif) -> gif
end;
mod_opt_type({convert, jpeg}) ->
fun(png) -> png;
(webp) -> webp;
(gif) -> gif
end;
mod_opt_type({convert, gif}) ->
fun(png) -> png;
(jpeg) -> jpeg;
(webp) -> webp
end;
mod_opt_type({convert, default}) ->
fun(png) -> png;
(webp) -> webp;
(jpeg) -> jpeg;
(gif) -> gif
end;
mod_opt_type(_) ->
[{convert, default},
{convert, webp},
{convert, png},
{convert, gif},
{convert, jpeg}].
mod_opt_type(rate_limit) ->
fun(I) when is_integer(I), I > 0 -> I end.
mod_options(_) ->
[{rate_limit, 10},
{convert,
[{T, undefined} || T <- [default|eimp:supported_formats()]]}].
+134 -39
View File
@@ -30,9 +30,9 @@
%% API
-export([start/2, stop/1, reload/3,
depends/2, mod_opt_type/1]).
depends/2, mod_opt_type/1, mod_options/1]).
-export([filter_packet/1, filter_offline_msg/1]).
-export([filter_packet/1, filter_offline_msg/1, filter_subscription/2]).
-include("xmpp.hrl").
-include("ejabberd.hrl").
@@ -40,15 +40,22 @@
-define(SETS, gb_sets).
%%%===================================================================
%%% Callbacks and hooks
%%%===================================================================
start(Host, _Opts) ->
ejabberd_hooks:add(user_receive_packet, Host,
?MODULE, filter_packet, 25),
ejabberd_hooks:add(roster_in_subscription, Host,
?MODULE, filter_subscription, 25),
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, filter_offline_msg, 25).
stop(Host) ->
ejabberd_hooks:delete(user_receive_packet, Host,
?MODULE, filter_packet, 25),
ejabberd_hooks:delete(roster_in_subscription, Host,
?MODULE, filter_subscription, 25),
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, filter_offline_msg, 25).
@@ -79,19 +86,74 @@ filter_offline_msg({_Action, #message{} = Msg} = Acc) ->
deny -> {stop, {drop, Msg}}
end.
filter_subscription(Acc, #presence{meta = #{captcha := passed}}) ->
Acc;
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
id = SID, type = subscribe} = Pres) ->
LServer = To#jid.lserver,
case gen_mod:get_module_opt(LServer, ?MODULE, drop) andalso
gen_mod:get_module_opt(LServer, ?MODULE, captcha) andalso
need_check(Pres) of
true ->
case check_subscription(From, To) of
false ->
BFrom = jid:remove_resource(From),
BTo = jid:remove_resource(To),
Limiter = jid:tolower(BFrom),
case ejabberd_captcha:create_captcha(
SID, BTo, BFrom, Lang, Limiter,
fun(Res) -> handle_captcha_result(Res, Pres) end) of
{ok, ID, Body, CaptchaEls} ->
Msg = #message{from = BTo, to = From,
id = ID, body = Body,
sub_els = CaptchaEls},
case gen_mod:get_module_opt(LServer, ?MODULE, log) of
true ->
?INFO_MSG("Challenge subscription request "
"from stranger ~s to ~s with "
"CAPTCHA",
[jid:encode(From), jid:encode(To)]);
false ->
ok
end,
ejabberd_router:route(Msg);
{error, limit} ->
ErrText = <<"Too many CAPTCHA requests">>,
Err = xmpp:err_resource_constraint(ErrText, Lang),
ejabberd_router:route_error(Pres, Err);
_ ->
ErrText = <<"Unable to generate a CAPTCHA">>,
Err = xmpp:err_internal_server_error(ErrText, Lang),
ejabberd_router:route_error(Pres, Err)
end,
{stop, false};
true ->
Acc
end;
false ->
Acc
end;
filter_subscription(Acc, _) ->
Acc.
handle_captcha_result(captcha_succeed, Pres) ->
Pres1 = xmpp:put_meta(Pres, captcha, passed),
ejabberd_router:route(Pres1);
handle_captcha_result(captcha_failed, #presence{lang = Lang} = Pres) ->
Txt = <<"The CAPTCHA verification has failed">>,
ejabberd_router:route_error(Pres, xmpp:err_not_allowed(Txt, Lang)).
%%%===================================================================
%%% Internal functions
%%%===================================================================
check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
LServer = To#jid.lserver,
AllowLocalUsers =
gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users, true),
case (Msg#message.body == [] andalso
Msg#message.subject == [])
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>) andalso
ejabberd_router:is_my_host(From#jid.lserver)) of
false ->
case need_check(Msg) of
true ->
case check_subscription(From, To) of
none ->
Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop, true),
Log = gen_mod:get_module_opt(LServer, ?MODULE, log, false),
false ->
Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop),
Log = gen_mod:get_module_opt(LServer, ?MODULE, log),
if
Log ->
?INFO_MSG("~s message from stranger ~s to ~s",
@@ -106,46 +168,67 @@ check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
Drop ->
Txt = <<"Messages from strangers are rejected">>,
Err = xmpp:err_policy_violation(Txt, Lang),
ejabberd_router:route_error(Msg, Err),
Msg1 = maybe_adjust_from(Msg),
ejabberd_router:route_error(Msg1, Err),
deny;
true ->
allow
end;
some ->
true ->
allow
end;
true ->
false ->
allow
end.
-spec check_subscription(jid(), jid()) -> none | some.
-spec maybe_adjust_from(message()) -> message().
maybe_adjust_from(#message{type = groupchat, from = From} = Msg) ->
Msg#message{from = jid:remove_resource(From)};
maybe_adjust_from(#message{} = Msg) ->
Msg.
-spec need_check(presence() | message()) -> boolean().
need_check(Pkt) ->
To = xmpp:get_to(Pkt),
From = xmpp:get_from(Pkt),
LServer = To#jid.lserver,
IsEmpty = case Pkt of
#message{body = [], subject = []} ->
true;
_ ->
false
end,
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
not (IsEmpty orelse acl:match_rule(LServer, Access, From) == allow
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
andalso ejabberd_router:is_my_host(From#jid.lserver))).
-spec check_subscription(jid(), jid()) -> boolean().
check_subscription(From, To) ->
{LocalUser, LocalServer, _} = jid:tolower(To),
{RemoteUser, RemoteServer, _} = jid:tolower(From),
case ejabberd_hooks:run_fold(
roster_get_jid_info, LocalServer,
{none, []}, [LocalUser, LocalServer, From]) of
{none, _} when RemoteUser == <<"">> ->
none;
{none, _} ->
case gen_mod:get_module_opt(LocalServer, ?MODULE,
allow_transports, true) of
true ->
%% Check if the contact's server is in the roster
case ejabberd_hooks:run_fold(
roster_get_jid_info, LocalServer,
{none, []},
[LocalUser, LocalServer, jid:make(RemoteServer)]) of
{none, _} -> none;
_ -> some
end;
false ->
none
end;
_ ->
some
case has_subscription(LocalUser, LocalServer, From) of
false when RemoteUser == <<"">> ->
false;
false ->
%% Check if the contact's server is in the roster
gen_mod:get_module_opt(LocalServer, ?MODULE, allow_transports)
andalso has_subscription(LocalUser, LocalServer,
jid:make(RemoteServer));
true ->
true
end.
-spec has_subscription(binary(), binary(), jid()) -> boolean().
has_subscription(User, Server, JID) ->
{Sub, Ask, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, Server,
{none, none, []},
[User, Server, JID]),
(Sub /= none) orelse (Ask == subscribe)
orelse (Ask == out) orelse (Ask == both).
sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
case ?SETS:next(sets_iterator_from(LBJID, Set)) of
{{U, S, _}, _} -> true;
@@ -183,4 +266,16 @@ mod_opt_type(allow_local_users) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(allow_transports) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) -> [drop, log, allow_local_users, allow_transports].
mod_opt_type(captcha) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(access) ->
fun acl:access_rules_validator/1.
mod_options(_) ->
[{access, none},
{drop, true},
{log, false},
{captcha, false},
{allow_local_users, true},
{allow_transports, true}].
+10 -16
View File
@@ -29,8 +29,8 @@
-protocol({xep, 191, '1.2'}).
-export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2,
disco_features/5]).
-export([start/2, stop/1, reload/3, process_iq/1, depends/2,
disco_features/5, mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -39,24 +39,17 @@
-include("mod_privacy.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
start(Host, _Opts) ->
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_BLOCKING, ?MODULE, process_iq, IQDisc).
?NS_BLOCKING, ?MODULE, process_iq).
stop(Host) ->
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
reload(Host, NewOpts, OldOpts) ->
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
?MODULE, process_iq, IQDisc);
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[{mod_privacy, hard}].
@@ -241,10 +234,11 @@ process_unblock(#iq{from = From} = IQ, LJIDs) ->
-spec broadcast_event(jid(), block() | unblock()) -> ok.
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
BFrom = jid:remove_resource(From),
lists:foreach(
fun(R) ->
To = jid:replace_resource(From, R),
IQ = #iq{type = set, from = From, to = To,
IQ = #iq{type = set, from = BFrom, to = To,
id = <<"push", (randoms:get_string())/binary>>,
sub_els = [Event]},
ejabberd_router:route(IQ)
@@ -267,5 +261,5 @@ err_db_failure(#iq{lang = Lang} = IQ) ->
Txt = <<"Database failure">>,
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
mod_options(_Host) ->
[].
+20 -19
View File
@@ -36,7 +36,7 @@
-export([start/2, stop/1, reload/3, process/2, open_session/2,
close_session/1, find_session/1, clean_cache/1]).
-export([depends/2, mod_opt_type/1]).
-export([depends/2, mod_opt_type/1, mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -144,7 +144,7 @@ reload(_Host, NewOpts, _OldOpts) ->
%%% Internal functions
%%%===================================================================
start_jiffy(Opts) ->
case gen_mod:get_opt(json, Opts, false) of
case gen_mod:get_opt(json, Opts) of
false ->
ok;
true ->
@@ -194,10 +194,20 @@ mod_opt_type(O) when O == cache_size; O == cache_life_time ->
fun(I) when is_integer(I), I>0 -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
mod_opt_type(_) ->
[json, max_concat, max_inactivity, max_pause, prebind, ram_db_type,
queue_type, use_cache, cache_size, cache_missed, cache_life_time].
end.
mod_options(Host) ->
[{json, false},
{max_concat, unlimited},
{max_inactivity, 30},
{max_pause, 120},
{prebind, false},
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
{queue_type, ejabberd_config:default_queue_type(Host)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
%%%----------------------------------------------------------------------
%%% Cache stuff
@@ -215,10 +225,7 @@ init_cache(Mod) ->
use_cache(Mod) ->
case erlang:function_exported(Mod, use_cache, 0) of
true -> Mod:use_cache();
false ->
gen_mod:get_module_opt(
global, ?MODULE, use_cache,
ejabberd_config:use_cache(global))
false -> gen_mod:get_module_opt(global, ?MODULE, use_cache)
end.
-spec cache_nodes(module()) -> [node()].
@@ -239,15 +246,9 @@ delete_cache(Mod, SID) ->
-spec cache_opts() -> [proplists:property()].
cache_opts() ->
MaxSize = gen_mod:get_module_opt(
global, ?MODULE, cache_size,
ejabberd_config:cache_size(global)),
CacheMissed = gen_mod:get_module_opt(
global, ?MODULE, cache_missed,
ejabberd_config:cache_missed(global)),
LifeTime = case gen_mod:get_module_opt(
global, ?MODULE, cache_life_time,
ejabberd_config:cache_life_time(global)) of
MaxSize = gen_mod:get_module_opt(global, ?MODULE, cache_size),
CacheMissed = gen_mod:get_module_opt(global, ?MODULE, cache_missed),
LifeTime = case gen_mod:get_module_opt(global, ?MODULE, cache_life_time) of
infinity -> infinity;
I -> timer:seconds(I)
end,
+79 -70
View File
@@ -38,7 +38,8 @@
-export([read_caps/1, list_features/1, caps_stream_features/2,
disco_features/5, disco_identity/5, disco_info/5,
get_features/2, export/1, import_info/0, import/5,
get_user_caps/2, import_start/2, import_stop/2]).
get_user_caps/2, import_start/2, import_stop/2,
compute_disco_hash/2, is_valid_node/1]).
%% gen_mod callbacks
-export([start/2, stop/1, reload/3, depends/2]).
@@ -48,7 +49,7 @@
handle_cast/2, terminate/2, code_change/3]).
-export([user_send_packet/1, user_receive_packet/1,
c2s_presence_in/2, mod_opt_type/1]).
c2s_presence_in/2, mod_opt_type/1, mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -66,6 +67,9 @@
{ok, non_neg_integer() | [binary()]} | error.
-callback caps_write(binary(), {binary(), binary()},
non_neg_integer() | [binary()]) -> any().
-callback use_cache(binary()) -> boolean().
-optional_callbacks([use_cache/1]).
start(Host, Opts) ->
gen_mod:start_child(?MODULE, Host, Opts).
@@ -78,17 +82,23 @@ get_features(_Host, nothing) -> [];
get_features(Host, #caps{node = Node, version = Version,
exts = Exts}) ->
SubNodes = [Version | Exts],
lists:foldl(fun (SubNode, Acc) ->
NodePair = {Node, SubNode},
case ets_cache:lookup(caps_features_cache, NodePair,
caps_read_fun(Host, NodePair))
of
{ok, Features} when is_list(Features) ->
Features ++ Acc;
_ -> Acc
end
end,
[], SubNodes).
Mod = gen_mod:db_mod(Host, ?MODULE),
lists:foldl(
fun(SubNode, Acc) ->
NodePair = {Node, SubNode},
Res = case use_cache(Mod, Host) of
true ->
ets_cache:lookup(caps_features_cache, NodePair,
caps_read_fun(Host, NodePair));
false ->
Mod:caps_read(Host, NodePair)
end,
case Res of
{ok, Features} when is_list(Features) ->
Features ++ Acc;
_ -> Acc
end
end, [], SubNodes).
-spec list_features(ejabberd_c2s:state()) -> [{ljid(), caps()}].
list_features(C2SState) ->
@@ -136,7 +146,7 @@ user_receive_packet({#presence{from = From, type = available} = Pkt,
case read_caps(Pkt) of
nothing -> ok;
#caps{version = Version, exts = Exts} = Caps ->
feature_request(LServer, From, To, Caps, [Version | Exts])
feature_request(LServer, To, From, Caps, [Version | Exts])
end;
true -> ok
end,
@@ -145,16 +155,15 @@ user_receive_packet(Acc) ->
Acc.
-spec caps_stream_features([xmpp_element()], binary()) -> [xmpp_element()].
caps_stream_features(Acc, MyHost) ->
case gen_mod:is_loaded(MyHost, ?MODULE) of
true ->
case make_my_disco_hash(MyHost) of
case make_my_disco_hash(MyHost) of
<<"">> ->
Acc;
Hash ->
Hash ->
[#caps{hash = <<"sha-1">>, node = ?EJABBERD_URI,
version = Hash}|Acc]
version = Hash} | Acc]
end;
false ->
Acc
@@ -203,13 +212,14 @@ disco_info(Acc, _, _, _Node, _Lang) ->
-spec c2s_presence_in(ejabberd_c2s:state(), presence()) -> ejabberd_c2s:state().
c2s_presence_in(C2SState,
#presence{from = From, to = To, type = Type} = Presence) ->
{Subscription, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, To#jid.lserver,
{none, []}, [To#jid.luser, To#jid.lserver, From]),
{Subscription, _, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, To#jid.lserver,
{none, none, []},
[To#jid.luser, To#jid.lserver, From]),
ToSelf = (From#jid.luser == To#jid.luser)
and (From#jid.lserver == To#jid.lserver),
Insert = (Type == available)
and ((Subscription == both) or (Subscription == to) or ToSelf),
and ((Subscription == both) or (Subscription == from) or ToSelf),
Delete = (Type == unavailable) or (Type == error),
if Insert or Delete ->
LFrom = jid:tolower(From),
@@ -250,30 +260,12 @@ reload(Host, NewOpts, OldOpts) ->
true ->
ok
end,
case gen_mod:is_equal_opt(cache_size, NewOpts, OldOpts,
ejabberd_config:cache_size(Host)) of
{false, MaxSize, _} ->
ets_cache:setopts(caps_features_cache, [{max_size, MaxSize}]),
ets_cache:setopts(caps_requests_cache, [{max_size, MaxSize}]);
true ->
ok
end,
case gen_mod:is_equal_opt(cache_life_time, NewOpts, OldOpts,
ejabberd_config:cache_life_time(Host)) of
{false, Time, _} ->
LifeTime = case Time of
infinity -> infinity;
_ -> timer:seconds(Time)
end,
ets_cache:setopts(caps_features_cache, [{life_time, LifeTime}]);
true ->
ok
end.
init_cache(NewMod, Host, NewOpts).
init([Host, Opts]) ->
process_flag(trap_exit, true),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
init_cache(Host, Opts),
init_cache(Mod, Host, Opts),
Mod:init(Host, Opts),
ejabberd_hooks:add(c2s_presence_in, Host, ?MODULE,
c2s_presence_in, 75),
@@ -334,8 +326,15 @@ feature_request(Host, From, To, Caps,
[SubNode | Tail] = SubNodes) ->
Node = Caps#caps.node,
NodePair = {Node, SubNode},
case ets_cache:lookup(caps_features_cache, NodePair,
caps_read_fun(Host, NodePair)) of
Mod = gen_mod:db_mod(Host, ?MODULE),
Res = case use_cache(Mod, Host) of
true ->
ets_cache:lookup(caps_features_cache, NodePair,
caps_read_fun(Host, NodePair));
false ->
Mod:caps_read(Host, NodePair)
end,
case Res of
{ok, Fs} when is_list(Fs) ->
feature_request(Host, From, To, Caps, Tail);
_ ->
@@ -370,7 +369,12 @@ feature_response(#iq{type = result, sub_els = [El]},
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:caps_write(LServer, NodePair, Features) of
ok ->
ets_cache:delete(caps_features_cache, NodePair);
case use_cache(Mod, LServer) of
true ->
ets_cache:delete(caps_features_cache, NodePair);
false ->
ok
end;
{error, _} ->
ok
end;
@@ -409,13 +413,13 @@ make_my_disco_hash(Host) ->
DiscoInfo = #disco_info{identities = Identities,
features = Feats,
xdata = Info},
make_disco_hash(DiscoInfo, sha);
compute_disco_hash(DiscoInfo, sha);
_Err -> <<"">>
end.
-type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
-spec make_disco_hash(disco_info(), digest_type()) -> binary().
make_disco_hash(DiscoInfo, Algo) ->
-spec compute_disco_hash(disco_info(), digest_type()) -> binary().
compute_disco_hash(DiscoInfo, Algo) ->
Concat = list_to_binary([concat_identities(DiscoInfo),
concat_features(DiscoInfo), concat_info(DiscoInfo)]),
base64:encode(case Algo of
@@ -431,17 +435,17 @@ make_disco_hash(DiscoInfo, Algo) ->
check_hash(Caps, DiscoInfo) ->
case Caps#caps.hash of
<<"md5">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, md5);
Caps#caps.version == compute_disco_hash(DiscoInfo, md5);
<<"sha-1">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, sha);
Caps#caps.version == compute_disco_hash(DiscoInfo, sha);
<<"sha-224">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, sha224);
Caps#caps.version == compute_disco_hash(DiscoInfo, sha224);
<<"sha-256">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, sha256);
Caps#caps.version == compute_disco_hash(DiscoInfo, sha256);
<<"sha-384">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, sha384);
Caps#caps.version == compute_disco_hash(DiscoInfo, sha384);
<<"sha-512">> ->
Caps#caps.version == make_disco_hash(DiscoInfo, sha512);
Caps#caps.version == compute_disco_hash(DiscoInfo, sha512);
_ -> true
end.
@@ -478,29 +482,29 @@ is_valid_node(Node) ->
false
end.
init_cache(Host, Opts) ->
CacheOpts = cache_opts(Host, Opts),
case use_cache(Host, Opts) of
init_cache(Mod, Host, Opts) ->
CacheOpts = cache_opts(Opts),
case use_cache(Mod, Host) of
true ->
ets_cache:new(caps_features_cache, CacheOpts);
false ->
ok
ets_cache:delete(caps_features_cache)
end,
CacheSize = proplists:get_value(max_size, CacheOpts),
ets_cache:new(caps_requests_cache,
[{max_size, CacheSize},
{life_time, timer:seconds(?BAD_HASH_LIFETIME)}]).
use_cache(Host, Opts) ->
gen_mod:get_opt(use_cache, Opts, ejabberd_config:use_cache(Host)).
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(Host);
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
end.
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
cache_opts(Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts),
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -547,6 +551,11 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) ->
[cache_life_time, cache_size, use_cache, cache_missed, db_type].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end.
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
+31 -40
View File
@@ -29,14 +29,15 @@
-author ('ecestari@process-one.net').
-protocol({xep, 280, '0.8'}).
-behavior(gen_mod).
-behaviour(gen_mod).
%% API:
-export([start/2, stop/1, reload/3]).
-export([user_send_packet/1, user_receive_packet/1,
iq_handler/1, remove_connection/4, disco_features/5,
is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1]).
is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1,
mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -61,7 +62,6 @@ is_carbon_copy(_) ->
false.
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
Mod = gen_mod:ram_db_mod(Host, ?MODULE),
init_cache(Mod, Host, Opts),
@@ -71,7 +71,7 @@ start(Host, Opts) ->
%% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90)
ejabberd_hooks:add(user_send_packet,Host, ?MODULE, user_send_packet, 89),
ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler, IQDisc).
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler).
stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2),
@@ -91,16 +91,9 @@ reload(Host, NewOpts, OldOpts) ->
end,
case use_cache(NewMod, Host) of
true ->
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, NewOpts));
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(NewOpts));
false ->
ok
end,
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2,
?MODULE, iq_handler, IQDisc);
true ->
ok
end.
-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
@@ -122,10 +115,10 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
{U, S, R} = jid:tolower(From),
Result = case El of
#carbons_enable{} ->
?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
?DEBUG("Carbons enabled for user ~s@~s/~s", [U,S,R]),
enable(S, U, R, ?NS_CARBONS_2);
#carbons_disable{} ->
?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
?DEBUG("Carbons disabled for user ~s@~s/~s", [U,S,R]),
disable(S, U, R)
end,
case Result of
@@ -171,9 +164,9 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
stanza() | {stop, stanza()}.
check_and_forward(JID, To, Packet, Direction)->
case is_chat_message(Packet) andalso
not is_muc_pm(To, Packet) andalso
xmpp:has_subtag(Packet, #carbons_private{}) == false andalso
xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of
not is_received_muc_pm(To, Packet, Direction) andalso
not xmpp:has_subtag(Packet, #carbons_private{}) andalso
not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
true ->
case is_carbon_copy(Packet) of
false ->
@@ -246,7 +239,7 @@ send_copies(JID, To, Packet, Direction)->
-spec build_forward_packet(jid(), message(), jid(), jid(), direction()) -> message().
build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
Forwarded = #forwarded{xml_els = [xmpp:encode(complete_packet(JID, Msg, Direction))]},
Forwarded = #forwarded{sub_els = [complete_packet(JID, Msg, Direction)]},
Carbon = case Direction of
sent -> #carbons_sent{forwarded = Forwarded};
received -> #carbons_received{forwarded = Forwarded}
@@ -289,9 +282,12 @@ is_chat_message(#message{type = normal, body = [_|_]}) ->
is_chat_message(_) ->
false.
is_muc_pm(#jid{lresource = <<>>}, _Packet) ->
-spec is_received_muc_pm(jid(), message(), direction()) -> boolean().
is_received_muc_pm(#jid{lresource = <<>>}, _Packet, _Direction) ->
false;
is_muc_pm(_To, Packet) ->
is_received_muc_pm(_To, _Packet, sent) ->
false;
is_received_muc_pm(_To, Packet, received) ->
xmpp:has_subtag(Packet, #muc_user{}).
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
@@ -321,22 +317,16 @@ list(User, Server) ->
init_cache(Mod, Host, Opts) ->
case use_cache(Mod, Host) of
true ->
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, Opts));
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Opts));
false ->
ets_cache:delete(?CARBONCOPY_CACHE)
end.
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(
cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(
cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(
cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
cache_opts(Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts),
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -346,10 +336,7 @@ cache_opts(Host, Opts) ->
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(Host);
false ->
gen_mod:get_module_opt(
Host, ?MODULE, use_cache,
ejabberd_config:use_cache(Host))
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -386,7 +373,6 @@ delete_cache(Mod, User, Server) ->
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun(B) when is_boolean(B) -> B end;
@@ -394,6 +380,11 @@ mod_opt_type(O) when O == cache_size; O == cache_life_time ->
fun(I) when is_integer(I), I>0 -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
mod_opt_type(_) ->
[ram_db_type, iqdisc, use_cache, cache_size, cache_missed, cache_life_time].
end.
mod_options(Host) ->
[{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
+34 -20
View File
@@ -28,17 +28,17 @@
-protocol({xep, 85, '2.1'}).
-protocol({xep, 352, '0.1'}).
-behavior(gen_mod).
-behaviour(gen_mod).
%% gen_mod callbacks.
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]).
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
%% ejabberd_hooks callbacks.
-export([filter_presence/1, filter_chat_states/1,
filter_pep/1, filter_other/1,
c2s_stream_started/2, add_stream_feature/2,
c2s_copy_session/2, c2s_authenticated_packet/2,
c2s_session_resumed/1]).
c2s_authenticated_packet/2, csi_activity/2,
c2s_copy_session/2, c2s_session_resumed/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -59,9 +59,9 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> ok.
start(Host, Opts) ->
QueuePresence = gen_mod:get_opt(queue_presence, Opts, true),
QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts, true),
QueuePEP = gen_mod:get_opt(queue_pep, Opts, true),
QueuePresence = gen_mod:get_opt(queue_presence, Opts),
QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts),
QueuePEP = gen_mod:get_opt(queue_pep, Opts),
if QueuePresence; QueueChatStates; QueuePEP ->
register_hooks(Host),
if QueuePresence ->
@@ -84,9 +84,9 @@ start(Host, Opts) ->
-spec stop(binary()) -> ok.
stop(Host) ->
QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence, true),
QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states, true),
QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep, true),
QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence),
QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states),
QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep),
if QueuePresence; QueueChatStates; QueuePEP ->
unregister_hooks(Host),
if QueuePresence ->
@@ -109,9 +109,9 @@ stop(Host) ->
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
reload(Host, NewOpts, _OldOpts) ->
QueuePresence = gen_mod:get_opt(queue_presence, NewOpts, true),
QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts, true),
QueuePEP = gen_mod:get_opt(queue_pep, NewOpts, true),
QueuePresence = gen_mod:get_opt(queue_presence, NewOpts),
QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts),
QueuePEP = gen_mod:get_opt(queue_pep, NewOpts),
if QueuePresence; QueueChatStates; QueuePEP ->
register_hooks(Host);
true ->
@@ -145,8 +145,12 @@ mod_opt_type(queue_presence) ->
mod_opt_type(queue_chat_states) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(queue_pep) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep].
fun(B) when is_boolean(B) -> B end.
mod_options(_) ->
[{queue_presence, true},
{queue_chat_states, true},
{queue_pep, true}].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
@@ -160,6 +164,8 @@ register_hooks(Host) ->
add_stream_feature, 50),
ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE,
c2s_authenticated_packet, 50),
ejabberd_hooks:add(csi_activity, Host, ?MODULE,
csi_activity, 50),
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE,
@@ -175,6 +181,8 @@ unregister_hooks(Host) ->
add_stream_feature, 50),
ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE,
c2s_authenticated_packet, 50),
ejabberd_hooks:delete(csi_activity, Host, ?MODULE,
csi_activity, 50),
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE,
@@ -190,14 +198,20 @@ c2s_stream_started(State, _) ->
init_csi_state(State).
-spec c2s_authenticated_packet(c2s_state(), xmpp_element()) -> c2s_state().
c2s_authenticated_packet(C2SState, #csi{type = active}) ->
C2SState1 = C2SState#{csi_state => active},
flush_queue(C2SState1);
c2s_authenticated_packet(C2SState, #csi{type = inactive}) ->
C2SState#{csi_state => inactive};
c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = active}) ->
ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [active]);
c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = inactive}) ->
ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [inactive]);
c2s_authenticated_packet(C2SState, _) ->
C2SState.
-spec csi_activity(c2s_state(), active | inactive) -> c2s_state().
csi_activity(C2SState, active) ->
C2SState1 = C2SState#{csi_state => active},
flush_queue(C2SState1);
csi_activity(C2SState, inactive) ->
C2SState#{csi_state => inactive}.
-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
c2s_copy_session(C2SState, #{csi_queue := Q}) ->
C2SState#{csi_queue => Q};
+7 -4
View File
@@ -35,7 +35,7 @@
get_local_features/5, get_local_items/5,
adhoc_local_items/4, adhoc_local_commands/4,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1,
adhoc_sm_items/4, adhoc_sm_commands/4, mod_options/1,
depends/2]).
-include("ejabberd.hrl").
@@ -1528,8 +1528,11 @@ set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang,
true = lists:member(Server, ?MYHOSTS),
true = Server == Host orelse
get_permission_level(From) == global,
ejabberd_auth:try_register(User, Server, Password),
{result, undefined};
case ejabberd_auth:try_register(User, Server, Password) of
ok -> {result, undefined};
{error, exists} -> {error, xmpp:err_conflict()};
{error, not_allowed} -> {error, xmpp:err_not_allowed()}
end;
set_form(From, Host, ?NS_ADMINL(<<"delete-user">>),
_Lang, XData) ->
AccountStringList = get_values(<<"accountjids">>,
@@ -1809,4 +1812,4 @@ set_sm_form(User, Server, <<"config">>,
set_sm_form(_User, _Server, _Node, _Request) ->
{error, xmpp:err_service_unavailable()}.
mod_opt_type(_) -> [].
mod_options(_) -> [].
+11 -10
View File
@@ -31,7 +31,7 @@
-behaviour(gen_mod).
%% API
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]).
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -61,7 +61,6 @@ stop(Host) ->
reload(_Host, _NewOpts, _OldOpts) ->
ok.
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(namespaces) ->
fun(L) ->
lists:map(
@@ -70,9 +69,10 @@ mod_opt_type(namespaces) ->
Access = proplists:get_value(access, Opts, none),
{NS, Attrs, Access}
end, L)
end;
mod_opt_type(_) ->
[namespaces, iqdisc].
end.
mod_options(_Host) ->
[{namespaces, []}].
depends(_, _) ->
[].
@@ -151,7 +151,7 @@ handle_cast({component_connected, Host}, State) ->
ServerHost = State#state.server_host,
To = jid:make(Host),
NSAttrsAccessList = gen_mod:get_module_opt(
ServerHost, ?MODULE, namespaces, []),
ServerHost, ?MODULE, namespaces),
lists:foreach(
fun({NS, _Attrs, Access}) ->
case acl:match_rule(ServerHost, Access, To) of
@@ -240,7 +240,7 @@ process_iq(#iq{to = To, lang = Lang, sub_els = [SubEl]} = IQ, Type) ->
case dict:find({NS, Type}, Delegations) of
{ok, {Host, _}} ->
Delegation = #delegation{
forwarded = #forwarded{xml_els = [xmpp:encode(IQ)]}},
forwarded = #forwarded{sub_els = [IQ]}},
NewFrom = jid:make(LServer),
NewTo = jid:make(Host),
ejabberd_router:route_iq(
@@ -259,9 +259,10 @@ process_iq(#iq{to = To, lang = Lang, sub_els = [SubEl]} = IQ, Type) ->
process_iq_result(#iq{from = From, to = To, id = ID, lang = Lang} = IQ,
#iq{type = result} = ResIQ) ->
try
#delegation{forwarded = #forwarded{xml_els = [SubEl]}} =
CodecOpts = ejabberd_config:codec_options(To#jid.lserver),
#delegation{forwarded = #forwarded{sub_els = [SubEl]}} =
xmpp:get_subtag(ResIQ, #delegation{}),
case xmpp:decode(SubEl, ?NS_CLIENT, [ignore_els]) of
case xmpp:decode(SubEl, ?NS_CLIENT, CodecOpts) of
#iq{from = To, to = From, type = Type, id = ID} = Reply
when Type == error; Type == result ->
ejabberd_router:route(Reply)
@@ -292,7 +293,7 @@ process_disco_info(State, Type, Host, NS, Info) ->
sub_els = [#delegation{delegated = [#delegated{ns = NS}]}]},
Delegations = dict:store({NS, Type}, {Host, Info}, State#state.delegations),
gen_iq_handler:add_iq_handler(Type, State#state.server_host, NS,
?MODULE, Type, gen_iq_handler:iqdisc(Host)),
?MODULE, Type),
ejabberd_router:route(Msg),
?INFO_MSG("Namespace '~s' is delegated to external component '~s'",
[NS, Host]),
+16 -32
View File
@@ -37,7 +37,8 @@
get_local_features/5, get_local_services/5,
process_sm_iq_items/1, process_sm_iq_info/1,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
get_info/5, transform_module_options/1, mod_opt_type/1, depends/2]).
get_info/5, transform_module_options/1, mod_opt_type/1,
mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -50,23 +51,20 @@
-type items_acc() :: {error, stanza_error()} | {result, [disco_item()]} | empty.
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_ITEMS, ?MODULE,
process_local_iq_items, IQDisc),
process_local_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_INFO, ?MODULE,
process_local_iq_info, IQDisc),
process_local_iq_info),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items,
IQDisc),
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info,
IQDisc),
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info),
catch ets:new(disco_extra_domains,
[named_table, ordered_set, public,
{heir, erlang:group_leader(), none}]),
ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
ExtraDomains = gen_mod:get_opt(extra_domains, Opts),
lists:foreach(fun (Domain) ->
register_extra_domain(Host, Domain)
end,
@@ -115,7 +113,7 @@ stop(Host) ->
ok.
reload(Host, NewOpts, OldOpts) ->
case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts, []) of
case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts) of
{false, NewDomains, OldDomains} ->
lists:foreach(
fun(Domain) ->
@@ -127,23 +125,6 @@ reload(Host, NewOpts, OldOpts) ->
end, OldDomains -- NewDomains);
true ->
ok
end,
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_ITEMS, ?MODULE,
process_local_iq_items, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_INFO, ?MODULE,
process_local_iq_info, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items,
IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info,
IQDisc);
true ->
ok
end.
-spec register_extra_domain(binary(), binary()) -> true.
@@ -197,7 +178,7 @@ process_local_iq_info(#iq{type = get, lang = Lang,
binary(), binary()) -> [identity()].
get_local_identity(Acc, _From, To, <<"">>, _Lang) ->
Host = To#jid.lserver,
Name = gen_mod:get_module_opt(Host, ?MODULE, name, ?T("ejabberd")),
Name = gen_mod:get_module_opt(Host, ?MODULE, name),
Acc ++ [#identity{category = <<"server">>,
type = <<"im">>,
name = Name}];
@@ -440,7 +421,7 @@ get_info(Acc, _, _, _Node, _) -> Acc.
-spec get_fields(binary(), module()) -> [xdata_field()].
get_fields(Host, Module) ->
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []),
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info),
Fields1 = lists:filter(fun ({Modules, _, _}) ->
case Modules of
all -> true;
@@ -457,7 +438,6 @@ depends(_Host, _Opts) ->
mod_opt_type(extra_domains) ->
fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(name) -> fun iolist_to_binary/1;
mod_opt_type(server_info) ->
fun (L) ->
@@ -468,5 +448,9 @@ mod_opt_type(server_info) ->
{Mods, Name, URLs}
end,
L)
end;
mod_opt_type(_) -> [extra_domains, iqdisc, server_info, name].
end.
mod_options(_Host) ->
[{extra_domains, []},
{server_info, []},
{name, ?T("ejabberd")}].
+8 -9
View File
@@ -36,7 +36,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, depends/2]).
mod_opt_type/1, depends/2, mod_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -63,8 +63,10 @@ depends(_Host, _Opts) ->
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(hosts) ->
fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(_) -> [host, hosts].
fun(L) -> lists:map(fun iolist_to_binary/1, L) end.
mod_options(_Host) ->
[{host, <<"echo.@HOST@">>}, {hosts, []}].
%%====================================================================
%% gen_server callbacks
@@ -79,8 +81,7 @@ mod_opt_type(_) -> [host, hosts].
%%--------------------------------------------------------------------
init([Host, Opts]) ->
process_flag(trap_exit, true),
Hosts = gen_mod:get_opt_hosts(Host, Opts,
<<"echo.@HOST@">>),
Hosts = gen_mod:get_opt_hosts(Host, Opts),
lists:foreach(
fun(H) ->
ejabberd_router:register_route(H, Host)
@@ -106,10 +107,8 @@ handle_call(stop, _From, State) ->
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({reload, Host, NewOpts, OldOpts}, State) ->
NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts,
<<"echo.@HOST@">>),
OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts,
<<"echo.@HOST@">>),
NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts),
OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts),
lists:foreach(
fun(H) ->
ejabberd_router:unregister_route(H)
+10 -11
View File
@@ -34,15 +34,13 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, depends/2]).
mod_opt_type/1, mod_options/1, depends/2]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp.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()}).
@@ -58,11 +56,9 @@ c2s_auth_result(#{ip := {Addr, _}, lserver := LServer} = State, false, _User) ->
State;
false ->
BanLifetime = gen_mod:get_module_opt(
LServer, ?MODULE, c2s_auth_ban_lifetime,
?C2S_AUTH_BAN_LIFETIME),
LServer, ?MODULE, c2s_auth_ban_lifetime),
MaxFailures = gen_mod:get_module_opt(
LServer, ?MODULE, c2s_max_auth_failures,
?C2S_MAX_AUTH_FAILURES),
LServer, ?MODULE, c2s_max_auth_failures),
UnbanTS = p1_time_compat:system_time(seconds) + BanLifetime,
Attempts = case ets:lookup(failed_auth, Addr) of
[{Addr, N, _, _}] ->
@@ -179,7 +175,7 @@ log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS)
{stop, ejabberd_c2s:send(State, Err)}.
is_whitelisted(Host, Addr) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
Access = gen_mod:get_module_opt(Host, ?MODULE, access),
acl:match_rule(Host, Access, Addr) == allow.
seconds_to_now(Secs) ->
@@ -194,6 +190,9 @@ mod_opt_type(access) ->
mod_opt_type(c2s_auth_ban_lifetime) ->
fun (T) when is_integer(T), T > 0 -> T end;
mod_opt_type(c2s_max_auth_failures) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(_) ->
[access, c2s_auth_ban_lifetime, c2s_max_auth_failures].
fun (I) when is_integer(I), I > 0 -> I end.
mod_options(_Host) ->
[{access, none},
{c2s_auth_ban_lifetime, 3600}, %% one hour
{c2s_max_auth_failures, 20}].
+7 -6
View File
@@ -43,7 +43,7 @@
%% add_commands allow exporting a class of commands, from
%% open: methods is not risky and can be called by without any access check
%% restricted (default): the same, but will appear only in ejabberdctl list.
%% admin auth is required with XMLRPC and HTTP API and checked for admin priviledges, works as usual in ejabberdctl.
%% admin auth is required with XMLRPC and HTTP API and checked for admin privileges, works as usual in ejabberdctl.
%% user - can be used through XMLRPC and HTTP API, even by user. Only admin can use the commands for other users.
%%
%% Then to perform an action, send a POST request to the following URL:
@@ -74,7 +74,8 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]).
-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2,
mod_options/1]).
-include("ejabberd.hrl").
-include("xmpp.hrl").
@@ -300,7 +301,7 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
throw:{not_allowed, Msg} ->
{401, iolist_to_binary(Msg)};
throw:{error, account_unprivileged} ->
{403, 31, <<"Command need to be run with admin priviledge.">>};
{403, 31, <<"Command need to be run with admin privilege.">>};
throw:{error, access_rules_unauthorized} ->
{403, 32, <<"AccessRules: Account does not have the right to perform the operation.">>};
throw:{invalid_parameter, Msg} ->
@@ -551,7 +552,7 @@ hide_sensitive_args(NonListArgs) ->
NonListArgs.
permission_addon() ->
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access, none),
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access),
Rules = acl:resolve_access(Access, global),
R = case Rules of
all ->
@@ -576,5 +577,5 @@ permission_addon() ->
end, {1, []}, R),
Res.
mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1;
mod_opt_type(_) -> [admin_ip_access].
mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1.
mod_options(_) -> [{admin_ip_access, none}].
+33 -52
View File
@@ -43,7 +43,7 @@
%% utility for other http modules
-export([content_type/3]).
-export([reopen_log/0, mod_opt_type/1, depends/2]).
-export([reopen_log/0, mod_opt_type/1, mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -69,9 +69,6 @@
-define(HTTP_ERR_HOST_UNKNOWN,
{-1, 410, [], <<"Host unknown">>}).
-define(DEFAULT_CONTENT_TYPE,
<<"application/octet-stream">>).
-define(DEFAULT_CONTENT_TYPES,
[{<<".css">>, <<"text/css">>},
{<<".gif">>, <<"image/gif">>},
@@ -126,24 +123,19 @@ init([Host, Opts]) ->
initialize(Host, Opts) ->
DocRoot = gen_mod:get_opt(docroot, Opts),
check_docroot_defined(DocRoot, Host),
DRInfo = check_docroot_exists(DocRoot),
check_docroot_is_dir(DRInfo, DocRoot),
check_docroot_is_readable(DRInfo, DocRoot),
AccessLog = gen_mod:get_opt(accesslog, Opts),
AccessLogFD = try_open_log(AccessLog, Host),
DirectoryIndices = gen_mod:get_opt(directory_indices, Opts, []),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []),
DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
?DEFAULT_CONTENT_TYPE),
UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts, []),
DirectoryIndices = gen_mod:get_opt(directory_indices, Opts),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
DefaultContentType = gen_mod:get_opt(default_content_type, Opts),
UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts),
UserAccess = case UserAccess0 of
[] -> none;
_ ->
dict:from_list(UserAccess0)
end,
ContentTypes = build_list_content_types(
gen_mod:get_opt(content_types, Opts, []),
gen_mod:get_opt(content_types, Opts),
?DEFAULT_CONTENT_TYPES),
?DEBUG("known content types: ~s",
[str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
@@ -173,37 +165,6 @@ build_list_content_types(AdminCTsUnsorted, DefaultCTsUnsorted) ->
|| {Extension, Value} <- CTsUnfiltered,
Value /= undefined].
check_docroot_defined(DocRoot, Host) ->
case DocRoot of
undefined -> throw({undefined_docroot_option, Host});
_ -> ok
end.
check_docroot_exists(DocRoot) ->
case filelib:ensure_dir(filename:join(DocRoot, "foo")) of
ok ->
case file:read_file_info(DocRoot) of
{error, Reason} ->
throw({error_access_docroot, DocRoot, Reason});
{ok, FI} -> FI
end;
{error, Reason} ->
throw({error_access_docroot, DocRoot, Reason})
end.
check_docroot_is_dir(DRInfo, DocRoot) ->
case DRInfo#file_info.type of
directory -> ok;
_ -> throw({docroot_not_directory, DocRoot})
end.
check_docroot_is_readable(DRInfo, DocRoot) ->
case DRInfo#file_info.access of
read -> ok;
read_write -> ok;
_ -> throw({docroot_not_readable, DocRoot})
end.
try_open_log(undefined, _Host) ->
undefined;
try_open_log(FN, _Host) ->
@@ -504,7 +465,10 @@ ip_to_string(Address) when size(Address) == 8 ->
Parts = lists:map(fun (Int) -> io_lib:format("~.16B", [Int]) end, tuple_to_list(Address)),
string:to_lower(lists:flatten(join(Parts, ":"))).
mod_opt_type(accesslog) -> fun iolist_to_binary/1;
mod_opt_type(accesslog) ->
fun(undefined) -> undefined;
(File) -> iolist_to_binary(File)
end;
mod_opt_type(content_types) ->
fun(L) when is_list(L) ->
lists:map(
@@ -519,15 +483,32 @@ mod_opt_type(default_content_type) ->
fun iolist_to_binary/1;
mod_opt_type(directory_indices) ->
fun (L) when is_list(L) -> L end;
mod_opt_type(docroot) -> fun (A) -> A end;
mod_opt_type(docroot) ->
fun(S) ->
Path = iolist_to_binary(S),
case filelib:ensure_dir(filename:join(Path, "foo")) of
ok ->
Path;
{error, Why} ->
?ERROR_MSG("Failed to create directory ~s: ~s",
[Path, file:format_error(Why)]),
erlang:error(badarg)
end
end;
mod_opt_type(must_authenticate_with) ->
fun (L) when is_list(L) ->
lists:map(fun(UP) when is_binary(UP) ->
[K, V] = binary:split(UP, <<":">>),
{K, V}
end, L)
end;
mod_opt_type(_) ->
[accesslog, content_types, custom_headers,
default_content_type, directory_indices, docroot,
must_authenticate_with].
end.
mod_options(_) ->
[{accesslog, undefined},
{content_types, []},
{default_content_type, <<"application/octet-stream">>},
{custom_headers, []},
{directory_indices, []},
{must_authenticate_with, []},
%% Required option
docroot].
+88 -126
View File
@@ -31,7 +31,7 @@
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
-define(SLOT_TIMEOUT, 18000000). % 5 hours.
-define(FORMAT(Error), file:format_error(Error)).
-define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))).
-define(URL_ENC(URL), binary_to_list(misc:url_encode(URL))).
-define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(misc:ip_to_list(IP))).
-define(STR_TO_INT(Str, B), binary_to_integer(iolist_to_binary(Str), B)).
-define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>).
@@ -43,6 +43,7 @@
{<<".gz">>, <<"application/x-gzip">>},
{<<".jpeg">>, <<"image/jpeg">>},
{<<".jpg">>, <<"image/jpeg">>},
{<<".m4a">>, <<"audio/mp4">>},
{<<".mp3">>, <<"audio/mpeg">>},
{<<".mp4">>, <<"video/mp4">>},
{<<".mpeg">>, <<"video/mpeg">>},
@@ -66,7 +67,8 @@
-export([start/2,
stop/1,
depends/2,
mod_opt_type/1]).
mod_opt_type/1,
mod_options/1]).
%% gen_server callbacks.
-export([init/1,
@@ -124,9 +126,8 @@
%% gen_mod/supervisor callbacks.
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> {ok, pid()}.
start(ServerHost, Opts) ->
case gen_mod:get_opt(rm_on_unregister, Opts, true) of
case gen_mod:get_opt(rm_on_unregister, Opts) of
true ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -137,9 +138,8 @@ start(ServerHost, Opts) ->
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc).
-spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister, true) of
case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister) of
true ->
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -150,7 +150,6 @@ stop(ServerHost) ->
gen_mod:stop_child(Proc).
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
mod_opt_type(host) ->
fun iolist_to_binary/1;
mod_opt_type(hosts) ->
@@ -170,9 +169,13 @@ mod_opt_type(jid_in_url) ->
(node) -> node
end;
mod_opt_type(file_mode) ->
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
fun(undefined) -> undefined;
(Mode) -> ?STR_TO_INT(Mode, 8)
end;
mod_opt_type(dir_mode) ->
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
fun(undefined) -> undefined;
(Mode) -> ?STR_TO_INT(Mode, 8)
end;
mod_opt_type(docroot) ->
fun iolist_to_binary/1;
mod_opt_type(put_url) ->
@@ -181,11 +184,13 @@ mod_opt_type(put_url) ->
end;
mod_opt_type(get_url) ->
fun(<<"http://", _/binary>> = URL) -> URL;
(<<"https://", _/binary>> = URL) -> URL
(<<"https://", _/binary>> = URL) -> URL;
(undefined) -> undefined
end;
mod_opt_type(service_url) ->
fun(<<"http://", _/binary>> = URL) -> URL;
(<<"https://", _/binary>> = URL) -> URL
(<<"https://", _/binary>> = URL) -> URL;
(undefined) -> undefined
end;
mod_opt_type(custom_headers) ->
fun(Headers) ->
@@ -196,39 +201,66 @@ mod_opt_type(custom_headers) ->
mod_opt_type(rm_on_unregister) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(thumbnail) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[host, hosts, name, access, max_size, secret_length, jid_in_url, file_mode,
dir_mode, docroot, put_url, get_url, service_url, custom_headers,
rm_on_unregister, thumbnail].
fun(true) ->
case eimp:supported_formats() of
[] ->
?WARNING_MSG("ejabberd is built without image converter "
"support, option '~s' is ignored",
[thumbnail]),
erlang:error(badarg);
_ ->
true
end;
(false) ->
false
end.
-spec mod_options(binary()) -> [{atom(), any()}].
mod_options(_Host) ->
[{host, <<"upload.@HOST@">>},
{hosts, []},
{name, ?T("HTTP File Upload")},
{access, local},
{max_size, 104857600},
{secret_length, 40},
{jid_in_url, sha1},
{file_mode, undefined},
{dir_mode, undefined},
{docroot, <<"@HOME@/upload">>},
{put_url, <<"http://@HOST@:5444">>},
{get_url, undefined},
{service_url, undefined},
{custom_headers, []},
{rm_on_unregister, true},
{thumbnail, true}].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
%%--------------------------------------------------------------------
%% gen_server callbacks.
%%--------------------------------------------------------------------
-spec init(list()) -> {ok, state()}.
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"upload.@HOST@">>),
Name = gen_mod:get_opt(name, Opts, ?T("HTTP File Upload")),
Access = gen_mod:get_opt(access, Opts, local),
MaxSize = gen_mod:get_opt(max_size, Opts, 104857600),
SecretLength = gen_mod:get_opt(secret_length, Opts, 40),
JIDinURL = gen_mod:get_opt(jid_in_url, Opts, sha1),
DocRoot = gen_mod:get_opt(docroot, Opts, <<"@HOME@/upload">>),
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
Name = gen_mod:get_opt(name, Opts),
Access = gen_mod:get_opt(access, Opts),
MaxSize = gen_mod:get_opt(max_size, Opts),
SecretLength = gen_mod:get_opt(secret_length, Opts),
JIDinURL = gen_mod:get_opt(jid_in_url, Opts),
DocRoot = gen_mod:get_opt(docroot, Opts),
FileMode = gen_mod:get_opt(file_mode, Opts),
DirMode = gen_mod:get_opt(dir_mode, Opts),
PutURL = gen_mod:get_opt(put_url, Opts, <<"http://@HOST@:5444">>),
GetURL = gen_mod:get_opt(get_url, Opts, PutURL),
PutURL = gen_mod:get_opt(put_url, Opts),
GetURL = case gen_mod:get_opt(get_url, Opts) of
undefined -> PutURL;
URL -> URL
end,
ServiceURL = gen_mod:get_opt(service_url, Opts),
Thumbnail = gen_mod:get_opt(thumbnail, Opts, true),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []),
Thumbnail = gen_mod:get_opt(thumbnail, Opts),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
DocRoot2 = expand_host(DocRoot1, ServerHost),
case DirMode of
@@ -237,20 +269,6 @@ init([ServerHost, Opts]) ->
Mode ->
file:change_mode(DocRoot2, Mode)
end,
case Thumbnail of
true ->
case misc:have_eimp() of
false ->
?ERROR_MSG("ejabberd is built without graphics support, "
"please rebuild it with --enable-graphics or "
"set 'thumbnail: false' for module '~s' in "
"ejabberd.yml", [?MODULE]);
_ ->
ok
end;
false ->
ok
end,
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, ServerHost)
@@ -271,7 +289,6 @@ init([ServerHost, Opts]) ->
pos_integer() | undefined,
pos_integer() | undefined}, state()} |
{reply, {error, atom()}, state()} | {noreply, state()}.
handle_call({use_slot, Slot, Size}, _From,
#state{file_mode = FileMode,
dir_mode = DirMode,
@@ -301,13 +318,11 @@ handle_call(Request, From, State) ->
{noreply, State}.
-spec handle_cast(_, state()) -> {noreply, state()}.
handle_cast(Request, State) ->
?ERROR_MSG("Got unexpected request: ~p", [Request]),
{noreply, State}.
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
handle_info({route, #iq{lang = Lang} = Packet}, State) ->
try xmpp:decode_els(Packet) of
IQ ->
@@ -339,13 +354,11 @@ handle_info(Info, State) ->
{noreply, State}.
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
?DEBUG("Updating HTTP upload process for ~s", [ServerHost]),
{ok, State}.
@@ -353,10 +366,8 @@ code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
%%--------------------------------------------------------------------
%% ejabberd_http callback.
%%--------------------------------------------------------------------
-spec process([binary()], #request{})
-> {pos_integer(), [{binary(), binary()}], binary()}.
process(LocalPath, #request{method = Method, host = Host, ip = IP})
when length(LocalPath) < 3,
Method == 'PUT' orelse
@@ -461,25 +472,20 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
%%--------------------------------------------------------------------
%% Exported utility functions.
%%--------------------------------------------------------------------
-spec get_proc_name(binary(), atom()) -> atom().
get_proc_name(ServerHost, ModuleName) ->
PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url,
<<"http://@HOST@">>),
PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url),
{ok, {_Scheme, _UserInfo, Host, _Port, Path, _Query}} =
http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),
ProcPrefix = list_to_binary(string:strip(Host ++ Path, right, $/)),
gen_mod:get_module_proc(ProcPrefix, ModuleName).
-spec expand_home(binary()) -> binary().
expand_home(Input) ->
{ok, [[Home]]} = init:get_argument(home),
misc:expand_keyword(<<"@HOME@">>, Input, Home).
-spec expand_host(binary(), binary()) -> binary().
expand_host(Input, Host) ->
misc:expand_keyword(<<"@HOST@">>, Input, Host).
@@ -490,7 +496,6 @@ expand_host(Input, Host) ->
%% XMPP request handling.
-spec process_iq(iq(), state()) -> {iq(), state()} | iq() | not_request.
process_iq(#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ,
#state{server_host = ServerHost, name = Name}) ->
AddInfo = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
@@ -518,7 +523,6 @@ process_iq(#iq{}, _State) ->
-spec process_slot_request(iq(), binary(), pos_integer(), binary(), binary(),
state()) -> {iq(), state()} | iq().
process_slot_request(#iq{lang = Lang, from = From} = IQ,
File, Size, CType, XMLNS,
#state{server_host = ServerHost,
@@ -549,7 +553,6 @@ process_slot_request(#iq{lang = Lang, from = From} = IQ,
-spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary())
-> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}.
create_slot(#state{service_url = undefined, max_size = MaxSize},
JID, File, Size, _ContentType, Lang) when MaxSize /= infinity,
Size > MaxSize ->
@@ -568,7 +571,7 @@ create_slot(#state{service_url = undefined,
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
[JID, UserDir, Size, Lang]) of
allow ->
RandStr = make_rand_string(SecretLength),
RandStr = randoms:get_alphanum_string(SecretLength),
FileStr = make_file_string(File),
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
[jid:encode(JID), File]),
@@ -626,76 +629,56 @@ create_slot(#state{service_url = ServiceURL},
end.
-spec add_slot(slot(), pos_integer(), timer:tref(), state()) -> state().
add_slot(Slot, Size, Timer, #state{slots = Slots} = State) ->
NewSlots = maps:put(Slot, {Size, Timer}, Slots),
State#state{slots = NewSlots}.
-spec get_slot(slot(), state()) -> {ok, {pos_integer(), timer:tref()}} | error.
get_slot(Slot, #state{slots = Slots}) ->
maps:find(Slot, Slots).
-spec del_slot(slot(), state()) -> state().
del_slot(Slot, #state{slots = Slots} = State) ->
NewSlots = maps:remove(Slot, Slots),
State#state{slots = NewSlots}.
-spec mk_slot(slot(), state(), binary()) -> upload_slot();
(binary(), binary(), binary()) -> upload_slot().
mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
PutURL = str:join([PutPrefix | Slot], <<$/>>),
GetURL = str:join([GetPrefix | Slot], <<$/>>),
mk_slot(PutURL, GetURL, XMLNS);
mk_slot(PutURL, GetURL, ?NS_HTTP_UPLOAD_0) ->
#upload_slot_0{get = GetURL, put = PutURL, xmlns = ?NS_HTTP_UPLOAD_0};
#upload_slot_0{get = misc:url_encode(GetURL),
put = misc:url_encode(PutURL),
xmlns = ?NS_HTTP_UPLOAD_0};
mk_slot(PutURL, GetURL, XMLNS) ->
#upload_slot{get = GetURL, put = PutURL, xmlns = XMLNS}.
#upload_slot{get = misc:url_encode(GetURL),
put = misc:url_encode(PutURL),
xmlns = XMLNS}.
-spec make_user_string(jid(), sha1 | node) -> binary().
make_user_string(#jid{luser = U, lserver = S}, sha1) ->
str:sha(<<U/binary, $@, S/binary>>);
make_user_string(#jid{luser = U}, node) ->
re:replace(U, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
replace_special_chars(U).
-spec make_file_string(binary()) -> binary().
make_file_string(File) ->
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
replace_special_chars(File).
-spec make_rand_string(non_neg_integer()) -> binary().
make_rand_string(Length) ->
list_to_binary(make_rand_string([], Length)).
-spec make_rand_string(string(), non_neg_integer()) -> string().
make_rand_string(S, 0) -> S;
make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1).
-spec make_rand_char() -> char().
make_rand_char() ->
map_int_to_char(randoms:uniform(0, 61)).
-spec map_int_to_char(0..61) -> char().
map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
-spec replace_special_chars(binary()) -> binary().
replace_special_chars(S) ->
re:replace(S, <<"[^\\p{Xan}_.-]">>, <<$_>>,
[unicode, global, {return, binary}]).
-spec yield_content_type(binary()) -> binary().
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
yield_content_type(Type) -> Type.
-spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> disco_info().
iq_disco_info(Host, Lang, Name, AddInfo) ->
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size, 104857600) of
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size) of
infinity ->
AddInfo;
MaxSize ->
@@ -726,7 +709,6 @@ iq_disco_info(Host, Lang, Name, AddInfo) ->
%% HTTP request handling.
-spec parse_http_request(#request{}) -> {atom(), slot()}.
parse_http_request(#request{host = Host, path = Path}) ->
PrefixLength = length(Path) - 3,
{ProcURL, Slot} = if PrefixLength > 0 ->
@@ -743,7 +725,6 @@ parse_http_request(#request{host = Host, path = Path}) ->
integer() | undefined,
binary(), slot(), boolean())
-> ok | {ok, [{binary(), binary()}], binary()} | {error, term()}.
store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
case do_store_file(Path, Data, FileMode, DirMode) of
ok when Thumbnail ->
@@ -776,7 +757,6 @@ store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
integer() | undefined,
integer() | undefined)
-> ok | {error, term()}.
do_store_file(Path, Data, FileMode, DirMode) ->
try
ok = filelib:ensure_dir(Path),
@@ -805,7 +785,6 @@ do_store_file(Path, Data, FileMode, DirMode) ->
end.
-spec guess_content_type(binary()) -> binary().
guess_content_type(FileName) ->
mod_http_fileserver:content_type(FileName,
?DEFAULT_CONTENT_TYPE,
@@ -813,20 +792,17 @@ guess_content_type(FileName) ->
-spec http_response(100..599)
-> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code) ->
http_response(Code, []).
-spec http_response(100..599, [{binary(), binary()}])
-> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code, ExtraHeaders) ->
Message = <<(code_to_message(Code))/binary, $\n>>,
http_response(Code, ExtraHeaders, Message).
-spec http_response(100..599, [{binary(), binary()}], binary())
-> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code, ExtraHeaders, Body) ->
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
true ->
@@ -837,7 +813,6 @@ http_response(Code, ExtraHeaders, Body) ->
{Code, Headers, Body}.
-spec code_to_message(100..599) -> binary().
code_to_message(201) -> <<"Upload successful.">>;
code_to_message(403) -> <<"Forbidden.">>;
code_to_message(404) -> <<"Not found.">>;
@@ -849,29 +824,21 @@ code_to_message(_Code) -> <<"">>.
%%--------------------------------------------------------------------
%% Image manipulation stuff.
%%--------------------------------------------------------------------
-spec identify(binary(), binary()) -> {ok, media_info()} | pass.
identify(Path, Data) ->
case misc:have_eimp() of
true ->
case eimp:identify(Data) of
{ok, Info} ->
{ok, #media_info{
type = proplists:get_value(type, Info),
width = proplists:get_value(width, Info),
height = proplists:get_value(height, Info)}};
{error, Why} ->
?DEBUG("Cannot identify type of ~s: ~s",
[Path, eimp:format_error(Why)]),
pass
end;
false ->
case eimp:identify(Data) of
{ok, Info} ->
{ok, #media_info{
type = proplists:get_value(type, Info),
width = proplists:get_value(width, Info),
height = proplists:get_value(height, Info)}};
{error, Why} ->
?DEBUG("Cannot identify type of ~s: ~s",
[Path, eimp:format_error(Why)]),
pass
end.
-spec convert(binary(), binary(), media_info()) -> {ok, binary(), media_info()} | pass.
convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) ->
if W * H >= 25000000 ->
?DEBUG("The image ~s is more than 25 Mpix", [Path]),
@@ -906,7 +873,6 @@ convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) ->
end.
-spec thumb_el(media_info(), binary()) -> xmlel().
thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
MimeType = <<"image/", (atom_to_binary(T, latin1))/binary>>,
Thumb = #thumbnail{'media-type' = MimeType, uri = URI,
@@ -916,14 +882,11 @@ thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
%%--------------------------------------------------------------------
%% Remove user.
%%--------------------------------------------------------------------
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
ServerHost = jid:nameprep(Server),
DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot,
<<"@HOME@/upload">>),
JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url, sha1),
DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot),
JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url),
DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
UserStr = make_user_string(jid:make(User, Server), JIDinURL),
UserDir = str:join([DocRoot1, UserStr], <<$/>>),
@@ -939,7 +902,6 @@ remove_user(User, Server) ->
ok.
-spec del_tree(file:filename_all()) -> ok | {error, term()}.
del_tree(Dir) when is_binary(Dir) ->
del_tree(binary_to_list(Dir));
del_tree(Dir) ->
+14 -29
View File
@@ -37,7 +37,8 @@
-export([start/2,
stop/1,
depends/2,
mod_opt_type/1]).
mod_opt_type/1,
mod_options/1]).
%% gen_server callbacks.
-export([init/1,
@@ -69,19 +70,16 @@
%% gen_mod/supervisor callbacks.
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> {ok, pid()}.
start(ServerHost, Opts) ->
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc).
-spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
gen_mod:stop_child(Proc).
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
mod_opt_type(access_soft_quota) ->
fun acl:shaper_rules_validator/1;
mod_opt_type(access_hard_quota) ->
@@ -89,28 +87,28 @@ mod_opt_type(access_hard_quota) ->
mod_opt_type(max_days) ->
fun(I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end;
mod_opt_type(_) ->
[access_soft_quota, access_hard_quota, max_days].
end.
-spec mod_options(binary()) -> [{atom(), any()}].
mod_options(_) ->
[{access_soft_quota, soft_upload_quota},
{access_hard_quota, hard_upload_quota},
{max_days, infinity}].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[{mod_http_upload, hard}].
%%--------------------------------------------------------------------
%% gen_server callbacks.
%%--------------------------------------------------------------------
-spec init(list()) -> {ok, state()}.
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts,
soft_upload_quota),
AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts,
hard_upload_quota),
MaxDays = gen_mod:get_opt(max_days, Opts, infinity),
DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot,
<<"@HOME@/upload">>),
AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts),
AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts),
MaxDays = gen_mod:get_opt(max_days, Opts),
DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot),
DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)),
DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost),
Timers = if MaxDays == infinity -> [];
@@ -129,13 +127,11 @@ init([ServerHost, Opts]) ->
timers = Timers}}.
-spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}.
handle_call(Request, From, State) ->
?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
{noreply, State}.
-spec handle_cast(_, state()) -> {noreply, state()}.
handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size},
#state{server_host = ServerHost,
access_soft_quota = AccessSoftQuota,
@@ -193,7 +189,6 @@ handle_cast(Request, State) ->
{noreply, State}.
-spec handle_info(_, state()) -> {noreply, state()}.
handle_info(sweep, #state{server_host = ServerHost,
docroot = DocRoot,
max_days = MaxDays} = State)
@@ -220,7 +215,6 @@ handle_info(Info, State) ->
{noreply, State}.
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
?DEBUG("Stopping upload quota process for ~s: ~p", [ServerHost, Reason]),
ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE,
@@ -228,7 +222,6 @@ terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
lists:foreach(fun timer:cancel/1, Timers).
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
?DEBUG("Updating upload quota process for ~s", [ServerHost]),
{ok, State}.
@@ -236,10 +229,8 @@ code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
%%--------------------------------------------------------------------
%% ejabberd_hooks callback.
%%--------------------------------------------------------------------
-spec handle_slot_request(allow | deny, jid(), binary(),
non_neg_integer(), binary()) -> allow | deny.
handle_slot_request(allow, #jid{lserver = ServerHost} = JID, Path, Size,
_Lang) ->
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
@@ -250,12 +241,10 @@ handle_slot_request(Acc, _JID, _Path, _Size, _Lang) -> Acc.
%%--------------------------------------------------------------------
%% Internal functions.
%%--------------------------------------------------------------------
-spec enforce_quota(file:filename_all(), non_neg_integer(),
non_neg_integer() | undefined, non_neg_integer(),
non_neg_integer())
-> non_neg_integer().
enforce_quota(_UserDir, SlotSize, OldSize, _MinSize, MaxSize)
when is_integer(OldSize), OldSize + SlotSize =< MaxSize ->
OldSize + SlotSize;
@@ -281,7 +270,6 @@ enforce_quota(UserDir, SlotSize, _OldSize, MinSize, MaxSize) ->
end.
-spec delete_old_files(file:filename_all(), integer()) -> ok.
delete_old_files(UserDir, CutOff) ->
FileInfo = gather_file_info(UserDir),
case [Path || {Path, _Size, Time} <- FileInfo, Time < CutOff] of
@@ -294,7 +282,6 @@ delete_old_files(UserDir, CutOff) ->
-spec gather_file_info(file:filename_all())
-> [{binary(), non_neg_integer(), non_neg_integer()}].
gather_file_info(Dir) when is_binary(Dir) ->
gather_file_info(binary_to_list(Dir));
gather_file_info(Dir) ->
@@ -329,7 +316,6 @@ gather_file_info(Dir) ->
end.
-spec del_file_and_dir(file:name_all()) -> ok.
del_file_and_dir(File) ->
case file:delete(File) of
ok ->
@@ -346,7 +332,6 @@ del_file_and_dir(File) ->
end.
-spec secs_since_epoch() -> non_neg_integer().
secs_since_epoch() ->
{MegaSecs, Secs, _MicroSecs} = os:timestamp(),
MegaSecs * 1000000 + Secs.
+33 -39
View File
@@ -39,7 +39,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, depends/2]).
mod_opt_type/1, mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -47,14 +47,8 @@
-include("mod_irc.hrl").
-include("translate.hrl").
-define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
-define(DEFAULT_IRC_PORT, 6667).
-define(DEFAULT_REALNAME, <<"WebIRC-User">>).
-define(DEFAULT_WEBIRC_PASSWORD, <<"">>).
-define(POSSIBLE_ENCODINGS,
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
<<"utf-8">>, <<"utf-8+latin-1">>]).
@@ -100,17 +94,16 @@ depends(_Host, _Opts) ->
init([Host, Opts]) ->
process_flag(trap_exit, true),
ejabberd:start_app(iconv),
MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"irc.@HOST@">>),
MyHosts = gen_mod:get_opt_hosts(Host, Opts),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
Access = gen_mod:get_opt(access, Opts, all),
Access = gen_mod:get_opt(access, Opts),
catch ets:new(irc_connection,
[named_table, public,
{keypos, #irc_connection.jid_server_host}]),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
lists:foreach(
fun(MyHost) ->
register_hooks(MyHost, IQDisc),
register_hooks(MyHost),
ejabberd_router:register_route(MyHost, Host)
end, MyHosts),
{ok,
@@ -136,37 +129,27 @@ handle_call(stop, _From, State) ->
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) ->
NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"irc.@HOST@">>),
OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"irc.@HOST@">>),
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts),
OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts),
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
Access = gen_mod:get_opt(access, NewOpts, all),
Access = gen_mod:get_opt(access, NewOpts),
if NewMod /= OldMod ->
NewMod:init(ServerHost, NewOpts);
true ->
ok
end,
if (NewIQDisc /= OldIQDisc) ->
lists:foreach(
fun(NewHost) ->
register_hooks(NewHost, NewIQDisc)
end, NewHosts -- (NewHosts -- OldHosts));
true ->
ok
end,
lists:foreach(
fun(NewHost) ->
ejabberd_router:register_route(NewHost, ServerHost),
register_hooks(NewHost, NewIQDisc)
register_hooks(NewHost)
end, NewHosts -- OldHosts),
lists:foreach(
fun(OldHost) ->
ejabberd_router:unregister_route(OldHost),
unregister_hooks(OldHost)
end, OldHosts -- NewHosts),
Access = gen_mod:get_opt(access, NewOpts, all),
Access = gen_mod:get_opt(access, NewOpts),
{noreply, State#state{hosts = NewHosts, access = Access}};
handle_cast(Msg, State) ->
?WARNING_MSG("unexpected cast: ~p", [Msg]),
@@ -213,17 +196,17 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
register_hooks(Host, IQDisc) ->
register_hooks(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
?MODULE, process_disco_info, IQDisc),
?MODULE, process_disco_info),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
?MODULE, process_disco_items, IQDisc),
?MODULE, process_disco_items),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER,
?MODULE, process_register, IQDisc),
?MODULE, process_register),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
?MODULE, process_vcard, IQDisc),
?MODULE, process_vcard),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
?MODULE, process_command, IQDisc).
?MODULE, process_command).
unregister_hooks(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
@@ -434,7 +417,7 @@ closed_connection(Host, From, Server) ->
ets:delete(irc_connection, {From, Server, Host}).
iq_disco(ServerHost, <<"">>, Lang) ->
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("IRC Transport")),
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
#disco_info{
identities = [#identity{category = <<"conference">>,
type = <<"irc">>,
@@ -595,18 +578,17 @@ get_connection_params(Host, From, IRCServer) ->
IRCServer).
get_default_encoding(ServerHost) ->
Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding,
?DEFAULT_IRC_ENCODING),
Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding),
?INFO_MSG("The default_encoding configured for "
"host ~p is: ~p~n",
[ServerHost, Result]),
Result.
get_realname(ServerHost) ->
gen_mod:get_module_opt(ServerHost, ?MODULE, realname, ?DEFAULT_REALNAME).
gen_mod:get_module_opt(ServerHost, ?MODULE, realname).
get_webirc_password(ServerHost) ->
gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password, ?DEFAULT_WEBIRC_PASSWORD).
gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password).
get_connection_params(Host, ServerHost, From,
IRCServer) ->
@@ -993,8 +975,20 @@ mod_opt_type(name) ->
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(hosts) ->
fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(_) ->
[access, db_type, default_encoding, host, hosts, name].
mod_opt_type(realname) ->
fun iolist_to_binary/1;
mod_opt_type(webirc_password) ->
fun iolist_to_binary/1.
mod_options(Host) ->
[{access, all},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{default_encoding, <<"iso8859-15">>},
{host, <<"irc.@HOST@">>},
{hosts, []},
{realname, <<"WebIRC-User">>},
{webirc_password, <<"">>},
{name, ?T("IRC Transport")}].
-spec extract_ident(stanza()) -> binary().
extract_ident(Packet) ->
+27 -40
View File
@@ -34,7 +34,7 @@
-export([start/2, stop/1, reload/3, process_local_iq/1, export/1,
process_sm_iq/1, on_presence_update/4, import_info/0,
import/5, import_start/2, store_last_info/4, get_last_info/2,
remove_user/2, mod_opt_type/1,
remove_user/2, mod_opt_type/1, mod_options/1,
register_user/2, depends/2, privacy_check_packet/4]).
-include("ejabberd.hrl").
@@ -59,14 +59,13 @@
-optional_callbacks([use_cache/1, cache_nodes/1]).
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
?NS_LAST, ?MODULE, process_local_iq),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_LAST, ?MODULE, process_sm_iq, IQDisc),
?NS_LAST, ?MODULE, process_sm_iq),
ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE,
privacy_check_packet, 30),
ejabberd_hooks:add(register_user, Host, ?MODULE,
@@ -98,16 +97,7 @@ reload(Host, NewOpts, OldOpts) ->
true ->
ok
end,
init_cache(NewMod, Host, NewOpts),
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
?MODULE, process_sm_iq, IQDisc);
true ->
ok
end.
init_cache(NewMod, Host, NewOpts).
%%%
%%% Uptime of ejabberd node
@@ -142,9 +132,9 @@ process_sm_iq(#iq{type = set, lang = Lang} = IQ) ->
process_sm_iq(#iq{from = From, to = To, lang = Lang} = IQ) ->
User = To#jid.luser,
Server = To#jid.lserver,
{Subscription, _Groups} =
{Subscription, _Ask, _Groups} =
ejabberd_hooks:run_fold(roster_get_jid_info, Server,
{none, []}, [User, Server, From]),
{none, none, []}, [User, Server, From]),
if (Subscription == both) or (Subscription == from) or
(From#jid.luser == To#jid.luser) and
(From#jid.lserver == To#jid.lserver) ->
@@ -166,9 +156,9 @@ privacy_check_packet(allow, C2SState,
case xmpp:has_subtag(IQ, #last{}) of
true ->
#jid{luser = LUser, lserver = LServer} = To,
{Sub, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, LServer,
{none, []}, [LUser, LServer, From]),
{Sub, _, _} = ejabberd_hooks:run_fold(
roster_get_jid_info, LServer,
{none, none, []}, [LUser, LServer, From]),
if Sub == from; Sub == both ->
Pres = #presence{from = To, to = From},
case ejabberd_hooks:run_fold(
@@ -198,7 +188,9 @@ get_last(LUser, LServer) ->
?LAST_CACHE, {LUser, LServer},
fun() -> Mod:get_last(LUser, LServer) end);
false ->
Mod:get_last(LUser, LServer)
Mod:get_last(LUser, LServer);
undefined ->
error
end,
case Res of
{ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status};
@@ -275,23 +267,17 @@ remove_user(User, Server) ->
init_cache(Mod, Host, Opts) ->
case use_cache(Mod, Host) of
true ->
CacheOpts = cache_opts(Host, Opts),
CacheOpts = cache_opts(Opts),
ets_cache:new(?LAST_CACHE, CacheOpts);
false ->
ets_cache:delete(?LAST_CACHE)
end.
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(
cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(
cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(
cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
cache_opts(Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts),
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -301,10 +287,7 @@ cache_opts(Host, Opts) ->
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(Host);
false ->
gen_mod:get_module_opt(
Host, ?MODULE, use_cache,
ejabberd_config:use_cache(Host))
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -340,12 +323,16 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
fun (I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed].
fun (B) when is_boolean(B) -> B end.
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
+1 -3
View File
@@ -45,9 +45,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(last_activity, storage_type) of
disc_only_copies ->
gen_mod:get_module_opt(
Host, mod_last, use_cache,
ejabberd_config:use_cache(Host));
gen_mod:get_module_opt(Host, mod_last, use_cache);
_ ->
false
end.
+2 -2
View File
@@ -25,7 +25,7 @@
-protocol({xep, 78, '2.5'}).
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]).
-export([start/2, stop/1, reload/3, depends/2, mod_options/1]).
%% hooks
-export([c2s_unauthenticated_packet/2, c2s_stream_features/2]).
@@ -54,7 +54,7 @@ reload(_Host, _NewOpts, _OldOpts) ->
depends(_Host, _Opts) ->
[].
mod_opt_type(_) ->
mod_options(_) ->
[].
-spec c2s_unauthenticated_packet(c2s_state(), iq()) ->
+149 -71
View File
@@ -39,7 +39,8 @@
disco_sm_features/5, remove_user/2, remove_room/3, mod_opt_type/1,
muc_process_iq/2, muc_filter_message/3, message_is_archived/3,
delete_old_messages/2, get_commands_spec/0, msg_to_el/4,
get_room_config/4, set_room_option/3, offline_message/1, export/1]).
get_room_config/4, set_room_option/3, offline_message/1, export/1,
mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2]).
-include("xmpp.hrl").
-include("logger.hrl").
@@ -66,19 +67,20 @@
-callback select(binary(), jid(), jid(), mam_query:result(),
#rsm_set{} | undefined, chat | groupchat) ->
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
-callback use_cache(binary(), gen_mod:opts()) -> boolean().
-callback use_cache(binary()) -> boolean().
-callback cache_nodes(binary()) -> [node()].
-callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}.
-optional_callbacks([use_cache/2]).
-optional_callbacks([use_cache/1, cache_nodes/1]).
%%%===================================================================
%%% API
%%%===================================================================
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Host, Opts),
register_iq_handlers(Host, IQDisc),
init_cache(Mod, Host, Opts),
register_iq_handlers(Host),
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
sm_receive_packet, 50),
ejabberd_hooks:add(user_receive_packet, Host, ?MODULE,
@@ -103,7 +105,7 @@ start(Host, Opts) ->
get_room_config, 50),
ejabberd_hooks:add(set_room_option, Host, ?MODULE,
set_room_option, 50),
case gen_mod:get_opt(assume_mam_usage, Opts, false) of
case gen_mod:get_opt(assume_mam_usage, Opts) of
true ->
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
@@ -113,30 +115,30 @@ start(Host, Opts) ->
ejabberd_commands:register_commands(get_commands_spec()),
ok.
use_cache(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 2) of
true -> Mod:use_cache(Host, Opts);
false ->
gen_mod:get_opt(use_cache, Opts,
ejabberd_config:use_cache(Host))
true -> Mod:use_cache(Host);
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
end.
init_cache(Host, Opts) ->
case use_cache(Host, Opts) of
cache_nodes(Mod, Host) ->
case erlang:function_exported(Mod, cache_nodes, 1) of
true -> Mod:cache_nodes(Host);
false -> ejabberd_cluster:get_nodes()
end.
init_cache(Mod, Host, Opts) ->
case use_cache(Mod, Host) of
true ->
ets_cache:new(archive_prefs_cache, cache_opts(Host, Opts));
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
false ->
ok
ets_cache:delete(archive_prefs_cache)
end.
cache_opts(Host, Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts,
ejabberd_config:cache_size(Host)),
CacheMissed = gen_mod:get_opt(cache_missed, Opts,
ejabberd_config:cache_missed(Host)),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts,
ejabberd_config:cache_life_time(Host)) of
cache_opts(Opts) ->
MaxSize = gen_mod:get_opt(cache_size, Opts),
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -168,7 +170,7 @@ stop(Host) ->
get_room_config, 50),
ejabberd_hooks:delete(set_room_option, Host, ?MODULE,
set_room_option, 50),
case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage, false) of
case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage) of
true ->
ejabberd_hooks:delete(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
@@ -190,14 +192,8 @@ reload(Host, NewOpts, OldOpts) ->
true ->
ok
end,
ets_cache:setopts(archive_prefs_cache, cache_opts(Host, NewOpts)),
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
register_iq_handlers(Host, IQDisc);
true ->
ok
end,
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts, false) of
init_cache(NewMod, Host, NewOpts),
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
{false, true, _} ->
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
@@ -211,24 +207,24 @@ reload(Host, NewOpts, OldOpts) ->
depends(_Host, _Opts) ->
[].
-spec register_iq_handlers(binary(), gen_iq_handler:type()) -> ok.
register_iq_handlers(Host, IQDisc) ->
-spec register_iq_handlers(binary()) -> ok.
register_iq_handlers(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP,
?MODULE, process_iq_v0_2, IQDisc),
?MODULE, process_iq_v0_2),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP,
?MODULE, process_iq_v0_2, IQDisc),
?MODULE, process_iq_v0_2),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_0,
?MODULE, process_iq_v0_3, IQDisc),
?MODULE, process_iq_v0_3),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_0, ?MODULE,
process_iq_v0_3, IQDisc),
process_iq_v0_3),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_1,
?MODULE, process_iq_v0_3, IQDisc),
?MODULE, process_iq_v0_3),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_1,
?MODULE, process_iq_v0_3, IQDisc),
?MODULE, process_iq_v0_3),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_2,
?MODULE, process_iq_v0_3, IQDisc),
?MODULE, process_iq_v0_3),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_2,
?MODULE, process_iq_v0_3, IQDisc).
?MODULE, process_iq_v0_3).
-spec unregister_iq_handlers(binary()) -> ok.
unregister_iq_handlers(Host) ->
@@ -247,8 +243,13 @@ remove_user(User, Server) ->
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_user(LUser, LServer),
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
ejabberd_cluster:get_nodes()).
case use_cache(Mod, LServer) of
true ->
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
cache_nodes(Mod, LServer));
false ->
ok
end.
-spec remove_room(binary(), binary(), binary()) -> ok.
remove_room(LServer, Name, Host) ->
@@ -258,6 +259,41 @@ remove_room(LServer, Name, Host) ->
Mod:remove_room(LServer, LName, LHost),
ok.
-spec remove_mam_for_user(binary(), binary()) ->
{ok, binary()} | {error, binary()}.
remove_mam_for_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:remove_from_archive(LUser, LServer, none) of
ok ->
{ok, <<"MAM archive removed">>};
{error, Bin} when is_binary(Bin) ->
{error, Bin};
{error, _} ->
{error, <<"Db returned error">>}
end.
-spec remove_mam_for_user_with_peer(binary(), binary(), binary()) ->
{ok, binary()} | {error, binary()}.
remove_mam_for_user_with_peer(User, Server, Peer) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
try jid:decode(Peer) of
Jid ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:remove_from_archive(LUser, LServer, Jid) of
ok ->
{ok, <<"MAM archive removed">>};
{error, Bin} when is_binary(Bin) ->
{error, Bin};
{error, _} ->
{error, <<"Db returned error">>}
end
catch _:_ ->
{error, <<"Invalid peer JID">>}
end.
-spec get_room_config([muc_roomconfig:property()], mod_muc_room:state(),
jid(), binary()) -> [muc_roomconfig:property()].
get_room_config(Fields, RoomState, _From, _Lang) ->
@@ -463,7 +499,7 @@ disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
message_is_archived(true, _C2SState, _Pkt) ->
true;
message_is_archived(false, #{lserver := LServer}, Pkt) ->
case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage, false) of
case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage) of
true ->
is_archived(Pkt, LServer);
false ->
@@ -480,7 +516,7 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
DBTypes = lists:usort(
lists:map(
fun(Host) ->
case gen_mod:db_type(Host, ?MODULE) of
case gen_mod:get_module_opt(Host, ?MODULE, db_type) of
sql -> {sql, Host};
Other -> {Other, global}
end
@@ -676,10 +712,10 @@ should_archive_peer(LUser, LServer,
always -> true;
never -> false;
roster ->
{Sub, _} = ejabberd_hooks:run_fold(
roster_get_jid_info,
LServer, {none, []},
[LUser, LServer, Peer]),
{Sub, _, _} = ejabberd_hooks:run_fold(
roster_get_jid_info,
LServer, {none, none, []},
[LUser, LServer, Peer]),
Sub == both orelse Sub == from orelse Sub == to
end
end
@@ -803,29 +839,39 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
Mod = gen_mod:db_mod(Host, ?MODULE),
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
ok ->
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
ejabberd_cluster:get_nodes());
case use_cache(Mod, LServer) of
true ->
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
cache_nodes(Mod, LServer));
false ->
ok
end;
_Err ->
{error, db_failure}
end.
get_prefs(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
fun() -> Mod:get_prefs(LUser, LServer) end),
Res = case use_cache(Mod, LServer) of
true ->
ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
fun() -> Mod:get_prefs(LUser, LServer) end);
false ->
Mod:get_prefs(LUser, LServer)
end,
case Res of
{ok, Prefs} ->
Prefs;
error ->
ActivateOpt = gen_mod:get_module_opt(
LServer, ?MODULE,
request_activates_archiving, false),
request_activates_archiving),
case ActivateOpt of
true ->
#archive_prefs{us = {LUser, LServer}, default = never};
false ->
Default = gen_mod:get_module_opt(
LServer, ?MODULE, default, never),
LServer, ?MODULE, default),
#archive_prefs{us = {LUser, LServer}, default = Default}
end
end.
@@ -838,20 +884,26 @@ prefs_el(Default, Always, Never, NS) ->
maybe_activate_mam(LUser, LServer) ->
ActivateOpt = gen_mod:get_module_opt(
LServer, ?MODULE, request_activates_archiving, false),
LServer, ?MODULE, request_activates_archiving),
case ActivateOpt of
true ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
fun() ->
Mod:get_prefs(LUser, LServer)
end),
Res = case use_cache(Mod, LServer) of
true ->
ets_cache:lookup(archive_prefs_cache,
{LUser, LServer},
fun() ->
Mod:get_prefs(LUser, LServer)
end);
false ->
Mod:get_prefs(LUser, LServer)
end,
case Res of
{ok, _Prefs} ->
ok;
error ->
Default = gen_mod:get_module_opt(
LServer, ?MODULE, default, never),
LServer, ?MODULE, default),
write_prefs(LUser, LServer, LServer, Default, [], [])
end;
false ->
@@ -922,13 +974,14 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
peer = Peer, id = ID},
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
CodecOpts = ejabberd_config:codec_options(LServer),
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
Pkt1 ->
Pkt2 = set_stanza_id(Pkt1, JidArchive, ID),
Pkt3 = maybe_update_from_to(
Pkt2, JidRequestor, JidArchive, Peer, MsgType, Nick),
Delay = #delay{stamp = TS, from = jid:make(LServer)},
{ok, #forwarded{xml_els = [xmpp:encode(Pkt3)], delay = Delay}}
{ok, #forwarded{sub_els = [Pkt3], delay = Delay}}
catch _:{xmpp_codec, Why} ->
?ERROR_MSG("Failed to decode raw element ~p from message "
"archive of user ~s: ~s",
@@ -1063,7 +1116,26 @@ get_commands_spec() ->
"Days to keep messages"],
args_example = [<<"all">>, 31],
args = [{type, binary}, {days, integer}],
result = {res, rescode}}].
result = {res, rescode}},
#ejabberd_commands{name = remove_mam_for_user, tags = [mam],
desc = "Remove mam archive for user",
module = ?MODULE, function = remove_mam_for_user,
args = [{user, binary}, {server, binary}],
args_desc = ["Username", "Server"],
args_example = [<<"bob">>, <<"example.com">>],
result = {res, restuple},
result_desc = "Result tuple",
result_example = {ok, <<"MAM archive removed">>}},
#ejabberd_commands{name = remove_mam_for_user_with_peer, tags = [mam],
desc = "Remove mam archive for user with peer",
module = ?MODULE, function = remove_mam_for_user_with_peer,
args = [{user, binary}, {server, binary}, {with, binary}],
args_desc = ["Username", "Server", "Peer"],
args_example = [<<"bob">>, <<"example.com">>, <<"anne@example.com">>],
result = {res, restuple},
result_desc = "Result tuple",
result_example = {ok, <<"MAM archive removed">>}}
].
mod_opt_type(assume_mam_usage) ->
fun (B) when is_boolean(B) -> B end;
@@ -1079,9 +1151,15 @@ mod_opt_type(default) ->
(never) -> never;
(roster) -> roster
end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(request_activates_archiving) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
[assume_mam_usage, cache_life_time, cache_size, use_cache, cache_missed,
db_type, default, iqdisc, request_activates_archiving].
fun (B) when is_boolean(B) -> B end.
mod_options(Host) ->
[{assume_mam_usage, false},
{default, never},
{request_activates_archiving, false},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
+19 -1
View File
@@ -28,7 +28,7 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6]).
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
@@ -67,6 +67,24 @@ remove_user(LUser, LServer) ->
remove_room(_LServer, LName, LHost) ->
remove_user(LName, LHost).
remove_from_archive(LUser, LServer, none) ->
US = {LUser, LServer},
case mnesia:transaction(fun () -> mnesia:delete({archive_msg, US}) end) of
{atomic, _} -> ok;
{aborted, Reason} -> {error, Reason}
end;
remove_from_archive(LUser, LServer, WithJid) ->
US = {LUser, LServer},
Peer = jid:remove_resource(jid:split(WithJid)),
F = fun () ->
Msgs = mnesia:match_object(#archive_msg{us = US, bare_peer = Peer, _ = '_'}),
lists:foreach(fun mnesia:delete_object/1, Msgs)
end,
case mnesia:transaction(F) of
{atomic, _} -> ok;
{aborted, Reason} -> {error, Reason}
end.
delete_old_messages(global, TimeStamp, Type) ->
mnesia:change_table_copy_type(archive_msg, node(), disc_copies),
Result = delete_old_user_messages(mnesia:dirty_first(archive_msg), TimeStamp, Type),
+17 -9
View File
@@ -30,7 +30,7 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1]).
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1, remove_from_archive/3]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
@@ -38,12 +38,6 @@
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
-ifdef(NEW_SQL_SCHEMA).
-define(USE_NEW_SCHEMA, true).
-else.
-define(USE_NEW_SCHEMA, false).
-endif.
%%%===================================================================
%%% API
%%%===================================================================
@@ -62,6 +56,20 @@ remove_room(LServer, LName, LHost) ->
LUser = jid:encode({LName, LHost, <<>>}),
remove_user(LUser, LServer).
remove_from_archive(LUser, LServer, none) ->
case ejabberd_sql:sql_query(LServer,
?SQL("delete from archive where username=%(LUser)s and %(LServer)H")) of
{error, Reason} -> {error, Reason};
_ -> ok
end;
remove_from_archive(LUser, LServer, WithJid) ->
Peer = jid:encode(jid:remove_resource(WithJid)),
case ejabberd_sql:sql_query(LServer,
?SQL("delete from archive where username=%(LUser)s and %(LServer)H and bare_peer=%(Peer)s")) of
{error, Reason} -> {error, Reason};
_ -> ok
end.
delete_old_messages(ServerHost, TimeStamp, Type) ->
TS = now_to_usec(TimeStamp),
case Type of
@@ -332,7 +340,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
SServer = Escape(LServer),
Query =
case ?USE_NEW_SCHEMA of
case ejabberd_sql:use_new_schema() of
true ->
[<<"SELECT ">>, TopClause,
<<" timestamp, xml, peer, kind, nick"
@@ -361,7 +369,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
[Query, <<" ORDER BY timestamp ASC ">>,
LimitClause, <<";">>]
end,
case ?USE_NEW_SCHEMA of
case ejabberd_sql:use_new_schema() of
true ->
{QueryPage,
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
+7 -6
View File
@@ -32,7 +32,7 @@
-include("logger.hrl").
-include("xmpp.hrl").
-export([start/2, stop/1, mod_opt_type/1, depends/2, reload/3]).
-export([start/2, stop/1, mod_opt_type/1, mod_options/1, depends/2, reload/3]).
-export([offline_message_hook/1,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
@@ -127,8 +127,8 @@ register_user(_User, Server) ->
%%====================================================================
push(Host, Probe) ->
IP = gen_mod:get_module_opt(Host, ?MODULE, ip, {127,0,0,1}),
Port = gen_mod:get_module_opt(Host, ?MODULE, port, 11111),
IP = gen_mod:get_module_opt(Host, ?MODULE, ip),
Port = gen_mod:get_module_opt(Host, ?MODULE, port),
send_metrics(Host, Probe, IP, Port).
send_metrics(Host, Probe, Peer, Port) ->
@@ -184,6 +184,7 @@ mod_opt_type(ip) ->
IP
end;
mod_opt_type(port) ->
fun(I) when is_integer(I), I>0, I<65536 -> I end;
mod_opt_type(_) ->
[ip, port].
fun(I) when is_integer(I), I>0, I<65536 -> I end.
mod_options(_) ->
[{ip, <<"127.0.0.1">>}, {port, 11111}].
+13 -12
View File
@@ -30,7 +30,7 @@
%% API
-export([start/2, stop/1, process_iq/1,
disco_items/5, disco_identity/5, disco_info/5,
disco_features/5, mod_opt_type/1, depends/2]).
disco_features/5, mod_opt_type/1, mod_options/1, depends/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -124,8 +124,7 @@ process_iq(#iq{lang = Lang} = IQ) ->
%%%===================================================================
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"mix.@HOST@">>),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)),
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
lists:foreach(
fun(Host) ->
ConfigTab = gen_mod:get_module_proc(Host, config),
@@ -140,20 +139,20 @@ init([ServerHost, Opts]) ->
ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_ITEMS, mod_disco,
process_local_iq_items, IQDisc),
process_local_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_INFO, mod_disco,
process_local_iq_info, IQDisc),
process_local_iq_info),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_ITEMS, mod_disco,
process_local_iq_items, IQDisc),
process_local_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_INFO, mod_disco,
process_local_iq_info, IQDisc),
process_local_iq_info),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
?NS_PUBSUB, mod_pubsub, iq_sm),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_MIX_0, ?MODULE, process_iq, IQDisc),
?NS_MIX_0, ?MODULE, process_iq),
ejabberd_router:register_route(Host, ServerHost)
end, Hosts),
{ok, #state{server_host = ServerHost, hosts = Hosts}}.
@@ -316,8 +315,10 @@ is_not_subscribed({error, StanzaError}) ->
depends(_Host, _Opts) ->
[{mod_pubsub, hard}].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(hosts) ->
fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(_) -> [host, hosts, iqdisc].
fun (L) -> lists:map(fun iolist_to_binary/1, L) end.
mod_options(_Host) ->
[{host, <<"mix.@HOST@">>},
{hosts, []}].
+109 -87
View File
@@ -69,7 +69,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, depends/2]).
mod_opt_type/1, mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -107,8 +107,7 @@
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
-callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer().
-callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}].
-callback get_subscribed_rooms(binary(), binary(), jid()) ->
{ok, [{ljid(), binary(), [binary()]}]} | {error, any()}.
-callback get_subscribed_rooms(binary(), binary(), jid()) -> [ljid()] | [].
%%====================================================================
%% API
@@ -233,7 +232,6 @@ get_online_rooms_by_user(ServerHost, LUser, LServer) ->
init([Host, Opts]) ->
process_flag(trap_exit, true),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
#state{access = Access, hosts = MyHosts,
history_size = HistorySize, queue_type = QueueType,
room_shaper = RoomShaper} = State = init_state(Host, Opts),
@@ -243,7 +241,7 @@ init([Host, Opts]) ->
RMod:init(Host, [{hosts, MyHosts}|Opts]),
lists:foreach(
fun(MyHost) ->
register_iq_handlers(MyHost, IQDisc),
register_iq_handlers(MyHost),
ejabberd_router:register_route(MyHost, Host),
load_permanent_rooms(MyHost, Host, Access, HistorySize,
RoomShaper, QueueType)
@@ -273,8 +271,6 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From,
{reply, ok, State}.
handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
NewRMod = gen_mod:ram_db_mod(ServerHost, NewOpts, ?MODULE),
OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
@@ -290,18 +286,10 @@ handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
true ->
ok
end,
if (NewIQDisc /= OldIQDisc) ->
lists:foreach(
fun(NewHost) ->
register_iq_handlers(NewHost, NewIQDisc)
end, NewHosts -- (NewHosts -- OldHosts));
true ->
ok
end,
lists:foreach(
fun(NewHost) ->
ejabberd_router:register_route(NewHost, ServerHost),
register_iq_handlers(NewHost, NewIQDisc)
register_iq_handlers(NewHost)
end, NewHosts -- OldHosts),
lists:foreach(
fun(OldHost) ->
@@ -353,18 +341,16 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
init_state(Host, Opts) ->
MyHosts = gen_mod:get_opt_hosts(Host, Opts,
<<"conference.@HOST@">>),
Access = gen_mod:get_opt(access, Opts, all),
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all),
HistorySize = gen_mod:get_opt(history_size, Opts, 20),
MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts, 100),
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
QueueType = gen_mod:get_opt(queue_type, Opts,
ejabberd_config:default_queue_type(Host)),
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
MyHosts = gen_mod:get_opt_hosts(Host, Opts),
Access = gen_mod:get_opt(access, Opts),
AccessCreate = gen_mod:get_opt(access_create, Opts),
AccessAdmin = gen_mod:get_opt(access_admin, Opts),
AccessPersistent = gen_mod:get_opt(access_persistent, Opts),
HistorySize = gen_mod:get_opt(history_size, Opts),
MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts),
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts),
QueueType = gen_mod:get_opt(queue_type, Opts),
RoomShaper = gen_mod:get_opt(room_shaper, Opts),
#state{hosts = MyHosts,
server_host = Host,
access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
@@ -374,19 +360,19 @@ init_state(Host, Opts) ->
max_rooms_discoitems = MaxRoomsDiscoItems,
room_shaper = RoomShaper}.
register_iq_handlers(Host, IQDisc) ->
register_iq_handlers(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER,
?MODULE, process_register, IQDisc),
?MODULE, process_register),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
?MODULE, process_vcard, IQDisc),
?MODULE, process_vcard),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUCSUB,
?MODULE, process_mucsub, IQDisc),
?MODULE, process_mucsub),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE,
?MODULE, process_muc_unique, IQDisc),
?MODULE, process_muc_unique),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
?MODULE, process_disco_info, IQDisc),
?MODULE, process_disco_info),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
?MODULE, process_disco_items, IQDisc).
?MODULE, process_disco_items).
unregister_iq_handlers(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER),
@@ -490,19 +476,28 @@ process_vcard(#iq{lang = Lang} = IQ) ->
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
-spec process_register(iq()) -> iq().
process_register(#iq{type = get, from = From, to = To, lang = Lang,
sub_els = [#register{}]} = IQ) ->
process_register(#iq{type = Type, from = From, to = To, lang = Lang,
sub_els = [El = #register{}]} = IQ) ->
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
process_register(#iq{type = set, from = From, to = To,
lang = Lang, sub_els = [El = #register{}]} = IQ) ->
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
{result, Result} ->
xmpp:make_iq_result(IQ, Result);
{error, Err} ->
AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
case acl:match_rule(ServerHost, AccessRegister, From) of
allow ->
case Type of
get ->
xmpp:make_iq_result(
IQ, iq_get_register_info(ServerHost, Host, From, Lang));
set ->
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
{result, Result} ->
xmpp:make_iq_result(IQ, Result);
{error, Err} ->
xmpp:make_error(IQ, Err)
end
end;
deny ->
ErrText = <<"Access denied by service policy">>,
Err = xmpp:err_forbidden(ErrText, Lang),
xmpp:make_error(IQ, Err)
end.
@@ -510,10 +505,11 @@ process_register(#iq{type = set, from = From, to = To,
process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
process_disco_info(#iq{type = get, to = To, lang = Lang,
process_disco_info(#iq{type = get, from = From, to = To, lang = Lang,
sub_els = [#disco_info{node = <<"">>}]} = IQ) ->
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
[ServerHost, ?MODULE, <<"">>, Lang]),
MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of
@@ -524,10 +520,14 @@ process_disco_info(#iq{type = get, to = To, lang = Lang,
true -> [?NS_RSM];
false -> []
end,
RegisterFeatures = case acl:match_rule(ServerHost, AccessRegister, From) of
allow -> [?NS_REGISTER];
deny -> []
end,
Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
?NS_REGISTER, ?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE
| RSMFeatures ++ MAMFeatures],
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("Chatrooms")),
?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE
| RegisterFeatures ++ RSMFeatures ++ MAMFeatures],
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
Identity = #identity{category = <<"conference">>,
type = <<"text">>,
name = translate:translate(Lang, Name)},
@@ -551,8 +551,7 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
MaxRoomsDiscoItems = gen_mod:get_module_opt(
ServerHost, ?MODULE, max_rooms_discoitems,
100),
ServerHost, ?MODULE, max_rooms_discoitems),
case iq_disco_items(ServerHost, Host, From, Lang,
MaxRoomsDiscoItems, Node, RSM) of
{error, Err} ->
@@ -605,8 +604,8 @@ check_user_can_create_room(ServerHost, AccessCreate,
end.
check_create_roomid(ServerHost, RoomID) ->
Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id, infinity),
Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id, ""),
Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id),
Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id),
(byte_size(RoomID) =< Max) and
(re:run(RoomID, Regexp, [unicode, {capture, none}]) == match).
@@ -741,7 +740,7 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
instructions = [Inst], fields = Fields},
#register{nick = Nick,
registered = Registered,
instructions =
instructions =
translate:translate(
Lang, <<"You need a client that supports x:data "
"to register the nickname">>),
@@ -877,6 +876,8 @@ mod_opt_type(access_create) ->
fun acl:access_rules_validator/1;
mod_opt_type(access_persistent) ->
fun acl:access_rules_validator/1;
mod_opt_type(access_register) ->
fun acl:access_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(history_size) ->
@@ -975,35 +976,56 @@ mod_opt_type({default_room_options, presence_broadcast}) ->
(participant) -> participant;
(visitor) -> visitor
end, L)
end;
mod_opt_type(_) ->
[access, access_admin, access_create, access_persistent,
db_type, ram_db_type, history_size, host, hosts, name,
max_room_desc, max_room_id, max_room_name,
max_rooms_discoitems, max_user_conferences, max_users,
max_users_admin_threshold, max_users_presence,
min_message_interval, min_presence_interval, queue_type,
regexp_room_id, room_shaper, user_message_shaper, user_presence_shaper,
{default_room_options, allow_change_subj},
{default_room_options, allow_private_messages},
{default_room_options, allow_query_users},
{default_room_options, allow_user_invites},
{default_room_options, allow_visitor_nickchange},
{default_room_options, allow_visitor_status},
{default_room_options, anonymous},
{default_room_options, captcha_protected},
{default_room_options, logging},
{default_room_options, members_by_default},
{default_room_options, members_only},
{default_room_options, moderated},
{default_room_options, password_protected},
{default_room_options, persistent},
{default_room_options, public},
{default_room_options, public_list},
{default_room_options, mam},
{default_room_options, allow_subscription},
{default_room_options, password},
{default_room_options, title},
{default_room_options, allow_private_messages_from_visitors},
{default_room_options, max_users},
{default_room_options, presence_broadcast}].
end.
mod_options(Host) ->
[{access, all},
{access_admin, none},
{access_create, all},
{access_persistent, all},
{access_register, all},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
{history_size, 20},
{host, <<"conference.@HOST@">>},
{hosts, []},
{name, ?T("Chatrooms")},
{max_room_desc, infinity},
{max_room_id, infinity},
{max_room_name, infinity},
{max_rooms_discoitems, 100},
{max_user_conferences, 10},
{max_users, 200},
{max_users_admin_threshold, 5},
{max_users_presence, 1000},
{min_message_interval, 0},
{min_presence_interval, 0},
{queue_type, ejabberd_config:default_queue_type(Host)},
{regexp_room_id, <<"">>},
{room_shaper, none},
{user_message_shaper, none},
{user_presence_shaper, none},
{default_room_options,
[{allow_change_subj,true},
{allow_private_messages,true},
{allow_query_users,true},
{allow_user_invites,false},
{allow_visitor_nickchange,true},
{allow_visitor_status,true},
{anonymous,true},
{captcha_protected,false},
{logging,false},
{members_by_default,true},
{members_only,false},
{moderated,true},
{password_protected,false},
{persistent,false},
{public,true},
{public_list,true},
{mam,false},
{allow_subscription,false},
{password,<<>>},
{title,<<>>},
{allow_private_messages_from_visitors,anyone},
{max_users,200},
{presence_broadcast,[moderator,participant,visitor]}]}].
+46 -17
View File
@@ -37,10 +37,10 @@
get_user_rooms/2, get_room_occupants/2,
get_room_occupants_number/2, send_direct_invitation/5,
change_room_option/4, get_room_options/2,
set_room_affiliation/4, get_room_affiliations/2,
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
web_menu_main/2, web_page_main/2, web_menu_host/3,
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
web_page_host/3, mod_opt_type/1, get_commands_spec/0]).
web_page_host/3, mod_options/1, get_commands_spec/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -313,8 +313,17 @@ get_commands_spec() ->
{affiliation, atom},
{reason, string}
]}}
}}}
].
}}},
#ejabberd_commands{name = get_room_affiliation, tags = [muc_room],
desc = "Get affiliation of a user in MUC room",
module = ?MODULE, function = get_room_affiliation,
args_desc = ["Room name", "MUC service", "User JID"],
args_example = ["room1", "muc.example.com", "user1@example.com"],
result_desc = "Affiliation of the user",
result_example = member,
args = [{name, binary}, {service, binary}, {jid, binary}],
result = {affiliation, atom}}
].
%%%
@@ -569,8 +578,10 @@ prepare_room_info(Room_info) ->
%% ok | error
%% @doc Create a room immediately with the default options.
create_room(Name1, Host1, ServerHost) ->
create_room_with_opts(Name1, Host1, ServerHost, []),
change_room_option(Name1, Host1, <<"persistent">>, <<"true">>).
case create_room_with_opts(Name1, Host1, ServerHost, []) of
ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
Error -> Error
end.
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
true = (error /= (Name = jid:nodeprep(Name1))),
@@ -578,7 +589,7 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
%% Get the default room options from the muc configuration
DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc,
default_room_options, []),
default_room_options),
%% Change default room options as required
FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts],
RoomOpts = lists:ukeymerge(1,
@@ -589,14 +600,13 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
mod_muc:store_room(ServerHost, Host, Name, RoomOpts),
%% Get all remaining mod_muc parameters that might be utilized
Access = gen_mod:get_module_opt(ServerHost, mod_muc, access, all),
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create, all),
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin, none),
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent, all),
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20),
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper, none),
QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type,
ejabberd_config:default_queue_type(ServerHost)),
Access = gen_mod:get_module_opt(ServerHost, mod_muc, access),
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create),
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin),
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent),
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size),
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper),
QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type),
%% If the room does not exist yet in the muc_online_room
case mod_muc:find_online_room(Name, Host) of
@@ -698,7 +708,7 @@ create_rooms_file(Filename) ->
file:close(F),
%% Read the default room options defined for the first virtual host
DefRoomOpts = gen_mod:get_module_opt(?MYNAME, mod_muc,
default_room_options, []),
default_room_options),
[muc_create_room(?MYNAME, A, DefRoomOpts) || A <- Rooms],
ok.
@@ -1033,6 +1043,25 @@ get_room_affiliations(Name, Service) ->
throw({error, "The room does not exist."})
end.
%%----------------------------
%% Get Room Affiliation
%%----------------------------
%% @spec(Name::binary(), Service::binary(), JID::binary()) ->
%% {Affiliation::string()}
%% @doc Get affiliation of a user in the room Name@Service.
get_room_affiliation(Name, Service, JID) ->
case mod_muc:find_online_room(Name, Service) of
{ok, Pid} ->
%% Get the PID of the online room, then request its state
{ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
UserJID = jid:decode(JID),
mod_muc_room:get_affiliation(UserJID, StateData);
error ->
throw({error, "The room does not exist."})
end.
%%----------------------------
%% Change Room Affiliation
%%----------------------------
@@ -1215,4 +1244,4 @@ find_hosts(ServerHost) ->
[]
end.
mod_opt_type(_) -> [].
mod_options(_) -> [].
+37 -24
View File
@@ -39,7 +39,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, depends/2]).
mod_opt_type/1, mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -138,16 +138,16 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
init_state(Host, Opts) ->
OutDir = gen_mod:get_opt(outdir, Opts, <<"www/muc">>),
DirType = gen_mod:get_opt(dirtype, Opts, subdirs),
DirName = gen_mod:get_opt(dirname, Opts, room_jid),
FileFormat = gen_mod:get_opt(file_format, Opts, html),
FilePermissions = gen_mod:get_opt(file_permissions, Opts, {644, 33}),
CSSFile = gen_mod:get_opt(cssfile, Opts, false),
AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin),
Timezone = gen_mod:get_opt(timezone, Opts, local),
Top_link = gen_mod:get_opt(top_link, Opts, {<<"/">>, <<"Home">>}),
NoFollow = gen_mod:get_opt(spam_prevention, Opts, true),
OutDir = gen_mod:get_opt(outdir, Opts),
DirType = gen_mod:get_opt(dirtype, Opts),
DirName = gen_mod:get_opt(dirname, Opts),
FileFormat = gen_mod:get_opt(file_format, Opts),
FilePermissions = gen_mod:get_opt(file_permissions, Opts),
CSSFile = gen_mod:get_opt(cssfile, Opts),
AccessLog = gen_mod:get_opt(access_log, Opts),
Timezone = gen_mod:get_opt(timezone, Opts),
Top_link = gen_mod:get_opt(top_link, Opts),
NoFollow = gen_mod:get_opt(spam_prevention, Opts),
Lang = ejabberd_config:get_lang(Host),
#logstate{host = Host, out_dir = OutDir,
dir_type = DirType, dir_name = DirName,
@@ -375,31 +375,31 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"leaves the room">>),
htmlize(Reason, NoFollow, FileFormat)]);
{kickban, <<"301">>, <<"">>} ->
{kickban, 301, <<"">>} ->
io_lib:format("<font class=\"mb\">~s ~s</font><br/>",
[Nick, ?T(<<"has been banned">>)]);
{kickban, <<"301">>, Reason} ->
{kickban, 301, Reason} ->
io_lib:format("<font class=\"mb\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"has been banned">>),
htmlize(Reason, FileFormat)]);
{kickban, <<"307">>, <<"">>} ->
{kickban, 307, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick, ?T(<<"has been kicked">>)]);
{kickban, <<"307">>, Reason} ->
{kickban, 307, Reason} ->
io_lib:format("<font class=\"mk\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"has been kicked">>),
htmlize(Reason, FileFormat)]);
{kickban, <<"321">>, <<"">>} ->
{kickban, 321, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because of an affiliation "
"change">>)]);
{kickban, <<"322">>, <<"">>} ->
{kickban, 322, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because the room has "
"been changed to members-only">>)]);
{kickban, <<"332">>, <<"">>} ->
{kickban, 332, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because of a system "
@@ -931,7 +931,10 @@ has_no_permanent_store_hint(Packet) ->
mod_opt_type(access_log) ->
fun acl:access_rules_validator/1;
mod_opt_type(cssfile) -> fun misc:try_read_file/1;
mod_opt_type(cssfile) ->
fun(false) -> false;
(File) -> misc:try_read_file(File)
end;
mod_opt_type(dirname) ->
fun (room_jid) -> room_jid;
(room_name) -> room_name
@@ -963,8 +966,18 @@ mod_opt_type(timezone) ->
mod_opt_type(top_link) ->
fun ([{S1, S2}]) ->
{iolist_to_binary(S1), iolist_to_binary(S2)}
end;
mod_opt_type(_) ->
[access_log, cssfile, dirname, dirtype, file_format,
{file_permissions, mode}, {file_permissions, group},
outdir, spam_prevention, timezone, top_link].
end.
mod_options(_) ->
[{access_log, muc_admin},
{cssfile, false},
{dirname, room_jid},
{dirtype, subdirs},
{file_format, html},
{file_permissions,
[{mode, 644},
{group, 33}]},
{outdir, <<"www/muc">>},
{spam_prevention, true},
{timezone, local},
{top_link, [{<<"/">>, <<"Home">>}]}].
+349 -138
View File
@@ -37,7 +37,9 @@
get_role/2,
get_affiliation/2,
is_occupant_or_admin/2,
route/2]).
route/2,
expand_opts/1,
config_fields/0]).
%% gen_fsm callbacks
-export([init/1,
@@ -164,8 +166,8 @@ normal_state({route, <<"">>,
Now = p1_time_compat:system_time(micro_seconds),
MinMessageInterval = trunc(gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, min_message_interval,
0) * 1000000),
mod_muc, min_message_interval)
* 1000000),
Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} =
shaper:update(Activity#activity.message_shaper, Size),
@@ -288,7 +290,7 @@ normal_state({route, <<"">>,
process_iq_admin(From, IQ, StateData);
?NS_MUC_OWNER ->
process_iq_owner(From, IQ, StateData);
?NS_DISCO_INFO when SubEl#disco_info.node == <<>> ->
?NS_DISCO_INFO ->
process_iq_disco_info(From, IQ, StateData);
?NS_DISCO_ITEMS ->
process_iq_disco_items(From, IQ, StateData);
@@ -346,8 +348,8 @@ normal_state({route, Nick, #presence{from = From} = Packet}, StateData) ->
Now = p1_time_compat:system_time(micro_seconds),
MinPresenceInterval =
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_presence_interval,
0) * 1000000),
mod_muc, min_presence_interval)
* 1000000),
if (Now >= Activity#activity.presence_time + MinPresenceInterval)
and (Activity#activity.presence == undefined) ->
NewActivity = Activity#activity{presence_time = Now},
@@ -500,13 +502,13 @@ handle_event(destroy, StateName, StateData) ->
handle_event({destroy, <<"">>}, StateName, StateData);
handle_event({set_affiliations, Affiliations},
StateName, StateData) ->
{next_state, StateName,
StateData#state{affiliations = Affiliations}};
NewStateData = set_affiliations(Affiliations, StateData),
{next_state, StateName, NewStateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
Len = ?DICT:size(StateData#state.users),
Len = ?DICT:size(StateData#state.nicks),
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
true ->
get_roomdesc_reply(JID, StateData,
@@ -1262,58 +1264,132 @@ set_affiliation(JID, Affiliation, StateData) ->
set_affiliation(JID, Affiliation, StateData, <<"">>).
-spec set_affiliation(jid(), affiliation(), state(), binary()) -> state().
set_affiliation(JID, Affiliation,
#state{config = #config{persistent = false}} = StateData,
Reason) ->
set_affiliation_fallback(JID, Affiliation, StateData, Reason);
set_affiliation(JID, Affiliation, StateData, Reason) ->
ServerHost = StateData#state.server_host,
Room = StateData#state.room,
Host = StateData#state.host,
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case Mod:set_affiliation(ServerHost, Room, Host, JID, Affiliation, Reason) of
ok ->
StateData;
{error, _} ->
set_affiliation_fallback(JID, Affiliation, StateData, Reason)
end.
-spec set_affiliation_fallback(jid(), affiliation(), state(), binary()) -> state().
set_affiliation_fallback(JID, Affiliation, StateData, Reason) ->
LJID = jid:remove_resource(jid:tolower(JID)),
Affiliations = case Affiliation of
none ->
(?DICT):erase(LJID, StateData#state.affiliations);
_ ->
(?DICT):store(LJID, {Affiliation, Reason},
StateData#state.affiliations)
none ->
(?DICT):erase(LJID, StateData#state.affiliations);
_ ->
(?DICT):store(LJID, {Affiliation, Reason},
StateData#state.affiliations)
end,
StateData#state{affiliations = Affiliations}.
-spec get_affiliation(jid(), state()) -> affiliation().
get_affiliation(JID, StateData) ->
{_AccessRoute, _AccessCreate, AccessAdmin,
_AccessPersistent} =
StateData#state.access,
Res = case acl:match_rule(StateData#state.server_host,
AccessAdmin, JID)
of
allow -> owner;
_ ->
LJID = jid:tolower(JID),
case (?DICT):find(LJID, StateData#state.affiliations) of
{ok, Affiliation} -> Affiliation;
_ ->
LJID1 = jid:remove_resource(LJID),
case (?DICT):find(LJID1, StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ ->
LJID2 = setelement(1, LJID, <<"">>),
case (?DICT):find(LJID2,
StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ ->
LJID3 = jid:remove_resource(LJID2),
case (?DICT):find(LJID3,
StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ -> none
end
end
end
end
end,
case Res of
{A, _Reason} -> A;
_ -> Res
-spec set_affiliations(?TDICT, state()) -> state().
set_affiliations(Affiliations,
#state{config = #config{persistent = false}} = StateData) ->
set_affiliations_fallback(Affiliations, StateData);
set_affiliations(Affiliations, StateData) ->
Room = StateData#state.room,
Host = StateData#state.host,
ServerHost = StateData#state.server_host,
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case Mod:set_affiliations(ServerHost, Room, Host, Affiliations) of
ok ->
StateData;
{error, _} ->
set_affiliations_fallback(Affiliations, StateData)
end.
-spec set_affiliations_fallback(?TDICT, state()) -> state().
set_affiliations_fallback(Affiliations, StateData) ->
StateData#state{affiliations = Affiliations}.
-spec get_affiliation(ljid() | jid(), state()) -> affiliation().
get_affiliation(#jid{} = JID, StateData) ->
case get_service_affiliation(JID, StateData) of
owner ->
owner;
none ->
case do_get_affiliation(JID, StateData) of
{Affiliation, _Reason} -> Affiliation;
Affiliation -> Affiliation
end
end;
get_affiliation(LJID, StateData) ->
get_affiliation(jid:make(LJID), StateData).
-spec do_get_affiliation(jid(), state()) -> affiliation().
do_get_affiliation(JID, #state{config = #config{persistent = false}} = StateData) ->
do_get_affiliation_fallback(JID, StateData);
do_get_affiliation(JID, StateData) ->
Room = StateData#state.room,
Host = StateData#state.host,
LServer = JID#jid.lserver,
LUser = JID#jid.luser,
ServerHost = StateData#state.server_host,
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case Mod:get_affiliation(ServerHost, Room, Host, LUser, LServer) of
{error, _} ->
do_get_affiliation_fallback(JID, StateData);
{ok, Affiliation} ->
Affiliation
end.
-spec do_get_affiliation_fallback(jid(), state()) -> affiliation().
do_get_affiliation_fallback(JID, StateData) ->
LJID = jid:tolower(JID),
case (?DICT):find(LJID, StateData#state.affiliations) of
{ok, Affiliation} -> Affiliation;
_ ->
LJID1 = jid:remove_resource(LJID),
case (?DICT):find(LJID1, StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ ->
LJID2 = setelement(1, LJID, <<"">>),
case (?DICT):find(LJID2,
StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ ->
LJID3 = jid:remove_resource(LJID2),
case (?DICT):find(LJID3,
StateData#state.affiliations)
of
{ok, Affiliation} -> Affiliation;
_ -> none
end
end
end
end.
-spec get_affiliations(state()) -> ?TDICT.
get_affiliations(#state{config = #config{persistent = false}} = StateData) ->
get_affiliations_callback(StateData);
get_affiliations(StateData) ->
Room = StateData#state.room,
Host = StateData#state.host,
ServerHost = StateData#state.server_host,
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case Mod:get_affiliations(ServerHost, Room, Host) of
{error, _} ->
get_affiliations_callback(StateData);
{ok, Affiliations} ->
Affiliations
end.
-spec get_affiliations_callback(state()) -> ?TDICT.
get_affiliations_callback(StateData) ->
StateData#state.affiliations.
-spec get_service_affiliation(jid(), state()) -> owner | none.
get_service_affiliation(JID, StateData) ->
{_AccessRoute, _AccessCreate, AccessAdmin,
@@ -1421,30 +1497,28 @@ get_max_users(StateData) ->
-spec get_service_max_users(state()) -> pos_integer().
get_service_max_users(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_users,
?MAX_USERS_DEFAULT).
mod_muc, max_users).
-spec get_max_users_admin_threshold(state()) -> pos_integer().
get_max_users_admin_threshold(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_users_admin_threshold,
5).
mod_muc, max_users_admin_threshold).
-spec room_queue_new(binary(), shaper:shaper(), _) -> p1_queue:queue().
room_queue_new(ServerHost, Shaper, QueueType) ->
HaveRoomShaper = Shaper /= none,
HaveMessageShaper = gen_mod:get_module_opt(
ServerHost, mod_muc, user_message_shaper,
none) /= none,
ServerHost, mod_muc,
user_message_shaper) /= none,
HavePresenceShaper = gen_mod:get_module_opt(
ServerHost, mod_muc, user_presence_shaper,
none) /= none,
ServerHost, mod_muc,
user_presence_shaper) /= none,
HaveMinMessageInterval = gen_mod:get_module_opt(
ServerHost, mod_muc, min_message_interval,
0) /= 0,
ServerHost, mod_muc,
min_message_interval) /= 0,
HaveMinPresenceInterval = gen_mod:get_module_opt(
ServerHost, mod_muc, min_presence_interval,
0) /= 0,
ServerHost, mod_muc,
min_presence_interval) /= 0,
if HaveRoomShaper or HaveMessageShaper or HavePresenceShaper
or HaveMinMessageInterval or HaveMinPresenceInterval ->
p1_queue:new(QueueType);
@@ -1461,12 +1535,10 @@ get_user_activity(JID, StateData) ->
error ->
MessageShaper =
shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, user_message_shaper,
none)),
mod_muc, user_message_shaper)),
PresenceShaper =
shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, user_presence_shaper,
none)),
mod_muc, user_presence_shaper)),
#activity{message_shaper = MessageShaper,
presence_shaper = PresenceShaper}
end.
@@ -1475,12 +1547,12 @@ get_user_activity(JID, StateData) ->
store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_message_interval,
0) * 1000),
mod_muc, min_message_interval)
* 1000),
MinPresenceInterval =
trunc(gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, min_presence_interval,
0) * 1000),
mod_muc, min_presence_interval)
* 1000),
Key = jid:tolower(JID),
Now = p1_time_compat:system_time(micro_seconds),
Activity1 = clean_treap(StateData#state.activity,
@@ -1788,8 +1860,7 @@ add_new_user(From, Nick, Packet, StateData) ->
NConferences = tab_count_user(From, StateData),
MaxConferences =
gen_mod:get_module_opt(StateData#state.server_host,
mod_muc, max_user_conferences,
10),
mod_muc, max_user_conferences),
Collision = nick_collision(From, Nick, StateData),
IsSubscribeRequest = not is_record(Packet, presence),
case {(ServiceAffiliation == owner orelse
@@ -2060,8 +2131,7 @@ filter_history(Queue, Now, Nick,
is_room_overcrowded(StateData) ->
MaxUsersPresence = gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, max_users_presence,
?DEFAULT_MAX_USERS_PRESENCE),
mod_muc, max_users_presence),
(?DICT):size(StateData#state.users) > MaxUsersPresence.
-spec presence_broadcast_allowed(jid(), state()) -> boolean().
@@ -2072,12 +2142,30 @@ presence_broadcast_allowed(JID, StateData) ->
-spec send_initial_presences_and_messages(
jid(), binary(), presence(), state(), state()) -> ok.
send_initial_presences_and_messages(From, Nick, Presence, NewState, OldState) ->
send_self_presence(From, NewState),
send_existing_presences(From, NewState),
send_initial_presence(From, NewState, OldState),
History = get_history(Nick, Presence, NewState),
send_history(From, History, NewState),
send_subject(From, OldState).
-spec send_self_presence(jid(), state()) -> ok.
send_self_presence(JID, State) ->
AvatarHash = (State#state.config)#config.vcard_xupdate,
DiscoInfo = make_disco_info(JID, State),
DiscoHash = mod_caps:compute_disco_hash(DiscoInfo, sha),
Els1 = [#caps{hash = <<"sha-1">>,
node = ?EJABBERD_URI,
version = DiscoHash}],
Els2 = if is_binary(AvatarHash) ->
[#vcard_xupdate{hash = AvatarHash}|Els1];
true ->
Els1
end,
ejabberd_router:route(#presence{from = State#state.jid, to = JID,
id = randoms:get_string(),
sub_els = Els2}).
-spec send_initial_presence(jid(), state(), state()) -> ok.
send_initial_presence(NJID, StateData, OldStateData) ->
send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
@@ -2584,16 +2672,35 @@ search_role(Role, StateData) ->
(?DICT):to_list(StateData#state.users)).
-spec search_affiliation(affiliation(), state()) ->
[{ljid(),
affiliation() | {affiliation(), binary()}}].
[{ljid(),
affiliation() | {affiliation(), binary()}}].
search_affiliation(Affiliation,
#state{config = #config{persistent = false}} = StateData) ->
search_affiliation_fallback(Affiliation, StateData);
search_affiliation(Affiliation, StateData) ->
lists:filter(fun ({_, A}) ->
case A of
{A1, _Reason} -> Affiliation == A1;
_ -> Affiliation == A
end
end,
(?DICT):to_list(StateData#state.affiliations)).
Room = StateData#state.room,
Host = StateData#state.host,
ServerHost = StateData#state.server_host,
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case Mod:search_affiliation(ServerHost, Room, Host, Affiliation) of
{ok, AffiliationList} ->
AffiliationList;
{error, _} ->
search_affiliation_fallback(Affiliation, StateData)
end.
-spec search_affiliation_fallback(affiliation(), state()) ->
[{ljid(),
affiliation() | {affiliation(), binary()}}].
search_affiliation_fallback(Affiliation, StateData) ->
lists:filter(
fun({_, A}) ->
case A of
{A1, _Reason} -> Affiliation == A1;
_ -> Affiliation == A
end
end,
(?DICT):to_list(StateData#state.affiliations)).
-spec process_admin_items_set(jid(), [muc_item()], binary(),
#state{}) -> {result, undefined, #state{}} |
@@ -3126,12 +3233,10 @@ is_allowed_room_name_desc_limits(Options, StateData) ->
RoomDesc = proplists:get_value(roomdesc, Options, <<"">>),
MaxRoomName = gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, max_room_name,
infinity),
mod_muc, max_room_name),
MaxRoomDesc = gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, max_room_desc,
infinity),
mod_muc, max_room_desc),
(byte_size(RoomName) =< MaxRoomName)
andalso (byte_size(RoomDesc) =< MaxRoomDesc).
@@ -3156,8 +3261,7 @@ is_password_settings_correct(Options, StateData) ->
get_default_room_maxusers(RoomState) ->
DefRoomOpts =
gen_mod:get_module_opt(RoomState#state.server_host,
mod_muc, default_room_options,
[]),
mod_muc, default_room_options),
RoomState2 = set_opts(DefRoomOpts, RoomState),
(RoomState2#state.config)#config.max_users.
@@ -3316,23 +3420,36 @@ set_config(Opts, Config, ServerHost, Lang) ->
-spec change_config(#config{}, state()) -> {result, undefined, state()}.
change_config(Config, StateData) ->
send_config_change_info(Config, StateData),
NSD = remove_subscriptions(StateData#state{config = Config}),
case {(StateData#state.config)#config.persistent,
Config#config.persistent}
of
{_, true} ->
store_room(NSD);
{true, false} ->
mod_muc:forget_room(NSD#state.server_host,
NSD#state.host, NSD#state.room);
{false, false} -> ok
end,
StateData0 = StateData#state{config = Config},
StateData1 = remove_subscriptions(StateData0),
StateData2 =
case {(StateData#state.config)#config.persistent,
Config#config.persistent} of
{WasPersistent, true} ->
if not WasPersistent ->
set_affiliations(StateData1#state.affiliations,
StateData1);
true ->
ok
end,
store_room(StateData1),
StateData1;
{true, false} ->
Affiliations = get_affiliations(StateData),
mod_muc:forget_room(StateData1#state.server_host,
StateData1#state.host,
StateData1#state.room),
StateData1#state{affiliations = Affiliations};
{false, false} ->
StateData1
end,
case {(StateData#state.config)#config.members_only,
Config#config.members_only}
of
{false, true} ->
NSD1 = remove_nonmembers(NSD), {result, undefined, NSD1};
_ -> {result, undefined, NSD}
Config#config.members_only} of
{false, true} ->
StateData3 = remove_nonmembers(StateData2),
{result, undefined, StateData3};
_ ->
{result, undefined, StateData2}
end.
-spec send_config_change_info(#config{}, state()) -> ok.
@@ -3351,12 +3468,15 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
end
++
case Old#config{anonymous = New#config.anonymous,
vcard = New#config.vcard,
logging = New#config.logging} of
New -> [];
_ -> [104]
end,
if Codes /= [] ->
lists:foreach(
fun({_LJID, #user{jid = JID}}) ->
send_self_presence(JID, StateData#state{config = New})
end, ?DICT:to_list(StateData#state.users)),
Message = #message{type = groupchat,
id = randoms:get_string(),
sub_els = [#muc_user{status_codes = Codes}]},
@@ -3384,7 +3504,8 @@ remove_nonmembers(StateData) ->
StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
-spec set_opts([{atom(), any()}], state()) -> state().
set_opts([], StateData) -> StateData;
set_opts([], StateData) ->
set_vcard_xupdate(StateData);
set_opts([{Opt, Val} | Opts], StateData) ->
NSD = case Opt of
title ->
@@ -3499,6 +3620,10 @@ set_opts([{Opt, Val} | Opts], StateData) ->
StateData#state{config =
(StateData#state.config)#config{vcard =
Val}};
vcard_xupdate ->
StateData#state{config =
(StateData#state.config)#config{vcard_xupdate =
Val}};
pubsub ->
StateData#state{config =
(StateData#state.config)#config{pubsub = Val}};
@@ -3533,6 +3658,20 @@ set_opts([{Opt, Val} | Opts], StateData) ->
end,
set_opts(Opts, NSD).
set_vcard_xupdate(#state{config =
#config{vcard = VCardRaw,
vcard_xupdate = undefined} = Config} = State)
when VCardRaw /= <<"">> ->
case fxml_stream:parse_element(VCardRaw) of
{error, _} ->
State;
El ->
Hash = mod_vcard_xupdate:compute_hash(El),
State#state{config = Config#config{vcard_xupdate = Hash}}
end;
set_vcard_xupdate(State) ->
State.
-define(MAKE_CONFIG_OPT(Opt),
{get_config_opt_name(Opt), element(Opt, Config)}).
@@ -3568,6 +3707,7 @@ make_opts(StateData) ->
?MAKE_CONFIG_OPT(#config.presence_broadcast),
?MAKE_CONFIG_OPT(#config.voice_request_min_interval),
?MAKE_CONFIG_OPT(#config.vcard),
?MAKE_CONFIG_OPT(#config.vcard_xupdate),
?MAKE_CONFIG_OPT(#config.pubsub),
{captcha_whitelist,
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
@@ -3577,6 +3717,35 @@ make_opts(StateData) ->
{subject_author, StateData#state.subject_author},
{subscribers, Subscribers}].
expand_opts(CompactOpts) ->
DefConfig = #config{},
Fields = record_info(fields, config),
{_, Opts1} =
lists:foldl(
fun(Field, {Pos, Opts}) ->
case lists:keyfind(Field, 1, CompactOpts) of
false ->
DefV = element(Pos, DefConfig),
DefVal = case (?SETS):is_set(DefV) of
true -> (?SETS):to_list(DefV);
false -> DefV
end,
{Pos+1, [{Field, DefVal}|Opts]};
{_, Val} ->
{Pos+1, [{Field, Val}|Opts]}
end
end, {2, []}, Fields),
SubjectAuthor = proplists:get_value(subject_author, CompactOpts, <<"">>),
Subject = proplists:get_value(subject, CompactOpts, <<"">>),
Subscribers = proplists:get_value(subscribers, CompactOpts, []),
[{subject, Subject},
{subject_author, SubjectAuthor},
{subscribers, Subscribers}
| lists:reverse(Opts1)].
config_fields() ->
[subject, subject_author, subscribers | record_info(fields, config)].
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
destroy_room(DEl, StateData) ->
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
@@ -3611,12 +3780,8 @@ destroy_room(DEl, StateData) ->
false -> Fiffalse
end).
-spec process_iq_disco_info(jid(), iq(), state()) ->
{result, disco_info()} | {error, stanza_error()}.
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
{error, xmpp:err_not_allowed(Txt, Lang)};
process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
-spec make_disco_info(jid(), state()) -> disco_info().
make_disco_info(_From, StateData) ->
Config = StateData#state.config,
Feats = [?NS_VCARD, ?NS_MUC,
?CONFIG_OPT_TO_FEATURE((Config#config.public),
@@ -3642,16 +3807,40 @@ process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
_ ->
[]
end,
{result, #disco_info{xdata = [iq_disco_info_extras(Lang, StateData)],
identities = [#identity{category = <<"conference">>,
type = <<"text">>,
name = get_title(StateData)}],
features = Feats}}.
#disco_info{identities = [#identity{category = <<"conference">>,
type = <<"text">>,
name = get_title(StateData)}],
features = Feats}.
-spec process_iq_disco_info(jid(), iq(), state()) ->
{result, disco_info()} | {error, stanza_error()}.
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
{error, xmpp:err_not_allowed(Txt, Lang)};
process_iq_disco_info(From, #iq{type = get, lang = Lang,
sub_els = [#disco_info{node = <<>>}]},
StateData) ->
DiscoInfo = make_disco_info(From, StateData),
Extras = iq_disco_info_extras(Lang, StateData),
{result, DiscoInfo#disco_info{xdata = [Extras]}};
process_iq_disco_info(From, #iq{type = get, lang = Lang,
sub_els = [#disco_info{node = Node}]},
StateData) ->
try
true = mod_caps:is_valid_node(Node),
DiscoInfo = make_disco_info(From, StateData),
Hash = mod_caps:compute_disco_hash(DiscoInfo, sha),
Node = <<(?EJABBERD_URI)/binary, $#, Hash/binary>>,
{result, DiscoInfo#disco_info{node = Node}}
catch _:{badmatch, _} ->
Txt = <<"Invalid node name">>,
{error, xmpp:err_item_not_found(Txt, Lang)}
end.
-spec iq_disco_info_extras(binary(), state()) -> xdata().
iq_disco_info_extras(Lang, StateData) ->
Fs1 = [{description, (StateData#state.config)#config.description},
{occupants, ?DICT:size(StateData#state.users)},
{occupants, ?DICT:size(StateData#state.nicks)},
{contactjid, get_owners(StateData)}],
Fs2 = case (StateData#state.config)#config.pubsub of
Node when is_binary(Node), Node /= <<"">> ->
@@ -3712,13 +3901,15 @@ process_iq_vcard(_From, #iq{type = get}, StateData) ->
{error, _} ->
{error, xmpp:err_item_not_found()}
end;
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [SubEl]},
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [Pkt]},
StateData) ->
case get_affiliation(From, StateData) of
owner ->
VCardRaw = fxml:element_to_binary(xmpp:encode(SubEl)),
SubEl = xmpp:encode(Pkt),
VCardRaw = fxml:element_to_binary(SubEl),
Hash = mod_vcard_xupdate:compute_hash(SubEl),
Config = StateData#state.config,
NewConfig = Config#config{vcard = VCardRaw},
NewConfig = Config#config{vcard = VCardRaw, vcard_xupdate = Hash},
change_config(NewConfig, StateData);
_ ->
ErrText = <<"Owner privileges required">>,
@@ -3884,20 +4075,18 @@ get_roomdesc_tail(StateData, Lang) ->
true -> <<"">>;
_ -> translate:translate(Lang, <<"private, ">>)
end,
Len = (?DICT):size(StateData#state.users),
Len = (?DICT):size(StateData#state.nicks),
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
-spec get_mucroom_disco_items(state()) -> disco_items().
get_mucroom_disco_items(StateData) ->
Items = lists:map(
fun({_LJID, Info}) ->
Nick = Info#user.nick,
#disco_item{jid = jid:make(StateData#state.room,
StateData#state.host,
Nick),
name = Nick}
end,
(?DICT):to_list(StateData#state.users)),
Items = ?DICT:fold(
fun(Nick, _, Acc) ->
[#disco_item{jid = jid:make(StateData#state.room,
StateData#state.host,
Nick),
name = Nick}|Acc]
end, [], StateData#state.nicks),
#disco_items{items = Items}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -4111,7 +4300,7 @@ send_subscriptions_change_notifications(From, Nick, Type, State) ->
node = ?NS_MUCSUB_NODES_SUBSCRIBERS,
items = [#ps_item{
id = randoms:get_string(),
xml_els = [xmpp:encode(Payload)]}]}}]},
sub_els = [Payload]}]}}]},
ejabberd_router:route(xmpp:set_from_to(Packet, From, JID));
false ->
ok
@@ -4142,19 +4331,41 @@ send_wrapped(From, To, Packet, Node, State) ->
ok
end;
true ->
case Packet of
#presence{type = unavailable} ->
case xmpp:get_subtag(Packet, #muc_user{}) of
#muc_user{destroy = Destroy,
status_codes = Codes} ->
case Destroy /= undefined orelse
(lists:member(110,Codes) andalso
not lists:member(303, Codes)) of
true ->
ejabberd_router:route(
#presence{from = State#state.jid, to = To,
id = randoms:get_string(),
type = unavailable});
false ->
ok
end;
_ ->
false
end;
_ ->
ok
end,
ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
end.
-spec wrap(jid(), jid(), stanza(), binary()) -> message().
wrap(From, To, Packet, Node) ->
El = xmpp:encode(xmpp:set_from_to(Packet, From, To)),
El = xmpp:set_from_to(Packet, From, To),
#message{
sub_els = [#ps_event{
items = #ps_items{
node = Node,
items = [#ps_item{
id = randoms:get_string(),
xml_els = [El]}]}}]}.
sub_els = [El]}]}}]}.
%% -spec send_multiple(jid(), binary(), [#user{}], stanza()) -> ok.
%% send_multiple(From, Server, Users, Packet) ->
+249 -276
View File
@@ -40,59 +40,43 @@
-export([init/1, handle_info/2, handle_call/3,
handle_cast/2, terminate/2, code_change/3]).
-export([purge_loop/1, mod_opt_type/1, depends/2]).
-export([purge_loop/1, mod_opt_type/1, mod_options/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("translate.hrl").
-include("xmpp.hrl").
-record(state,
{lserver, lservice, access, service_limits}).
-record(multicastc, {rserver :: binary(),
response,
ts :: integer()}).
-record(dest, {jid_string :: binary() | none,
jid_jid :: xmpp:jid(),
type :: to | cc | bcc,
address :: address()}).
-type limit_value() :: {default | custom, integer()}.
-record(limits, {message :: limit_value(),
presence :: limit_value()}).
-record(service_limits, {local :: #limits{},
remote :: #limits{}}).
-type routing() :: route_single | {route_multicast, binary(), #service_limits{}}.
-record(group, {server :: binary(),
dests :: [#dest{}],
multicast :: routing(),
others :: [#address{}],
addresses :: [#address{}]}).
-record(state, {lserver :: binary(),
lservice :: binary(),
access :: atom(),
service_limits :: #service_limits{}}).
-type state() :: #state{}.
-record(multicastc, {rserver, response, ts}).
%% ts: timestamp (in seconds) when the cache item was last updated
-record(dest, {jid_string = none :: binary(),
jid_jid :: jid(),
type :: atom(),
full_xml :: address()}).
%% jid_string = string()
%% jid_jid = jid()
%% full_xml = xml()
-record(group,
{server, dests, multicast, others, addresses}).
%% server = string()
%% dests = [string()]
%% multicast = {cached, local_server} | {cached, string()} | {cached, not_supported} | {obsolete, not_supported} | {obsolete, string()} | not_cached
%% after being updated, possible values are: local | multicast_not_supported | {multicast_supported, string(), limits()}
%% others = [xml()]
%% packet = xml()
-record(waiter,
{awaiting, group, renewal = false, sender, packet,
aattrs, addresses}).
%% awaiting = {[Remote_service], Local_service, Type_awaiting}
%% Remote_service = Local_service = string()
%% Type_awaiting = info | items
%% group = #group
%% renewal = true | false
%% sender = From
%% packet = xml()
%% aattrs = [xml()]
-record(limits, {message, presence}).
%% message = presence = integer() | infinite
-record(service_limits, {local, remote}).
%% All the elements are of type value()
-define(VERSION_MULTICAST, <<"$Revision: 440 $ ">>).
@@ -104,6 +88,8 @@
-define(MAXTIME_CACHE_NEGATIVE, 86400).
-define(MAXTIME_CACHE_NEGOTIATING, 600).
-define(CACHE_PURGE_TIMER, 86400000).
-define(DISCO_QUERY_TIMEOUT, 10000).
@@ -130,15 +116,14 @@ reload(LServerS, NewOpts, OldOpts) ->
%% gen_server callbacks
%%====================================================================
-spec init(list()) -> {ok, state()}.
init([LServerS, Opts]) ->
process_flag(trap_exit, true),
LServiceS = gen_mod:get_opt_host(LServerS, Opts,
<<"multicast.@HOST@">>),
Access = gen_mod:get_opt(access, Opts, all),
SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts, [])),
[LServiceS|_] = gen_mod:get_opt_hosts(LServerS, Opts),
Access = gen_mod:get_opt(access, Opts),
SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts)),
create_cache(),
try_start_loop(),
create_pool(),
ejabberd_router_multicast:register_route(LServerS),
ejabberd_router:register_route(LServiceS, LServerS),
{ok,
@@ -150,10 +135,9 @@ handle_call(stop, _From, State) ->
handle_cast({reload, NewOpts, NewOpts},
#state{lserver = LServerS, lservice = OldLServiceS} = State) ->
Access = gen_mod:get_opt(access, NewOpts, all),
SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts, [])),
NewLServiceS = gen_mod:get_opt_host(LServerS, NewOpts,
<<"multicast.@HOST@">>),
Access = gen_mod:get_opt(access, NewOpts),
SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts)),
[NewLServiceS|_] = gen_mod:get_opt_hosts(LServerS, NewOpts),
if NewLServiceS /= OldLServiceS ->
ejabberd_router:register_route(NewLServiceS, LServerS),
ejabberd_router:unregister_route(OldLServiceS);
@@ -261,8 +245,7 @@ process_iq(_, _) ->
-define(FEATURE(Feat), Feat).
iq_disco_info(From, Lang, State) ->
Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE,
name, ?T("Multicast")),
Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE, name),
#disco_info{
identities = [#identity{category = <<"service">>,
type = <<"multicast">>,
@@ -280,21 +263,22 @@ iq_vcard(Lang) ->
%%% Route
%%%-------------------------
-spec route_trusted(binary(), binary(), jid(), [jid()], stanza()) -> 'ok'.
route_trusted(LServiceS, LServerS, FromJID,
Destinations, Packet) ->
Packet_stripped = Packet,
AAttrs = [],
Delivereds = [],
Dests2 = lists:map(
fun(D) ->
#dest{jid_string = jid:encode(D),
jid_jid = D, type = bcc,
full_xml = #address{type = bcc, jid = D}}
jid_jid = D, type = bcc,
address = #address{type = bcc, jid = D}}
end, Destinations),
Groups = group_dests(Dests2),
route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, AAttrs).
Delivereds, Packet_stripped).
-spec route_untrusted(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
try route_untrusted2(LServiceS, LServerS, Access,
SLimits, Packet)
@@ -324,6 +308,7 @@ route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
<<"Unknown problem">>)
end.
-spec route_untrusted2(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
FromJID = xmpp:get_from(Packet),
ok = check_access(LServerS, Access, FromJID),
@@ -336,53 +321,40 @@ route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
Groups = group_dests(Dests2),
ok = check_relay(FromJID#jid.server, LServerS, Groups),
route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, []).
Delivereds, Packet_stripped).
-spec route_common(binary(), binary(), jid(), [#group{}],
[address()], stanza(), list()) -> any().
[address()], stanza()) -> 'ok'.
route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, AAttrs) ->
Groups2 = look_cached_servers(LServerS, Groups),
Delivereds, Packet_stripped) ->
Groups2 = look_cached_servers(LServerS, LServiceS, Groups),
Groups3 = build_others_xml(Groups2),
Groups4 = add_addresses(Delivereds, Groups3),
AGroups = decide_action_groups(Groups4),
act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
act_groups(FromJID, Packet_stripped, LServiceS,
AGroups).
act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
AGroups) ->
[perform(FromJID, Packet_stripped, AAttrs, LServiceS,
AGroup)
|| AGroup <- AGroups].
-spec act_groups(jid(), stanza(), binary(), [{routing(), #group{}}]) -> 'ok'.
act_groups(FromJID, Packet_stripped, LServiceS, AGroups) ->
lists:foreach(
fun(AGroup) ->
perform(FromJID, Packet_stripped, LServiceS,
AGroup)
end, AGroups).
perform(From, Packet, AAttrs, _,
-spec perform(jid(), stanza(), binary(),
{routing(), #group{}}) -> 'ok'.
perform(From, Packet, _,
{route_single, Group}) ->
[route_packet(From, ToUser, Packet, AAttrs,
Group#group.others, Group#group.addresses)
|| ToUser <- Group#group.dests];
perform(From, Packet, AAttrs, _,
lists:foreach(
fun(ToUser) ->
route_packet(From, ToUser, Packet,
Group#group.others, Group#group.addresses)
end, Group#group.dests);
perform(From, Packet, _,
{{route_multicast, JID, RLimits}, Group}) ->
route_packet_multicast(From, JID, Packet, AAttrs,
Group#group.dests, Group#group.addresses, RLimits);
perform(From, Packet, AAttrs, LServiceS,
{{ask, Old_service, renewal}, Group}) ->
send_query_info(Old_service, LServiceS),
add_waiter(#waiter{awaiting =
{[Old_service], LServiceS, info},
group = Group, renewal = true, sender = From,
packet = Packet, aattrs = AAttrs,
addresses = Group#group.addresses});
perform(_From, _Packet, _AAttrs, LServiceS,
{{ask, LServiceS, _}, _Group}) ->
ok;
perform(From, Packet, AAttrs, LServiceS,
{{ask, Server, not_renewal}, Group}) ->
send_query_info(Server, LServiceS),
add_waiter(#waiter{awaiting =
{[Server], LServiceS, info},
group = Group, renewal = false, sender = From,
packet = Packet, aattrs = AAttrs,
addresses = Group#group.addresses}).
route_packet_multicast(From, JID, Packet,
Group#group.dests, Group#group.addresses, RLimits).
%%%-------------------------
%%% Check access permission
@@ -430,7 +402,7 @@ split_addresses_todeliver(Addresses) ->
%%% Check does not exceed limit of destinations
%%%-------------------------
-spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok.
-spec check_limit_dests(#service_limits{}, jid(), stanza(), [address()]) -> ok.
check_limit_dests(SLimits, FromJID, Packet,
Addresses) ->
SenderT = sender_type(FromJID),
@@ -451,10 +423,10 @@ check_limit_dests(SLimits, FromJID, Packet,
convert_dest_record(Addrs) ->
lists:map(
fun(#address{jid = undefined} = Addr) ->
#dest{jid_string = none, full_xml = Addr};
#dest{jid_string = none, address = Addr};
(#address{jid = JID, type = Type} = Addr) ->
#dest{jid_string = jid:encode(JID), jid_jid = JID,
type = Type, full_xml = Addr}
type = Type, address = Addr}
end, Addrs).
%%%-------------------------
@@ -472,9 +444,9 @@ split_dests_jid(Dests) ->
end,
Dests).
-spec report_not_jid(jid(), stanza(), #dest{}) -> any().
-spec report_not_jid(jid(), stanza(), [#dest{}]) -> any().
report_not_jid(From, Packet, Dests) ->
Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml))
Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.address))
|| Dest <- Dests],
[route_error(xmpp:set_from_to(Packet, From, From), jid_malformed,
<<"This service can not process the address: ",
@@ -500,14 +472,14 @@ group_dests(Dests) ->
%%% Look for cached responses
%%%-------------------------
look_cached_servers(LServerS, Groups) ->
[look_cached(LServerS, Group) || Group <- Groups].
look_cached_servers(LServerS, LServiceS, Groups) ->
[look_cached(LServerS, LServiceS, Group) || Group <- Groups].
look_cached(LServerS, G) ->
look_cached(LServerS, LServiceS, G) ->
Maxtime_positive = (?MAXTIME_CACHE_POSITIVE),
Maxtime_negative = (?MAXTIME_CACHE_NEGATIVE),
Cached_response = search_server_on_cache(G#group.server,
LServerS,
LServerS, LServiceS,
{Maxtime_positive,
Maxtime_negative}),
G#group{multicast = Cached_response}.
@@ -523,7 +495,7 @@ build_others_xml(Groups) ->
build_other_xml(Dests) ->
lists:foldl(fun (Dest, R) ->
XML = Dest#dest.full_xml,
XML = Dest#dest.address,
case Dest#dest.type of
to -> [add_delivered(XML) | R];
cc -> [add_delivered(XML) | R];
@@ -557,53 +529,38 @@ add_addresses2(Delivereds, [Group | Groups], Res, Pa,
%%% Decide action groups
%%%-------------------------
-spec decide_action_groups([#group{}]) -> [{routing(), #group{}}].
decide_action_groups(Groups) ->
[{decide_action_group(Group), Group}
[{Group#group.multicast, Group}
|| Group <- Groups].
decide_action_group(Group) ->
Server = Group#group.server,
case Group#group.multicast of
{cached, local_server} ->
%% Send a copy of the packet to each local user on Dests
route_single;
{cached, not_supported} ->
%% Send a copy of the packet to each remote user on Dests
route_single;
{cached, {multicast_supported, JID, RLimits}} ->
{route_multicast, JID, RLimits};
{obsolete,
{multicast_supported, Old_service, _RLimits}} ->
{ask, Old_service, renewal};
{obsolete, not_supported} -> {ask, Server, not_renewal};
not_cached -> {ask, Server, not_renewal}
end.
%%%-------------------------
%%% Route packet
%%%-------------------------
route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
-spec route_packet(jid(), #dest{}, xmpp:stanza(), [addresses()], [addresses()]) -> 'ok'.
route_packet(From, ToDest, Packet, Others, Addresses) ->
Dests = case ToDest#dest.type of
bcc -> [];
_ -> [ToDest]
end,
route_packet2(From, ToDest#dest.jid_string, Dests,
Packet, AAttrs, {Others, Addresses}).
Packet, {Others, Addresses}).
route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
-spec route_packet_multicast(jid(), binary(), xmpp:stanza(), [#dest{}], [address()], #limits{}) -> 'ok'.
route_packet_multicast(From, ToS, Packet, Dests,
Addresses, Limits) ->
Type_of_stanza = type_of_stanza(Packet),
{_Type, Limit_number} = get_limit_number(Type_of_stanza,
Limits),
Fragmented_dests = fragment_dests(Dests, Limit_number),
[route_packet2(From, ToS, DFragment, Packet, AAttrs,
Addresses)
|| DFragment <- Fragmented_dests].
lists:foreach(fun(DFragment) ->
route_packet2(From, ToS, DFragment, Packet,
Addresses)
end, Fragmented_dests).
-spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok.
route_packet2(From, ToS, Dests, Packet, _AAttrs,
Addresses) ->
-spec route_packet2(jid(), binary(), [#dest{}], xmpp:stanza(), {[address()], [address()]} | [address()]) -> 'ok'.
route_packet2(From, ToS, Dests, Packet, Addresses) ->
Els = case append_dests(Dests, Addresses) of
[] ->
xmpp:get_els(Packet);
@@ -616,10 +573,10 @@ route_packet2(From, ToS, Dests, Packet, _AAttrs,
-spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()].
append_dests(_Dests, {Others, Addresses}) ->
Addresses++Others;
Addresses ++ Others;
append_dests([], Addresses) -> Addresses;
append_dests([Dest | Dests], Addresses) ->
append_dests(Dests, [Dest#dest.full_xml | Addresses]).
append_dests(Dests, [Dest#dest.address | Addresses]).
%%%-------------------------
%%% Check relay
@@ -650,20 +607,22 @@ check_relay_required(LServerS, Groups) ->
%%% Check protocol support: Send request
%%%-------------------------
send_query_info(RServerS, LServiceS) ->
-spec send_query_info(binary(), binary(), binary()) -> ok.
send_query_info(RServerS, LServiceS, ID) ->
case str:str(RServerS, <<"echo.">>) of
1 -> false;
_ -> send_query(RServerS, LServiceS, #disco_info{})
1 -> ok;
_ -> send_query(RServerS, LServiceS, ID, #disco_info{})
end.
send_query_items(RServerS, LServiceS) ->
send_query(RServerS, LServiceS, #disco_items{}).
-spec send_query_items(binary(), binary(), binary()) -> ok.
send_query_items(RServerS, LServiceS, ID) ->
send_query(RServerS, LServiceS, ID, #disco_items{}).
-spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok.
send_query(RServerS, LServiceS, SubEl) ->
-spec send_query(binary(), binary(), binary(), disco_info()|disco_items()) -> ok.
send_query(RServerS, LServiceS, ID, SubEl) ->
Packet = #iq{from = stj(LServiceS),
to = stj(RServerS),
id = randoms:get_string(),
id = ID,
type = get, sub_els = [SubEl]},
ejabberd_router:route(Packet).
@@ -673,10 +632,31 @@ send_query(RServerS, LServiceS, SubEl) ->
process_iqreply_error(LServiceS, Packet) ->
FromS = jts(xmpp:get_from(Packet)),
case search_waiter(FromS, LServiceS, info) of
{found_waiter, Waiter} ->
received_awaiter(FromS, Waiter, LServiceS);
_ -> ok
ID = Packet#iq.id,
case str:tokens(ID, <<"/">>) of
[RServer, _] ->
case look_server(RServer) of
{cached, {_Response, {wait_for_info, ID}}, _TS}
when RServer == FromS ->
add_response(RServer, not_supported, cached);
{cached, {_Response, {wait_for_items, ID}}, _TS}
when RServer == FromS ->
add_response(RServer, not_supported, cached);
{cached, {Response, {wait_for_items_info, ID, Items}},
_TS} ->
case lists:member(FromS, Items) of
true ->
received_awaiter(
FromS, RServer, Response, ID, Items,
LServiceS);
false ->
ok
end;
_ ->
ok
end;
_ ->
ok
end.
%%%-------------------------
@@ -684,12 +664,12 @@ process_iqreply_error(LServiceS, Packet) ->
%%%-------------------------
-spec process_iqreply_result(binary(), iq()) -> any().
process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
process_iqreply_result(LServiceS, #iq{from = From, id = ID, sub_els = [SubEl]}) ->
case SubEl of
#disco_info{} ->
process_discoinfo_result(From, LServiceS, SubEl);
process_discoinfo_result(From, LServiceS, ID, SubEl);
#disco_items{} ->
process_discoitems_result(From, LServiceS, SubEl);
process_discoitems_result(From, LServiceS, ID, SubEl);
_ ->
ok
end.
@@ -698,46 +678,53 @@ process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
%%% Check protocol support: Receive response: Disco Info
%%%-------------------------
process_discoinfo_result(From, LServiceS, DiscoInfo) ->
process_discoinfo_result(From, LServiceS, ID, DiscoInfo) ->
FromS = jts(From),
case search_waiter(FromS, LServiceS, info) of
{found_waiter, Waiter} ->
process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo,
Waiter);
_ -> ok
case str:tokens(ID, <<"/">>) of
[RServer, _] ->
case look_server(RServer) of
{cached, {Response, {wait_for_info, ID} = ST}, _TS}
when RServer == FromS ->
process_discoinfo_result2(
From, FromS, LServiceS, DiscoInfo,
RServer, Response, ST);
{cached, {Response, {wait_for_items_info, ID, Items} = ST},
_TS} ->
case lists:member(FromS, Items) of
true ->
process_discoinfo_result2(
From, FromS, LServiceS, DiscoInfo,
RServer, Response, ST);
false ->
ok
end;
_ ->
ok
end;
_ ->
ok
end.
process_discoinfo_result2(From, FromS, LServiceS,
#disco_info{features = Feats} = DiscoInfo,
Waiter) ->
RServer, Response, ST) ->
Multicast_support = lists:member(?NS_ADDRESS, Feats),
Group = Waiter#waiter.group,
RServer = Group#group.server,
case Multicast_support of
true ->
SenderT = sender_type(From),
RLimits = get_limits_xml(DiscoInfo, SenderT),
add_response(RServer, {multicast_supported, FromS, RLimits}),
FromM = Waiter#waiter.sender,
DestsM = Group#group.dests,
PacketM = Waiter#waiter.packet,
AAttrsM = Waiter#waiter.aattrs,
AddressesM = Waiter#waiter.addresses,
RServiceM = FromS,
route_packet_multicast(FromM, RServiceM, PacketM,
AAttrsM, DestsM, AddressesM, RLimits),
delo_waiter(Waiter);
add_response(RServer, {multicast_supported, FromS, RLimits}, cached);
false ->
case FromS of
RServer ->
send_query_items(FromS, LServiceS),
delo_waiter(Waiter),
add_waiter(Waiter#waiter{awaiting =
{[FromS], LServiceS, items},
renewal = false});
%% We asked a component, and it does not support XEP33
_ -> received_awaiter(FromS, Waiter, LServiceS)
end
case ST of
{wait_for_info, _ID} ->
Random = randoms:get_string(),
ID = <<RServer/binary, $/, Random/binary>>,
send_query_items(FromS, LServiceS, ID),
add_response(RServer, Response, {wait_for_items, ID});
%% We asked a component, and it does not support XEP33
{wait_for_items_info, ID, Items} ->
received_awaiter(FromS, RServer, Response, ID, Items, LServiceS)
end
end.
get_limits_xml(DiscoInfo, SenderT) ->
@@ -781,26 +768,32 @@ get_limits_values(Fields) ->
%%% Check protocol support: Receive response: Disco Items
%%%-------------------------
process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
process_discoitems_result(From, LServiceS, ID, #disco_items{items = Items}) ->
FromS = jts(From),
case search_waiter(FromS, LServiceS, items) of
{found_waiter, Waiter} ->
List = lists:flatmap(
fun(#disco_item{jid = #jid{luser = <<"">>,
lresource = <<"">>} = J}) ->
[J];
(_) ->
[]
end, Items),
case List of
[] ->
received_awaiter(FromS, Waiter, LServiceS);
case str:tokens(ID, <<"/">>) of
[FromS = RServer, _] ->
case look_server(RServer) of
{cached, {Response, {wait_for_items, ID}}, _TS} ->
List = lists:flatmap(
fun(#disco_item{jid = #jid{luser = <<"">>,
lserver = LServer,
lresource = <<"">>}}) ->
[LServer];
(_) ->
[]
end, Items),
case List of
[] ->
add_response(RServer, not_supported, cached);
_ ->
Random = randoms:get_string(),
ID2 = <<RServer/binary, $/, Random/binary>>,
[send_query_info(Item, LServiceS, ID2) || Item <- List],
add_response(RServer, Response,
{wait_for_items_info, ID2, List})
end;
_ ->
[send_query_info(Item, LServiceS) || Item <- List],
delo_waiter(Waiter),
add_waiter(Waiter#waiter{awaiting =
{List, LServiceS, info},
renewal = false})
ok
end;
_ ->
ok
@@ -810,33 +803,12 @@ process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
%%% Check protocol support: Receive response: Received awaiter
%%%-------------------------
received_awaiter(JID, Waiter, LServiceS) ->
{JIDs, LServiceS, _} = Waiter#waiter.awaiting,
delo_waiter(Waiter),
Group = Waiter#waiter.group,
RServer = Group#group.server,
received_awaiter(JID, RServer, Response, ID, JIDs, _LServiceS) ->
case lists:delete(JID, JIDs) of
[] ->
case Waiter#waiter.renewal of
false ->
add_response(RServer, not_supported),
From = Waiter#waiter.sender,
Packet = Waiter#waiter.packet,
AAttrs = Waiter#waiter.aattrs,
Others = Group#group.others,
Addresses = Waiter#waiter.addresses,
[route_packet(From, ToUser, Packet, AAttrs, Others, Addresses)
|| ToUser <- Group#group.dests];
true ->
send_query_info(RServer, LServiceS),
add_waiter(Waiter#waiter{awaiting =
{[RServer], LServiceS, info},
renewal = false})
end;
JIDs2 ->
add_waiter(Waiter#waiter{awaiting =
{JIDs2, LServiceS, info},
renewal = false})
[] ->
add_response(RServer, not_supported, cached);
JIDs2 ->
add_response(RServer, Response, {wait_for_items_info, ID, JIDs2})
end.
%%%-------------------------
@@ -848,25 +820,52 @@ create_cache() ->
[{ram_copies, [node()]},
{attributes, record_info(fields, multicastc)}]).
add_response(RServer, Response) ->
add_response(RServer, Response, State) ->
Secs = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
mnesia:dirty_write(#multicastc{rserver = RServer,
response = Response, ts = Secs}).
response = {Response, State}, ts = Secs}).
search_server_on_cache(RServer, LServerS, _Maxmins)
search_server_on_cache(RServer, LServerS, _LServiceS, _Maxmins)
when RServer == LServerS ->
{cached, local_server};
search_server_on_cache(RServer, _LServerS, Maxmins) ->
route_single;
search_server_on_cache(RServer, _LServerS, LServiceS, Maxmins) ->
case look_server(RServer) of
not_cached -> not_cached;
{cached, Response, Ts} ->
Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
case is_obsolete(Response, Ts, Now, Maxmins) of
false -> {cached, Response};
true -> {obsolete, Response}
end
not_cached ->
query_info(RServer, LServiceS, not_supported),
route_single;
{cached, {Response, State}, TS} ->
Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
Response2 =
case State of
cached ->
case is_obsolete(Response, TS, Now, Maxmins) of
false -> ok;
true ->
query_info(RServer, LServiceS, Response)
end,
Response;
_ ->
if
Now - TS > ?MAXTIME_CACHE_NEGOTIATING ->
query_info(RServer, LServiceS, not_supported),
not_supported;
true ->
Response
end
end,
case Response2 of
not_supported -> route_single;
{multicast_supported, Service, Limits} ->
{route_multicast, Service, Limits}
end
end.
query_info(RServer, LServiceS, Response) ->
Random = randoms:get_string(),
ID = <<RServer/binary, $/, Random/binary>>,
send_query_info(RServer, LServiceS, ID),
add_response(RServer, Response, {wait_for_info, ID}).
look_server(RServer) ->
case mnesia:dirty_read(multicastc, RServer) of
[] -> not_cached;
@@ -937,44 +936,6 @@ purge_loop(NM) ->
try_stop -> purge_loop_finished
end.
%%%-------------------------
%%% Pool
%%%-------------------------
create_pool() ->
catch
begin
ets:new(multicastp,
[duplicate_bag, public, named_table, {keypos, 2}]),
ets:give_away(multicastp, whereis(ejabberd), ok)
end.
add_waiter(Waiter) ->
true = ets:insert(multicastp, Waiter).
delo_waiter(Waiter) ->
true = ets:delete_object(multicastp, Waiter).
-spec search_waiter(binary(), binary(), info | items) ->
{found_waiter, #waiter{}} | waiter_not_found.
search_waiter(JID, LServiceS, Type) ->
Rs = ets:foldl(fun (W, Res) ->
{JIDs, LServiceS1, Type1} = W#waiter.awaiting,
case lists:member(JID, JIDs) and
(LServiceS == LServiceS1)
and (Type1 == Type)
of
true -> Res ++ [W];
false -> Res
end
end,
[], multicastp),
case Rs of
[R | _] -> {found_waiter, R};
[] -> waiter_not_found
end.
%%%-------------------------
%%% Limits: utils
%%%-------------------------
@@ -1008,11 +969,13 @@ get_from_limitopts(LimitOpts, SenderT) ->
build_remote_limit_record(LimitOpts, SenderT) ->
build_limit_record(LimitOpts, SenderT).
-spec build_limit_record(any(), local | remote) -> #limits{}.
build_limit_record(LimitOpts, SenderT) ->
Limits = [get_limit_value(Name, Default, LimitOpts)
|| {Name, Default} <- list_of_limits(SenderT)],
list_to_tuple([limits | Limits]).
-spec get_limit_value(atom(), integer(), any()) -> limit_value().
get_limit_value(Name, Default, LimitOpts) ->
case lists:keysearch(Name, 1, LimitOpts) of
{value, {Name, Number}} -> {custom, Number};
@@ -1021,11 +984,13 @@ get_limit_value(Name, Default, LimitOpts) ->
type_of_stanza(Stanza) -> element(1, Stanza).
-spec get_limit_number(message | presence, #limits{}) -> limit_value().
get_limit_number(message, Limits) ->
Limits#limits.message;
get_limit_number(presence, Limits) ->
Limits#limits.presence.
-spec get_slimit_group(local | remote, #service_limits{}) -> #limits{}.
get_slimit_group(local, SLimits) ->
SLimits#service_limits.local;
get_slimit_group(remote, SLimits) ->
@@ -1121,6 +1086,8 @@ depends(_Host, _Opts) ->
mod_opt_type(access) ->
fun acl:access_rules_validator/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(hosts) ->
fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(name) -> fun iolist_to_binary/1;
mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) ->
fun(L) ->
@@ -1130,5 +1097,11 @@ mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) ->
({message, I} = O) when is_integer(I) -> O;
({presence, I} = O) when is_integer(I) -> O
end, L)
end;
mod_opt_type(_) -> [access, host, {limits, local}, {limits, remote}, name].
end.
mod_options(_Host) ->
[{access, all},
{host, <<"multicast.@HOST@">>},
{hosts, []},
{limits, [{local, []}, {remote, []}]},
{name, ?T("Multicast")}].
+14 -19
View File
@@ -63,7 +63,7 @@
webadmin_user/4,
webadmin_user_parse_query/5]).
-export([mod_opt_type/1, depends/2]).
-export([mod_opt_type/1, mod_options/1, depends/2]).
-deprecated({get_queue_length,2}).
@@ -108,7 +108,6 @@ depends(_Host, _Opts) ->
start(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
store_packet, 50),
ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50),
@@ -132,7 +131,7 @@ start(Host, Opts) ->
ejabberd_hooks:add(webadmin_user_parse_query, Host,
?MODULE, webadmin_user_parse_query, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE,
?MODULE, handle_offline_query, IQDisc).
?MODULE, handle_offline_query).
stop(Host) ->
ejabberd_hooks:delete(offline_message_hook, Host,
@@ -162,13 +161,6 @@ reload(Host, NewOpts, OldOpts) ->
NewMod:init(Host, NewOpts);
true ->
ok
end,
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE,
?MODULE, handle_offline_query, IQDisc);
true ->
ok
end.
-spec store_offline_msg(#offline_msg{}) -> ok | {error, full | any()}.
@@ -187,8 +179,7 @@ store_offline_msg(#offline_msg{us = {User, Server}} = Msg) ->
end.
get_max_user_messages(User, Server) ->
Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages,
max_user_offline_messages),
Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages),
case acl:match_rule(Server, Access, jid:make(User, Server)) of
Max when is_integer(Max) -> Max;
infinity -> infinity;
@@ -388,8 +379,7 @@ need_to_store(LServer, #message{type = Type} = Packet) ->
false;
none ->
case gen_mod:get_module_opt(
LServer, ?MODULE, store_empty_body,
unless_chat_state) of
LServer, ?MODULE, store_empty_body) of
true ->
true;
false ->
@@ -598,7 +588,8 @@ get_offline_els(LUser, LServer) ->
-spec offline_msg_to_route(binary(), #offline_msg{}) ->
{route, message()} | error.
offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, [ignore_els]) of
CodecOpts = ejabberd_config:codec_options(LServer),
try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, CodecOpts) of
Pkt ->
Pkt1 = xmpp:set_from_to(Pkt, From, To),
Pkt2 = add_delay_info(Pkt1, LServer, R#offline_msg.timestamp),
@@ -613,10 +604,11 @@ offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
-spec read_messages(binary(), binary()) -> [{binary(), message()}].
read_messages(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
CodecOpts = ejabberd_config:codec_options(LServer),
lists:flatmap(
fun({Seq, From, To, TS, El}) ->
Node = integer_to_binary(Seq),
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
Pkt ->
Node = integer_to_binary(Seq),
Pkt1 = add_delay_info(Pkt, LServer, TS),
@@ -849,6 +841,9 @@ mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(store_empty_body) ->
fun (V) when is_boolean(V) -> V;
(unless_chat_state) -> unless_chat_state
end;
mod_opt_type(_) ->
[access_max_user_messages, db_type, store_empty_body].
end.
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{access_max_user_messages, max_user_offline_messages},
{store_empty_body, unless_chat_state}].
+26 -30
View File
@@ -29,19 +29,15 @@
-protocol({xep, 199, '2.0'}).
-behavior(gen_mod).
-behaviour(gen_mod).
-behavior(gen_server).
-behaviour(gen_server).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
-define(DEFAULT_SEND_PINGS, false).
-define(DEFAULT_PING_INTERVAL, 60).
%% API
-export([start_ping/2, stop_ping/2]).
@@ -53,14 +49,14 @@
handle_cast/2, handle_info/2, code_change/3]).
-export([iq_ping/1, user_online/3, user_offline/3,
user_send/1, mod_opt_type/1, depends/2]).
user_send/1, mod_opt_type/1, mod_options/1, depends/2]).
-record(state,
{host = <<"">>,
send_pings = ?DEFAULT_SEND_PINGS :: boolean(),
ping_interval = ?DEFAULT_PING_INTERVAL :: non_neg_integer(),
ping_ack_timeout = undefined :: non_neg_integer(),
timeout_action = none :: none | kill,
{host = <<"">> :: binary(),
send_pings :: boolean(),
ping_interval :: non_neg_integer(),
ping_ack_timeout :: undefined | non_neg_integer(),
timeout_action ::none | kill,
timers = maps:new() :: map()}).
%%====================================================================
@@ -95,8 +91,7 @@ reload(Host, NewOpts, OldOpts) ->
init([Host, Opts]) ->
process_flag(trap_exit, true),
State = init_state(Host, Opts),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
register_iq_handlers(Host, IQDisc),
register_iq_handlers(Host),
case State#state.send_pings of
true -> register_hooks(Host);
false -> ok
@@ -112,12 +107,8 @@ handle_call(stop, _From, State) ->
handle_call(_Req, _From, State) ->
{reply, {error, badarg}, State}.
handle_cast({reload, Host, NewOpts, OldOpts},
handle_cast({reload, Host, NewOpts, _OldOpts},
#state{timers = Timers} = OldState) ->
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} -> register_iq_handlers(Host, IQDisc);
true -> ok
end,
NewState = init_state(Host, NewOpts),
case {NewState#state.send_pings, OldState#state.send_pings} of
{true, false} -> register_hooks(Host);
@@ -196,10 +187,10 @@ user_send({Packet, #{jid := JID} = C2SState}) ->
%% Internal functions
%%====================================================================
init_state(Host, Opts) ->
SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS),
PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL),
SendPings = gen_mod:get_opt(send_pings, Opts),
PingInterval = gen_mod:get_opt(ping_interval, Opts),
PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts),
TimeoutAction = gen_mod:get_opt(timeout_action, Opts, none),
TimeoutAction = gen_mod:get_opt(timeout_action, Opts),
#state{host = Host,
send_pings = SendPings,
ping_interval = PingInterval,
@@ -223,11 +214,11 @@ unregister_hooks(Host) ->
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
user_send, 100).
register_iq_handlers(Host, IQDisc) ->
register_iq_handlers(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING,
?MODULE, iq_ping, IQDisc),
?MODULE, iq_ping),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PING,
?MODULE, iq_ping, IQDisc).
?MODULE, iq_ping).
unregister_iq_handlers(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PING),
@@ -267,16 +258,21 @@ cancel_timer(TRef) ->
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(ping_interval) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(ping_ack_timeout) ->
fun(I) when is_integer(I), I>0 -> timer:seconds(I) end;
fun(undefined) -> undefined;
(I) when is_integer(I), I>0 -> timer:seconds(I)
end;
mod_opt_type(send_pings) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(timeout_action) ->
fun (none) -> none;
(kill) -> kill
end;
mod_opt_type(_) ->
[iqdisc, ping_interval, ping_ack_timeout, send_pings, timeout_action].
end.
mod_options(_Host) ->
[{ping_interval, 60},
{ping_ack_timeout, undefined},
{send_pings, false},
{timeout_action, none}].

Some files were not shown because too many files have changed in this diff Show More