Compare commits

..

252 Commits

Author SHA1 Message Date
Christophe Romain 639c2fb640 Add pubsub subscribe/unsubscribe hook 2016-04-28 15:57:55 +02:00
Evgeniy Khramtsov c585f74730 Better formatting of configuration problem log message 2016-04-28 09:03:05 +03:00
Mickael Remond d18fe138aa Update esip and stun to match Fast TLS version 2016-04-27 16:22:47 +02:00
Christophe Romain d6f02a51e4 Use fast_tls 1.0.3 2016-04-27 16:18:24 +02:00
Evgeniy Khramtsov 9c369b7a8c Improve detection of databases supported by modules (#1092) 2016-04-27 17:10:50 +03:00
Christophe Romain d8bb5d9c01 Force ERL_PATH for elixir 2016-04-27 12:32:01 +02:00
Mickael Remond 26d3a978cc Update stun and esip dependencies 2016-04-27 12:19:55 +02:00
Mickael Remond 0f06ed8a9b Prepare 16.04 release 2016-04-27 11:59:05 +02:00
Mickael Remond d1db9f92c4 We now use fast_tls 1.0.2 2016-04-27 11:54:50 +02:00
Evgeniy Khramtsov 52fde758b3 Get rid of "internal" DB type. This also fixes #1092 2016-04-27 09:44:32 +03:00
Christophe Romain b79aef3bbc SCRIPT_DIR needed for release 2016-04-25 15:26:23 +02:00
Christophe Romain bae24464d3 Remove useless variable and quote EPMD and SPOOL_DIR 2016-04-25 14:26:04 +02:00
Christophe Romain ef90a389c1 Fix use of pubsub node plugin when configured with default_node_config 2016-04-25 09:44:46 +02:00
Holger Weiss 65ad70d7dc Fix error text for message bounces 2016-04-25 00:53:48 +02:00
Holger Weiss a37cf33358 Drop headline messages sent to offline resources
Don't bounce an error when a message of type "headline" is sent to an
unavailable resource.  This is consistent with how headline messages
sent to the bare JID of an offline user are dropped, and it avoids a
presence leak.
2016-04-25 00:02:12 +02:00
Holger Weiss 58478e52bf Don't omit bounces for messages of type "result" 2016-04-24 22:47:53 +02:00
Holger Weiss b79f09d0eb Match namespace when checking for chat states
When checking for standalone chat states, match the namespace rather
than the names of the elements defined in the current XEP-0085 revision.
2016-04-24 17:16:28 +02:00
Holger Weiss beeb1c82d9 Fix check for standalone chat state notifications
Ignore whitespace (and other XML CDATA) when checking whether a message
stanza is a standalone chat state notification.
2016-04-24 17:09:56 +02:00
badlop 2660dcabba Merge pull request #931 from cclam0827/dev/mod_ping
change mod_ping Timers using maps instead of dict
2016-04-22 13:07:03 +02:00
Holger Weiss f0e6def3ed Set default value for pubsub#itemreply option 2016-04-21 23:47:07 +02:00
Christophe Romain 8487b51ecd Fix node ping command 2016-04-21 12:16:21 +02:00
Christophe Romain f7d4aae64d Use UUID for ctl node name (#1021) 2016-04-21 12:00:51 +02:00
Paweł Chmielowski 97e3a33077 Accept commands: add_commands syntax (along commands: - add_commands) 2016-04-21 11:16:36 +02:00
Evgeniy Khramtsov 1aae8a9fda Rename odbc to sql everywhere 2016-04-20 13:25:42 +03:00
Evgeniy Khramtsov fafeeb80c2 Rename ejabberd_sm_odbc -> ejabberd_sm_sql 2016-04-20 09:11:02 +03:00
Christophe Romain 655c22021b Fix hometree root check (#1070) 2016-04-19 15:18:32 +02:00
Holger Weiss 382c6ce1fb Specify type of second terminate/2 parameter 2016-04-19 09:15:09 +02:00
Paweł Chmielowski 0bdcafdb02 Use erlang 18.3 in travis tests 2016-04-18 14:25:41 +02:00
Badlop e5e4f39c01 Remove --auth in ejabberd_ctl.erl as it's useless, still useful for mod_rest 2016-04-15 15:35:57 +02:00
Evgeniy Khramtsov 222572bd56 Clean mod_carboncopy.erl from DB specific code 2016-04-15 15:48:56 +03:00
Evgeniy Khramtsov 64fdbe7866 Add mod_mam header file 2016-04-15 15:13:38 +03:00
Evgeniy Khramtsov fb0ecf3361 Merge branch 'move-db-code' 2016-04-15 15:12:12 +03:00
Evgeniy Khramtsov 52571e8624 Clean mod_mam.erl from DB specific code 2016-04-15 15:11:31 +03:00
Evgeniy Khramtsov 901d2e0aed Clean mod_offline.erl from DB specific code 2016-04-15 13:44:33 +03:00
Evgeniy Khramtsov 860db2ddca Clean mod_blocking.erl from DB specific code 2016-04-14 14:17:20 +03:00
Evgeniy Khramtsov c8c4a41b66 Clean mod_privacy.erl from DB specific code 2016-04-14 14:16:32 +03:00
Evgeniy Khramtsov 79d64e0d71 Clean mod_irc.erl from DB specific code 2016-04-14 12:18:04 +03:00
Evgeniy Khramtsov f8e3560ad2 Clean mod_shared_roster.erl from DB specific code 2016-04-14 11:45:43 +03:00
Evgeniy Khramtsov 398f1de5ae Clean mod_roster.erl from DB specific code 2016-04-14 10:58:32 +03:00
Evgeniy Khramtsov 0b439a7d5b Clean mod_muc.erl from DB specific code 2016-04-13 21:07:32 +03:00
Evgeniy Khramtsov ae69f09257 Clean mod_vcard.erl from DB specific code 2016-04-13 17:37:52 +03:00
Evgeniy Khramtsov ef70ce65ab Clean mod_private.erl from DB specific code 2016-04-13 14:09:34 +03:00
Evgeniy Khramtsov b5d1ce795f Clean mod_announce.erl from DB specific code 2016-04-13 13:04:04 +03:00
Evgeniy Khramtsov cd094bc903 Clean mod_caps.erl from DB specific code 2016-04-13 11:41:04 +03:00
Evgeniy Khramtsov 2d7e03f5e1 Clean mod_vcard_xupdate.erl from DB specific code 2016-04-13 11:06:59 +03:00
Mickaël Rémond b2abc1edb7 Add preliminary tests on ACL module and prepare clean-up / refactor 2016-04-13 09:45:56 +02:00
Evgeniy Khramtsov 7fd4808cde Clean mod_last.erl from DB specific code 2016-04-13 09:59:39 +03:00
Evgeniy Khramtsov 5eef8a8bcf Make it possible to get DB backend of a module 2016-04-13 09:56:10 +03:00
Mickael Remond cd2e2b1a88 Synchronizing master changes 2016-04-12 10:34:24 +02:00
Mickael Remond 5958a91428 Fix typo in error message 2016-04-12 10:27:43 +02:00
Holger Weiss 15d184a909 Disable TLS compression for s2s by default
TLS compression is not recommended, and it's already disabled by default
for c2s connections and for ejabberd_http.
2016-04-11 22:50:11 +02:00
Badlop 8c8a6869be process2/2 is needed by mod_rest to provide its own AccessCommands
See processone/ejabberd-contrib#161
2016-04-11 17:21:44 +02:00
Badlop 695592a38c Update check_password tests are the command now is fixed 2016-04-11 13:39:35 +02:00
badlop bd3b7cd2df Merge pull request #1064 from sezuan/fix_check_password
Fix check_password
2016-04-11 13:38:22 +02:00
Matthias Rieber b67dc00db2 Fix check_password 2016-04-10 15:37:36 +02:00
Mickael Remond 127342449e Allow testing user pattern directly in access rules 2016-04-08 19:45:25 +02:00
Paweł Chmielowski b4739396ec Switch to varchar(64) in mysql user.server/salt as text can't have default values 2016-04-08 17:50:59 +02:00
Mickael Remond 1dbdd58b1b Add TODO to improve ACL 2016-04-08 12:55:35 +02:00
Evgeniy Khramtsov c5dbdfc71a 'serverkey' and 'salt' should have empty string as default 2016-04-08 13:02:08 +03:00
Paweł Chmielowski 86dfbe6ece Make sure that ejabberd_sm sid are unique 2016-04-08 10:52:29 +02:00
Evgeniy Khramtsov b83ec483e9 Send stream trailer at the very end 2016-04-08 11:49:50 +03:00
Paweł Chmielowski afd3accf75 Generate shorted jid for anonymous connections 2016-04-07 16:47:30 +02:00
Paweł Chmielowski 0935f493ee Add tests for anonymous and digest-md5 auth 2016-04-07 16:47:01 +02:00
Mickael Remond 373f9fb0eb Add tests on Access rules returning values 2016-04-07 13:04:58 +02:00
Mickael Remond 60c0c8e968 Add test when mixing ip / user rules 2016-04-07 12:35:29 +02:00
Paweł Chmielowski 18557fa3d6 Start of tests for cyrsasl module 2016-04-07 12:28:19 +02:00
Mickael Remond a938af4180 IP based ACL / Access rules and sequential evaluation of rules 2016-04-07 12:06:30 +02:00
Paweł Chmielowski d5c29360fb Fix anonymous auth 2016-04-07 10:02:37 +02:00
Mickael Remond 48a1d818d6 Rebase master 2016-04-06 18:14:47 +02:00
Mickael Remond eb36440c2e Variant for user ACL test 2016-04-06 18:13:08 +02:00
Mickael Remond 47039aed15 Allow clearing all ACL and access rules 2016-04-06 18:13:08 +02:00
Mickael Remond d45ad3e3a5 Add initial basic ACL test 2016-04-06 18:13:08 +02:00
Mickael Remond 5ad8c790c7 Export add_access/3 to allow setting ACL outside of yaml config file 2016-04-06 18:13:08 +02:00
Mickael Remond 5efcf0a175 Stringprep can already be started, do not check result 2016-04-06 18:11:46 +02:00
Mickael Remond 0916694e0e Merge branch 'master' of github.com:processone/ejabberd 2016-04-06 17:56:01 +02:00
Mickael Remond 2900aa208f Log Elixir test result for investigation and include this log file in travis for troubleshooting failed tests 2016-04-06 17:55:56 +02:00
Badlop 27b4217a9d Tweak srg_get_info result formatting (#1048) 2016-04-06 17:55:19 +02:00
Mickael Remond a9e50468b6 Better error message in logs 2016-04-06 15:07:44 +02:00
Mickael Remond abf768274a Fix error message paramater formatting 2016-04-06 15:05:19 +02:00
Mickael Remond f78b170c24 Add initial basic ACL test 2016-04-06 13:59:33 +02:00
Mickael Remond 991529a657 Export add_access/3 to allow setting ACL outside of yaml config file 2016-04-06 13:59:06 +02:00
Mickael Remond b2279d481d Merge branch 'master' of github.com:processone/ejabberd 2016-04-06 13:57:12 +02:00
Mickael Remond 267fdb2e95 Now we need to start stringprep before config 2016-04-06 13:51:05 +02:00
badlop d9f1061b8a Merge pull request #1051 from genric/patch-1
Fix mod_muc_admin:set_room_affiliation options persistence
2016-04-06 13:44:12 +02:00
badlop dd654fa794 Merge pull request #1052 from genric/patch-2
Fix mod_muc_admin:get_room_options
2016-04-06 13:25:46 +02:00
Mickael Remond 1b9b5f8e6a Merge branch 'master' of github.com:processone/ejabberd 2016-04-06 12:59:58 +02:00
Mickael Remond 67b9b82261 We need to set hosts in options to be able to retrieve 'MYHOSTS' 2016-04-06 12:59:27 +02:00
Mickaël Rémond 2eee2de6e2 Merge pull request #1054 from richp10/master
upgrade stringprep to 1.03
2016-04-06 12:05:26 +02:00
richp10 acc11195f8 upgrade stringprep to 1.03 2016-04-06 08:40:10 +00:00
genric be7f65da05 Fix mod_muc_admin:get_room_options
Fix mod_muc_admin:get_room_options to match the ejabberd_commands result spec.
2016-04-05 14:13:28 +02:00
Evgeniy Khramtsov 232915184c Merge branch 'add-error-reason' 2016-04-05 13:10:09 +03:00
Evgeniy Khramtsov 9ac6e4edf7 Replace more ?ERR_* macros with ?ERRT_* 2016-04-05 13:09:44 +03:00
genric 490aa2c6a6 Fix mod_muc_admin:set_room_affiliation
Add missing options so they are stored when set_room_affiliation is invoked, instead of being ignored and set to default values after muc restart.
2016-04-04 14:02:34 +02:00
Mickaël Rémond ca9ac019eb Merge pull request #1046 from processone/commands-update
Add support for versioning in ejabberd commands
2016-04-01 14:24:08 +02:00
Mickael Remond e24da5789e Apply fixes and remove tests for missing methods 2016-04-01 13:05:41 +02:00
Mickael Remond 47266de6d7 Do not use underscore variable 2016-04-01 12:24:49 +02:00
Mickael Remond f243c30847 Rollback mod_admin_extra 2016-04-01 12:24:00 +02:00
Mickael Remond 2a2a47b5c6 Fix merge issues 2016-04-01 12:12:19 +02:00
Mickael Remond b5f1479763 Fix tests, they are now running fine locally 2016-04-01 11:13:48 +02:00
Mickael Remond a8f92ae767 Add logger macro to help troubleshooting Elixir tests 2016-04-01 11:11:42 +02:00
Mickael Remond 97d345d287 Port mod_admin_extra test to work with new API 2016-03-31 22:01:57 +02:00
Mickael Remond ef2e2e45b3 Fix failing tests 2016-03-31 17:34:58 +02:00
Mickael Remond 3290a5ff15 Force protobuffs version to be able to use new meck release 2016-03-31 16:05:18 +02:00
Mickael Remond 7988e2e350 Merge lastest commits from master 2016-03-31 15:37:21 +02:00
Mickael Remond dc0ca51ef1 Try keeping names of test same as master to limit merge conflicts 2016-03-31 15:06:41 +02:00
Mickael Remond 78c4a0d65f Add test file whose name was conflicting during merge 2016-03-31 14:42:08 +02:00
Mickael Remond 91233eafaa Use version of moka that do not depend on proper 2016-03-31 14:36:30 +02:00
Mickael Remond da0751239c Rename conflicting test file after merge 2016-03-31 14:33:34 +02:00
Alexey Shchepin 3dc55c6d47 Commands refactor, first pass.
- add API versionning
- changed error handling, based on exception
- commands moved/merged from mod_admin_p1 to mod_admin_extra
- command bufixes
- add some elixir unit test cases

Squashed commit of the following:

commit dd59855b3486f78a9349756e4f102e79b3accff8
Merge: 14e8ffc 506e08e
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Oct 30 11:43:18 2015 +0100

    Merge branch '3.2.x' into api

commit 14e8ffce78cbea6c8605371d1fc50a0c1d1e012c
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Oct 27 16:35:17 2015 +0100

    Added OAuth tests to ejabberd_commands

commit f81c550c14628edfe4861c228576cb767924366a
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Oct 27 16:34:55 2015 +0100

    Added some mod_http_api tests

commit 6a64578d5b2ba532a2feb6503ed98561e56d5d53
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Mon Oct 26 15:29:36 2015 +0100

    Fix get_last command test

    Previous version won't work with dst.

commit 27e0cde9e9c1f001effe68f8424a365ad947c068
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Oct 23 17:59:34 2015 +0200

    Add tests on admin command policy

commit 19dad8d54f54c9fabd454280483cccfb06c8e78a
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Oct 23 16:49:36 2015 +0200

    Added command related tests (http api & user policy)

commit e0e596ab4a3f3a70aba5f374f028939ab794de33
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Oct 23 16:49:16 2015 +0200

    Fix command call.

commit 128cd7d1ede3c47a34f8ec3a750c980ccad2c61d
Merge: 60c4c4c 447313c
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Thu Oct 22 14:48:39 2015 +0200

    Merge branch '3.2.x' into api

commit 60c4c4c0751302524c14219c6bc8c56a6069a689
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Thu Oct 22 14:45:57 2015 +0200

    Fix ejabberd_commands spec.

commit 8e145c28c5da762c2b93ee32327eff1db94ebfed
Merge: 397273a f13dc94
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Wed Oct 21 18:26:07 2015 +0200

    Merge branch '3.2.x' into api

commit 397273a23ed415feac87aed33da6452229793387
Merge: c30e89b f289e27
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Wed Oct 21 15:27:45 2015 +0200

    Merge branch '3.2.x' into api

commit c30e89bb8a0013bff37e61e4c6953350c9c1f313
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Wed Oct 21 12:47:02 2015 +0200

    Merge mod_http_api

commit 7b0db22b4acd48ff6fabce41c1b2525e6580a3c5
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Oct 16 11:55:48 2015 +0200

    Fix exunit tests to run with common_test suites

commit d8b1a89800ac7379a57a7eb4a09c3c93c3e1e5eb
Merge: 2879ae8 63455b3
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Thu Oct 15 11:39:45 2015 +0200

    Merge branch '3.2.x' into api

commit 2879ae87ff3eee369ef3d780136b96ecff5285d1
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Wed Oct 14 14:53:44 2015 +0200

    Fix update_roster command.

commit a1d453dd7a3afda9861a8d747494a45057ad574b
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Oct 13 16:14:28 2015 +0200

    API commands refactor

    Moving and/or merging commands from mod_admin_p1 to mod_admin_extra

commit b709ed26b0fc0ca4f3bdd5a59fa58ec7e3db97fa
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Wed Oct 7 15:10:01 2015 +0200

    Add tests on commands

commit 6711687bee9c672cb3d5aed0744e13420ecf6dbd
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Sep 29 15:58:16 2015 +0200

    Add ejabberd_commands tests

commit df8682f419cf3877e77e36a19bca0fc55dc991f8
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Mon Sep 28 14:54:39 2015 +0200

    Added API versioning for ejabberdctl and rest commands

commit cd017b0e3aac431bc3ee807ceb7f8641e1523ef5
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Fri Sep 18 11:21:45 2015 +0200

    Better error handling of HTTP API commands.

commit ca5cb6acd8e4643f9d6c484d2277b0d7e88471e5
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Sep 15 15:03:05 2015 +0200

    add commands to mod_admin_extra:
    - get_offline_count
    - get_presence
    - change_password

commit 7f583fa099e30ac2b0915669fd8f102ac565b833
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Tue Sep 15 15:02:16 2015 +0200

    Improve REST API error handling

commit 14753b1c02cdce434a786b7f80f6c09f0d210075
Author: Jerome Sautret <jerome.sautret@process-one.net>
Date:   Mon Sep 14 10:51:17 2015 +0200

    Change REST API return codes for integer type.
2016-03-31 14:53:31 +03:00
Mickael Remond d35c5ebde5 Test / Document ejabberd_commands checks 2016-03-31 13:14:06 +02:00
Mickael Remond 3cfcdbb245 Check that various type of commands are properly rejected without auth 2016-03-31 12:38:53 +02:00
Mickaël Rémond 7c2998a55d Merge pull request #1044 from processone/http-api
Add ability to call open ejabberd commands through ReST API
2016-03-31 11:37:14 +02:00
Evgeniy Khramtsov fced8dc3d9 Replace some ?ERR_* macros with ?ERRT_* 2016-03-31 11:00:29 +03:00
Mickael Remond c00cfca8e7 mix version updated for 16.03 release 2016-03-30 19:21:12 +02:00
Mickael Remond 3c480a5b0b Fix Dialyzer inconsistency 2016-03-30 16:47:40 +02:00
Paweł Chmielowski b160bd7ac1 Provide authzid in scream response 2016-03-30 16:08:04 +02:00
Mickael Remond 809057678b Better error report when command is not exposed through API 2016-03-30 15:59:29 +02:00
Mickael Remond 36ac1cd6c7 Returns unauthorized error when we do not have correct credentials 2016-03-30 14:49:19 +02:00
Mickael Remond ead83b008c HTTP ReST API now supports 'open' ejabberd commands 2016-03-30 14:23:09 +02:00
Mickael Remond 6f25122f8c Support flagging so Elixir tests as pending 2016-03-30 13:59:01 +02:00
Holger Weiss bf79f223df Travis CI: Cosmetic changes to Riak setup commands 2016-03-30 01:15:12 +02:00
Holger Weiss e58b62f737 Travis CI: Revert to checking for skipped tests
Now that the issues with running Riak on Travis are solved, we can check
for skipped test cases again.
2016-03-30 01:02:27 +02:00
Mickael Remond 82cf7f7ca8 Adds support for option admin_ip_access on mod_http_api
This allows granting access to admin commands to backend, by using IP address restrictions.
(Pawel Chmielowski)
2016-03-29 19:40:20 +02:00
Mickael Remond 6d7891ed16 Merge branch 'master' of github.com:processone/ejabberd 2016-03-29 15:45:57 +02:00
Mickael Remond c7c70ffa0a Add basic test for command registration 2016-03-29 15:45:48 +02:00
Badlop 78a44d8099 Move start and stop_modules/0 from ejabberd_app to gen_mod (#1039) 2016-03-29 15:26:34 +02:00
Mickael Remond b49a615e21 Fix commands api option 2016-03-29 13:19:16 +02:00
Mickael Remond 3b2d0fd24a Fix commands access check.
Fixes ECS-20
2016-03-29 13:06:13 +02:00
Mickael Remond aa15148898 Fix commands access check. 2016-03-29 13:05:12 +02:00
Badlop 3809b898aa Pass noauth when auth isn't provided, reverts a1129dc (processone/ejabberd-contrib#159) 2016-03-29 12:51:26 +02:00
Badlop e386bf6b58 In SQL files create Users table with SCRAM support by default (#956) 2016-03-29 12:37:49 +02:00
Mickael Remond 221d8e0e5d Merge branch 'master' of github.com:processone/ejabberd 2016-03-29 11:21:58 +02:00
Mickael Remond 53d12caa56 Fix log printout
Log is not only called for admin commands. It is call for all commands call.
2016-03-29 11:21:53 +02:00
Evgeniy Khramtsov b0ef3e66a8 Update MSSQL schema 2016-03-29 11:53:13 +03:00
Evgeniy Khramtsov 7a9e93839a Fix some LIMIT related problems with MSSQL 2016-03-29 11:34:00 +03:00
Mickael Remond 54ddc990c2 Use new fast_yaml 2016-03-29 09:21:24 +02:00
Mickaël Rémond 4afe0b195c Merge pull request #1036 from processone/shared-roster-ldap
Fix issue getting shared roster
2016-03-25 19:36:10 +01:00
Evgeny Khramtsov 494e638f03 Merge pull request #1024 from rbarlow/luerl_release
Use the v0.2 release of luerl instead of a commit.
2016-03-25 21:29:19 +04:00
Evgeny Khramtsov 915ccbbdfb Merge pull request #684 from wcy123/master
bug fix: ejabberd:start_app need to pass Type to application:start
2016-03-25 21:00:35 +04:00
Mickael Remond 381065397f Fix issue getting shared roster
I rollbacked to correct version and slightly refactored the code
2016-03-25 17:44:12 +01:00
Mickael Remond a08ecc0f41 Proper naming for LDAP test function for shared roster 2016-03-25 17:42:58 +01:00
Mickael Remond eace5fc463 Switch back to proper log level 2016-03-25 17:42:19 +01:00
Mickael Remond 0afcf561d6 Add test to demonstrate issue to retrieve roster with mod_shared_roster_ldap 2016-03-25 17:30:12 +01:00
Evgeniy Khramtsov 46568fb959 Merge commit 'refs/pull/524/head' of github.com:processone/ejabberd into sasl-api-change 2016-03-25 18:16:50 +03:00
Mickaël Rémond c7cf95ba99 Merge pull request #1035 from processone/modular-tests-run
Allow running test groups independently
2016-03-24 15:46:51 +01:00
Christophe Romain 914578a85e Fix start via systemd (#978) 2016-03-24 11:06:42 +01:00
Christophe Romain df4c551f06 Specify lacking nodename (thanks to hamano)(#1020) 2016-03-24 10:21:51 +01:00
Mickael Remond a3a33bd5fc Allow running test groups independently
We need to be able to run only a few test groups, even if we do not have all
database backends installed and configured locally.

ejabberd test suite configures a specific host per backend. I changed ejabberd
to allow ignoring some hosts from config file on start, by providing the exact
list of hosts we want to start.

This is done by setting an ejabberd app Erlang environment variable 'hosts' and
passing the list of hosts we want to actually define.

When doing so, the backend specific hosts defined in ejabberd test configuration file
are simply ignored. As a result, we do not try to connect to unavailable backends.

I linked that part to CT run test by defining the hosts list based on environment variable
CT_BACKENDS. This variable is expected to be a comma separated list of available backends.

When Erlang Common Tests are run with that environment variable set, only the host matching
the name of the backend will be set, plus the default "localhost", common to many tests.

This can be combined with rebar ct groups list.

Example commands to run tests:
CT_BACKENDS=riak,mnesia rebar ct suites=ejabberd
CT_BACKENDS=mnesia rebar ct suites=ejabberd groups=mnesia
2016-03-24 10:02:13 +01:00
Mickael Remond 7066338948 Enable Riak test suite on Travis-CI 2016-03-23 16:16:50 +01:00
Evgeniy Khramtsov cb27a3540e Fix is_connected/0 function 2016-03-22 20:01:23 +03:00
Evgeniy Khramtsov 61e914a83f Keep alive Riak connections by default 2016-03-22 19:32:30 +03:00
Evgeniy Khramtsov b90c3764c0 Fix a typo in travis.yml 2016-03-22 19:05:38 +03:00
Evgeniy Khramtsov bdce5556bd Tell Travis not to fail if some tests are skipped 2016-03-22 19:03:06 +03:00
Evgeniy Khramtsov 57f7b34b90 Do not auto append IP suffix to usernames (#1008) 2016-03-22 13:25:34 +03:00
Mickael Remond f8cf1aef91 Disable Riak test on Travis as they are too unpredictable on Travis
For now, we are running them on local Jenkins
2016-03-21 19:43:59 +01:00
Badlop e7ef65a22d Improve ban_account command to work with other DBs than Mnesia (#977) 2016-03-21 18:30:05 +01:00
Badlop 107569a17d New command delete_mnesia deletes all tables that can be exported 2016-03-21 16:19:06 +01:00
Mickael Remond 0112135096 Elixir test suite is already run with global CT run command 2016-03-21 14:12:00 +01:00
Mickaël Rémond 4be9cc1b6d Merge pull request #1029 from processone/coveralls
Add support for test code coverage
2016-03-21 12:45:02 +01:00
Mickael Remond 95475966fd We do not have C code to cover, they are in dependencies. 2016-03-21 12:15:07 +01:00
Mickael Remond ef04dd75aa Add Coveralls support 2016-03-21 12:01:20 +01:00
Paweł Chmielowski 3441157a38 Use static key for mysql repo 2016-03-21 11:02:32 +01:00
Mickael Remond c98df3c0da Attempting to use other keyserver, as we see failure from Travis fetching 2016-03-21 10:06:07 +01:00
Mickael Remond c924cd42ff Run basic Elixir unit tests 2016-03-21 09:45:40 +01:00
Mickael Remond 31c194a682 Add simple Elixir unit test on jid:from_string 2016-03-21 09:44:23 +01:00
Mickael Remond 5b7dc0c215 Merge branch 'master' of github.com:processone/ejabberd 2016-03-21 09:43:14 +01:00
Mickael Remond 0e3026539e Comment on error failures in logs + indenting 2016-03-21 09:42:59 +01:00
Badlop dc7b2c45c2 Fix indentation of mod_register in default configuration file 2016-03-19 17:42:12 +01:00
Evgeny Khramtsov cf9ef456b7 Merge pull request #1022 from hamano/riak_auth
riak authentication support
2016-03-18 23:10:37 +04:00
Randy Barlow 7b5825a205 Use the v0.2 release of luerl instead of a commit.
The luerl project has made a tag for v0.2, which should be usable
by ejabberd. Discussion about the v0.2 release is here:

https://github.com/rvirding/luerl/issues/60
2016-03-18 14:22:59 -04:00
HAMANO Tsukasa 2d103b4ae1 support riak authentication 2016-03-19 01:41:14 +09:00
Christophe Romain 939bb244e1 Extend scope of ejabberdctl ping 2016-03-18 11:08:30 +01:00
Christophe Romain f19a54e9a1 Escape quoting node name for ejabberdctl ping 2016-03-18 10:22:11 +01:00
Paweł Chmielowski ef02053a9d Fix issue #1015 2016-03-17 18:41:39 +01:00
Mickaël Rémond d9bb3730b7 Merge pull request #926 from lpil/fix/remove-empty-mod
Remove empty and unused module
2016-03-16 18:28:01 +01:00
Paweł Chmielowski 7b72247b2c Don't use jlib:jid_remove_resource 2016-03-16 13:32:19 +01:00
Paweł Chmielowski 34bc698526 Merge pull request #1011 from oxoWrk/master
Bare JID in 'from' of Roster Push (RFC 6121 section 2.1.6) in mod_adm…
2016-03-16 11:37:25 +01:00
Paweł Chmielowski efbaba5d04 Make auto generated resources shorter 2016-03-16 11:30:45 +01:00
Badlop c985a2bd3d Start ezlib only if required, as it's optional (#1006) 2016-03-16 11:11:43 +01:00
Mickael Remond 74053b114e When building Elixir inside ejabberd, rely on version 1.1 for Erlang R17 compliance 2016-03-16 09:01:40 +01:00
Mickael Remond b40154f8f4 Update OTP release to use and drop release 17.1
Attempt to build with Elixir
2016-03-16 08:55:25 +01:00
Mickael Remond 3c2cd91fb1 Merge branch 'master' of github.com:processone/ejabberd 2016-03-15 22:42:15 +01:00
Mickael Remond 367adc2113 Better error reporting when running Elixir test suite 2016-03-15 22:42:07 +01:00
Paweł Chmielowski 5b5548b8ca Produce less verbose logs for tests on travis 2016-03-15 18:14:27 +01:00
root 058b3d96bf Bare JID in 'from' of Roster Push (RFC 6121 section 2.1.6) in mod_admin_extra 2016-03-15 10:57:56 +05:00
Holger Weiss 68675effac Apply cosmetic changes to GitHub templates 2016-03-15 00:12:01 +01:00
badlop 3fef1a4435 Merge pull request #991 from suchatorg/patch-1
Update Galician (galego) translations
2016-03-14 17:08:30 +01:00
Carlos 7f25c3b3e8 Update gl.po 2016-03-14 16:52:16 +01:00
Evgeny Khramtsov dcefb6bbe3 Merge pull request #980 from sharewax/EJAB-1480
EJAB-1480: fix issue with retreiving user roster
2016-03-14 17:49:01 +03:00
Badlop 5351e8236d Fix Addresses element which lacked others local destinations
When sending single packet, in addresses include all other group
destinations, not only oneself
2016-03-14 12:53:14 +01:00
Carlos 9440049208 Update gl.po 2016-03-14 12:47:52 +01:00
Holger Weiss b871fbba1b Fix result type of "connected_users_info" command
Closes #1002.
2016-03-14 00:51:12 +01:00
Holger Weiss 91573a8e82 Don't store watchdog notifications in MAM archives 2016-03-14 00:05:50 +01:00
Evgeniy Khramtsov 55c567ff00 Unregister route at the very end 2016-03-13 17:37:39 +03:00
Evgeniy Khramtsov 5a4b7817df Add ODBC backend for MIX 2016-03-13 15:38:50 +03:00
Evgeniy Khramtsov 50e5cdc2fa Add ODBC tests for MIX 2016-03-13 13:16:55 +03:00
Evgeniy Khramtsov 5045fb584d Define pubsub node configuration per route/host explicitly 2016-03-13 13:16:43 +03:00
Evgeniy Khramtsov 357e48fb6b Make it possible to get virtual host of a registered route 2016-03-13 11:38:40 +03:00
Mickael Remond 9ceeaf213b Provide guidance for issue reporting and pull requests on Github 2016-03-12 17:54:23 +01:00
Christophe Romain 9297782868 Fix config fetch after host/serverhost cleanup 2016-03-11 17:25:46 +01:00
Holger Weiss 0d1edc4771 Don't enable in-band registration by default 2016-03-11 12:31:16 +01:00
Mickaël Rémond 1336c6c9fa Merge pull request #997 from tnull/fix_auth_method_order
Minimal auth_method ordering fix during configuration merge.
2016-03-11 11:37:50 +01:00
Elias Rohrer 8b03c0a385 Minimal auth_method ordering fix 2016-03-10 17:52:55 +01:00
Evgeniy Khramtsov 15ee72138a Add tests for MIX 2016-03-10 17:42:37 +03:00
Mickael Remond 92a0181932 Lager to Elixir Logger bridge is now compliant with ejabberd loglevel set / get
This should fix #966
2016-03-09 22:30:46 +01:00
Mickael Remond 035c63fd2a Fix call to lager_util:is_loggable/3 2016-03-09 21:03:06 +01:00
Mickael Remond 9e6efaf9bc Use p1_time_compat util for generating timestamp 2016-03-09 20:57:01 +01:00
Mickael Remond f4ee8a2505 Add Elixir Logger Backend to bridge logs from lager
We will need to support loglevel bridging.
It should help with #966
2016-03-09 19:12:56 +01:00
Paweł Chmielowski 842d52352a Fix escaping of argument in iexlive and iexdebug 2016-03-09 14:26:28 +01:00
Evgeniy Khramtsov e31799a3b1 Define mod_opt_type/1 callback 2016-03-09 11:19:15 +03:00
Evgeniy Khramtsov 1860801e36 Unregister hooks and iq handlers on terminate 2016-03-09 11:14:45 +03:00
Holger Weiss ae4fa22180 mod_http_upload: Add XEP-0363 v0.2 support
Include the maximum file size in the service discovery information, as
specified by XEP-0363, version 0.2.
2016-03-09 00:27:06 +01:00
Evgeniy Khramtsov b5121a346d Experimental MIX (XEP-0369) support 2016-03-08 20:04:29 +03:00
Carlos b6289d646f Update gl.po 2016-03-08 01:21:37 +01:00
Carlos c065a2c5b9 Update gl.po 2016-03-07 22:26:46 +01:00
badlop 6e40573c13 Merge pull request #989 from galambalazs/patch-1
fix syntax highlighting by keeping "~s" together
2016-03-07 19:06:59 +01:00
badlop eb0890284a Merge pull request #988 from tnull/edoc_fix
Fixed type specifications for 'rebar doc'
2016-03-07 18:44:05 +01:00
Badlop 16c1b9a5c2 Fix format_result also in xmlrpc, after aa5caa3 (#982) 2016-03-07 17:34:08 +01:00
Balázs Galambosi 83accedded fix syntax highlighting by keeping "~s" together 2016-03-07 16:06:18 +01:00
Elias Rohrer 8e6a301026 Fixed type specifications for 'rebar doc'
- Fixed type @specs and -specs to remove 'rebar doc' errors
- Removed a lot of wrong and deprecated documentation in ejabberd_piefxis.erl
2016-03-07 15:06:19 +01:00
Anton Samets 4013629e5d EJAB-1480: fix issue with retreiving user roster 2016-03-04 15:52:38 +03:00
Paweł Chmielowski 6e14a47316 Define opt_type required be ejabberd_config behaviour. 2016-03-04 11:45:18 +01:00
Paweł Chmielowski 304afd75ac Compile ejabberd_config early to stop undefined behaviour warnings 2016-03-04 11:32:34 +01:00
Badlop 9c3d57e63e Mark get_queue_length obsolete, and use count_offline_messages (#970) 2016-03-04 11:09:14 +01:00
Christophe Romain 44978ce978 ext_mod: switch to fast_xml and remove old p1_logger reference 2016-03-03 15:46:15 +01:00
Christophe Romain 10d6c330a5 Fix pubsub disco after host/serverhost cleanup 2016-03-03 14:52:03 +01:00
Evgeniy Khramtsov e95cf420a2 Enable flexible offline on disco#info as well 2016-03-03 14:10:40 +03:00
Christophe Romain 3ecd7e850c Use Elixir v1.2.3 2016-03-03 10:36:13 +01:00
Christophe Romain fee5873310 Minor format cleanup 2016-03-03 10:34:45 +01:00
Mickael Remond 96b09c587d Use upcoming version of ejabberd with Elixir 1.2 2016-03-02 13:35:35 +01:00
Alexey Shchepin 79853ad44f Missed a few calls in previous commits 2016-03-02 02:00:02 +03:00
Alexey Shchepin 9a049442ff Raise an error when there are no fields to set in ?SQL_UPSERT 2016-03-02 00:12:49 +03:00
Alexey Shchepin e21f25f5b9 Update more SQL queries 2016-03-02 00:12:49 +03:00
Alexey Shchepin 1f9fd25ff8 Update more SQL queries 2016-03-02 00:12:49 +03:00
Alexey Shchepin 968576d4f2 Update p1_pgsql tag 2016-03-02 00:12:49 +03:00
Alexey Shchepin d8fbe8a289 Update more SQL queries 2016-03-02 00:12:49 +03:00
Alexey Shchepin 6d7ce0237a Update mod_last SQL queries to the new API 2016-03-02 00:12:49 +03:00
Alexey Shchepin 2d042f078e New parse transform for ?SQL_UPSERT and ?SQL_UPSERT_T 2016-03-02 00:12:49 +03:00
Alexey Shchepin 3d8219d8f9 Update mod_roster and ejabberd_auth_odbc SQL queries to the new API 2016-03-02 00:12:49 +03:00
Alexey Shchepin 7f3bffe821 Allow balanced expressions inside @(...) in ejabberd_sql_pt 2016-03-02 00:12:49 +03:00
Alexey Shchepin 99255631dd Updated some mod_offline SQL queries to the new API 2016-03-02 00:12:11 +03:00
Alexey Shchepin ba35c1ed9d Use 'any' to match any DBMS in sql_query 2016-03-01 22:50:20 +03:00
Alexey Shchepin 437e768e4a Better error handling in ejabberd_sql_pt 2016-03-01 22:50:07 +03:00
Alexey Shchepin c58a4be6ee Support for run-time SQL queries selection depending on DBMS version 2016-03-01 22:49:56 +03:00
Alexey Shchepin 6374ef4866 New parse transform for SQL queries, use prepare/execute calls with Postgres 2016-03-01 22:48:30 +03:00
Mickael Remond eeac7f9b02 Update ejabberd version for hex.pm release 2016-02-29 17:51:06 +01:00
Paweł Chmielowski 8c49d1e1af Disable back undefined_function_calls xref check 2016-02-29 14:56:28 +01:00
Richard ae77b1300a change mod_ping Timers using maps instead of dict 2016-01-29 00:07:38 +08:00
Louis Pilfold d89bbba181 Remove empty and unused module 2016-01-26 21:25:34 +00:00
wcy123 3e49bf0e83 bug fix: ejabberd:start_app need to pass Type to application:start 2015-07-30 18:57:59 +08:00
Ben Langfeld 917d48f30b Use SASL PLAIN authzid as client identity if auth module permits it
This allows the authentication modules to perform SASL proxy authentication. It puts the onus on them to authorize the authcid to masquerade as the authzid. Doesn't currently implement such functionality in existing auth modules, since they cannot currently codify a relationship between the two identities. Does not permit the authzid to use a domain differently from the one of the connection.

Note: digest might not work, but I have no interest in it, being deprecated.
2015-05-06 14:12:15 -03:00
Ben Langfeld d9814709e2 Remove commented code 2015-05-06 12:55:35 -03:00
216 changed files with 15490 additions and 9117 deletions
+15
View File
@@ -0,0 +1,15 @@
> What version of ejabberd are you using?
> What operating system (version) are you using?
> How did you install ejabberd (source, package, distribution)?
> What did not work as expected? Are there error messages in the log? What
> was the unexpected behavior? What was the expected result?
+20
View File
@@ -0,0 +1,20 @@
We are open to contributions for ejabberd, as GitHub pull requests (PR).
Here are a few points to consider before submitting your PR. (You can
remove the whole text after reading.)
1. Does this PR address an issue? Please reference it in the PR
description.
2. Have you properly described the proposed change?
3. Please make sure the change is atomic and does only touch the needed
modules. If you have other changes/fixes to provide, please submit
them as separate PRs.
4. If your change or new feature involves storage backends, did you make
sure your change works with all backends?
5. Do you provide tests? How can we check the behavior of the code?
6. Did you consider documentation changes in the
processone/docs.ejabberd.im repository?
+19 -5
View File
@@ -1,9 +1,8 @@
language: erlang
otp_release:
- 17.1
- 17.5
- 18.0
- 18.3
services:
- riak
@@ -18,19 +17,26 @@ before_install:
# See: https://github.com/travis-ci/travis-ci/issues/1986
#
- sudo sed -i -e s/table_cache/table_open_cache/ -e /log_slow_queries/d /etc/mysql/my.cnf
- sudo apt-key adv --keyserver pgp.mit.edu --recv-keys 5072E1F5
- sudo apt-key adv --import .travis/mysql_repo_key.asc
- sudo add-apt-repository 'deb http://repo.mysql.com/apt/ubuntu/ precise mysql-5.6'
- sudo apt-get -qq update
- sudo apt-get -qq -o Dpkg::Options::=--force-confold install mysql-server-5.6
# /END MYSQL 5.6
- pip install --user coveralls-merge
install:
- sudo apt-get -qq install libexpat1-dev libyaml-dev libpam0g-dev libsqlite3-dev
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"
- 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';"
@@ -40,18 +46,26 @@ before_script:
script:
- ./autogen.sh
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc --disable-elixir
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc
- make
- make install
- make xref
- make test
- sed -i -e 's/ct:pal/ct:log/' test/suite.erl
- ln -sf ../sql priv/
- escript ./rebar skip_deps=true ct -v
- grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' 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 '{}' ';'
after_success:
- coveralls-merge erlang.json
notifications:
email: false
+96
View File
@@ -0,0 +1,96 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (SunOS)
mQGiBD4+owwRBAC14GIfUfCyEDSIePvEW3SAFUdJBtoQHH/nJKZyQT7h9bPlUWC3
RODjQReyCITRrdwyrKUGku2FmeVGwn2u2WmDMNABLnpprWPkBdCk96+OmSLN9brZ
fw2vOUgCmYv2hW0hyDHuvYlQA/BThQoADgj8AW6/0Lo7V1W9/8VuHP0gQwCgvzV3
BqOxRznNCRCRxAuAuVztHRcEAJooQK1+iSiunZMYD1WufeXfshc57S/+yeJkegNW
hxwR9pRWVArNYJdDRT+rf2RUe3vpquKNQU/hnEIUHJRQqYHo8gTxvxXNQc7fJYLV
K2HtkrPbP72vwsEKMYhhr0eKCbtLGfls9krjJ6sBgACyP/Vb7hiPwxh6rDZ7ITnE
kYpXBACmWpP8NJTkamEnPCia2ZoOHODANwpUkP43I7jsDmgtobZX9qnrAXw+uNDI
QJEXM6FSbi0LLtZciNlYsafwAPEOMDKpMqAK6IyisNtPvaLd8lH0bPAnWqcyefep
rv0sxxqUEMcM3o7wwgfN83POkDasDbs3pjwPhxvhz6//62zQJ7Q2TXlTUUwgUmVs
ZWFzZSBFbmdpbmVlcmluZyA8bXlzcWwtYnVpbGRAb3NzLm9yYWNsZS5jb20+iGkE
ExECACkCGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAIZAQUCUwHUZgUJGmbLywAK
CRCMcY07UHLh9V+DAKCjS1gGwgVI/eut+5L+l2v3ybl+ZgCcD7ZoA341HtoroV3U
6xRD09fUgeq0O015U1FMIFBhY2thZ2Ugc2lnbmluZyBrZXkgKHd3dy5teXNxbC5j
b20pIDxidWlsZEBteXNxbC5jb20+iG8EMBECAC8FAk53Pa0oHSBidWlsZEBteXNx
bC5jb20gd2lsbCBzdG9wIHdvcmtpbmcgc29vbgAKCRCMcY07UHLh9bU9AJ9xDK0o
xJFL9vTl9OSZC4lX0K9AzwCcCrS9cnJyz79eaRjL0s2r/CcljdyIZQQTEQIAHQUC
R6yUtAUJDTBYqAULBwoDBAMVAwIDFgIBAheAABIJEIxxjTtQcuH1B2VHUEcAAQGu
kgCffz4GUEjzXkOi71VcwgCxASTgbe0An34LPr1j9fCbrXWXO14msIADfb5piEwE
ExECAAwFAj4+o9EFgwlmALsACgkQSVDhKrJykfIk4QCfWbEeKN+3TRspe+5xKj+k
QJSammIAnjUz0xFWPlVx0f8o38qNG1bq0cU9iEwEExECAAwFAj5CggMFgwliIokA
CgkQtvXNTca6JD+WkQCgiGmnoGjMojynp5ppvMXkyUkfnykAoK79E6h8rwkSDZou
iz7nMRisH8uyiEYEEBECAAYFAj+s468ACgkQr8UjSHiDdA/2lgCg21IhIMMABTYd
p/IBiUsP/JQLiEoAnRzMywEtujQz/E9ono7H1DkebDa4iEYEEBECAAYFAj+0Q3cA
CgkQhZavqzBzTmbGwwCdFqD1frViC7WRt8GKoOS7hzNN32kAnirlbwpnT7a6NOsQ
83nk11a2dePhiEYEEBECAAYFAkNbs+oACgkQi9gubzC5S1x/dACdELKoXQKkwJN0
gZztsM7kjsIgyFMAnRRMbHQ7V39XC90OIpaPjk3a01tgiEYEExECAAYFAkTxMyYA
CgkQ9knE9GCTUwwKcQCgibak/SwhxWH1ijRhgYCo5GtM4vcAnAhtzL57wcw1Kg1X
m7nVGetUqJ7fiEwEEBECAAwFAkGBywEFgwYi2YsACgkQGFnQH2d7oexCjQCcD8sJ
NDc/mS8m8OGDUOx9VMWcnGkAnj1YWOD+Qhxo3mI/Ul9oEAhNkjcfiEwEEBECAAwF
AkGByzQFgwYi2VgACgkQgcL36+ITtpIiIwCdFVNVUB8xe8mFXoPm4d9Z54PTjpMA
niSPA/ZsfJ3oOMLKar4F0QPPrdrGiEwEEBECAAwFAkGBy2IFgwYi2SoACgkQa3Ds
2V3D9HMJqgCbBYzr5GPXOXgP88jKzmdbjweqXeEAnRss4G2G/3qD7uhTL1SPT1SH
jWUXiEwEEBECAAwFAkHQkyQFgwXUEWgACgkQfSXKCsEpp8JiVQCghvWvkPqowsw8
w7WSseTcw1tflvkAni+vLHl/DqIly0LkZYn5jzK1dpvfiEwEEBECAAwFAkIrW7oF
gwV5SNIACgkQ5hukiRXruavzEwCgkzL5QkLSypcw9LGHcFSx1ya0VL4An35nXkum
g6cCJ1NP8r2I4NcZWIrqiEwEEhECAAwFAkAqWToFgwd6S1IACgkQPKEfNJT6+GEm
XACcD+A53A5OGM7w750W11ukq4iZ9ckAnRMvndAqn3YTOxxlLPj2UPZiSgSqiEwE
EhECAAwFAkA9+roFgwdmqdIACgkQ8tdcY+OcZZyy3wCgtDcwlaq20w0cNuXFLLNe
EUaFFTwAni6RHN80moSVAdDTRkzZacJU3M5QiEwEEhECAAwFAkEOCoQFgwaWmggA
CgkQOcor9D1qil/83QCeITZ9wIo7XAMjC6y4ZWUL4m+edZsAoMOhRIRi42fmrNFu
vNZbnMGej81viEwEEhECAAwFAkKApTQFgwUj/1gACgkQBA3AhXyDn6jjJACcD1A4
UtXk84J13JQyoH9+dy24714Aniwlsso/9ndICJOkqs2j5dlHFq6oiEwEExECAAwF
Aj5NTYQFgwlXVwgACgkQLbt2v63UyTMFDACglT5G5NVKf5Mj65bFSlPzb92zk2QA
n1uc2h19/IwwrsbIyK/9POJ+JMP7iEwEExECAAwFAkHXgHYFgwXNJBYACgkQZu/b
yM2C/T4/vACfXe67xiSHB80wkmFZ2krb+oz/gBAAnjR2ucpbaonkQQgnC3GnBqmC
vNaJiEwEExECAAwFAkIYgQ4FgwWMI34ACgkQdsEDHKIxbqGg7gCfQi2HcrHn+yLF
uNlH1oSOh48ZM0oAn3hKV0uIRJphonHaUYiUP1ttWgdBiGUEExECAB0FCwcKAwQD
FQMCAxYCAQIXgAUCS3AvygUJEPPzpwASB2VHUEcAAQEJEIxxjTtQcuH1sNsAniYp
YBGqy/HhMnw3WE8kXahOOR5KAJ4xUmWPGYP4l3hKxyNK9OAUbpDVYIh7BDARAgA7
BQJCdzX1NB0AT29wcy4uLiBzaG91bGQgaGF2ZSBiZWVuIGxvY2FsISBJJ20gKnNv
KiBzdHVwaWQuLi4ACgkQOcor9D1qil/vRwCdFo08f66oKLiuEAqzlf9iDlPozEEA
n2EgvCYLCCHjfGosrkrU3WK5NFVgiI8EMBECAE8FAkVvAL9IHQBTaG91bGQgaGF2
ZSBiZWVuIGEgbG9jYWwgc2lnbmF0dXJlLCBvciBzb21ldGhpbmcgLSBXVEYgd2Fz
IEkgdGhpbmtpbmc/AAoJEDnKK/Q9aopfoPsAn3BVqKOalJeF0xPSvLR90PsRlnmG
AJ44oisY7Tl3NJbPgZal8W32fbqgbIkCIgQQAQIADAUCQYHLhQWDBiLZBwAKCRCq
4+bOZqFEaKgvEACCErnaHGyUYa0wETjj6DLEXsqeOiXad4i9aBQxnD35GUgcFofC
/nCY4XcnCMMEnmdQ9ofUuU3OBJ6BNJIbEusAabgLooebP/3KEaiCIiyhHYU5jarp
ZAh+Zopgs3Oc11mQ1tIaS69iJxrGTLodkAsAJAeEUwTPq9fHFFzC1eGBysoyFWg4
bIjz/zClI+qyTbFA5g6tRoiXTo8ko7QhY2AA5UGEg+83Hdb6akC04Z2QRErxKAqr
phHzj8XpjVOsQAdAi/qVKQeNKROlJ+iq6+YesmcWGfzeb87dGNweVFDJIGA0qY27
pTb2lExYjsRFN4Cb13NfodAbMTOxcAWZ7jAPCxAPlHUG++mHMrhQXEToZnBFE4nb
nC7vOBNgWdjUgXcpkUCkop4b17BFpR+k8ZtYLSS8p2LLz4uAeCcSm2/msJxT7rC/
FvoH8428oHincqs2ICo9zO/Ud4HmmO0O+SsZdVKIIjinGyOVWb4OOzkAlnnhEZ3o
6hAHcREIsBgPwEYVTj/9ZdC0AO44Nj9cU7awaqgtrnwwfr/o4V2gl8bLSkltZU27
/29HeuOeFGjlFe0YrDd/aRNsxbyb2O28H4sG1CVZmC5uK1iQBDiSyA7Q0bbdofCW
oQzm5twlpKWnY8Oe0ub9XP5p/sVfck4FceWFHwv+/PC9RzSl33lQ6vM2wIkCIgQT
AQIADAUCQp8KHAWDBQWacAAKCRDYwgoJWiRXzyE+D/9uc7z6fIsalfOYoLN60ajA
bQbI/uRKBFugyZ5RoaItusn9Z2rAtn61WrFhu4uCSJtFN1ny2RERg40f56pTghKr
D+YEt+Nze6+FKQ5AbGIdFsR/2bUk+ZZRSt83e14Lcb6ii/fJfzkoIox9ltkifQxq
Y7Tvk4noKu4oLSc8O1Wsfc/y0B9sYUUCmUfcnq58DEmGie9ovUslmyt5NPnveXxp
5UeaRc5Rqt9tK2B4A+7/cqENrdZJbAMSunt2+2fkYiRunAFPKPBdJBsY1sxeL/A9
aKe0viKEXQdAWqdNZKNCi8rd/oOP99/9lMbFudAbX6nL2DSb1OG2Z7NWEqgIAzjm
pwYYPCKeVz5Q8R+if9/fe5+STY/55OaI33fJ2H3v+U435VjYqbrerWe36xJItcJe
qUzW71fQtXi1CTEl3w2ch7VF5oj/QyjabLnAlHgSlkSi6p7By5C2MnbCHlCfPnIi
nPhFoRcRGPjJe9nFwGs+QblvS/Chzc2WX3s/2SWm4gEUKRX4zsAJ5ocyfa/vkxCk
SxK/erWlCPf/J1T70+i5waXDN/E3enSet/WL7h94pQKpjz8OdGL4JSBHuAVGA+a+
dknqnPF0KMKLhjrgV+L7O84FhbmAP7PXm3xmiMPriXf+el5fZZequQoIagf8rdRH
HhRJxQgI0HNknkaOqs8dtrkCDQQ+PqMdEAgA7+GJfxbMdY4wslPnjH9rF4N2qfWs
EN/lxaZoJYc3a6M02WCnHl6ahT2/tBK2w1QI4YFteR47gCvtgb6O1JHffOo2HfLm
RDRiRjd1DTCHqeyX7CHhcghj/dNRlW2Z0l5QFEcmV9U0Vhp3aFfWC4Ujfs3LU+hk
AWzE7zaD5cH9J7yv/6xuZVw411x0h4UqsTcWMu0iM1BzELqX1DY7LwoPEb/O9Rkb
f4fmLe11EzIaCa4PqARXQZc4dhSinMt6K3X4BrRsKTfozBu74F47D8Ilbf5vSYHb
uE5p/1oIDznkg/p8kW+3FxuWrycciqFTcNz215yyX39LXFnlLzKUb/F5GwADBQf+
Lwqqa8CGrRfsOAJxim63CHfty5mUc5rUSnTslGYEIOCR1BeQauyPZbPDsDD9MZ1Z
aSafanFvwFG6Llx9xkU7tzq+vKLoWkm4u5xf3vn55VjnSd1aQ9eQnUcXiL4cnBGo
TbOWI39EcyzgslzBdC++MPjcQTcA7p6JUVsP6oAB3FQWg54tuUo0Ec8bsM8b3Ev4
2LmuQT5NdKHGwHsXTPtl0klk4bQk4OajHsiy1BMahpT27jWjJlMiJc+IWJ0mghkK
Ht926s/ymfdf5HkdQ1cyvsz5tryVI3Fx78XeSYfQvuuwqp2H139pXGEkg0n6KdUO
etdZWhe70YGNPw1yjWJT1IhUBBgRAgAMBQJOdz3tBQkT+wG4ABIHZUdQRwABAQkQ
jHGNO1By4fUUmwCbBYr2+bBEn/L2BOcnw9Z/QFWuhRMAoKVgCFm5fadQ3Afi+UQl
AcOphrnJ
=443I
-----END PGP PUBLIC KEY BLOCK-----
+5
View File
@@ -0,0 +1,5 @@
{level, details}.
{incl_dirs, ["src", "ebin"]}.
{excl_mods, [eldap, 'ELDAPv3']}.
{export, "logs/all.coverdata"}.
+1 -1
View File
@@ -7,7 +7,7 @@ User=ejabberd
Group=ejabberd
LimitNOFILE=16000
RestartSec=5
ExecStart=/bin/sh @ctlscriptpath@/ejabberdctl start
ExecStart=@ctlscriptpath@/ejabberdctl start
ExecStop=@ctlscriptpath@/ejabberdctl stop
ExecReload=@ctlscriptpath@/ejabberdctl reload_config
Type=oneshot
+33 -39
View File
@@ -254,10 +254,10 @@ auth_method: internal
## extauth_program: "/path/to/authentication/script"
##
## Authentication using ODBC
## Authentication using SQL
## Remember to setup a database in the next section.
##
## auth_method: odbc
## auth_method: sql
##
## Authentication using PAM
@@ -330,26 +330,26 @@ auth_method: internal
##
## MySQL server:
##
## odbc_type: mysql
## odbc_server: "server"
## odbc_database: "database"
## odbc_username: "username"
## odbc_password: "password"
## sql_type: mysql
## sql_server: "server"
## sql_database: "database"
## sql_username: "username"
## sql_password: "password"
##
## If you want to specify the port:
## odbc_port: 1234
## sql_port: 1234
##
## PostgreSQL server:
##
## odbc_type: pgsql
## odbc_server: "server"
## odbc_database: "database"
## odbc_username: "username"
## odbc_password: "password"
## sql_type: pgsql
## sql_server: "server"
## sql_database: "database"
## sql_username: "username"
## sql_password: "password"
##
## If you want to specify the port:
## odbc_port: 1234
## sql_port: 1234
##
## If you use PostgreSQL, have a large database, and need a
## faster but inexact replacement for "select count(*) from users"
@@ -359,25 +359,25 @@ auth_method: internal
##
## SQLite:
##
## odbc_type: sqlite
## odbc_database: "/path/to/database.db"
## sql_type: sqlite
## sql_database: "/path/to/database.db"
##
## ODBC compatible or MSSQL server:
##
## odbc_type: odbc
## odbc_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
## sql_type: odbc
## sql_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
##
## Number of connections to open to the database for each virtual host
##
## odbc_pool_size: 10
## sql_pool_size: 10
##
## Interval to make a dummy SQL request to keep the connections to the
## database alive. Specify in seconds: for example 28800 means 8 hours
##
## odbc_keepalive_interval: undefined
## sql_keepalive_interval: undefined
###. ===============
###' TRAFFIC SHAPERS
@@ -616,45 +616,39 @@ modules:
- "flat"
- "hometree"
- "pep" # pep requires mod_caps
mod_register:
## mod_register:
##
## Protect In-Band account registrations with CAPTCHA.
##
## captcha_protected: true
## captcha_protected: true
##
## Set the minimum informational entropy for passwords.
##
## password_strength: 32
## password_strength: 32
##
## After successful registration, the user receives
## a message with this subject and body.
##
welcome_message:
subject: "Welcome!"
body: |-
Hi.
Welcome to this XMPP server.
## welcome_message:
## subject: "Welcome!"
## body: |-
## Hi.
## Welcome to this XMPP server.
##
## When a user registers, send a notification to
## these XMPP accounts.
##
## registration_watchers:
## - "admin1@example.org"
## registration_watchers:
## - "admin1@example.org"
##
## Only clients in the server machine can register accounts
##
ip_access: trusted_network
## ip_access: trusted_network
##
## Local c2s or remote s2s users cannot register accounts
##
## access_from: deny
access: register
## access_from: deny
## access: register
mod_roster: {}
mod_shared_roster: {}
mod_stats: {}
+65 -112
View File
@@ -83,18 +83,9 @@ if [ "$EJABBERD_DOC_PATH" = "" ] ; then
fi
if [ "$ERLANG_NODE_ARG" != "" ] ; then
ERLANG_NODE=$ERLANG_NODE_ARG
NODE=${ERLANG_NODE%@*}
fi
if [ "{{release}}" != "true" ] ; then
if [ "$EJABBERDDIR" = "" ] ; then
EJABBERDDIR={{libdir}}/ejabberd
fi
if [ "$EJABBERD_PRIV_PATH" = "" ] ; then
EJABBERD_PRIV_PATH=$EJABBERDDIR/priv
fi
if [ "$EJABBERD_BIN_PATH" = "" ] ; then
EJABBERD_BIN_PATH=$EJABBERD_PRIV_PATH/bin
fi
if [ "{{release}}" != "true" -a "$EJABBERD_BIN_PATH" = "" ] ; then
EJABBERD_BIN_PATH={{libdir}}/ejabberd/priv/bin
fi
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
DATETIME=`date "+%Y%m%d-%H%M%S"`
@@ -141,8 +132,8 @@ fi
[ -z "$date" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_date '$date'"
[ -z "$EJABBERD_OPTS" ] || EJABBERD_OPTS="-ejabberd ${EJABBERD_OPTS}"
[ -d $SPOOL_DIR ] || $EXEC_CMD "mkdir -p $SPOOL_DIR"
cd $SPOOL_DIR
[ -d "$SPOOL_DIR" ] || $EXEC_CMD "mkdir -p $SPOOL_DIR"
cd "$SPOOL_DIR"
# export global variables
export EJABBERD_CONFIG_PATH
@@ -159,6 +150,15 @@ export CONTRIB_MODULES_PATH
export CONTRIB_MODULES_CONF_DIR
export ERL_LIBS
shell_escape_str()
{
if test $# -eq 0; then
printf '"" '
else
shell_escape "$@"
fi
}
shell_escape()
{
local RES=()
@@ -190,8 +190,8 @@ start()
debug()
{
debugwarning
TTY=`tty | sed -e 's/.*\///g'`
CMD="`shell_escape \"$ERL\" \"$NAME\" \"debug-${TTY}-${ERLANG_NODE}\"` \
NID=$(uid debug)
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \
-remsh $ERLANG_NODE \
-hidden \
$KERNEL_OPTS \
@@ -204,15 +204,15 @@ debug()
iexdebug()
{
debugwarning
TTY=`tty | sed -e 's/.*\///g'`
# Elixir shell is hidden as default
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"debug-${TTY}-${ERLANG_NODE}\"` \
NID=$(uid debug)
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"$NID\"` \
-remsh $ERLANG_NODE \
--erl \"`shell_escape \"$KERNEL_OPTS\"\" \
--erl \"`shell_escape \"$ERLANG_OPTS\"\" \
--erl \"`shell_escape \"${ARGS[@]}\"\" \
--erl \"`shell_escape \"$@\"\""
$EXEC_CMD "$CMD"
--erl `shell_escape \"$KERNEL_OPTS\"` \
--erl `shell_escape \"$ERLANG_OPTS\"` \
--erl `shell_escape \"${ARGS[@]}\"` \
--erl `shell_escape_str \"$@\"`"
$EXEC_CMD "ERL_PATH=$\"$ERL\" $CMD"
}
# start interactive server
@@ -233,15 +233,16 @@ live()
iexlive()
{
livewarning
echo $@
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"${ERLANG_NODE}\"` \
--erl \"-mnesia dir \\\"$SPOOL_DIR\\\"\" \
--erl \"`shell_escape \"$KERNEL_OPTS\"`\" \
--erl \"`shell_escape \"$EJABBERD_OPTS\"`\" \
--app ejabberd \
--erl \"`shell_escape \"$ERLANG_OPTS\"`\" \
--erl \"`shell_escape \"${ARGS[@]}\"`\" \
--erl \"`shell_escape \"$@\"`\""
$EXEC_CMD "$CMD"
--erl `shell_escape \"$ERLANG_OPTS\"` \
--erl `shell_escape \"${ARGS[@]}\"` \
--erl `shell_escape_str \"$@\"`"
$EXEC_CMD "ERL_PATH=\"$ERL\" $CMD"
}
# start server in the foreground
@@ -309,20 +310,27 @@ livewarning()
etop()
{
TTY=`tty | sed -e 's/.*\///g'`
NID=$(uid top)
$EXEC_CMD "$ERL \
$NAME debug-${TTY}-${ERLANG_NODE} \
$NAME $NID \
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
}
ping()
{
TTY=`tty | sed -e 's/.*\///g'`
[ -z "$1" ] && PEER=${ERLANG_NODE} || PEER=$1
if [ "$PEER" = "${PEER%.*}" ] ; then
PING_NAME="-sname"
PING_NODE=$(hostname -s)
else
PING_NAME="-name"
PING_NODE=$(hostname)
fi
NID=$(uid ping ${PING_NODE})
$EXEC_CMD "$ERL \
$NAME ping-${TTY}-${ERLANG_NODE} \
-hidden \
$KERNEL_OPTS $ERLANG_OPTS \
-eval 'io:format(\"~p~n\",[net_adm:ping($1)])' \
$PING_NAME $NID \
-hidden $KERNEL_OPTS $ERLANG_OPTS \
-eval 'io:format(\"~p~n\",[net_adm:ping('\"'\"'$PEER'\"'\"')])' \
-s erlang halt -output text -noinput"
}
@@ -350,97 +358,42 @@ help()
# common control function
ctl()
{
# Control number of connections identifiers
# using flock if available. Expects a linux-style
# flock that can lock a file descriptor.
MAXCONNID=100
CONNLOCKDIR={{localstatedir}}/lock/ejabberdctl
FLOCK=/usr/bin/flock
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
JOT=/usr/bin/jot
if [ ! -x "$JOT" ] ; then
# no flock or jot, simply invoke ctlexec()
CTL_CONN="ctl-${ERLANG_NODE}"
ctlexec $CTL_CONN "$@"
result=$?
else
# no flock, but at least there is jot
RAND=`jot -r 1 0 $MAXCONNID`
CTL_CONN="ctl-${RAND}-${ERLANG_NODE}"
ctlexec $CTL_CONN "$@"
result=$?
fi
else
# we have flock so we get a lock
# on one of a limited number of
# conn names -- this allows
# concurrent invocations using a bound
# number of atoms
for N in `seq 1 $MAXCONNID`; do
CTL_CONN="ejabberdctl-$N"
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
(
exec 8>"$CTL_LOCKFILE"
if flock --nb 8; then
ctlexec $CTL_CONN "$@"
ssresult=$?
# segregate from possible flock exit(1)
ssresult=`expr $ssresult \* 10`
exit $ssresult
else
exit 1
fi
)
result=$?
if [ $result -eq 1 ] ; then
# means we errored out in flock
# rather than in the exec - stay in the loop
# trying other conn names...
badlock=1
else
badlock=""
break;
fi
done
result=`expr $result / 10`
fi
if [ "$badlock" ] ;then
echo "Ran out of connections to try. Your ejabberd processes" >&2
echo "may be stuck or this is a very busy server. For very" >&2
echo "busy servers, consider raising MAXCONNID in ejabberdctl">&2
exit 1;
fi
case $result in
0) :;;
1) :;;
2) help;;
3) help;;
esac
return $result
}
ctlexec()
{
CONN_NAME=$1; shift
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$CONN_NAME\"` \
NID=$(uid ctl)
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \
-noinput -hidden $KERNEL_OPTS -s ejabberd_ctl \
-extra `shell_escape \"$ERLANG_NODE\"` $EJABBERD_NO_TIMEOUT \
`shell_escape \"$@\"`"
$EXEC_CMD "$CMD"
result=$?
case $result in
2) help;;
3) help;;
*) :;;
esac
return $result
}
uid()
{
uuid=$(uuidgen 2>/dev/null)
[ -z "$uuid" -a -f /proc/sys/kernel/random/uuid ] && uuid=$(</proc/sys/kernel/random/uuid)
[ -z "$uuid" ] && uuid=$(printf "%X" $RANDOM$(date +%M%S)$$)
uuid=${uuid%%-*}
[ $# -eq 0 ] && echo ${uuid}-${ERLANG_NODE}
[ $# -eq 1 ] && echo ${uuid}-${1}-${ERLANG_NODE}
[ $# -eq 2 ] && echo ${uuid}-${1}@${2}
}
# stop epmd if there is no other running node
stop_epmd()
{
$EPMD -names 2>/dev/null | grep -q name || $EPMD -kill >/dev/null
"$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null
}
# make sure node not already running and node name unregistered
check_start()
{
$EPMD -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
"$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
ps ux | grep -v grep | grep -q " $ERLANG_NODE " && {
echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running."
exit 4
@@ -451,7 +404,7 @@ check_start()
echo "Shutdown all other erlang nodes, and call 'epmd -kill'."
exit 5
} || {
$EPMD -kill >/dev/null
"$EPMD" -kill >/dev/null
}
}
}
@@ -485,7 +438,7 @@ case "${ARGS[0]}" in
'live') live;;
'iexlive') iexlive;;
'foreground') foreground;;
'ping'*) ping ${ARGS# ping};;
'ping'*) ping ${ARGS[1]};;
'etop') etop;;
'started') wait_for_status 0 30 2;; # wait 30x2s before timeout
'stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout
+3 -2
View File
@@ -31,8 +31,9 @@
tags = [] :: [atom()] | '_' | '$2',
desc = "" :: string() | '_' | '$3',
longdesc = "" :: string() | '_',
module :: atom(),
function :: atom(),
version = 0 :: integer(),
module :: atom() | '_',
function :: atom() | '_',
args = [] :: [aterm()] | '_' | '$1' | '$2',
policy = restricted :: open | restricted | admin | user,
result = {res, rescode} :: rterm() | '_' | '$2',
+1 -2
View File
@@ -23,8 +23,7 @@
path = [] :: [binary()],
q = [] :: [{binary() | nokey, binary()}],
us = {<<>>, <<>>} :: {binary(), binary()},
auth :: {binary(), binary()} |
{auth_jid, {binary(), binary()}, jlib:jid()},
auth :: {binary(), binary()} | {oauth, binary(), []} | undefined,
lang = <<"">> :: binary(),
data = <<"">> :: binary(),
ip :: {inet:ip_address(), inet:port_number()},
+33
View File
@@ -0,0 +1,33 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2016 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.
%%%
%%%----------------------------------------------------------------------
-define(SQL_MARK, sql__mark_).
-define(SQL(SQL), ?SQL_MARK(SQL)).
-define(SQL_UPSERT_MARK, sql_upsert__mark_).
-define(SQL_UPSERT(Host, Table, Fields),
ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))).
-define(SQL_UPSERT_T(Table, Fields),
ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))).
-record(sql_query, {hash, format_query, format_res, args, loc}).
-record(sql_escape, {string, integer, boolean}).
+7
View File
@@ -34,3 +34,10 @@
-define(CRITICAL_MSG(Format, Args),
lager:critical(Format, Args)).
%% Use only when trying to troubleshoot test problem with ExUnit
-define(EXUNIT_LOG(Format, Args),
case lists:keyfind(logger, 1, application:loaded_applications()) of
false -> ok;
_ -> 'Elixir.Logger':bare_log(error, io_lib:format(Format, Args), [?MODULE])
end).
+5
View File
@@ -0,0 +1,5 @@
-record(motd, {server = <<"">> :: binary(),
packet = #xmlel{} :: xmlel()}).
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
dummy = [] :: [] | '_'}).
+4
View File
@@ -0,0 +1,4 @@
-record(caps_features,
{node_pair = {<<"">>, <<"">>} :: {binary(), binary()},
features = [] :: [binary()] | pos_integer()
}).
+4
View File
@@ -0,0 +1,4 @@
-type matchspec_atom() :: '_' | '$1' | '$2' | '$3'.
-record(carboncopy, {us :: {binary(), binary()} | matchspec_atom(),
resource :: binary() | matchspec_atom(),
version :: binary() | matchspec_atom()}).
+15
View File
@@ -0,0 +1,15 @@
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
{binary(), binary(), inet:port_number()} |
{binary(), binary()} |
{binary()}.
-type irc_data() :: [{username, binary()} | {connections_params, [conn_param()]}].
-record(irc_connection,
{jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()},
pid = self() :: pid()}).
-record(irc_custom,
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()},
binary()},
data = [] :: irc_data()}).
+3
View File
@@ -0,0 +1,3 @@
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
timestamp = 0 :: non_neg_integer(),
status = <<"">> :: binary()}).
+15
View File
@@ -0,0 +1,15 @@
-record(archive_msg,
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
id = <<>> :: binary() | '_',
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_' | '$1',
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
packet = #xmlel{} :: xmlel() | '_',
nick = <<"">> :: binary(),
type = chat :: chat | groupchat}).
-record(archive_prefs,
{us = {<<"">>, <<"">>} :: {binary(), binary()},
default = never :: never | always | roster,
always = [] :: [ljid()],
never = [] :: [ljid()]}).
+4
View File
@@ -0,0 +1,4 @@
-record(private_storage,
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
'$1' | '_'},
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
+5
View File
@@ -0,0 +1,5 @@
-record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()},
opts = [] :: list() | '_' | '$2'}).
-record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()},
group_host = {<<"">>, <<"">>} :: {binary(), binary()}}).
+8
View File
@@ -0,0 +1,8 @@
-record(vcard_search,
{us, user, luser, fn, lfn, family, lfamily, given,
lgiven, middle, lmiddle, nickname, lnickname, bday,
lbday, ctry, lctry, locality, llocality, email, lemail,
orgname, lorgname, orgunit, lorgunit}).
-record(vcard, {us = {<<"">>, <<"">>} :: {binary(), binary()} | binary(),
vcard = #xmlel{} :: xmlel()}).
+2
View File
@@ -0,0 +1,2 @@
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
hash = <<>> :: binary()}).
+7
View File
@@ -157,3 +157,10 @@
-define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>).
-define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>).
-define(NS_NICK, <<"http://jabber.org/protocol/nick">>).
-define(NS_MIX_0, <<"urn:xmpp:mix:0">>).
-define(NS_MIX_SERVICEINFO_0, <<"urn:xmpp:mix:0#serviceinfo">>).
-define(NS_MIX_NODES_MESSAGES, <<"urn:xmpp:mix:nodes:messages">>).
-define(NS_MIX_NODES_PRESENCE, <<"urn:xmpp:mix:nodes:presence">>).
-define(NS_MIX_NODES_PARTICIPANTS, <<"urn:xmpp:mix:nodes:participants">>).
-define(NS_MIX_NODES_SUBJECT, <<"urn:xmpp:mix:nodes:subject">>).
-define(NS_MIX_NODES_CONFIG, <<"urn:xmpp:mix:nodes:config">>).
+1 -1
View File
@@ -65,7 +65,7 @@
%% note: pos_integer() should always be used, but we allow anything else coded
%% as binary, so one can have a custom implementation of nodetree with custom
%% indexing (see nodetree_virtual). this also allows to use any kind of key for
%% indexing nodes, as this can be usefull with external backends such as odbc.
%% indexing nodes, as this can be usefull with external backends such as sql.
-type(itemId() :: binary()).
%% @type itemId() = string().
+130
View File
@@ -0,0 +1,130 @@
defmodule ExUnit.CTFormatter do
@moduledoc false
use GenEvent
import ExUnit.Formatter, only: [format_time: 2, format_filters: 2, format_test_failure: 5,
format_test_case_failure: 5]
def init(opts) do
file = File.open! "exunit.log", [:append]
# We do not print filter in log file as exclusion of test with tag
# pending: true is always done
config = %{
file: file,
seed: opts[:seed],
trace: opts[:trace],
colors: Keyword.put_new(opts[:colors], :enabled, false),
width: 80,
tests_counter: 0,
failures_counter: 0,
skipped_counter: 0,
invalids_counter: 0
}
{:ok, config}
end
def handle_event({:suite_started, _opts}, config) do
{:ok, config}
end
def handle_event({:suite_finished, run_us, load_us}, config) do
print_suite(config, run_us, load_us)
File.close config[:file]
:remove_handler
end
def handle_event({:test_started, %ExUnit.Test{} = test}, config) do
if config.tests_counter == 0, do: IO.binwrite config[:file], "== Running #{test.case} ==\n\n"
{:ok, config}
end
def handle_event({:test_finished, %ExUnit.Test{state: nil} = _test}, config) do
IO.binwrite config[:file], "."
{:ok, %{config | tests_counter: config.tests_counter + 1}}
end
def handle_event({:test_finished, %ExUnit.Test{state: {:skip, _}} = _test}, config) do
{:ok, %{config | tests_counter: config.tests_counter + 1,
skipped_counter: config.skipped_counter + 1}}
end
def handle_event({:test_finished, %ExUnit.Test{state: {:invalid, _}} = _test}, config) do
IO.binwrite config[:file], "?"
{:ok, %{config | tests_counter: config.tests_counter + 1,
invalids_counter: config.invalids_counter + 1}}
end
def handle_event({:test_finished, %ExUnit.Test{state: {:failed, failures}} = test}, config) do
formatted = format_test_failure(test, failures, config.failures_counter + 1,
config.width, &formatter(&1, &2, config))
print_failure(formatted, config)
print_logs(test.logs)
{:ok, %{config | tests_counter: config.tests_counter + 1,
failures_counter: config.failures_counter + 1}}
end
def handle_event({:case_started, %ExUnit.TestCase{}}, config) do
{:ok, config}
end
def handle_event({:case_finished, %ExUnit.TestCase{state: nil}}, config) do
{:ok, config}
end
def handle_event({:case_finished, %ExUnit.TestCase{state: {:failed, failures}} = test_case}, config) do
formatted = format_test_case_failure(test_case, failures, config.failures_counter + 1,
config.width, &formatter(&1, &2, config))
print_failure(formatted, config)
{:ok, %{config | failures_counter: config.failures_counter + 1}}
end
## Printing
defp print_suite(config, run_us, load_us) do
IO.binwrite config[:file], "\n\n"
IO.binwrite config[:file], format_time(run_us, load_us)
IO.binwrite config[:file], "\n\n"
# singular/plural
test_pl = pluralize(config.tests_counter, "test", "tests")
failure_pl = pluralize(config.failures_counter, "failure", "failures")
message =
"#{config.tests_counter} #{test_pl}, #{config.failures_counter} #{failure_pl}"
|> if_true(config.skipped_counter > 0, & &1 <> ", #{config.skipped_counter} skipped")
|> if_true(config.invalids_counter > 0, & &1 <> ", #{config.invalids_counter} invalid")
cond do
config.failures_counter > 0 -> IO.binwrite config[:file], message
config.invalids_counter > 0 -> IO.binwrite config[:file], message
true -> IO.binwrite config[:file], message
end
IO.binwrite config[:file], "\nRandomized with seed #{config.seed}\n\n\n\n"
end
defp if_true(value, false, _fun), do: value
defp if_true(value, true, fun), do: fun.(value)
defp print_failure(formatted, config) do
IO.binwrite config[:file], "\n"
IO.binwrite config[:file], formatted
IO.binwrite config[:file], "\n"
end
defp formatter(_, msg, _config),
do: msg
defp pluralize(1, singular, _plural), do: singular
defp pluralize(_, _singular, plural), do: plural
defp print_logs(""), do: nil
defp print_logs(output) do
indent = "\n "
output = String.replace(output, "\n", indent)
IO.puts([" The following output was logged:", indent | output])
end
end
-2
View File
@@ -1,2 +0,0 @@
defmodule Ejabberd do
end
+2 -2
View File
@@ -3,9 +3,9 @@ defmodule Ejabberd.Mixfile do
def project do
[app: :ejabberd,
version: "16.01.0-beta1",
version: "16.04.0",
description: description,
elixir: "~> 1.1",
elixir: "~> 1.2",
elixirc_paths: ["lib"],
compile_path: ".",
compilers: [:asn1] ++ Mix.compilers,
+13 -12
View File
@@ -1,13 +1,14 @@
%{"bbmustache": {:hex, :bbmustache, "1.0.3"},
%{"bbmustache": {:hex, :bbmustache, "1.0.4"},
"cache_tab": {:hex, :cache_tab, "1.0.2"},
"cf": {:hex, :cf, "0.2.1"},
"eredis": {:hex, :eredis, "1.0.8"},
"erlware_commons": {:hex, :erlware_commons, "0.15.0"},
"esip": {:hex, :esip, "1.0.2"},
"exrm": {:hex, :exrm, "1.0.0-rc7"},
"erlware_commons": {:hex, :erlware_commons, "0.19.0"},
"esip": {:hex, :esip, "1.0.4"},
"exrm": {:hex, :exrm, "1.0.3"},
"ezlib": {:hex, :ezlib, "1.0.1"},
"fast_tls": {:hex, :fast_tls, "1.0.1"},
"fast_xml": {:hex, :fast_xml, "1.1.3"},
"fast_yaml": {:hex, :fast_yaml, "1.0.2"},
"fast_tls": {:hex, :fast_tls, "1.0.3"},
"fast_xml": {:hex, :fast_xml, "1.1.11"},
"fast_yaml": {:hex, :fast_yaml, "1.0.3"},
"getopt": {:hex, :getopt, "0.8.2"},
"goldrush": {:hex, :goldrush, "0.1.7"},
"iconv": {:hex, :iconv, "1.0.0"},
@@ -15,11 +16,11 @@
"lager": {:hex, :lager, "3.0.2"},
"p1_mysql": {:hex, :p1_mysql, "1.0.1"},
"p1_oauth2": {:hex, :p1_oauth2, "0.6.1"},
"p1_pgsql": {:hex, :p1_pgsql, "1.0.1"},
"p1_pgsql": {:hex, :p1_pgsql, "1.1.0"},
"p1_utils": {:hex, :p1_utils, "1.0.3"},
"p1_xmlrpc": {:hex, :p1_xmlrpc, "1.15.1"},
"providers": {:hex, :providers, "1.4.1"},
"relx": {:hex, :relx, "3.5.0"},
"providers": {:hex, :providers, "1.6.0"},
"relx": {:hex, :relx, "3.19.0"},
"sqlite3": {:hex, :sqlite3, "1.1.5"},
"stringprep": {:hex, :stringprep, "1.0.2"},
"stun": {:hex, :stun, "1.0.1"}}
"stringprep": {:hex, :stringprep, "1.0.3"},
"stun": {:hex, :stun, "1.0.3"}}
+113 -135
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: 2.1.0-alpha\n"
"Last-Translator: Carlos E. Lopez - suso AT jabber-hispano.org\n"
"Project-Id-Version: 16.02\n"
"Last-Translator: Carlos E. Lopez - carlos AT suchat.org\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -9,7 +9,7 @@ msgstr ""
#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853
msgid "Use of STARTTLS required"
msgstr "É obrigatorio usar STARTTLS"
msgstr "Requírese o uso de STARTTLS"
#: ejabberd_c2s.erl:604
msgid "No resource provided"
@@ -26,11 +26,11 @@ msgstr "foi expulsado"
#: ejabberd_c2s.erl:2114
msgid "Your active privacy list has denied the routing of this stanza."
msgstr ""
msgstr "A súa lista de privacidade activa negou o encaminamiento desta estrofa."
#: ejabberd_c2s.erl:2429
msgid "Too many unacked stanzas"
msgstr ""
msgstr "Demasiadas mensaxes sen recoñecer recibilos"
#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284
msgid "Enter the text you see"
@@ -43,11 +43,11 @@ msgstr ""
#: ejabberd_captcha.erl:192
msgid "If you don't see the CAPTCHA image here, visit the web page."
msgstr ""
msgstr "Si non ves a imaxe CAPTCHA aquí, visita a páxina web."
#: ejabberd_captcha.erl:227
msgid "CAPTCHA web page"
msgstr ""
msgstr "CAPTCHA páxina Web"
#: ejabberd_captcha.erl:381
msgid "The CAPTCHA is valid."
@@ -59,9 +59,8 @@ msgid "User"
msgstr "Usuario"
#: ejabberd_oauth.erl:256
#, fuzzy
msgid "Server"
msgstr "Servidor ~b"
msgstr "Servidor"
#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398
#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123
@@ -71,7 +70,7 @@ msgstr "Contrasinal"
#: ejabberd_oauth.erl:267
msgid "Accept"
msgstr ""
msgstr "Aceptar"
#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214
#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246
@@ -238,7 +237,6 @@ msgid "Outgoing s2s Connections:"
msgstr "Conexións S2S saíntes:"
#: ejabberd_web_admin.erl:1559
#, fuzzy
msgid "Incoming s2s Connections:"
msgstr "Conexións S2S saíntes:"
@@ -253,9 +251,8 @@ msgid "Change Password"
msgstr "Cambiar contrasinal"
#: ejabberd_web_admin.erl:1673
#, fuzzy
msgid "User ~s"
msgstr "Usuario "
msgstr "Usuario ~s"
#: ejabberd_web_admin.erl:1684
msgid "Connected Resources:"
@@ -287,9 +284,8 @@ msgid "Stopped Nodes"
msgstr "Nodos detidos"
#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858
#, fuzzy
msgid "Node ~p"
msgstr "Nodo "
msgstr "Nodo ~p"
#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611
msgid "Database"
@@ -297,7 +293,7 @@ msgstr "Base de datos"
#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648
msgid "Backup"
msgstr "Gardar copia de seguridade"
msgstr "Copia de seguridade"
#: ejabberd_web_admin.erl:1845
msgid "Listened Ports"
@@ -326,9 +322,8 @@ msgid "RPC Call Error"
msgstr "Erro na chamada RPC"
#: ejabberd_web_admin.erl:1917
#, fuzzy
msgid "Database Tables at ~p"
msgstr "Táboas da base de datos en "
msgstr "Táboas da base de datos en ~p"
#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616
msgid "Name"
@@ -336,7 +331,7 @@ msgstr "Nome"
#: ejabberd_web_admin.erl:1928
msgid "Storage Type"
msgstr "Tipo de almacenamiento"
msgstr "Tipo de almacenamento"
#: ejabberd_web_admin.erl:1929
msgid "Elements"
@@ -351,9 +346,8 @@ msgid "Error"
msgstr "Erro"
#: ejabberd_web_admin.erl:1955
#, fuzzy
msgid "Backup of ~p"
msgstr "Copia de seguridade de "
msgstr "Copia de seguridade de ~p"
#: ejabberd_web_admin.erl:1959
msgid ""
@@ -387,7 +381,7 @@ msgid ""
"Restore binary backup after next ejabberd restart (requires less memory):"
msgstr ""
"Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd "
"(require menos memoria que se instantánea):"
"(require menos memoria):"
#: ejabberd_web_admin.erl:1999
msgid "Store plain text backup:"
@@ -399,7 +393,7 @@ msgstr "Restaurar copias de seguridade de texto plano inmediatamente:"
#: ejabberd_web_admin.erl:2019
msgid "Import users data from a PIEFXIS file (XEP-0227):"
msgstr "Importar usuarios desde un fichero PIEFXIS"
msgstr "Importar usuarios en un fichero PIEFXIS (XEP-0227):"
#: ejabberd_web_admin.erl:2032
msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):"
@@ -409,17 +403,15 @@ msgstr ""
#: ejabberd_web_admin.erl:2044
msgid "Export data of users in a host to PIEFXIS files (XEP-0227):"
msgstr ""
"Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS "
"(XEP-0227):"
msgstr "Exportar datos dos usuarios dun dominio a ficheiros PIEFXIS (XEP-0227):"
#: ejabberd_web_admin.erl:2060
msgid "Export all tables as SQL queries to a file:"
msgstr ""
msgstr "Exportar todas as táboas a un ficheiro SQL:"
#: ejabberd_web_admin.erl:2076
msgid "Import user data from jabberd14 spool file:"
msgstr "Importar usuario de fichero spool de jabberd14:"
msgstr "Importar usuario de ficheiro spool de jabberd14:"
#: ejabberd_web_admin.erl:2087
msgid "Import users data from jabberd14 spool directory:"
@@ -430,9 +422,8 @@ msgid "Listened Ports at "
msgstr "Portos de escoita en "
#: ejabberd_web_admin.erl:2144
#, fuzzy
msgid "Modules at ~p"
msgstr "Módulos en "
msgstr "Módulos en ~p"
#: ejabberd_web_admin.erl:2175
msgid "Statistics of ~p"
@@ -463,9 +454,8 @@ msgid "Transactions Logged:"
msgstr "Transaccións rexistradas:"
#: ejabberd_web_admin.erl:2243
#, fuzzy
msgid "Update ~p"
msgstr "Actualizar"
msgstr "Actualizar ~p"
#: ejabberd_web_admin.erl:2254
msgid "Update plan"
@@ -525,7 +515,7 @@ msgstr "Pong"
#: mod_announce.erl:523
msgid "Really delete message of the day?"
msgstr "Está seguro de quere borrar a mensaxe do dia?"
msgstr "¿Está seguro que quere borrar a mensaxe do dia?"
#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298
msgid "Subject"
@@ -553,7 +543,7 @@ msgstr "Enviar anuncio a todos os usuarios en todos os dominios"
#: mod_announce.erl:668
msgid "Send announcement to all online users"
msgstr "Enviar anuncio a todos los usuarios conectados"
msgstr "Enviar anuncio a todos os usuarios conectados"
#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291
msgid "Send announcement to all online users on all hosts"
@@ -595,7 +585,7 @@ msgstr "Iniciar módulos"
#: mod_configure.erl:156 mod_configure.erl:638
msgid "Stop Modules"
msgstr "Detener módulos"
msgstr "Deter módulos"
#: mod_configure.erl:162 mod_configure.erl:650
msgid "Restore"
@@ -844,18 +834,20 @@ msgid ""
"Too many (~p) failed authentications from this IP address (~s). The address "
"will be unblocked at ~s UTC"
msgstr ""
"Demasiados (~p) fallou autenticaciones desde esta dirección IP (~s). A dirección "
"será desbloqueada as ~s UTC"
#: mod_http_upload.erl:586
msgid "Please specify file size."
msgstr ""
msgstr "Por favor, especifica o tamaño do arquivo"
#: mod_http_upload.erl:590
msgid "Please specify file name."
msgstr ""
msgstr "Por favor, indique o nome do arquivo."
#: mod_ip_blacklist.erl:121
msgid "This IP address is blacklisted in ~s"
msgstr ""
msgstr "Esta dirección IP está na lista negra en ~s"
#: mod_irc.erl:220 mod_muc.erl:467
msgid "Access denied by service policy"
@@ -884,8 +876,8 @@ msgid ""
"Enter username, encodings, ports and passwords you wish to use for "
"connecting to IRC servers"
msgstr ""
"Introduza o nome de usuario, codificaciones de carácter, portos e "
"contrasinal que pretende utilizar a conectar a servidores de IRC"
"Introduce o nome de usuario, codificaciones de carácteres, portos e "
"contrasinai que queiras usar ao conectar nos servidores de IRC"
#: mod_irc.erl:667
msgid "IRC Username"
@@ -970,9 +962,8 @@ msgid "Server ~b"
msgstr "Servidor ~b"
#: mod_mam.erl:541
#, fuzzy
msgid "Only members may query archives of this room"
msgstr "Só os moderadores están autorizados a cambiar o tema nesta sala"
msgstr "Só membros poden consultar o arquivo de mensaxes da sala"
#: mod_muc.erl:585
msgid "Only service administrators are allowed to send service messages"
@@ -994,10 +985,9 @@ msgstr "Salas de charla"
#: mod_muc.erl:781
msgid "Empty Rooms"
msgstr ""
msgstr "Salas baleiras"
#: mod_muc.erl:933
#, fuzzy
msgid "You need a client that supports x:data to register the nickname"
msgstr ""
"Necesitas un cliente con soporte de x:data para poder rexistrar o alcume"
@@ -1030,34 +1020,31 @@ msgstr "Módulo de MUC para ejabberd"
#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246
#: mod_muc_admin.erl:320
msgid "Multi-User Chat"
msgstr ""
msgstr "Salas de Charla"
#: mod_muc_admin.erl:249
#, fuzzy
msgid "Total rooms"
msgstr "Salas de charla"
msgstr "Salas totais"
#: mod_muc_admin.erl:250
#, fuzzy
msgid "Permanent rooms"
msgstr "sae da sala"
msgstr "Salas permanentes"
#: mod_muc_admin.erl:251
#, fuzzy
msgid "Registered nicknames"
msgstr "Usuarios rexistrados"
msgstr "Alcumes rexistrados"
#: mod_muc_admin.erl:254
msgid "List of rooms"
msgstr ""
msgstr "Lista de salas"
#: mod_muc_log.erl:398 mod_muc_log.erl:407
msgid "Chatroom configuration modified"
msgstr "Configuración de la sala modificada"
msgstr "Configuración da sala modificada"
#: mod_muc_log.erl:410
msgid "joins the room"
msgstr "entra en la sala"
msgstr "entra na sala"
#: mod_muc_log.erl:413 mod_muc_log.erl:416
msgid "leaves the room"
@@ -1077,7 +1064,7 @@ msgstr "foi expulsado, porque a sala cambiouse a só-membros"
#: mod_muc_log.erl:445
msgid "has been kicked because of a system shutdown"
msgstr "foi expulsado por mor dun sistema de peche"
msgstr "foi expulsado porque o sistema vaise a deter"
#: mod_muc_log.erl:450
msgid "is now known as"
@@ -1088,24 +1075,20 @@ msgid " has set the subject to: "
msgstr " puxo o asunto: "
#: mod_muc_log.erl:493
#, fuzzy
msgid "Chatroom is created"
msgstr "Salas de charla"
msgstr "Creouse a sala"
#: mod_muc_log.erl:495
#, fuzzy
msgid "Chatroom is destroyed"
msgstr "Salas de charla"
msgstr "Destruíuse a sala"
#: mod_muc_log.erl:497
#, fuzzy
msgid "Chatroom is started"
msgstr "Salas de charla"
msgstr "Iniciouse a sala"
#: mod_muc_log.erl:499
#, fuzzy
msgid "Chatroom is stopped"
msgstr "Salas de charla"
msgstr "Detívose a sala"
#: mod_muc_log.erl:503
msgid "Monday"
@@ -1200,6 +1183,8 @@ msgid ""
"It is not allowed to send error messages to the room. The participant (~s) "
"has sent an error message (~s) and got kicked from the room"
msgstr ""
"Non está permitido enviar mensaxes de erro á sala. Este participante (~s) "
"enviou unha mensaxe de erro (~s) e foi expulsado da sala"
#: mod_muc_room.erl:241
msgid "It is not allowed to send private messages to the conference"
@@ -1207,20 +1192,19 @@ msgstr "Impedir o envio de mensaxes privadas á sala"
#: mod_muc_room.erl:316
msgid "Please, wait for a while before sending new voice request"
msgstr ""
msgstr "Por favor, espera un pouco antes de enviar outra petición de voz"
#: mod_muc_room.erl:329
msgid "Voice requests are disabled in this conference"
msgstr ""
msgstr "As peticións de voz están desactivadas nesta sala"
#: mod_muc_room.erl:347
msgid "Failed to extract JID from your voice request approval"
msgstr ""
msgstr "Fallo ao extraer o Jabber ID da túa aprobación de petición de voz"
#: mod_muc_room.erl:377
#, fuzzy
msgid "Only moderators can approve voice requests"
msgstr "Permitir aos usuarios enviar invitacións"
msgstr "Só os moderadores poden aprobar peticións de voz"
#: mod_muc_room.erl:389
msgid "Improper message type"
@@ -1269,11 +1253,11 @@ msgstr "Os visitantes non poden enviar mensaxes a todos os ocupantes"
#: mod_muc_room.erl:1080
msgid "Visitors are not allowed to change their nicknames in this room"
msgstr ""
"Os visitantes non están autorizados a cambiar os seus That alcumes nesta sala"
"Os visitantes non teñen permitido cambiar os seus alcumes nesta sala"
#: mod_muc_room.erl:1093 mod_muc_room.erl:1835
msgid "That nickname is already in use by another occupant"
msgstr "Ese alcume que xa está en uso por outro ocupante"
msgstr "Ese alcume xa está a ser usado por outro ocupante"
#: mod_muc_room.erl:1822
msgid "You have been banned from this room"
@@ -1289,12 +1273,11 @@ msgstr "Necesítase contrasinal para entrar nesta sala"
#: mod_muc_room.erl:1898 mod_register.erl:295
msgid "Too many CAPTCHA requests"
msgstr ""
msgstr "Demasiadas peticións de CAPTCHA"
#: mod_muc_room.erl:1908 mod_register.erl:301
#, fuzzy
msgid "Unable to generate a CAPTCHA"
msgstr "Non se pode xerar un CAPTCHA"
msgstr "No se pudo generar un CAPTCHA"
#: mod_muc_room.erl:1919
msgid "Incorrect password"
@@ -1378,20 +1361,19 @@ msgstr "calquera"
#: mod_muc_room.erl:3471
msgid "Roles for which Presence is Broadcasted"
msgstr ""
msgstr "Roles para os que si se difunde a súa Presenza"
#: mod_muc_room.erl:3486
#, fuzzy
msgid "Moderator"
msgstr "só moderadores"
msgstr "Moderator"
#: mod_muc_room.erl:3496
msgid "Participant"
msgstr ""
msgstr "Participante"
#: mod_muc_room.erl:3506
msgid "Visitor"
msgstr ""
msgstr "Visitante"
#: mod_muc_room.erl:3513
msgid "Make room members-only"
@@ -1414,13 +1396,12 @@ msgid "Allow users to send private messages"
msgstr "Permitir aos usuarios enviar mensaxes privadas"
#: mod_muc_room.erl:3533
#, fuzzy
msgid "Allow visitors to send private messages to"
msgstr "Permitir aos usuarios enviar mensaxes privadas"
msgstr "Permitir aos visitantes enviar mensaxes privadas a"
#: mod_muc_room.erl:3551
msgid "nobody"
msgstr ""
msgstr "ninguén"
#: mod_muc_room.erl:3576
msgid "Allow users to query other users"
@@ -1440,13 +1421,12 @@ msgid "Allow visitors to change nickname"
msgstr "Permitir aos visitantes cambiarse o alcume"
#: mod_muc_room.erl:3589
#, fuzzy
msgid "Allow visitors to send voice requests"
msgstr "Permitir aos usuarios enviar invitacións"
msgstr "Permitir aos visitantes enviar peticións de voz"
#: mod_muc_room.erl:3592
msgid "Minimum interval between voice requests (in seconds)"
msgstr ""
msgstr "Intervalo mínimo entre peticións de voz (en segundos)"
#: mod_muc_room.erl:3599
msgid "Make room CAPTCHA protected"
@@ -1454,11 +1434,11 @@ msgstr "Protexer a sala con CAPTCHA"
#: mod_muc_room.erl:3606
msgid "Enable message archiving"
msgstr ""
msgstr "Activar o almacenamento de mensaxes"
#: mod_muc_room.erl:3612
msgid "Exclude Jabber IDs from CAPTCHA challenge"
msgstr ""
msgstr "Excluír Jabber IDs das probas de CAPTCHA"
#: mod_muc_room.erl:3621
msgid "Enable logging"
@@ -1478,20 +1458,19 @@ msgstr "privado"
#: mod_muc_room.erl:4326
msgid "Voice request"
msgstr ""
msgstr "Petición de voz"
#: mod_muc_room.erl:4331
msgid "Either approve or decline the voice request."
msgstr ""
msgstr "Aproba ou rexeita a petición de voz."
#: mod_muc_room.erl:4351
#, fuzzy
msgid "User JID"
msgstr "Usuario "
msgstr "Jabber ID do usuario"
#: mod_muc_room.erl:4355
msgid "Grant voice to this person?"
msgstr ""
msgstr "¿Conceder voz a esta persoa?"
#: mod_muc_room.erl:4498
msgid "~s invites you to the room ~s"
@@ -1503,11 +1482,11 @@ msgstr "a contrasinal é"
#: mod_multicast.erl:291
msgid "Multicast"
msgstr ""
msgstr "Multicast"
#: mod_multicast.erl:306
msgid "ejabberd Multicast service"
msgstr ""
msgstr "Servizo Multicast de ejabberd"
#: mod_offline.erl:647
msgid ""
@@ -1546,7 +1525,7 @@ msgstr "Borrar Todas as Mensaxes Sen conexión"
#: mod_proxy65_service.erl:248
msgid "ejabberd SOCKS5 Bytestreams module"
msgstr "ejabberd SOCKS5 Bytestreams module"
msgstr "Módulo SOCKS5 Bytestreams para ejabberd"
#: mod_pubsub.erl:1102
msgid "Publish-Subscribe"
@@ -1566,7 +1545,7 @@ msgstr "Decidir se aprobar a subscripción desta entidade."
#: mod_pubsub.erl:1559
msgid "Node ID"
msgstr "Nodo IDE"
msgstr "Nodo ID"
#: mod_pubsub.erl:1571
msgid "Subscriber Address"
@@ -1602,7 +1581,7 @@ msgstr "Persistir elementos ao almacenar"
#: mod_pubsub.erl:3757
msgid "A friendly name for the node"
msgstr "Un nome para o nodo"
msgstr "Un nome sinxelo para o nodo"
#: mod_pubsub.erl:3759
msgid "Max # of items to persist"
@@ -1626,12 +1605,11 @@ msgstr "Especificar o modelo do publicante"
#: mod_pubsub.erl:3769
msgid "Purge all items when the relevant publisher goes offline"
msgstr ""
msgstr "Purgar todos os elementos cando o editor correspondente desconéctase"
#: mod_pubsub.erl:3771
#, fuzzy
msgid "Specify the event message type"
msgstr "Especifica o modelo de acceso"
msgstr "Especifica o tipo da mensaxe de evento"
#: mod_pubsub.erl:3773
msgid "Max payload size in bytes"
@@ -1650,15 +1628,13 @@ msgid "The collections with which a node is affiliated"
msgstr "As coleccións coas que un nodo está afiliado"
#: mod_register.erl:209
#, fuzzy
msgid "The CAPTCHA verification has failed"
msgstr "O CAPTCHA é válido."
msgstr "A verificación de CAPTCHA fallou"
#: mod_register.erl:253
#, fuzzy
msgid "You need a client that supports x:data and CAPTCHA to register"
msgstr ""
"Necesitas un cliente con soporte de x:data para poder rexistrar o alcume"
"Necesitas un cliente con soporte de x:data e CAPTCHA para rexistrarche"
#: mod_register.erl:259 mod_register.erl:320
msgid "Choose a username and password to register with this server"
@@ -1666,9 +1642,8 @@ msgstr ""
"Escolle un nome de usuario e contrasinal para rexistrarche neste servidor"
#: mod_register.erl:373 mod_register.erl:421
#, fuzzy
msgid "The password is too weak"
msgstr "a contrasinal é"
msgstr "O contrasinal é demasiado débil"
#: mod_register.erl:426
msgid "Users are not allowed to register accounts so quickly"
@@ -1676,39 +1651,39 @@ msgstr "Os usuarios non están autorizados a rexistrar contas con tanta rapidez"
#: mod_register_web.erl:105
msgid "Your Jabber account was successfully created."
msgstr ""
msgstr "A súa conta Jabber creouse correctamente."
#: mod_register_web.erl:110
msgid "There was an error creating the account: "
msgstr ""
msgstr "Produciuse un erro ao crear a conta: "
#: mod_register_web.erl:119
msgid "Your Jabber account was successfully deleted."
msgstr ""
msgstr "A súa conta Jabber eliminouse correctamente."
#: mod_register_web.erl:124
msgid "There was an error deleting the account: "
msgstr ""
msgstr "Produciuse un erro ao eliminar a conta: "
#: mod_register_web.erl:135
msgid "The password of your Jabber account was successfully changed."
msgstr ""
msgstr "O contrasinal da súa conta Jabber cambiouse correctamente."
#: mod_register_web.erl:140
msgid "There was an error changing the password: "
msgstr ""
msgstr "Produciuse un erro ao cambiar o contrasinal: "
#: mod_register_web.erl:175 mod_register_web.erl:183
msgid "Jabber Account Registration"
msgstr ""
msgstr "Rexistro de conta Jabber"
#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212
msgid "Register a Jabber account"
msgstr ""
msgstr "Rexistrar unha conta Jabber"
#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461
msgid "Unregister a Jabber account"
msgstr ""
msgstr "Eliminar o rexistro dunha conta Jabber"
#: mod_register_web.erl:214
msgid ""
@@ -1716,40 +1691,45 @@ msgid ""
"(Jabber IDentifier) will be of the form: username@server. Please read "
"carefully the instructions to fill correctly the fields."
msgstr ""
"Esta páxina permite crear unha conta Jabber neste servidor Jabber. o seu JID "
"(Jabber IDentificador) será da forma: nomeusuario@servidor. Por favor le "
"coidadosamente as instrucións para encher correctamente os campos."
#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469
#, fuzzy
msgid "Username:"
msgstr "Nome de usuario en IRC"
msgstr "Nome de usuario:"
#: mod_register_web.erl:230
msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth."
msgstr ""
msgstr "Esta é insensible: Macbeth é o mesmo que MacBeth e Macbeth."
#: mod_register_web.erl:233
msgid "Characters not allowed:"
msgstr ""
msgstr "Caracteres non permitidos:"
#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473
#, fuzzy
msgid "Server:"
msgstr "Servidor ~b"
msgstr "Servidor:"
#: mod_register_web.erl:245
msgid ""
"Don't tell your password to anybody, not even the administrators of the "
"Jabber server."
"Jabber Server."
msgstr ""
"Non lle diga o seu contrasinal a ninguén, nin sequera os administradores do "
"Servidor Jabber."
#: mod_register_web.erl:249
msgid "You can later change your password using a Jabber client."
msgstr ""
msgstr "Máis tarde, pode cambiar o seu contrasinal utilizando un cliente Jabber."
#: mod_register_web.erl:252
msgid ""
"Some Jabber clients can store your password in the computer, but you should "
"do this only in your personal computer for safety reasons."
msgstr ""
"Algúns clientes Jabber pode almacenar o contrasinal no computador, pero debe "
"facer isto só no seu computador persoal por razóns de seguridade."
#: mod_register_web.erl:256
msgid ""
@@ -1757,34 +1737,33 @@ msgid ""
"Jabber there isn't an automated way to recover your password if you forget "
"it."
msgstr ""
"Memorice o seu contrasinal ou escribilo nun papel colocado nun lugar seguro. En "
"Jabber non hai unha forma automatizada para recuperar o seu contrasinal si "
"a esquece"
#: mod_register_web.erl:262 mod_register_web.erl:374
#, fuzzy
msgid "Password Verification:"
msgstr "Verificación da contrasinal"
#: mod_register_web.erl:269
#, fuzzy
msgid "Register"
msgstr "Lista de contactos"
msgstr "Rexistrar"
#: mod_register_web.erl:366
#, fuzzy
msgid "Old Password:"
msgstr "Contrasinal:"
msgstr "Contrasinal anterior:"
#: mod_register_web.erl:370
#, fuzzy
msgid "New Password:"
msgstr "Contrasinal:"
msgstr "Novo contrasinal:"
#: mod_register_web.erl:463
msgid "This page allows to unregister a Jabber account in this Jabber server."
msgstr ""
msgstr "Esta páxina permite anular o rexistro dunha conta Jabber neste servidor Jabber."
#: mod_register_web.erl:480
msgid "Unregister"
msgstr ""
msgstr "Eliminar rexistro"
#: mod_roster.erl:1436
msgid "Subscription"
@@ -1872,8 +1851,8 @@ msgid ""
"Fill in the form to search for any matching Jabber User (Add * to the end of "
"field to match substring)"
msgstr ""
"Enche o formulario para buscar usuarios Jabber. Engade * ao final dun campo "
"para buscar subcadenas."
"Enche o formulario para buscar usuarios Jabber (Engade * ao final dun campo "
"para buscar subcadenas)"
#: mod_vcard.erl:490 mod_vcard.erl:615
msgid "Full Name"
@@ -1901,7 +1880,7 @@ msgstr "Necesitas un cliente con soporte de x:data para poder buscar"
#: mod_vcard.erl:519 mod_vcard_ldap.erl:531
msgid "vCard User Search"
msgstr "Procura de usuario en vCard"
msgstr "vCard busqueda de usuario"
#: mod_vcard.erl:580 mod_vcard_ldap.erl:586
msgid "ejabberd vCard module"
@@ -1942,7 +1921,6 @@ msgstr "Rechea campos para buscar usuarios Jabber que concuerden"
#~ "Este participante é expulsado da sala, porque el enviou un erro de "
#~ "presenza"
#, fuzzy
#~ msgid "CAPTCHA test failed"
#~ msgstr "O CAPTCHA é válido."
+36 -25
View File
@@ -8,44 +8,49 @@
%%%-------------------------------------------------------------------
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.0.2"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.3"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.4"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.2"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.1"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.2"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.3"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.3"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.3"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.1"}}},
{esip, ".*", {git, "https://github.com/processone/esip", "1.0.2"}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.2"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.3"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.4"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.3"}}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.7"}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2.git", {tag, "0.6.1"}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}},
{p1_xmlrpc, ".*", {git, "https://github.com/processone/p1_xmlrpc", {tag, "1.15.1"}}},
{luerl, ".*", {git, "https://github.com/rvirding/luerl",
"9524d0309a88b7c62ae93da0b632b185de3ba9db"}},
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
{tag, "1.0.1"}}}},
{tag, "1.0.1"}}}},
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
{tag, "1.0.1"}}}},
{tag, "1.1.0"}}}},
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
{tag, "1.1.5"}}}},
{tag, "1.1.5"}}}},
{if_var_true, pam, {p1_pam, ".*", {git, "https://github.com/processone/epam",
{tag, "1.0.0"}}}},
{tag, "1.0.0"}}}},
{if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib",
{tag, "1.0.1"}}}},
{if_var_true, riak, {hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang",
"908a24fda4a46776a5135db60ca071e3d783f9f6"}}}, % for riak_pb-2.1.0.7
{tag, "1.0.1"}}}},
{if_var_true, riak, {riakc, ".*", {git, "https://github.com/basho/riak-erlang-client",
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
%% Forces correct dependency for riakc and allow using newer meck version)
{if_var_true, riak, {hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang",
"908a24fda4a46776a5135db60ca071e3d783f9f6"}}}, % for riak_pb-2.1.0.7
{if_var_true, riak, {protobuffs, ".*", {git, "https://github.com/basho/erlang_protobuffs",
"6e7fc924506e2dc166a6170e580ce1d95ebbd5bd"}}}, % for riak_pb-2.1.0.7 with correct meck dependency
%% Elixir support, needed to run tests
{if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
{tag, "v1.1.0"}}}},
{tag, "v1.1.1"}}}},
%% TODO: When modules are fully migrated to new structure and mix, we will not need anymore rebar_elixir_plugin
{if_var_true, elixir, {rebar_elixir_plugin, ".*",
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}},
{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.0"}}}},
{if_var_true, tools, {meck, "0.8.2", {git, "https://github.com/eproxus/meck",
{tag, "0.8.2"}}}},
{tag, "1.0.0"}}}},
{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.git",
{tag, "1.0.5b"}}}},
{if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis",
{tag, "v1.0.8"}}}}]}.
{tag, "v1.0.8"}}}}]}.
{if_var_true, latest_deps,
{floating_deps, [cache_tab,
@@ -53,7 +58,7 @@
stringprep,
fast_xml,
esip,
luerl,
luerl,
stun,
fast_yaml,
p1_utils,
@@ -63,8 +68,11 @@
ezlib,
iconv]}}.
{erl_first_files, ["src/ejabberd_config.erl"]}.
{erl_opts, [nowarn_deprecated_function,
{if_var_false, debug, no_debug_info},
{if_var_true, debug, debug_info},
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
{if_var_match, db_type, mssql, {d, 'mssql'}},
{if_var_true, erlang_deprecated_types, {d, 'ERL_DEPRECATED_TYPES'}},
@@ -90,7 +98,7 @@
{xref_warnings, false}.
{xref_checks, [deprecated_function_calls, undefined_function_calls]}.
{xref_checks, [deprecated_function_calls]}.
{xref_exclusions, [
"(\"gen_transport\":_/_)",
@@ -109,6 +117,9 @@
{eunit_compile_opts, [{i, "tools"}]}.
{cover_enabled, true}.
{cover_export_enabled, true}.
{post_hook_configure, [{"fast_tls", []},
{"stringprep", []},
{"fast_yaml", []},
+26 -1
View File
@@ -7,6 +7,20 @@
%%% Created : 1 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
ModCfg0 = fun(F, 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}
end,
case Tail of
[] ->
[{Key, Op(OldVal)} | PartCfg];
_ ->
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
end
end,
ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end.
Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config")) of
{ok, Terms} ->
Terms;
@@ -107,9 +121,20 @@ Conf5 = case lists:keytake(floating_deps, 1, Conf3) of
Conf3
end,
%% When running Travis test, upload test coverage result to coveralls:
Conf6 = case os:getenv("TRAVIS") of
"true" ->
JobId = os:getenv("TRAVIS_JOB_ID"),
CfgTemp = ModCfg(Conf5, [deps], fun(V) -> [{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}|V] end, []),
ModCfg(CfgTemp, [post_hooks], fun(V) -> V ++ [{ct, "echo '\n%%! -pa ebin/ deps/coveralls/ebin\nmain(_)->{ok,F}=file:open(\"erlang.json\",[write]),io:fwrite(F,\"~s\",[coveralls:convert_file(\"logs/all.coverdata\", \""++JobId++"\", \"travis-ci\")]).' > getcover.erl"},
{ct, "escript ./getcover.erl"}] end, []);
_ ->
Conf5
end,
%io:format("ejabberd configuration:~n ~p~n", [Conf5]),
Conf5.
Conf6.
%% Local Variables:
%% mode: erlang
+3
View File
@@ -19,6 +19,9 @@
CREATE TABLE users (
username text PRIMARY KEY,
password text NOT NULL,
serverkey text NOT NULL DEFAULT '',
salt text NOT NULL DEFAULT '',
iterationcount integer NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
BIN
View File
Binary file not shown.
+6 -3
View File
@@ -19,12 +19,15 @@
CREATE TABLE users (
username varchar(191) PRIMARY KEY,
password text NOT NULL,
serverkey varchar(64) NOT NULL DEFAULT '',
salt varchar(64) NOT NULL DEFAULT '',
iterationcount integer NOT NULL DEFAULT 0,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- To support SCRAM auth:
-- ALTER TABLE users ADD COLUMN serverkey text NOT NULL DEFAULT '';
-- ALTER TABLE users ADD COLUMN salt text NOT NULL DEFAULT '';
-- Add support for SCRAM auth to a database created before ejabberd 16.03:
-- ALTER TABLE users ADD COLUMN serverkey varchar(64) NOT NULL DEFAULT '';
-- ALTER TABLE users ADD COLUMN salt varchar(64) NOT NULL DEFAULT '';
-- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0;
CREATE TABLE last (
+4 -1
View File
@@ -19,10 +19,13 @@
CREATE TABLE users (
username text PRIMARY KEY,
"password" text NOT NULL,
serverkey text NOT NULL DEFAULT '',
salt text NOT NULL DEFAULT '',
iterationcount integer NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
-- To support SCRAM auth:
-- Add support for SCRAM auth to a database created before ejabberd 16.03:
-- ALTER TABLE users ADD COLUMN serverkey text NOT NULL DEFAULT '';
-- ALTER TABLE users ADD COLUMN salt text NOT NULL DEFAULT '';
-- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0;
+34 -2
View File
@@ -31,9 +31,11 @@
-export([start/0, to_record/3, add/3, add_list/3,
add_local/3, add_list_local/3, load_from_config/0,
match_rule/3, match_acl/3, transform_options/1,
match_rule/3, match_access/4, match_acl/3, transform_options/1,
opt_type/1]).
-export([add_access/3, clear/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
@@ -43,6 +45,7 @@
rules = [] :: [access_rule()]}).
-type regexp() :: binary().
-type iprange() :: {inet:ip_address(), integer()} | binary().
-type glob() :: binary().
-type access_name() :: atom().
-type access_rule() :: {atom(), any()}.
@@ -61,7 +64,7 @@
{user_glob, {glob(), host()} | glob()} |
{server_glob, glob()} |
{resource_glob, glob()} |
{ip, {inet:ip_address(), integer()}} |
{ip, iprange()} |
{node_glob, {glob(), glob()}}.
-type acl() :: #acl{aclname :: aclname(),
@@ -204,6 +207,12 @@ load_from_config() ->
end, AccessRules)
end, Hosts).
%% Delete all previous set ACLs and Access rules
clear() ->
mnesia:clear_table(acl),
mnesia:clear_table(access),
ok.
b(S) ->
iolist_to_binary(S).
@@ -246,6 +255,19 @@ normalize_spec(Spec) ->
end
end.
-spec match_access(global | binary(), access_name(),
jid() | ljid() | inet:ip_address(),
atom()) -> any().
match_access(_Host, all, _JID, _Default) ->
allow;
match_access(_Host, none, _JID, _Default) ->
deny;
match_access(_Host, {user, UserPattern}, JID, Default) ->
match_user_spec({user, UserPattern}, JID, Default);
match_access(Host, AccessRule, JID, _Default) ->
match_rule(Host, AccessRule, JID).
-spec match_rule(global | binary(), access_name(),
jid() | ljid() | inet:ip_address()) -> any().
@@ -348,6 +370,16 @@ match_acl(ACL, JID, Host) ->
get_aclspecs(ACL, Host) ->
ets:lookup(acl, {ACL, Host}) ++ ets:lookup(acl, {ACL, global}).
match_user_spec(Spec, JID, Default) ->
case do_match_user_spec(Spec, jid:tolower(JID)) of
true -> Default;
false -> deny
end.
do_match_user_spec({user, {U, S}}, {User, Server, _Resource}) ->
U == User andalso S == Server.
is_regexp_match(String, RegExp) ->
case ejabberd_regexp:run(String, RegExp) of
nomatch -> false;
+3 -1
View File
@@ -68,7 +68,9 @@ parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}
xdata = XData,
others = Others
};
parse_request(_) -> {error, ?ERR_BAD_REQUEST}.
parse_request(#iq{lang = Lang}) ->
Text = <<"Failed to parse ad-hoc command request">>,
{error, ?ERRT_BAD_REQUEST(Lang, Text)}.
%% Borrowed from mod_vcard.erl
find_xdata_el(#xmlel{children = SubEls}) ->
+1 -1
View File
@@ -132,7 +132,7 @@ register_mechanism(Mechanism, Module, PasswordType) ->
%% end.
check_credentials(_State, Props) ->
User = proplists:get_value(username, Props, <<>>),
User = proplists:get_value(authzid, Props, <<>>),
case jid:nodeprep(User) of
error -> {error, <<"not-authorized">>};
<<"">> -> {error, <<"not-authorized">>};
+2 -3
View File
@@ -45,9 +45,8 @@ mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
mech_step(#state{server = Server} = S, ClientIn) ->
User = iolist_to_binary([randoms:get_string(),
randoms:get_string(),
randoms:get_string()]),
jlib:integer_to_binary(p1_time_compat:unique_integer([positive]))]),
case ejabberd_auth:is_user_exists(User, Server) of
true -> mech_step(S, ClientIn);
false -> {ok, [{username, User}, {auth_module, ejabberd_auth_anonymous}]}
false -> {ok, [{username, User}, {authzid, User}, {auth_module, ejabberd_auth_anonymous}]}
end.
+7 -7
View File
@@ -50,7 +50,7 @@
username = <<"">> :: binary(),
authzid = <<"">> :: binary(),
get_password = fun(_) -> {false, <<>>} end :: get_password_fun(),
check_password = fun(_, _, _, _) -> false end :: check_password_fun(),
check_password = fun(_, _, _, _, _) -> false end :: check_password_fun(),
auth_module :: atom(),
host = <<"">> :: binary(),
hostfqdn = <<"">> :: binary()}).
@@ -83,9 +83,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
bad -> {error, <<"bad-protocol">>};
KeyVals ->
DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
%DigestURI = fxml:get_attr_s(<<"digest-uri">>, KeyVals),
UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
%UserName = fxml:get_attr_s(<<"username">>, KeyVals),
case is_digesturi_valid(DigestURI, State#state.host,
State#state.hostfqdn)
of
@@ -97,13 +95,11 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
{error, <<"not-authorized">>, UserName};
true ->
AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
%AuthzId = fxml:get_attr_s(<<"authzid">>, KeyVals),
case (State#state.get_password)(UserName) of
{false, _} -> {error, <<"not-authorized">>, UserName};
{Passwd, AuthModule} ->
case (State#state.check_password)(UserName, <<"">>,
case (State#state.check_password)(UserName, UserName, <<"">>,
proplists:get_value(<<"response">>, KeyVals, <<>>),
%fxml:get_attr_s(<<"response">>, KeyVals),
fun (PW) ->
response(KeyVals,
UserName,
@@ -130,7 +126,11 @@ mech_step(#state{step = 5, auth_module = AuthModule,
username = UserName, authzid = AuthzId},
<<"">>) ->
{ok,
[{username, UserName}, {authzid, AuthzId},
[{username, UserName}, {authzid, case AuthzId of
<<"">> -> UserName;
_ -> AuthzId
end
},
{auth_module, AuthModule}]};
mech_step(A, B) ->
?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]),
+9 -4
View File
@@ -45,7 +45,7 @@ mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
mech_step(State, ClientIn) ->
case prepare(ClientIn) of
[AuthzId, User, Password] ->
case (State#state.check_password)(User, Password) of
case (State#state.check_password)(User, AuthzId, Password) of
{true, AuthModule} ->
{ok,
[{username, User}, {authzid, AuthzId},
@@ -60,12 +60,17 @@ prepare(ClientIn) ->
[<<"">>, UserMaybeDomain, Password] ->
case parse_domain(UserMaybeDomain) of
%% <NUL>login@domain<NUL>pwd
[User, _Domain] -> [UserMaybeDomain, User, Password];
[User, _Domain] -> [User, User, Password];
%% <NUL>login<NUL>pwd
[User] -> [<<"">>, User, Password]
[User] -> [User, User, Password]
end;
[AuthzId, User, Password] ->
case parse_domain(AuthzId) of
%% login@domain<NUL>login<NUL>pwd
[AuthzId, User, Password] -> [AuthzId, User, Password];
[AuthzUser, _Domain] -> [AuthzUser, User, Password];
%% login<NUL>login<NUL>pwd
[AuthzUser] -> [AuthzUser, User, Password]
end;
_ -> error
end.
+2 -1
View File
@@ -159,7 +159,8 @@ mech_step(#state{step = 4} = State, ClientIn) ->
ServerSignature =
scram:server_signature(State#state.server_key,
AuthMessage),
{ok, [{username, State#state.username}],
{ok, [{username, State#state.username},
{authzid, State#state.username}],
<<"v=",
(jlib:encode_base64(ServerSignature))/binary>>};
true -> {error, <<"bad-auth">>, State#state.username}
+1 -1
View File
@@ -79,7 +79,7 @@ is_loaded() ->
start_app(App, Type, StartFlag) when not is_list(App) ->
start_app([App], Type, StartFlag);
start_app([App|Apps], Type, StartFlag) ->
case application:start(App) of
case application:start(App,Type) of
ok ->
spawn(fun() -> check_app_modules(App, StartFlag) end),
start_app(Apps, Type, StartFlag);
+11 -7
View File
@@ -192,16 +192,20 @@ get_commands_spec() ->
module = ejabberd_piefxis, function = export_host,
args = [{dir, string}, {host, string}], result = {res, rescode}},
#ejabberd_commands{name = export_odbc, tags = [mnesia, odbc],
#ejabberd_commands{name = export_sql, tags = [mnesia, sql],
desc = "Export all tables as SQL queries to a file",
module = ejd2odbc, function = export,
module = ejd2sql, function = export,
args = [{host, string}, {file, string}], result = {res, rescode}},
#ejabberd_commands{name = convert_to_scram, tags = [odbc],
#ejabberd_commands{name = delete_mnesia, tags = [mnesia, sql],
desc = "Export all tables as SQL queries to a file",
module = ejd2sql, function = delete,
args = [{host, string}], result = {res, rescode}},
#ejabberd_commands{name = convert_to_scram, tags = [sql],
desc = "Convert the passwords in 'users' ODBC table to SCRAM",
module = ejabberd_auth_odbc, function = convert_to_scram,
module = ejabberd_auth_sql, function = convert_to_scram,
args = [{host, binary}], result = {res, rescode}},
#ejabberd_commands{name = import_prosody, tags = [mnesia, odbc, riak],
#ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak],
desc = "Import data from Prosody",
module = prosody2ejabberd, function = from_dir,
args = [{dir, string}], result = {res, rescode}},
@@ -221,9 +225,9 @@ get_commands_spec() ->
module = ?MODULE, function = delete_old_messages,
args = [{days, integer}], result = {res, rescode}},
#ejabberd_commands{name = export2odbc, tags = [mnesia],
#ejabberd_commands{name = export2sql, tags = [mnesia],
desc = "Export virtual host information from Mnesia tables to SQL files",
module = ejd2odbc, function = export,
module = ejd2sql, function = export,
args = [{host, string}, {directory, string}],
result = {res, rescode}},
#ejabberd_commands{name = set_master, tags = [mnesia],
+3 -40
View File
@@ -30,7 +30,7 @@
-behaviour(application).
-export([start_modules/0, start/2, prep_stop/1, stop/1,
-export([start/2, prep_stop/1, stop/1,
init/0, opt_type/1]).
-include("ejabberd.hrl").
@@ -71,7 +71,7 @@ start(normal, _Args) ->
maybe_add_nameservers(),
ejabberd_auth:start(),
ejabberd_oauth:start(),
start_modules(),
gen_mod:start_modules(),
ejabberd_listener:start_listeners(),
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
Sup;
@@ -83,7 +83,7 @@ start(_, _) ->
%% before shutting down the processes of the application.
prep_stop(State) ->
ejabberd_listener:stop_listeners(),
stop_modules(),
gen_mod:stop_modules(),
ejabberd_admin:stop(),
broadcast_c2s_shutdown(),
timer:sleep(5000),
@@ -137,42 +137,6 @@ db_init() ->
ejabberd:start_app(mnesia, permanent),
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
%% Start all the modules in all the hosts
start_modules() ->
lists:foreach(
fun(Host) ->
Modules = ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
fun({M, A}) when is_atom(M), is_list(A) ->
{M, A}
end, Mods)
end, []),
lists:foreach(
fun({Module, Args}) ->
gen_mod:start_module(Host, Module, Args)
end, Modules)
end, ?MYHOSTS).
%% Stop all the modules in all the hosts
stop_modules() ->
lists:foreach(
fun(Host) ->
Modules = ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
fun({M, A}) when is_atom(M), is_list(A) ->
{M, A}
end, Mods)
end, []),
lists:foreach(
fun({Module, _Args}) ->
gen_mod:stop_module_keep_config(Host, Module)
end, Modules)
end, ?MYHOSTS).
connect_nodes() ->
Nodes = ejabberd_config:get_option(
cluster_nodes,
@@ -256,7 +220,6 @@ start_apps() ->
ejabberd:start_app(fast_tls),
ejabberd:start_app(fast_xml),
ejabberd:start_app(stringprep),
ejabberd:start_app(ezlib),
ejabberd:start_app(cache_tab).
opt_type(net_ticktime) ->
+28 -37
View File
@@ -32,9 +32,9 @@
-author('alexey@process-one.net').
%% External exports
-export([start/0, set_password/3, check_password/3,
check_password/5, check_password_with_authmodule/3,
check_password_with_authmodule/5, try_register/3,
-export([start/0, set_password/3, check_password/4,
check_password/6, check_password_with_authmodule/4,
check_password_with_authmodule/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2, export/1, import/1,
get_vh_registered_users_number/1, import/3,
@@ -63,8 +63,8 @@
-callback remove_user(binary(), binary()) -> any().
-callback remove_user(binary(), binary(), binary()) -> any().
-callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}.
-callback check_password(binary(), binary(), binary()) -> boolean().
-callback check_password(binary(), binary(), binary(), binary(),
-callback check_password(binary(), binary(), binary(), binary()) -> boolean().
-callback check_password(binary(), binary(), binary(), binary(), binary(),
fun((binary()) -> binary())) -> boolean().
-callback try_register(binary(), binary(), binary()) -> {atomic, atom()} |
{error, atom()}.
@@ -102,10 +102,10 @@ store_type(Server) ->
end,
plain, auth_modules(Server)).
-spec check_password(binary(), binary(), binary()) -> boolean().
-spec check_password(binary(), binary(), binary(), binary()) -> boolean().
check_password(User, Server, Password) ->
case check_password_with_authmodule(User, Server,
check_password(User, AuthzId, Server, Password) ->
case check_password_with_authmodule(User, AuthzId, Server,
Password)
of
{true, _AuthModule} -> true;
@@ -113,15 +113,15 @@ check_password(User, Server, Password) ->
end.
%% @doc Check if the user and password can login in server.
%% @spec (User::string(), Server::string(), Password::string(),
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string(),
%% Digest::string(), DigestGen::function()) ->
%% true | false
-spec check_password(binary(), binary(), binary(), binary(),
-spec check_password(binary(), binary(), binary(), binary(), binary(),
fun((binary()) -> binary())) -> boolean().
check_password(User, Server, Password, Digest,
check_password(User, AuthzId, Server, Password, Digest,
DigestGen) ->
case check_password_with_authmodule(User, Server,
case check_password_with_authmodule(User, AuthzId, Server,
Password, Digest, DigestGen)
of
{true, _AuthModule} -> true;
@@ -132,28 +132,28 @@ check_password(User, Server, Password, Digest,
%% The user can login if at least an authentication method accepts the user
%% and the password.
%% The first authentication method that accepts the credentials is returned.
%% @spec (User::string(), Server::string(), Password::string()) ->
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string()) ->
%% {true, AuthModule} | false
%% where
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
%% | ejabberd_auth_internal | ejabberd_auth_ldap
%% | ejabberd_auth_odbc | ejabberd_auth_pam
-spec check_password_with_authmodule(binary(), binary(), binary()) -> false |
%% | ejabberd_auth_mnesia | ejabberd_auth_ldap
%% | ejabberd_auth_sql | ejabberd_auth_pam | ejabberd_auth_riak
-spec check_password_with_authmodule(binary(), binary(), binary(), binary()) -> false |
{true, atom()}.
check_password_with_authmodule(User, Server,
check_password_with_authmodule(User, AuthzId, Server,
Password) ->
check_password_loop(auth_modules(Server),
[User, Server, Password]).
[User, AuthzId, Server, Password]).
-spec check_password_with_authmodule(binary(), binary(), binary(), binary(),
-spec check_password_with_authmodule(binary(), binary(), binary(), binary(), binary(),
fun((binary()) -> binary())) -> false |
{true, atom()}.
check_password_with_authmodule(User, Server, Password,
check_password_with_authmodule(User, AuthzId, Server, Password,
Digest, DigestGen) ->
check_password_loop(auth_modules(Server),
[User, Server, Password, Digest, DigestGen]).
[User, AuthzId, Server, Password, Digest, DigestGen]).
check_password_loop([], _Args) -> false;
check_password_loop([AuthModule | AuthModules], Args) ->
@@ -428,30 +428,21 @@ auth_modules() ->
%% Return the list of authenticated modules for a given host
auth_modules(Server) ->
LServer = jid:nameprep(Server),
Default = case gen_mod:default_db(LServer) of
mnesia -> internal;
DBType -> DBType
end,
Default = ejabberd_config:default_db(LServer, ?MODULE),
Methods = ejabberd_config:get_option(
{auth_method, LServer},
fun(V) when is_list(V) ->
true = lists:all(fun is_atom/1, V),
V;
(V) when is_atom(V) ->
[V]
end, [Default]),
{auth_method, LServer}, opt_type(auth_method), [Default]),
[jlib:binary_to_atom(<<"ejabberd_auth_",
(jlib:atom_to_binary(M))/binary>>)
|| M <- Methods].
export(Server) ->
ejabberd_auth_internal:export(Server).
ejabberd_auth_mnesia:export(Server).
import(Server) ->
ejabberd_auth_internal:import(Server).
ejabberd_auth_mnesia:import(Server).
import(Server, mnesia, Passwd) ->
ejabberd_auth_internal:import(Server, mnesia, Passwd);
ejabberd_auth_mnesia:import(Server, mnesia, Passwd);
import(Server, riak, Passwd) ->
ejabberd_auth_riak:import(Server, riak, Passwd);
import(_, _, _) ->
@@ -459,7 +450,7 @@ import(_, _, _) ->
opt_type(auth_method) ->
fun (V) when is_list(V) ->
true = lists:all(fun is_atom/1, V), V;
(V) when is_atom(V) -> [V]
lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
(V) -> [ejabberd_config:v_db(?MODULE, V)]
end;
opt_type(_) -> [auth_method].
+6 -6
View File
@@ -38,8 +38,8 @@
unregister_connection/3
]).
-export([login/2, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([login/2, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -56,7 +56,7 @@
%% Create the anonymous table if at least one virtual host has anonymous features enabled
%% Register to login / logout events
-record(anonymous, {us = {<<"">>, <<"">>} :: {binary(), binary()},
sid = {p1_time_compat:timestamp(), self()} :: ejabberd_sm:sid()}).
sid = ejabberd_sm:make_sid() :: ejabberd_sm:sid()}).
start(Host) ->
%% TODO: Check cluster mode
@@ -175,11 +175,11 @@ purge_hook(true, LUser, LServer) ->
%% When anonymous login is enabled, check the password for permenant users
%% before allowing access
check_password(User, Server, Password) ->
check_password(User, Server, Password, undefined,
check_password(User, AuthzId, Server, Password) ->
check_password(User, AuthzId, Server, Password, undefined,
undefined).
check_password(User, Server, _Password, _Digest,
check_password(User, _AuthzId, Server, _Password, _Digest,
_DigestGen) ->
case
ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
+46 -42
View File
@@ -31,8 +31,8 @@
-behaviour(ejabberd_auth).
-export([start/1, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -56,7 +56,7 @@ start(Host) ->
"extauth"),
extauth:start(Host, Cmd),
check_cache_last_options(Host),
ejabberd_auth_internal:start(Host).
ejabberd_auth_mnesia:start(Host).
check_cache_last_options(Server) ->
case get_cache_option(Server) of
@@ -76,21 +76,25 @@ plain_password_required() -> true.
store_type() -> external.
check_password(User, Server, Password) ->
check_password(User, AuthzId, Server, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
case get_cache_option(Server) of
false -> check_password_extauth(User, Server, Password);
false -> check_password_extauth(User, AuthzId, Server, Password);
{true, CacheTime} ->
check_password_cache(User, Server, Password, CacheTime)
check_password_cache(User, AuthzId, Server, Password, CacheTime)
end
end.
check_password(User, Server, Password, _Digest,
check_password(User, AuthzId, Server, Password, _Digest,
_DigestGen) ->
check_password(User, Server, Password).
check_password(User, AuthzId, Server, Password).
set_password(User, Server, Password) ->
case extauth:set_password(User, Server, Password) of
true ->
set_password_internal(User, Server, Password), ok;
set_password_mnesia(User, Server, Password), ok;
_ -> {error, unknown_problem}
end.
@@ -102,20 +106,20 @@ try_register(User, Server, Password) ->
end.
dirty_get_registered_users() ->
ejabberd_auth_internal:dirty_get_registered_users().
ejabberd_auth_mnesia:dirty_get_registered_users().
get_vh_registered_users(Server) ->
ejabberd_auth_internal:get_vh_registered_users(Server).
ejabberd_auth_mnesia:get_vh_registered_users(Server).
get_vh_registered_users(Server, Data) ->
ejabberd_auth_internal:get_vh_registered_users(Server,
ejabberd_auth_mnesia:get_vh_registered_users(Server,
Data).
get_vh_registered_users_number(Server) ->
ejabberd_auth_internal:get_vh_registered_users_number(Server).
ejabberd_auth_mnesia:get_vh_registered_users_number(Server).
get_vh_registered_users_number(Server, Data) ->
ejabberd_auth_internal:get_vh_registered_users_number(Server,
ejabberd_auth_mnesia:get_vh_registered_users_number(Server,
Data).
%% The password can only be returned if cache is enabled, cached info exists and is fresh enough.
@@ -147,7 +151,7 @@ remove_user(User, Server) ->
case get_cache_option(Server) of
false -> false;
{true, _CacheTime} ->
ejabberd_auth_internal:remove_user(User, Server)
ejabberd_auth_mnesia:remove_user(User, Server)
end
end.
@@ -158,7 +162,7 @@ remove_user(User, Server, Password) ->
case get_cache_option(Server) of
false -> false;
{true, _CacheTime} ->
ejabberd_auth_internal:remove_user(User, Server,
ejabberd_auth_mnesia:remove_user(User, Server,
Password)
end
end.
@@ -178,8 +182,8 @@ get_cache_option(Host) ->
CacheTime -> {true, CacheTime}
end.
%% @spec (User, Server, Password) -> true | false
check_password_extauth(User, Server, Password) ->
%% @spec (User, AuthzId, Server, Password) -> true | false
check_password_extauth(User, _AuthzId, Server, Password) ->
extauth:check_password(User, Server, Password) andalso
Password /= <<"">>.
@@ -187,45 +191,45 @@ check_password_extauth(User, Server, Password) ->
try_register_extauth(User, Server, Password) ->
extauth:try_register(User, Server, Password).
check_password_cache(User, Server, Password, 0) ->
check_password_external_cache(User, Server, Password);
check_password_cache(User, Server, Password,
check_password_cache(User, AuthzId, Server, Password, 0) ->
check_password_external_cache(User, AuthzId, Server, Password);
check_password_cache(User, AuthzId, Server, Password,
CacheTime) ->
case get_last_access(User, Server) of
online ->
check_password_internal(User, Server, Password);
check_password_mnesia(User, AuthzId, Server, Password);
never ->
check_password_external_cache(User, Server, Password);
check_password_external_cache(User, AuthzId, Server, Password);
mod_last_required ->
?ERROR_MSG("extauth is used, extauth_cache is enabled "
"but mod_last is not enabled in that "
"host",
[]),
check_password_external_cache(User, Server, Password);
check_password_external_cache(User, AuthzId, Server, Password);
TimeStamp ->
case is_fresh_enough(TimeStamp, CacheTime) of
%% If no need to refresh, check password against Mnesia
true ->
case check_password_internal(User, Server, Password) of
case check_password_mnesia(User, AuthzId, Server, Password) of
%% If password valid in Mnesia, accept it
true -> true;
%% Else (password nonvalid in Mnesia), check in extauth and cache result
false ->
check_password_external_cache(User, Server, Password)
check_password_external_cache(User, AuthzId, Server, Password)
end;
%% Else (need to refresh), check in extauth and cache result
false ->
check_password_external_cache(User, Server, Password)
check_password_external_cache(User, AuthzId, Server, Password)
end
end.
get_password_internal(User, Server) ->
ejabberd_auth_internal:get_password(User, Server).
get_password_mnesia(User, Server) ->
ejabberd_auth_mnesia:get_password(User, Server).
%% @spec (User, Server, CacheTime) -> false | Password::string()
-spec get_password_cache(User::binary(), Server::binary(), CacheTime::integer()) -> Password::string() | false.
get_password_cache(User, Server, CacheTime) ->
case get_last_access(User, Server) of
online -> get_password_internal(User, Server);
online -> get_password_mnesia(User, Server);
never -> false;
mod_last_required ->
?ERROR_MSG("extauth is used, extauth_cache is enabled "
@@ -235,16 +239,16 @@ get_password_cache(User, Server, CacheTime) ->
false;
TimeStamp ->
case is_fresh_enough(TimeStamp, CacheTime) of
true -> get_password_internal(User, Server);
true -> get_password_mnesia(User, Server);
false -> false
end
end.
%% Check the password using extauth; if success then cache it
check_password_external_cache(User, Server, Password) ->
case check_password_extauth(User, Server, Password) of
check_password_external_cache(User, AuthzId, Server, Password) ->
case check_password_extauth(User, AuthzId, Server, Password) of
true ->
set_password_internal(User, Server, Password), true;
set_password_mnesia(User, Server, Password), true;
false -> false
end.
@@ -252,31 +256,31 @@ check_password_external_cache(User, Server, Password) ->
try_register_external_cache(User, Server, Password) ->
case try_register_extauth(User, Server, Password) of
{atomic, ok} = R ->
set_password_internal(User, Server, Password), R;
set_password_mnesia(User, Server, Password), R;
_ -> {error, not_allowed}
end.
%% @spec (User, Server, Password) -> true | false
check_password_internal(User, Server, Password) ->
ejabberd_auth_internal:check_password(User, Server,
%% @spec (User, AuthzId, Server, Password) -> true | false
check_password_mnesia(User, AuthzId, Server, Password) ->
ejabberd_auth_mnesia:check_password(User, AuthzId, Server,
Password).
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
set_password_internal(User, Server, Password) ->
set_password_mnesia(User, Server, Password) ->
%% @spec (TimeLast, CacheTime) -> true | false
%% TimeLast = online | never | integer()
%% CacheTime = integer() | false
ejabberd_auth_internal:set_password(User, Server,
ejabberd_auth_mnesia:set_password(User, Server,
Password).
is_fresh_enough(TimeStampLast, CacheTime) ->
Now = p1_time_compat:system_time(seconds),
TimeStampLast + CacheTime > Now.
%% @spec (User, Server) -> online | never | mod_last_required | TimeStamp::integer()
%% Code copied from mod_configure.erl
%% Code copied from web/ejabberd_web_admin.erl
%% TODO: Update time format to XEP-0202: Entity Time
-spec(get_last_access(User::binary(), Server::binary()) -> (online | never | mod_last_required | integer())).
get_last_access(User, Server) ->
case ejabberd_sm:get_user_resources(User, Server) of
[] ->
+8 -4
View File
@@ -37,7 +37,7 @@
handle_cast/2, terminate/2, code_change/3]).
-export([start/1, stop/1, start_link/1, set_password/3,
check_password/3, check_password/5, try_register/3,
check_password/4, check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -116,7 +116,10 @@ plain_password_required() -> true.
store_type() -> external.
check_password(User, Server, Password) ->
check_password(User, AuthzId, Server, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
if Password == <<"">> -> false;
true ->
case catch check_password_ldap(User, Server, Password)
@@ -124,11 +127,12 @@ check_password(User, Server, Password) ->
{'EXIT', _} -> false;
Result -> Result
end
end
end.
check_password(User, Server, Password, _Digest,
check_password(User, AuthzId, Server, Password, _Digest,
_DigestGen) ->
check_password(User, Server, Password).
check_password(User, AuthzId, Server, Password).
set_password(User, Server, Password) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_internal.erl
%%% File : ejabberd_auth_mnesia.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Authentification via mnesia
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
@@ -23,7 +23,7 @@
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_auth_internal).
-module(ejabberd_auth_mnesia).
-behaviour(ejabberd_config).
@@ -31,8 +31,8 @@
-behaviour(ejabberd_auth).
-export([start/1, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -86,9 +86,12 @@ store_type() ->
true -> scram %% allows: PLAIN SCRAM
end.
check_password(User, Server, Password) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
check_password(User, AuthzId, Server, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Password}]
@@ -98,12 +101,16 @@ check_password(User, Server, Password) ->
when is_record(Scram, scram) ->
is_password_scram_valid(Password, Scram);
_ -> false
end
end.
check_password(User, Server, Password, Digest,
check_password(User, AuthzId, Server, Password, Digest,
DigestGen) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Passwd}] when is_binary(Passwd) ->
@@ -125,6 +132,7 @@ check_password(User, Server, Password, Digest,
true -> (Passwd == Password) and (Password /= <<"">>)
end;
_ -> false
end
end.
%% @spec (User::string(), Server::string(), Password::string()) ->
@@ -466,8 +474,8 @@ export(_Server) ->
[{passwd,
fun(Host, #passwd{us = {LUser, LServer}, password = Password})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
Username = ejabberd_sql:escape(LUser),
Pass = ejabberd_sql:escape(Password),
[[<<"delete from users where username='">>, Username, <<"';">>],
[<<"insert into users(username, password) "
"values ('">>, Username, <<"', '">>, Pass, <<"');">>]];
+9 -5
View File
@@ -30,8 +30,8 @@
-behaviour(ejabberd_auth).
-export([start/1, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -46,11 +46,14 @@ start(_Host) ->
set_password(_User, _Server, _Password) ->
{error, not_allowed}.
check_password(User, Server, Password, _Digest,
check_password(User, AuthzId, Server, Password, _Digest,
_DigestGen) ->
check_password(User, Server, Password).
check_password(User, AuthzId, Server, Password).
check_password(User, Host, Password) ->
check_password(User, AuthzId, Host, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
Service = get_pam_service(Host),
UserInfo = case get_pam_userinfotype(Host) of
username -> User;
@@ -61,6 +64,7 @@ check_password(User, Host, Password) ->
of
true -> true;
_ -> false
end
end.
try_register(_User, _Server, _Password) ->
+18 -10
View File
@@ -30,8 +30,8 @@
-behaviour(ejabberd_auth).
%% External exports
-export([start/1, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -66,9 +66,12 @@ store_type() ->
passwd_schema() ->
{record_info(fields, passwd), #passwd{}}.
check_password(User, Server, Password) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
check_password(User, AuthzId, Server, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
{ok, #passwd{password = Password}} when is_binary(Password) ->
Password /= <<"">>;
@@ -76,12 +79,16 @@ check_password(User, Server, Password) ->
is_password_scram_valid(Password, Scram);
_ ->
false
end
end.
check_password(User, Server, Password, Digest,
check_password(User, AuthzId, Server, Password, Digest,
DigestGen) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
{ok, #passwd{password = Passwd}} when is_binary(Passwd) ->
DigRes = if Digest /= <<"">> ->
@@ -102,6 +109,7 @@ check_password(User, Server, Password, Digest,
true -> (Passwd == Password) and (Password /= <<"">>)
end;
_ -> false
end
end.
set_password(User, Server, Password) ->
@@ -283,8 +291,8 @@ export(_Server) ->
[{passwd,
fun(Host, #passwd{us = {LUser, LServer}, password = Password})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
Username = ejabberd_sql:escape(LUser),
Pass = ejabberd_sql:escape(Password),
[[<<"delete from users where username='">>, Username, <<"';">>],
[<<"insert into users(username, password) "
"values ('">>, Username, <<"', '">>, Pass, <<"');">>]];
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_odbc.erl
%%% File : ejabberd_auth_sql.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Authentification via ODBC
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
@@ -23,7 +23,7 @@
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_auth_odbc).
-module(ejabberd_auth_sql).
-behaviour(ejabberd_config).
@@ -31,8 +31,8 @@
-behaviour(ejabberd_auth).
-export([start/1, set_password/3, check_password/3,
check_password/5, try_register/3,
-export([start/1, set_password/3, check_password/4,
check_password/6, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
@@ -63,31 +63,30 @@ store_type() ->
true -> scram %% allows: PLAIN SCRAM
end.
%% @spec (User, Server, Password) -> true | false | {error, Error}
check_password(User, Server, Password) ->
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
%% @spec (User, AuthzId, Server, Password) -> true | false | {error, Error}
check_password(User, AuthzId, Server, Password) ->
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
if (LUser == error) or (LServer == error) ->
false;
(LUser == <<>>) or (LServer == <<>>) ->
false;
true ->
Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
try odbc_queries:get_password_scram(LServer, Username) of
{selected, [<<"password">>, <<"serverkey">>,
<<"salt">>, <<"iterationcount">>],
[[StoredKey, ServerKey, Salt, IterationCount]]} ->
try sql_queries:get_password_scram(LServer, LUser) of
{selected,
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
Scram =
#scram{storedkey = StoredKey,
serverkey = ServerKey,
salt = Salt,
iterationcount = binary_to_integer(
IterationCount)},
iterationcount = IterationCount},
is_password_scram_valid(Password, Scram);
{selected, [<<"password">>, <<"serverkey">>,
<<"salt">>, <<"iterationcount">>], []} ->
{selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -96,12 +95,12 @@ check_password(User, Server, Password) ->
false %% Typical error is database not accessible
end;
false ->
try odbc_queries:get_password(LServer, Username) of
{selected, [<<"password">>], [[Password]]} ->
try sql_queries:get_password(LServer, LUser) of
{selected, [{Password}]} ->
Password /= <<"">>;
{selected, [<<"password">>], [[_Password2]]} ->
{selected, [{_Password2}]} ->
false; %% Password is not correct
{selected, [<<"password">>], []} ->
{selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -110,13 +109,17 @@ check_password(User, Server, Password) ->
false %% Typical error is database not accessible
end
end
end
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
check_password(User, Server, Password, Digest,
%% @spec (User, AuthzId, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
check_password(User, AuthzId, Server, Password, Digest,
DigestGen) ->
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
if AuthzId /= <<>> andalso AuthzId /= User ->
false;
true ->
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
if (LUser == error) or (LServer == error) ->
false;
(LUser == <<>>) or (LServer == <<>>) ->
@@ -124,10 +127,9 @@ check_password(User, Server, Password, Digest,
true ->
case is_scrammed() of
false ->
Username = ejabberd_odbc:escape(LUser),
try odbc_queries:get_password(LServer, Username) of
try sql_queries:get_password(LServer, LUser) of
%% Account exists, check if password is valid
{selected, [<<"password">>], [[Passwd]]} ->
{selected, [{Passwd}]} ->
DigRes = if Digest /= <<"">> ->
Digest == DigestGen(Passwd);
true -> false
@@ -135,7 +137,7 @@ check_password(User, Server, Password, Digest,
if DigRes -> true;
true -> (Passwd == Password) and (Password /= <<"">>)
end;
{selected, [<<"password">>], []} ->
{selected, []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
@@ -146,6 +148,7 @@ check_password(User, Server, Password, Digest,
true ->
false
end
end
end.
%% @spec (User::string(), Server::string(), Password::string()) ->
@@ -158,26 +161,24 @@ set_password(User, Server, Password) ->
(LUser == <<>>) or (LServer == <<>>) ->
{error, invalid_jid};
true ->
Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
Scram = password_to_scram(Password),
case catch odbc_queries:set_password_scram_t(
case catch sql_queries:set_password_scram_t(
LServer,
Username,
ejabberd_odbc:escape(Scram#scram.storedkey),
ejabberd_odbc:escape(Scram#scram.serverkey),
ejabberd_odbc:escape(Scram#scram.salt),
integer_to_binary(Scram#scram.iterationcount)
LUser,
Scram#scram.storedkey,
Scram#scram.serverkey,
Scram#scram.salt,
Scram#scram.iterationcount
)
of
{atomic, ok} -> ok;
Other -> {error, Other}
end;
false ->
Pass = ejabberd_odbc:escape(Password),
case catch odbc_queries:set_password_t(LServer,
Username, Pass)
case catch sql_queries:set_password_t(LServer,
LUser, Password)
of
{atomic, ok} -> ok;
Other -> {error, Other}
@@ -194,26 +195,23 @@ try_register(User, Server, Password) ->
(LUser == <<>>) or (LServer == <<>>) ->
{error, invalid_jid};
true ->
Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
Scram = password_to_scram(Password),
case catch odbc_queries:add_user_scram(
case catch sql_queries:add_user_scram(
LServer,
Username,
ejabberd_odbc:escape(Scram#scram.storedkey),
ejabberd_odbc:escape(Scram#scram.serverkey),
ejabberd_odbc:escape(Scram#scram.salt),
integer_to_binary(Scram#scram.iterationcount)
LUser,
Scram#scram.storedkey,
Scram#scram.serverkey,
Scram#scram.salt,
Scram#scram.iterationcount
) of
{updated, 1} -> {atomic, ok};
_ -> {atomic, exists}
end;
false ->
Pass = ejabberd_odbc:escape(Password),
case catch odbc_queries:add_user(LServer, Username,
Pass)
of
case catch sql_queries:add_user(LServer, LUser,
Password) of
{updated, 1} -> {atomic, ok};
_ -> {atomic, exists}
end
@@ -221,42 +219,58 @@ try_register(User, Server, Password) ->
end.
dirty_get_registered_users() ->
Servers = ejabberd_config:get_vh_by_auth_method(odbc),
Servers = ejabberd_config:get_vh_by_auth_method(sql),
lists:flatmap(fun (Server) ->
get_vh_registered_users(Server)
end,
Servers).
get_vh_registered_users(Server) ->
LServer = jid:nameprep(Server),
case catch odbc_queries:list_users(LServer) of
{selected, [<<"username">>], Res} ->
[{U, LServer} || [U] <- Res];
_ -> []
case jid:nameprep(Server) of
error -> [];
<<>> -> [];
LServer ->
case catch sql_queries:list_users(LServer) of
{selected, Res} ->
[{U, LServer} || {U} <- Res];
_ -> []
end
end.
get_vh_registered_users(Server, Opts) ->
LServer = jid:nameprep(Server),
case catch odbc_queries:list_users(LServer, Opts) of
{selected, [<<"username">>], Res} ->
[{U, LServer} || [U] <- Res];
_ -> []
case jid:nameprep(Server) of
error -> [];
<<>> -> [];
LServer ->
case catch sql_queries:list_users(LServer, Opts) of
{selected, Res} ->
[{U, LServer} || {U} <- Res];
_ -> []
end
end.
get_vh_registered_users_number(Server) ->
LServer = jid:nameprep(Server),
case catch odbc_queries:users_number(LServer) of
{selected, [_], [[Res]]} ->
jlib:binary_to_integer(Res);
_ -> 0
case jid:nameprep(Server) of
error -> 0;
<<>> -> 0;
LServer ->
case catch sql_queries:users_number(LServer) of
{selected, [{Res}]} ->
Res;
_ -> 0
end
end.
get_vh_registered_users_number(Server, Opts) ->
LServer = jid:nameprep(Server),
case catch odbc_queries:users_number(LServer, Opts) of
{selected, [_], [[Res]]} ->
jlib:binary_to_integer(Res);
_Other -> 0
case jid:nameprep(Server) of
error -> 0;
<<>> -> 0;
LServer ->
case catch sql_queries:users_number(LServer, Opts) of
{selected, [{Res}]} ->
Res;
_Other -> 0
end
end.
get_password(User, Server) ->
@@ -267,24 +281,22 @@ get_password(User, Server) ->
(LUser == <<>>) or (LServer == <<>>) ->
false;
true ->
Username = ejabberd_odbc:escape(LUser),
case is_scrammed() of
true ->
case catch odbc_queries:get_password_scram(
LServer, Username) of
{selected, [<<"password">>, <<"serverkey">>,
<<"salt">>, <<"iterationcount">>],
[[StoredKey, ServerKey, Salt, IterationCount]]} ->
case catch sql_queries:get_password_scram(
LServer, LUser) of
{selected,
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
{jlib:decode_base64(StoredKey),
jlib:decode_base64(ServerKey),
jlib:decode_base64(Salt),
binary_to_integer(IterationCount)};
IterationCount};
_ -> false
end;
false ->
case catch odbc_queries:get_password(LServer, Username)
case catch sql_queries:get_password(LServer, LUser)
of
{selected, [<<"password">>], [[Password]]} -> Password;
{selected, [{Password}]} -> Password;
_ -> false
end
end
@@ -300,9 +312,8 @@ get_password_s(User, Server) ->
true ->
case is_scrammed() of
false ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_password(LServer, Username) of
{selected, [<<"password">>], [[Password]]} -> Password;
case catch sql_queries:get_password(LServer, LUser) of
{selected, [{Password}]} -> Password;
_ -> <<"">>
end;
true -> <<"">>
@@ -311,15 +322,17 @@ get_password_s(User, Server) ->
%% @spec (User, Server) -> true | false | {error, Error}
is_user_exists(User, Server) ->
case jid:nodeprep(User) of
error -> false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jid:nameprep(Server),
try odbc_queries:get_password(LServer, Username) of
{selected, [<<"password">>], [[_Password]]} ->
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
if (LUser == error) or (LServer == error) ->
false;
(LUser == <<>>) or (LServer == <<>>) ->
false;
true ->
try sql_queries:get_password(LServer, LUser) of
{selected, [{_Password}]} ->
true; %% Account exists
{selected, [<<"password">>], []} ->
{selected, []} ->
false; %% Account does not exist
{error, Error} -> {error, Error}
catch
@@ -331,12 +344,14 @@ is_user_exists(User, Server) ->
%% @doc Remove user.
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
case jid:nodeprep(User) of
error -> error;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jid:nameprep(Server),
catch odbc_queries:del_user(LServer, Username),
LServer = jid:nameprep(Server),
LUser = jid:nodeprep(User),
if (LUser == error) or (LServer == error) ->
error;
(LUser == <<>>) or (LServer == <<>>) ->
error;
true ->
catch sql_queries:del_user(LServer, LUser),
ok
end.
@@ -352,27 +367,23 @@ remove_user(User, Server, Password) ->
true ->
case is_scrammed() of
true ->
case check_password(User, Server, Password) of
case check_password(User, <<"">>, Server, Password) of
true ->
remove_user(User, Server),
ok;
false -> not_allowed
end;
false ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
F = fun () ->
Result = odbc_queries:del_user_return_password(
LServer, Username, Pass),
Result = sql_queries:del_user_return_password(
LServer, LUser, Password),
case Result of
{selected, [<<"password">>],
[[Password]]} -> ok;
{selected, [<<"password">>],
[]} -> not_exists;
{selected, [{Password}]} -> ok;
{selected, []} -> not_exists;
_ -> not_allowed
end
end,
{atomic, Result} = odbc_queries:sql_transaction(
{atomic, Result} = sql_queries:sql_transaction(
LServer, F),
Result
end
@@ -416,7 +427,7 @@ is_password_scram_valid(Password, Scram) ->
set_password_scram_t(Username,
StoredKey, ServerKey, Salt, IterationCount) ->
odbc_queries:update_t(<<"users">>,
sql_queries:update_t(<<"users">>,
[<<"username">>,
<<"password">>,
<<"serverkey">>,
@@ -436,7 +447,7 @@ convert_to_scram(Server) ->
{error, {incorrect_server_name, Server}};
true ->
F = fun () ->
case ejabberd_odbc:sql_query_t(
case ejabberd_sql:sql_query_t(
[<<"select username, password from users where "
"iterationcount=0 limit ">>,
integer_to_binary(?BATCH_SIZE),
@@ -446,13 +457,13 @@ convert_to_scram(Server) ->
{selected, [<<"username">>, <<"password">>], Rs} ->
lists:foreach(
fun([LUser, Password]) ->
Username = ejabberd_odbc:escape(LUser),
Username = ejabberd_sql:escape(LUser),
Scram = password_to_scram(Password),
set_password_scram_t(
Username,
ejabberd_odbc:escape(Scram#scram.storedkey),
ejabberd_odbc:escape(Scram#scram.serverkey),
ejabberd_odbc:escape(Scram#scram.salt),
ejabberd_sql:escape(Scram#scram.storedkey),
ejabberd_sql:escape(Scram#scram.serverkey),
ejabberd_sql:escape(Scram#scram.salt),
integer_to_binary(Scram#scram.iterationcount)
)
end, Rs),
@@ -460,7 +471,7 @@ convert_to_scram(Server) ->
Err -> {bad_reply, Err}
end
end,
case odbc_queries:sql_transaction(LServer, F) of
case sql_queries:sql_transaction(LServer, F) of
{atomic, ok} -> ok;
{atomic, continue} -> convert_to_scram(Server);
{atomic, Error} -> {error, Error};
+82 -82
View File
@@ -139,8 +139,8 @@
-define(STREAM_HEADER,
<<"<?xml version='1.0'?><stream:stream "
"xmlns='jabber:client' xmlns:stream='http://et"
"herx.jabber.org/streams' id='~s' from='~s'~s~"
"s>">>).
"herx.jabber.org/streams' id='~s' from='~s'~s"
"~s>">>).
-define(STREAM_TRAILER, <<"</stream:stream>">>).
@@ -327,7 +327,7 @@ init([{SockMod, Socket}, Opts]) ->
xml_socket = XMLSocket, zlib = Zlib, tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled, tls_options = TLSOpts,
sid = {p1_time_compat:timestamp(), self()}, streamid = new_id(),
sid = ejabberd_sm:make_sid(), streamid = new_id(),
access = Access, shaper = Shaper, ip = IP,
mgmt_state = StreamMgmtState,
mgmt_max_queue = MaxAckQueue,
@@ -382,13 +382,13 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
ejabberd_auth:get_password_with_authmodule(
U, Server)
end,
fun (U, P) ->
fun(U, AuthzId, P) ->
ejabberd_auth:check_password_with_authmodule(
U, Server, P)
U, AuthzId, Server, P)
end,
fun (U, P, D, DG) ->
fun(U, AuthzId, P, D, DG) ->
ejabberd_auth:check_password_with_authmodule(
U, Server, P, D, DG)
U, AuthzId, Server, P, D, DG)
end),
Mechs =
case TLSEnabled or not TLSRequired of
@@ -522,7 +522,6 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
send_element(StateData,
?POLICY_VIOLATION_ERR(Lang,
<<"Use of STARTTLS required">>)),
send_trailer(StateData),
{stop, normal, StateData};
true ->
fsm_next_state(wait_for_auth,
@@ -537,34 +536,28 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
[jlib:ip_to_list(IP), LogReason]),
send_header(StateData, Server, <<"">>, DefaultLang),
send_element(StateData, ?POLICY_VIOLATION_ERR(Lang, ReasonT)),
send_trailer(StateData),
{stop, normal, StateData};
_ ->
send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
send_element(StateData, ?HOST_UNKNOWN_ERR),
send_trailer(StateData),
{stop, normal, StateData}
end;
_ ->
send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
send_element(StateData, ?INVALID_NS_ERR),
send_trailer(StateData),
{stop, normal, StateData}
end;
wait_for_stream(timeout, StateData) ->
{stop, normal, StateData};
wait_for_stream({xmlstreamelement, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_stream({xmlstreamend, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_stream({xmlstreamerror, _}, StateData) ->
send_header(StateData, ?MYNAME, <<"1.0">>, <<"">>),
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_stream(closed, StateData) ->
{stop, normal, StateData};
@@ -619,8 +612,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
send_element(StateData, Res),
fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {_U, _P, _D, <<"">>}} ->
Err = jlib:make_error_reply(El,
?ERR_AUTH_NO_RESOURCE_PROVIDED((StateData#state.lang))),
Lang = StateData#state.lang,
Txt = <<"No resource provided">>,
Err = jlib:make_error_reply(El, ?ERRT_NOT_ACCEPTABLE(Lang, Txt)),
send_element(StateData, Err),
fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {U, P, D, R}} ->
@@ -634,7 +628,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
DGen = fun (PW) ->
p1_sha:sha(<<(StateData#state.streamid)/binary, PW/binary>>)
end,
case ejabberd_auth:check_password_with_authmodule(U,
case ejabberd_auth:check_password_with_authmodule(U, U,
StateData#state.server,
P, D, DGen)
of
@@ -685,7 +679,10 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, U, StateData#state.server,
StateData#state.ip]),
Err = jlib:make_error_reply(El, ?ERR_NOT_AUTHORIZED),
Lang = StateData#state.lang,
Txt = <<"Legacy authentication failed">>,
Err = jlib:make_error_reply(
El, ?ERRT_NOT_AUTHORIZED(Lang, Txt)),
send_element(StateData, Err),
fsm_next_state(wait_for_auth, StateData)
end;
@@ -706,7 +703,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
[false, U, StateData#state.server,
StateData#state.ip]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
Lang = StateData#state.lang,
Txt = <<"Legacy authentication forbidden">>,
Err = jlib:make_error_reply(El, ?ERRT_NOT_ALLOWED(Lang, Txt)),
send_element(StateData, Err),
fsm_next_state(wait_for_auth, StateData)
end
@@ -718,10 +717,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
wait_for_auth(timeout, StateData) ->
{stop, normal, StateData};
wait_for_auth({xmlstreamend, _Name}, StateData) ->
send_trailer(StateData), {stop, normal, StateData};
{stop, normal, StateData};
wait_for_auth({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_auth(closed, StateData) ->
{stop, normal, StateData};
@@ -752,9 +750,7 @@ wait_for_feature_request({xmlstreamelement, El},
of
{ok, Props} ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
%U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
%AuthModule = fxml:get_attr_s(auth_module, Props),
U = identity(Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -839,7 +835,6 @@ wait_for_feature_request({xmlstreamelement, El},
send_element(StateData,
?POLICY_VIOLATION_ERR(Lang,
<<"Use of STARTTLS required">>)),
send_trailer(StateData),
{stop, normal, StateData};
true ->
process_unauthenticated_stanza(StateData, El),
@@ -850,11 +845,10 @@ wait_for_feature_request(timeout, StateData) ->
{stop, normal, StateData};
wait_for_feature_request({xmlstreamend, _Name},
StateData) ->
send_trailer(StateData), {stop, normal, StateData};
{stop, normal, StateData};
wait_for_feature_request({xmlstreamerror, _},
StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_feature_request(closed, StateData) ->
{stop, normal, StateData};
@@ -876,9 +870,7 @@ wait_for_sasl_response({xmlstreamelement, El},
{ok, Props} ->
catch
(StateData#state.sockmod):reset_stream(StateData#state.socket),
% U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
% AuthModule = fxml:get_attr_s(auth_module, Props),
U = identity(Props),
AuthModule = proplists:get_value(auth_module, Props, <<>>),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -899,9 +891,7 @@ wait_for_sasl_response({xmlstreamelement, El},
user = U});
{ok, Props, ServerOut} ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
% U = fxml:get_attr_s(username, Props),
U = proplists:get_value(username, Props, <<>>),
% AuthModule = fxml:get_attr_s(auth_module, Props),
U = identity(Props),
AuthModule = proplists:get_value(auth_module, Props, undefined),
?INFO_MSG("(~w) Accepted authentication for ~s "
"by ~p from ~s",
@@ -963,11 +953,10 @@ wait_for_sasl_response(timeout, StateData) ->
{stop, normal, StateData};
wait_for_sasl_response({xmlstreamend, _Name},
StateData) ->
send_trailer(StateData), {stop, normal, StateData};
{stop, normal, StateData};
wait_for_sasl_response({xmlstreamerror, _},
StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_sasl_response(closed, StateData) ->
{stop, normal, StateData};
@@ -999,7 +988,7 @@ resource_conflict_action(U, S, R) ->
acceptnew -> {accept_resource, R};
closenew -> closenew;
setresource ->
Rnew = iolist_to_binary([randoms:get_string(),randoms:get_string(),randoms:get_string()]),
Rnew = new_uniq_id(),
{accept_resource, Rnew}
end.
@@ -1019,20 +1008,20 @@ wait_for_bind({xmlstreamelement, #xmlel{name = Name, attrs = Attrs} = El},
end;
wait_for_bind({xmlstreamelement, El}, StateData) ->
case jlib:iq_query_info(El) of
#iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} =
#iq{type = set, lang = Lang, xmlns = ?NS_BIND, sub_el = SubEl} =
IQ ->
U = StateData#state.user,
R1 = fxml:get_path_s(SubEl,
[{elem, <<"resource">>}, cdata]),
R = case jid:resourceprep(R1) of
error -> error;
<<"">> ->
iolist_to_binary([randoms:get_string(),randoms:get_string(),randoms:get_string()]);
<<"">> -> new_uniq_id();
Resource -> Resource
end,
case R of
error ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
Txt = <<"Malformed resource">>,
Err = jlib:make_error_reply(El, ?ERRT_BAD_REQUEST(Lang, Txt)),
send_element(StateData, Err),
fsm_next_state(wait_for_bind, StateData);
_ ->
@@ -1092,10 +1081,9 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
wait_for_bind(timeout, StateData) ->
{stop, normal, StateData};
wait_for_bind({xmlstreamend, _Name}, StateData) ->
send_trailer(StateData), {stop, normal, StateData};
{stop, normal, StateData};
wait_for_bind({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
wait_for_bind(closed, StateData) ->
{stop, normal, StateData};
@@ -1106,6 +1094,7 @@ open_session(StateData) ->
U = StateData#state.user,
R = StateData#state.resource,
JID = StateData#state.jid,
Lang = StateData#state.lang,
case acl:match_rule(StateData#state.server,
StateData#state.access, JID) of
allow ->
@@ -1143,7 +1132,8 @@ open_session(StateData) ->
StateData#state.server, [JID]),
?INFO_MSG("(~w) Forbidden session for ~s",
[StateData#state.socket, jid:to_string(JID)]),
{error, ?ERR_NOT_ALLOWED}
Txt = <<"Denied by ACL">>,
{error, ?ERRT_NOT_ALLOWED(Lang, Txt)}
end.
session_established({xmlstreamelement, #xmlel{name = Name} = El}, StateData)
@@ -1166,7 +1156,6 @@ session_established({xmlstreamelement, El},
case check_from(El, FromJID) of
'invalid-from' ->
send_element(StateData, ?INVALID_FROM),
send_trailer(StateData),
{stop, normal, StateData};
_NewEl ->
session_established2(El, StateData)
@@ -1179,17 +1168,15 @@ session_established(timeout, StateData) ->
[?MODULE, Options, session_established, StateData]),
fsm_next_state(session_established, StateData);
session_established({xmlstreamend, _Name}, StateData) ->
send_trailer(StateData), {stop, normal, StateData};
{stop, normal, StateData};
session_established({xmlstreamerror,
<<"XML stanza is too big">> = E},
StateData) ->
send_element(StateData,
?POLICY_VIOLATION_ERR((StateData#state.lang), E)),
send_trailer(StateData),
{stop, normal, StateData};
session_established({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
session_established(closed, #state{mgmt_state = active} = StateData) ->
catch (StateData#state.sockmod):close(StateData#state.socket),
@@ -1344,7 +1331,6 @@ handle_info(kick, StateName, StateData) ->
handle_info({kick, kicked_by_admin, Xmlelement}, StateName, StateData);
handle_info({kick, Reason, Xmlelement}, _StateName, StateData) ->
send_element(StateData, Xmlelement),
send_trailer(StateData),
{stop, normal,
StateData#state{authenticated = Reason}};
handle_info({route, _From, _To, {broadcast, Data}},
@@ -1357,7 +1343,6 @@ handle_info({route, _From, _To, {broadcast, Data}},
{exit, Reason} ->
Lang = StateData#state.lang,
send_element(StateData, ?SERRT_CONFLICT(Lang, Reason)),
catch send_trailer(StateData),
{stop, normal, StateData};
{privacy_list, PrivList, PrivListName} ->
case ejabberd_hooks:run_fold(privacy_updated_list,
@@ -1628,7 +1613,6 @@ handle_info({route, From, To,
<<"error">> -> ok;
<<"groupchat">> -> ok;
<<"headline">> -> ok;
<<"result">> -> ok;
_ ->
Err =
jlib:make_error_reply(Packet,
@@ -1671,11 +1655,9 @@ handle_info(system_shutdown, StateName, StateData) ->
wait_for_stream ->
send_header(StateData, ?MYNAME, <<"1.0">>, <<"en">>),
send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
send_trailer(StateData),
ok;
_ ->
send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
send_trailer(StateData),
ok
end,
{stop, normal, StateData};
@@ -1807,6 +1789,7 @@ terminate(_Reason, StateName, StateData) ->
ok
end
end,
catch send_trailer(StateData),
(StateData#state.sockmod):close(StateData#state.socket),
ok.
@@ -1913,6 +1896,10 @@ send_trailer(StateData) ->
new_id() -> randoms:get_string().
new_uniq_id() ->
iolist_to_binary([randoms:get_string(),
jlib:integer_to_binary(p1_time_compat:unique_integer([positive]))]).
is_auth_packet(El) ->
case jlib:iq_query_info(El) of
#iq{id = ID, type = Type, xmlns = ?NS_AUTH, sub_el = SubEl} ->
@@ -2278,30 +2265,32 @@ get_priority_from_presence(PresencePacket) ->
end.
process_privacy_iq(From, To,
#iq{type = Type, sub_el = SubEl} = IQ, StateData) ->
{Res, NewStateData} = case Type of
get ->
R = ejabberd_hooks:run_fold(privacy_iq_get,
StateData#state.server,
{error,
?ERR_FEATURE_NOT_IMPLEMENTED},
[From, To, IQ,
StateData#state.privacy_list]),
{R, StateData};
set ->
case ejabberd_hooks:run_fold(privacy_iq_set,
StateData#state.server,
{error,
?ERR_FEATURE_NOT_IMPLEMENTED},
[From, To, IQ])
of
{result, R, NewPrivList} ->
{{result, R},
StateData#state{privacy_list =
NewPrivList}};
R -> {R, StateData}
end
end,
#iq{type = Type, lang = Lang, sub_el = SubEl} = IQ, StateData) ->
Txt = <<"No module is handling this query">>,
{Res, NewStateData} =
case Type of
get ->
R = ejabberd_hooks:run_fold(
privacy_iq_get,
StateData#state.server,
{error, ?ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Txt)},
[From, To, IQ,
StateData#state.privacy_list]),
{R, StateData};
set ->
case ejabberd_hooks:run_fold(
privacy_iq_set,
StateData#state.server,
{error, ?ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Txt)},
[From, To, IQ])
of
{result, R, NewPrivList} ->
{{result, R},
StateData#state{privacy_list =
NewPrivList}};
R -> {R, StateData}
end
end,
IQRes = case Res of
{result, Result} ->
IQ#iq{type = result, sub_el = Result};
@@ -2368,15 +2357,16 @@ process_unauthenticated_stanza(StateData, El) ->
_ -> El
end,
case jlib:iq_query_info(NewEl) of
#iq{} = IQ ->
#iq{lang = L} = IQ ->
Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq,
StateData#state.server, empty,
[StateData#state.server, IQ,
StateData#state.ip]),
case Res of
empty ->
Txt = <<"Authentication required">>,
ResIQ = IQ#iq{type = error,
sub_el = [?ERR_SERVICE_UNAVAILABLE]},
sub_el = [?ERRT_SERVICE_UNAVAILABLE(L, Txt)]},
Res1 = jlib:replace_from_to(jid:make(<<"">>,
StateData#state.server,
<<"">>),
@@ -2422,7 +2412,6 @@ fsm_next_state(session_established, #state{mgmt_max_queue = exceeded} =
Err = ?SERRT_POLICY_VIOLATION(StateData#state.lang,
<<"Too many unacked stanzas">>),
send_element(StateData, Err),
send_trailer(StateData),
{stop, normal, StateData#state{mgmt_resend = false}};
fsm_next_state(session_established, #state{mgmt_state = pending} = StateData) ->
fsm_next_state(wait_for_resume, StateData);
@@ -2882,6 +2871,7 @@ handle_unacked_stanzas(StateData)
false
end
end,
Lang = StateData#state.lang,
ReRoute = case ResendOnTimeout of
true ->
fun(From, To, El, Time) ->
@@ -2890,9 +2880,11 @@ handle_unacked_stanzas(StateData)
end;
false ->
fun(From, To, El, _Time) ->
Txt = <<"User session not found">>,
Err =
jlib:make_error_reply(El,
?ERR_SERVICE_UNAVAILABLE),
jlib:make_error_reply(
El,
?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
ejabberd_router:route(To, From, Err)
end
end,
@@ -2900,7 +2892,9 @@ handle_unacked_stanzas(StateData)
?DEBUG("Dropping presence stanza from ~s",
[jid:to_string(From)]);
(From, To, #xmlel{name = <<"iq">>} = El, _Time) ->
Err = jlib:make_error_reply(El, ?ERR_SERVICE_UNAVAILABLE),
Txt = <<"User session not found">>,
Err = jlib:make_error_reply(
El, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
ejabberd_router:route(To, From, Err);
(From, To, El, Time) ->
%% We'll drop the stanza if it was <forwarded/> by some
@@ -3126,6 +3120,12 @@ pack_string(String, Pack) ->
transform_listen_option(Opt, Opts) ->
[Opt|Opts].
identity(Props) ->
case proplists:get_value(authzid, Props, <<>>) of
<<>> -> proplists:get_value(username, Props, <<>>);
AuthzId -> AuthzId
end.
opt_type(domain_certfile) -> fun iolist_to_binary/1;
opt_type(max_fsm_queue) ->
fun (I) when is_integer(I), I > 0 -> I end;
+221 -101
View File
@@ -90,7 +90,8 @@
%%% PowFloat = math:pow(Base, Exponent),
%%% round(PowFloat).</pre>
%%%
%%% Since this function will be called by ejabberd_commands, it must be exported.
%%% Since this function will be called by ejabberd_commands, it must
%%% be exported.
%%% Add to your module:
%%% <pre>-export([calc_power/2]).</pre>
%%%
@@ -201,24 +202,34 @@
%%% TODO: consider this feature:
%%% All commands are catched. If an error happens, return the restuple:
%%% {error, flattened error string}
%%% This means that ecomm call APIs (ejabberd_ctl, ejabberd_xmlrpc) need to allows this.
%%% And ejabberd_xmlrpc must be prepared to handle such an unexpected response.
%%% This means that ecomm call APIs (ejabberd_ctl, ejabberd_xmlrpc)
%%% need to allows this. And ejabberd_xmlrpc must be prepared to
%%% handle such an unexpected response.
-module(ejabberd_commands).
-author('badlop@process-one.net').
-define(DEFAULT_VERSION, 1000000).
-export([init/0,
list_commands/0,
list_commands/1,
get_command_format/1,
get_command_format/2,
get_command_format/2,
get_command_format/3,
get_command_policy/1,
get_command_definition/1,
get_command_definition/2,
get_tags_commands/0,
get_tags_commands/1,
get_commands/0,
register_commands/1,
unregister_commands/1,
execute_command/2,
execute_command/4,
execute_command/3,
execute_command/4,
execute_command/5,
opt_type/1,
get_commands_spec/0
]).
@@ -226,6 +237,7 @@
-include("ejabberd_commands.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(POLICY_ACCESS, '$policy').
@@ -260,23 +272,26 @@ get_commands_spec() ->
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
result_example = ok}].
init() ->
ets:new(ejabberd_commands, [named_table, set, public,
{keypos, #ejabberd_commands.name}]),
mnesia:delete_table(ejabberd_commands),
mnesia:create_table(ejabberd_commands,
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, ejabberd_commands)},
{type, bag}]),
mnesia:add_table_copy(ejabberd_commands, node(), ram_copies),
register_commands(get_commands_spec()).
-spec register_commands([ejabberd_commands()]) -> ok.
%% @doc Register ejabberd commands.
%% If a command is already registered, a warning is printed and the old command is preserved.
%% If a command is already registered, a warning is printed and the
%% old command is preserved.
register_commands(Commands) ->
lists:foreach(
fun(Command) ->
case ets:insert_new(ejabberd_commands, Command) of
true ->
ok;
false ->
?DEBUG("This command is already defined:~n~p", [Command])
end
% XXX check if command exists
mnesia:dirty_write(Command)
% ?DEBUG("This command is already defined:~n~p", [Command])
end,
Commands).
@@ -286,7 +301,7 @@ register_commands(Commands) ->
unregister_commands(Commands) ->
lists:foreach(
fun(Command) ->
ets:delete_object(ejabberd_commands, Command)
mnesia:dirty_delete_object(Command)
end,
Commands).
@@ -294,94 +309,194 @@ unregister_commands(Commands) ->
%% @doc Get a list of all the available commands, arguments and description.
list_commands() ->
Commands = ets:match(ejabberd_commands,
#ejabberd_commands{name = '$1',
args = '$2',
desc = '$3',
_ = '_'}),
[{A, B, C} || [A, B, C] <- Commands].
list_commands(?DEFAULT_VERSION).
-spec list_commands_policy() -> [{atom(), [aterm()], string(), atom()}].
-spec list_commands(integer()) -> [{atom(), [aterm()], string()}].
%% @doc Get a list of all the available commands, arguments, description, and
%% policy.
list_commands_policy() ->
Commands = ets:match(ejabberd_commands,
#ejabberd_commands{name = '$1',
args = '$2',
desc = '$3',
policy = '$4',
_ = '_'}),
[{A, B, C, D} || [A, B, C, D] <- Commands].
%% @doc Get a list of all the available commands, arguments and
%% description in a given API verion.
list_commands(Version) ->
Commands = get_commands_definition(Version),
[{Name, Args, Desc} || #ejabberd_commands{name = Name,
args = Args,
desc = Desc} <- Commands].
-spec get_command_format(atom()) -> {[aterm()], rterm()} | {error, command_unknown}.
-spec list_commands_policy(integer()) ->
[{atom(), [aterm()], string(), atom()}].
%% @doc Get a list of all the available commands, arguments,
%% description, and policy in a given API version.
list_commands_policy(Version) ->
Commands = get_commands_definition(Version),
[{Name, Args, Desc, Policy} ||
#ejabberd_commands{name = Name,
args = Args,
desc = Desc,
policy = Policy} <- Commands].
-spec get_command_format(atom()) -> {[aterm()], rterm()}.
%% @doc Get the format of arguments and result of a command.
get_command_format(Name) ->
get_command_format(Name, noauth).
get_command_format(Name, noauth, ?DEFAULT_VERSION).
get_command_format(Name, Version) when is_integer(Version) ->
get_command_format(Name, noauth, Version);
get_command_format(Name, Auth) ->
get_command_format(Name, Auth, ?DEFAULT_VERSION).
get_command_format(Name, Auth) ->
-spec get_command_format(atom(),
{binary(), binary(), binary(), boolean()} |
noauth | admin,
integer()) ->
{[aterm()], rterm()}.
get_command_format(Name, Auth, Version) ->
Admin = is_admin(Name, Auth),
Matched = ets:match(ejabberd_commands,
#ejabberd_commands{name = Name,
args = '$1',
result = '$2',
policy = '$3',
_ = '_'}),
case Matched of
[] ->
{error, command_unknown};
[[Args, Result, user]] when Admin;
Auth == noauth ->
#ejabberd_commands{args = Args,
result = Result,
policy = Policy} =
get_command_definition(Name, Version),
case Policy of
user when Admin;
Auth == noauth ->
{[{user, binary}, {server, binary} | Args], Result};
[[Args, Result, _]] ->
_ ->
{Args, Result}
end.
-spec get_command_definition(atom()) -> ejabberd_commands() | command_not_found.
-spec get_command_policy(atom()) -> {ok, open|user|admin|restricted} | {error, command_not_found}.
%% @doc return command policy.
get_command_policy(Name) ->
case get_command_definition(Name) of
#ejabberd_commands{policy = Policy} ->
{ok, Policy};
command_not_found ->
{error, command_not_found}
end.
-spec get_command_definition(atom()) -> ejabberd_commands().
%% @doc Get the definition record of a command.
get_command_definition(Name) ->
case ets:lookup(ejabberd_commands, Name) of
[E] -> E;
[] -> command_not_found
get_command_definition(Name, ?DEFAULT_VERSION).
-spec get_command_definition(atom(), integer()) -> ejabberd_commands().
%% @doc Get the definition record of a command in a given API version.
get_command_definition(Name, Version) ->
case lists:reverse(
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = N, version = V} = C)
when N == Name, V =< Version ->
{V, C}
end)))) of
[{_, Command} | _ ] -> Command;
_E -> throw(unknown_command)
end.
%% @spec (Name::atom(), Arguments) -> ResultTerm | {error, command_unknown}
%% @doc Execute a command.
execute_command(Name, Arguments) ->
execute_command([], noauth, Name, Arguments).
-spec get_commands_definition(integer()) -> [ejabberd_commands()].
-spec execute_command([{atom(), [atom()], [any()]}],
{binary(), binary(), binary(), boolean()} |
noauth | admin,
atom(),
[any()]
% @doc Returns all commands for a given API version
get_commands_definition(Version) ->
L = lists:reverse(
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = Name, version = V} = C)
when V =< Version ->
{Name, V, C}
end)))),
F = fun({_Name, _V, Command}, []) ->
[Command];
({Name, _V, _Command}, [#ejabberd_commands{name=Name}|_T] = Acc) ->
Acc;
({_Name, _V, Command}, Acc) -> [Command | Acc]
end,
lists:foldl(F, [], L).
%% @spec (Name::atom(), Arguments) -> ResultTerm
%% where
%% Arguments = [any()]
%% @doc Execute a command.
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data |
%% no_auth_provided
execute_command(Name, Arguments) ->
execute_command(Name, Arguments, ?DEFAULT_VERSION).
-spec execute_command(atom(),
[any()],
integer() |
{binary(), binary(), binary(), boolean()} |
noauth | admin
) -> any().
%% @spec (AccessCommands, Auth, Name::atom(), Arguments) -> ResultTerm | {error, Error}
%% @spec (Name::atom(), Arguments, integer() | Auth) -> ResultTerm
%% where
%% AccessCommands = [{Access, CommandNames, Arguments}]
%% Auth = {User::string(), Server::string(), Password::string(),
%% Admin::boolean()}
%% | noauth
%% | admin
%% Arguments = [any()]
%%
%% @doc Execute a command in a given API version
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data |
%% no_auth_provided
execute_command(Name, Arguments, Version) when is_integer(Version) ->
execute_command([], noauth, Name, Arguments, Version);
execute_command(Name, Arguments, Auth) ->
execute_command([], Auth, Name, Arguments, ?DEFAULT_VERSION).
%% @spec (AccessCommands, Auth, Name::atom(), Arguments) ->
%% ResultTerm | {error, Error}
%% where
%% AccessCommands = [{Access, CommandNames, Arguments}] | undefined
%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()}
%% | noauth
%% | admin
%% Method = atom()
%% Arguments = [any()]
%% Error = command_unknown | account_unprivileged | invalid_account_data | no_auth_provided
execute_command(AccessCommands1, Auth1, Name, Arguments) ->
%%
%% @doc Execute a command
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided
execute_command(AccessCommands, Auth, Name, Arguments) ->
execute_command(AccessCommands, Auth, Name, Arguments, ?DEFAULT_VERSION).
-spec execute_command([{atom(), [atom()], [any()]}] | undefined,
{binary(), binary(), binary(), boolean()} |
noauth | admin,
atom(),
[any()],
integer()
) -> any().
%% @spec (AccessCommands, Auth, Name::atom(), Arguments, integer()) -> ResultTerm
%% where
%% AccessCommands = [{Access, CommandNames, Arguments}] | undefined
%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()}
%% | noauth
%% | admin
%% Arguments = [any()]
%%
%% @doc Execute a command in a given API version
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided
execute_command(AccessCommands1, Auth1, Name, Arguments, Version) ->
Auth = case is_admin(Name, Auth1) of
true -> admin;
false -> Auth1
end,
case ets:lookup(ejabberd_commands, Name) of
[Command] ->
AccessCommands = get_access_commands(AccessCommands1),
try check_access_commands(AccessCommands, Auth, Name, Command, Arguments) of
ok -> execute_command2(Auth, Command, Arguments)
catch
{error, Error} -> {error, Error}
end;
[] -> {error, command_unknown}
Command = get_command_definition(Name, Version),
AccessCommands = get_access_commands(AccessCommands1, Version),
case check_access_commands(AccessCommands, Auth, Name, Command, Arguments) of
ok -> execute_command2(Auth, Command, Arguments)
end.
execute_command2(
@@ -407,26 +522,25 @@ execute_command2(Command, Arguments) ->
Module = Command#ejabberd_commands.module,
Function = Command#ejabberd_commands.function,
?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]),
try apply(Module, Function, Arguments) of
Response ->
Response
catch
Problem ->
{error, Problem}
end.
apply(Module, Function, Arguments).
-spec get_tags_commands() -> [{string(), [string()]}].
%% @spec () -> [{Tag::string(), [CommandName::string()]}]
%% @doc Get all the tags and associated commands.
get_tags_commands() ->
CommandTags = ets:match(ejabberd_commands,
#ejabberd_commands{
name = '$1',
tags = '$2',
_ = '_'}),
get_tags_commands(?DEFAULT_VERSION).
-spec get_tags_commands(integer()) -> [{string(), [string()]}].
%% @spec (integer) -> [{Tag::string(), [CommandName::string()]}]
%% @doc Get all the tags and associated commands in a given API version
get_tags_commands(Version) ->
CommandTags = [{Name, Tags} ||
#ejabberd_commands{name = Name, tags = Tags}
<- get_commands_definition(Version)],
Dict = lists:foldl(
fun([CommandNameAtom, CTags], D) ->
fun({CommandNameAtom, CTags}, D) ->
CommandName = atom_to_list(CommandNameAtom),
case CTags of
[] ->
@@ -445,7 +559,6 @@ get_tags_commands() ->
CommandTags),
orddict:to_list(Dict).
%% -----------------------------
%% Access verification
%% -----------------------------
@@ -465,7 +578,7 @@ check_access_commands([], _Auth, _Method, _Command, _Arguments) ->
check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
Command =
case {Command1#ejabberd_commands.policy, Auth} of
{user, {_, _, _}} ->
{user, {_, _, _, _}} ->
Command1;
{user, _} ->
Command1#ejabberd_commands{
@@ -479,7 +592,8 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
fun({Access, Commands, ArgumentRestrictions}) ->
case check_access(Command, Access, Auth) of
true ->
check_access_command(Commands, Command, ArgumentRestrictions,
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
@@ -488,7 +602,8 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
ArgumentRestrictions = [],
case check_access(Command, Access, Auth) of
true ->
check_access_command(Commands, Command, ArgumentRestrictions,
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
@@ -517,7 +632,7 @@ check_auth(Command, {User, Server, {oauth, Token}, _}) ->
end;
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
%% Check the account exists and password is valid
case ejabberd_auth:check_password(User, Server, Password) of
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
true -> {ok, User, Server};
_ -> throw({error, invalid_account_data})
end.
@@ -551,9 +666,11 @@ check_access2(Access, User, Server) ->
deny -> false
end.
check_access_command(Commands, Command, ArgumentRestrictions, Method, Arguments) ->
check_access_command(Commands, Command, ArgumentRestrictions,
Method, Arguments) ->
case Commands==all orelse lists:member(Method, Commands) of
true -> check_access_arguments(Command, ArgumentRestrictions, Arguments);
true -> check_access_arguments(Command, ArgumentRestrictions,
Arguments);
false -> false
end.
@@ -577,25 +694,28 @@ tag_arguments(ArgsDefs, Args) ->
Args).
get_access_commands(undefined) ->
Cmds = get_commands(),
get_access_commands(undefined, Version) ->
Cmds = get_commands(Version),
[{?POLICY_ACCESS, Cmds, []}];
get_access_commands(AccessCommands) ->
get_access_commands(AccessCommands, _Version) ->
AccessCommands.
get_commands() ->
Opts = ejabberd_config:get_option(
get_commands(?DEFAULT_VERSION).
get_commands(Version) ->
Opts0 = ejabberd_config:get_option(
commands,
fun(V) when is_list(V) -> V end,
[]),
CommandsList = list_commands_policy(),
Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0),
CommandsList = list_commands_policy(Version),
OpenCmds = [N || {N, _, _, open} <- CommandsList],
RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList],
AdminCmds = [N || {N, _, _, admin} <- CommandsList],
UserCmds = [N || {N, _, _, user} <- CommandsList],
Cmds =
lists:foldl(
fun({add_commands, L}, Acc) ->
fun([{add_commands, L}], Acc) ->
Cmds = case L of
open -> OpenCmds;
restricted -> RestrictedCmds;
@@ -604,7 +724,7 @@ get_commands() ->
_ when is_list(L) -> L
end,
lists:usort(Cmds ++ Acc);
({remove_commands, L}, Acc) ->
([{remove_commands, L}], Acc) ->
Cmds = case L of
open -> OpenCmds;
restricted -> RestrictedCmds;
+172 -53
View File
@@ -34,10 +34,12 @@
get_vh_by_auth_method/1, is_file_readable/1,
get_version/0, get_myhosts/0, get_mylang/0,
prepare_opt_val/4, convert_table_to_binary/5,
transform_options/1, collect_options/1,
convert_to_yaml/1, convert_to_yaml/2,
transform_options/1, collect_options/1, default_db/2,
convert_to_yaml/1, convert_to_yaml/2, v_db/2,
env_binary_to_list/2, opt_type/1, may_hide_data/1]).
-export([start/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_config.hrl").
@@ -52,8 +54,48 @@
%% @type macro_value() = term().
start() ->
mnesia_init(),
Config = get_ejabberd_config_path(),
State0 = read_file(Config),
State1 = hosts_to_start(State0),
State2 = validate_opts(State1),
%% This start time is used by mod_last:
UnixTime = p1_time_compat:system_time(seconds),
SharedKey = case erlang:get_cookie() of
nocookie ->
p1_sha:sha(randoms:get_string());
Cookie ->
p1_sha:sha(jlib:atom_to_binary(Cookie))
end,
State3 = set_option({node_start, global}, UnixTime, State2),
State4 = set_option({shared_key, global}, SharedKey, State3),
set_opts(State4).
%% When starting ejabberd for testing, we sometimes want to start a
%% subset of hosts from the one define in the config file.
%% This function override the host list read from config file by the
%% one we provide.
%% Hosts to start are defined in an ejabberd application environment
%% variable 'hosts' to make it easy to ignore some host in config
%% file.
hosts_to_start(State) ->
case application:get_env(ejabberd, hosts) of
undefined ->
%% Start all hosts as defined in config file
State;
{ok, Hosts} ->
set_hosts_in_options(Hosts, State)
end.
%% @private
%% At the moment, these functions are mainly used to setup unit tests.
-spec(start/2 :: (Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok).
start(Hosts, Opts) ->
mnesia_init(),
set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})).
mnesia_init() ->
case catch mnesia:table_info(local_config, storage_type) of
disc_copies ->
mnesia:delete_table(local_config);
@@ -64,21 +106,7 @@ start() ->
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, local_config)}]),
mnesia:add_table_copy(local_config, node(), ram_copies),
Config = get_ejabberd_config_path(),
State0 = read_file(Config),
State = validate_opts(State0),
%% This start time is used by mod_last:
UnixTime = p1_time_compat:system_time(seconds),
SharedKey = case erlang:get_cookie() of
nocookie ->
p1_sha:sha(randoms:get_string());
Cookie ->
p1_sha:sha(jlib:atom_to_binary(Cookie))
end,
State1 = set_option({node_start, global}, UnixTime, State),
State2 = set_option({shared_key, global}, SharedKey, State1),
set_opts(State2).
mnesia:add_table_copy(local_config, node(), ram_copies).
%% @doc Get the filename of the ejabberd configuration file.
%% The filename can be specified with: erl -config "/path/to/ejabberd.yml".
@@ -112,7 +140,7 @@ get_env_config() ->
%% @doc Read the ejabberd configuration file.
%% It also includes additional configuration files and replaces macros.
%% This function will crash if finds some error in the configuration file.
%% @spec (File::string()) -> #state{}.
%% @spec (File::string()) -> #state{}
read_file(File) ->
read_file(File, [{replace_macros, true},
{include_files, true},
@@ -277,7 +305,7 @@ search_hosts(Term, State) ->
{host, Host} ->
if
State#state.hosts == [] ->
add_hosts_to_option([Host], State);
set_hosts_in_options([Host], State);
true ->
?ERROR_MSG("Can't load config file: "
"too many hosts definitions", []),
@@ -286,7 +314,7 @@ search_hosts(Term, State) ->
{hosts, Hosts} ->
if
State#state.hosts == [] ->
add_hosts_to_option(Hosts, State);
set_hosts_in_options(Hosts, State);
true ->
?ERROR_MSG("Can't load config file: "
"too many hosts definitions", []),
@@ -296,9 +324,12 @@ search_hosts(Term, State) ->
State
end.
add_hosts_to_option(Hosts, State) ->
set_hosts_in_options(Hosts, State) ->
PrepHosts = normalize_hosts(Hosts),
set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts}).
NewOpts = lists:filter(fun({local_config,{hosts,global},_}) -> false;
(_) -> true
end, State#state.opts),
set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts, opts = NewOpts}).
normalize_hosts(Hosts) ->
normalize_hosts(Hosts,[]).
@@ -408,7 +439,7 @@ maps_to_lists(IMap) ->
end, [], IMap).
merge_configs(Terms, ResMap) ->
lists:foldl(fun({Name, Val}, Map) when is_list(Val) ->
lists:foldl(fun({Name, Val}, Map) when is_list(Val), Name =/= auth_method ->
Old = maps:get(Name, Map, #{}),
New = lists:foldl(fun(SVal, OMap) ->
NVal = if Name == host_config orelse Name == append_host_config ->
@@ -620,14 +651,42 @@ process_host_term(Term, Host, State, Action) ->
{hosts, _} ->
State;
{Opt, Val} when Action == set ->
set_option({Opt, Host}, Val, State);
set_option({rename_option(Opt), Host}, change_val(Opt, Val), State);
{Opt, Val} when Action == append ->
append_option({Opt, Host}, Val, State);
append_option({rename_option(Opt), Host}, change_val(Opt, Val), State);
Opt ->
?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]),
State
end.
rename_option(Option) when is_atom(Option) ->
case atom_to_list(Option) of
"odbc_" ++ T ->
NewOption = list_to_atom("sql_" ++ T),
?WARNING_MSG("Option '~s' is obsoleted, use '~s' instead",
[Option, NewOption]),
NewOption;
_ ->
Option
end;
rename_option(Option) ->
Option.
change_val(auth_method, Val) ->
prepare_opt_val(auth_method, Val,
fun(V) ->
L = if is_list(V) -> V;
true -> [V]
end,
lists:map(
fun(odbc) -> sql;
(internal) -> mnesia;
(A) when is_atom(A) -> A
end, L)
end, [mnesia]);
change_val(_Opt, Val) ->
Val.
set_option(Opt, Val, State) ->
State#state{opts = [#local_config{key = Opt, value = Val} |
State#state.opts]}.
@@ -694,13 +753,11 @@ prepare_opt_val(Opt, Val, F, Default) ->
end,
case Res of
{'EXIT', _} ->
?INFO_MSG("Configuration problem:~n"
"** Option: ~s~n"
"** Invalid value: ~s~n"
"** Using as fallback: ~s",
[format_term(Opt),
format_term(Val),
format_term(Default)]),
?WARNING_MSG("incorrect value '~s' of option '~s', "
"using '~s' as fallback",
[format_term(Val),
format_term(Opt),
format_term(Default)]),
Default;
_ ->
Res
@@ -756,9 +813,57 @@ get_option(Opt, F, Default) ->
end
end.
init_module_db_table(Modules) ->
catch ets:new(module_db, [named_table, public, bag]),
%% Dirty hack for mod_pubsub
ets:insert(module_db, {mod_pubsub, mnesia}),
ets:insert(module_db, {mod_pubsub, sql}),
lists:foreach(
fun(M) ->
case re:split(atom_to_list(M), "_", [{return, list}]) of
[_] ->
ok;
Parts ->
[Suffix|T] = lists:reverse(Parts),
BareMod = string:join(lists:reverse(T), "_"),
ets:insert(module_db, {list_to_atom(BareMod),
list_to_atom(Suffix)})
end
end, Modules).
-spec v_db(module(), atom()) -> atom().
v_db(Mod, internal) -> v_db(Mod, mnesia);
v_db(Mod, odbc) -> v_db(Mod, sql);
v_db(Mod, Type) ->
case ets:match_object(module_db, {Mod, Type}) of
[_|_] -> Type;
[] -> erlang:error(badarg)
end.
-spec default_db(binary(), module()) -> atom().
default_db(Host, Module) ->
case ejabberd_config:get_option(
{default_db, Host}, fun(T) when is_atom(T) -> T end) of
undefined ->
mnesia;
DBType ->
try
v_db(Module, DBType)
catch error:badarg ->
?WARNING_MSG("Module '~s' doesn't support database '~s' "
"defined in option 'default_db', using "
"'mnesia' as fallback", [Module, DBType]),
mnesia
end
end.
get_modules_with_options() ->
{ok, Mods} = application:get_key(ejabberd, modules),
ExtMods = [Name || {Name, _Details} <- ext_mod:installed()],
AllMods = [?MODULE|ExtMods++Mods],
init_module_db_table(AllMods),
lists:foldl(
fun(Mod, D) ->
case catch Mod:opt_type('') of
@@ -770,7 +875,7 @@ get_modules_with_options() ->
{'EXIT', {undef, _}} ->
D
end
end, dict:new(), [?MODULE|ExtMods++Mods]).
end, dict:new(), AllMods).
validate_opts(#state{opts = Opts} = State) ->
ModOpts = get_modules_with_options(),
@@ -798,11 +903,25 @@ validate_opts(#state{opts = Opts} = State) ->
-spec get_vh_by_auth_method(atom()) -> [binary()].
%% Return the list of hosts handled by a given module
%% Return the list of hosts with a given auth method
get_vh_by_auth_method(AuthMethod) ->
mnesia:dirty_select(local_config,
[{#local_config{key = {auth_method, '$1'},
value=AuthMethod},[],['$1']}]).
Cfgs = mnesia:dirty_match_object(local_config,
#local_config{key = {auth_method, '_'},
_ = '_'}),
lists:flatmap(
fun(#local_config{key = {auth_method, Host}, value = M}) ->
Methods = if not is_list(M) -> [M];
true -> M
end,
case lists:member(AuthMethod, Methods) of
true when Host == global ->
get_myhosts();
true ->
[Host];
false ->
[]
end
end, Cfgs).
%% @spec (Path::string()) -> true | false
is_file_readable(Path) ->
@@ -836,20 +955,20 @@ get_mylang() ->
fun iolist_to_binary/1,
<<"en">>).
replace_module(mod_announce_odbc) -> {mod_announce, odbc};
replace_module(mod_blocking_odbc) -> {mod_blocking, odbc};
replace_module(mod_caps_odbc) -> {mod_caps, odbc};
replace_module(mod_irc_odbc) -> {mod_irc, odbc};
replace_module(mod_last_odbc) -> {mod_last, odbc};
replace_module(mod_muc_odbc) -> {mod_muc, odbc};
replace_module(mod_offline_odbc) -> {mod_offline, odbc};
replace_module(mod_privacy_odbc) -> {mod_privacy, odbc};
replace_module(mod_private_odbc) -> {mod_private, odbc};
replace_module(mod_roster_odbc) -> {mod_roster, odbc};
replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc};
replace_module(mod_vcard_odbc) -> {mod_vcard, odbc};
replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc};
replace_module(mod_pubsub_odbc) -> {mod_pubsub, odbc};
replace_module(mod_announce_odbc) -> {mod_announce, sql};
replace_module(mod_blocking_odbc) -> {mod_blocking, sql};
replace_module(mod_caps_odbc) -> {mod_caps, sql};
replace_module(mod_irc_odbc) -> {mod_irc, sql};
replace_module(mod_last_odbc) -> {mod_last, sql};
replace_module(mod_muc_odbc) -> {mod_muc, sql};
replace_module(mod_offline_odbc) -> {mod_offline, sql};
replace_module(mod_privacy_odbc) -> {mod_privacy, sql};
replace_module(mod_private_odbc) -> {mod_private, sql};
replace_module(mod_roster_odbc) -> {mod_roster, sql};
replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, sql};
replace_module(mod_vcard_odbc) -> {mod_vcard, sql};
replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, sql};
replace_module(mod_pubsub_odbc) -> {mod_pubsub, sql};
replace_module(Module) ->
case is_elixir_module(Module) of
true -> expand_elixir_module(Module);
@@ -954,7 +1073,7 @@ transform_terms(Terms) ->
mod_last,
ejabberd_s2s,
ejabberd_listener,
ejabberd_odbc_sup,
ejabberd_sql_sup,
shaper,
ejabberd_s2s_out,
acl,
+86 -56
View File
@@ -57,6 +57,8 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-define(DEFAULT_VERSION, 1000000).
%%-----------------------------
%% Module
@@ -69,7 +71,7 @@ start() ->
[SNode3 | Args3] ->
[SNode3, 60000, Args3];
_ ->
print_usage(),
print_usage(?DEFAULT_VERSION),
halt(?STATUS_USAGE)
end,
SNode1 = case string:tokens(SNode, "@") of
@@ -93,6 +95,9 @@ start() ->
[Node, Reason]),
%% TODO: show minimal start help
?STATUS_BADRPC;
{invalid_version, V} ->
print("Invalid API version number: ~p~n", [V]),
?STATUS_ERROR;
S ->
S
end,
@@ -126,11 +131,17 @@ unregister_commands(CmdDescs, Module, Function) ->
%% Process
%%-----------------------------
-spec process([string()]) -> non_neg_integer().
process(Args) ->
process(Args, ?DEFAULT_VERSION).
-spec process([string()], non_neg_integer()) -> non_neg_integer().
%% The commands status, stop and restart are defined here to ensure
%% they are usable even if ejabberd is completely stopped.
process(["status"]) ->
process(["status"], _Version) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
print("The node ~p is ~p with status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
@@ -146,24 +157,24 @@ process(["status"]) ->
?STATUS_SUCCESS
end;
process(["stop"]) ->
process(["stop"], _Version) ->
%%ejabberd_cover:stop(),
init:stop(),
?STATUS_SUCCESS;
process(["restart"]) ->
process(["restart"], _Version) ->
init:restart(),
?STATUS_SUCCESS;
process(["mnesia"]) ->
process(["mnesia"], _Version) ->
print("~p~n", [mnesia:system_info(all)]),
?STATUS_SUCCESS;
process(["mnesia", "info"]) ->
process(["mnesia", "info"], _Version) ->
mnesia:info(),
?STATUS_SUCCESS;
process(["mnesia", Arg]) ->
process(["mnesia", Arg], _Version) ->
case catch mnesia:system_info(list_to_atom(Arg)) of
{'EXIT', Error} -> print("Error: ~p~n", [Error]);
Return -> print("~p~n", [Return])
@@ -172,23 +183,23 @@ process(["mnesia", Arg]) ->
%% The arguments --long and --dual are not documented because they are
%% automatically selected depending in the number of columns of the shell
process(["help" | Mode]) ->
process(["help" | Mode], Version) ->
{MaxC, ShCode} = get_shell_info(),
case Mode of
[] ->
print_usage(dual, MaxC, ShCode),
print_usage(dual, MaxC, ShCode, Version),
?STATUS_USAGE;
["--dual"] ->
print_usage(dual, MaxC, ShCode),
print_usage(dual, MaxC, ShCode, Version),
?STATUS_USAGE;
["--long"] ->
print_usage(long, MaxC, ShCode),
print_usage(long, MaxC, ShCode, Version),
?STATUS_USAGE;
["--tags"] ->
print_usage_tags(MaxC, ShCode),
print_usage_tags(MaxC, ShCode, Version),
?STATUS_SUCCESS;
["--tags", Tag] ->
print_usage_tags(Tag, MaxC, ShCode),
print_usage_tags(Tag, MaxC, ShCode, Version),
?STATUS_SUCCESS;
["help"] ->
print_usage_help(MaxC, ShCode),
@@ -196,13 +207,22 @@ process(["help" | Mode]) ->
[CmdString | _] ->
CmdStringU = ejabberd_regexp:greplace(
list_to_binary(CmdString), <<"-">>, <<"_">>),
print_usage_commands(binary_to_list(CmdStringU), MaxC, ShCode),
print_usage_commands2(binary_to_list(CmdStringU), MaxC, ShCode, Version),
?STATUS_SUCCESS
end;
process(Args) ->
process(["--version", Arg | Args], _) ->
Version =
try
list_to_integer(Arg)
catch _:_ ->
throw({invalid_version, Arg})
end,
process(Args, Version);
process(Args, Version) ->
AccessCommands = get_accesscommands(),
{String, Code} = process2(Args, AccessCommands),
{String, Code} = process2(Args, AccessCommands, Version),
case String of
[] -> ok;
_ ->
@@ -211,18 +231,25 @@ process(Args) ->
Code.
%% @spec (Args::[string()], AccessCommands) -> {String::string(), Code::integer()}
process2(["--auth", User, Server, Pass | Args], AccessCommands) ->
process2(Args, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass), true}, AccessCommands);
process2(Args, AccessCommands) ->
process2(Args, admin, AccessCommands).
process2(Args, AccessCommands, ?DEFAULT_VERSION).
process2(Args, Auth, AccessCommands) ->
case try_run_ctp(Args, Auth, AccessCommands) of
%% @spec (Args::[string()], AccessCommands, Version) -> {String::string(), Code::integer()}
process2(["--auth", User, Server, Pass | Args], AccessCommands, Version) ->
process2(Args, AccessCommands, {list_to_binary(User), list_to_binary(Server),
list_to_binary(Pass), true}, Version);
process2(Args, AccessCommands, Version) ->
process2(Args, AccessCommands, admin, Version).
process2(Args, AccessCommands, Auth, Version) ->
case try_run_ctp(Args, Auth, AccessCommands, Version) of
{String, wrong_command_arguments}
when is_list(String) ->
io:format(lists:flatten(["\n" | String]++["\n"])),
[CommandString | _] = Args,
process(["help" | [CommandString]]),
process(["help" | [CommandString]], Version),
{lists:flatten(String), ?STATUS_ERROR};
{String, Code}
when is_list(String) and is_integer(Code) ->
@@ -246,29 +273,29 @@ get_accesscommands() ->
%%-----------------------------
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
try_run_ctp(Args, Auth, AccessCommands) ->
try_run_ctp(Args, Auth, AccessCommands, Version) ->
try ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false when Args /= [] ->
try_call_command(Args, Auth, AccessCommands);
try_call_command(Args, Auth, AccessCommands, Version);
false ->
print_usage(),
print_usage(Version),
{"", ?STATUS_USAGE};
Status ->
{"", Status}
catch
exit:Why ->
print_usage(),
print_usage(Version),
{io_lib:format("Error in ejabberd ctl process: ~p", [Why]), ?STATUS_USAGE};
Error:Why ->
%% In this case probably ejabberd is not started, so let's show Status
process(["status"]),
process(["status"], Version),
print("~n", []),
{io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
try_call_command(Args, Auth, AccessCommands) ->
try call_command(Args, Auth, AccessCommands) of
try_call_command(Args, Auth, AccessCommands, Version) ->
try call_command(Args, Auth, AccessCommands, Version) of
{error, command_unknown} ->
{io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR};
{error, wrong_command_arguments} ->
@@ -276,24 +303,28 @@ try_call_command(Args, Auth, AccessCommands) ->
Res ->
Res
catch
throw:Error ->
{io_lib:format("~p", [Error]), ?STATUS_ERROR};
A:Why ->
Stack = erlang:get_stacktrace(),
{io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType}
call_command([CmdString | Args], Auth, AccessCommands) ->
call_command([CmdString | Args], Auth, AccessCommands, Version) ->
CmdStringU = ejabberd_regexp:greplace(
list_to_binary(CmdString), <<"-">>, <<"_">>),
Command = list_to_atom(binary_to_list(CmdStringU)),
case ejabberd_commands:get_command_format(Command, Auth) of
case ejabberd_commands:get_command_format(Command, Auth, Version) of
{error, command_unknown} ->
{error, command_unknown};
{ArgsFormat, ResultFormat} ->
case (catch format_args(Args, ArgsFormat)) of
ArgsFormatted when is_list(ArgsFormatted) ->
Result = ejabberd_commands:execute_command(AccessCommands, Auth, Command,
ArgsFormatted),
Result = ejabberd_commands:execute_command(AccessCommands,
Auth, Command,
ArgsFormatted,
Version),
format_result(Result, ResultFormat);
{'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} ->
{NumCompa, TextCompa} =
@@ -404,8 +435,8 @@ make_status(ok) -> ?STATUS_SUCCESS;
make_status(true) -> ?STATUS_SUCCESS;
make_status(_Error) -> ?STATUS_ERROR.
get_list_commands() ->
try ejabberd_commands:list_commands() of
get_list_commands(Version) ->
try ejabberd_commands:list_commands(Version) of
Commands ->
[tuple_command_help(Command)
|| {N,_,_}=Command <- Commands,
@@ -458,10 +489,10 @@ get_list_ctls() ->
-define(U2, "\e[24m").
-define(U(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end).
print_usage() ->
print_usage(Version) ->
{MaxC, ShCode} = get_shell_info(),
print_usage(dual, MaxC, ShCode).
print_usage(HelpMode, MaxC, ShCode) ->
print_usage(dual, MaxC, ShCode, Version).
print_usage(HelpMode, MaxC, ShCode, Version) ->
AllCommands =
[
{"status", [], "Get ejabberd status"},
@@ -469,12 +500,11 @@ print_usage(HelpMode, MaxC, ShCode) ->
{"restart", [], "Restart ejabberd"},
{"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"},
{"mnesia", ["[info]"], "show information of Mnesia system"}] ++
get_list_commands() ++
get_list_commands(Version) ++
get_list_ctls(),
print(
["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--auth ",
?U("user"), " ", ?U("host"), " ", ?U("password"), "] ",
["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] ",
?U("command"), " [", ?U("options"), "]\n"
"\n"
"Available commands in this ejabberd node:\n"], []),
@@ -598,9 +628,9 @@ format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
%% Print Tags
%%-----------------------------
print_usage_tags(MaxC, ShCode) ->
print_usage_tags(MaxC, ShCode, Version) ->
print("Available tags and commands:", []),
TagsCommands = ejabberd_commands:get_tags_commands(),
TagsCommands = ejabberd_commands:get_tags_commands(Version),
lists:foreach(
fun({Tag, Commands} = _TagCommands) ->
print(["\n\n ", ?B(Tag), "\n "], []),
@@ -611,10 +641,10 @@ print_usage_tags(MaxC, ShCode) ->
TagsCommands),
print("\n\n", []).
print_usage_tags(Tag, MaxC, ShCode) ->
print_usage_tags(Tag, MaxC, ShCode, Version) ->
print(["Available commands with tag ", ?B(Tag), ":", "\n"], []),
HelpMode = long,
TagsCommands = ejabberd_commands:get_tags_commands(),
TagsCommands = ejabberd_commands:get_tags_commands(Version),
CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of
{value, {Tag, CNs}} -> CNs;
false -> []
@@ -622,7 +652,7 @@ print_usage_tags(Tag, MaxC, ShCode) ->
CommandsList = lists:map(
fun(NameString) ->
C = ejabberd_commands:get_command_definition(
list_to_atom(NameString)),
list_to_atom(NameString), Version),
#ejabberd_commands{name = Name,
args = Args,
desc = Desc} = C,
@@ -673,20 +703,20 @@ print_usage_help(MaxC, ShCode) ->
%%-----------------------------
%% @spec (CmdSubString::string(), MaxC::integer(), ShCode::boolean()) -> ok
print_usage_commands(CmdSubString, MaxC, ShCode) ->
print_usage_commands2(CmdSubString, MaxC, ShCode, Version) ->
%% Get which command names match this substring
AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands()],
AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)],
Cmds = filter_commands(AllCommandsNames, CmdSubString),
case Cmds of
[] -> io:format("Error: not command found that match: ~p~n", [CmdSubString]);
_ -> print_usage_commands2(lists:sort(Cmds), MaxC, ShCode)
[] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]);
_ -> print_usage_commands3(lists:sort(Cmds), MaxC, ShCode, Version)
end.
print_usage_commands2(Cmds, MaxC, ShCode) ->
print_usage_commands3(Cmds, MaxC, ShCode, Version) ->
%% Then for each one print it
lists:mapfoldl(
fun(Cmd, Remaining) ->
print_usage_command(Cmd, MaxC, ShCode),
print_usage_command(Cmd, MaxC, ShCode, Version),
case Remaining > 1 of
true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []);
false -> ok
@@ -716,16 +746,16 @@ filter_commands_regexp(All, Glob) ->
All).
%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok
print_usage_command(Cmd, MaxC, ShCode) ->
print_usage_command(Cmd, MaxC, ShCode, Version) ->
Name = list_to_atom(Cmd),
case ejabberd_commands:get_command_definition(Name) of
case ejabberd_commands:get_command_definition(Name, Version) of
command_not_found ->
io:format("Error: command ~p not known.~n", [Cmd]);
C ->
print_usage_command(Cmd, C, MaxC, ShCode)
print_usage_command2(Cmd, C, MaxC, ShCode)
end.
print_usage_command(Cmd, C, MaxC, ShCode) ->
print_usage_command2(Cmd, C, MaxC, ShCode) ->
#ejabberd_commands{
tags = TagsAtoms,
desc = Desc,
+1
View File
@@ -753,6 +753,7 @@ code_to_phrase(503) -> <<"Service Unavailable">>;
code_to_phrase(504) -> <<"Gateway Timeout">>;
code_to_phrase(505) -> <<"HTTP Version Not Supported">>.
-spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | undefined.
parse_auth(<<"Basic ", Auth64/binary>>) ->
Auth = jlib:decode_base64(Auth64),
%% Auth should be a string with the format: user@server:password
+10 -5
View File
@@ -32,7 +32,7 @@
%% API
-export([start_link/0]).
-export([route/3, route_iq/4, route_iq/5,
-export([route/3, route_iq/4, route_iq/5, process_iq/3,
process_iq_reply/3, register_iq_handler/4,
register_iq_handler/5, register_iq_response_handler/4,
register_iq_response_handler/5, unregister_iq_handler/2,
@@ -74,7 +74,7 @@ start_link() ->
process_iq(From, To, Packet) ->
IQ = jlib:iq_query_info(Packet),
case IQ of
#iq{xmlns = XMLNS} ->
#iq{xmlns = XMLNS, lang = Lang} ->
Host = To#jid.lserver,
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
[{_, Module, Function}] ->
@@ -87,8 +87,10 @@ process_iq(From, To, Packet) ->
gen_iq_handler:handle(Host, Module, Function, Opts,
From, To, IQ);
[] ->
Err = jlib:make_error_reply(Packet,
?ERR_FEATURE_NOT_IMPLEMENTED),
Txt = <<"No module is handling this query">>,
Err = jlib:make_error_reply(
Packet,
?ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Txt)),
ejabberd_router:route(To, From, Err)
end;
reply ->
@@ -166,8 +168,10 @@ refresh_iq_handlers() ->
ejabberd_local ! refresh_iq_handlers.
bounce_resource_packet(From, To, Packet) ->
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"No available resource found">>,
Err = jlib:make_error_reply(Packet,
?ERR_ITEM_NOT_FOUND),
?ERRT_ITEM_NOT_FOUND(Lang, Txt)),
ejabberd_router:route(To, From, Err),
stop.
@@ -178,6 +182,7 @@ bounce_resource_packet(From, To, Packet) ->
init([]) ->
lists:foreach(fun (Host) ->
ejabberd_router:register_route(Host,
Host,
{apply, ?MODULE,
route}),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
+54 -2
View File
@@ -50,6 +50,7 @@
%% "ejabberd.log" in current directory.
%% Note: If the directory where to place the ejabberd log file to not exist,
%% it is not created and no log file will be generated.
%% @spec () -> string()
get_log_path() ->
case ejabberd_config:env_binary_to_list(ejabberd, log_path) of
{ok, Path} ->
@@ -99,7 +100,33 @@ get_string_env(Name, Default) ->
Default
end.
%% @spec () -> ok
start() ->
StartedApps = application:which_applications(5000),
case lists:keyfind(logger, 1, StartedApps) of
%% Elixir logger is started. We assume everything is in place
%% to use lager to Elixir logger bridge.
{logger, _, _} ->
error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []),
%% Do not start lager, we rely on Elixir Logger
do_start_for_logger();
_ ->
do_start()
end.
do_start_for_logger() ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']),
application:set_env(lager, crash_log, false),
application:set_env(lager, handlers, [{elixir_logger_backend, [{level, info}]}]),
ejabberd:start_app(lager),
ok.
%% Start lager
do_start() ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
@@ -126,10 +153,12 @@ start() ->
ejabberd:start_app(lager),
ok.
%% @spec () -> ok
reopen_log() ->
%% Lager detects external log rotation automatically.
ok.
%% @spec () -> ok
rotate_log() ->
lager_crash_log ! rotate,
lists:foreach(
@@ -139,8 +168,9 @@ rotate_log() ->
ok
end, gen_event:which_handlers(lager_event)).
%% @spec () -> {loglevel(), atom(), string()}
get() ->
case lager:get_loglevel(lager_console_backend) of
case get_lager_loglevel() of
none -> {0, no_log, "No log"};
emergency -> {1, critical, "Critical"};
alert -> {1, critical, "Critical"};
@@ -152,6 +182,7 @@ get() ->
debug -> {5, debug, "Debug"}
end.
%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()}
set(LogLevel) when is_integer(LogLevel) ->
LagerLogLevel = case LogLevel of
0 -> none;
@@ -162,7 +193,7 @@ set(LogLevel) when is_integer(LogLevel) ->
5 -> debug;
E -> throw({wrong_loglevel, E})
end,
case lager:get_loglevel(lager_console_backend) of
case get_lager_loglevel() of
LagerLogLevel ->
ok;
_ ->
@@ -172,6 +203,8 @@ set(LogLevel) when is_integer(LogLevel) ->
lager:set_loglevel(H, LagerLogLevel);
(lager_console_backend = H) ->
lager:set_loglevel(H, LagerLogLevel);
(elixir_logger_backend = H) ->
lager:set_loglevel(H, LagerLogLevel);
(_) ->
ok
end, gen_event:which_handlers(lager_event))
@@ -180,3 +213,22 @@ set(LogLevel) when is_integer(LogLevel) ->
set({_LogLevel, _}) ->
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
{module, lager}.
get_lager_loglevel() ->
Handlers = get_lager_handlers(),
lists:foldl(fun(lager_console_backend, _Acc) ->
lager:get_loglevel(lager_console_backend);
(elixir_logger_backend, _Acc) ->
lager:get_loglevel(elixir_logger_backend);
(_, Acc) ->
Acc
end,
none, Handlers).
get_lager_handlers() ->
case catch gen_event:which_handlers(lager_event) of
{'EXIT',noproc} ->
[];
Result ->
Result
end.
+1 -1
View File
@@ -134,7 +134,7 @@ authenticate_user({User, Server}, {password, Password} = Ctx) ->
none),
case acl:match_rule(JID#jid.lserver, Access, JID) of
allow ->
case ejabberd_auth:check_password(User, Server, Password) of
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
true ->
{ok, {Ctx, {user, User, Server}}};
false ->
+2 -83
View File
@@ -28,8 +28,8 @@
%%% Not implemented:
%%% - write mod_piefxis with ejabberdctl commands
%%% - Export from mod_offline_odbc.erl
%%% - Export from mod_private_odbc.erl
%%% - Export from mod_offline_sql.erl
%%% - Export from mod_private_sql.erl
%%% - XEP-227: 6. Security Considerations
%%% - Other schemas of XInclude are not tested, and may not be imported correctly.
%%% - If a host has many users, split that host in XML files with 50 users each.
@@ -80,7 +80,6 @@ import_file(FileName) ->
import_file(FileName, #state{}).
-spec import_file(binary(), state()) -> ok | {error, atom()}.
import_file(FileName, State) ->
case file:open(FileName, [read, binary]) of
{ok, Fd} ->
@@ -97,72 +96,14 @@ import_file(FileName, State) ->
{error, Reason}
end.
%%%==================================
%%%% Process Elements
%%%==================================
%%%% Process Element
%%%==================================
%%%% Add user
%% @spec (El::xmlel(), Domain::string(), User::binary(), Password::binary() | none)
%% -> ok | {error, ErrorText::string()}
%% @doc Add a new user to the database.
%% If user already exists, it will be only updated.
-spec export_server(binary()) -> any().
%% @spec (User::string(), Password::string(), Domain::string())
%% -> ok | {atomic, exists} | {error, not_allowed}
%% @doc Create a new user
export_server(Dir) ->
export_hosts(?MYHOSTS, Dir).
%%%==================================
%%%% Populate user
%% @spec (User::string(), Domain::string(), El::xml())
%% -> ok | {error, not_found}
%%
%% @doc Add a new user from a XML file with a roster list.
%%
%% Example of a file:
%% ```
%% <?xml version='1.0' encoding='UTF-8'?>
%% <server-data xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'>
%% <host jid='localhost'>
%% <user name='juliet' password='s3crEt'>
%% <query xmlns='jabber:iq:roster'>
%% <item jid='romeo@montague.net'
%% name='Romeo'
%% subscription='both'>
%% <group>Friends</group>
%% </item>
%% </query>
%% </user>
%% </host>
%% </server-data>
%% '''
-spec export_host(binary(), binary()) -> any().
export_host(Dir, Host) ->
export_hosts([Host], Dir).
%% @spec User = String with the user name
%% Domain = String with a domain name
%% El = Sub XML element with vCard tags values
%% @ret ok | {error, not_found}
%% @doc Read vcards from the XML and send it to the server
%%
%% Example:
%% ```
%% <?xml version='1.0' encoding='UTF-8'?>
%% <server-data xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'>
%% <host jid='localhost'>
%% <user name='admin' password='s3crEt'>
%% <vCard xmlns='vcard-temp'>
%% <FN>Admin</FN>
%% </vCard>
%% </user>
%% </host>
%% </server-data>
%% '''
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -194,11 +135,6 @@ export_hosts(Hosts, Dir) ->
{error, Reason}
end.
%% @spec User = String with the user name
%% Domain = String with a domain name
%% El = Sub XML element with offline messages values
%% @ret ok | {error, not_found}
%% @doc Read off-line message from the XML and send it to the server
export_host(Dir, FnH, Host) ->
DFn = make_host_basefilename(Dir, FnH),
case file:open(DFn, [raw, write]) of
@@ -223,11 +159,6 @@ export_host(Dir, FnH, Host) ->
{error, Reason}
end.
%% @spec User = String with the user name
%% Domain = String with a domain name
%% El = Sub XML element with private storage values
%% @ret ok | {error, not_found}
%% @doc Private storage parsing
export_users([{User, _S}|Users], Server, Fd) ->
case export_user(User, Server, Fd) of
ok ->
@@ -238,8 +169,6 @@ export_users([{User, _S}|Users], Server, Fd) ->
export_users([], _Server, _Fd) ->
ok.
%%%==================================
%%%% Utilities
export_user(User, Server, Fd) ->
Password = ejabberd_auth:get_password_s(User, Server),
LServer = jid:nameprep(Server),
@@ -289,7 +218,6 @@ get_vcard(User, Server) ->
[]
end.
%%%==================================
get_offline(User, Server) ->
case mod_offline:get_offline_els(User, Server) of
[] ->
@@ -306,7 +234,6 @@ get_offline(User, Server) ->
[#xmlel{name = <<"offline-messages">>, children = NewEls}]
end.
%%%% Export hosts
get_privacy(User, Server) ->
case mod_privacy:get_user_lists(User, Server) of
{ok, #privacy{default = Default,
@@ -333,7 +260,6 @@ get_privacy(User, Server) ->
[]
end.
%% @spec (Dir::string(), Hosts::[string()]) -> ok
get_roster(User, Server) ->
JID = jid:make(User, Server, <<>>),
case mod_roster:get_roster(User, Server) of
@@ -576,8 +502,6 @@ process_roster(El, State = #state{user = U, server = S}) ->
stop("Failed to write roster: ~p", [Err])
end.
%%%==================================
%%%% Export server
process_privacy(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_privacy:process_iq_set(
@@ -603,7 +527,6 @@ process_privacy(El, State = #state{user = U, server = S}) ->
{ok, State}
end.
%% @spec (Dir::string()) -> ok
process_private(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_private:process_sm_iq(
@@ -614,8 +537,6 @@ process_private(El, State = #state{user = U, server = S}) ->
stop("Failed to write private: ~p", [Err])
end.
%%%==================================
%%%% Export host
process_vcard(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_vcard:process_sm_iq(
@@ -626,7 +547,6 @@ process_vcard(El, State = #state{user = U, server = S}) ->
stop("Failed to write vcard: ~p", [Err])
end.
%% @spec (Dir::string(), Host::string()) -> ok
process_offline_msg(El, State = #state{user = U, server = S}) ->
FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
@@ -643,7 +563,6 @@ process_offline_msg(El, State = #state{user = U, server = S}) ->
stop("Invalid 'from' = ~s", [FromS])
end.
%% @spec (Dir::string(), Fn::string(), Host::string()) -> ok
process_presence(El, #state{user = U, server = S} = State) ->
FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
+17 -17
View File
@@ -35,10 +35,10 @@
-include("logger.hrl").
start() ->
file:delete(ejabberd_odbc:freetds_config()),
file:delete(ejabberd_odbc:odbc_config()),
file:delete(ejabberd_odbc:odbcinst_config()),
case lists:any(fun(H) -> needs_odbc(H) /= false end,
file:delete(ejabberd_sql:freetds_config()),
file:delete(ejabberd_sql:odbc_config()),
file:delete(ejabberd_sql:odbcinst_config()),
case lists:any(fun(H) -> needs_sql(H) /= false end,
?MYHOSTS) of
true ->
start_hosts();
@@ -49,34 +49,34 @@ start() ->
%% Start relationnal DB module on the nodes where it is needed
start_hosts() ->
lists:foreach(fun (Host) ->
case needs_odbc(Host) of
{true, App} -> start_odbc(Host, App);
case needs_sql(Host) of
{true, App} -> start_sql(Host, App);
false -> ok
end
end,
?MYHOSTS).
%% Start the ODBC module on the given host
start_odbc(Host, App) ->
%% Start the SQL module on the given host
start_sql(Host, App) ->
ejabberd:start_app(App),
Supervisor_name = gen_mod:get_module_proc(Host,
ejabberd_odbc_sup),
ejabberd_sql_sup),
ChildSpec = {Supervisor_name,
{ejabberd_odbc_sup, start_link, [Host]}, transient,
infinity, supervisor, [ejabberd_odbc_sup]},
{ejabberd_sql_sup, start_link, [Host]}, transient,
infinity, supervisor, [ejabberd_sql_sup]},
case supervisor:start_child(ejabberd_sup, ChildSpec) of
{ok, _PID} -> ok;
_Error ->
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying."
"..~n",
[Supervisor_name, _Error]),
start_odbc(Host, App)
start_sql(Host, App)
end.
%% Returns {true, App} if we have configured odbc for the given host
needs_odbc(Host) ->
%% Returns {true, App} if we have configured sql for the given host
needs_sql(Host) ->
LHost = jid:nameprep(Host),
case ejabberd_config:get_option({odbc_type, LHost},
case ejabberd_config:get_option({sql_type, LHost},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
@@ -91,11 +91,11 @@ needs_odbc(Host) ->
undefined -> false
end.
opt_type(odbc_type) ->
opt_type(sql_type) ->
fun (mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
(mssql) -> mssql;
(odbc) -> odbc
end;
opt_type(_) -> [odbc_type].
opt_type(_) -> [sql_type].
+1
View File
@@ -141,6 +141,7 @@ handle_call({starttls, TLSSocket}, _From, State) ->
handle_call({compress, Data}, _From,
#state{socket = Socket, sock_mod = SockMod} =
State) ->
ejabberd:start_app(ezlib),
{ok, ZlibSocket} = ezlib:enable_zlib(SockMod,
Socket),
if Data /= undefined -> do_send(State, Data);
+17 -8
View File
@@ -28,7 +28,7 @@
-behaviour(gen_server).
%% API
-export([start_link/4, get_proc/1, make_bucket/1, put/2, put/3,
-export([start_link/5, get_proc/1, make_bucket/1, put/2, put/3,
get/2, get/3, get_by_index/4, delete/1, delete/2,
count_by_index/3, get_by_index_range/5,
get_keys/1, get_keys_by_index/3, is_connected/0,
@@ -68,12 +68,20 @@
%%% API
%%%===================================================================
%% @private
start_link(Num, Server, Port, _StartInterval) ->
gen_server:start_link({local, get_proc(Num)}, ?MODULE, [Server, Port], []).
start_link(Num, Server, Port, _StartInterval, Options) ->
gen_server:start_link({local, get_proc(Num)}, ?MODULE, [Server, Port, Options], []).
%% @private
is_connected() ->
catch riakc_pb_socket:is_connected(get_random_pid()).
lists:all(
fun({_Id, Pid, _Type, _Modules}) when is_pid(Pid) ->
case catch riakc_pb_socket:is_connected(get_riak_pid(Pid)) of
true -> true;
_ -> false
end;
(_) ->
false
end, supervisor:which_children(ejabberd_riak_sup)).
%% @private
get_proc(I) ->
@@ -429,10 +437,8 @@ map_key(Obj, _, _) ->
%%% gen_server API
%%%===================================================================
%% @private
init([Server, Port]) ->
case riakc_pb_socket:start(
Server, Port,
[auto_reconnect]) of
init([Server, Port, Options]) ->
case riakc_pb_socket:start(Server, Port, Options) of
{ok, Pid} ->
erlang:monitor(process, Pid),
{ok, #state{pid = Pid}};
@@ -517,6 +523,9 @@ make_invalid_object(Val) ->
get_random_pid() ->
PoolPid = ejabberd_riak_sup:get_random_pid(),
get_riak_pid(PoolPid).
get_riak_pid(PoolPid) ->
case catch gen_server:call(PoolPid, get_pid) of
{ok, Pid} ->
Pid;
+43 -4
View File
@@ -70,8 +70,8 @@ is_riak_configured(Host) ->
{modules, Host},
fun(L) when is_list(L) -> L end, []),
ModuleWithRiakDBConfigured = lists:any(
fun({_Module, Opts}) ->
gen_mod:db_type(Host, Opts) == riak
fun({Module, Opts}) ->
gen_mod:db_type(Host, Opts, Module) == riak
end, Modules),
ServerConfigured or PortConfigured
or AuthConfigured or ModuleWithRiakDBConfigured.
@@ -103,12 +103,27 @@ init([]) ->
StartInterval = get_start_interval(),
Server = get_riak_server(),
Port = get_riak_port(),
CACertFile = get_riak_cacertfile(),
Username = get_riak_username(),
Password = get_riak_password(),
Options = lists:filter(
fun(X) -> X /= nil end,
[auto_reconnect,
{keepalive, true},
if CACertFile /= nil -> {cacertfile ,CACertFile};
true -> nil
end,
if (Username /= nil) and (Password /= nil) ->
{credentials, Username, Password};
true -> nil
end
]),
{ok, {{one_for_one, PoolSize*10, 1},
lists:map(
fun(I) ->
{ejabberd_riak:get_proc(I),
{ejabberd_riak, start_link,
[I, Server, Port, StartInterval*1000]},
[I, Server, Port, StartInterval*1000, Options]},
transient, 2000, worker, [?MODULE]}
end, lists:seq(1, PoolSize))}}.
@@ -131,6 +146,27 @@ get_riak_server() ->
binary_to_list(iolist_to_binary(S))
end, ?DEFAULT_RIAK_HOST).
get_riak_cacertfile() ->
ejabberd_config:get_option(
riak_cacertfile,
fun(S) ->
binary_to_list(iolist_to_binary(S))
end, nil).
get_riak_username() ->
ejabberd_config:get_option(
riak_username,
fun(S) ->
binary_to_list(iolist_to_binary(S))
end, nil).
get_riak_password() ->
ejabberd_config:get_option(
riak_password,
fun(S) ->
binary_to_list(iolist_to_binary(S))
end, nil).
get_riak_port() ->
ejabberd_config:get_option(
riak_port,
@@ -162,6 +198,9 @@ opt_type(riak_port) -> fun (_) -> true end;
opt_type(riak_server) -> fun (_) -> true end;
opt_type(riak_start_interval) ->
fun (N) when is_integer(N), N >= 1 -> N end;
opt_type(riak_cacertfile) -> fun iolist_to_binary/1;
opt_type(riak_username) -> fun iolist_to_binary/1;
opt_type(riak_password) -> fun iolist_to_binary/1;
opt_type(_) ->
[modules, riak_pool_size, riak_port, riak_server,
riak_start_interval].
riak_start_interval, riak_cacertfile, riak_username, riak_password].
+67 -42
View File
@@ -36,7 +36,9 @@
route_error/4,
register_route/1,
register_route/2,
register_route/3,
register_routes/1,
host_of_route/1,
unregister_route/1,
unregister_routes/1,
dirty_get_all_routes/0,
@@ -55,7 +57,7 @@
-type local_hint() :: undefined | integer() | {apply, atom(), atom()}.
-record(route, {domain, pid, local_hint}).
-record(route, {domain, server_host, pid, local_hint}).
-record(state, {}).
@@ -94,19 +96,29 @@ route_error(From, To, ErrPacket, OrigPacket) ->
-spec register_route(binary()) -> term().
register_route(Domain) ->
register_route(Domain, undefined).
?WARNING_MSG("~s:register_route/1 is deprected, "
"use ~s:register_route/2 instead",
[?MODULE, ?MODULE]),
register_route(Domain, ?MYNAME).
-spec register_route(binary(), local_hint()) -> term().
-spec register_route(binary(), binary()) -> term().
register_route(Domain, LocalHint) ->
case jid:nameprep(Domain) of
error -> erlang:error({invalid_domain, Domain});
LDomain ->
register_route(Domain, ServerHost) ->
register_route(Domain, ServerHost, undefined).
-spec register_route(binary(), binary(), local_hint()) -> term().
register_route(Domain, ServerHost, LocalHint) ->
case {jid:nameprep(Domain), jid:nameprep(ServerHost)} of
{error, _} -> erlang:error({invalid_domain, Domain});
{_, error} -> erlang:error({invalid_domain, ServerHost});
{LDomain, LServerHost} ->
Pid = self(),
case get_component_number(LDomain) of
undefined ->
F = fun () ->
mnesia:write(#route{domain = LDomain, pid = Pid,
server_host = LServerHost,
local_hint = LocalHint})
end,
mnesia:transaction(F);
@@ -115,46 +127,42 @@ register_route(Domain, LocalHint) ->
case mnesia:wread({route, LDomain}) of
[] ->
mnesia:write(#route{domain = LDomain,
server_host = LServerHost,
pid = Pid,
local_hint = 1}),
lists:foreach(fun (I) ->
mnesia:write(#route{domain
=
LDomain,
pid
=
undefined,
local_hint
=
I})
end,
lists:seq(2, N));
lists:foreach(
fun (I) ->
mnesia:write(
#route{domain = LDomain,
pid = undefined,
server_host = LServerHost,
local_hint = I})
end,
lists:seq(2, N));
Rs ->
lists:any(fun (#route{pid = undefined,
local_hint = I} =
R) ->
mnesia:write(#route{domain =
LDomain,
pid =
Pid,
local_hint
=
I}),
mnesia:delete_object(R),
true;
(_) -> false
end,
Rs)
lists:any(
fun (#route{pid = undefined,
local_hint = I} = R) ->
mnesia:write(
#route{domain = LDomain,
pid = Pid,
server_host = LServerHost,
local_hint = I}),
mnesia:delete_object(R),
true;
(_) -> false
end,
Rs)
end
end,
mnesia:transaction(F)
end
end.
-spec register_routes([binary()]) -> ok.
-spec register_routes([{binary(), binary()}]) -> ok.
register_routes(Domains) ->
lists:foreach(fun (Domain) -> register_route(Domain)
lists:foreach(fun ({Domain, ServerHost}) -> register_route(Domain, ServerHost)
end,
Domains).
@@ -183,7 +191,9 @@ unregister_route(Domain) ->
of
[R] ->
I = R#route.local_hint,
ServerHost = R#route.server_host,
mnesia:write(#route{domain = LDomain,
server_host = ServerHost,
pid = undefined,
local_hint = I}),
mnesia:delete_object(R);
@@ -211,6 +221,20 @@ dirty_get_all_routes() ->
dirty_get_all_domains() ->
lists:usort(mnesia:dirty_all_keys(route)).
-spec host_of_route(binary()) -> binary().
host_of_route(Domain) ->
case jid:nameprep(Domain) of
error ->
erlang:error({invalid_domain, Domain});
LDomain ->
case mnesia:dirty_read(route, LDomain) of
[#route{server_host = ServerHost}|_] ->
ServerHost;
[] ->
erlang:error({unregistered_route, Domain})
end
end.
%%====================================================================
%% gen_server callbacks
@@ -283,8 +307,11 @@ handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
if is_integer(E#route.local_hint) ->
LDomain = E#route.domain,
I = E#route.local_hint,
ServerHost = E#route.server_host,
mnesia:write(#route{domain =
LDomain,
server_host =
ServerHost,
pid =
undefined,
local_hint =
@@ -394,12 +421,10 @@ get_component_number(LDomain) ->
undefined).
update_tables() ->
case catch mnesia:table_info(route, attributes) of
[domain, node, pid] -> mnesia:delete_table(route);
[domain, pid] -> mnesia:delete_table(route);
[domain, pid, local_hint] -> ok;
[domain, pid, local_hint|_] -> mnesia:delete_table(route);
{'EXIT', _} -> ok
try
mnesia:transform_table(route, ignore, record_info(fields, route))
catch exit:{aborted, {no_exists, _}} ->
ok
end,
case lists:member(local_route,
mnesia:system_info(tables))
+4 -2
View File
@@ -312,8 +312,10 @@ do_route(From, To, Packet) ->
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"No s2s connection found">>,
Err = jlib:make_error_reply(
Packet, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
ejabberd_router:route(To, From, Err)
end,
false
+1 -1
View File
@@ -325,7 +325,7 @@ wait_for_feature_request({xmlstreamelement, El},
{s2s_tls_compression, StateData#state.server},
fun(true) -> true;
(false) -> false
end, true) of
end, false) of
true -> lists:delete(compression_none, TLSOpts1);
false -> [compression_none | TLSOpts1]
end,
+1 -1
View File
@@ -192,7 +192,7 @@ init([From, Server, Type]) ->
{s2s_tls_compression, From},
fun(true) -> true;
(false) -> false
end, true) of
end, false) of
false -> [compression_none | TLSOpts4];
true -> TLSOpts4
end,
+7 -3
View File
@@ -222,7 +222,7 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
send_text(StateData, <<"<handshake/>">>),
lists:foreach(
fun (H) ->
ejabberd_router:register_route(H),
ejabberd_router:register_route(H, ?MYNAME),
?INFO_MSG("Route registered for service ~p~n",
[H])
end, dict:fetch_keys(StateData#state.host_opts)),
@@ -280,7 +280,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
and (FromJID /= error) ->
ejabberd_router:route(FromJID, ToJID, NewEl);
true ->
Err = jlib:make_error_reply(NewEl, ?ERR_BAD_REQUEST),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, El),
Txt = <<"Incorrect stanza name or from/to JID">>,
Err = jlib:make_error_reply(NewEl, ?ERRT_BAD_REQUEST(Lang, Txt)),
send_element(StateData, Err),
error
end,
@@ -360,7 +362,9 @@ handle_info({route, From, To, Packet}, StateName,
attrs = Attrs2, children = Els}),
send_text(StateData, Text);
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ALLOWED(Lang, Txt)),
ejabberd_router:route_error(To, From, Err, Packet)
end,
{next_state, StateName, StateData};
+33 -24
View File
@@ -35,6 +35,7 @@
-export([start/0,
start_link/0,
route/3,
process_iq/3,
open_session/5,
open_session/6,
close_session/4,
@@ -65,7 +66,8 @@
get_max_user_sessions/2,
get_all_pids/0,
is_existing_resource/3,
get_commands_spec/0
get_commands_spec/0,
make_sid/0
]).
-export([init/1, handle_call/3, handle_cast/2,
@@ -158,8 +160,10 @@ check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
-spec bounce_offline_message(jid(), jid(), xmlel()) -> stop.
bounce_offline_message(From, To, Packet) ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"User session not found">>,
Err = jlib:make_error_reply(
Packet, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
ejabberd_router:route(To, From, Err),
stop.
@@ -422,6 +426,7 @@ do_route(From, To, #xmlel{} = Packet) ->
#jid{user = User, server = Server,
luser = LUser, lserver = LServer, lresource = LResource} = To,
#xmlel{name = Name, attrs = Attrs} = Packet,
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
case LResource of
<<"">> ->
case Name of
@@ -495,8 +500,9 @@ do_route(From, To, #xmlel{} = Packet) ->
<<"headline">> -> route_message(From, To, Packet, headline);
<<"error">> -> ok;
<<"groupchat">> ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
ErrTxt = <<"User session not found">>,
Err = jlib:make_error_reply(
Packet, ?ERRT_SERVICE_UNAVAILABLE(Lang, ErrTxt)),
ejabberd_router:route(To, From, Err);
_ ->
route_message(From, To, Packet, normal)
@@ -514,10 +520,13 @@ do_route(From, To, #xmlel{} = Packet) ->
<<"chat">> -> route_message(From, To, Packet, chat);
<<"normal">> -> route_message(From, To, Packet, normal);
<<"">> -> route_message(From, To, Packet, normal);
<<"headline">> -> ok;
<<"error">> -> ok;
_ ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
ErrTxt = <<"User session not found">>,
Err = jlib:make_error_reply(
Packet,
?ERRT_SERVICE_UNAVAILABLE(Lang, ErrTxt)),
ejabberd_router:route(To, From, Err)
end;
<<"iq">> ->
@@ -525,8 +534,10 @@ do_route(From, To, #xmlel{} = Packet) ->
<<"error">> -> ok;
<<"result">> -> ok;
_ ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
ErrTxt = <<"User session not found">>,
Err = jlib:make_error_reply(
Packet,
?ERRT_SERVICE_UNAVAILABLE(Lang, ErrTxt)),
ejabberd_router:route(To, From, Err)
end;
_ -> ?DEBUG("packet dropped~n", [])
@@ -683,7 +694,7 @@ get_max_user_sessions(LUser, Host) ->
process_iq(From, To, Packet) ->
IQ = jlib:iq_query_info(Packet),
case IQ of
#iq{xmlns = XMLNS} ->
#iq{xmlns = XMLNS, lang = Lang} ->
Host = To#jid.lserver,
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
[{_, Module, Function}] ->
@@ -696,8 +707,10 @@ process_iq(From, To, Packet) ->
gen_iq_handler:handle(Host, Module, Function, Opts,
From, To, IQ);
[] ->
Err = jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
Txt = <<"No module is handling this query">>,
Err = jlib:make_error_reply(
Packet,
?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
ejabberd_router:route(To, From, Err)
end;
reply -> ok;
@@ -720,12 +733,10 @@ force_update_presence({LUser, LServer}) ->
-spec get_sm_backend(binary()) -> module().
get_sm_backend(Host) ->
DBType = ejabberd_config:get_option({sm_db_type, Host},
fun(mnesia) -> mnesia;
(internal) -> mnesia;
(odbc) -> odbc;
(redis) -> redis
end, mnesia),
DBType = ejabberd_config:get_option(
{sm_db_type, Host},
fun(T) -> ejabberd_config:v_db(?MODULE, T) end,
mnesia),
list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)).
-spec get_sm_backends() -> [module()].
@@ -795,10 +806,8 @@ kick_user(User, Server) ->
end, Resources),
length(Resources).
opt_type(sm_db_type) ->
fun (mnesia) -> mnesia;
(internal) -> mnesia;
(odbc) -> odbc;
(redis) -> redis
end;
make_sid() ->
{p1_time_compat:unique_timestamp(), self()}.
opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
opt_type(_) -> [sm_db_type].
@@ -6,7 +6,7 @@
%%% @end
%%% Created : 9 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(ejabberd_sm_odbc).
-module(ejabberd_sm_sql).
-behaviour(ejabberd_sm).
@@ -29,11 +29,11 @@
%%%===================================================================
-spec init() -> ok | {error, any()}.
init() ->
Node = ejabberd_odbc:escape(jlib:atom_to_binary(node())),
Node = ejabberd_sql:escape(jlib:atom_to_binary(node())),
?INFO_MSG("Cleaning SQL SM table...", []),
lists:foldl(
fun(Host, ok) ->
case ejabberd_odbc:sql_query(
case ejabberd_sql:sql_query(
Host, [<<"delete from sm where node='">>, Node, <<"'">>]) of
{updated, _} ->
ok;
@@ -47,14 +47,14 @@ init() ->
set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R},
priority = Priority, info = Info}) ->
Username = ejabberd_odbc:escape(U),
Resource = ejabberd_odbc:escape(R),
InfoS = ejabberd_odbc:encode_term(Info),
Username = ejabberd_sql:escape(U),
Resource = ejabberd_sql:escape(R),
InfoS = ejabberd_sql:encode_term(Info),
PrioS = enc_priority(Priority),
TS = now_to_timestamp(Now),
PidS = list_to_binary(erlang:pid_to_list(Pid)),
Node = ejabberd_odbc:escape(jlib:atom_to_binary(node(Pid))),
case odbc_queries:update(
Node = ejabberd_sql:escape(jlib:atom_to_binary(node(Pid))),
case sql_queries:update(
LServer,
<<"sm">>,
[<<"usec">>, <<"pid">>, <<"node">>, <<"username">>,
@@ -70,12 +70,12 @@ set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R},
delete_session(_LUser, LServer, _LResource, {Now, Pid}) ->
TS = now_to_timestamp(Now),
PidS = list_to_binary(erlang:pid_to_list(Pid)),
case ejabberd_odbc:sql_query(
case ejabberd_sql:sql_query(
LServer,
[<<"select usec, pid, username, resource, priority, info ">>,
<<"from sm where usec='">>, TS, <<"' and pid='">>,PidS, <<"'">>]) of
{selected, _, [Row]} ->
ejabberd_odbc:sql_query(
ejabberd_sql:sql_query(
LServer, [<<"delete from sm where usec='">>,
TS, <<"' and pid='">>, PidS, <<"'">>]),
{ok, row_to_session(LServer, Row)};
@@ -93,7 +93,7 @@ get_sessions() ->
end, ejabberd_sm:get_vh_by_backend(?MODULE)).
get_sessions(LServer) ->
case ejabberd_odbc:sql_query(
case ejabberd_sql:sql_query(
LServer, [<<"select usec, pid, username, ">>,
<<"resource, priority, info from sm">>]) of
{selected, _, Rows} ->
@@ -104,8 +104,8 @@ get_sessions(LServer) ->
end.
get_sessions(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
case ejabberd_odbc:sql_query(
Username = ejabberd_sql:escape(LUser),
case ejabberd_sql:sql_query(
LServer, [<<"select usec, pid, username, ">>,
<<"resource, priority, info from sm where ">>,
<<"username='">>, Username, <<"'">>]) of
@@ -117,9 +117,9 @@ get_sessions(LUser, LServer) ->
end.
get_sessions(LUser, LServer, LResource) ->
Username = ejabberd_odbc:escape(LUser),
Resource = ejabberd_odbc:escape(LResource),
case ejabberd_odbc:sql_query(
Username = ejabberd_sql:escape(LUser),
Resource = ejabberd_sql:escape(LResource),
case ejabberd_sql:sql_query(
LServer, [<<"select usec, pid, username, ">>,
<<"resource, priority, info from sm where ">>,
<<"username='">>, Username, <<"' and resource='">>,
@@ -162,7 +162,7 @@ row_to_session(LServer, [USec, PidS, User, Resource, PrioS, InfoS]) ->
Now = timestamp_to_now(USec),
Pid = erlang:list_to_pid(binary_to_list(PidS)),
Priority = dec_priority(PrioS),
Info = ejabberd_odbc:decode_term(InfoS),
Info = ejabberd_sql:decode_term(InfoS),
#session{sid = {Now, Pid}, us = {User, LServer},
usr = {User, LServer, Resource},
priority = Priority,
+258 -62
View File
@@ -23,7 +23,7 @@
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_odbc).
-module(ejabberd_sql).
-behaviour(ejabberd_config).
@@ -41,6 +41,7 @@
sql_bloc/2,
escape/1,
escape_like/1,
escape_like_arg/1,
to_bool/1,
sqlite_db/1,
sqlite_file/1,
@@ -63,18 +64,20 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
-record(state,
{db_ref = self() :: pid(),
db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql,
db_version = undefined :: undefined | non_neg_integer(),
start_interval = 0 :: non_neg_integer(),
host = <<"">> :: binary(),
max_pending_requests_len :: non_neg_integer(),
pending_requests = {0, queue:new()} :: {non_neg_integer(), ?TQUEUE}}).
-define(STATE_KEY, ejabberd_odbc_state).
-define(STATE_KEY, ejabberd_sql_state).
-define(NESTING_KEY, ejabberd_odbc_nesting_level).
-define(NESTING_KEY, ejabberd_sql_nesting_level).
-define(TOP_LEVEL_TXN, 0).
@@ -92,6 +95,8 @@
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
-define(PREPARE_KEY, ejabberd_sql_prepare).
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -108,19 +113,21 @@
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
(?GEN_FSM):start(ejabberd_odbc, [Host],
(?GEN_FSM):start(ejabberd_sql, [Host],
fsm_limit_opts() ++ (?FSMOPTS)).
start_link(Host, StartInterval) ->
(?GEN_FSM):start_link(ejabberd_odbc,
(?GEN_FSM):start_link(ejabberd_sql,
[Host, StartInterval],
fsm_limit_opts() ++ (?FSMOPTS)).
-type sql_query() :: [sql_query() | binary()].
-type sql_query() :: [sql_query() | binary()] | #sql_query{} |
fun(() -> any()) | fun((atom(), _) -> any()).
-type sql_query_result() :: {updated, non_neg_integer()} |
{error, binary()} |
{selected, [binary()],
[[binary()]]}.
[[binary()]]} |
{selected, [any()]}.
-spec sql_query(binary(), sql_query()) -> sql_query_result().
@@ -150,7 +157,7 @@ sql_bloc(Host, F) -> sql_call(Host, {sql_bloc, F}).
sql_call(Host, Msg) ->
case get(?STATE_KEY) of
undefined ->
case ejabberd_odbc_sup:get_random_pid(Host) of
case ejabberd_sql_sup:get_random_pid(Host) of
none -> {error, <<"Unknown Host">>};
Pid ->
(?GEN_FSM):sync_send_event(Pid,{sql_cmd, Msg,
@@ -183,7 +190,7 @@ sql_query_t(Query) ->
%% Escape character that will confuse an SQL engine
escape(S) ->
<< <<(odbc_queries:escape(Char))/binary>> || <<Char>> <= S >>.
<< <<(sql_queries:escape(Char))/binary>> || <<Char>> <= S >>.
%% Escape character that will confuse an SQL engine
%% Percent and underscore only need to be escaped for pattern matching like
@@ -192,7 +199,14 @@ escape_like(S) when is_binary(S) ->
<< <<(escape_like(C))/binary>> || <<C>> <= S >>;
escape_like($%) -> <<"\\%">>;
escape_like($_) -> <<"\\_">>;
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> odbc_queries:escape(C).
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> sql_queries:escape(C).
escape_like_arg(S) when is_binary(S) ->
<< <<(escape_like_arg(C))/binary>> || <<C>> <= S >>;
escape_like_arg($%) -> <<"\\%">>;
escape_like_arg($_) -> <<"\\_">>;
escape_like_arg($\\) -> <<"\\\\">>;
escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>.
to_bool(<<"t">>) -> true;
to_bool(<<"true">>) -> true;
@@ -218,7 +232,7 @@ sqlite_db(Host) ->
-spec sqlite_file(binary()) -> string().
sqlite_file(Host) ->
case ejabberd_config:get_option({odbc_database, Host},
case ejabberd_config:get_option({sql_database, Host},
fun iolist_to_binary/1) of
undefined ->
{ok, Cwd} = file:get_cwd(),
@@ -233,7 +247,7 @@ sqlite_file(Host) ->
%%%----------------------------------------------------------------------
init([Host, StartInterval]) ->
case ejabberd_config:get_option(
{odbc_keepalive_interval, Host},
{sql_keepalive_interval, Host},
fun(I) when is_integer(I), I>0 -> I end) of
undefined ->
ok;
@@ -243,7 +257,7 @@ init([Host, StartInterval]) ->
end,
[DBType | _] = db_opts(Host),
(?GEN_FSM):send_event(self(), connect),
ejabberd_odbc_sup:add_pid(Host, self()),
ejabberd_sql_sup:add_pid(Host, self()),
{ok, connecting,
#state{db_type = DBType, host = Host,
max_pending_requests_len = max_fsm_queue(),
@@ -255,19 +269,21 @@ connecting(connect, #state{host = Host} = State) ->
[mysql | Args] -> apply(fun mysql_connect/5, Args);
[pgsql | Args] -> apply(fun pgsql_connect/5, Args);
[sqlite | Args] -> apply(fun sqlite_connect/1, Args);
[mssql | Args] -> apply(fun odbc_connect/1, Args);
[odbc | Args] -> apply(fun odbc_connect/1, Args)
end,
{_, PendingRequests} = State#state.pending_requests,
case ConnectRes of
{ok, Ref} ->
erlang:monitor(process, Ref),
lists:foreach(fun (Req) ->
(?GEN_FSM):send_event(self(), Req)
end,
queue:to_list(PendingRequests)),
{next_state, session_established,
State#state{db_ref = Ref,
pending_requests = {0, queue:new()}}};
{ok, Ref} ->
erlang:monitor(process, Ref),
lists:foreach(fun (Req) ->
(?GEN_FSM):send_event(self(), Req)
end,
queue:to_list(PendingRequests)),
State1 = State#state{db_ref = Ref,
pending_requests = {0, queue:new()}},
State2 = get_db_version(State1),
{next_state, session_established, State2};
{error, Reason} ->
?INFO_MSG("~p connection failed:~n** Reason: ~p~n** "
"Retry after: ~p seconds",
@@ -355,7 +371,7 @@ handle_info(Info, StateName, State) ->
{next_state, StateName, State}.
terminate(_Reason, _StateName, State) ->
ejabberd_odbc_sup:remove_pid(State#state.host, self()),
ejabberd_sql_sup:remove_pid(State#state.host, self()),
case State#state.db_type of
mysql -> catch p1_mysql_conn:stop(State#state.db_ref);
sqlite -> catch sqlite3:close(sqlite_db(State#state.host));
@@ -469,12 +485,82 @@ execute_bloc(F) ->
Res -> {atomic, Res}
end.
execute_fun(F) when is_function(F, 0) ->
F();
execute_fun(F) when is_function(F, 2) ->
State = get(?STATE_KEY),
F(State#state.db_type, State#state.db_version).
sql_query_internal([{_, _} | _] = Queries) ->
State = get(?STATE_KEY),
case select_sql_query(Queries, State) of
undefined ->
{error, <<"no matching query for the current DBMS found">>};
Query ->
sql_query_internal(Query)
end;
sql_query_internal(#sql_query{} = Query) ->
State = get(?STATE_KEY),
Res =
try
case State#state.db_type of
odbc ->
generic_sql_query(Query);
mssql ->
generic_sql_query(Query);
pgsql ->
Key = {?PREPARE_KEY, Query#sql_query.hash},
case get(Key) of
undefined ->
case pgsql_prepare(Query, State) of
{ok, _, _, _} ->
put(Key, prepared);
{error, Error} ->
?ERROR_MSG("PREPARE failed for SQL query "
"at ~p: ~p",
[Query#sql_query.loc, Error]),
put(Key, ignore)
end;
_ ->
ok
end,
case get(Key) of
prepared ->
pgsql_execute_sql_query(Query, State);
_ ->
generic_sql_query(Query)
end;
mysql ->
generic_sql_query(Query);
sqlite ->
generic_sql_query(Query)
end
catch
Class:Reason ->
ST = erlang:get_stacktrace(),
?ERROR_MSG("Internal error while processing SQL query: ~p",
[{Class, Reason, ST}]),
{error, <<"internal error">>}
end,
case Res of
{error, <<"No SQL-driver information available.">>} ->
{updated, 0};
_Else -> Res
end;
sql_query_internal(F) when is_function(F) ->
case catch execute_fun(F) of
{'EXIT', Reason} -> {error, Reason};
Res -> Res
end;
sql_query_internal(Query) ->
State = get(?STATE_KEY),
?DEBUG("SQL: \"~s\"", [Query]),
Res = case State#state.db_type of
odbc ->
to_odbc(odbc:sql_query(State#state.db_ref, Query,
to_odbc(odbc:sql_query(State#state.db_ref, [Query],
(?TRANSACTION_TIMEOUT) - 1000));
mssql ->
to_odbc(odbc:sql_query(State#state.db_ref, [Query],
(?TRANSACTION_TIMEOUT) - 1000));
pgsql ->
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query));
@@ -495,6 +581,93 @@ sql_query_internal(Query) ->
_Else -> Res
end.
select_sql_query(Queries, State) ->
select_sql_query(
Queries, State#state.db_type, State#state.db_version, undefined).
select_sql_query([], _Type, _Version, undefined) ->
undefined;
select_sql_query([], _Type, _Version, Query) ->
Query;
select_sql_query([{any, Query} | _], _Type, _Version, _) ->
Query;
select_sql_query([{Type, Query} | _], Type, _Version, _) ->
Query;
select_sql_query([{{Type, _Version1}, Query1} | Rest], Type, undefined, _) ->
select_sql_query(Rest, Type, undefined, Query1);
select_sql_query([{{Type, Version1}, Query1} | Rest], Type, Version, Query) ->
if
Version >= Version1 ->
Query1;
true ->
select_sql_query(Rest, Type, Version, Query)
end;
select_sql_query([{_, _} | Rest], Type, Version, Query) ->
select_sql_query(Rest, Type, Version, Query).
generic_sql_query(SQLQuery) ->
sql_query_format_res(
sql_query_internal(generic_sql_query_format(SQLQuery)),
SQLQuery).
generic_sql_query_format(SQLQuery) ->
Args = (SQLQuery#sql_query.args)(generic_escape()),
(SQLQuery#sql_query.format_query)(Args).
generic_escape() ->
#sql_escape{string = fun(X) -> <<"'", (escape(X))/binary, "'">> end,
integer = fun(X) -> integer_to_binary(X) end,
boolean = fun(true) -> <<"1">>;
(false) -> <<"0">>
end
}.
pgsql_prepare(SQLQuery, State) ->
Escape = #sql_escape{_ = fun(X) -> X end},
N = length((SQLQuery#sql_query.args)(Escape)),
Args = [<<$$, (integer_to_binary(I))/binary>> || I <- lists:seq(1, N)],
Query = (SQLQuery#sql_query.format_query)(Args),
pgsql:prepare(State#state.db_ref, SQLQuery#sql_query.hash, Query).
pgsql_execute_escape() ->
#sql_escape{string = fun(X) -> X end,
integer = fun(X) -> [integer_to_binary(X)] end,
boolean = fun(true) -> "1";
(false) -> "0"
end
}.
pgsql_execute_sql_query(SQLQuery, State) ->
Args = (SQLQuery#sql_query.args)(pgsql_execute_escape()),
ExecuteRes =
pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args),
% {T, ExecuteRes} =
% timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]),
% io:format("T ~s ~p~n", [SQLQuery#sql_query.hash, T]),
Res = pgsql_execute_to_odbc(ExecuteRes),
sql_query_format_res(Res, SQLQuery).
sql_query_format_res({selected, _, Rows}, SQLQuery) ->
Res =
lists:flatmap(
fun(Row) ->
try
[(SQLQuery#sql_query.format_res)(Row)]
catch
Class:Reason ->
ST = erlang:get_stacktrace(),
?ERROR_MSG("Error while processing "
"SQL query result: ~p~n"
"row: ~p",
[{Class, Reason, ST}, Row]),
[]
end
end, Rows),
{selected, Res};
sql_query_format_res(Res, _SQLQuery) ->
Res.
%% Generate the OTP callback return tuple depending on the driver result.
abort_on_driver_error({error, <<"query timed out">>} =
Reply,
@@ -606,6 +779,18 @@ pgsql_item_to_odbc(<<"UPDATE ", N/binary>>) ->
pgsql_item_to_odbc({error, Error}) -> {error, Error};
pgsql_item_to_odbc(_) -> {updated, undefined}.
pgsql_execute_to_odbc({ok, {<<"SELECT", _/binary>>, Rows}}) ->
{selected, [], [[Field || {_, Field} <- Row] || Row <- Rows]};
pgsql_execute_to_odbc({ok, {'INSERT', N}}) ->
{updated, N};
pgsql_execute_to_odbc({ok, {'DELETE', N}}) ->
{updated, N};
pgsql_execute_to_odbc({ok, {'UPDATE', N}}) ->
{updated, N};
pgsql_execute_to_odbc({error, Error}) -> {error, Error};
pgsql_execute_to_odbc(_) -> {updated, undefined}.
%% == Native MySQL code
%% part of init/1
@@ -658,6 +843,24 @@ to_odbc({error, Reason}) when is_list(Reason) ->
to_odbc(Res) ->
Res.
get_db_version(#state{db_type = pgsql} = State) ->
case pgsql:squery(State#state.db_ref,
<<"select current_setting('server_version_num')">>) of
{ok, [{_, _, [[SVersion]]}]} ->
case catch binary_to_integer(SVersion) of
Version when is_integer(Version) ->
State#state{db_version = Version};
Error ->
?WARNING_MSG("error getting pgsql version: ~p", [Error]),
State
end;
Res ->
?WARNING_MSG("error getting pgsql version: ~p", [Res]),
State
end;
get_db_version(State) ->
State.
log(Level, Format, Args) ->
case Level of
debug -> ?DEBUG(Format, Args);
@@ -666,14 +869,14 @@ log(Level, Format, Args) ->
end.
db_opts(Host) ->
Type = ejabberd_config:get_option({odbc_type, Host},
Type = ejabberd_config:get_option({sql_type, Host},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
(mssql) -> mssql;
(odbc) -> odbc
end, odbc),
Server = ejabberd_config:get_option({odbc_server, Host},
Server = ejabberd_config:get_option({sql_server, Host},
fun iolist_to_binary/1,
<<"localhost">>),
case Type of
@@ -683,41 +886,40 @@ db_opts(Host) ->
[sqlite, Host];
_ ->
Port = ejabberd_config:get_option(
{odbc_port, Host},
{sql_port, Host},
fun(P) when is_integer(P), P > 0, P < 65536 -> P end,
case Type of
mssql -> ?MSSQL_PORT;
mysql -> ?MYSQL_PORT;
pgsql -> ?PGSQL_PORT
end),
DB = ejabberd_config:get_option({odbc_database, Host},
DB = ejabberd_config:get_option({sql_database, Host},
fun iolist_to_binary/1,
<<"ejabberd">>),
User = ejabberd_config:get_option({odbc_username, Host},
User = ejabberd_config:get_option({sql_username, Host},
fun iolist_to_binary/1,
<<"ejabberd">>),
Pass = ejabberd_config:get_option({odbc_password, Host},
Pass = ejabberd_config:get_option({sql_password, Host},
fun iolist_to_binary/1,
<<"">>),
case Type of
mssql ->
Username = get_mssql_user(Server, User),
[odbc, <<"DSN=", Host/binary, ";UID=", Username/binary,
";PWD=", Pass/binary>>];
[mssql, <<"DSN=", Host/binary, ";UID=", User/binary,
";PWD=", Pass/binary>>];
_ ->
[Type, Server, Port, DB, User, Pass]
end
end.
init_mssql(Host) ->
Server = ejabberd_config:get_option({odbc_server, Host},
Server = ejabberd_config:get_option({sql_server, Host},
fun iolist_to_binary/1,
<<"localhost">>),
Port = ejabberd_config:get_option(
{odbc_port, Host},
{sql_port, Host},
fun(P) when is_integer(P), P > 0, P < 65536 -> P end,
?MSSQL_PORT),
DB = ejabberd_config:get_option({odbc_database, Host},
DB = ejabberd_config:get_option({sql_database, Host},
fun iolist_to_binary/1,
<<"ejabberd">>),
FreeTDS = io_lib:fwrite("[~s]~n"
@@ -762,21 +964,6 @@ init_mssql(Host) ->
Err
end.
get_mssql_user(Server, User) ->
HostName = case inet_parse:address(binary_to_list(Server)) of
{ok, _} ->
Server;
{error, _} ->
hd(str:tokens(Server, <<".">>))
end,
UserName = case str:chr(User, $@) of
0 ->
<<User/binary, $@, HostName/binary>>;
_ ->
User
end,
UserName.
tmp_dir() ->
filename:join(["/tmp", "ejabberd"]).
@@ -800,30 +987,39 @@ fsm_limit_opts() ->
_ -> []
end.
check_error({error, Why} = Err, #sql_query{} = Query) ->
?ERROR_MSG("SQL query '~s' at ~p failed: ~p",
[Query#sql_query.hash, Query#sql_query.loc, Why]),
Err;
check_error({error, Why} = Err, Query) ->
?ERROR_MSG("SQL query '~s' failed: ~p", [Query, Why]),
case catch iolist_to_binary(Query) of
SQuery when is_binary(SQuery) ->
?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Why]);
_ ->
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Why])
end,
Err;
check_error(Result, _Query) ->
Result.
opt_type(max_fsm_queue) ->
fun (N) when is_integer(N), N > 0 -> N end;
opt_type(odbc_database) -> fun iolist_to_binary/1;
opt_type(odbc_keepalive_interval) ->
opt_type(sql_database) -> fun iolist_to_binary/1;
opt_type(sql_keepalive_interval) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(odbc_password) -> fun iolist_to_binary/1;
opt_type(odbc_port) ->
opt_type(sql_password) -> fun iolist_to_binary/1;
opt_type(sql_port) ->
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
opt_type(odbc_server) -> fun iolist_to_binary/1;
opt_type(odbc_type) ->
opt_type(sql_server) -> fun iolist_to_binary/1;
opt_type(sql_type) ->
fun (mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
(mssql) -> mssql;
(odbc) -> odbc
end;
opt_type(odbc_username) -> fun iolist_to_binary/1;
opt_type(sql_username) -> fun iolist_to_binary/1;
opt_type(_) ->
[max_fsm_queue, odbc_database, odbc_keepalive_interval,
odbc_password, odbc_port, odbc_server, odbc_type,
odbc_username].
[max_fsm_queue, sql_database, sql_keepalive_interval,
sql_password, sql_port, sql_server, sql_type,
sql_username].
+544
View File
@@ -0,0 +1,544 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_sql_pt.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Description : Parse transform for SQL queries
%%%
%%% Created : 20 Jan 2016 by Alexey Shchepin <alexey@process-one.net>
%%%-------------------------------------------------------------------
-module(ejabberd_sql_pt).
%% API
-export([parse_transform/2]).
-export([parse/2]).
-include("ejabberd_sql_pt.hrl").
-record(state, {loc,
'query' = [],
params = [],
param_pos = 0,
args = [],
res = [],
res_vars = [],
res_pos = 0}).
-define(QUERY_RECORD, "sql_query").
-define(ESCAPE_RECORD, "sql_escape").
-define(ESCAPE_VAR, "__SQLEscape").
-define(MOD, sql__module_).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function:
%% Description:
%%--------------------------------------------------------------------
parse_transform(AST, _Options) ->
%io:format("PT: ~p~nOpts: ~p~n", [AST, Options]),
NewAST = top_transform(AST),
%io:format("NewPT: ~p~n", [NewAST]),
NewAST.
%%====================================================================
%% Internal functions
%%====================================================================
transform(Form) ->
case erl_syntax:type(Form) of
application ->
case erl_syntax_lib:analyze_application(Form) of
{?SQL_MARK, 1} ->
case erl_syntax:application_arguments(Form) of
[Arg] ->
case erl_syntax:type(Arg) of
string ->
S = erl_syntax:string_value(Arg),
Pos = erl_syntax:get_pos(Arg),
ParseRes = parse(S, Pos),
set_pos(make_sql_query(ParseRes), Pos);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL argument must be "
"a constant string"})
end;
_ ->
throw({error, erl_syntax:get_pos(Form),
"wrong number of ?SQL args"})
end;
{?SQL_UPSERT_MARK, 2} ->
case erl_syntax:application_arguments(Form) of
[TableArg, FieldsArg] ->
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),
set_pos(
make_sql_upsert(Table, ParseRes, Pos),
Pos);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL_UPSERT arguments must be "
"a constant string and a list"})
end;
_ ->
throw({error, erl_syntax:get_pos(Form),
"wrong number of ?SQL_UPSERT args"})
end;
_ ->
Form
end;
attribute ->
case erl_syntax:atom_value(erl_syntax:attribute_name(Form)) of
module ->
case erl_syntax:attribute_arguments(Form) of
[M | _] ->
Module = erl_syntax:atom_value(M),
%io:format("module ~p~n", [Module]),
put(?MOD, Module),
Form;
_ ->
Form
end;
_ ->
Form
end;
_ ->
Form
end.
top_transform(Forms) when is_list(Forms) ->
lists:map(
fun(Form) ->
try
Form2 = erl_syntax_lib:map(
fun(Node) ->
%io:format("asd ~p~n", [Node]),
transform(Node)
end, Form),
Form3 = erl_syntax:revert(Form2),
Form3
catch
throw:{error, Line, Error} ->
{error, {Line, erl_parse, Error}}
end
end, Forms).
parse(S, Loc) ->
parse1(S, [], #state{loc = Loc}).
parse(S, ParamPos, Loc) ->
parse1(S, [], #state{loc = Loc, param_pos = ParamPos}).
parse1([], Acc, State) ->
State1 = append_string(lists:reverse(Acc), State),
State1#state{'query' = lists:reverse(State1#state.'query'),
params = lists:reverse(State1#state.params),
args = lists:reverse(State1#state.args),
res = lists:reverse(State1#state.res),
res_vars = lists:reverse(State1#state.res_vars)
};
parse1([$@, $( | S], Acc, State) ->
State1 = append_string(lists:reverse(Acc), State),
{Name, Type, S1, State2} = parse_name(S, State1),
Var = "__V" ++ integer_to_list(State2#state.res_pos),
EVar = erl_syntax:variable(Var),
Convert =
case Type of
integer ->
erl_syntax:application(
erl_syntax:atom(binary_to_integer),
[EVar]);
string ->
EVar;
boolean ->
erl_syntax:application(
erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(to_bool),
[EVar])
end,
State3 = append_string(Name, State2),
State4 = State3#state{res_pos = State3#state.res_pos + 1,
res = [Convert | State3#state.res],
res_vars = [EVar | State3#state.res_vars]},
parse1(S1, [], State4);
parse1([$%, $( | S], Acc, State) ->
State1 = append_string(lists:reverse(Acc), State),
{Name, Type, S1, State2} = parse_name(S, State1),
Var = State2#state.param_pos,
Convert =
erl_syntax:application(
erl_syntax:record_access(
erl_syntax:variable(?ESCAPE_VAR),
erl_syntax:atom(?ESCAPE_RECORD),
erl_syntax:atom(Type)),
[erl_syntax:variable(Name)]),
State3 = State2,
State4 =
State3#state{'query' = [{var, Var} | State3#state.'query'],
args = [Convert | State3#state.args],
params = [Var | State3#state.params],
param_pos = State3#state.param_pos + 1},
parse1(S1, [], State4);
parse1([C | S], Acc, State) ->
parse1(S, [C | Acc], State).
append_string([], State) ->
State;
append_string(S, State) ->
State#state{query = [{str, S} | State#state.query]}.
parse_name(S, State) ->
parse_name(S, [], 0, State).
parse_name([], _Acc, _Depth, State) ->
throw({error, State#state.loc,
"expected ')', found end of string"});
parse_name([$), T | S], Acc, 0, State) ->
Type =
case T of
$d -> integer;
$s -> string;
$b -> boolean;
_ ->
throw({error, State#state.loc,
["unknown type specifier '", T, "'"]})
end,
{lists:reverse(Acc), Type, S, State};
parse_name([$)], _Acc, 0, State) ->
throw({error, State#state.loc,
"expected type specifier, found end of string"});
parse_name([$( = C | S], Acc, Depth, State) ->
parse_name(S, [C | Acc], Depth + 1, State);
parse_name([$) = C | S], Acc, Depth, State) ->
parse_name(S, [C | Acc], Depth - 1, State);
parse_name([C | S], Acc, Depth, State) ->
parse_name(S, [C | Acc], Depth, State).
make_var(V) ->
Var = "__V" ++ integer_to_list(V),
erl_syntax:variable(Var).
make_sql_query(State) ->
Hash = erlang:phash2(State#state{loc = undefined}),
SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
Query = pack_query(State#state.'query'),
EQuery =
lists:map(
fun({str, S}) ->
erl_syntax:binary(
[erl_syntax:binary_field(
erl_syntax:string(S))]);
({var, V}) -> make_var(V)
end, Query),
erl_syntax:record_expr(
erl_syntax:atom(?QUERY_RECORD),
[erl_syntax:record_field(
erl_syntax:atom(hash),
%erl_syntax:abstract(SHash)
erl_syntax:binary(
[erl_syntax:binary_field(
erl_syntax:string(binary_to_list(SHash)))])),
erl_syntax:record_field(
erl_syntax:atom(args),
erl_syntax:fun_expr(
[erl_syntax:clause(
[erl_syntax:variable(?ESCAPE_VAR)],
none,
[erl_syntax:list(State#state.args)]
)])),
erl_syntax:record_field(
erl_syntax:atom(format_query),
erl_syntax:fun_expr(
[erl_syntax:clause(
[erl_syntax:list(lists:map(fun make_var/1, State#state.params))],
none,
[erl_syntax:list(EQuery)]
)])),
erl_syntax:record_field(
erl_syntax:atom(format_res),
erl_syntax:fun_expr(
[erl_syntax:clause(
[erl_syntax:list(State#state.res_vars)],
none,
[erl_syntax:tuple(State#state.res)]
)])),
erl_syntax:record_field(
erl_syntax:atom(loc),
erl_syntax:abstract({get(?MOD), State#state.loc}))
]).
pack_query([]) ->
[];
pack_query([{str, S1}, {str, S2} | Rest]) ->
pack_query([{str, S1 ++ S2} | Rest]);
pack_query([X | Rest]) ->
[X | pack_query(Rest)].
parse_upsert(Fields) ->
{Fs, _} =
lists:foldr(
fun(F, {Acc, Param}) ->
case erl_syntax:type(F) of
string ->
V = erl_syntax:string_value(F),
{_, _, State} = Res =
parse_upsert_field(
V, Param, erl_syntax:get_pos(F)),
{[Res | Acc], State#state.param_pos};
_ ->
throw({error, erl_syntax:get_pos(F),
"?SQL_UPSERT field must be "
"a constant string"})
end
end, {[], 0}, Fields),
%io:format("asd ~p~n", [{Fields, Fs}]),
Fs.
parse_upsert_field([$! | S], ParamPos, Loc) ->
{Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc),
{Name, true, ParseState};
parse_upsert_field(S, ParamPos, Loc) ->
{Name, ParseState} = parse_upsert_field1(S, [], ParamPos, Loc),
{Name, false, ParseState}.
parse_upsert_field1([], _Acc, _ParamPos, Loc) ->
throw({error, 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)};
parse_upsert_field1([C | S], Acc, ParamPos, Loc) ->
parse_upsert_field1(S, [C | Acc], ParamPos, Loc).
make_sql_upsert(Table, ParseRes, Pos) ->
check_upsert(ParseRes, Pos),
erl_syntax:fun_expr(
[erl_syntax:clause(
[erl_syntax:atom(pgsql), erl_syntax:variable("__Version")],
[erl_syntax:infix_expr(
erl_syntax:variable("__Version"),
erl_syntax:operator('>='),
erl_syntax:integer(90100))],
[make_sql_upsert_pgsql901(Table, ParseRes),
erl_syntax:atom(ok)]),
erl_syntax:clause(
[erl_syntax:underscore(), erl_syntax:underscore()],
none,
[make_sql_upsert_generic(Table, ParseRes)])
]).
make_sql_upsert_generic(Table, ParseRes) ->
Update = make_sql_query(make_sql_upsert_update(Table, ParseRes)),
Insert = make_sql_query(make_sql_upsert_insert(Table, ParseRes)),
InsertBranch =
erl_syntax:case_expr(
erl_syntax:application(
erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(sql_query_t),
[Insert]),
[erl_syntax:clause(
[erl_syntax:abstract({updated, 1})],
none,
[erl_syntax:atom(ok)]),
erl_syntax:clause(
[erl_syntax:variable("__UpdateRes")],
none,
[erl_syntax:variable("__UpdateRes")])]),
erl_syntax:case_expr(
erl_syntax:application(
erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(sql_query_t),
[Update]),
[erl_syntax:clause(
[erl_syntax:abstract({updated, 1})],
none,
[erl_syntax:atom(ok)]),
erl_syntax:clause(
[erl_syntax:underscore()],
none,
[InsertBranch])]).
make_sql_upsert_update(Table, ParseRes) ->
WPairs =
lists:flatmap(
fun({_Field, false, _ST}) ->
[];
({Field, true, ST}) ->
[ST#state{
'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'
}]
end, ParseRes),
Where = join_states(WPairs, " AND "),
SPairs =
lists:flatmap(
fun({_Field, true, _ST}) ->
[];
({Field, false, ST}) ->
[ST#state{
'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'
}]
end, ParseRes),
Set = join_states(SPairs, ", "),
State =
concat_states(
[#state{'query' = [{str, "UPDATE "}, {str, Table}, {str, " SET "}]},
Set,
#state{'query' = [{str, " WHERE "}]},
Where
]),
State.
make_sql_upsert_insert(Table, ParseRes) ->
Vals =
lists:map(
fun({_Field, _, ST}) ->
ST
end, ParseRes),
Fields =
lists:map(
fun({Field, _, _ST}) ->
#state{'query' = [{str, Field}]}
end, ParseRes),
State =
concat_states(
[#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]},
join_states(Fields, ", "),
#state{'query' = [{str, ") VALUES ("}]},
join_states(Vals, ", "),
#state{'query' = [{str, ")"}]}
]),
State.
make_sql_upsert_pgsql901(Table, ParseRes) ->
Update = make_sql_upsert_update(Table, ParseRes),
Vals =
lists:map(
fun({_Field, _, ST}) ->
ST
end, ParseRes),
Fields =
lists:map(
fun({Field, _, _ST}) ->
#state{'query' = [{str, Field}]}
end, ParseRes),
Insert =
concat_states(
[#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]},
join_states(Fields, ", "),
#state{'query' = [{str, ") SELECT "}]},
join_states(Vals, ", "),
#state{'query' = [{str, " WHERE NOT EXISTS (SELECT * FROM upsert)"}]}
]),
State =
concat_states(
[#state{'query' = [{str, "WITH upsert AS ("}]},
Update,
#state{'query' = [{str, " RETURNING *) "}]},
Insert
]),
Upsert = make_sql_query(State),
erl_syntax:application(
erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(sql_query_t),
[Upsert]).
check_upsert(ParseRes, Pos) ->
Set =
lists:filter(
fun({_Field, Match, _ST}) ->
not Match
end, ParseRes),
case Set of
[] ->
throw({error, Pos,
"No ?SQL_UPSERT fields to set, use INSERT instead"});
_ ->
ok
end,
ok.
concat_states(States) ->
lists:foldr(
fun(ST11, ST2) ->
ST1 = resolve_vars(ST11, ST2),
ST1#state{
'query' = ST1#state.'query' ++ ST2#state.'query',
params = ST1#state.params ++ ST2#state.params,
args = ST1#state.args ++ ST2#state.args,
res = ST1#state.res ++ ST2#state.res,
res_vars = ST1#state.res_vars ++ ST2#state.res_vars,
loc = case ST1#state.loc of
undefined -> ST2#state.loc;
_ -> ST1#state.loc
end
}
end, #state{}, States).
resolve_vars(ST1, ST2) ->
Max = lists:max([0 | ST1#state.params ++ ST2#state.params]),
{Map, _} =
lists:foldl(
fun(Var, {Acc, New}) ->
case lists:member(Var, ST2#state.params) of
true ->
{dict:store(Var, New, Acc), New + 1};
false ->
{Acc, New}
end
end, {dict:new(), Max + 1}, ST1#state.params),
NewParams =
lists:map(
fun(Var) ->
case dict:find(Var, Map) of
{ok, New} ->
New;
error ->
Var
end
end, ST1#state.params),
NewQuery =
lists:map(
fun({var, Var}) ->
case dict:find(Var, Map) of
{ok, New} ->
{var, New};
error ->
{var, Var}
end;
(S) -> S
end, ST1#state.'query'),
ST1#state{params = NewParams, 'query' = NewQuery}.
join_states([], _Sep) ->
#state{};
join_states([H | T], Sep) ->
J = [[H] | [[#state{'query' = [{str, Sep}]}, X] || X <- T]],
concat_states(lists:append(J)).
set_pos(Tree, Pos) ->
erl_syntax_lib:map(
fun(Node) ->
case erl_syntax:get_pos(Node) of
0 -> erl_syntax:set_pos(Node, Pos);
_ -> Node
end
end, Tree).
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_odbc_sup.erl
%%% File : ejabberd_sql_sup.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : ODBC connections supervisor
%%% Purpose : SQL connections supervisor
%%% Created : 22 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
@@ -23,7 +23,7 @@
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_odbc_sup).
-module(ejabberd_sql_sup).
-behaviour(ejabberd_config).
@@ -42,7 +42,7 @@
-define(DEFAULT_POOL_SIZE, 10).
-define(DEFAULT_ODBC_START_INTERVAL, 30).
-define(DEFAULT_SQL_START_INTERVAL, 30).
-define(CONNECT_TIMEOUT, 500).
@@ -62,14 +62,14 @@ start_link(Host) ->
init([Host]) ->
PoolSize = ejabberd_config:get_option(
{odbc_pool_size, Host},
{sql_pool_size, Host},
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_POOL_SIZE),
StartInterval = ejabberd_config:get_option(
{odbc_start_interval, Host},
{sql_start_interval, Host},
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_ODBC_START_INTERVAL),
Type = ejabberd_config:get_option({odbc_type, Host},
?DEFAULT_SQL_START_INTERVAL),
Type = ejabberd_config:get_option({sql_type, Host},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
@@ -80,7 +80,7 @@ init([Host]) ->
sqlite ->
check_sqlite_db(Host);
mssql ->
ejabberd_odbc:init_mssql(Host);
ejabberd_sql:init_mssql(Host);
_ ->
ok
end,
@@ -89,7 +89,7 @@ init([Host]) ->
{{one_for_one, PoolSize * 10, 1},
lists:map(fun (I) ->
{I,
{ejabberd_odbc, start_link,
{ejabberd_sql, start_link,
[Host, StartInterval * 1000]},
transient, 2000, worker, [?MODULE]}
end,
@@ -121,12 +121,12 @@ transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
[{odbc_type, Type},
{odbc_server, Server},
{odbc_port, Port},
{odbc_database, DB},
{odbc_username, User},
{odbc_password, Pass}|Opts];
[{sql_type, Type},
{sql_server, Server},
{sql_port, Port},
{sql_database, DB},
{sql_username, User},
{sql_password, Pass}|Opts];
transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
@@ -137,8 +137,8 @@ transform_options(Opt, Opts) ->
[Opt|Opts].
check_sqlite_db(Host) ->
DB = ejabberd_odbc:sqlite_db(Host),
File = ejabberd_odbc:sqlite_file(Host),
DB = ejabberd_sql:sqlite_db(Host),
File = ejabberd_sql:sqlite_file(Host),
Ret = case filelib:ensure_dir(File) of
ok ->
case sqlite3:open(DB, [{file, File}]) of
@@ -211,11 +211,11 @@ read_lines(Fd, File, Acc) ->
[]
end.
opt_type(odbc_pool_size) ->
opt_type(sql_pool_size) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(odbc_start_interval) ->
opt_type(sql_start_interval) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(odbc_type) ->
opt_type(sql_type) ->
fun (mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
@@ -223,4 +223,4 @@ opt_type(odbc_type) ->
(odbc) -> odbc
end;
opt_type(_) ->
[odbc_pool_size, odbc_start_interval, odbc_type].
[sql_pool_size, sql_start_interval, sql_type].
+8 -2
View File
@@ -186,18 +186,24 @@ process_large_heap(Pid, Info) ->
"much memory:~n~p~n~s",
[node(), Pid, Info, DetailedInfo])),
From = jid:make(<<"">>, Host, <<"watchdog">>),
Hint = [#xmlel{name = <<"no-permanent-store">>,
attrs = [{<<"xmlns">>, ?NS_HINTS}]}],
lists:foreach(fun (JID) ->
send_message(From, jid:make(JID), Body)
send_message(From, jid:make(JID), Body, Hint)
end, JIDs).
send_message(From, To, Body) ->
send_message(From, To, Body, []).
send_message(From, To, Body, ExtraEls) ->
ejabberd_router:route(From, To,
#xmlel{name = <<"message">>,
attrs = [{<<"type">>, <<"chat">>}],
children =
[#xmlel{name = <<"body">>, attrs = [],
children =
[{xmlcdata, Body}]}]}).
[{xmlcdata, Body}]}
| ExtraEls]}).
get_admin_jids() ->
ejabberd_config:get_option(
+3 -4
View File
@@ -264,7 +264,7 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) ->
get_auth_account(HostOfRule, AccessRule, User, Server,
Pass) ->
case ejabberd_auth:check_password(User, Server, Pass) of
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true ->
case is_acl_match(HostOfRule, AccessRule,
jid:make(User, Server, <<"">>))
@@ -1520,8 +1520,7 @@ get_offlinemsg_length(ModOffline, User, Server) ->
case ModOffline of
none -> <<"disabled">>;
_ ->
pretty_string_int(ModOffline:get_queue_length(User,
Server))
pretty_string_int(ModOffline:count_offline_messages(User,Server))
end.
get_offlinemsg_module(Server) ->
@@ -2415,7 +2414,7 @@ node_backup_parse_query(Node, Query) ->
lists:keysearch(<<Action/binary,
"host">>,
1, Query),
ejabberd_cluster:call(Node, ejd2odbc,
ejabberd_cluster:call(Node, ejd2sql,
export, [Host, Path]);
<<"import_file">> ->
ejabberd_cluster:call(Node, ejabberd_admin,
+1 -1
View File
@@ -491,7 +491,7 @@ format_result(Atom, {Name, atom}) ->
[{Name, iolist_to_binary(atom_to_list(Atom))}]};
format_result(Int, {Name, integer}) ->
{struct, [{Name, Int}]};
format_result(String, {Name, string}) when is_list(String) ->
format_result([A|_]=String, {Name, string}) when is_list(String) and is_integer(A) ->
{struct, [{Name, lists:flatten(String)}]};
format_result(Binary, {Name, string}) when is_binary(Binary) ->
{struct, [{Name, binary_to_list(Binary)}]};
+44 -11
View File
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
%%% File : ejd2odbc.erl
%%% File : ejd2sql.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Export some mnesia tables to SQL DB
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
@@ -23,14 +23,14 @@
%%%
%%%----------------------------------------------------------------------
-module(ejd2odbc).
-module(ejd2sql).
-author('alexey@process-one.net').
-include("logger.hrl").
-export([export/2, export/3, import_file/2, import/2,
import/3]).
import/3, delete/1]).
-define(MAX_RECORDS_PER_TRANSACTION, 100).
@@ -43,7 +43,7 @@
%%% A table can be converted from Mnesia to an ODBC database by calling
%%% one of the API function with the following parameters:
%%% - Server is the server domain you want to convert
%%% - Output can be either odbc to export to the configured relational
%%% - Output can be either sql to export to the configured relational
%%% database or "Filename" to export to text file.
modules() ->
@@ -80,6 +80,20 @@ export(Server, Output, Module) ->
end, Module:export(Server)),
close_output(Output, IO).
delete(Server) ->
Modules = modules(),
lists:foreach(
fun(Module) ->
delete(Server, Module)
end, Modules).
delete(Server, Module) ->
LServer = jid:nameprep(iolist_to_binary(Server)),
lists:foreach(
fun({Table, ConvertFun}) ->
delete(LServer, Table, ConvertFun)
end, Module:export(Server)).
import_file(Server, FileName) when is_binary(FileName) ->
import(Server, binary_to_list(FileName));
import_file(Server, FileName) ->
@@ -90,7 +104,7 @@ import_file(Server, FileName) ->
LServer = jid:nameprep(Server),
Mods = [{Mod, gen_mod:db_type(LServer, Mod)}
|| Mod <- modules(), gen_mod:is_loaded(LServer, Mod)],
AuthMods = case lists:member(ejabberd_auth_internal,
AuthMods = case lists:member(ejabberd_auth_mnesia,
ejabberd_auth:auth_modules(LServer)) of
true ->
[{ejabberd_auth, mnesia}];
@@ -154,17 +168,36 @@ export(LServer, Table, IO, ConvertFun) ->
output(_LServer, _Table, _IO, []) ->
ok;
output(LServer, _Table, odbc, SQLs) ->
ejabberd_odbc:sql_transaction(LServer, SQLs);
output(LServer, _Table, sql, SQLs) ->
ejabberd_sql:sql_transaction(LServer, SQLs);
output(_LServer, Table, Fd, SQLs) ->
file:write(Fd, ["-- \n-- Mnesia table: ", atom_to_list(Table),
"\n--\n", SQLs]).
delete(LServer, Table, ConvertFun) ->
F = fun () ->
mnesia:write_lock_table(Table),
{_N, SQLs} =
mnesia:foldl(
fun(R, {N, SQLs} = Acc) ->
case ConvertFun(LServer, R) of
[] ->
Acc;
_SQL ->
mnesia:delete_object(R),
Acc
end
end,
{0, []}, Table),
delete(LServer, Table, SQLs)
end,
mnesia:transaction(F).
import(LServer, SelectQuery, IO, ConvertFun, Opts) ->
F = case proplists:get_bool(fast, Opts) of
true ->
fun() ->
case ejabberd_odbc:sql_query_t(SelectQuery) of
case ejabberd_sql:sql_query_t(SelectQuery) of
{selected, _, Rows} ->
lists:foldl(fun process_sql_row/2,
{IO, ConvertFun, undefined}, Rows);
@@ -174,16 +207,16 @@ import(LServer, SelectQuery, IO, ConvertFun, Opts) ->
end;
false ->
fun() ->
ejabberd_odbc:sql_query_t(
ejabberd_sql:sql_query_t(
[iolist_to_binary(
[<<"declare c cursor for ">>, SelectQuery])]),
fetch(IO, ConvertFun, undefined)
end
end,
ejabberd_odbc:sql_transaction(LServer, F).
ejabberd_sql:sql_transaction(LServer, F).
fetch(IO, ConvertFun, PrevRow) ->
case ejabberd_odbc:sql_query_t([<<"fetch c;">>]) of
case ejabberd_sql:sql_query_t([<<"fetch c;">>]) of
{selected, _, [Row]} ->
process_sql_row(Row, {IO, ConvertFun, PrevRow}),
fetch(IO, ConvertFun, Row);
+122
View File
@@ -0,0 +1,122 @@
%%%-------------------------------------------------------------------
%%% @author Mickael Remond <mremond@process-one.net>
%%% @doc
%%% This module bridges lager logs to Elixir Logger.
%%% @end
%%% Created : 9 March 2016 by Mickael Remond <mremond@process-one.net>
%%%
%%% ejabberd, Copyright (C) 2002-2016 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(elixir_logger_backend).
-behaviour(gen_event).
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {level = debug}).
init(Opts) ->
Level = proplists:get_value(level, Opts, debug),
State = #state{level = Level},
{ok, State}.
%% @private
handle_event({log, LagerMsg}, State) ->
#{mode := Mode, truncate := Truncate, level := MinLevel, utc_log := UTCLog} = 'Elixir.Logger.Config':'__data__'(),
MsgLevel = severity_to_level(lager_msg:severity(LagerMsg)),
case {lager_util:is_loggable(LagerMsg, lager_util:level_to_num(State#state.level), ?MODULE),
'Elixir.Logger':compare_levels(MsgLevel, MinLevel)} of
{_, lt}->
{ok, State};
{true, _} ->
Metadata = normalize_pid(lager_msg:metadata(LagerMsg)),
Message = 'Elixir.Logger.Utils':truncate(lager_msg:message(LagerMsg), Truncate),
Timestamp = timestamp(lager_msg:timestamp(LagerMsg), UTCLog),
GroupLeader = case proplists:get_value(pid, Metadata, self()) of
Pid when is_pid(Pid) ->
erlang:process_info(self(), group_leader);
_ -> {group_leader, self()}
end,
notify(Mode, {MsgLevel, GroupLeader, {'Elixir.Logger', Message, Timestamp, Metadata}}),
{ok, State};
_ ->
{ok, State}
end;
handle_event(_Msg, State) ->
{ok, State}.
%% @private
%% TODO Handle loglevels
handle_call(get_loglevel, State) ->
{ok, lager_util:config_to_mask(State#state.level), State};
handle_call({set_loglevel, Config}, State) ->
{ok, ok, State#state{level = Config}}.
%% @private
handle_info(_Msg, State) ->
{ok, State}.
%% @private
terminate(_Reason, _State) ->
ok.
%% @private
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
notify(sync, Msg) ->
gen_event:sync_notify('Elixir.Logger', Msg);
notify(async, Msg) ->
gen_event:notify('Elixir.Logger', Msg).
normalize_pid(Metadata) ->
case proplists:get_value(pid, Metadata) of
Pid when is_pid(Pid) -> Metadata;
Pid when is_list(Pid) ->
M1 = proplists:delete(pid, Metadata),
case catch erlang:list_to_pid(Pid) of
{'EXIT', _} ->
M1;
PidAsPid ->
[{pid, PidAsPid}|M1]
end;
_ ->
proplists:delete(pid, Metadata)
end.
%% Return timestamp with milliseconds
timestamp(Time, UTCLog) ->
{_, _, Micro} = p1_time_compat:timestamp(),
{Date, {Hours, Minutes, Seconds}} =
case UTCLog of
true -> calendar:now_to_universal_time(Time);
false -> calendar:now_to_local_time(Time)
end,
{Date, {Hours, Minutes, Seconds, Micro div 1000}}.
severity_to_level(debug) -> debug;
severity_to_level(info) -> info;
severity_to_level(notice) -> info;
severity_to_level(warning) -> warn;
severity_to_level(error) -> error;
severity_to_level(critical) -> error;
severity_to_level(alert) -> error;
severity_to_level(emergency) -> error.
+2 -3
View File
@@ -509,12 +509,11 @@ compile(_Module, _Spec, DestDir) ->
filelib:ensure_dir(filename:join(Ebin, ".")),
EjabBin = filename:dirname(code:which(ejabberd)),
EjabInc = filename:join(filename:dirname(EjabBin), "include"),
XmlHrl = filename:join(EjabInc, "xml.hrl"),
Logger = [{d, 'P1LOGGER'} || code:is_loaded(lager)==false],
XmlHrl = filename:join(EjabInc, "fxml.hrl"),
ExtLib = [{d, 'NO_EXT_LIB'} || filelib:is_file(XmlHrl)],
Options = [{outdir, Ebin}, {i, "include"}, {i, EjabInc},
verbose, report_errors, report_warnings]
++ Logger ++ ExtLib,
++ ExtLib,
[file:copy(App, Ebin) || App <- filelib:wildcard("src/*.app")],
Result = [case compile:file(File, Options) of
{ok, _} -> ok;
+82 -32
View File
@@ -31,11 +31,12 @@
-export([start/0, start_module/2, start_module/3,
stop_module/2, stop_module_keep_config/2, get_opt/3,
get_opt/4, get_opt_host/3, db_type/1, db_type/2,
get_opt/4, get_opt_host/3, db_type/2, db_type/3,
get_module_opt/4, get_module_opt/5, get_module_opt_host/3,
loaded_modules/1, loaded_modules_with_opts/1,
get_hosts/2, get_module_proc/2, is_loaded/2,
start_modules/1, default_db/1, v_db/1, opt_type/1]).
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
opt_type/1, db_mod/2, db_mod/3]).
%%-export([behaviour_info/1]).
@@ -47,7 +48,7 @@
opts = [] :: opts() | '_' | '$2'}).
-type opts() :: [{atom(), any()}].
-type db_type() :: odbc | mnesia | riak.
-type db_type() :: sql | mnesia | riak.
-callback start(binary(), opts()) -> any().
-callback stop(binary()) -> any().
@@ -64,23 +65,38 @@ start() ->
{keypos, #ejabberd_module.module_host}]),
ok.
-spec start_modules() -> any().
%% Start all the modules in all the hosts
start_modules() ->
lists:foreach(
fun(Host) ->
start_modules(Host)
end, ?MYHOSTS).
get_modules_options(Host) ->
ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
fun({M, A}) when is_atom(M), is_list(A) ->
{M, A}
end, Mods)
end, []).
-spec start_modules(binary()) -> any().
start_modules(Host) ->
Modules = ejabberd_config:get_option(
{modules, Host},
fun(L) when is_list(L) -> L end, []),
Modules = get_modules_options(Host),
lists:foreach(
fun({Module, Opts}) ->
start_module(Host, Module, Opts)
end, Modules).
fun({Module, Opts}) ->
start_module(Host, Module, Opts)
end, Modules).
-spec start_module(binary(), atom()) -> any().
start_module(Host, Module) ->
Modules = ejabberd_config:get_option(
{modules, Host},
fun(L) when is_list(L) -> L end, []),
Modules = get_modules_options(Host),
case lists:keyfind(Module, 1, Modules) of
{_, Opts} ->
start_module(Host, Module, Opts);
@@ -121,6 +137,23 @@ is_app_running(AppName) ->
lists:keymember(AppName, 1,
application:which_applications(Timeout)).
-spec stop_modules() -> any().
stop_modules() ->
lists:foreach(
fun(Host) ->
stop_modules(Host)
end, ?MYHOSTS).
-spec stop_modules(binary()) -> any().
stop_modules(Host) ->
Modules = get_modules_options(Host),
lists:foreach(
fun({Module, _Args}) ->
gen_mod:stop_module_keep_config(Host, Module)
end, Modules).
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
stop_module(Host, Module) ->
@@ -262,29 +295,46 @@ validate_opts(Module, Opts) ->
false
end, Opts).
-spec v_db(db_type() | internal) -> db_type().
v_db(odbc) -> odbc;
v_db(internal) -> mnesia;
v_db(mnesia) -> mnesia;
v_db(riak) -> riak.
-spec db_type(opts()) -> db_type().
db_type(Opts) ->
db_type(global, Opts).
-spec db_type(binary() | global, atom() | opts()) -> db_type().
-spec db_type(binary() | global, module()) -> db_type();
(opts(), module()) -> db_type().
db_type(Opts, Module) when is_list(Opts) ->
db_type(global, Opts, Module);
db_type(Host, Module) when is_atom(Module) ->
get_module_opt(Host, Module, db_type, fun v_db/1, default_db(Host));
db_type(Host, Opts) when is_list(Opts) ->
get_opt(db_type, Opts, fun v_db/1, default_db(Host)).
case Module:mod_opt_type(db_type) of
F when is_function(F) ->
case get_module_opt(Host, Module, db_type, F) of
undefined -> ejabberd_config:default_db(Host, Module);
Type -> Type
end;
_ ->
undefined
end.
-spec default_db(binary() | global) -> db_type().
-spec db_type(binary(), opts(), module()) -> db_type().
default_db(Host) ->
ejabberd_config:get_option({default_db, Host}, fun v_db/1, mnesia).
db_type(Host, Opts, Module) ->
case Module:mod_opt_type(db_type) of
F when is_function(F) ->
case get_opt(db_type, Opts, F) of
undefined -> ejabberd_config:default_db(Host, Module);
Type -> Type
end;
_ ->
undefined
end.
-spec db_mod(binary() | global | db_type(), module()) -> module().
db_mod(Type, Module) when is_atom(Type) ->
list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type));
db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
db_mod(db_type(Host, Module), Module).
-spec db_mod(binary() | global, opts(), module()) -> module().
db_mod(Host, Opts, Module) when is_list(Opts) ->
db_mod(db_type(Host, Opts, Module), Module).
-spec loaded_modules(binary()) -> [atom()].
@@ -332,6 +382,6 @@ get_module_proc(Host, Base) ->
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).
opt_type(default_db) -> fun v_db/1;
opt_type(default_db) -> fun(T) when is_atom(T) -> T end;
opt_type(modules) -> fun (L) when is_list(L) -> L end;
opt_type(_) -> [default_db, modules].
+7 -3
View File
@@ -87,9 +87,13 @@ split(#jid{user = U, server = S, resource = R}) ->
split(_) ->
error.
-spec from_string(binary()) -> jid() | error.
from_string(S) ->
-spec from_string([binary()|string()]) -> jid() | error.
from_string(S) when is_list(S) ->
%% We do not accept list because we want to enforce good practice of
%% using binaries for string. However, we do not let it crash to avoid
%% losing associated ets table.
{error, need_jid_as_binary};
from_string(S) when is_binary(S) ->
SplitPattern = ets:lookup_element(jlib, string_to_jid_pattern, 2),
Size = size(S),
End = Size-1,
+5 -16
View File
@@ -530,22 +530,11 @@ rsm_encode_count(Count, Arr) ->
-spec is_standalone_chat_state(xmlel()) -> boolean().
is_standalone_chat_state(#xmlel{name = <<"message">>} = El) ->
ChatStates = [<<"active">>, <<"inactive">>, <<"gone">>, <<"composing">>,
<<"paused">>],
Stripped =
lists:foldl(fun(ChatState, AccEl) ->
fxml:remove_subtags(AccEl, ChatState,
{<<"xmlns">>, ?NS_CHATSTATES})
end, El, ChatStates),
case Stripped of
#xmlel{children = [#xmlel{name = <<"thread">>}]} ->
true;
#xmlel{children = []} ->
true;
_ ->
false
end;
is_standalone_chat_state(#xmlel{name = <<"message">>, children = Els}) ->
Stripped = [El || #xmlel{name = Name, attrs = Attrs} = El <- Els,
fxml:get_attr_s(<<"xmlns">>, Attrs) /= ?NS_CHATSTATES,
Name /= <<"thread">>],
Stripped == [];
is_standalone_chat_state(_El) -> false.
-spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp())
+6 -3
View File
@@ -233,7 +233,7 @@ process_sm_iq(From, To, IQ) ->
process_adhoc_request(From, To, IQ, adhoc_sm_commands).
process_adhoc_request(From, To,
#iq{sub_el = SubEl} = IQ, Hook) ->
#iq{sub_el = SubEl, lang = Lang} = IQ, Hook) ->
?DEBUG("About to parse ~p...", [IQ]),
case adhoc:parse_request(IQ) of
{error, Error} ->
@@ -245,8 +245,9 @@ process_adhoc_request(From, To,
of
ignore -> ignore;
empty ->
Txt = <<"No hook has processed this command">>,
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
sub_el = [SubEl, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)]};
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]};
Command -> IQ#iq{type = result, sub_el = [Command]}
@@ -277,7 +278,9 @@ ping_command(_Acc, _From, _To,
[{<<"info">>,
translate:translate(Lang,
<<"Pong">>)}]});
true -> {error, ?ERR_BAD_REQUEST}
true ->
Txt = <<"Incorrect value of 'action' attribute">>,
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
end;
ping_command(Acc, _From, _To, _Request) -> Acc.
+42 -39
View File
@@ -31,7 +31,7 @@
-include("logger.hrl").
-export([start/2, stop/1, compile/1, get_cookie/0,
remove_node/1, set_password/3,
remove_node/1, set_password/3, check_password/3,
check_password_hash/4, delete_old_users/1,
delete_old_users_vhost/2, ban_account/3,
num_active_users/2, num_resources/2, resource_num/3,
@@ -162,7 +162,7 @@ get_commands_spec() ->
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password, tags = [accounts],
desc = "Check if a password is correct",
module = ejabberd_auth, function = check_password,
module = ?MODULE, function = check_password,
args = [{user, binary}, {host, binary}, {password, binary}],
args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>],
args_desc = ["User name to check", "Server to check", "Password to check"],
@@ -531,7 +531,7 @@ get_commands_spec() ->
tags = [offline],
desc = "Get the number of unread offline messages",
policy = user,
module = mod_offline, function = get_queue_length,
module = mod_offline, function = count_offline_messages,
args = [],
result = {res, integer}},
#ejabberd_commands{name = send_message, tags = [stanza],
@@ -590,36 +590,38 @@ remove_node(Node) ->
%%%
set_password(User, Host, Password) ->
case ejabberd_auth:set_password(User, Host, Password) of
ok ->
ok;
_ ->
error
end.
Fun = fun () -> ejabberd_auth:set_password(User, Host, Password) end,
user_action(User, Host, Fun, ok).
check_password(User, Host, Password) ->
ejabberd_auth: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
{A, _} when is_tuple(A) -> scrammed;
{_, "md5"} -> get_md5(AccountPass);
{_, "sha"} -> get_sha(AccountPass);
_ -> undefined
{_, <<"md5">>} -> get_md5(AccountPass);
{_, <<"sha">>} -> get_sha(AccountPass);
{_, Method} ->
?ERROR_MSG("check_password_hash called "
"with hash method: ~p", [Method]),
undefined
end,
case AccountPassHash of
scrammed ->
?ERROR_MSG("Passwords are scrammed, and check_password_hash can not work.", []),
?ERROR_MSG("Passwords are scrammed, and check_password_hash cannot work.", []),
throw(passwords_scrammed_command_cannot_work);
undefined -> error;
undefined -> throw(unkown_hash_method);
PasswordHash -> ok;
_ -> error
_ -> false
end.
get_md5(AccountPass) ->
lists:flatten([io_lib:format("~.16B", [X])
|| X <- binary_to_list(erlang:md5(AccountPass))]).
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|| X <- binary_to_list(erlang:md5(AccountPass))]).
get_sha(AccountPass) ->
lists:flatten([io_lib:format("~.16B", [X])
|| X <- binary_to_list(p1_sha:sha1(AccountPass))]).
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|| X <- binary_to_list(p1_sha:sha1(AccountPass))]).
num_active_users(Host, Days) ->
list_last_activity(Host, true, Days).
@@ -748,21 +750,7 @@ kick_sessions(User, Server, Reason) ->
fun(Resource) ->
kick_this_session(User, Server, Resource, Reason)
end,
get_resources(User, Server)).
get_resources(User, Server) ->
lists:map(
fun(Session) ->
element(3, Session#session.usr)
end,
get_sessions(User, Server)).
get_sessions(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
true = is_list(Sessions),
Sessions.
ejabberd_sm:get_user_resources(User, Server)).
set_random_password(User, Server, Reason) ->
NewPass = build_random_password(Reason),
@@ -796,7 +784,8 @@ resource_num(User, Host, Num) ->
true ->
lists:nth(Num, Resources);
false ->
lists:flatten(io_lib:format("Error: Wrong resource number: ~p", [Num]))
throw({bad_argument,
lists:flatten(io_lib:format("Wrong resource number: ~p", [Num]))})
end.
kick_session(User, Server, Resource, ReasonText) ->
@@ -861,7 +850,8 @@ connected_users_info() ->
PI when is_integer(PI) -> PI;
_ -> nil
end,
{[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, PriorityI, NodeS, Uptime}
{binary_to_list(<<U/binary, $@, S/binary, $/, R/binary>>),
atom_to_list(Conn), IPS, Port, PriorityI, NodeS, Uptime}
end,
USRIs).
@@ -1193,7 +1183,7 @@ push_roster_item(LU, LS, R, U, S, Action) ->
ejabberd_sm:route(LJID, LJID, BroadcastEl),
Item = build_roster_item(U, S, Action),
ResIQ = build_iq_roster_push(Item),
ejabberd_router:route(LJID, LJID, ResIQ).
ejabberd_router:route(jid:remove_resource(LJID), LJID, ResIQ).
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
{xmlel, <<"item">>,
@@ -1322,8 +1312,7 @@ srg_get_info(Group, Host) ->
Os when is_list(Os) -> Os;
error -> []
end,
[{jlib:atom_to_binary(Title),
io_lib:format("~p", [btl(Value)])} || {Title, Value} <- Opts].
[{jlib:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
btl([]) -> [];
btl([B|L]) -> [btl(B)|btl(L)];
@@ -1582,6 +1571,20 @@ decide_rip_jid({UName, UServer}, Match_list) ->
end,
Match_list).
user_action(User, Server, Fun, OK) ->
case ejabberd_auth:is_user_exists(User, Server) of
true ->
case catch Fun() of
OK -> ok;
{error, Error} -> throw(Error);
Error ->
?ERROR_MSG("Command returned: ~p", [Error]),
1
end;
false ->
throw({not_found, "unknown_user"})
end.
%% Copied from ejabberd-2.0.0/src/acl.erl
is_regexp_match(String, RegExp) ->
case ejabberd_regexp:run(String, RegExp) of
+103 -328
View File
@@ -41,11 +41,16 @@
-include("logger.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
-include("mod_announce.hrl").
-record(motd, {server = <<"">> :: binary(),
packet = #xmlel{} :: xmlel()}).
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
dummy = [] :: [] | '_'}).
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #motd{} | #motd_users{}) -> ok | pass.
-callback set_motd_users(binary(), [{binary(), binary(), binary()}]) -> {atomic, any()}.
-callback set_motd(binary(), xmlel()) -> {atomic, any()}.
-callback delete_motd(binary()) -> {atomic, any()}.
-callback get_motd(binary()) -> {ok, xmlel()} | error.
-callback is_motd_user(binary(), binary()) -> boolean().
-callback set_motd_user(binary(), binary()) -> {atomic, any()}.
-define(PROCNAME, ejabberd_announce).
@@ -55,20 +60,8 @@
tokenize(Node) -> str:tokens(Node, <<"/#">>).
start(Host, Opts) ->
case gen_mod:db_type(Host, Opts) of
mnesia ->
mnesia:create_table(motd,
[{disc_copies, [node()]},
{attributes,
record_info(fields, motd)}]),
mnesia:create_table(motd_users,
[{disc_copies, [node()]},
{attributes,
record_info(fields, motd_users)}]),
update_tables();
_ ->
ok
end,
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
@@ -211,15 +204,15 @@ disco_identity(Acc, _From, _To, Node, Lang) ->
%%-------------------------------------------------------------------------
-define(INFO_RESULT(Allow, Feats),
-define(INFO_RESULT(Allow, Feats, Lang),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
{error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
allow ->
{result, Feats}
end).
disco_features(Acc, From, #jid{lserver = LServer} = _To, <<"announce">>, _Lang) ->
disco_features(Acc, From, #jid{lserver = LServer} = _To, <<"announce">>, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
@@ -229,13 +222,14 @@ disco_features(Acc, From, #jid{lserver = LServer} = _To, <<"announce">>, _Lang)
case {acl:match_rule(LServer, Access1, From),
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
{error, ?ERR_FORBIDDEN};
Txt = <<"Denied by ACL">>,
{error, ?ERRT_FORBIDDEN(Lang, Txt)};
_ ->
{result, []}
end
end;
disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
@@ -246,25 +240,25 @@ disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
AllowGlobal = acl:match_rule(global, AccessGlobal, From),
case Node of
?NS_ADMIN_ANNOUNCE ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
?NS_ADMIN_ANNOUNCE_ALL ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
?NS_ADMIN_SET_MOTD ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
?NS_ADMIN_EDIT_MOTD ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
?NS_ADMIN_DELETE_MOTD ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
?NS_ADMIN_ANNOUNCE_ALLHOSTS ->
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang);
?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS ->
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang);
?NS_ADMIN_SET_MOTD_ALLHOSTS ->
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang);
?NS_ADMIN_EDIT_MOTD_ALLHOSTS ->
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang);
?NS_ADMIN_DELETE_MOTD_ALLHOSTS ->
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
?INFO_RESULT(AllowGlobal, [?NS_COMMANDS], Lang);
_ ->
Acc
end
@@ -283,10 +277,10 @@ disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
}
)).
-define(ITEMS_RESULT(Allow, Items),
-define(ITEMS_RESULT(Allow, Items, Lang),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
{error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
allow ->
{result, Items}
end).
@@ -320,7 +314,7 @@ disco_items(Acc, From, #jid{lserver = LServer} = To, <<"announce">>, Lang) ->
announce_items(Acc, From, To, Lang)
end;
disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
@@ -331,25 +325,25 @@ disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
AllowGlobal = acl:match_rule(global, AccessGlobal, From),
case Node of
?NS_ADMIN_ANNOUNCE ->
?ITEMS_RESULT(Allow, []);
?ITEMS_RESULT(Allow, [], Lang);
?NS_ADMIN_ANNOUNCE_ALL ->
?ITEMS_RESULT(Allow, []);
?ITEMS_RESULT(Allow, [], Lang);
?NS_ADMIN_SET_MOTD ->
?ITEMS_RESULT(Allow, []);
?ITEMS_RESULT(Allow, [], Lang);
?NS_ADMIN_EDIT_MOTD ->
?ITEMS_RESULT(Allow, []);
?ITEMS_RESULT(Allow, [], Lang);
?NS_ADMIN_DELETE_MOTD ->
?ITEMS_RESULT(Allow, []);
?ITEMS_RESULT(Allow, [], Lang);
?NS_ADMIN_ANNOUNCE_ALLHOSTS ->
?ITEMS_RESULT(AllowGlobal, []);
?ITEMS_RESULT(AllowGlobal, [], Lang);
?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS ->
?ITEMS_RESULT(AllowGlobal, []);
?ITEMS_RESULT(AllowGlobal, [], Lang);
?NS_ADMIN_SET_MOTD_ALLHOSTS ->
?ITEMS_RESULT(AllowGlobal, []);
?ITEMS_RESULT(AllowGlobal, [], Lang);
?NS_ADMIN_EDIT_MOTD_ALLHOSTS ->
?ITEMS_RESULT(AllowGlobal, []);
?ITEMS_RESULT(AllowGlobal, [], Lang);
?NS_ADMIN_DELETE_MOTD_ALLHOSTS ->
?ITEMS_RESULT(AllowGlobal, []);
?ITEMS_RESULT(AllowGlobal, [], Lang);
_ ->
Acc
end
@@ -396,7 +390,8 @@ announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang)
commands_result(Allow, From, To, Request) ->
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
Lang = Request#adhoc_request.lang,
{error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
allow ->
announce_commands(From, To, Request)
end.
@@ -463,12 +458,13 @@ announce_commands(From, To,
%% User returns form.
case jlib:parse_xdata_submit(XData) of
invalid ->
{error, ?ERR_BAD_REQUEST};
{error, ?ERRT_BAD_REQUEST(Lang, <<"Incorrect data form">>)};
Fields ->
handle_adhoc_form(From, To, Request, Fields)
end;
true ->
{error, ?ERR_BAD_REQUEST}
Txt = <<"Incorrect action or data form">>,
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
end.
-define(VVALUE(Val),
@@ -688,7 +684,9 @@ announce_all(From, To, Packet) ->
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
Local = jid:make(<<>>, To#jid.server, <<>>),
@@ -703,7 +701,9 @@ announce_all_hosts_all(From, To, Packet) ->
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
Local = jid:make(<<>>, To#jid.server, <<>>),
@@ -719,7 +719,9 @@ announce_online(From, To, Packet) ->
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:get_vh_session_list(Host),
@@ -731,7 +733,9 @@ announce_all_hosts_online(From, To, Packet) ->
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
@@ -752,7 +756,9 @@ announce_motd(From, To, Packet) ->
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd(Host, Packet)
@@ -762,7 +768,9 @@ announce_all_hosts_motd(From, To, Packet) ->
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
@@ -774,48 +782,17 @@ announce_motd(Host, Packet) ->
announce_motd_update(LServer, Packet),
Sessions = ejabberd_sm:get_vh_session_list(LServer),
announce_online1(Sessions, LServer, Packet),
case gen_mod:db_type(LServer, ?MODULE) of
mnesia ->
F = fun() ->
lists:foreach(
fun({U, S, _R}) ->
mnesia:write(#motd_users{us = {U, S}})
end, Sessions)
end,
mnesia:transaction(F);
riak ->
try
lists:foreach(
fun({U, S, _R}) ->
ok = ejabberd_riak:put(#motd_users{us = {U, S}},
motd_users_schema(),
[{'2i', [{<<"server">>, S}]}])
end, Sessions),
{atomic, ok}
catch _:{badmatch, Err} ->
{atomic, Err}
end;
odbc ->
F = fun() ->
lists:foreach(
fun({U, _S, _R}) ->
Username = ejabberd_odbc:escape(U),
odbc_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[Username, <<"">>],
[<<"username='">>, Username, <<"'">>])
end, Sessions)
end,
ejabberd_odbc:sql_transaction(LServer, F)
end.
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:set_motd_users(LServer, Sessions).
announce_motd_update(From, To, Packet) ->
Host = To#jid.lserver,
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(Host, Packet)
@@ -825,7 +802,9 @@ announce_all_hosts_motd_update(From, To, Packet) ->
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
@@ -834,34 +813,17 @@ announce_all_hosts_motd_update(From, To, Packet) ->
announce_motd_update(LServer, Packet) ->
announce_motd_delete(LServer),
case gen_mod:db_type(LServer, ?MODULE) of
mnesia ->
F = fun() ->
mnesia:write(#motd{server = LServer, packet = Packet})
end,
mnesia:transaction(F);
riak ->
{atomic, ejabberd_riak:put(#motd{server = LServer,
packet = Packet},
motd_schema())};
odbc ->
XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet)),
F = fun() ->
odbc_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[<<"">>, XML],
[<<"username=''">>])
end,
ejabberd_odbc:sql_transaction(LServer, F)
end.
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:set_motd(LServer, Packet).
announce_motd_delete(From, To, Packet) ->
Host = To#jid.lserver,
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_delete(Host)
@@ -871,7 +833,9 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Txt = <<"Denied by ACL">>,
Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
@@ -879,112 +843,30 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
end.
announce_motd_delete(LServer) ->
case gen_mod:db_type(LServer, ?MODULE) of
mnesia ->
F = fun() ->
mnesia:delete({motd, LServer}),
mnesia:write_lock_table(motd_users),
Users = mnesia:select(
motd_users,
[{#motd_users{us = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]),
lists:foreach(fun(US) ->
mnesia:delete({motd_users, US})
end, Users)
end,
mnesia:transaction(F);
riak ->
try
ok = ejabberd_riak:delete(motd, LServer),
ok = ejabberd_riak:delete_by_index(motd_users,
<<"server">>,
LServer),
{atomic, ok}
catch _:{badmatch, Err} ->
{atomic, Err}
end;
odbc ->
F = fun() ->
ejabberd_odbc:sql_query_t([<<"delete from motd;">>])
end,
ejabberd_odbc:sql_transaction(LServer, F)
end.
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:delete_motd(LServer).
send_motd(JID) ->
send_motd(JID, gen_mod:db_type(JID#jid.lserver, ?MODULE)).
send_motd(#jid{luser = LUser, lserver = LServer} = JID, mnesia) ->
case catch mnesia:dirty_read({motd, LServer}) of
[#motd{packet = Packet}] ->
US = {LUser, LServer},
case catch mnesia:dirty_read({motd_users, US}) of
[#motd_users{}] ->
ok;
_ ->
send_motd(#jid{luser = LUser, lserver = LServer} = JID) when LUser /= <<>> ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:get_motd(LServer) of
{ok, Packet} ->
case Mod:is_motd_user(LUser, LServer) of
false ->
Local = jid:make(<<>>, LServer, <<>>),
ejabberd_router:route(Local, JID, Packet),
F = fun() ->
mnesia:write(#motd_users{us = US})
end,
mnesia:transaction(F)
Mod:set_motd_user(LUser, LServer);
true ->
ok
end;
_ ->
error ->
ok
end;
send_motd(#jid{luser = LUser, lserver = LServer} = JID, riak) ->
case catch ejabberd_riak:get(motd, motd_schema(), LServer) of
{ok, #motd{packet = Packet}} ->
US = {LUser, LServer},
case ejabberd_riak:get(motd_users, motd_users_schema(), US) of
{ok, #motd_users{}} ->
ok;
_ ->
Local = jid:make(<<>>, LServer, <<>>),
ejabberd_router:route(Local, JID, Packet),
{atomic, ejabberd_riak:put(
#motd_users{us = US}, motd_users_schema(),
[{'2i', [{<<"server">>, LServer}]}])}
end;
_ ->
ok
end;
send_motd(#jid{luser = LUser, lserver = LServer} = JID, odbc) when LUser /= <<>> ->
case catch ejabberd_odbc:sql_query(
LServer, [<<"select xml from motd where username='';">>]) of
{selected, [<<"xml">>], [[XML]]} ->
case fxml_stream:parse_element(XML) of
{error, _} ->
ok;
Packet ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(
LServer,
[<<"select username from motd "
"where username='">>, Username, <<"';">>]) of
{selected, [<<"username">>], []} ->
Local = jid:make(<<"">>, LServer, <<"">>),
ejabberd_router:route(Local, JID, Packet),
F = fun() ->
odbc_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[Username, <<"">>],
[<<"username='">>, Username, <<"'">>])
end,
ejabberd_odbc:sql_transaction(LServer, F);
_ ->
ok
end
end;
_ ->
ok
end;
send_motd(_, odbc) ->
send_motd(_) ->
ok.
get_stored_motd(LServer) ->
case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:get_motd(LServer) of
{ok, Packet} ->
{fxml:get_subtag_cdata(Packet, <<"subject">>),
fxml:get_subtag_cdata(Packet, <<"body">>)};
@@ -992,34 +874,6 @@ get_stored_motd(LServer) ->
{<<>>, <<>>}
end.
get_stored_motd_packet(LServer, mnesia) ->
case catch mnesia:dirty_read({motd, LServer}) of
[#motd{packet = Packet}] ->
{ok, Packet};
_ ->
error
end;
get_stored_motd_packet(LServer, riak) ->
case ejabberd_riak:get(motd, motd_schema(), LServer) of
{ok, #motd{packet = Packet}} ->
{ok, Packet};
_ ->
error
end;
get_stored_motd_packet(LServer, odbc) ->
case catch ejabberd_odbc:sql_query(
LServer, [<<"select xml from motd where username='';">>]) of
{selected, [<<"xml">>], [[XML]]} ->
case fxml_stream:parse_element(XML) of
{error, _} ->
error;
Packet ->
{ok, Packet}
end;
_ ->
error
end.
%% This function is similar to others, but doesn't perform any ACL verification
send_announcement_to_all(Host, SubjectS, BodyS) ->
SubjectEls = if SubjectS /= <<>> ->
@@ -1053,98 +907,19 @@ get_access(Host) ->
none).
%%-------------------------------------------------------------------------
update_tables() ->
update_motd_table(),
update_motd_users_table().
update_motd_table() ->
Fields = record_info(fields, motd),
case mnesia:table_info(motd, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
motd, Fields, set,
fun(#motd{server = S}) -> S end,
fun(#motd{server = S, packet = P} = R) ->
NewS = iolist_to_binary(S),
NewP = fxml:to_xmlel(P),
R#motd{server = NewS, packet = NewP}
end);
_ ->
?INFO_MSG("Recreating motd table", []),
mnesia:transform_table(motd, ignore, Fields)
end.
update_motd_users_table() ->
Fields = record_info(fields, motd_users),
case mnesia:table_info(motd_users, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
motd_users, Fields, set,
fun(#motd_users{us = {U, _}}) -> U end,
fun(#motd_users{us = {U, S}} = R) ->
NewUS = {iolist_to_binary(U),
iolist_to_binary(S)},
R#motd_users{us = NewUS}
end);
_ ->
?INFO_MSG("Recreating motd_users table", []),
mnesia:transform_table(motd_users, ignore, Fields)
end.
motd_schema() ->
{record_info(fields, motd), #motd{}}.
motd_users_schema() ->
{record_info(fields, motd_users), #motd_users{}}.
export(_Server) ->
[{motd,
fun(Host, #motd{server = LServer, packet = El})
when LServer == Host ->
[[<<"delete from motd where username='';">>],
[<<"insert into motd(username, xml) values ('', '">>,
ejabberd_odbc:escape(fxml:element_to_binary(El)),
<<"');">>]];
(_Host, _R) ->
[]
end},
{motd_users,
fun(Host, #motd_users{us = {LUser, LServer}})
when LServer == Host, LUser /= <<"">> ->
Username = ejabberd_odbc:escape(LUser),
[[<<"delete from motd where username='">>, Username, <<"';">>],
[<<"insert into motd(username, xml) values ('">>,
Username, <<"', '');">>]];
(_Host, _R) ->
[]
end}].
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import(LServer) ->
[{<<"select xml from motd where username='';">>,
fun([XML]) ->
El = fxml_stream:parse_element(XML),
#motd{server = LServer, packet = El}
end},
{<<"select username from motd where xml='';">>,
fun([LUser]) ->
#motd_users{us = {LUser, LServer}}
end}].
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:import(LServer).
import(_LServer, mnesia, #motd{} = Motd) ->
mnesia:dirty_write(Motd);
import(_LServer, mnesia, #motd_users{} = Users) ->
mnesia:dirty_write(Users);
import(_LServer, riak, #motd{} = Motd) ->
ejabberd_riak:put(Motd, motd_schema());
import(_LServer, riak, #motd_users{us = {_, S}} = Users) ->
ejabberd_riak:put(Users, motd_users_schema(),
[{'2i', [{<<"server">>, S}]}]);
import(_, _, _) ->
pass.
import(LServer, DBType, LA) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, LA).
mod_opt_type(access) ->
fun (A) when is_atom(A) -> A end;
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [access, db_type].
+129
View File
@@ -0,0 +1,129 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_announce_mnesia).
-behaviour(mod_announce).
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
-include("jlib.hrl").
-include("mod_announce.hrl").
-include("logger.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
mnesia:create_table(motd,
[{disc_copies, [node()]},
{attributes,
record_info(fields, motd)}]),
mnesia:create_table(motd_users,
[{disc_copies, [node()]},
{attributes,
record_info(fields, motd_users)}]),
update_tables().
set_motd_users(_LServer, USRs) ->
F = fun() ->
lists:foreach(
fun({U, S, _R}) ->
mnesia:write(#motd_users{us = {U, S}})
end, USRs)
end,
mnesia:transaction(F).
set_motd(LServer, Packet) ->
F = fun() ->
mnesia:write(#motd{server = LServer, packet = Packet})
end,
mnesia:transaction(F).
delete_motd(LServer) ->
F = fun() ->
mnesia:delete({motd, LServer}),
mnesia:write_lock_table(motd_users),
Users = mnesia:select(
motd_users,
[{#motd_users{us = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]),
lists:foreach(fun(US) ->
mnesia:delete({motd_users, US})
end, Users)
end,
mnesia:transaction(F).
get_motd(LServer) ->
case mnesia:dirty_read({motd, LServer}) of
[#motd{packet = Packet}] ->
{ok, Packet};
_ ->
error
end.
is_motd_user(LUser, LServer) ->
case mnesia:dirty_read({motd_users, {LUser, LServer}}) of
[#motd_users{}] -> true;
_ -> false
end.
set_motd_user(LUser, LServer) ->
F = fun() ->
mnesia:write(#motd_users{us = {LUser, LServer}})
end,
mnesia:transaction(F).
import(_LServer, #motd{} = Motd) ->
mnesia:dirty_write(Motd);
import(_LServer, #motd_users{} = Users) ->
mnesia:dirty_write(Users).
%%%===================================================================
%%% Internal functions
%%%===================================================================
update_tables() ->
update_motd_table(),
update_motd_users_table().
update_motd_table() ->
Fields = record_info(fields, motd),
case mnesia:table_info(motd, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
motd, Fields, set,
fun(#motd{server = S}) -> S end,
fun(#motd{server = S, packet = P} = R) ->
NewS = iolist_to_binary(S),
NewP = fxml:to_xmlel(P),
R#motd{server = NewS, packet = NewP}
end);
_ ->
?INFO_MSG("Recreating motd table", []),
mnesia:transform_table(motd, ignore, Fields)
end.
update_motd_users_table() ->
Fields = record_info(fields, motd_users),
case mnesia:table_info(motd_users, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
motd_users, Fields, set,
fun(#motd_users{us = {U, _}}) -> U end,
fun(#motd_users{us = {U, S}} = R) ->
NewUS = {iolist_to_binary(U),
iolist_to_binary(S)},
R#motd_users{us = NewUS}
end);
_ ->
?INFO_MSG("Recreating motd_users table", []),
mnesia:transform_table(motd_users, ignore, Fields)
end.
+87
View File
@@ -0,0 +1,87 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_announce_riak).
-behaviour(mod_announce).
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
-include("jlib.hrl").
-include("mod_announce.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
set_motd_users(_LServer, USRs) ->
try
lists:foreach(
fun({U, S, _R}) ->
ok = ejabberd_riak:put(#motd_users{us = {U, S}},
motd_users_schema(),
[{'2i', [{<<"server">>, S}]}])
end, USRs),
{atomic, ok}
catch _:{badmatch, Err} ->
{atomic, Err}
end.
set_motd(LServer, Packet) ->
{atomic, ejabberd_riak:put(#motd{server = LServer,
packet = Packet},
motd_schema())}.
delete_motd(LServer) ->
try
ok = ejabberd_riak:delete(motd, LServer),
ok = ejabberd_riak:delete_by_index(motd_users,
<<"server">>,
LServer),
{atomic, ok}
catch _:{badmatch, Err} ->
{atomic, Err}
end.
get_motd(LServer) ->
case ejabberd_riak:get(motd, motd_schema(), LServer) of
{ok, #motd{packet = Packet}} ->
{ok, Packet};
_ ->
error
end.
is_motd_user(LUser, LServer) ->
case ejabberd_riak:get(motd_users, motd_users_schema(),
{LUser, LServer}) of
{ok, #motd_users{}} -> true;
_ -> false
end.
set_motd_user(LUser, LServer) ->
{atomic, ejabberd_riak:put(
#motd_users{us = {LUser, LServer}}, motd_users_schema(),
[{'2i', [{<<"server">>, LServer}]}])}.
import(_LServer, #motd{} = Motd) ->
ejabberd_riak:put(Motd, motd_schema());
import(_LServer, #motd_users{us = {_, S}} = Users) ->
ejabberd_riak:put(Users, motd_users_schema(),
[{'2i', [{<<"server">>, S}]}]).
%%%===================================================================
%%% Internal functions
%%%===================================================================
motd_schema() ->
{record_info(fields, motd), #motd{}}.
motd_users_schema() ->
{record_info(fields, motd_users), #motd_users{}}.
+132
View File
@@ -0,0 +1,132 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_announce_sql).
-behaviour(mod_announce).
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
get_motd/1, is_motd_user/2, set_motd_user/2, import/1,
import/2, export/1]).
-include("jlib.hrl").
-include("mod_announce.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
set_motd_users(LServer, USRs) ->
F = fun() ->
lists:foreach(
fun({U, _S, _R}) ->
Username = ejabberd_sql:escape(U),
sql_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[Username, <<"">>],
[<<"username='">>, Username, <<"'">>])
end, USRs)
end,
ejabberd_sql:sql_transaction(LServer, F).
set_motd(LServer, Packet) ->
XML = ejabberd_sql:escape(fxml:element_to_binary(Packet)),
F = fun() ->
sql_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[<<"">>, XML],
[<<"username=''">>])
end,
ejabberd_sql:sql_transaction(LServer, F).
delete_motd(LServer) ->
F = fun() ->
ejabberd_sql:sql_query_t([<<"delete from motd;">>])
end,
ejabberd_sql:sql_transaction(LServer, F).
get_motd(LServer) ->
case catch ejabberd_sql:sql_query(
LServer, [<<"select xml from motd where username='';">>]) of
{selected, [<<"xml">>], [[XML]]} ->
case fxml_stream:parse_element(XML) of
{error, _} ->
error;
Packet ->
{ok, Packet}
end;
_ ->
error
end.
is_motd_user(LUser, LServer) ->
Username = ejabberd_sql:escape(LUser),
case catch ejabberd_sql:sql_query(
LServer,
[<<"select username from motd "
"where username='">>, Username, <<"';">>]) of
{selected, [<<"username">>], [_|_]} ->
true;
_ ->
false
end.
set_motd_user(LUser, LServer) ->
Username = ejabberd_sql:escape(LUser),
F = fun() ->
sql_queries:update_t(
<<"motd">>,
[<<"username">>, <<"xml">>],
[Username, <<"">>],
[<<"username='">>, Username, <<"'">>])
end,
ejabberd_sql:sql_transaction(LServer, F).
export(_Server) ->
[{motd,
fun(Host, #motd{server = LServer, packet = El})
when LServer == Host ->
[[<<"delete from motd where username='';">>],
[<<"insert into motd(username, xml) values ('', '">>,
ejabberd_sql:escape(fxml:element_to_binary(El)),
<<"');">>]];
(_Host, _R) ->
[]
end},
{motd_users,
fun(Host, #motd_users{us = {LUser, LServer}})
when LServer == Host, LUser /= <<"">> ->
Username = ejabberd_sql:escape(LUser),
[[<<"delete from motd where username='">>, Username, <<"';">>],
[<<"insert into motd(username, xml) values ('">>,
Username, <<"', '');">>]];
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select xml from motd where username='';">>,
fun([XML]) ->
El = fxml_stream:parse_element(XML),
#motd{server = LServer, packet = El}
end},
{<<"select username from motd where xml='';">>,
fun([LUser]) ->
#motd_users{us = {LUser, LServer}}
end}].
import(_, _) ->
pass.
%%%===================================================================
%%% Internal functions
%%%===================================================================
+36 -237
View File
@@ -39,6 +39,10 @@
-include("mod_privacy.hrl").
-callback process_blocklist_block(binary(), binary(), function()) -> {atomic, any()}.
-callback unblock_by_filter(binary(), binary(), function()) -> {atomic, any()}.
-callback process_blocklist_get(binary(), binary()) -> [listitem()] | error.
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
@@ -64,29 +68,33 @@ process_iq(_From, _To, IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
process_iq_get(_, From, _To,
#iq{xmlns = ?NS_BLOCKING,
#iq{xmlns = ?NS_BLOCKING, lang = Lang,
sub_el = #xmlel{name = <<"blocklist">>}},
_) ->
#jid{luser = LUser, lserver = LServer} = From,
{stop, process_blocklist_get(LUser, LServer)};
{stop, process_blocklist_get(LUser, LServer, Lang)};
process_iq_get(Acc, _, _, _, _) -> Acc.
process_iq_set(_, From, _To,
#iq{xmlns = ?NS_BLOCKING,
#iq{xmlns = ?NS_BLOCKING, lang = Lang,
sub_el =
#xmlel{name = SubElName, children = SubEls}}) ->
#jid{luser = LUser, lserver = LServer} = From,
Res = case {SubElName, fxml:remove_cdata(SubEls)} of
{<<"block">>, []} -> {error, ?ERR_BAD_REQUEST};
{<<"block">>, []} ->
Txt = <<"No items found in this query">>,
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
{<<"block">>, Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_block(LUser, LServer, JIDs);
process_blocklist_block(LUser, LServer, JIDs, Lang);
{<<"unblock">>, []} ->
process_blocklist_unblock_all(LUser, LServer);
process_blocklist_unblock_all(LUser, LServer, Lang);
{<<"unblock">>, Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_unblock(LUser, LServer, JIDs);
_ -> {error, ?ERR_BAD_REQUEST}
process_blocklist_unblock(LUser, LServer, JIDs, Lang);
_ ->
Txt = <<"Unknown blocking command">>,
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
end,
{stop, Res};
process_iq_set(Acc, _, _, _) -> Acc.
@@ -125,7 +133,7 @@ parse_blocklist_items([#xmlel{name = <<"item">>,
parse_blocklist_items([_ | Els], JIDs) ->
parse_blocklist_items(Els, JIDs).
process_blocklist_block(LUser, LServer, JIDs) ->
process_blocklist_block(LUser, LServer, JIDs, Lang) ->
Filter = fun (List) ->
AlreadyBlocked = list_to_blocklist_jids(List, []),
lists:foldr(fun (JID, List1) ->
@@ -143,9 +151,8 @@ process_blocklist_block(LUser, LServer, JIDs) ->
end,
List, JIDs)
end,
case process_blocklist_block(LUser, LServer, Filter,
gen_mod:db_type(LServer, mod_privacy))
of
Mod = db_mod(LServer),
case Mod:process_blocklist_block(LUser, LServer, Filter) of
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default,
@@ -155,110 +162,17 @@ process_blocklist_block(LUser, LServer, JIDs) ->
{result, [], UserList};
_Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
{error, ?ERR_INTERNAL_SERVER_ERROR}
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
end.
process_blocklist_block(LUser, LServer, Filter,
mnesia) ->
F = fun () ->
case mnesia:wread({privacy, {LUser, LServer}}) of
[] ->
P = #privacy{us = {LUser, LServer}},
NewDefault = <<"Blocked contacts">>,
NewLists1 = [],
List = [];
[#privacy{default = Default, lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
NewDefault = <<"Blocked contacts">>,
NewLists1 = Lists,
List = []
end
end,
NewList = Filter(List),
NewLists = [{NewDefault, NewList} | NewLists1],
mnesia:write(P#privacy{default = NewDefault,
lists = NewLists}),
{ok, NewDefault, NewList}
end,
mnesia:transaction(F);
process_blocklist_block(LUser, LServer, Filter,
riak) ->
{atomic,
begin
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
{LUser, LServer}) of
{ok, #privacy{default = Default, lists = Lists} = P} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
NewDefault = <<"Blocked contacts">>,
NewLists1 = Lists,
List = []
end;
{error, _} ->
P = #privacy{us = {LUser, LServer}},
NewDefault = <<"Blocked contacts">>,
NewLists1 = [],
List = []
end,
NewList = Filter(List),
NewLists = [{NewDefault, NewList} | NewLists1],
case ejabberd_riak:put(P#privacy{default = NewDefault,
lists = NewLists},
mod_privacy:privacy_schema()) of
ok ->
{ok, NewDefault, NewList};
Err ->
Err
end
end};
process_blocklist_block(LUser, LServer, Filter, odbc) ->
F = fun () ->
Default = case
mod_privacy:sql_get_default_privacy_list_t(LUser)
of
{selected, [<<"name">>], []} ->
Name = <<"Blocked contacts">>,
mod_privacy:sql_add_privacy_list(LUser, Name),
mod_privacy:sql_set_default_privacy_list(LUser,
Name),
Name;
{selected, [<<"name">>], [[Name]]} -> Name
end,
{selected, [<<"id">>], [[ID]]} =
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
_ -> List = []
end,
NewList = Filter(List),
NewRItems = lists:map(fun mod_privacy:item_to_raw/1,
NewList),
mod_privacy:sql_set_privacy_list(ID, NewRItems),
{ok, Default, NewList}
end,
ejabberd_odbc:sql_transaction(LServer, F).
process_blocklist_unblock_all(LUser, LServer) ->
process_blocklist_unblock_all(LUser, LServer, Lang) ->
Filter = fun (List) ->
lists:filter(fun (#listitem{action = A}) -> A =/= deny
end,
List)
end,
DBType = gen_mod:db_type(LServer, mod_privacy),
case unblock_by_filter(LUser, LServer, Filter, DBType) of
Mod = db_mod(LServer),
case Mod:unblock_by_filter(LUser, LServer, Filter) of
{atomic, ok} -> {result, []};
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
@@ -268,10 +182,10 @@ process_blocklist_unblock_all(LUser, LServer) ->
{result, [], UserList};
_Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]),
{error, ?ERR_INTERNAL_SERVER_ERROR}
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
end.
process_blocklist_unblock(LUser, LServer, JIDs) ->
process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
Filter = fun (List) ->
lists:filter(fun (#listitem{action = deny, type = jid,
value = JID}) ->
@@ -280,8 +194,8 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
end,
List)
end,
DBType = gen_mod:db_type(LServer, mod_privacy),
case unblock_by_filter(LUser, LServer, Filter, DBType) of
Mod = db_mod(LServer),
case Mod:unblock_by_filter(LUser, LServer, Filter) of
{atomic, ok} -> {result, []};
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
@@ -292,84 +206,9 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
{result, [], UserList};
_Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
{error, ?ERR_INTERNAL_SERVER_ERROR}
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
end.
unblock_by_filter(LUser, LServer, Filter, mnesia) ->
F = fun () ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
% No lists, nothing to unblock
ok;
[#privacy{default = Default, lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
% No default list, nothing to unblock
ok
end
end
end,
mnesia:transaction(F);
unblock_by_filter(LUser, LServer, Filter, riak) ->
{atomic,
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
{LUser, LServer}) of
{error, _} ->
%% No lists, nothing to unblock
ok;
{ok, #privacy{default = Default, lists = Lists} = P} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
case ejabberd_riak:put(P#privacy{lists = NewLists},
mod_privacy:privacy_schema()) of
ok ->
{ok, Default, NewList};
Err ->
Err
end;
false ->
%% No default list, nothing to unblock
ok
end
end};
unblock_by_filter(LUser, LServer, Filter, odbc) ->
F = fun () ->
case mod_privacy:sql_get_default_privacy_list_t(LUser)
of
{selected, [<<"name">>], []} -> ok;
{selected, [<<"name">>], [[Default]]} ->
{selected, [<<"id">>], [[ID]]} =
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1,
RItems),
NewList = Filter(List),
NewRItems = lists:map(fun mod_privacy:item_to_raw/1,
NewList),
mod_privacy:sql_set_privacy_list(ID, NewRItems),
{ok, Default, NewList};
_ -> ok
end;
_ -> ok
end
end,
ejabberd_odbc:sql_transaction(LServer, F).
make_userlist(Name, List) ->
NeedDb = mod_privacy:is_list_needdb(List),
#userlist{name = Name, list = List, needdb = NeedDb}.
@@ -385,11 +224,11 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
ejabberd_sm:route(JID, JID,
{broadcast, {blocking, Event}}).
process_blocklist_get(LUser, LServer) ->
case process_blocklist_get(LUser, LServer,
gen_mod:db_type(LServer, mod_privacy))
of
error -> {error, ?ERR_INTERNAL_SERVER_ERROR};
process_blocklist_get(LUser, LServer, Lang) ->
Mod = db_mod(LServer),
case Mod:process_blocklist_get(LUser, LServer) of
error ->
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
List ->
JIDs = list_to_blocklist_jids(List, []),
Items = lists:map(fun (JID) ->
@@ -407,49 +246,9 @@ process_blocklist_get(LUser, LServer) ->
children = Items}]}
end.
process_blocklist_get(LUser, LServer, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer})
of
{'EXIT', _Reason} -> error;
[] -> [];
[#privacy{default = Default, lists = Lists}] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} -> List;
_ -> []
end
end;
process_blocklist_get(LUser, LServer, riak) ->
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
{LUser, LServer}) of
{ok, #privacy{default = Default, lists = Lists}} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} -> List;
_ -> []
end;
{error, notfound} ->
[];
{error, _} ->
error
end;
process_blocklist_get(LUser, LServer, odbc) ->
case catch
mod_privacy:sql_get_default_privacy_list(LUser, LServer)
of
{selected, [<<"name">>], []} -> [];
{selected, [<<"name">>], [[Default]]} ->
case catch mod_privacy:sql_get_privacy_list_data(LUser,
LServer, Default)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems} ->
lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
{'EXIT', _} -> error
end;
{'EXIT', _} -> error
end.
db_mod(LServer) ->
DBType = gen_mod:db_type(LServer, mod_privacy),
gen_mod:db_mod(DBType, ?MODULE).
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
+85
View File
@@ -0,0 +1,85 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_blocking_mnesia).
-behaviour(mod_blocking).
%% API
-export([process_blocklist_block/3, unblock_by_filter/3,
process_blocklist_get/2]).
-include("jlib.hrl").
-include("mod_privacy.hrl").
%%%===================================================================
%%% API
%%%===================================================================
process_blocklist_block(LUser, LServer, Filter) ->
F = fun () ->
case mnesia:wread({privacy, {LUser, LServer}}) of
[] ->
P = #privacy{us = {LUser, LServer}},
NewDefault = <<"Blocked contacts">>,
NewLists1 = [],
List = [];
[#privacy{default = Default, lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
NewDefault = <<"Blocked contacts">>,
NewLists1 = Lists,
List = []
end
end,
NewList = Filter(List),
NewLists = [{NewDefault, NewList} | NewLists1],
mnesia:write(P#privacy{default = NewDefault,
lists = NewLists}),
{ok, NewDefault, NewList}
end,
mnesia:transaction(F).
unblock_by_filter(LUser, LServer, Filter) ->
F = fun () ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
%% No lists, nothing to unblock
ok;
[#privacy{default = Default, lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
%% No default list, nothing to unblock
ok
end
end
end,
mnesia:transaction(F).
process_blocklist_get(LUser, LServer) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} -> error;
[] -> [];
[#privacy{default = Default, lists = Lists}] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} -> List;
_ -> []
end
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
+98
View File
@@ -0,0 +1,98 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_blocking_riak).
-behaviour(mod_blocking).
%% API
-export([process_blocklist_block/3, unblock_by_filter/3,
process_blocklist_get/2]).
-include("jlib.hrl").
-include("mod_privacy.hrl").
%%%===================================================================
%%% API
%%%===================================================================
process_blocklist_block(LUser, LServer, Filter) ->
{atomic,
begin
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
{LUser, LServer}) of
{ok, #privacy{default = Default, lists = Lists} = P} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
NewDefault = <<"Blocked contacts">>,
NewLists1 = Lists,
List = []
end;
{error, _} ->
P = #privacy{us = {LUser, LServer}},
NewDefault = <<"Blocked contacts">>,
NewLists1 = [],
List = []
end,
NewList = Filter(List),
NewLists = [{NewDefault, NewList} | NewLists1],
case ejabberd_riak:put(P#privacy{default = NewDefault,
lists = NewLists},
mod_privacy_riak:privacy_schema()) of
ok ->
{ok, NewDefault, NewList};
Err ->
Err
end
end}.
unblock_by_filter(LUser, LServer, Filter) ->
{atomic,
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
{LUser, LServer}) of
{error, _} ->
%% No lists, nothing to unblock
ok;
{ok, #privacy{default = Default, lists = Lists} = P} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
case ejabberd_riak:put(P#privacy{lists = NewLists},
mod_privacy_riak:privacy_schema()) of
ok ->
{ok, Default, NewList};
Err ->
Err
end;
false ->
%% No default list, nothing to unblock
ok
end
end}.
process_blocklist_get(LUser, LServer) ->
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
{LUser, LServer}) of
{ok, #privacy{default = Default, lists = Lists}} ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} -> List;
_ -> []
end;
{error, notfound} ->
[];
{error, _} ->
error
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
+87
View File
@@ -0,0 +1,87 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_blocking_sql).
-behaviour(mod_blocking).
%% API
-export([process_blocklist_block/3, unblock_by_filter/3,
process_blocklist_get/2]).
-include("jlib.hrl").
-include("mod_privacy.hrl").
%%%===================================================================
%%% API
%%%===================================================================
process_blocklist_block(LUser, LServer, Filter) ->
F = fun () ->
Default = case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
{selected, []} ->
Name = <<"Blocked contacts">>,
mod_privacy_sql:sql_add_privacy_list(LUser, Name),
mod_privacy_sql:sql_set_default_privacy_list(LUser, Name),
Name;
{selected, [{Name}]} -> Name
end,
{selected, [{ID}]} =
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
{selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
_ ->
List = []
end,
NewList = Filter(List),
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
NewList),
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
{ok, Default, NewList}
end,
ejabberd_sql:sql_transaction(LServer, F).
unblock_by_filter(LUser, LServer, Filter) ->
F = fun () ->
case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
{selected, []} -> ok;
{selected, [{Default}]} ->
{selected, [{ID}]} =
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
{selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1,
RItems),
NewList = Filter(List),
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
NewList),
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
{ok, Default, NewList};
_ -> ok
end;
_ -> ok
end
end,
ejabberd_sql:sql_transaction(LServer, F).
process_blocklist_get(LUser, LServer) ->
case catch mod_privacy_sql:sql_get_default_privacy_list(LUser, LServer) of
{selected, []} -> [];
{selected, [{Default}]} ->
case catch mod_privacy_sql:sql_get_privacy_list_data(
LUser, LServer, Default) of
{selected, RItems} ->
lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
{'EXIT', _} -> error
end;
{'EXIT', _} -> error
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
+20 -126
View File
@@ -80,6 +80,12 @@
-record(state, {host = <<"">> :: binary()}).
-callback init(binary(), gen_mod:opts()) -> any().
-callback caps_read(binary(), {binary(), binary()}) ->
{ok, non_neg_integer() | [binary()]} | error.
-callback caps_write(binary(), {binary(), binary()},
non_neg_integer() | [binary()]) -> any().
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE,
@@ -300,28 +306,9 @@ c2s_broadcast_recipients(InAcc, Host, C2SState,
end;
c2s_broadcast_recipients(Acc, _, _, _, _, _) -> Acc.
init_db(mnesia, _Host) ->
case catch mnesia:table_info(caps_features, storage_type) of
{'EXIT', _} ->
ok;
disc_only_copies ->
ok;
_ ->
mnesia:delete_table(caps_features)
end,
mnesia:create_table(caps_features,
[{disc_only_copies, [node()]},
{local_content, true},
{attributes,
record_info(fields, caps_features)}]),
update_table(),
mnesia:add_table_copy(caps_features, node(),
disc_only_copies);
init_db(_, _) ->
ok.
init([Host, Opts]) ->
init_db(gen_mod:db_type(Host, Opts), Host),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
MaxSize = gen_mod:get_opt(cache_size, Opts,
fun(I) when is_integer(I), I>0 -> I end,
1000),
@@ -450,65 +437,13 @@ feature_response(_IQResult, Host, From, Caps,
caps_read_fun(Host, Node) ->
LServer = jid:nameprep(Host),
DBType = gen_mod:db_type(LServer, ?MODULE),
caps_read_fun(LServer, Node, DBType).
caps_read_fun(_LServer, Node, mnesia) ->
fun () ->
case mnesia:dirty_read({caps_features, Node}) of
[#caps_features{features = Features}] -> {ok, Features};
_ -> error
end
end;
caps_read_fun(_LServer, Node, riak) ->
fun() ->
case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of
{ok, #caps_features{features = Features}} -> {ok, Features};
_ -> error
end
end;
caps_read_fun(LServer, {Node, SubNode}, odbc) ->
fun() ->
SNode = ejabberd_odbc:escape(Node),
SSubNode = ejabberd_odbc:escape(SubNode),
case ejabberd_odbc:sql_query(
LServer, [<<"select feature from caps_features where ">>,
<<"node='">>, SNode, <<"' and subnode='">>,
SSubNode, <<"';">>]) of
{selected, [<<"feature">>], [[H]|_] = Fs} ->
case catch jlib:binary_to_integer(H) of
Int when is_integer(Int), Int>=0 ->
{ok, Int};
_ ->
{ok, lists:flatten(Fs)}
end;
_ ->
error
end
end.
Mod = gen_mod:db_mod(LServer, ?MODULE),
fun() -> Mod:caps_read(LServer, Node) end.
caps_write_fun(Host, Node, Features) ->
LServer = jid:nameprep(Host),
DBType = gen_mod:db_type(LServer, ?MODULE),
caps_write_fun(LServer, Node, Features, DBType).
caps_write_fun(_LServer, Node, Features, mnesia) ->
fun () ->
mnesia:dirty_write(#caps_features{node_pair = Node,
features = Features})
end;
caps_write_fun(_LServer, Node, Features, riak) ->
fun () ->
ejabberd_riak:put(#caps_features{node_pair = Node,
features = Features},
caps_features_schema())
end;
caps_write_fun(LServer, NodePair, Features, odbc) ->
fun () ->
ejabberd_odbc:sql_transaction(
LServer,
sql_write_features_t(NodePair, Features))
end.
Mod = gen_mod:db_mod(LServer, ?MODULE),
fun() -> Mod:caps_write(LServer, Node, Features) end.
make_my_disco_hash(Host) ->
JID = jid:make(<<"">>, Host, <<"">>),
@@ -658,64 +593,23 @@ is_valid_node(Node) ->
false
end.
update_table() ->
Fields = record_info(fields, caps_features),
case mnesia:table_info(caps_features, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
caps_features, Fields, set,
fun(#caps_features{node_pair = {N, _}}) -> N end,
fun(#caps_features{node_pair = {N, P},
features = Fs} = R) ->
NewFs = if is_integer(Fs) ->
Fs;
true ->
[iolist_to_binary(F) || F <- Fs]
end,
R#caps_features{node_pair = {iolist_to_binary(N),
iolist_to_binary(P)},
features = NewFs}
end);
_ ->
?INFO_MSG("Recreating caps_features table", []),
mnesia:transform_table(caps_features, ignore, Fields)
end.
sql_write_features_t({Node, SubNode}, Features) ->
SNode = ejabberd_odbc:escape(Node),
SSubNode = ejabberd_odbc:escape(SubNode),
NewFeatures = if is_integer(Features) ->
[jlib:integer_to_binary(Features)];
true ->
Features
end,
[[<<"delete from caps_features where node='">>,
SNode, <<"' and subnode='">>, SSubNode, <<"';">>]|
[[<<"insert into caps_features(node, subnode, feature) ">>,
<<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>,
ejabberd_odbc:escape(F), <<"');">>] || F <- NewFeatures]].
caps_features_schema() ->
{record_info(fields, caps_features), #caps_features{}}.
export(_Server) ->
[{caps_features,
fun(_Host, #caps_features{node_pair = NodePair,
features = Features}) ->
sql_write_features_t(NodePair, Features);
(_Host, _R) ->
[]
end}].
export(LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:export(LServer).
import_info() ->
[{<<"caps_features">>, 4}].
import_start(LServer, DBType) ->
ets:new(caps_features_tmp, [private, named_table, bag]),
init_db(DBType, LServer),
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:init(LServer, []),
ok.
import(_LServer, {odbc, _}, _DBType, <<"caps_features">>,
import(_LServer, {sql, _}, _DBType, <<"caps_features">>,
[Node, SubNode, Feature, _TimeStamp]) ->
Feature1 = case catch jlib:binary_to_integer(Feature) of
I when is_integer(I), I>0 -> I;
@@ -748,7 +642,7 @@ import_next(LServer, DBType, NodePair) ->
ejabberd_riak:put(
#caps_features{node_pair = NodePair, features = Features},
caps_features_schema());
_ when DBType == odbc ->
_ when DBType == sql ->
ok
end,
import_next(LServer, DBType, ets:next(caps_features_tmp, NodePair)).
@@ -757,6 +651,6 @@ mod_opt_type(cache_life_time) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(cache_size) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) ->
[cache_life_time, cache_size, db_type].
+73
View File
@@ -0,0 +1,73 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_caps_mnesia).
-behaviour(mod_caps).
%% API
-export([init/2, caps_read/2, caps_write/3]).
-include("mod_caps.hrl").
-include("logger.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
case catch mnesia:table_info(caps_features, storage_type) of
{'EXIT', _} ->
ok;
disc_only_copies ->
ok;
_ ->
mnesia:delete_table(caps_features)
end,
mnesia:create_table(caps_features,
[{disc_only_copies, [node()]},
{local_content, true},
{attributes,
record_info(fields, caps_features)}]),
update_table(),
mnesia:add_table_copy(caps_features, node(),
disc_only_copies).
caps_read(_LServer, Node) ->
case mnesia:dirty_read({caps_features, Node}) of
[#caps_features{features = Features}] -> {ok, Features};
_ -> error
end.
caps_write(_LServer, Node, Features) ->
mnesia:dirty_write(#caps_features{node_pair = Node,
features = Features}).
%%%===================================================================
%%% Internal functions
%%%===================================================================
update_table() ->
Fields = record_info(fields, caps_features),
case mnesia:table_info(caps_features, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
caps_features, Fields, set,
fun(#caps_features{node_pair = {N, _}}) -> N end,
fun(#caps_features{node_pair = {N, P},
features = Fs} = R) ->
NewFs = if is_integer(Fs) ->
Fs;
true ->
[iolist_to_binary(F) || F <- Fs]
end,
R#caps_features{node_pair = {iolist_to_binary(N),
iolist_to_binary(P)},
features = NewFs}
end);
_ ->
?INFO_MSG("Recreating caps_features table", []),
mnesia:transform_table(caps_features, ignore, Fields)
end.
+38
View File
@@ -0,0 +1,38 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_caps_riak).
-behaviour(mod_caps).
%% API
-export([init/2, caps_read/2, caps_write/3]).
-include("mod_caps.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
caps_read(_LServer, Node) ->
case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of
{ok, #caps_features{features = Features}} -> {ok, Features};
_ -> error
end.
caps_write(_LServer, Node, Features) ->
ejabberd_riak:put(#caps_features{node_pair = Node,
features = Features},
caps_features_schema()).
%%%===================================================================
%%% Internal functions
%%%===================================================================
caps_features_schema() ->
{record_info(fields, caps_features), #caps_features{}}.
+71
View File
@@ -0,0 +1,71 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_caps_sql).
-behaviour(mod_caps).
%% API
-export([init/2, caps_read/2, caps_write/3, export/1]).
-include("mod_caps.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
ok.
caps_read(LServer, {Node, SubNode}) ->
SNode = ejabberd_sql:escape(Node),
SSubNode = ejabberd_sql:escape(SubNode),
case ejabberd_sql:sql_query(
LServer, [<<"select feature from caps_features where ">>,
<<"node='">>, SNode, <<"' and subnode='">>,
SSubNode, <<"';">>]) of
{selected, [<<"feature">>], [[H]|_] = Fs} ->
case catch jlib:binary_to_integer(H) of
Int when is_integer(Int), Int>=0 ->
{ok, Int};
_ ->
{ok, lists:flatten(Fs)}
end;
_ ->
error
end.
caps_write(LServer, NodePair, Features) ->
ejabberd_sql:sql_transaction(
LServer,
sql_write_features_t(NodePair, Features)).
export(_Server) ->
[{caps_features,
fun(_Host, #caps_features{node_pair = NodePair,
features = Features}) ->
sql_write_features_t(NodePair, Features);
(_Host, _R) ->
[]
end}].
%%%===================================================================
%%% Internal functions
%%%===================================================================
sql_write_features_t({Node, SubNode}, Features) ->
SNode = ejabberd_sql:escape(Node),
SSubNode = ejabberd_sql:escape(SubNode),
NewFeatures = if is_integer(Features) ->
[jlib:integer_to_binary(Features)];
true ->
Features
end,
[[<<"delete from caps_features where node='">>,
SNode, <<"' and subnode='">>, SSubNode, <<"';">>]|
[[<<"insert into caps_features(node, subnode, feature) ">>,
<<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>,
ejabberd_sql:escape(F), <<"');">>] || F <- NewFeatures]].
+23 -33
View File
@@ -43,12 +43,11 @@
-include("logger.hrl").
-include("jlib.hrl").
-define(PROCNAME, ?MODULE).
-define(TABLE, carboncopy).
-type matchspec_atom() :: '_' | '$1' | '$2' | '$3'.
-record(carboncopy,{us :: {binary(), binary()} | matchspec_atom(),
resource :: binary() | matchspec_atom(),
version :: binary() | matchspec_atom()}).
-callback init(binary(), gen_mod:opts()) -> any().
-callback enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
-callback disable(binary(), binary(), binary()) -> ok | {error, any()}.
-callback list(binary(), binary()) -> [{binary(), binary()}].
is_carbon_copy(Packet) ->
is_carbon_copy(Packet, <<"sent">>) orelse
@@ -69,17 +68,8 @@ start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts,fun gen_iq_handler:check_type/1, one_queue),
mod_disco:register_feature(Host, ?NS_CARBONS_1),
mod_disco:register_feature(Host, ?NS_CARBONS_2),
Fields = record_info(fields, ?TABLE),
try mnesia:table_info(?TABLE, attributes) of
Fields -> ok;
_ -> mnesia:delete_table(?TABLE) %% recreate..
catch _:_Error -> ok %%probably table don't exist
end,
mnesia:create_table(?TABLE,
[{ram_copies, [node()]},
{attributes, record_info(fields, ?TABLE)},
{type, bag}]),
mnesia:add_table_copy(?TABLE, node(), ram_copies),
Mod = gen_mod:db_mod(Host, ?MODULE),
Mod:init(Host, Opts),
ejabberd_hooks:add(unset_presence_hook,Host, ?MODULE, remove_connection, 10),
%% 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),
@@ -102,7 +92,9 @@ iq_handler2(From, To, IQ) ->
iq_handler1(From, To, IQ) ->
iq_handler(From, To, IQ, ?NS_CARBONS_1).
iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children = []}} = IQ, CC)->
iq_handler(From, _To,
#iq{type=set, lang = Lang,
sub_el = #xmlel{name = Operation} = SubEl} = IQ, CC)->
?DEBUG("carbons IQ received: ~p", [IQ]),
{U, S, R} = jid:tolower(From),
Result = case Operation of
@@ -118,12 +110,14 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children
?DEBUG("carbons IQ result: ok", []),
IQ#iq{type=result, sub_el=[]};
{error,_Error} ->
?WARNING_MSG("Error enabling / disabling carbons: ~p", [Result]),
IQ#iq{type=error,sub_el = [?ERR_BAD_REQUEST]}
?ERROR_MSG("Error enabling / disabling carbons: ~p", [Result]),
Txt = <<"Database failure">>,
IQ#iq{type=error,sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]}
end;
iq_handler(_From, _To, IQ, _CC)->
IQ#iq{type=error, sub_el = [?ERR_NOT_ALLOWED]}.
iq_handler(_From, _To, #iq{lang = Lang, sub_el = SubEl} = IQ, _CC)->
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
IQ#iq{type=error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]}.
user_send_packet(Packet, _C2SState, From, To) ->
check_and_forward(From, To, Packet, sent).
@@ -240,18 +234,13 @@ build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_1) ->
enable(Host, U, R, CC)->
?DEBUG("enabling for ~p", [U]),
try mnesia:dirty_write(#carboncopy{us = {U, Host}, resource=R, version = CC}) of
ok -> ok
catch _:Error -> {error, Error}
end.
Mod = gen_mod:db_mod(Host, ?MODULE),
Mod:enable(U, Host, R, CC).
disable(Host, U, R)->
?DEBUG("disabling for ~p", [U]),
ToDelete = mnesia:dirty_match_object(?TABLE, #carboncopy{us = {U, Host}, resource = R, version = '_'}),
try lists:foreach(fun mnesia:dirty_delete_object/1, ToDelete) of
ok -> ok
catch _:Error -> {error, Error}
end.
Mod = gen_mod:db_mod(Host, ?MODULE),
Mod:disable(U, Host, R).
complete_packet(From, #xmlel{name = <<"message">>, attrs = OrigAttrs} = Packet, sent) ->
%% if this is a packet sent by user on this host, then Packet doesn't
@@ -286,8 +275,9 @@ has_non_empty_body(Packet) ->
%% list {resource, cc_version} with carbons enabled for given user and host
list(User, Server) ->
mnesia:dirty_select(?TABLE, [{#carboncopy{us = {User, Server}, resource = '$2', version = '$3'}, [], [{{'$2','$3'}}]}]).
Mod = gen_mod:db_mod(Server, ?MODULE),
Mod:list(User, Server).
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [db_type, iqdisc].

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