Compare commits

...

132 Commits

Author SHA1 Message Date
Paweł Chmielowski b160bd7ac1 Provide authzid in scream response 2016-03-30 16:08:04 +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
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
97 changed files with 4388 additions and 2290 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?
+18 -5
View File
@@ -1,9 +1,8 @@
language: erlang
otp_release:
- 17.1
- 17.5
- 18.0
- 18.2.1
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,25 @@ 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:
# 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
+13 -19
View File
@@ -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: {}
+27 -10
View File
@@ -159,6 +159,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=()
@@ -208,10 +217,10 @@ iexdebug()
# Elixir shell is hidden as default
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"debug-${TTY}-${ERLANG_NODE}\"` \
-remsh $ERLANG_NODE \
--erl \"`shell_escape \"$KERNEL_OPTS\"\" \
--erl \"`shell_escape \"$ERLANG_OPTS\"\" \
--erl \"`shell_escape \"${ARGS[@]}\"\" \
--erl \"`shell_escape \"$@\"\""
--erl `shell_escape \"$KERNEL_OPTS\"` \
--erl `shell_escape \"$ERLANG_OPTS\"` \
--erl `shell_escape \"${ARGS[@]}\"` \
--erl `shell_escape_str \"$@\"`"
$EXEC_CMD "$CMD"
}
@@ -233,14 +242,15 @@ 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 \"$@\"`\""
--erl `shell_escape \"$ERLANG_OPTS\"` \
--erl `shell_escape \"${ARGS[@]}\"` \
--erl `shell_escape_str \"$@\"`"
$EXEC_CMD "$CMD"
}
@@ -318,11 +328,18 @@ etop()
ping()
{
TTY=`tty | sed -e 's/.*\///g'`
if [ "$1" = "${1%.*}" ] ; then
PING_NAME="-sname"
PING_NODE=$(hostname -s)
else
PING_NAME="-name"
PING_NODE=$(hostname)
fi
$EXEC_CMD "$ERL \
$NAME ping-${TTY}-${ERLANG_NODE} \
$PING_NAME ping-${TTY}@${PING_NODE} \
-hidden \
$KERNEL_OPTS $ERLANG_OPTS \
-eval 'io:format(\"~p~n\",[net_adm:ping($1)])' \
-eval 'io:format(\"~p~n\",[net_adm:ping('\"'\"'$1'\"'\"')])' \
-s erlang halt -output text -noinput"
}
@@ -377,7 +394,7 @@ ctl()
# concurrent invocations using a bound
# number of atoms
for N in `seq 1 $MAXCONNID`; do
CTL_CONN="ejabberdctl-$N"
CTL_CONN="ctl-$N-${ERLANG_NODE}"
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
(
exec 8>"$CTL_LOCKFILE"
+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_odbc:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))).
-define(SQL_UPSERT_T(Table, Fields),
ejabberd_odbc: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
@@ -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">>).
-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.02.0",
description: description,
elixir: "~> 1.1",
elixir: "~> 1.2",
elixirc_paths: ["lib"],
compile_path: ".",
compilers: [:asn1] ++ Mix.compilers,
+10 -9
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"},
"erlware_commons": {:hex, :erlware_commons, "0.19.0"},
"esip": {:hex, :esip, "1.0.2"},
"exrm": {:hex, :exrm, "1.0.0-rc7"},
"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_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.18.0"},
"sqlite3": {:hex, :sqlite3, "1.1.5"},
"stringprep": {:hex, :stringprep, "1.0.2"},
"stringprep": {:hex, :stringprep, "1.0.3"},
"stun": {:hex, :stun, "1.0.1"}}
+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."
+24 -19
View File
@@ -14,38 +14,37 @@
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.2"}}},
{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"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.2"}}},
{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"}}}},
{tag, "1.0.1"}}}},
{if_var_true, riak, {hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang",
"908a24fda4a46776a5135db60ca071e3d783f9f6"}}}, % for riak_pb-2.1.0.7
"908a24fda4a46776a5135db60ca071e3d783f9f6"}}}, % for riak_pb-2.1.0.7
{if_var_true, riak, {riakc, ".*", {git, "https://github.com/basho/riak-erlang-client",
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
{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"}}}},
{tag, "1.0.0"}}}},
{if_var_true, tools, {meck, "0.8.2", {git, "https://github.com/eproxus/meck",
{tag, "0.8.2"}}}},
{tag, "0.8.2"}}}},
{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 +52,7 @@
stringprep,
fast_xml,
esip,
luerl,
luerl,
stun,
fast_yaml,
p1_utils,
@@ -63,8 +62,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 +92,7 @@
{xref_warnings, false}.
{xref_checks, [deprecated_function_calls, undefined_function_calls]}.
{xref_checks, [deprecated_function_calls]}.
{xref_exclusions, [
"(\"gen_transport\":_/_)",
@@ -109,6 +111,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.
+4 -1
View File
@@ -19,10 +19,13 @@
CREATE TABLE users (
username varchar(191) 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
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 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;
+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;
+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">>};
+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);
+4
View File
@@ -196,6 +196,10 @@ get_commands_spec() ->
desc = "Export all tables as SQL queries to a file",
module = ejd2odbc, function = export,
args = [{host, string}, {file, string}], result = {res, rescode}},
#ejabberd_commands{name = delete_mnesia, tags = [mnesia, odbc],
desc = "Export all tables as SQL queries to a file",
module = ejd2odbc, function = delete,
args = [{host, string}], result = {res, rescode}},
#ejabberd_commands{name = convert_to_scram, tags = [odbc],
desc = "Convert the passwords in 'users' ODBC table to SCRAM",
module = ejabberd_auth_odbc, function = convert_to_scram,
+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) ->
+20 -20
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_odbc | 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) ->
+5 -5
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,
@@ -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,
+29 -25
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,
@@ -76,16 +76,20 @@ 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
@@ -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,42 +191,42 @@ 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_internal(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_internal(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).
%% @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);
@@ -241,8 +245,8 @@ get_password_cache(User, Server, CacheTime) ->
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;
false -> false
@@ -256,9 +260,9 @@ try_register_external_cache(User, Server, Password) ->
_ -> {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_internal(User, AuthzId, Server, Password) ->
ejabberd_auth_internal:check_password(User, AuthzId, Server,
Password).
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
@@ -273,10 +277,10 @@ 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
[] ->
+16 -8
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,
@@ -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()) ->
+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),
+109 -98
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,
@@ -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 odbc_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 odbc_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 odbc_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(
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)
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(
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 odbc_queries:add_user(LServer, LUser,
Password) of
{updated, 1} -> {atomic, ok};
_ -> {atomic, exists}
end
@@ -228,35 +226,51 @@ dirty_get_registered_users() ->
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 odbc_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 odbc_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 odbc_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 odbc_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]]} ->
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 odbc_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 odbc_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 odbc_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 odbc_queries:del_user(LServer, LUser),
ok
end.
@@ -352,23 +367,19 @@ 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),
LServer, LUser, Password),
case Result of
{selected, [<<"password">>],
[[Password]]} -> ok;
{selected, [<<"password">>],
[]} -> not_exists;
{selected, [{Password}]} -> ok;
{selected, []} -> not_exists;
_ -> not_allowed
end
end,
+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) ->
+16 -8
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) ->
+22 -19
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>">>).
@@ -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
@@ -634,7 +634,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
@@ -752,9 +752,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",
@@ -876,9 +874,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 +895,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",
@@ -999,7 +993,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.
@@ -1026,8 +1020,7 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
[{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
@@ -1913,6 +1906,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} ->
@@ -3126,6 +3123,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;
+6 -6
View File
@@ -352,7 +352,7 @@ get_command_definition(Name) ->
execute_command(Name, Arguments) ->
execute_command([], noauth, Name, Arguments).
-spec execute_command([{atom(), [atom()], [any()]}],
-spec execute_command([{atom(), [atom()], [any()]}] | undefined,
{binary(), binary(), binary(), boolean()} |
noauth | admin,
atom(),
@@ -361,7 +361,7 @@ execute_command(Name, Arguments) ->
%% @spec (AccessCommands, Auth, Name::atom(), Arguments) -> ResultTerm | {error, Error}
%% where
%% AccessCommands = [{Access, CommandNames, Arguments}]
%% AccessCommands = [{Access, CommandNames, Arguments}] | undefined
%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()}
%% | noauth
%% | admin
@@ -465,7 +465,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{
@@ -517,7 +517,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.
@@ -595,7 +595,7 @@ get_commands() ->
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 +604,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;
+53 -22
View File
@@ -38,6 +38,8 @@
convert_to_yaml/1, convert_to_yaml/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(#state{hosts = Hosts, 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 ->
+1 -1
View File
@@ -214,7 +214,7 @@ process(Args) ->
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, noauth, AccessCommands).
process2(Args, Auth, AccessCommands) ->
case try_run_ctp(Args, Auth, AccessCommands) of
+2 -1
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,
@@ -178,6 +178,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 ->
+227 -31
View File
@@ -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,10 +64,12 @@
-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(),
@@ -92,6 +95,8 @@
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
-define(PREPARE_KEY, ejabberd_odbc_prepare).
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -116,11 +121,13 @@ start_link(Host, StartInterval) ->
[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().
@@ -194,6 +201,13 @@ escape_like($%) -> <<"\\%">>;
escape_like($_) -> <<"\\_">>;
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> odbc_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;
to_bool(<<"1">>) -> true;
@@ -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",
@@ -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);
@@ -701,9 +904,8 @@ db_opts(Host) ->
<<"">>),
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
@@ -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,8 +987,17 @@ 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.
-81
View File
@@ -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
+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;
+41 -2
View File
@@ -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))
+1 -1
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)),
+1
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,
+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_odbc),
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_odbc),
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_odbc),
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_odbc),
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).
+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(
+2 -3
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) ->
+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)}]};
+34 -1
View File
@@ -30,7 +30,7 @@
-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).
@@ -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) ->
@@ -160,6 +174,25 @@ 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 ->
+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;
+43 -10
View File
@@ -35,7 +35,8 @@
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,
default_db/1, v_db/1, opt_type/1]).
%%-export([behaviour_info/1]).
@@ -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) ->
+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 -18
View File
@@ -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],
@@ -748,21 +748,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),
@@ -861,7 +847,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 +1180,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">>,
+13 -27
View File
@@ -223,23 +223,18 @@ process_blocklist_block(LUser, LServer, Filter, odbc) ->
Default = case
mod_privacy:sql_get_default_privacy_list_t(LUser)
of
{selected, [<<"name">>], []} ->
{selected, []} ->
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
{selected, [{Name}]} -> Name
end,
{selected, [<<"id">>], [[ID]]} =
{selected, [{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 = [_ | _]} ->
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
{selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
_ -> List = []
end,
@@ -345,17 +340,12 @@ 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]]} =
{selected, []} -> ok;
{selected, [{Default}]} ->
{selected, [{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 = [_ | _]} ->
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
{selected, RItems = [_ | _]} ->
List = lists:flatmap(fun mod_privacy:raw_to_item/1,
RItems),
NewList = Filter(List),
@@ -435,16 +425,12 @@ process_blocklist_get(LUser, LServer, odbc) ->
case catch
mod_privacy:sql_get_default_privacy_list(LUser, LServer)
of
{selected, [<<"name">>], []} -> [];
{selected, [<<"name">>], [[Default]]} ->
{selected, []} -> [];
{selected, [{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} ->
{selected, RItems} ->
lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
{'EXIT', _} -> error
end;
+1 -1
View File
@@ -86,7 +86,7 @@ stop(Host) ->
init([Host, Opts]) ->
MyHost = gen_mod:get_opt_host(Host, Opts,
<<"echo.@HOST@">>),
ejabberd_router:register_route(MyHost),
ejabberd_router:register_route(MyHost, Host),
{ok, #state{host = MyHost}}.
%%--------------------------------------------------------------------
+87 -38
View File
@@ -28,9 +28,9 @@
%% in ejabberd_http listener
%% request_handlers:
%% "/api": mod_http_api
%%
%%
%% Access rights are defined with:
%% commands_admin_access: configure
%% commands_admin_access: configure
%% commands:
%% - add_commands: user
%%
@@ -43,6 +43,25 @@
%%
%% Then to perform an action, send a POST request to the following URL:
%% http://localhost:5280/api/<call_name>
%%
%% It's also possible to enable unrestricted access to some commands from group
%% of IP addresses by using option `admin_ip_access` by having fragment like
%% this in configuration file:
%% modules:
%% mod_http_api:
%% admin_ip_access: admin_ip_access_rule
%%...
%% access:
%% admin_ip_access_rule:
%% admin_ip_acl:
%% - command1
%% - command2
%% %% use `all` to give access to all commands
%%...
%% acl:
%% admin_ip_acl:
%% ip:
%% - "127.0.0.1/8"
-module(mod_http_api).
@@ -102,46 +121,72 @@ stop(_Host) ->
%% basic auth
%% ----------
check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
when HTTPAuth /= undefined ->
check_permissions(Request, Command) ->
case catch binary_to_existing_atom(Command, utf8) of
Call when is_atom(Call) ->
Admin =
case lists:keysearch(<<"X-Admin">>, 1, Headers) of
{value, {_, <<"true">>}} -> true;
_ -> false
end,
Auth =
case HTTPAuth of
{SJID, Pass} ->
case jid:from_string(SJID) of
#jid{user = User, server = Server} ->
case ejabberd_auth:check_password(User, Server, Pass) of
true -> {ok, {User, Server, Pass, Admin}};
false -> false
end;
_ ->
false
end;
{oauth, Token, _} ->
case ejabberd_oauth:check_token(Command, Token) of
{ok, User, Server} ->
{ok, {User, Server, {oauth, Token}, Admin}};
false ->
false
check_permissions2(Request, Call);
_ ->
unauthorized_response()
end.
check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call)
when HTTPAuth /= undefined ->
Admin =
case lists:keysearch(<<"X-Admin">>, 1, Headers) of
{value, {_, <<"true">>}} -> true;
_ -> false
end,
Auth =
case HTTPAuth of
{SJID, Pass} ->
case jid:from_string(SJID) of
#jid{user = User, server = Server} ->
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true -> {ok, {User, Server, Pass, Admin}};
false -> false
end;
_ ->
false
end,
case Auth of
{ok, A} -> {allowed, Call, A};
end;
{oauth, Token, _} ->
case oauth_check_token(Call, Token) of
{ok, User, Server} ->
{ok, {User, Server, {oauth, Token}, Admin}};
false ->
false
end;
_ ->
false
end,
case Auth of
{ok, A} -> {allowed, Call, A};
_ -> unauthorized_response()
end;
check_permissions2(#request{ip={IP, _Port}}, Call) ->
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access,
mod_opt_type(admin_ip_access),
none),
Res = acl:match_rule(global, Access, IP),
case Res of
all ->
{allowed, Call, admin};
[all] ->
{allowed, Call, admin};
allow ->
{allowed, Call, admin};
Commands when is_list(Commands) ->
case lists:member(Call, Commands) of
true -> {allowed, Call, admin};
_ -> unauthorized_response()
end;
_ ->
unauthorized_response()
end;
check_permissions(_, _Command) ->
unauthorized_response().
end.
oauth_check_token(Scope, Token) when is_atom(Scope) ->
oauth_check_token(atom_to_binary(Scope, utf8), Token);
oauth_check_token(Scope, Token) ->
ejabberd_oauth:check_token(Scope, Token).
%% ------------------
%% command processing
@@ -162,7 +207,7 @@ process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) ->
{allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args),
json_response(Code, jiffy:encode(Result));
ErrorResponse ->
ErrorResponse -> %% Should we reply 403 ?
ErrorResponse
end
catch _:Error ->
@@ -309,7 +354,11 @@ match(Args, Spec) ->
[{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
ejabberd_command(Auth, Cmd, Args, Default) ->
case catch ejabberd_commands:execute_command(undefined, Auth, Cmd, Args) of
Access = case Auth of
admin -> [];
_ -> undefined
end,
case catch ejabberd_commands:execute_command(Access, Auth, Cmd, Args) of
{'EXIT', _} -> Default;
{error, _} -> Default;
Result -> Result
@@ -385,8 +434,8 @@ json_response(Code, Body) when is_integer(Code) ->
log(Call, Args, {Addr, Port}) ->
AddrS = jlib:ip_to_list({Addr, Port}),
?INFO_MSG("Admin call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]).
?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]).
mod_opt_type(access) ->
mod_opt_type(admin_ip_access) ->
fun(Access) when is_atom(Access) -> Access end;
mod_opt_type(_) -> [access].
mod_opt_type(_) -> [admin_ip_access].
+33 -5
View File
@@ -306,7 +306,7 @@ init({ServerHost, Opts}) ->
false ->
ok
end,
ejabberd_router:register_route(Host),
ejabberd_router:register_route(Host, ServerHost),
{ok, #state{server_host = ServerHost, host = Host, name = Name,
access = Access, max_size = MaxSize,
secret_length = SecretLength, jid_in_url = JIDinURL,
@@ -535,7 +535,8 @@ process_iq(_From,
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
children = iq_disco_info(Lang, Name) ++ AddInfo}]};
children = iq_disco_info(ServerHost, Lang, Name)
++ AddInfo}]};
process_iq(From,
#iq{type = get, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ,
#state{server_host = ServerHost, access = Access} = State)
@@ -751,9 +752,36 @@ map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
yield_content_type(Type) -> Type.
-spec iq_disco_info(binary(), binary()) -> [xmlel()].
-spec iq_disco_info(binary(), binary(), binary()) -> [xmlel()].
iq_disco_info(Lang, Name) ->
iq_disco_info(Host, Lang, Name) ->
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size,
fun(I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end,
104857600) of
infinity ->
[];
MaxSize ->
MaxSizeStr = jlib:integer_to_binary(MaxSize),
Fields = [#xmlel{name = <<"field">>,
attrs = [{<<"type">>, <<"hidden">>},
{<<"var">>, <<"FORM_TYPE">>}],
children = [#xmlel{name = <<"value">>,
children =
[{xmlcdata,
?NS_HTTP_UPLOAD}]}]},
#xmlel{name = <<"field">>,
attrs = [{<<"var">>, <<"max-file-size">>}],
children = [#xmlel{name = <<"value">>,
children =
[{xmlcdata,
MaxSizeStr}]}]}],
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"result">>}],
children = Fields}]
end,
[#xmlel{name = <<"identity">>,
attrs = [{<<"category">>, <<"store">>},
{<<"type">>, <<"file">>},
@@ -761,7 +789,7 @@ iq_disco_info(Lang, Name) ->
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD}]},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]}].
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]} | Form].
%% HTTP request handling.
+1 -1
View File
@@ -133,7 +133,7 @@ init([Host, Opts]) ->
catch ets:new(irc_connection,
[named_table, public,
{keypos, #irc_connection.jid_server_host}]),
ejabberd_router:register_route(MyHost),
ejabberd_router:register_route(MyHost, Host),
{ok,
#state{host = MyHost, server_host = Host,
access = Access}}.
+8 -20
View File
@@ -185,18 +185,12 @@ get_last(LUser, LServer, riak) ->
Err
end;
get_last(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_last(LServer, Username) of
{selected, [<<"seconds">>, <<"state">>], []} ->
not_found;
{selected, [<<"seconds">>, <<"state">>],
[[STimeStamp, Status]]} ->
case catch jlib:binary_to_integer(STimeStamp) of
TimeStamp when is_integer(TimeStamp) ->
{ok, TimeStamp, Status};
Reason -> {error, {invalid_timestamp, Reason}}
end;
Reason -> {error, {invalid_result, Reason}}
case catch odbc_queries:get_last(LServer, LUser) of
{selected, []} ->
not_found;
{selected, [{TimeStamp, Status}]} ->
{ok, TimeStamp, Status};
Reason -> {error, {invalid_result, Reason}}
end.
get_last_iq(IQ, SubEl, LUser, LServer) ->
@@ -260,12 +254,7 @@ store_last_info(LUser, LServer, TimeStamp, Status,
last_activity_schema())};
store_last_info(LUser, LServer, TimeStamp, Status,
odbc) ->
Username = ejabberd_odbc:escape(LUser),
Seconds =
ejabberd_odbc:escape(iolist_to_binary(integer_to_list(TimeStamp))),
State = ejabberd_odbc:escape(Status),
odbc_queries:set_last_t(LServer, Username, Seconds,
State).
odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status).
%% @spec (LUser::string(), LServer::string()) ->
%% {ok, TimeStamp::integer(), Status::string()} | not_found
@@ -286,8 +275,7 @@ remove_user(LUser, LServer, mnesia) ->
F = fun () -> mnesia:delete({last_activity, US}) end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_last(LServer, Username);
odbc_queries:del_last(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
+11 -3
View File
@@ -1260,7 +1260,7 @@ make_matchspec(LUser, LServer, Start, End, none) ->
Msg
end).
make_sql_query(User, _LServer, Start, End, With, RSM) ->
make_sql_query(User, LServer, Start, End, With, RSM) ->
{Max, Direction, ID} = case RSM of
#rsm_in{} ->
{RSM#rsm_in.max,
@@ -1269,11 +1269,19 @@ make_sql_query(User, _LServer, Start, End, With, RSM) ->
none ->
{none, none, <<>>}
end,
LimitClause = if is_integer(Max), Max >= 0 ->
ODBCType = ejabberd_config:get_option(
{odbc_type, LServer},
ejabberd_odbc:opt_type(odbc_type)),
LimitClause = if is_integer(Max), Max >= 0, ODBCType /= mssql ->
[<<" limit ">>, jlib:integer_to_binary(Max+1)];
true ->
[]
end,
TopClause = if is_integer(Max), Max >= 0, ODBCType == mssql ->
[<<" TOP ">>, jlib:integer_to_binary(Max+1)];
true ->
[]
end,
WithClause = case With of
{text, <<>>} ->
[];
@@ -1320,7 +1328,7 @@ make_sql_query(User, _LServer, Start, End, With, RSM) ->
end,
SUser = ejabberd_odbc:escape(User),
Query = [<<"SELECT timestamp, xml, peer, kind, nick"
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
" FROM archive WHERE username='">>,
SUser, <<"'">>, WithClause, StartClause, EndClause,
PageClause],
+4 -1
View File
@@ -39,7 +39,7 @@
s2s_send_packet, s2s_receive_packet,
remove_user, register_user]).
-export([start/2, stop/1, send_metrics/4]).
-export([start/2, stop/1, send_metrics/4, opt_type/1]).
-export([offline_message_hook/3,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
@@ -123,3 +123,6 @@ send_metrics(Host, Probe, Peer, Port) ->
Error ->
?WARNING_MSG("can not open udp socket to grapherl: ~p", [Error])
end.
opt_type(_) ->
[].
+347
View File
@@ -0,0 +1,347 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 2 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(mod_mix).
-behaviour(gen_server).
-behaviour(gen_mod).
%% API
-export([start_link/2, start/2, stop/1, process_iq/3,
disco_items/5, disco_identity/5, disco_info/5,
disco_features/5, mod_opt_type/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("logger.hrl").
-include("jlib.hrl").
-include("pubsub.hrl").
-define(PROCNAME, ejabberd_mod_mix).
-define(NODES, [?NS_MIX_NODES_MESSAGES,
?NS_MIX_NODES_PRESENCE,
?NS_MIX_NODES_PARTICIPANTS,
?NS_MIX_NODES_SUBJECT,
?NS_MIX_NODES_CONFIG]).
-record(state, {server_host :: binary(),
host :: binary()}).
%%%===================================================================
%%% API
%%%===================================================================
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 5000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc),
ok.
disco_features(_Acc, _From, _To, _Node, _Lang) ->
{result, [?NS_MIX_0]}.
disco_items(_Acc, _From, To, _Node, _Lang) when To#jid.luser /= <<"">> ->
To_s = jid:to_string(jid:remove_resource(To)),
{result, [#xmlel{name = <<"item">>,
attrs = [{<<"jid">>, To_s},
{<<"node">>, Node}]} || Node <- ?NODES]};
disco_items(_Acc, _From, _To, _Node, _Lang) ->
{result, []}.
disco_identity(Acc, _From, To, _Node, _Lang) when To#jid.luser == <<"">> ->
Acc ++ [#xmlel{name = <<"identity">>,
attrs =
[{<<"category">>, <<"conference">>},
{<<"name">>, <<"MIX service">>},
{<<"type">>, <<"text">>}]}];
disco_identity(Acc, _From, _To, _Node, _Lang) ->
Acc ++ [#xmlel{name = <<"identity">>,
attrs =
[{<<"category">>, <<"conference">>},
{<<"type">>, <<"mix">>}]}].
disco_info(_Acc, _From, To, _Node, _Lang) when is_atom(To) ->
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"result">>}],
children = [#xmlel{name = <<"field">>,
attrs = [{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children = [#xmlel{name = <<"value">>,
children = [{xmlcdata,
?NS_MIX_SERVICEINFO_0}]}]}]}];
disco_info(Acc, _From, _To, _Node, _Lang) ->
Acc.
process_iq(From, To,
#iq{type = set, sub_el = #xmlel{name = <<"join">>} = SubEl} = IQ) ->
Nodes = lists:flatmap(
fun(#xmlel{name = <<"subscribe">>, attrs = Attrs}) ->
Node = fxml:get_attr_s(<<"node">>, Attrs),
case lists:member(Node, ?NODES) of
true -> [Node];
false -> []
end;
(_) ->
[]
end, SubEl#xmlel.children),
case subscribe_nodes(From, To, Nodes) of
{result, _} ->
case publish_participant(From, To) of
{result, _} ->
LFrom_s = jid:to_string(jid:tolower(jid:remove_resource(From))),
Subscribe = [#xmlel{name = <<"subscribe">>,
attrs = [{<<"node">>, Node}]} || Node <- Nodes],
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"join">>,
attrs = [{<<"jid">>, LFrom_s},
{<<"xmlns">>, ?NS_MIX_0}],
children = Subscribe}]};
{error, Err} ->
IQ#iq{type = error, sub_el = [SubEl, Err]}
end;
{error, Err} ->
IQ#iq{type = error, sub_el = [SubEl, Err]}
end;
process_iq(From, To,
#iq{type = set, sub_el = #xmlel{name = <<"leave">>} = SubEl} = IQ) ->
case delete_participant(From, To) of
{result, _} ->
case unsubscribe_nodes(From, To, ?NODES) of
{result, _} ->
IQ#iq{type = result, sub_el = []};
{error, Err} ->
IQ#iq{type = error, sub_el = [SubEl, Err]}
end;
{error, Err} ->
IQ#iq{type = error, sub_el = [SubEl, Err]}
end;
process_iq(_From, _To, #iq{sub_el = SubEl} = IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}.
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([ServerHost, Opts]) ->
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"mix.@HOST@">>),
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
ConfigTab = gen_mod:get_module_proc(Host, config),
ets:new(ConfigTab, [named_table]),
ets:insert(ConfigTab, {plugins, [<<"mix">>]}),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100),
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100),
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_ITEMS, mod_disco,
process_local_iq_items, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_INFO, mod_disco,
process_local_iq_info, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_ITEMS, mod_disco,
process_local_iq_items, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_DISCO_INFO, mod_disco,
process_local_iq_info, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_MIX_0, ?MODULE, process_iq, IQDisc),
ejabberd_router:register_route(Host, ServerHost),
{ok, #state{server_host = ServerHost, host = Host}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({route, From, To, Packet}, State) ->
case catch do_route(State, From, To, Packet) of
{'EXIT', _} = Err ->
try
?ERROR_MSG("failed to route packet ~p from '~s' to '~s': ~p",
[Packet, jid:to_string(From), jid:to_string(To), Err]),
ErrPkt = jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR),
ejabberd_router:route_error(To, From, ErrPkt, Packet)
catch _:_ ->
ok
end;
_ ->
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #state{host = Host}) ->
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100),
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100),
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0),
ejabberd_router:unregister_route(Host),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
do_route(_State, From, To, #xmlel{name = <<"iq">>} = Packet) ->
if To#jid.luser == <<"">> ->
ejabberd_local:process_iq(From, To, Packet);
true ->
ejabberd_sm:process_iq(From, To, Packet)
end;
do_route(_State, From, To, #xmlel{name = <<"presence">>} = Packet)
when To#jid.luser /= <<"">> ->
case fxml:get_tag_attr_s(<<"type">>, Packet) of
<<"unavailable">> ->
delete_presence(From, To);
_ ->
ok
end;
do_route(_State, _From, _To, _Packet) ->
ok.
subscribe_nodes(From, To, Nodes) ->
LTo = jid:tolower(jid:remove_resource(To)),
LFrom = jid:tolower(jid:remove_resource(From)),
From_s = jid:to_string(LFrom),
lists:foldl(
fun(_Node, {error, _} = Err) ->
Err;
(Node, {result, _}) ->
case mod_pubsub:subscribe_node(LTo, Node, From, From_s, []) of
{error, _} = Err ->
case is_item_not_found(Err) of
true ->
case mod_pubsub:create_node(
LTo, To#jid.lserver, Node, LFrom, <<"mix">>) of
{result, _} ->
mod_pubsub:subscribe_node(LTo, Node, From, From_s, []);
Error ->
Error
end;
false ->
Err
end;
{result, _} = Result ->
Result
end
end, {result, []}, Nodes).
unsubscribe_nodes(From, To, Nodes) ->
LTo = jid:tolower(jid:remove_resource(To)),
LFrom = jid:tolower(jid:remove_resource(From)),
From_s = jid:to_string(LFrom),
lists:foldl(
fun(_Node, {error, _} = Err) ->
Err;
(Node, {result, _} = Result) ->
case mod_pubsub:unsubscribe_node(LTo, Node, From, From_s, <<"">>) of
{error, _} = Err ->
case is_not_subscribed(Err) of
true -> Result;
_ -> Err
end;
{result, _} = Res ->
Res
end
end, {result, []}, Nodes).
publish_participant(From, To) ->
LFrom = jid:tolower(jid:remove_resource(From)),
LTo = jid:tolower(jid:remove_resource(To)),
Participant = #xmlel{name = <<"participant">>,
attrs = [{<<"xmlns">>, ?NS_MIX_0},
{<<"jid">>, jid:to_string(LFrom)}]},
ItemID = p1_sha:sha(jid:to_string(LFrom)),
mod_pubsub:publish_item(
LTo, To#jid.lserver, ?NS_MIX_NODES_PARTICIPANTS,
From, ItemID, [Participant]).
delete_presence(From, To) ->
LFrom = jid:tolower(From),
LTo = jid:tolower(jid:remove_resource(To)),
case mod_pubsub:get_items(LTo, ?NS_MIX_NODES_PRESENCE) of
Items when is_list(Items) ->
lists:foreach(
fun(#pubsub_item{modification = {_, LJID},
itemid = {ItemID, _}}) when LJID == LFrom ->
delete_item(From, To, ?NS_MIX_NODES_PRESENCE, ItemID);
(_) ->
ok
end, Items);
_ ->
ok
end.
delete_participant(From, To) ->
LFrom = jid:tolower(jid:remove_resource(From)),
ItemID = p1_sha:sha(jid:to_string(LFrom)),
delete_presence(From, To),
delete_item(From, To, ?NS_MIX_NODES_PARTICIPANTS, ItemID).
delete_item(From, To, Node, ItemID) ->
LTo = jid:tolower(jid:remove_resource(To)),
case mod_pubsub:delete_item(
LTo, Node, From, ItemID, true) of
{result, _} = Res ->
Res;
{error, _} = Err ->
case is_item_not_found(Err) of
true -> {result, []};
false -> Err
end
end.
is_item_not_found({error, ErrEl}) ->
case fxml:get_subtag_with_xmlns(
ErrEl, <<"item-not-found">>, ?NS_STANZAS) of
#xmlel{} -> true;
_ -> false
end.
is_not_subscribed({error, ErrEl}) ->
case fxml:get_subtag_with_xmlns(
ErrEl, <<"not-subscribed">>, ?NS_PUBSUB_ERRORS) of
#xmlel{} -> true;
_ -> false
end.
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(_) -> [host, iqdisc].
+1 -1
View File
@@ -373,7 +373,7 @@ init([Host, Opts]) ->
RoomShaper = gen_mod:get_opt(room_shaper, Opts,
fun(A) when is_atom(A) -> A end,
none),
ejabberd_router:register_route(MyHost),
ejabberd_router:register_route(MyHost, Host),
load_permanent_rooms(MyHost, Host,
{Access, AccessCreate, AccessAdmin, AccessPersistent},
HistorySize, RoomShaper),
+8 -5
View File
@@ -151,7 +151,7 @@ init([LServerS, Opts]) ->
try_start_loop(),
create_pool(),
ejabberd_router_multicast:register_route(LServerS),
ejabberd_router:register_route(LServiceS),
ejabberd_router:register_route(LServiceS, LServerS),
{ok,
#state{lservice = LServiceS, lserver = LServerS,
access = Access, service_limits = SLimits}}.
@@ -395,7 +395,7 @@ act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
perform(From, Packet, AAttrs, _,
{route_single, Group}) ->
[route_packet(From, ToUser, Packet, AAttrs,
Group#group.addresses)
Group#group.others, Group#group.addresses)
|| ToUser <- Group#group.dests];
perform(From, Packet, AAttrs, _,
{{route_multicast, JID, RLimits}, Group}) ->
@@ -634,13 +634,13 @@ decide_action_group(Group) ->
%%% Route packet
%%%-------------------------
route_packet(From, ToDest, Packet, AAttrs, Addresses) ->
route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
Dests = case ToDest#dest.type of
<<"bcc">> -> [];
_ -> [ToDest]
end,
route_packet2(From, ToDest#dest.jid_string, Dests,
Packet, AAttrs, Addresses).
Packet, AAttrs, {Others, Addresses}).
route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
Addresses, Limits) ->
@@ -666,6 +666,8 @@ route_packet2(From, ToS, Dests, Packet, AAttrs,
ToJID = stj(ToS),
ejabberd_router:route(From, ToJID, Packet2).
append_dests(_Dests, {Others, Addresses}) ->
Addresses++Others;
append_dests([], Addresses) -> Addresses;
append_dests([Dest | Dests], Addresses) ->
append_dests(Dests, [Dest#dest.full_xml | Addresses]).
@@ -912,8 +914,9 @@ received_awaiter(JID, Waiter, LServiceS) ->
From = Waiter#waiter.sender,
Packet = Waiter#waiter.packet,
AAttrs = Waiter#waiter.aattrs,
Others = Group#group.others,
Addresses = Waiter#waiter.addresses,
[route_packet(From, ToUser, Packet, AAttrs, Addresses)
[route_packet(From, ToUser, Packet, AAttrs, Others, Addresses)
|| ToUser <- Group#group.dests];
true ->
send_query_info(RServer, LServiceS),
+49 -68
View File
@@ -25,6 +25,8 @@
-module(mod_offline).
-compile([{parse_transform, ejabberd_sql_pt}]).
-author('alexey@process-one.net').
-protocol({xep, 13, '1.2'}).
@@ -38,8 +40,6 @@
-behaviour(gen_mod).
-export([count_offline_messages/2]).
-export([start/2,
start_link/2,
stop/1,
@@ -59,6 +59,7 @@
import/3,
export/1,
get_queue_length/2,
count_offline_messages/2,
get_offline_els/2,
webadmin_page/3,
webadmin_user/4,
@@ -68,6 +69,8 @@
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
-deprecated({get_queue_length,2}).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -79,6 +82,8 @@
-include("mod_offline.hrl").
-include("ejabberd_sql_pt.hrl").
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
@@ -337,9 +342,15 @@ get_sm_items(_Acc, #jid{luser = U, lserver = S, lresource = R} = JID,
get_sm_items(Acc, _From, _To, _Node, _Lang) ->
Acc.
get_info(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S},
?NS_FLEX_OFFLINE, _Lang) ->
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
N = jlib:integer_to_binary(count_offline_messages(U, S)),
case ejabberd_sm:get_session_pid(U, S, R) of
Pid when is_pid(Pid) ->
Pid ! dont_ask_offline;
none ->
ok
end,
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"result">>}],
@@ -686,13 +697,10 @@ pop_offline_messages(Ls, LUser, LServer, mnesia) ->
_ -> Ls
end;
pop_offline_messages(Ls, LUser, LServer, odbc) ->
EUser = ejabberd_odbc:escape(LUser),
case odbc_queries:get_and_del_spool_msg_t(LServer,
EUser)
of
{atomic, {selected, [<<"username">>, <<"xml">>], Rs}} ->
case odbc_queries:get_and_del_spool_msg_t(LServer, LUser) of
{atomic, {selected, Rs}} ->
Ls ++
lists:flatmap(fun ([_, XML]) ->
lists:flatmap(fun ({_, XML}) ->
case fxml_stream:parse_element(XML) of
{error, _Reason} ->
[];
@@ -811,8 +819,7 @@ remove_user(LUser, LServer, mnesia) ->
F = fun () -> mnesia:delete({offline_msg, US}) end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_spool_msg(LServer, Username);
odbc_queries:del_spool_msg(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(offline_msg,
<<"us">>, {LUser, LServer})}.
@@ -883,13 +890,13 @@ get_offline_els(LUser, LServer, DBType)
jlib:replace_from_to(From, To, Packet)
end, Msgs);
get_offline_els(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(LServer,
[<<"select xml from spool where username='">>,
Username, <<"' order by seq;">>]) of
{selected, [<<"xml">>], Rs} ->
case catch ejabberd_odbc:sql_query(
LServer,
?SQL("select @(xml)s from spool where "
"username=%(LUser)s order by seq")) of
{selected, Rs} ->
lists:flatmap(
fun([XML]) ->
fun({XML}) ->
case fxml_stream:parse_element(XML) of
#xmlel{} = El ->
case offline_msg_to_route(LServer, El) of
@@ -1050,20 +1057,20 @@ read_all_msgs(LUser, LServer, riak) ->
[]
end;
read_all_msgs(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(LServer,
[<<"select xml from spool where username='">>,
Username, <<"' order by seq;">>])
of
{selected, [<<"xml">>], Rs} ->
lists:flatmap(fun ([XML]) ->
case fxml_stream:parse_element(XML) of
{error, _Reason} -> [];
El -> [El]
end
end,
Rs);
_ -> []
case catch ejabberd_odbc:sql_query(
LServer,
?SQL("select @(xml)s from spool where "
"username=%(LUser)s order by seq")) of
{selected, Rs} ->
lists:flatmap(
fun({XML}) ->
case fxml_stream:parse_element(XML) of
{error, _Reason} -> [];
El -> [El]
end
end,
Rs);
_ -> []
end.
format_user_queue(Msgs, DBType) when DBType == mnesia; DBType == riak ->
@@ -1246,30 +1253,7 @@ us_to_list({User, Server}) ->
jid:to_string({User, Server, <<"">>}).
get_queue_length(LUser, LServer) ->
get_queue_length(LUser, LServer,
gen_mod:db_type(LServer, ?MODULE)).
get_queue_length(LUser, LServer, mnesia) ->
length(mnesia:dirty_read({offline_msg,
{LUser, LServer}}));
get_queue_length(LUser, LServer, riak) ->
case ejabberd_riak:count_by_index(offline_msg,
<<"us">>, {LUser, LServer}) of
{ok, N} ->
N;
_ ->
0
end;
get_queue_length(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch ejabberd_odbc:sql_query(LServer,
[<<"select count(*) from spool where username='">>,
Username, <<"';">>])
of
{selected, [_], [[SCount]]} ->
jlib:binary_to_integer(SCount);
_ -> 0
end.
count_offline_messages(LUser, LServer).
get_messages_subset(User, Host, MsgsAll, DBType) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
@@ -1311,7 +1295,7 @@ get_messages_subset2(Max, Length, MsgsAll, odbc) ->
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
webadmin_user(Acc, User, Server, Lang) ->
QueueLen = get_queue_length(jid:nodeprep(User),
QueueLen = count_offline_messages(jid:nodeprep(User),
jid:nameprep(Server)),
FQueueLen = [?AC(<<"queue/">>,
(iolist_to_binary(integer_to_list(QueueLen))))],
@@ -1342,8 +1326,7 @@ delete_all_msgs(LUser, LServer, riak) ->
<<"us">>, {LUser, LServer}),
{atomic, Res};
delete_all_msgs(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_spool_msg(LServer, Username),
odbc_queries:del_spool_msg(LServer, LUser),
{atomic, ok}.
webadmin_user_parse_query(_, <<"removealloffline">>,
@@ -1379,15 +1362,13 @@ count_offline_messages(LUser, LServer, mnesia) ->
_ -> 0
end;
count_offline_messages(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:count_records_where(LServer,
<<"spool">>,
<<"where username='",
Username/binary, "'">>)
of
{selected, [_], [[Res]]} ->
jlib:binary_to_integer(Res);
_ -> 0
case catch ejabberd_odbc:sql_query(
LServer,
?SQL("select @(count(*))d from spool "
"where username=%(LUser)s")) of
{selected, [{Res}]} ->
Res;
_ -> 0
end;
count_offline_messages(LUser, LServer, riak) ->
case ejabberd_riak:count_by_index(
+49 -119
View File
@@ -181,16 +181,14 @@ process_lists_get(LUser, LServer, _Active, riak) ->
error
end;
process_lists_get(LUser, LServer, _Active, odbc) ->
Default = case catch sql_get_default_privacy_list(LUser,
LServer)
of
{selected, [<<"name">>], []} -> none;
{selected, [<<"name">>], [[DefName]]} -> DefName;
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
{selected, []} -> none;
{selected, [{DefName}]} -> DefName;
_ -> none
end,
case catch sql_get_privacy_list_names(LUser, LServer) of
{selected, [<<"name">>], Names} ->
LItems = lists:map(fun ([N]) ->
{selected, Names} ->
LItems = lists:map(fun ({N}) ->
#xmlel{name = <<"list">>,
attrs = [{<<"name">>, N}],
children = []}
@@ -242,17 +240,11 @@ process_list_get(LUser, LServer, Name, riak) ->
error
end;
process_list_get(LUser, LServer, Name, odbc) ->
case catch sql_get_privacy_list_id(LUser, LServer, Name)
of
{selected, [<<"id">>], []} -> not_found;
{selected, [<<"id">>], [[ID]]} ->
case catch sql_get_privacy_list_data_by_id(ID, LServer)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems} ->
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
{selected, []} -> not_found;
{selected, [{ID}]} ->
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
{selected, RItems} ->
lists:flatmap(fun raw_to_item/1, RItems);
_ -> error
end;
@@ -405,9 +397,9 @@ process_default_set(LUser, LServer, {value, Name},
odbc) ->
F = fun () ->
case sql_get_privacy_list_names_t(LUser) of
{selected, [<<"name">>], []} -> not_found;
{selected, [<<"name">>], Names} ->
case lists:member([Name], Names) of
{selected, []} -> not_found;
{selected, Names} ->
case lists:member({Name}, Names) of
true -> sql_set_default_privacy_list(LUser, Name), ok;
false -> not_found
end
@@ -473,17 +465,11 @@ process_active_set(LUser, LServer, Name, riak) ->
error
end;
process_active_set(LUser, LServer, Name, odbc) ->
case catch sql_get_privacy_list_id(LUser, LServer, Name)
of
{selected, [<<"id">>], []} -> error;
{selected, [<<"id">>], [[ID]]} ->
case catch sql_get_privacy_list_data_by_id(ID, LServer)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems} ->
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
{selected, []} -> error;
{selected, [{ID}]} ->
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
{selected, RItems} ->
lists:flatmap(fun raw_to_item/1, RItems);
_ -> error
end;
@@ -520,9 +506,9 @@ remove_privacy_list(LUser, LServer, Name, riak) ->
remove_privacy_list(LUser, LServer, Name, odbc) ->
F = fun () ->
case sql_get_default_privacy_list_t(LUser) of
{selected, [<<"name">>], []} ->
{selected, []} ->
sql_remove_privacy_list(LUser, Name), ok;
{selected, [<<"name">>], [[Default]]} ->
{selected, [{Default}]} ->
if Name == Default -> conflict;
true -> sql_remove_privacy_list(LUser, Name), ok
end
@@ -590,12 +576,12 @@ set_privacy_list(LUser, LServer, Name, List, odbc) ->
RItems = lists:map(fun item_to_raw/1, List),
F = fun () ->
ID = case sql_get_privacy_list_id_t(LUser, Name) of
{selected, [<<"id">>], []} ->
{selected, []} ->
sql_add_privacy_list(LUser, Name),
{selected, [<<"id">>], [[I]]} =
{selected, [{I}]} =
sql_get_privacy_list_id_t(LUser, Name),
I;
{selected, [<<"id">>], [[I]]} -> I
{selected, [{I}]} -> I
end,
sql_set_privacy_list(ID, RItems),
ok
@@ -785,16 +771,11 @@ get_user_list(_, LUser, LServer, riak) ->
get_user_list(_, LUser, LServer, odbc) ->
case catch sql_get_default_privacy_list(LUser, LServer)
of
{selected, [<<"name">>], []} -> {none, []};
{selected, [<<"name">>], [[Default]]} ->
{selected, []} -> {none, []};
{selected, [{Default}]} ->
case catch 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} ->
Default) of
{selected, RItems} ->
{Default, lists:flatmap(fun raw_to_item/1, RItems)};
_ -> {none, []}
end;
@@ -822,26 +803,21 @@ get_user_lists(LUser, LServer, riak) ->
end;
get_user_lists(LUser, LServer, odbc) ->
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
{selected, [<<"name">>], []} ->
{selected, []} ->
none;
{selected, [<<"name">>], [[DefName]]} ->
{selected, [{DefName}]} ->
DefName;
_ ->
none
end,
case catch sql_get_privacy_list_names(LUser, LServer) of
{selected, [<<"name">>], Names} ->
{selected, Names} ->
Lists =
lists:flatmap(
fun([Name]) ->
fun({Name}) ->
case catch sql_get_privacy_list_data(
LUser, LServer, Name) of
{selected,
[<<"t">>, <<"value">>, <<"action">>,
<<"ord">>, <<"match_all">>, <<"match_iq">>,
<<"match_message">>, <<"match_presence_in">>,
<<"match_presence_out">>],
RItems} ->
{selected, RItems} ->
[{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
_ ->
[]
@@ -994,9 +970,9 @@ updated_list(_, #userlist{name = OldName} = Old,
true -> Old
end.
raw_to_item([SType, SValue, SAction, SOrder, SMatchAll,
SMatchIQ, SMatchMessage, SMatchPresenceIn,
SMatchPresenceOut] = Row) ->
raw_to_item({SType, SValue, SAction, Order, MatchAll,
MatchIQ, MatchMessage, MatchPresenceIn,
MatchPresenceOut} = Row) ->
try
{Type, Value} = case SType of
<<"n">> -> {none, none};
@@ -1018,12 +994,6 @@ raw_to_item([SType, SValue, SAction, SOrder, SMatchAll,
<<"a">> -> allow;
<<"d">> -> deny
end,
Order = jlib:binary_to_integer(SOrder),
MatchAll = ejabberd_odbc:to_bool(SMatchAll),
MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
[#listitem{type = Type, value = Value, action = Action,
order = Order, match_all = MatchAll, match_iq = MatchIQ,
match_message = MatchMessage,
@@ -1057,58 +1027,29 @@ item_to_raw(#listitem{type = Type, value = Value,
allow -> <<"a">>;
deny -> <<"d">>
end,
SOrder = iolist_to_binary(integer_to_list(Order)),
SMatchAll = if MatchAll -> <<"1">>;
true -> <<"0">>
end,
SMatchIQ = if MatchIQ -> <<"1">>;
true -> <<"0">>
end,
SMatchMessage = if MatchMessage -> <<"1">>;
true -> <<"0">>
end,
SMatchPresenceIn = if MatchPresenceIn -> <<"1">>;
true -> <<"0">>
end,
SMatchPresenceOut = if MatchPresenceOut -> <<"1">>;
true -> <<"0">>
end,
[SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
sql_get_default_privacy_list(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:get_default_privacy_list(LServer,
Username).
odbc_queries:get_default_privacy_list(LServer, LUser).
sql_get_default_privacy_list_t(LUser) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:get_default_privacy_list_t(Username).
odbc_queries:get_default_privacy_list_t(LUser).
sql_get_privacy_list_names(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:get_privacy_list_names(LServer, Username).
odbc_queries:get_privacy_list_names(LServer, LUser).
sql_get_privacy_list_names_t(LUser) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:get_privacy_list_names_t(Username).
odbc_queries:get_privacy_list_names_t(LUser).
sql_get_privacy_list_id(LUser, LServer, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:get_privacy_list_id(LServer, Username,
SName).
odbc_queries:get_privacy_list_id(LServer, LUser, Name).
sql_get_privacy_list_id_t(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:get_privacy_list_id_t(Username, SName).
odbc_queries:get_privacy_list_id_t(LUser, Name).
sql_get_privacy_list_data(LUser, LServer, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:get_privacy_list_data(LServer, Username,
SName).
odbc_queries:get_privacy_list_data(LServer, LUser, Name).
sql_get_privacy_list_data_t(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
@@ -1122,33 +1063,22 @@ sql_get_privacy_list_data_by_id_t(ID) ->
odbc_queries:get_privacy_list_data_by_id_t(ID).
sql_set_default_privacy_list(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:set_default_privacy_list(Username, SName).
odbc_queries:set_default_privacy_list(LUser, Name).
sql_unset_default_privacy_list(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:unset_default_privacy_list(LServer,
Username).
odbc_queries:unset_default_privacy_list(LServer, LUser).
sql_remove_privacy_list(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:remove_privacy_list(Username, SName).
odbc_queries:remove_privacy_list(LUser, Name).
sql_add_privacy_list(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:add_privacy_list(Username, SName).
odbc_queries:add_privacy_list(LUser, Name).
sql_set_privacy_list(ID, RItems) ->
odbc_queries:set_privacy_list(ID, RItems).
sql_del_privacy_lists(LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
Server = ejabberd_odbc:escape(LServer),
odbc_queries:del_privacy_lists(LServer, Server,
Username).
odbc_queries:del_privacy_lists(LServer, LUser).
update_table() ->
Fields = record_info(fields, privacy),
+8 -16
View File
@@ -152,11 +152,8 @@ set_data(LUser, LServer, {XmlNS, Xmlel}, mnesia) ->
{LUser, LServer, XmlNS},
xml = Xmlel});
set_data(LUser, LServer, {XMLNS, El}, odbc) ->
Username = ejabberd_odbc:escape(LUser),
LXMLNS = ejabberd_odbc:escape(XMLNS),
SData = ejabberd_odbc:escape(fxml:element_to_binary(El)),
odbc_queries:set_private_data(LServer, Username, LXMLNS,
SData);
SData = fxml:element_to_binary(El),
odbc_queries:set_private_data(LServer, LUser, XMLNS, SData);
set_data(LUser, LServer, {XMLNS, El}, riak) ->
ejabberd_riak:put(#private_storage{usns = {LUser, LServer, XMLNS},
xml = El},
@@ -184,12 +181,10 @@ get_data(LUser, LServer, mnesia,
end;
get_data(LUser, LServer, odbc, [{XMLNS, El} | Els],
Res) ->
Username = ejabberd_odbc:escape(LUser),
LXMLNS = ejabberd_odbc:escape(XMLNS),
case catch odbc_queries:get_private_data(LServer,
Username, LXMLNS)
LUser, XMLNS)
of
{selected, [<<"data">>], [[SData]]} ->
{selected, [{SData}]} ->
case fxml_stream:parse_element(SData) of
Data when is_record(Data, xmlel) ->
get_data(LUser, LServer, odbc, Els, [Data | Res])
@@ -217,11 +212,10 @@ get_all_data(LUser, LServer, mnesia) ->
xml = '$1'},
[], ['$1']}]));
get_all_data(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_private_data(LServer, Username) of
{selected, [<<"namespace">>, <<"data">>], Res} ->
case catch odbc_queries:get_private_data(LServer, LUser) of
{selected, Res} ->
lists:flatmap(
fun([_, SData]) ->
fun({_, SData}) ->
case fxml_stream:parse_element(SData) of
#xmlel{} = El ->
[El];
@@ -269,9 +263,7 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_user_private_storage(LServer,
Username);
odbc_queries:del_user_private_storage(LServer, LUser);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(private_storage,
<<"us">>, {LUser, LServer})}.
+1 -1
View File
@@ -63,7 +63,7 @@ start_link(Host, Opts) ->
init([Host, Opts]) ->
State = parse_options(Host, Opts),
ejabberd_router:register_route(State#state.myhost),
ejabberd_router:register_route(State#state.myhost, Host),
{ok, State}.
terminate(_Reason, #state{myhost = MyHost}) ->
+1 -1
View File
@@ -153,7 +153,7 @@ wait_for_auth(Packet,
#state{socket = Socket, host = Host} = StateData) ->
case mod_proxy65_lib:unpack_auth_request(Packet) of
{User, Pass} ->
Result = ejabberd_auth:check_password(User, Host, Pass),
Result = ejabberd_auth:check_password(User, <<"">>, Host, Pass),
gen_tcp:send(Socket,
mod_proxy65_lib:make_auth_reply(Result)),
case Result of
+45 -33
View File
@@ -63,7 +63,7 @@
%% exports for console debug manual use
-export([create_node/5, create_node/7, delete_node/3,
subscribe_node/5, unsubscribe_node/5, publish_item/6,
delete_item/4, send_items/7, get_items/2, get_item/3,
delete_item/4, delete_item/5, send_items/7, get_items/2, get_item/3,
get_cached_item/2, get_configure/5, set_configure/5,
tree_action/3, node_action/4, node_call/4]).
@@ -241,6 +241,7 @@ stop(Host) ->
init([ServerHost, Opts]) ->
?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
ejabberd_router:register_route(Host, ServerHost),
Access = gen_mod:get_opt(access_createnode, Opts,
fun(A) when is_atom(A) -> A end, all),
PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts,
@@ -256,22 +257,26 @@ init([ServerHost, Opts]) ->
DefaultNodeCfg = gen_mod:get_opt(default_node_config, Opts,
fun(A) when is_list(A) -> filter_node_options(A) end, []),
pubsub_index:init(Host, ServerHost, Opts),
ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
mnesia:create_table(pubsub_last_item,
[{ram_copies, [node()]},
{attributes, record_info(fields, pubsub_last_item)}]),
mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {nodetree, NodeTree}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {plugins, Plugins}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {last_item_cache, LastItemCache}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_items_node, MaxItemsNode}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_subscriptions_node, MaxSubsNode}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {default_node_config, DefaultNodeCfg}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {pep_mapping, PepMapping}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {access, Access}),
lists:foreach(
fun(H) ->
T = gen_mod:get_module_proc(H, config),
ets:new(T, [set, named_table]),
ets:insert(T, {nodetree, NodeTree}),
ets:insert(T, {plugins, Plugins}),
ets:insert(T, {last_item_cache, LastItemCache}),
ets:insert(T, {max_items_node, MaxItemsNode}),
ets:insert(T, {max_subscriptions_node, MaxSubsNode}),
ets:insert(T, {default_node_config, DefaultNodeCfg}),
ets:insert(T, {pep_mapping, PepMapping}),
ets:insert(T, {ignore_pep_from_offline, PepOffline}),
ets:insert(T, {host, Host}),
ets:insert(T, {access, Access})
end, [Host, ServerHost]),
ejabberd_hooks:add(sm_remove_connection_hook, ServerHost,
?MODULE, on_user_offline, 75),
ejabberd_hooks:add(disco_local_identity, ServerHost,
@@ -309,7 +314,6 @@ init([ServerHost, Opts]) ->
false ->
ok
end,
ejabberd_router:register_route(Host),
pubsub_migrate:update_node_database(Host, ServerHost),
pubsub_migrate:update_state_database(Host, ServerHost),
pubsub_migrate:update_lastitem_database(Host, ServerHost),
@@ -504,7 +508,7 @@ disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
-> [binary(),...]
).
disco_local_features(Acc, _From, To, <<>>, _Lang) ->
Host = To#jid.lserver,
Host = host(To#jid.lserver),
Feats = case Acc of
{result, I} -> I;
_ -> []
@@ -873,7 +877,6 @@ handle_info(_Info, State) ->
%% @private
terminate(_Reason,
#state{host = Host, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) ->
ejabberd_router:unregister_route(Host),
case lists:member(?PEPNODE, Plugins) of
true ->
ejabberd_hooks:delete(caps_add, ServerHost,
@@ -918,7 +921,8 @@ terminate(_Reason,
Pid ->
Pid ! stop
end,
terminate_plugins(Host, ServerHost, Plugins, TreePlugin).
terminate_plugins(Host, ServerHost, Plugins, TreePlugin),
ejabberd_router:unregister_route(Host).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -1775,6 +1779,20 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
%%<li>nodetree create_node checks if nodeid already exists</li>
%%<li>node plugin create_node just sets default affiliation/subscription</li>
%%</ul>
-spec(create_node/5 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
Node :: <<>> | mod_pubsub:nodeId(),
Owner :: jid(),
Type :: binary())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
create_node(Host, ServerHost, Node, Owner, Type) ->
create_node(Host, ServerHost, Node, Owner, Type, all, []).
-spec(create_node/7 ::
(
Host :: mod_pubsub:host(),
@@ -1788,8 +1806,6 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
%%%
| {error, xmlel()}
).
create_node(Host, ServerHost, Node, Owner, Type) ->
create_node(Host, ServerHost, Node, Owner, Type, all, []).
create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
case lists:member(<<"instant-nodes">>, plugin_features(Host, Type)) of
true ->
@@ -3629,7 +3645,7 @@ get_option(Options, Var, Def) ->
end.
node_options(Host, Type) ->
case config(serverhost(Host), default_node_config) of
case config(Host, default_node_config) of
undefined -> node_plugin_options(Host, Type);
[] -> node_plugin_options(Host, Type);
Config -> Config
@@ -3941,22 +3957,16 @@ set_xoption(Host, [{<<"pubsub#itemreply">>, [Val]} | Opts], NewOpts) ->
set_xoption(Host, [_ | Opts], NewOpts) ->
set_xoption(Host, Opts, NewOpts).
get_max_items_node({_, ServerHost, _}) ->
get_max_items_node(ServerHost);
get_max_items_node(Host) ->
config(serverhost(Host), max_items_node, undefined).
config(Host, max_items_node, undefined).
get_max_subscriptions_node({_, ServerHost, _}) ->
get_max_subscriptions_node(ServerHost);
get_max_subscriptions_node(Host) ->
config(serverhost(Host), max_subscriptions_node, undefined).
config(Host, max_subscriptions_node, undefined).
%%%% last item cache handling
is_last_item_cache_enabled({_, ServerHost, _}) ->
is_last_item_cache_enabled(ServerHost);
is_last_item_cache_enabled(Host) ->
config(serverhost(Host), last_item_cache, false).
config(Host, last_item_cache, false).
set_cached_item({_, ServerHost, _}, Nidx, ItemId, Publisher, Payload) ->
set_cached_item(ServerHost, Nidx, ItemId, Publisher, Payload);
@@ -4007,13 +4017,12 @@ host(ServerHost) ->
config(ServerHost, host, <<"pubsub.", ServerHost/binary>>).
serverhost({_U, ServerHost, _R})->
ServerHost;
serverhost(ServerHost);
serverhost(Host) ->
[_, ServerHost] = binary:split(Host, <<".">>),
ServerHost.
ejabberd_router:host_of_route(Host).
tree(Host) ->
case config(serverhost(Host), nodetree) of
case config(Host, nodetree) of
undefined -> tree(Host, ?STDTREE);
Tree -> Tree
end.
@@ -4035,7 +4044,7 @@ plugin(Host, Name) ->
end.
plugins(Host) ->
case config(serverhost(Host), plugins) of
case config(Host, plugins) of
undefined -> [?STDNODE];
[] -> [?STDNODE];
Plugins -> Plugins
@@ -4050,6 +4059,9 @@ subscription_plugin(Host) ->
config(ServerHost, Key) ->
config(ServerHost, Key, undefined).
config({_User, Host, _Resource}, Key, Default) ->
config(Host, Key, Default);
config(ServerHost, Key, Default) ->
case catch ets:lookup(gen_mod:get_module_proc(ServerHost, config), Key) of
[{Key, Value}] -> Value;
+1 -1
View File
@@ -438,7 +438,7 @@ check_account_exists(Username, Host) ->
end.
check_password(Username, Host, Password) ->
case ejabberd_auth:check_password(Username, Host,
case ejabberd_auth:check_password(Username, <<"">>, Host,
Password)
of
true -> password_correct;
+95 -118
View File
@@ -203,11 +203,9 @@ read_roster_version(LUser, LServer, mnesia) ->
[] -> error
end;
read_roster_version(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case odbc_queries:get_roster_version(LServer, Username)
of
{selected, [<<"version">>], [[Version]]} -> Version;
{selected, [<<"version">>], []} -> error
case odbc_queries:get_roster_version(LServer, LUser) of
{selected, [{Version}]} -> Version;
{selected, []} -> error
end;
read_roster_version(LServer, LUser, riak) ->
case ejabberd_riak:get(roster_version, roster_version_schema(),
@@ -369,46 +367,37 @@ get_roster(LUser, LServer, riak) ->
_Err -> []
end;
get_roster(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_roster(LServer, Username) of
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
Items}
when is_list(Items) ->
JIDGroups = case catch
odbc_queries:get_roster_jid_groups(LServer,
Username)
of
{selected, [<<"jid">>, <<"grp">>], JGrps}
when is_list(JGrps) ->
JGrps;
_ -> []
end,
GroupsDict = lists:foldl(fun ([J, G], Acc) ->
dict:append(J, G, Acc)
end,
dict:new(), JIDGroups),
RItems = lists:flatmap(fun (I) ->
case raw_to_record(LServer, I) of
%% Bad JID in database:
error -> [];
R ->
SJID =
jid:to_string(R#roster.jid),
Groups = case dict:find(SJID,
GroupsDict)
of
{ok, Gs} -> Gs;
error -> []
end,
[R#roster{groups = Groups}]
end
end,
Items),
RItems;
_ -> []
case catch odbc_queries:get_roster(LServer, LUser) of
{selected, Items} when is_list(Items) ->
JIDGroups = case catch odbc_queries:get_roster_jid_groups(
LServer, LUser) of
{selected, JGrps}
when is_list(JGrps) ->
JGrps;
_ -> []
end,
GroupsDict = lists:foldl(fun({J, G}, Acc) ->
dict:append(J, G, Acc)
end,
dict:new(), JIDGroups),
RItems =
lists:flatmap(
fun(I) ->
case raw_to_record(LServer, I) of
%% Bad JID in database:
error -> [];
R ->
SJID = jid:to_string(R#roster.jid),
Groups = case dict:find(SJID, GroupsDict) of
{ok, Gs} -> Gs;
error -> []
end,
[R#roster{groups = Groups}]
end
end,
Items),
RItems;
_ -> []
end.
set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
@@ -460,14 +449,8 @@ get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
xs = []}
end;
get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
Res} =
odbc_queries:get_roster_by_jid(LServer, Username, SJID),
{selected, Res} =
odbc_queries:get_roster_by_jid(LServer, LUser, jid:to_string(LJID)),
case Res of
[] ->
#roster{usj = {LUser, LServer, LJID},
@@ -662,14 +645,8 @@ get_subscription_lists(_, LUser, LServer, mnesia) ->
_ -> []
end;
get_subscription_lists(_, LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_roster(LServer, Username) of
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
Items}
when is_list(Items) ->
case catch odbc_queries:get_roster(LServer, LUser) of
{selected, Items} when is_list(Items) ->
lists:map(fun(I) -> raw_to_record(LServer, I) end, Items);
_ -> []
end;
@@ -711,12 +688,9 @@ roster_subscribe_t(LUser, LServer, LJID, Item) ->
roster_subscribe_t(_LUser, _LServer, _LJID, Item,
mnesia) ->
mnesia:write(Item);
roster_subscribe_t(LUser, LServer, LJID, Item, odbc) ->
ItemVals = record_to_string(Item),
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
odbc_queries:roster_subscribe(LServer, Username, SJID,
ItemVals);
roster_subscribe_t(_LUser, _LServer, _LJID, Item, odbc) ->
ItemVals = record_to_row(Item),
odbc_queries:roster_subscribe(ItemVals);
roster_subscribe_t(LUser, LServer, _LJID, Item, riak) ->
ejabberd_riak:put(Item, roster_schema(),
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
@@ -750,30 +724,18 @@ get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
end;
get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
odbc) ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
case odbc_queries:get_roster_by_jid(LServer, Username,
SJID)
of
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
[I]} ->
R = raw_to_record(LServer, I),
Groups = case odbc_queries:get_roster_groups(LServer,
Username, SJID)
of
{selected, [<<"grp">>], JGrps} when is_list(JGrps) ->
[JGrp || [JGrp] <- JGrps];
_ -> []
end,
R#roster{groups = Groups};
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
[]} ->
SJID = jid:to_string(LJID),
case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
{selected, [I]} ->
R = raw_to_record(LServer, I),
Groups =
case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
{selected, JGrps} when is_list(JGrps) ->
[JGrp || {JGrp} <- JGrps];
_ -> []
end,
R#roster{groups = Groups};
{selected, []} ->
#roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer}, jid = LJID}
end;
@@ -995,8 +957,7 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_user_roster_t(LServer, Username),
odbc_queries:del_user_roster_t(LServer, LUser),
ok;
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
@@ -1064,11 +1025,10 @@ update_roster_t(_LUser, _LServer, _LJID, Item,
mnesia) ->
mnesia:write(Item);
update_roster_t(LUser, LServer, LJID, Item, odbc) ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
ItemVals = record_to_string(Item),
ItemGroups = groups_to_string(Item),
odbc_queries:update_roster(LServer, Username, SJID, ItemVals,
SJID = jid:to_string(LJID),
ItemVals = record_to_row(Item),
ItemGroups = Item#roster.groups,
odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
ItemGroups);
update_roster_t(LUser, LServer, _LJID, Item, riak) ->
ejabberd_riak:put(Item, roster_schema(),
@@ -1081,9 +1041,8 @@ del_roster_t(LUser, LServer, LJID) ->
del_roster_t(LUser, LServer, LJID, mnesia) ->
mnesia:delete({roster, {LUser, LServer, LJID}});
del_roster_t(LUser, LServer, LJID, odbc) ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
odbc_queries:del_roster(LServer, Username, SJID);
SJID = jid:to_string(LJID),
odbc_queries:del_roster(LServer, LUser, SJID);
del_roster_t(LUser, LServer, LJID, riak) ->
ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
@@ -1184,14 +1143,8 @@ get_in_pending_subscriptions(Ls, User, Server, odbc) ->
JID = jid:make(User, Server, <<"">>),
LUser = JID#jid.luser,
LServer = JID#jid.lserver,
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_roster(LServer, Username) of
{selected,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
Items}
when is_list(Items) ->
case catch odbc_queries:get_roster(LServer, LUser) of
{selected, Items} when is_list(Items) ->
Ls ++
lists:map(fun (R) ->
Message = R#roster.askmessage,
@@ -1243,12 +1196,9 @@ read_subscription_and_groups(LUser, LServer, LJID,
end;
read_subscription_and_groups(LUser, LServer, LJID,
odbc) ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
case catch odbc_queries:get_subscription(LServer,
Username, SJID)
of
{selected, [<<"subscription">>], [[SSubscription]]} ->
SJID = jid:to_string(LJID),
case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
{selected, [{SSubscription}]} ->
Subscription = case SSubscription of
<<"B">> -> both;
<<"T">> -> to;
@@ -1256,11 +1206,11 @@ read_subscription_and_groups(LUser, LServer, LJID,
_ -> none
end,
Groups = case catch
odbc_queries:get_rostergroup_by_jid(LServer, Username,
odbc_queries:get_rostergroup_by_jid(LServer, LUser,
SJID)
of
{selected, [<<"grp">>], JGrps} when is_list(JGrps) ->
[JGrp || [JGrp] <- JGrps];
{selected, JGrps} when is_list(JGrps) ->
[JGrp || {JGrp} <- JGrps];
_ -> []
end,
{Subscription, Groups};
@@ -1297,6 +1247,12 @@ get_jid_info(_, User, Server, JID) ->
raw_to_record(LServer,
[User, SJID, Nick, SSubscription, SAsk, SAskMessage,
_SServer, _SSubscribe, _SType]) ->
raw_to_record(LServer,
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
_SServer, _SSubscribe, _SType});
raw_to_record(LServer,
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
_SServer, _SSubscribe, _SType}) ->
case jid:from_string(SJID) of
error -> error;
JID ->
@@ -1346,6 +1302,27 @@ record_to_string(#roster{us = {User, _Server},
[Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
<<"N">>, <<"">>, <<"item">>].
record_to_row(
#roster{us = {LUser, _LServer},
jid = JID, name = Name, subscription = Subscription,
ask = Ask, askmessage = AskMessage}) ->
SJID = jid:to_string(jid:tolower(JID)),
SSubscription = case Subscription of
both -> <<"B">>;
to -> <<"T">>;
from -> <<"F">>;
none -> <<"N">>
end,
SAsk = case Ask of
subscribe -> <<"S">>;
unsubscribe -> <<"U">>;
both -> <<"B">>;
out -> <<"O">>;
in -> <<"I">>;
none -> <<"N">>
end,
{LUser, SJID, Name, SSubscription, SAsk, AskMessage}.
groups_to_string(#roster{us = {User, _Server},
jid = JID, groups = Groups}) ->
Username = ejabberd_odbc:escape(User),
File diff suppressed because it is too large Load Diff
+23 -44
View File
@@ -25,6 +25,8 @@
-module(mod_vcard).
-compile([{parse_transform, ejabberd_sql_pt}]).
-author('alexey@process-one.net').
-protocol({xep, 54, '1.2'}).
@@ -39,6 +41,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
-include("jlib.hrl").
@@ -102,7 +105,7 @@ init(Host, ServerHost, Search) ->
case Search of
false -> loop(Host, ServerHost);
_ ->
ejabberd_router:register_route(Host),
ejabberd_router:register_route(Host, ServerHost),
loop(Host, ServerHost)
end.
@@ -212,14 +215,13 @@ get_vcard(LUser, LServer, mnesia) ->
{aborted, _Reason} -> error
end;
get_vcard(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_vcard(LServer, Username) of
{selected, [<<"vcard">>], [[SVCARD]]} ->
case catch odbc_queries:get_vcard(LServer, LUser) of
{selected, [{SVCARD}]} ->
case fxml_stream:parse_element(SVCARD) of
{error, _Reason} -> error;
VCARD -> [VCARD]
end;
{selected, [<<"vcard">>], []} -> [];
{selected, []} -> [];
_ -> error
end;
get_vcard(LUser, LServer, riak) ->
@@ -336,39 +338,14 @@ set_vcard(User, LServer, VCARD) ->
{<<"orgunit">>, OrgUnit},
{<<"lorgunit">>, LOrgUnit}]}]);
odbc ->
Username = ejabberd_odbc:escape(User),
LUsername = ejabberd_odbc:escape(LUser),
SVCARD =
ejabberd_odbc:escape(fxml:element_to_binary(VCARD)),
SFN = ejabberd_odbc:escape(FN),
SLFN = ejabberd_odbc:escape(LFN),
SFamily = ejabberd_odbc:escape(Family),
SLFamily = ejabberd_odbc:escape(LFamily),
SGiven = ejabberd_odbc:escape(Given),
SLGiven = ejabberd_odbc:escape(LGiven),
SMiddle = ejabberd_odbc:escape(Middle),
SLMiddle = ejabberd_odbc:escape(LMiddle),
SNickname = ejabberd_odbc:escape(Nickname),
SLNickname = ejabberd_odbc:escape(LNickname),
SBDay = ejabberd_odbc:escape(BDay),
SLBDay = ejabberd_odbc:escape(LBDay),
SCTRY = ejabberd_odbc:escape(CTRY),
SLCTRY = ejabberd_odbc:escape(LCTRY),
SLocality = ejabberd_odbc:escape(Locality),
SLLocality = ejabberd_odbc:escape(LLocality),
SEMail = ejabberd_odbc:escape(EMail),
SLEMail = ejabberd_odbc:escape(LEMail),
SOrgName = ejabberd_odbc:escape(OrgName),
SLOrgName = ejabberd_odbc:escape(LOrgName),
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY,
SEMail, SFN, SFamily, SGiven, SLBDay,
SLCTRY, SLEMail, SLFN, SLFamily,
SLGiven, SLLocality, SLMiddle,
SLNickname, SLOrgName, SLOrgUnit,
SLocality, SMiddle, SNickname, SOrgName,
SOrgUnit, SVCARD, Username)
SVCARD = fxml:element_to_binary(VCARD),
odbc_queries:set_vcard(LServer, LUser, BDay, CTRY,
EMail, FN, Family, Given, LBDay,
LCTRY, LEMail, LFN, LFamily,
LGiven, LLocality, LMiddle,
LNickname, LOrgName, LOrgUnit,
Locality, Middle, Nickname, OrgName,
OrgUnit, SVCARD, User)
end,
ejabberd_hooks:run(vcard_set, LServer,
[LUser, LServer, VCARD])
@@ -929,12 +906,14 @@ remove_user(LUser, LServer, mnesia) ->
end,
mnesia:transaction(F);
remove_user(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
ejabberd_odbc:sql_transaction(LServer,
[[<<"delete from vcard where username='">>,
Username, <<"';">>],
[<<"delete from vcard_search where lusername='">>,
Username, <<"';">>]]);
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
ejabberd_odbc:sql_query_t(
?SQL("delete from vcard where username=%(LUser)s")),
ejabberd_odbc:sql_query_t(
?SQL("delete from vcard_search where lusername=%(LUser)s"))
end);
remove_user(LUser, LServer, riak) ->
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
+1 -1
View File
@@ -173,7 +173,7 @@ init([Host, Opts]) ->
State#state.password, State#state.tls_options),
case State#state.search of
true ->
ejabberd_router:register_route(State#state.myhost);
ejabberd_router:register_route(State#state.myhost, Host);
_ -> ok
end,
{ok, State}.
+29 -11
View File
@@ -697,12 +697,21 @@ get_items(Nidx, _From,
{selected, [_], [[C]]} -> C;
_ -> <<"0">>
end,
case catch
ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
AttrName, <<" ">>, Order, <<" limit ">>, jlib:i2l(Max), <<" ;">>])
of
Query = fun(mssql, _) ->
ejabberd_odbc:sql_query_t(
[<<"select top ">>, jlib:i2l(Max),
<<" itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
AttrName, <<" ">>, Order, <<";">>]);
(_, _) ->
ejabberd_odbc:sql_query_t(
[<<"select itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
AttrName, <<" ">>, Order, <<" limit ">>, jlib:i2l(Max), <<" ;">>])
end,
case catch ejabberd_odbc:sql_query_t(Query) of
{selected,
[<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} ->
case RItems of
@@ -760,11 +769,20 @@ get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM
end.
get_last_items(Nidx, _From, Count) ->
case catch
ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' order by modification desc limit ">>, jlib:i2l(Count), <<";">>])
of
Limit = jlib:i2l(Count),
Query = fun(mssql, _) ->
ejabberd_odbc:sql_query_t(
[<<"select top ">>, Limit,
<<" itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' order by modification desc ;">>]);
(_, _) ->
ejabberd_odbc:sql_query_t(
[<<"select itemid, publisher, creation, modification, payload "
"from pubsub_item where nodeid='">>, Nidx,
<<"' order by modification desc limit ">>, Limit, <<";">>])
end,
case catch ejabberd_odbc:sql_query_t(Query) of
{selected,
[<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} ->
{result, [raw_to_item(Nidx, RItem) || RItem <- RItems]};
+167
View File
@@ -0,0 +1,167 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(node_mix).
-behaviour(gen_pubsub_node).
%% API
-export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
-include("pubsub.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(Host, ServerHost, Opts) ->
node_flat:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) ->
node_flat:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, open},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{broadcast_all_resources, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"item-ids">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(Nidx, Owner) ->
node_flat:create_node(Nidx, Owner).
delete_node(Removed) ->
node_flat:delete_node(Removed).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_flat:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_flat:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(Nidx, Owner) ->
node_flat:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_flat:get_entity_affiliations(Host, Owner).
get_node_affiliations(Nidx) ->
node_flat:get_node_affiliations(Nidx).
get_affiliation(Nidx, Owner) ->
node_flat:get_affiliation(Nidx, Owner).
set_affiliation(Nidx, Owner, Affiliation) ->
node_flat:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) ->
node_flat:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(Nidx) ->
node_flat:get_node_subscriptions(Nidx).
get_subscriptions(Nidx, Owner) ->
node_flat:get_subscriptions(Nidx, Owner).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_flat:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_flat:get_pending_nodes(Host, Owner).
get_states(Nidx) ->
node_flat:get_states(Nidx).
get_state(Nidx, JID) ->
node_flat:get_state(Nidx, JID).
set_state(State) ->
node_flat:set_state(State).
get_items(Nidx, From, RSM) ->
node_flat:get_items(Nidx, From, RSM).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_flat:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(Nidx, ItemId) ->
node_flat:get_item(Nidx, ItemId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_flat:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) ->
node_flat:set_item(Item).
get_item_name(Host, Node, Id) ->
node_flat:get_item_name(Host, Node, Id).
node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) ->
node_flat:path_to_node(Path).
%%%===================================================================
%%% Internal functions
%%%===================================================================
+170
View File
@@ -0,0 +1,170 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2016, Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(node_mix_odbc).
-behaviour(gen_pubsub_node).
%% API
-export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1, get_entity_subscriptions_for_send_last/2]).
-include("pubsub.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(Host, ServerHost, Opts) ->
node_flat_odbc:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) ->
node_flat_odbc:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, open},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{broadcast_all_resources, true},
{presence_based_delivery, false}].
features() ->
[<<"create-nodes">>,
<<"delete-nodes">>,
<<"delete-items">>,
<<"instant-nodes">>,
<<"item-ids">>,
<<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_flat_odbc:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(Nidx, Owner) ->
node_flat_odbc:create_node(Nidx, Owner).
delete_node(Removed) ->
node_flat_odbc:delete_node(Removed).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_flat_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_flat_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_flat_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_flat_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_flat_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
purge_node(Nidx, Owner) ->
node_flat_odbc:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) ->
node_flat_odbc:get_entity_affiliations(Host, Owner).
get_node_affiliations(Nidx) ->
node_flat_odbc:get_node_affiliations(Nidx).
get_affiliation(Nidx, Owner) ->
node_flat_odbc:get_affiliation(Nidx, Owner).
set_affiliation(Nidx, Owner, Affiliation) ->
node_flat_odbc:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) ->
node_flat_odbc:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(Nidx) ->
node_flat_odbc:get_node_subscriptions(Nidx).
get_subscriptions(Nidx, Owner) ->
node_flat_odbc:get_subscriptions(Nidx, Owner).
set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_flat_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_flat_odbc:get_pending_nodes(Host, Owner).
get_states(Nidx) ->
node_flat_odbc:get_states(Nidx).
get_state(Nidx, JID) ->
node_flat_odbc:get_state(Nidx, JID).
set_state(State) ->
node_flat_odbc:set_state(State).
get_items(Nidx, From, RSM) ->
node_flat_odbc:get_items(Nidx, From, RSM).
get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_flat_odbc:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(Nidx, ItemId) ->
node_flat_odbc:get_item(Nidx, ItemId).
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_flat_odbc:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
set_item(Item) ->
node_flat_odbc:set_item(Item).
get_item_name(Host, Node, Id) ->
node_flat_odbc:get_item_name(Host, Node, Id).
node_to_path(Node) ->
node_flat_odbc:node_to_path(Node).
path_to_node(Path) ->
node_flat_odbc:path_to_node(Path).
get_entity_subscriptions_for_send_last(Host, Owner) ->
node_flat_odbc:get_entity_subscriptions_for_send_last(Host, Owner).
%%%===================================================================
%%% Internal functions
%%%===================================================================
+364 -378
View File
@@ -25,6 +25,8 @@
-module(odbc_queries).
-compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(ejabberd_config).
-author("mremond@process-one.net").
@@ -40,7 +42,7 @@
get_roster_groups/3, del_user_roster_t/2,
get_roster_by_jid/3, get_rostergroup_by_jid/3,
del_roster/3, del_roster_sql/2, update_roster/5,
update_roster_sql/4, roster_subscribe/4,
update_roster_sql/4, roster_subscribe/1,
get_subscription/3, set_private_data/4,
set_private_data_sql/3, get_private_data/3,
get_private_data/2, del_user_private_storage/2,
@@ -54,12 +56,13 @@
set_default_privacy_list/2,
unset_default_privacy_list/2, remove_privacy_list/2,
add_privacy_list/2, set_privacy_list/2,
del_privacy_lists/3, set_vcard/26, get_vcard/2,
del_privacy_lists/2, set_vcard/26, get_vcard/2,
escape/1, count_records_where/3, get_roster_version/2,
set_roster_version/2, opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
%% Almost a copy of string:join/2.
%% We use this version because string:join/2 is relatively
@@ -119,95 +122,92 @@ update(LServer, Table, Fields, Vals, Where) ->
sql_transaction(LServer, F) ->
ejabberd_odbc:sql_transaction(LServer, F).
get_last(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select seconds, state from last where "
"username='">>,
Username, <<"'">>]).
set_last_t(LServer, Username, Seconds, State) ->
update(LServer, <<"last">>,
[<<"username">>, <<"seconds">>, <<"state">>],
[Username, Seconds, State],
[<<"username='">>, Username, <<"'">>]).
del_last(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"delete from last where username='">>, Username,
<<"'">>]).
get_password(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select password from users where username='">>,
Username, <<"';">>]).
get_password_scram(LServer, Username) ->
get_last(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
[<<"select password, serverkey, salt, iterationcount from users where "
"username='">>, Username, <<"';">>]).
?SQL("select @(seconds)d, @(state)s from last"
" where username=%(LUser)s")).
set_password_t(LServer, Username, Pass) ->
ejabberd_odbc:sql_transaction(LServer,
fun () ->
update_t(<<"users">>,
[<<"username">>,
<<"password">>],
[Username, Pass],
[<<"username='">>, Username,
<<"'">>])
end).
set_last_t(LServer, LUser, TimeStamp, Status) ->
?SQL_UPSERT(LServer, "last",
["!username=%(LUser)s",
"seconds=%(TimeStamp)d",
"state=%(Status)s"]).
set_password_scram_t(LServer, Username,
del_last(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from last where username=%(LUser)s")).
get_password(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(password)s from users where username=%(LUser)s")).
get_password_scram(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d"
" from users"
" where username=%(LUser)s")).
set_password_t(LServer, LUser, Password) ->
ejabberd_odbc:sql_transaction(
LServer,
fun () ->
?SQL_UPSERT_T(
"users",
["!username=%(LUser)s",
"password=%(Password)s"])
end).
set_password_scram_t(LServer, LUser,
StoredKey, ServerKey, Salt, IterationCount) ->
ejabberd_odbc:sql_transaction(LServer,
fun () ->
update_t(<<"users">>,
[<<"username">>,
<<"password">>,
<<"serverkey">>,
<<"salt">>,
<<"iterationcount">>],
[Username, StoredKey,
ServerKey, Salt,
IterationCount],
[<<"username='">>, Username,
<<"'">>])
end).
ejabberd_odbc:sql_transaction(
LServer,
fun () ->
?SQL_UPSERT_T(
"users",
["!username=%(LUser)s",
"password=%(StoredKey)s",
"serverkey=%(ServerKey)s",
"salt=%(Salt)s",
"iterationcount=%(IterationCount)d"])
end).
add_user(LServer, Username, Pass) ->
ejabberd_odbc:sql_query(LServer,
[<<"insert into users(username, password) "
"values ('">>,
Username, <<"', '">>, Pass, <<"');">>]).
add_user(LServer, LUser, Password) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("insert into users(username, password) "
"values (%(LUser)s, %(Password)s)")).
add_user_scram(LServer, Username,
add_user_scram(LServer, LUser,
StoredKey, ServerKey, Salt, IterationCount) ->
ejabberd_odbc:sql_query(LServer,
[<<"insert into users(username, password, serverkey, salt, iterationcount) "
"values ('">>,
Username, <<"', '">>, StoredKey, <<"', '">>,
ServerKey, <<"', '">>,
Salt, <<"', '">>,
IterationCount, <<"');">>]).
ejabberd_odbc:sql_query(
LServer,
?SQL("insert into users(username, password, serverkey, salt, "
"iterationcount) "
"values (%(LUser)s, %(StoredKey)s, %(ServerKey)s,"
" %(Salt)s, %(IterationCount)d)")).
del_user(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"delete from users where username='">>, Username,
<<"';">>]).
del_user(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from users where username=%(LUser)s")).
del_user_return_password(_LServer, Username, Pass) ->
del_user_return_password(_LServer, LUser, Password) ->
P =
ejabberd_odbc:sql_query_t([<<"select password from users where username='">>,
Username, <<"';">>]),
ejabberd_odbc:sql_query_t([<<"delete from users where username='">>,
Username, <<"' and password='">>, Pass,
<<"';">>]),
ejabberd_odbc:sql_query_t(
?SQL("select @(password)s from users where username=%(LUser)s")),
ejabberd_odbc:sql_query_t(
?SQL("delete from users"
" where username=%(LUser)s and password=%(Password)s")),
P.
list_users(LServer) ->
ejabberd_odbc:sql_query(LServer,
[<<"select username from users">>]).
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(username)s from users")).
list_users(LServer, [{from, Start}, {to, End}])
when is_integer(Start) and is_integer(End) ->
@@ -222,64 +222,54 @@ list_users(LServer,
{offset, Start - 1}]);
list_users(LServer, [{limit, Limit}, {offset, Offset}])
when is_integer(Limit) and is_integer(Offset) ->
ejabberd_odbc:sql_query(LServer,
[list_to_binary(
io_lib:format(
"select username from users " ++
"order by username " ++
"limit ~w offset ~w",
[Limit, Offset]))]);
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(username)s from users "
"order by username "
"limit %(Limit)d offset %(Offset)d"));
list_users(LServer,
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_binary(Prefix) and is_integer(Limit) and
is_integer(Offset) ->
ejabberd_odbc:sql_query(LServer,
[list_to_binary(
io_lib:format(
"select username from users " ++
"where username like '~s%' " ++
"order by username " ++
"limit ~w offset ~w ",
[Prefix, Limit, Offset]))]).
SPrefix = ejabberd_odbc:escape_like_arg(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(username)s from users "
"where username like %(SPrefix2)s "
"order by username "
"limit %(Limit)d offset %(Offset)d")).
users_number(LServer) ->
Type = ejabberd_config:get_option({odbc_type, LServer},
fun(pgsql) -> pgsql;
(mysql) -> mysql;
(sqlite) -> sqlite;
(odbc) -> odbc
end, odbc),
case Type of
pgsql ->
case
ejabberd_config:get_option(
{pgsql_users_number_estimate, LServer},
fun(V) when is_boolean(V) -> V end,
false)
of
true ->
ejabberd_odbc:sql_query(LServer,
[<<"select reltuples from pg_class where "
"oid = 'users'::regclass::oid">>]);
_ ->
ejabberd_odbc:sql_query(LServer,
[<<"select count(*) from users">>])
ejabberd_odbc:sql_query(
LServer,
fun(pgsql, _) ->
case
ejabberd_config:get_option(
{pgsql_users_number_estimate, LServer},
fun(V) when is_boolean(V) -> V end,
false) of
true ->
ejabberd_odbc:sql_query_t(
?SQL("select @(reltuples :: bigint)d from pg_class"
" where oid = 'users'::regclass::oid"));
_ ->
ejabberd_odbc:sql_query_t(
?SQL("select @(count(*))d from users"))
end;
_ ->
ejabberd_odbc:sql_query(LServer,
[<<"select count(*) from users">>])
end.
(_Type, _) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(count(*))d from users"))
end).
users_number(LServer, [{prefix, Prefix}])
when is_binary(Prefix) ->
ejabberd_odbc:sql_query(LServer,
[list_to_binary(
io_lib:fwrite(
"select count(*) from users " ++
%% Warning: Escape prefix at higher level to prevent SQL
%% injection.
"where username like '~s%'",
[Prefix]))]);
SPrefix = ejabberd_odbc:escape_like_arg(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(count(*))d from users "
"where username like %(SPrefix2)s"));
users_number(LServer, []) ->
users_number(LServer).
@@ -291,74 +281,71 @@ add_spool_sql(Username, XML) ->
add_spool(LServer, Queries) ->
ejabberd_odbc:sql_transaction(LServer, Queries).
get_and_del_spool_msg_t(LServer, Username) ->
get_and_del_spool_msg_t(LServer, LUser) ->
F = fun () ->
Result =
ejabberd_odbc:sql_query_t([<<"select username, xml from spool where "
"username='">>,
Username,
<<"' order by seq;">>]),
ejabberd_odbc:sql_query_t([<<"delete from spool where username='">>,
Username, <<"';">>]),
ejabberd_odbc:sql_query_t(
?SQL("select @(username)s, @(xml)s from spool where "
"username=%(LUser)s order by seq;")),
ejabberd_odbc:sql_query_t(
?SQL("delete from spool where username=%(LUser)s;")),
Result
end,
ejabberd_odbc:sql_transaction(LServer, F).
del_spool_msg(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"delete from spool where username='">>, Username,
<<"';">>]).
del_spool_msg(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from spool where username=%(LUser)s")).
get_roster(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, "
"type from rosterusers where username='">>,
Username, <<"'">>]).
get_roster(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s, "
"@(ask)s, @(askmessage)s, @(server)s, @(subscribe)s, "
"@(type)s from rosterusers where username=%(LUser)s")).
get_roster_jid_groups(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select jid, grp from rostergroups where "
"username='">>,
Username, <<"'">>]).
get_roster_jid_groups(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(jid)s, @(grp)s from rostergroups where "
"username=%(LUser)s")).
get_roster_groups(_LServer, Username, SJID) ->
ejabberd_odbc:sql_query_t([<<"select grp from rostergroups where username='">>,
Username, <<"' and jid='">>, SJID, <<"';">>]).
get_roster_groups(_LServer, LUser, SJID) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(grp)s from rostergroups"
" where username=%(LUser)s and jid=%(SJID)s")).
del_user_roster_t(LServer, Username) ->
ejabberd_odbc:sql_transaction(LServer,
fun () ->
ejabberd_odbc:sql_query_t([<<"delete from rosterusers where "
"username='">>,
Username,
<<"';">>]),
ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
"username='">>,
Username,
<<"';">>])
end).
del_user_roster_t(LServer, LUser) ->
ejabberd_odbc:sql_transaction(
LServer,
fun () ->
ejabberd_odbc:sql_query_t(
?SQL("delete from rosterusers where username=%(LUser)s")),
ejabberd_odbc:sql_query_t(
?SQL("delete from rostergroups where username=%(LUser)s"))
end).
get_roster_by_jid(_LServer, Username, SJID) ->
ejabberd_odbc:sql_query_t([<<"select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, "
"type from rosterusers where username='">>,
Username, <<"' and jid='">>, SJID, <<"';">>]).
get_roster_by_jid(_LServer, LUser, SJID) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s,"
" @(ask)s, @(askmessage)s, @(server)s, @(subscribe)s,"
" @(type)s from rosterusers"
" where username=%(LUser)s and jid=%(SJID)s")).
get_rostergroup_by_jid(LServer, Username, SJID) ->
ejabberd_odbc:sql_query(LServer,
[<<"select grp from rostergroups where username='">>,
Username, <<"' and jid='">>, SJID, <<"'">>]).
get_rostergroup_by_jid(LServer, LUser, SJID) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(grp)s from rostergroups"
" where username=%(LUser)s and jid=%(SJID)s")).
del_roster(_LServer, Username, SJID) ->
ejabberd_odbc:sql_query_t([<<"delete from rosterusers where "
"username='">>,
Username, <<"' and jid='">>, SJID,
<<"';">>]),
ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
"username='">>,
Username, <<"' and jid='">>, SJID,
<<"';">>]).
del_roster(_LServer, LUser, SJID) ->
ejabberd_odbc:sql_query_t(
?SQL("delete from rosterusers"
" where username=%(LUser)s and jid=%(SJID)s")),
ejabberd_odbc:sql_query_t(
?SQL("delete from rostergroups"
" where username=%(LUser)s and jid=%(SJID)s")).
del_roster_sql(Username, SJID) ->
[[<<"delete from rosterusers where "
@@ -368,27 +355,19 @@ del_roster_sql(Username, SJID) ->
"username='">>,
Username, <<"' and jid='">>, SJID, <<"';">>]].
update_roster(_LServer, Username, SJID, ItemVals,
update_roster(_LServer, LUser, SJID, ItemVals,
ItemGroups) ->
update_t(<<"rosterusers">>,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
ItemVals,
[<<"username='">>, Username, <<"' and jid='">>, SJID,
<<"'">>]),
ejabberd_odbc:sql_query_t([<<"delete from rostergroups where "
"username='">>,
Username, <<"' and jid='">>, SJID,
<<"';">>]),
lists:foreach(fun (ItemGroup) ->
ejabberd_odbc:sql_query_t([<<"insert into rostergroups( "
" username, jid, grp) values ('">>,
join(ItemGroup,
<<"', '">>),
<<"');">>])
end,
ItemGroups).
roster_subscribe(ItemVals),
ejabberd_odbc:sql_query_t(
?SQL("delete from rostergroups"
" where username=%(LUser)s and jid=%(SJID)s")),
lists:foreach(
fun(ItemGroup) ->
ejabberd_odbc:sql_query_t(
?SQL("insert into rostergroups(username, jid, grp) "
"values (%(LUser)s, %(SJID)s, %(ItemGroup)s)"))
end,
ItemGroups).
update_roster_sql(Username, SJID, ItemVals,
ItemGroups) ->
@@ -410,27 +389,31 @@ update_roster_sql(Username, SJID, ItemVals,
join(ItemGroup, <<"', '">>), <<"');">>]
|| ItemGroup <- ItemGroups].
roster_subscribe(_LServer, Username, SJID, ItemVals) ->
update_t(<<"rosterusers">>,
[<<"username">>, <<"jid">>, <<"nick">>,
<<"subscription">>, <<"ask">>, <<"askmessage">>,
<<"server">>, <<"subscribe">>, <<"type">>],
ItemVals,
[<<"username='">>, Username, <<"' and jid='">>, SJID,
<<"'">>]).
roster_subscribe({LUser, SJID, Name, SSubscription, SAsk, AskMessage}) ->
?SQL_UPSERT_T(
"rosterusers",
["!username=%(LUser)s",
"!jid=%(SJID)s",
"nick=%(Name)s",
"subscription=%(SSubscription)s",
"ask=%(SAsk)s",
"askmessage=%(AskMessage)s",
"server='N'",
"subscribe=''",
"type='item'"]).
get_subscription(LServer, Username, SJID) ->
ejabberd_odbc:sql_query(LServer,
[<<"select subscription from rosterusers "
"where username='">>,
Username, <<"' and jid='">>, SJID, <<"'">>]).
get_subscription(LServer, LUser, SJID) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(subscription)s from rosterusers "
"where username=%(LUser)s and jid=%(SJID)s")).
set_private_data(_LServer, Username, LXMLNS, SData) ->
update_t(<<"private_storage">>,
[<<"username">>, <<"namespace">>, <<"data">>],
[Username, LXMLNS, SData],
[<<"username='">>, Username, <<"' and namespace='">>,
LXMLNS, <<"'">>]).
set_private_data(_LServer, LUser, XMLNS, SData) ->
?SQL_UPSERT_T(
"private_storage",
["!username=%(LUser)s",
"!namespace=%(XMLNS)s",
"data=%(SData)s"]).
set_private_data_sql(Username, LXMLNS, SData) ->
[[<<"delete from private_storage where username='">>,
@@ -440,187 +423,189 @@ set_private_data_sql(Username, LXMLNS, SData) ->
Username, <<"', '">>, LXMLNS, <<"', '">>, SData,
<<"');">>]].
get_private_data(LServer, Username, LXMLNS) ->
ejabberd_odbc:sql_query(LServer,
[<<"select data from private_storage where "
"username='">>,
Username, <<"' and namespace='">>, LXMLNS,
<<"';">>]).
get_private_data(LServer, LUser, XMLNS) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(data)s from private_storage"
" where username=%(LUser)s and namespace=%(XMLNS)s")).
get_private_data(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select namespace, data from private_storage "
"where username='">>, Username, <<"';">>]).
get_private_data(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(namespace)s, @(data)s from private_storage"
" where username=%(LUser)s")).
del_user_private_storage(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"delete from private_storage where username='">>,
Username, <<"';">>]).
del_user_private_storage(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from private_storage"
" where username=%(LUser)s")).
set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, SFN,
SFamily, SGiven, SLBDay, SLCTRY, SLEMail, SLFN,
SLFamily, SLGiven, SLLocality, SLMiddle, SLNickname,
SLOrgName, SLOrgUnit, SLocality, SMiddle, SNickname,
SOrgName, SOrgUnit, SVCARD, Username) ->
ejabberd_odbc:sql_transaction(LServer,
fun () ->
update_t(<<"vcard">>,
[<<"username">>,
<<"vcard">>],
[LUsername, SVCARD],
[<<"username='">>, LUsername,
<<"'">>]),
update_t(<<"vcard_search">>,
[<<"username">>,
<<"lusername">>, <<"fn">>,
<<"lfn">>, <<"family">>,
<<"lfamily">>, <<"given">>,
<<"lgiven">>, <<"middle">>,
<<"lmiddle">>,
<<"nickname">>,
<<"lnickname">>, <<"bday">>,
<<"lbday">>, <<"ctry">>,
<<"lctry">>, <<"locality">>,
<<"llocality">>,
<<"email">>, <<"lemail">>,
<<"orgname">>,
<<"lorgname">>,
<<"orgunit">>,
<<"lorgunit">>],
[Username, LUsername, SFN,
SLFN, SFamily, SLFamily,
SGiven, SLGiven, SMiddle,
SLMiddle, SNickname,
SLNickname, SBDay, SLBDay,
SCTRY, SLCTRY, SLocality,
SLLocality, SEMail, SLEMail,
SOrgName, SLOrgName,
SOrgUnit, SLOrgUnit],
[<<"lusername='">>,
LUsername, <<"'">>])
end).
set_vcard(LServer, LUser, BDay, CTRY, EMail, FN,
Family, Given, LBDay, LCTRY, LEMail, LFN,
LFamily, LGiven, LLocality, LMiddle, LNickname,
LOrgName, LOrgUnit, Locality, Middle, Nickname,
OrgName, OrgUnit, SVCARD, User) ->
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
?SQL_UPSERT(LServer, "vcard",
["!username=%(LUser)s",
"vcard=%(SVCARD)s"]),
?SQL_UPSERT(LServer, "vcard_search",
["username=%(User)s",
"!lusername=%(LUser)s",
"fn=%(FN)s",
"lfn=%(LFN)s",
"family=%(Family)s",
"lfamily=%(LFamily)s",
"given=%(Given)s",
"lgiven=%(LGiven)s",
"middle=%(Middle)s",
"lmiddle=%(LMiddle)s",
"nickname=%(Nickname)s",
"lnickname=%(LNickname)s",
"bday=%(BDay)s",
"lbday=%(LBDay)s",
"ctry=%(CTRY)s",
"lctry=%(LCTRY)s",
"locality=%(Locality)s",
"llocality=%(LLocality)s",
"email=%(EMail)s",
"lemail=%(LEMail)s",
"orgname=%(OrgName)s",
"lorgname=%(LOrgName)s",
"orgunit=%(OrgUnit)s",
"lorgunit=%(LOrgUnit)s"])
end).
get_vcard(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select vcard from vcard where username='">>,
Username, <<"';">>]).
get_vcard(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(vcard)s from vcard where username=%(LUser)s")).
get_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select name from privacy_default_list "
"where username='">>,
Username, <<"';">>]).
get_default_privacy_list(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(name)s from privacy_default_list "
"where username=%(LUser)s")).
get_default_privacy_list_t(Username) ->
ejabberd_odbc:sql_query_t([<<"select name from privacy_default_list "
"where username='">>,
Username, <<"';">>]).
get_default_privacy_list_t(LUser) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(name)s from privacy_default_list "
"where username=%(LUser)s")).
get_privacy_list_names(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"select name from privacy_list where "
"username='">>,
Username, <<"';">>]).
get_privacy_list_names(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(name)s from privacy_list"
" where username=%(LUser)s")).
get_privacy_list_names_t(Username) ->
ejabberd_odbc:sql_query_t([<<"select name from privacy_list where "
"username='">>,
Username, <<"';">>]).
get_privacy_list_names_t(LUser) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(name)s from privacy_list"
" where username=%(LUser)s")).
get_privacy_list_id(LServer, Username, SName) ->
ejabberd_odbc:sql_query(LServer,
[<<"select id from privacy_list where username='">>,
Username, <<"' and name='">>, SName, <<"';">>]).
get_privacy_list_id(LServer, LUser, Name) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(id)d from privacy_list"
" where username=%(LUser)s and name=%(Name)s")).
get_privacy_list_id_t(Username, SName) ->
ejabberd_odbc:sql_query_t([<<"select id from privacy_list where username='">>,
Username, <<"' and name='">>, SName, <<"';">>]).
get_privacy_list_id_t(LUser, Name) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(id)d from privacy_list"
" where username=%(LUser)s and name=%(Name)s")).
get_privacy_list_data(LServer, Username, SName) ->
ejabberd_odbc:sql_query(LServer,
[<<"select t, value, action, ord, match_all, "
"match_iq, match_message, match_presence_in, "
"match_presence_out from privacy_list_data "
"where id = (select id from privacy_list "
"where username='">>,
Username, <<"' and name='">>, SName,
<<"') order by ord;">>]).
get_privacy_list_data(LServer, LUser, Name) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id ="
" (select id from privacy_list"
" where username=%(LUser)s and name=%(Name)s) "
"order by ord")).
get_privacy_list_data_t(Username, SName) ->
ejabberd_odbc:sql_query_t([<<"select t, value, action, ord, match_all, "
"match_iq, match_message, match_presence_in, "
"match_presence_out from privacy_list_data "
"where id = (select id from privacy_list "
"where username='">>,
Username, <<"' and name='">>, SName,
<<"') order by ord;">>]).
%% Not used?
get_privacy_list_data_t(LUser, Name) ->
ejabberd_odbc:sql_query_t(
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id ="
" (select id from privacy_list"
" where username=%(LUser)s and name=%(Name)s) "
"order by ord")).
get_privacy_list_data_by_id(LServer, ID) ->
ejabberd_odbc:sql_query(LServer,
[<<"select t, value, action, ord, match_all, "
"match_iq, match_message, match_presence_in, "
"match_presence_out from privacy_list_data "
"where id='">>,
ID, <<"' order by ord;">>]).
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id=%(ID)d order by ord")).
get_privacy_list_data_by_id_t(ID) ->
ejabberd_odbc:sql_query_t([<<"select t, value, action, ord, match_all, "
"match_iq, match_message, match_presence_in, "
"match_presence_out from privacy_list_data "
"where id='">>,
ID, <<"' order by ord;">>]).
ejabberd_odbc:sql_query_t(
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id=%(ID)d order by ord")).
set_default_privacy_list(Username, SName) ->
update_t(<<"privacy_default_list">>,
[<<"username">>, <<"name">>], [Username, SName],
[<<"username='">>, Username, <<"'">>]).
set_default_privacy_list(LUser, Name) ->
?SQL_UPSERT_T(
"privacy_default_list",
["!username=%(LUser)s",
"name=%(Name)s"]).
unset_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query(LServer,
[<<"delete from privacy_default_list "
" where username='">>,
Username, <<"';">>]).
unset_default_privacy_list(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from privacy_default_list"
" where username=%(LUser)s")).
remove_privacy_list(Username, SName) ->
ejabberd_odbc:sql_query_t([<<"delete from privacy_list where username='">>,
Username, <<"' and name='">>, SName, <<"';">>]).
remove_privacy_list(LUser, Name) ->
ejabberd_odbc:sql_query_t(
?SQL("delete from privacy_list where"
" username=%(LUser)s and name=%(Name)s")).
add_privacy_list(Username, SName) ->
ejabberd_odbc:sql_query_t([<<"insert into privacy_list(username, name) "
"values ('">>,
Username, <<"', '">>, SName, <<"');">>]).
add_privacy_list(LUser, Name) ->
ejabberd_odbc:sql_query_t(
?SQL("insert into privacy_list(username, name) "
"values (%(LUser)s, %(Name)s)")).
set_privacy_list(ID, RItems) ->
ejabberd_odbc:sql_query_t([<<"delete from privacy_list_data where "
"id='">>,
ID, <<"';">>]),
lists:foreach(fun (Items) ->
ejabberd_odbc:sql_query_t([<<"insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, match_prese"
"nce_out ) values ('">>,
ID, <<"', '">>,
join(Items, <<"', '">>),
<<"');">>])
ejabberd_odbc:sql_query_t(
?SQL("delete from privacy_list_data where id=%(ID)d")),
lists:foreach(
fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
ejabberd_odbc:sql_query_t(
?SQL("insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, match_presence_out) "
"values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
" %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
" %(MatchMessage)b, %(MatchPresenceIn)b,"
" %(MatchPresenceOut)b)"))
end,
RItems).
del_privacy_lists(LServer, Server, Username) ->
%% Characters to escape
%% Count number of records in a table given a where clause
ejabberd_odbc:sql_query(LServer,
[<<"delete from privacy_list where username='">>,
Username, <<"';">>]),
ejabberd_odbc:sql_query(LServer,
[<<"delete from privacy_list_data where "
"value='">>,
<<Username/binary, "@", Server/binary>>,
<<"';">>]),
ejabberd_odbc:sql_query(LServer,
[<<"delete from privacy_default_list where "
"username='">>,
Username, <<"';">>]).
del_privacy_lists(LServer, LUser) ->
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from privacy_list where username=%(LUser)s")),
%US = <<LUser/binary, "@", LServer/binary>>,
%ejabberd_odbc:sql_query(
% LServer,
% ?SQL("delete from privacy_list_data where value=%(US)s")),
ejabberd_odbc:sql_query(
LServer,
?SQL("delete from privacy_default_list where username=%(LUser)s")).
%% Characters to escape
escape($\000) -> <<"\\0">>;
escape($\n) -> <<"\\n">>;
escape($\t) -> <<"\\t">>;
@@ -631,16 +616,17 @@ escape($") -> <<"\\\"">>;
escape($\\) -> <<"\\\\">>;
escape(C) -> <<C>>.
%% Count number of records in a table given a where clause
count_records_where(LServer, Table, WhereClause) ->
ejabberd_odbc:sql_query(LServer,
[<<"select count(*) from ">>, Table, <<" ">>,
WhereClause, <<";">>]).
get_roster_version(LServer, LUser) ->
ejabberd_odbc:sql_query(LServer,
[<<"select version from roster_version where "
"username = '">>,
LUser, <<"'">>]).
ejabberd_odbc:sql_query(
LServer,
?SQL("select @(version)s from roster_version"
" where username = %(LUser)s")).
set_roster_version(LUser, Version) ->
update_t(<<"roster_version">>,
+147 -16
View File
@@ -12,8 +12,8 @@
-import(suite, [init_config/1, connect/1, disconnect/1,
recv/0, send/2, send_recv/2, my_jid/1, server_jid/1,
pubsub_jid/1, proxy_jid/1, muc_jid/1,
muc_room_jid/1, get_features/2, re_register/1,
pubsub_jid/1, proxy_jid/1, muc_jid/1, muc_room_jid/1,
mix_jid/1, mix_room_jid/1, get_features/2, re_register/1,
is_feature_advertised/2, subscribe_to_events/1,
is_feature_advertised/3, set_opt/3, auth_SASL/2,
wait_for_master/1, wait_for_slave/1,
@@ -35,22 +35,57 @@ init_per_suite(Config) ->
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile),
ok = application:start(ejabberd),
start_ejabberd(NewConfig),
NewConfig.
end_per_suite(_Config) ->
ok.
start_ejabberd(Config) ->
case proplists:get_value(backends, Config) of
all ->
ok = application:start(ejabberd, transient);
Backends when is_list(Backends) ->
Hosts = lists:map(fun(Backend) -> Backend ++ ".localhost" end, Backends),
application:load(ejabberd),
AllHosts = Hosts ++ ["localhost"], %% We always need localhost for the generic no_db tests
application:set_env(ejabberd, hosts, AllHosts),
ok = application:start(ejabberd, transient)
end.
init_per_group(no_db, Config) ->
end_per_suite(_Config) ->
application:stop(ejabberd).
-define(BACKENDS, [mnesia,redis,mysql,pgsql,sqlite,ldap,extauth,riak]).
init_per_group(Group, Config) ->
case lists:member(Group, ?BACKENDS) of
false ->
%% Not a backend related group, do default init:
do_init_per_group(Group, Config);
true ->
case proplists:get_value(backends, Config) of
all ->
%% All backends enabled
do_init_per_group(Group, Config);
Backends ->
%% Skipped backends that were not explicitely enabled
case lists:member(atom_to_list(Group), Backends) of
true ->
do_init_per_group(Group, Config);
false ->
{skip, {disabled_backend, Group}}
end
end
end.
do_init_per_group(no_db, Config) ->
re_register(Config),
Config;
init_per_group(mnesia, Config) ->
do_init_per_group(mnesia, Config) ->
mod_muc:shutdown_rooms(?MNESIA_VHOST),
set_opt(server, ?MNESIA_VHOST, Config);
init_per_group(redis, Config) ->
do_init_per_group(redis, Config) ->
mod_muc:shutdown_rooms(?REDIS_VHOST),
set_opt(server, ?REDIS_VHOST, Config);
init_per_group(mysql, Config) ->
do_init_per_group(mysql, Config) ->
case catch ejabberd_odbc:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?MYSQL_VHOST),
@@ -59,7 +94,7 @@ init_per_group(mysql, Config) ->
Err ->
{skip, {mysql_not_available, Err}}
end;
init_per_group(pgsql, Config) ->
do_init_per_group(pgsql, Config) ->
case catch ejabberd_odbc:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?PGSQL_VHOST),
@@ -68,7 +103,7 @@ init_per_group(pgsql, Config) ->
Err ->
{skip, {pgsql_not_available, Err}}
end;
init_per_group(sqlite, Config) ->
do_init_per_group(sqlite, Config) ->
case catch ejabberd_odbc:sql_query(?SQLITE_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?SQLITE_VHOST),
@@ -76,11 +111,11 @@ init_per_group(sqlite, Config) ->
Err ->
{skip, {sqlite_not_available, Err}}
end;
init_per_group(ldap, Config) ->
do_init_per_group(ldap, Config) ->
set_opt(server, ?LDAP_VHOST, Config);
init_per_group(extauth, Config) ->
do_init_per_group(extauth, Config) ->
set_opt(server, ?EXTAUTH_VHOST, Config);
init_per_group(riak, Config) ->
do_init_per_group(riak, Config) ->
case ejabberd_riak:is_connected() of
true ->
mod_muc:shutdown_rooms(?RIAK_VHOST),
@@ -89,7 +124,7 @@ init_per_group(riak, Config) ->
Err ->
{skip, {riak_not_available, Err}}
end;
init_per_group(_GroupName, Config) ->
do_init_per_group(_GroupName, Config) ->
Pid = start_event_relay(),
set_opt(event_relay, Pid, Config).
@@ -249,6 +284,8 @@ db_tests(DB) when DB == mnesia; DB == redis ->
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
{test_mix, [parallel],
[mix_master, mix_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
@@ -291,6 +328,8 @@ db_tests(_) ->
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
{test_mix, [parallel],
[mix_master, mix_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
@@ -315,7 +354,8 @@ db_tests(_) ->
ldap_tests() ->
[{ldap_tests, [sequence],
[test_auth,
vcard_get]}].
vcard_get,
ldap_shared_roster_get]}].
extauth_tests() ->
[{extauth_tests, [sequence],
@@ -761,6 +801,13 @@ vcard_get(Config) ->
send_recv(Config, #iq{type = get, sub_els = [#vcard{}]}),
disconnect(Config).
ldap_shared_roster_get(Config) ->
Item = #roster_item{jid = jid:from_string(<<"user2@ldap.localhost">>), name = <<"Test User 2">>,
groups = [<<"group1">>], subscription = both},
#iq{type = result, sub_els = [#roster{items = [Item]}]} =
send_recv(Config, #iq{type = get, sub_els = [#roster{}]}),
disconnect(Config).
vcard_xupdate_master(Config) ->
Img = <<137, "PNG\r\n", 26, $\n>>,
ImgHash = p1_sha:sha(Img),
@@ -882,6 +929,90 @@ pubsub(Config) ->
jid = my_jid(Config)}}]}),
disconnect(Config).
mix_master(Config) ->
MIX = mix_jid(Config),
Room = mix_room_jid(Config),
MyJID = my_jid(Config),
MyBareJID = jid:remove_resource(MyJID),
true = is_feature_advertised(Config, ?NS_MIX_0, MIX),
#iq{type = result,
sub_els =
[#disco_info{
identities = [#identity{category = <<"conference">>,
type = <<"text">>}],
xdata = [#xdata{type = result, fields = XFields}]}]} =
send_recv(Config, #iq{type = get, to = MIX, sub_els = [#disco_info{}]}),
true = lists:any(
fun(#xdata_field{var = <<"FORM_TYPE">>,
values = [?NS_MIX_SERVICEINFO_0]}) -> true;
(_) -> false
end, XFields),
%% Joining
Nodes = [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE,
?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT,
?NS_MIX_NODES_CONFIG],
I0 = send(Config, #iq{type = set, to = Room,
sub_els = [#mix_join{subscribe = Nodes}]}),
{_, #message{sub_els =
[#pubsub_event{
items = [#pubsub_event_items{
node = ?NS_MIX_NODES_PARTICIPANTS,
items = [#pubsub_event_item{
id = ParticipantID,
xml_els = [PXML]}]}]}]}} =
?recv2(#iq{type = result, id = I0,
sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]},
#message{from = Room}),
#mix_participant{jid = MyBareJID} = xmpp_codec:decode(PXML),
%% Coming online
PresenceID = randoms:get_string(),
Presence = xmpp_codec:encode(#presence{}),
I1 = send(
Config,
#iq{type = set, to = Room,
sub_els =
[#pubsub{
publish = #pubsub_publish{
node = ?NS_MIX_NODES_PRESENCE,
items = [#pubsub_item{
id = PresenceID,
xml_els = [Presence]}]}}]}),
?recv2(#iq{type = result, id = I1,
sub_els =
[#pubsub{
publish = #pubsub_publish{
node = ?NS_MIX_NODES_PRESENCE,
items = [#pubsub_item{id = PresenceID}]}}]},
#message{from = Room,
sub_els =
[#pubsub_event{
items = [#pubsub_event_items{
node = ?NS_MIX_NODES_PRESENCE,
items = [#pubsub_event_item{
id = PresenceID,
xml_els = [Presence]}]}]}]}),
%% Coming offline
send(Config, #presence{type = unavailable, to = Room}),
%% Receiving presence retract event
#message{from = Room,
sub_els = [#pubsub_event{
items = [#pubsub_event_items{
node = ?NS_MIX_NODES_PRESENCE,
retract = [PresenceID]}]}]} = recv(),
%% Leaving
I2 = send(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}),
?recv2(#iq{type = result, id = I2, sub_els = []},
#message{from = Room,
sub_els =
[#pubsub_event{
items = [#pubsub_event_items{
node = ?NS_MIX_NODES_PARTICIPANTS,
retract = [ParticipantID]}]}]}),
disconnect(Config).
mix_slave(Config) ->
disconnect(Config).
roster_subscribe_master(Config) ->
send(Config, #presence{}),
?recv1(#presence{}),
+17
View File
@@ -10,6 +10,10 @@ dn: ou=users,dc=localhost
ou: users
objectClass: organizationalUnit
dn: ou=groups,dc=localhost
ou: groups
objectClass: organizationalUnit
dn: uid=test_single,ou=users,dc=localhost
uid: test_single
mail: test_single@localhost
@@ -33,3 +37,16 @@ objectClass: person
jpegPhoto:: /9g=
cn: Test Slave
password: password
dn: uid=user2,ou=users,dc=localhost
uid: user2
mail: user2@localhost
objectClass: person
cn: Test User 2
password: password
dn: cn=group1,ou=groups,dc=localhost
objectClass: posixGroup
memberUid: test_single
memberUid: user2
cn: group1
+14
View File
@@ -35,6 +35,7 @@ host_config:
- "flat"
- "hometree"
- "pep"
mod_mix: []
mod_roster:
versioning: true
store_current_id: true
@@ -88,6 +89,7 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
mod_mix: []
mod_roster:
versioning: true
store_current_id: true
@@ -147,6 +149,7 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
mod_mix: []
mod_roster:
versioning: true
store_current_id: true
@@ -197,6 +200,7 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
mod_mix: []
mod_roster:
versioning: true
store_current_id: true
@@ -252,6 +256,7 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
mod_mix: []
mod_roster:
versioning: true
store_current_id: true
@@ -331,6 +336,15 @@ Welcome to this XMPP server."
auth_method: ldap
modules:
mod_vcard_ldap: []
mod_roster: [] # mod_roster is required by mod_shared_roster
mod_shared_roster_ldap:
ldap_auth_check: off
ldap_base: "dc=localhost"
ldap_rfilter: "(objectClass=posixGroup)"
ldap_gfilter: "(&(objectClass=posixGroup)(cn=%g))"
ldap_memberattr: "memberUid"
ldap_ufilter: "(uid=%u)"
ldap_userdesc: "cn"
mod_adhoc: []
mod_configure: []
mod_disco: []
+57
View File
@@ -0,0 +1,57 @@
# ----------------------------------------------------------------------
#
# 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.
#
# ----------------------------------------------------------------------
defmodule EjabberdCommandsTest do
@author "mremond@process-one.net"
use ExUnit.Case, async: true
require Record
Record.defrecord :ejabberd_commands, Record.extract(:ejabberd_commands, from_lib: "ejabberd/include/ejabberd_commands.hrl")
setup_all do
:ejabberd_commands.init
end
test "Check that we can register a command" do
assert :ejabberd_commands.register_commands([user_test_command]) == :ok
commands = :ejabberd_commands.list_commands
assert Enum.member?(commands, {:test_user, [], "Test user"})
end
# test "Check that a user can use a user command" do
# [Command] = ets:lookup(ejabberd_commands, test_user),
# AccessCommands = ejabberd_commands:get_access_commands(undefined),
# ejabberd_commands:check_access_commands(AccessCommands, {<<"test">>,<<"localhost">>, {oauth,<<"MyToken">>}, false}, test_user, Command, []).
# end
defp user_test_command do
ejabberd_commands(name: :test_user, tags: [:roster],
desc: "Test user",
policy: :user,
module: __MODULE__,
function: :test_user,
args: [],
result: {:contacts, {:list, {:contact, {:tuple, [
{:jid, :string},
{:nick, :string}
]}}}})
end
end
+31 -20
View File
@@ -18,30 +18,41 @@
#
# ----------------------------------------------------------------------
# Notes on the tests:
#
# This test suite will print out errors in logs for tests:
#
# test "Error in run_fold is ignored"
# test "Throw in run_fold is ignored"
# test "Exit in run_fold is ignored"
#
# Those tests are not failing and we can safely ignore those errors in
# log as we are exercising hook handler recovery from that situation.
defmodule EjabberdHooksTest do
use ExUnit.Case, async: true
@author "mremond@process-one.net"
@host <<"domain.net">>
@self __MODULE__
setup_all do
{:ok, _Pid} = :ejabberd_hooks.start_link
:ok
end
setup do
:meck.unload
:true = :ejabberd_hooks.delete_all_hooks
:ok
end
end
test "An anonymous function can be added as a hook" do
hookname = :test_fun_hook
:ok = :ejabberd_hooks.add(hookname, @host, fn _ -> :ok end, 50)
[{50, :undefined, _}] = :ejabberd_hooks.get_handlers(hookname, @host)
end
test "A module function can be added as a hook" do
hookname = :test_mod_hook
callback = :hook_callback
@@ -51,7 +62,7 @@ defmodule EjabberdHooksTest do
test "An anonymous function can be removed from hook handlers" do
hookname = :test_fun_hook
anon_fun = fn _ -> :ok end
anon_fun = fn _ -> :ok end
:ok = :ejabberd_hooks.add(hookname, @host, anon_fun, 50)
:ok = :ejabberd_hooks.delete(hookname, @host, anon_fun, 50)
[] = :ejabberd_hooks.get_handlers(hookname, @host)
@@ -77,20 +88,20 @@ defmodule EjabberdHooksTest do
end
# TODO test "Several handlers are run in order by hook"
test "Hook run chain is stopped when handler return 'stop'" do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, fn _ -> :stop end)
mock(modulename, :hook_callback2, fn _ -> :end_result end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 50)
:ok = :ejabberd_hooks.run(hookname, @host, [:hook_params])
# callback2 is never run:
[{_pid, {^modulename, _callback, [:hook_params]}, :stop}] = :meck.history(modulename)
[{_pid, {^modulename, _callback, [:hook_params]}, :stop}] = :meck.history(modulename)
end
test "Run fold hooks accumulate state in correct order through handlers" do
@@ -99,10 +110,10 @@ defmodule EjabberdHooksTest do
modulename = :hook_module
mock(modulename, :hook_callback1, fn(list, user) -> [user|list] end)
mock(modulename, :hook_callback2, fn(list, _user) -> ["jid2"|list] end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
["jid2", "jid1"] = :ejabberd_hooks.run_fold(hookname, @host, [], ["jid1"])
end
@@ -115,12 +126,12 @@ defmodule EjabberdHooksTest do
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:second = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Both module have been called:
2 = length(:meck.history(modulename))
end
# TODO: Test with ability to stop and return a value
test "Hook run_fold chain is stopped when handler return 'stop'" do
# setup test
@@ -148,19 +159,19 @@ defmodule EjabberdHooksTest do
test "Exit in run_fold is ignored" do
run_fold_crash(fn(_acc) -> exit :crashed end)
end
# test for run hook with various number of params
def run_hook(params, fun, result) do
def run_hook(params, fun, result) do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
callback = :hook_callback
mock(modulename, callback, fun)
# Then check
:ok = :ejabberd_hooks.add(hookname, @host, modulename, callback, 40)
:ok = :ejabberd_hooks.run(hookname, @host, params)
[{_pid, {^modulename, ^callback, ^params}, ^result}] = :meck.history(modulename)
[{_pid, {^modulename, ^callback, ^params}, ^result}] = :meck.history(modulename)
end
def run_fold_crash(crash_fun) do
@@ -175,7 +186,7 @@ defmodule EjabberdHooksTest do
:final = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Both handlers were called
2 = length(:meck.history(modulename))
2 = length(:meck.history(modulename))
end
# TODO refactor: Move to ejabberd_test_mock
@@ -188,5 +199,5 @@ defmodule EjabberdHooksTest do
:meck.expect(module, function, fun)
end
end
+23 -2
View File
@@ -8,7 +8,7 @@
%%% Example: Is run with:
%%% ./rebar skip_deps=true ct suites=elixir
%%% or from ejabber overall test suite:
%%% make test
%%% make quicktest
%%% @end
%%% Created : 19 Feb 2015 by Mickael Remond <mremond@process-one.net>
%%%-------------------------------------------------------------------
@@ -17,6 +17,10 @@
-compile(export_all).
init_per_suite(Config) ->
check_meck(),
Config.
init_per_testcase(_TestCase, Config) ->
process_flag(error_handler, ?MODULE),
Config.
@@ -32,9 +36,19 @@ all() ->
[]
end.
check_meck() ->
case catch meck:module_info(module) of
meck ->
ok;
{'EXIT',{undef, _}} ->
ct:print("meck is not available. Please make sure you configured ejabberd with --enable-elixir --enable-tools"),
ok
end.
is_elixir_available() ->
case catch elixir:module_info() of
{'EXIT',{undef,_}} ->
ct:print("ejabberd has not been build with Elixir support, skipping Elixir tests."),
false;
ModInfo when is_list(ModInfo) ->
true
@@ -55,7 +69,14 @@ run_elixir_test(Func) ->
'Elixir.Code':load_file(list_to_binary(filename:join(test_dir(), atom_to_list(Func)))),
%% I did not use map syntax, so that this file can still be build under R16
ResultMap = 'Elixir.ExUnit':run(),
{ok, 0} = maps:find(failures, ResultMap).
case maps:find(failures, ResultMap) of
{ok, 0} ->
%% Zero failures
ok;
{ok, Failures} ->
ct:print("Elixir tests failed: ~.10B~nSee logs for details", [Failures]),
ct:fail(elixir_test_failure)
end.
test_dir() ->
{ok, CWD} = file:get_cwd(),
+44
View File
@@ -0,0 +1,44 @@
# ----------------------------------------------------------------------
#
# 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.
#
# ----------------------------------------------------------------------
defmodule JidTest do
@author "mremond@process-one.net"
use ExUnit.Case, async: true
require Record
Record.defrecord :jid, Record.extract(:jid, from_lib: "ejabberd/include/jlib.hrl")
setup_all do
:stringprep.start
:jid.start
end
test "create a jid from a binary" do
jid = :jid.from_string("test@localhost/resource")
assert jid(jid, :user) == "test"
assert jid(jid, :server) == "localhost"
assert jid(jid, :resource) == "resource"
end
test "Check that sending a list to from_string/1 does not crash the jid process" do
{:error, :need_jid_as_binary} = :jid.from_string('test@localhost/resource')
end
end
+21 -1
View File
@@ -65,9 +65,21 @@ init_config(Config) ->
{resource, <<"resource">>},
{master_resource, <<"master_resource">>},
{slave_resource, <<"slave_resource">>},
{password, <<"password">>}
{password, <<"password">>},
{backends, get_config_backends()}
|Config].
%% Read environment variable CT_DB=riak,mysql to limit the backends to test.
%% You can thus limit the backend you want to test with:
%% CT_BACKENDS=riak,mysql rebar ct suites=ejabberd
get_config_backends() ->
case os:getenv("CT_BACKENDS") of
false -> all;
String ->
Backends0 = string:tokens(String, ","),
lists:map(fun(Backend) -> string:strip(Backend, both, $ ) end, Backends0)
end.
process_config_tpl(Content, []) ->
Content;
process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->
@@ -354,6 +366,14 @@ muc_room_jid(Config) ->
Server = ?config(server, Config),
jid:make(<<"test">>, <<"conference.", Server/binary>>, <<>>).
mix_jid(Config) ->
Server = ?config(server, Config),
jid:make(<<>>, <<"mix.", Server/binary>>, <<>>).
mix_room_jid(Config) ->
Server = ?config(server, Config),
jid:make(<<"test">>, <<"mix.", Server/binary>>, <<>>).
id() ->
id(undefined).
+215 -7
View File
@@ -15,6 +15,16 @@ decode(_el) -> decode(_el, []).
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"participant">>, <<"urn:xmpp:mix:0">>} ->
decode_mix_participant(<<"urn:xmpp:mix:0">>, IgnoreEls,
_el);
{<<"leave">>, <<"urn:xmpp:mix:0">>} ->
decode_mix_leave(<<"urn:xmpp:mix:0">>, IgnoreEls, _el);
{<<"join">>, <<"urn:xmpp:mix:0">>} ->
decode_mix_join(<<"urn:xmpp:mix:0">>, IgnoreEls, _el);
{<<"subscribe">>, <<"urn:xmpp:mix:0">>} ->
decode_mix_subscribe(<<"urn:xmpp:mix:0">>, IgnoreEls,
_el);
{<<"offline">>,
<<"http://jabber.org/protocol/offline">>} ->
decode_offline(<<"http://jabber.org/protocol/offline">>,
@@ -1088,6 +1098,10 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"participant">>, <<"urn:xmpp:mix:0">>} -> true;
{<<"leave">>, <<"urn:xmpp:mix:0">>} -> true;
{<<"join">>, <<"urn:xmpp:mix:0">>} -> true;
{<<"subscribe">>, <<"urn:xmpp:mix:0">>} -> true;
{<<"offline">>,
<<"http://jabber.org/protocol/offline">>} ->
true;
@@ -1987,7 +2001,7 @@ encode({pubsub_items, _, _, _, _} = Items) ->
encode_pubsub_items(Items,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/pubsub">>}]);
encode({pubsub_event_item, _, _, _} = Item) ->
encode({pubsub_event_item, _, _, _, _} = Item) ->
encode_pubsub_event_item(Item,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/pubsub#event">>}]);
@@ -2160,7 +2174,16 @@ encode({offline_item, _, _} = Item) ->
encode({offline, _, _, _} = Offline) ->
encode_offline(Offline,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/offline">>}]).
<<"http://jabber.org/protocol/offline">>}]);
encode({mix_join, _, _} = Join) ->
encode_mix_join(Join,
[{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]);
encode({mix_leave} = Leave) ->
encode_mix_leave(Leave,
[{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]);
encode({mix_participant, _, _} = Participant) ->
encode_mix_participant(Participant,
[{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]).
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@@ -2286,7 +2309,7 @@ get_ns({pubsub_item, _, _}) ->
<<"http://jabber.org/protocol/pubsub">>;
get_ns({pubsub_items, _, _, _, _}) ->
<<"http://jabber.org/protocol/pubsub">>;
get_ns({pubsub_event_item, _, _, _}) ->
get_ns({pubsub_event_item, _, _, _, _}) ->
<<"http://jabber.org/protocol/pubsub#event">>;
get_ns({pubsub_event_items, _, _, _}) ->
<<"http://jabber.org/protocol/pubsub#event">>;
@@ -2359,6 +2382,9 @@ get_ns({offline_item, _, _}) ->
<<"http://jabber.org/protocol/offline">>;
get_ns({offline, _, _, _}) ->
<<"http://jabber.org/protocol/offline">>;
get_ns({mix_join, _, _}) -> <<"urn:xmpp:mix:0">>;
get_ns({mix_leave}) -> <<"urn:xmpp:mix:0">>;
get_ns({mix_participant, _, _}) -> <<"urn:xmpp:mix:0">>;
get_ns(_) -> <<>>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
@@ -2505,7 +2531,8 @@ pp(pubsub_subscription, 4) -> [jid, node, subid, type];
pp(pubsub_affiliation, 2) -> [node, type];
pp(pubsub_item, 2) -> [id, xml_els];
pp(pubsub_items, 4) -> [node, max_items, subid, items];
pp(pubsub_event_item, 3) -> [id, node, publisher];
pp(pubsub_event_item, 4) ->
[id, node, publisher, xml_els];
pp(pubsub_event_items, 3) -> [node, retract, items];
pp(pubsub_event, 1) -> [items];
pp(pubsub_subscribe, 2) -> [node, jid];
@@ -2564,6 +2591,9 @@ pp(sm_a, 2) -> [h, xmlns];
pp(sm_failed, 2) -> [reason, xmlns];
pp(offline_item, 2) -> [node, action];
pp(offline, 3) -> [items, purge, fetch];
pp(mix_join, 2) -> [jid, subscribe];
pp(mix_leave, 0) -> [];
pp(mix_participant, 2) -> [jid, nick];
pp(_, _) -> no.
enc_bool(false) -> <<"false">>;
@@ -2606,6 +2636,170 @@ dec_tzo(Val) ->
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
decode_mix_participant(__TopXMLNS, __IgnoreEls,
{xmlel, <<"participant">>, _attrs, _els}) ->
{Jid, Nick} = decode_mix_participant_attrs(__TopXMLNS,
_attrs, undefined, undefined),
{mix_participant, Jid, Nick}.
decode_mix_participant_attrs(__TopXMLNS,
[{<<"jid">>, _val} | _attrs], _Jid, Nick) ->
decode_mix_participant_attrs(__TopXMLNS, _attrs, _val,
Nick);
decode_mix_participant_attrs(__TopXMLNS,
[{<<"nick">>, _val} | _attrs], Jid, _Nick) ->
decode_mix_participant_attrs(__TopXMLNS, _attrs, Jid,
_val);
decode_mix_participant_attrs(__TopXMLNS, [_ | _attrs],
Jid, Nick) ->
decode_mix_participant_attrs(__TopXMLNS, _attrs, Jid,
Nick);
decode_mix_participant_attrs(__TopXMLNS, [], Jid,
Nick) ->
{decode_mix_participant_attr_jid(__TopXMLNS, Jid),
decode_mix_participant_attr_nick(__TopXMLNS, Nick)}.
encode_mix_participant({mix_participant, Jid, Nick},
_xmlns_attrs) ->
_els = [],
_attrs = encode_mix_participant_attr_nick(Nick,
encode_mix_participant_attr_jid(Jid,
_xmlns_attrs)),
{xmlel, <<"participant">>, _attrs, _els}.
decode_mix_participant_attr_jid(__TopXMLNS,
undefined) ->
erlang:error({xmpp_codec,
{missing_attr, <<"jid">>, <<"participant">>,
__TopXMLNS}});
decode_mix_participant_attr_jid(__TopXMLNS, _val) ->
case catch dec_jid(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"jid">>, <<"participant">>,
__TopXMLNS}});
_res -> _res
end.
encode_mix_participant_attr_jid(_val, _acc) ->
[{<<"jid">>, enc_jid(_val)} | _acc].
decode_mix_participant_attr_nick(__TopXMLNS,
undefined) ->
undefined;
decode_mix_participant_attr_nick(__TopXMLNS, _val) ->
_val.
encode_mix_participant_attr_nick(undefined, _acc) ->
_acc;
encode_mix_participant_attr_nick(_val, _acc) ->
[{<<"nick">>, _val} | _acc].
decode_mix_leave(__TopXMLNS, __IgnoreEls,
{xmlel, <<"leave">>, _attrs, _els}) ->
{mix_leave}.
encode_mix_leave({mix_leave}, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"leave">>, _attrs, _els}.
decode_mix_join(__TopXMLNS, __IgnoreEls,
{xmlel, <<"join">>, _attrs, _els}) ->
Subscribe = decode_mix_join_els(__TopXMLNS, __IgnoreEls,
_els, []),
Jid = decode_mix_join_attrs(__TopXMLNS, _attrs,
undefined),
{mix_join, Jid, Subscribe}.
decode_mix_join_els(__TopXMLNS, __IgnoreEls, [],
Subscribe) ->
lists:reverse(Subscribe);
decode_mix_join_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"subscribe">>, _attrs, _} = _el | _els],
Subscribe) ->
_xmlns = get_attr(<<"xmlns">>, _attrs),
if _xmlns == <<>>; _xmlns == __TopXMLNS ->
decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
[decode_mix_subscribe(__TopXMLNS, __IgnoreEls,
_el)
| Subscribe]);
true ->
decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
Subscribe)
end;
decode_mix_join_els(__TopXMLNS, __IgnoreEls, [_ | _els],
Subscribe) ->
decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
Subscribe).
decode_mix_join_attrs(__TopXMLNS,
[{<<"jid">>, _val} | _attrs], _Jid) ->
decode_mix_join_attrs(__TopXMLNS, _attrs, _val);
decode_mix_join_attrs(__TopXMLNS, [_ | _attrs], Jid) ->
decode_mix_join_attrs(__TopXMLNS, _attrs, Jid);
decode_mix_join_attrs(__TopXMLNS, [], Jid) ->
decode_mix_join_attr_jid(__TopXMLNS, Jid).
encode_mix_join({mix_join, Jid, Subscribe},
_xmlns_attrs) ->
_els =
lists:reverse('encode_mix_join_$subscribe'(Subscribe,
[])),
_attrs = encode_mix_join_attr_jid(Jid, _xmlns_attrs),
{xmlel, <<"join">>, _attrs, _els}.
'encode_mix_join_$subscribe'([], _acc) -> _acc;
'encode_mix_join_$subscribe'([Subscribe | _els],
_acc) ->
'encode_mix_join_$subscribe'(_els,
[encode_mix_subscribe(Subscribe, []) | _acc]).
decode_mix_join_attr_jid(__TopXMLNS, undefined) ->
undefined;
decode_mix_join_attr_jid(__TopXMLNS, _val) ->
case catch dec_jid(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"jid">>, <<"join">>, __TopXMLNS}});
_res -> _res
end.
encode_mix_join_attr_jid(undefined, _acc) -> _acc;
encode_mix_join_attr_jid(_val, _acc) ->
[{<<"jid">>, enc_jid(_val)} | _acc].
decode_mix_subscribe(__TopXMLNS, __IgnoreEls,
{xmlel, <<"subscribe">>, _attrs, _els}) ->
Node = decode_mix_subscribe_attrs(__TopXMLNS, _attrs,
undefined),
Node.
decode_mix_subscribe_attrs(__TopXMLNS,
[{<<"node">>, _val} | _attrs], _Node) ->
decode_mix_subscribe_attrs(__TopXMLNS, _attrs, _val);
decode_mix_subscribe_attrs(__TopXMLNS, [_ | _attrs],
Node) ->
decode_mix_subscribe_attrs(__TopXMLNS, _attrs, Node);
decode_mix_subscribe_attrs(__TopXMLNS, [], Node) ->
decode_mix_subscribe_attr_node(__TopXMLNS, Node).
encode_mix_subscribe(Node, _xmlns_attrs) ->
_els = [],
_attrs = encode_mix_subscribe_attr_node(Node,
_xmlns_attrs),
{xmlel, <<"subscribe">>, _attrs, _els}.
decode_mix_subscribe_attr_node(__TopXMLNS, undefined) ->
erlang:error({xmpp_codec,
{missing_attr, <<"node">>, <<"subscribe">>,
__TopXMLNS}});
decode_mix_subscribe_attr_node(__TopXMLNS, _val) ->
_val.
encode_mix_subscribe_attr_node(_val, _acc) ->
[{<<"node">>, _val} | _acc].
decode_offline(__TopXMLNS, __IgnoreEls,
{xmlel, <<"offline">>, _attrs, _els}) ->
{Items, Purge, Fetch} = decode_offline_els(__TopXMLNS,
@@ -7883,10 +8077,24 @@ encode_pubsub_event_items_attr_node(_val, _acc) ->
decode_pubsub_event_item(__TopXMLNS, __IgnoreEls,
{xmlel, <<"item">>, _attrs, _els}) ->
__Xmls = decode_pubsub_event_item_els(__TopXMLNS,
__IgnoreEls, _els, []),
{Id, Node, Publisher} =
decode_pubsub_event_item_attrs(__TopXMLNS, _attrs,
undefined, undefined, undefined),
{pubsub_event_item, Id, Node, Publisher}.
{pubsub_event_item, Id, Node, Publisher, __Xmls}.
decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
[], __Xmls) ->
lists:reverse(__Xmls);
decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
[{xmlel, _, _, _} = _el | _els], __Xmls) ->
decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
_els, [_el | __Xmls]);
decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
[_ | _els], __Xmls) ->
decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
_els, __Xmls).
decode_pubsub_event_item_attrs(__TopXMLNS,
[{<<"id">>, _val} | _attrs], _Id, Node,
@@ -7915,9 +8123,9 @@ decode_pubsub_event_item_attrs(__TopXMLNS, [], Id, Node,
Publisher)}.
encode_pubsub_event_item({pubsub_event_item, Id, Node,
Publisher},
Publisher, __Xmls},
_xmlns_attrs) ->
_els = [],
_els = __Xmls,
_attrs =
encode_pubsub_event_item_attr_publisher(Publisher,
encode_pubsub_event_item_attr_node(Node,
+10 -1
View File
@@ -36,6 +36,8 @@
jid :: any(),
subid :: binary()}).
-record(mix_leave, {}).
-record(ping, {}).
-record(delay, {stamp :: any(),
@@ -98,7 +100,8 @@
-record(pubsub_event_item, {id :: binary(),
node :: binary(),
publisher :: binary()}).
publisher :: binary(),
xml_els = [] :: [any()]}).
-record(sm_r, {xmlns :: binary()}).
@@ -229,6 +232,9 @@
notify = false :: any(),
items = [] :: [#pubsub_item{}]}).
-record(mix_participant, {jid :: any(),
nick :: binary()}).
-record(vcard_geo, {lat :: binary(),
lon :: binary()}).
@@ -471,6 +477,9 @@
error :: #error{},
sub_els = [] :: [any()]}).
-record(mix_join, {jid :: any(),
subscribe = [] :: [binary()]}).
-record(privacy_item, {order :: non_neg_integer(),
action :: 'allow' | 'deny',
type :: 'group' | 'jid' | 'subscription',
+36 -1
View File
@@ -1645,7 +1645,7 @@
-xml(pubsub_event_item,
#elem{name = <<"item">>,
xmlns = <<"http://jabber.org/protocol/pubsub#event">>,
result = {pubsub_event_item, '$id', '$node', '$publisher'},
result = {pubsub_event_item, '$id', '$node', '$publisher', '$_xmls'},
attrs = [#attr{name = <<"id">>},
#attr{name = <<"node">>},
#attr{name = <<"publisher">>}]}).
@@ -2427,6 +2427,41 @@
label = '$fetch', default = false},
#ref{name = offline_item, min = 0, label = '$items'}]}).
-xml(mix_subscribe,
#elem{name = <<"subscribe">>,
xmlns = <<"urn:xmpp:mix:0">>,
result = '$node',
attrs = [#attr{name = <<"node">>,
required = true,
label = '$node'}]}).
-xml(mix_join,
#elem{name = <<"join">>,
xmlns = <<"urn:xmpp:mix:0">>,
result = {mix_join, '$jid', '$subscribe'},
attrs = [#attr{name = <<"jid">>,
label = '$jid',
dec = {dec_jid, []},
enc = {enc_jid, []}}],
refs = [#ref{name = mix_subscribe, min = 0, label = '$subscribe'}]}).
-xml(mix_leave,
#elem{name = <<"leave">>,
xmlns = <<"urn:xmpp:mix:0">>,
result = {mix_leave}}).
-xml(mix_participant,
#elem{name = <<"participant">>,
xmlns = <<"urn:xmpp:mix:0">>,
result = {mix_participant, '$jid', '$nick'},
attrs = [#attr{name = <<"jid">>,
required = true,
label = '$jid',
dec = {dec_jid, []},
enc = {enc_jid, []}},
#attr{name = <<"nick">>,
label = '$nick'}]}).
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),