Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b160bd7ac1 | |||
| bf79f223df | |||
| e58b62f737 | |||
| 82cf7f7ca8 | |||
| 6d7891ed16 | |||
| c7c70ffa0a | |||
| 78a44d8099 | |||
| b49a615e21 | |||
| 3b2d0fd24a | |||
| aa15148898 | |||
| 3809b898aa | |||
| e386bf6b58 | |||
| 221d8e0e5d | |||
| 53d12caa56 | |||
| b0ef3e66a8 | |||
| 7a9e93839a | |||
| 54ddc990c2 | |||
| 4afe0b195c | |||
| 494e638f03 | |||
| 915ccbbdfb | |||
| 381065397f | |||
| a08ecc0f41 | |||
| eace5fc463 | |||
| 0afcf561d6 | |||
| 46568fb959 | |||
| c7cf95ba99 | |||
| 914578a85e | |||
| df4c551f06 | |||
| a3a33bd5fc | |||
| 7066338948 | |||
| cb27a3540e | |||
| 61e914a83f | |||
| b90c3764c0 | |||
| bdce5556bd | |||
| 57f7b34b90 | |||
| f8cf1aef91 | |||
| e7ef65a22d | |||
| 107569a17d | |||
| 0112135096 | |||
| 4be9cc1b6d | |||
| 95475966fd | |||
| ef04dd75aa | |||
| 3441157a38 | |||
| c98df3c0da | |||
| c924cd42ff | |||
| 31c194a682 | |||
| 5b7dc0c215 | |||
| 0e3026539e | |||
| dc7b2c45c2 | |||
| cf9ef456b7 | |||
| 7b5825a205 | |||
| 2d103b4ae1 | |||
| 939bb244e1 | |||
| f19a54e9a1 | |||
| ef02053a9d | |||
| d9bb3730b7 | |||
| 7b72247b2c | |||
| 34bc698526 | |||
| efbaba5d04 | |||
| c985a2bd3d | |||
| 74053b114e | |||
| b40154f8f4 | |||
| 3c2cd91fb1 | |||
| 367adc2113 | |||
| 5b5548b8ca | |||
| 058b3d96bf | |||
| 68675effac | |||
| 3fef1a4435 | |||
| 7f25c3b3e8 | |||
| dcefb6bbe3 | |||
| 5351e8236d | |||
| 9440049208 | |||
| b871fbba1b | |||
| 91573a8e82 | |||
| 55c567ff00 | |||
| 5a4b7817df | |||
| 50e5cdc2fa | |||
| 5045fb584d | |||
| 357e48fb6b | |||
| 9ceeaf213b | |||
| 9297782868 | |||
| 0d1edc4771 | |||
| 1336c6c9fa | |||
| 8b03c0a385 | |||
| 15ee72138a | |||
| 92a0181932 | |||
| 035c63fd2a | |||
| 9e6efaf9bc | |||
| f4ee8a2505 | |||
| 842d52352a | |||
| e31799a3b1 | |||
| 1860801e36 | |||
| ae4fa22180 | |||
| b5121a346d | |||
| b6289d646f | |||
| c065a2c5b9 | |||
| 6e40573c13 | |||
| eb0890284a | |||
| 16c1b9a5c2 | |||
| 83accedded | |||
| 8e6a301026 | |||
| 4013629e5d | |||
| 6e14a47316 | |||
| 304afd75ac | |||
| 9c3d57e63e | |||
| 44978ce978 | |||
| 10d6c330a5 | |||
| e95cf420a2 | |||
| 3ecd7e850c | |||
| fee5873310 | |||
| 96b09c587d | |||
| 79853ad44f | |||
| 9a049442ff | |||
| e21f25f5b9 | |||
| 1f9fd25ff8 | |||
| 968576d4f2 | |||
| d8fbe8a289 | |||
| 6d7ce0237a | |||
| 2d042f078e | |||
| 3d8219d8f9 | |||
| 7f3bffe821 | |||
| 99255631dd | |||
| ba35c1ed9d | |||
| 437e768e4a | |||
| c58a4be6ee | |||
| 6374ef4866 | |||
| eeac7f9b02 | |||
| 8c49d1e1af | |||
| d89bbba181 | |||
| 3e49bf0e83 | |||
| 917d48f30b | |||
| d9814709e2 |
@@ -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?
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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-----
|
||||
@@ -0,0 +1,5 @@
|
||||
{level, details}.
|
||||
{incl_dirs, ["src", "ebin"]}.
|
||||
{excl_mods, [eldap, 'ELDAPv3']}.
|
||||
{export, "logs/all.coverdata"}.
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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}).
|
||||
|
||||
@@ -157,3 +157,10 @@
|
||||
-define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>).
|
||||
-define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>).
|
||||
-define(NS_NICK, <<"http://jabber.org/protocol/nick">>).
|
||||
-define(NS_MIX_0, <<"urn:xmpp:mix:0">>).
|
||||
-define(NS_MIX_SERVICEINFO_0, <<"urn:xmpp:mix:0#serviceinfo">>).
|
||||
-define(NS_MIX_NODES_MESSAGES, <<"urn:xmpp:mix:nodes:messages">>).
|
||||
-define(NS_MIX_NODES_PRESENCE, <<"urn:xmpp:mix:nodes:presence">>).
|
||||
-define(NS_MIX_NODES_PARTICIPANTS, <<"urn:xmpp:mix:nodes:participants">>).
|
||||
-define(NS_MIX_NODES_SUBJECT, <<"urn:xmpp:mix:nodes:subject">>).
|
||||
-define(NS_MIX_NODES_CONFIG, <<"urn:xmpp:mix:nodes:config">>).
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
defmodule Ejabberd do
|
||||
end
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
Binary file not shown.
+4
-1
@@ -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
@@ -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
@@ -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">>};
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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) ->
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
[] ->
|
||||
|
||||
@@ -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()) ->
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
-export([start/0,
|
||||
start_link/0,
|
||||
route/3,
|
||||
process_iq/3,
|
||||
open_session/5,
|
||||
open_session/6,
|
||||
close_session/4,
|
||||
|
||||
@@ -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).
|
||||
@@ -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(
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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
@@ -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 ->
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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})}.
|
||||
|
||||
@@ -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}) ->
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
+334
-659
File diff suppressed because it is too large
Load Diff
+23
-44
@@ -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})}.
|
||||
|
||||
|
||||
@@ -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
@@ -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]};
|
||||
|
||||
@@ -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
|
||||
%%%===================================================================
|
||||
@@ -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
@@ -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
@@ -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{}),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: []
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user