Compare commits
273 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea47790807 | |||
| 160ffce090 | |||
| 074ebd80f6 | |||
| 1ed788d00c | |||
| 45eb08d05c | |||
| a6c06964e1 | |||
| 0ae3f624ca | |||
| 86048f8a25 | |||
| 2b9c7ed407 | |||
| 8ebcba4d08 | |||
| e85fa96cf7 | |||
| 0e081ba73e | |||
| 6845896d12 | |||
| 9139ea86fb | |||
| 59a148d80b | |||
| 109ed8f2f6 | |||
| a9539fef22 | |||
| 122dfec03d | |||
| 2eb907dc7f | |||
| 59ce0ba6c8 | |||
| 5d27c975dc | |||
| b8883b5a61 | |||
| 53ae25ad8f | |||
| b07b10bdaa | |||
| 624485fe26 | |||
| 3f901b3793 | |||
| 0b31aa490b | |||
| e37a1a73f1 | |||
| 932d995a1d | |||
| d3a9fbf62f | |||
| 94ef57721b | |||
| 207c0eebe4 | |||
| 133bc764cd | |||
| 8ebf31d949 | |||
| cc9a1a0917 | |||
| a84be928ef | |||
| fcff3c60b1 | |||
| d79ddd7b5c | |||
| 1cdca1ab99 | |||
| 87f8355908 | |||
| 9ed5ba01b2 | |||
| 4b3db3a9cb | |||
| 8a960f77d4 | |||
| d4cd3ddc32 | |||
| 88749e2cdb | |||
| 1214a83cca | |||
| 8e3bbcac9f | |||
| 0ad319e288 | |||
| 9a351c0aff | |||
| d642a9db88 | |||
| 0c4f5dbb7e | |||
| 181019198c | |||
| 61dcab13a4 | |||
| 9c1c854138 | |||
| 912d4e2165 | |||
| 41a24a8f8e | |||
| 3c3dd80ea9 | |||
| 68f8194886 | |||
| 06e9d34018 | |||
| 1d80addb7d | |||
| 43498b39c1 | |||
| 2b09d6a761 | |||
| a5eabcea35 | |||
| 78d4e90d47 | |||
| 133c45ce2b | |||
| d43d9ff0e2 | |||
| da9bcc3370 | |||
| 48594544ed | |||
| d16b99d830 | |||
| 7d9c2b77f2 | |||
| d11d9db3d6 | |||
| 2001418edd | |||
| 9f7d3520aa | |||
| 602bfa3c3c | |||
| 9253f3d113 | |||
| dd93c0b457 | |||
| a73aac691e | |||
| 36891175ec | |||
| cb2b927085 | |||
| 3438f22de5 | |||
| 30393bb690 | |||
| a8b11b6474 | |||
| 432ca80db6 | |||
| e369a93809 | |||
| 74e96afc10 | |||
| 5181983d97 | |||
| 0352b97f50 | |||
| b010a1a0a0 | |||
| 9a99284dfd | |||
| ae88be2011 | |||
| 7f97f3ae75 | |||
| 3d4f65812e | |||
| 914fae3d3e | |||
| d1e072821e | |||
| 989da356c4 | |||
| c1521d3f13 | |||
| a16acd77ed | |||
| 510925c9a1 | |||
| ed2b07fc10 | |||
| ebd50f8a69 | |||
| d8f831de09 | |||
| 73af98a8dc | |||
| 984a00195a | |||
| 39fa1a810d | |||
| e3a03394c7 | |||
| aa162f30df | |||
| 5dcf2cde9c | |||
| b72ed7afa4 | |||
| c109d3eff0 | |||
| 0d743da595 | |||
| ef57067edc | |||
| e054c2800b | |||
| 49f1b4a691 | |||
| 8b61c7fe4b | |||
| 9bac2fa185 | |||
| c3f62c037d | |||
| 4ddee2d89b | |||
| 054426072e | |||
| f28200b6de | |||
| 8c16400332 | |||
| 0394baaa7a | |||
| 1b3a6dd54a | |||
| 6d1ea222c0 | |||
| 08f3d066b1 | |||
| ddca2e8b4a | |||
| a2b2a27bb6 | |||
| d60d72d7bf | |||
| 7ff5f2d3fa | |||
| 1866b56e3b | |||
| e96bfbdbfa | |||
| 3cc964fbcc | |||
| 29f6c43ae3 | |||
| 03de853e4f | |||
| fb367469d4 | |||
| d2cdfa66f9 | |||
| d5c1174385 | |||
| dd888f90ec | |||
| bb9593dd12 | |||
| de385591d0 | |||
| 78dae4036e | |||
| dafea66c0f | |||
| c851f9608a | |||
| 3367c5b120 | |||
| d4579d2a20 | |||
| 410ac9b966 | |||
| adf0d7de91 | |||
| 8c03427c25 | |||
| c156eabb24 | |||
| 1d6cbd2561 | |||
| a7a1e7be94 | |||
| 8673d2926d | |||
| 2d246f61dd | |||
| 9de2ca4568 | |||
| b545301f63 | |||
| 145c0116bf | |||
| 79c511a441 | |||
| 90b22da880 | |||
| 57936bfa4e | |||
| 5c931d7004 | |||
| 88d0b71d58 | |||
| b416527e4f | |||
| 8c8c480477 | |||
| 66132353df | |||
| b30a9f2f75 | |||
| 0cbd41fbdc | |||
| 5b055d7eec | |||
| 01a1f929b4 | |||
| f0f3ec211e | |||
| fd76bc9242 | |||
| 6cd70947be | |||
| 8cefe58a89 | |||
| c3361bab95 | |||
| f3f3b1586e | |||
| 0bf93eefcb | |||
| 10e01b7bfc | |||
| 8f0e066135 | |||
| dfd96b6037 | |||
| 5b373470ac | |||
| 0146189b65 | |||
| 4c4c82897c | |||
| 5509e648ad | |||
| c9ba0e83d2 | |||
| b56c012407 | |||
| 9b48dc9cc3 | |||
| efb4fd0d10 | |||
| 83e2462853 | |||
| cdfd0cce7b | |||
| 2d45832a39 | |||
| 1af2cf37ea | |||
| ca022b6d1f | |||
| e54f1a8485 | |||
| 1be2112634 | |||
| 6dc452e7f5 | |||
| 57a3512dcc | |||
| 1de69174ef | |||
| 26b9d25f32 | |||
| 8ad6afd652 | |||
| 7fed5a3eb6 | |||
| b199b68380 | |||
| e433a63105 | |||
| 68c9328a9c | |||
| 6601f182c4 | |||
| 326db5535c | |||
| 2539be1a04 | |||
| 4e9930597d | |||
| 2dfb5a6a5c | |||
| 8faa6afa67 | |||
| 12e537c43f | |||
| 4394ec38b6 | |||
| 420e05fa0d | |||
| e2fb154fe9 | |||
| e9f219a0ac | |||
| 711c5c0d54 | |||
| f9ed34db4d | |||
| 9a895058e7 | |||
| e76a57e144 | |||
| 6fc6bdefc2 | |||
| 96e35a3248 | |||
| 48be8e7b1e | |||
| f40f3a9da7 | |||
| f81b49fe44 | |||
| 395d2e86bc | |||
| 5b3af9d4cd | |||
| 7e5d766a02 | |||
| c5dd1bdd9d | |||
| d03432a956 | |||
| 4b747c2c78 | |||
| 3a566e3cdf | |||
| b915469f5e | |||
| 8b9166d067 | |||
| dc6861eb73 | |||
| 90a4aafec0 | |||
| 4c06f13d18 | |||
| 8c796ed027 | |||
| 68d12017cc | |||
| 491993d401 | |||
| a981bf9a59 | |||
| 920e4512b6 | |||
| fbdcc44fd9 | |||
| b2b29269ec | |||
| 11811e5f48 | |||
| b7f62a4fa7 | |||
| 0bb14d16c7 | |||
| 59f5a098b5 | |||
| ed1ee6061e | |||
| 50b645aa92 | |||
| 52f2a7de4b | |||
| bce8922e5d | |||
| 86236431b9 | |||
| c0d4d31b5b | |||
| 295bec8551 | |||
| b341a3cef3 | |||
| fface33d54 | |||
| fbf6ba2738 | |||
| 38ec3f66c7 | |||
| 56dc625f9a | |||
| 7c5ee93c88 | |||
| 77163c43d2 | |||
| d1d02e2f26 | |||
| 6b8bc811ac | |||
| b662ec2a78 | |||
| 8ca035496e | |||
| a463f5a25a | |||
| dce4e4de6d | |||
| 9b70177fd5 | |||
| 1fbb36c34a | |||
| 46abf7cfab | |||
| 62cb398734 | |||
| dff940b89e | |||
| 66591b1c0d | |||
| b094ce8ea5 | |||
| 9c82c2f6d0 | |||
| 0a40ab93c8 |
@@ -1,15 +0,0 @@
|
||||
> 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,21 @@
|
||||
Environment
|
||||
-----------
|
||||
- ejabberd version: 18.09
|
||||
- Erlang version: `erl +V`
|
||||
- OS: Linux (Debian)
|
||||
- Installed from: source | distro package | official deb/rpm | official binary installer | other
|
||||
|
||||
Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml
|
||||
---------------------------------------------------------------------------
|
||||
```yaml
|
||||
loglevel: 4
|
||||
...
|
||||
```
|
||||
|
||||
Errors from error.log/crash.log
|
||||
-------------------------------
|
||||
No errors
|
||||
|
||||
Bug description
|
||||
---------------
|
||||
Nothing works, plz halp :(
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
+6
-3
@@ -1,9 +1,9 @@
|
||||
language: erlang
|
||||
|
||||
otp_release:
|
||||
- 17.5
|
||||
- 18.3
|
||||
- 19.2
|
||||
- 19.0
|
||||
- 20.3
|
||||
- 21.1
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
@@ -33,9 +33,12 @@ before_script:
|
||||
- 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';"
|
||||
- mysql -u root ejabberd_test < sql/mysql.sql
|
||||
- psql -U postgres -c "CREATE USER ejabberd_test WITH PASSWORD 'ejabberd_test';"
|
||||
- psql -U postgres -c "CREATE DATABASE ejabberd_test;"
|
||||
- psql -U postgres ejabberd_test -f sql/pg.sql
|
||||
- psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;"
|
||||
- psql -U postgres ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;"
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
# Contributing to ejabberd
|
||||
|
||||
We'd love for you to contribute to our source code and to make ejabberd even better than it is
|
||||
today! Here are the guidelines we'd like you to follow:
|
||||
|
||||
* [Code of Conduct](#coc)
|
||||
* [Questions and Problems](#question)
|
||||
* [Issues and Bugs](#issue)
|
||||
* [Feature Requests](#feature)
|
||||
* [Issue Submission Guidelines](#submit)
|
||||
* [Pull Request Submission Guidelines](#submit-pr)
|
||||
* [Signing the CLA](#cla)
|
||||
|
||||
## <a name="coc"></a> Code of Conduct
|
||||
|
||||
Help us keep ejabberd community open-minded and inclusive. Please read and follow our [Code of Conduct][coc].
|
||||
|
||||
## <a name="requests"></a> Questions, Bugs, Features
|
||||
|
||||
### <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
Do not open issues for general support questions as we want to keep GitHub issues for bug reports
|
||||
and feature requests. You've got much better chances of getting your question answered on dedicated
|
||||
support platforms, the best being [Stack Overflow][stackoverflow].
|
||||
|
||||
Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
- there are thousands of people willing to help on Stack Overflow
|
||||
- questions and answers stay available for public viewing so your question / answer might help
|
||||
someone else
|
||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time, we will systematically close all issues that are requests for general
|
||||
support and redirect people to the section you are reading right now.
|
||||
|
||||
Other channels for support are:
|
||||
- [ejabberd Mailing List][list]
|
||||
- [ejabberd XMPP room][muc]: ejabberd@conference.process-one.net
|
||||
|
||||
### <a name="issue"></a> Found an Issue or Bug?
|
||||
|
||||
If you find a bug in the source code, you can help us by submitting an issue to our
|
||||
[GitHub Repository][github]. Even better, you can submit a Pull Request with a fix.
|
||||
|
||||
### <a name="feature"></a> Missing a Feature?
|
||||
|
||||
You can request a new feature by submitting an issue to our [GitHub Repository][github-issues].
|
||||
|
||||
If you would like to implement a new feature then consider what kind of change it is:
|
||||
|
||||
* **Major Changes** that you wish to contribute to the project should be discussed first in an
|
||||
[GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature.
|
||||
* **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github]
|
||||
as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr).
|
||||
|
||||
## <a name="submit"></a> Issue Submission Guidelines
|
||||
|
||||
Before you submit your issue search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize
|
||||
the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.
|
||||
|
||||
The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to
|
||||
make it easier to understand and categorize the issue.
|
||||
|
||||
## <a name="submit-pr"></a> Pull Request Submission Guidelines
|
||||
|
||||
By submitting a pull request for a code or doc contribution, you need to have the right
|
||||
to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla]
|
||||
for details.
|
||||
|
||||
Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search [GitHub][github-pr] for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Create the [development environment][developer-setup]
|
||||
* Make your changes in a new git branch:
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
* Test your changes and, if relevant, expand the automated test suite.
|
||||
* Create your patch commit, including appropriate test cases.
|
||||
* If the changes affect public APIs, change or add relevant [documentation][doc-repo].
|
||||
* Commit your changes using a descriptive commit message.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
|
||||
* Push your branch to GitHub:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In GitHub, send a pull request to `ejabberd:master`. This will trigger the Travis integration and run the test.
|
||||
We will also notify you if you have not yet signed the [contribution agreement][cla].
|
||||
|
||||
* If you find that the Travis integration has failed, look into the logs on Travis to find out
|
||||
if your changes caused test failures, the commit message was malformed etc. If you find that the
|
||||
tests failed or times out for unrelated reasons, you can ping a team member so that the build can be
|
||||
restarted.
|
||||
|
||||
* If we suggest changes, then:
|
||||
|
||||
* Make the required updates.
|
||||
* Test your changes and test cases.
|
||||
* Commit your changes to your branch (e.g. `my-fix-branch`).
|
||||
* Push the changes to your GitHub repository (this will update your Pull Request).
|
||||
|
||||
You can also amend the initial commits and force push them to the branch.
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push origin my-fix-branch -f
|
||||
```
|
||||
|
||||
This is generally easier to follow, but separate commits are useful if the Pull Request contains
|
||||
iterations that might be interesting to see side-by-side.
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
## <a name="cla"></a> Signing the Contributor License Agreement (CLA)
|
||||
|
||||
Upon submmitting a Pull Request, we will ask you to sign our CLA if you haven't done
|
||||
so before. It's a quick process, we promise, and you will be able to do it all online
|
||||
|
||||
You can read [ProcessOne Contribution License Agreement][cla] in PDF.
|
||||
|
||||
This is part of the legal framework of the open-source ecosystem that adds some red tape,
|
||||
but protects both the contributor and the company / foundation behind the project. It also
|
||||
gives us the option to relicense the code with a more permissive license in the future.
|
||||
|
||||
|
||||
[coc]: https://github.com/processone/ejabberd/blob/master/CODE_OF_CONDUCT.md
|
||||
[stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest
|
||||
[list]: http://lists.jabber.ru/mailman/listinfo/ejabberd
|
||||
[muc]: xmpp:ejabberd@conference.process-one.net
|
||||
[github]: https://github.com/processone/ejabberd
|
||||
[github-issues]: https://github.com/processone/ejabberd/issues
|
||||
[github-new-issue]: https://github.com/processone/ejabberd/issues/new
|
||||
[github-pr]: https://github.com/processone/ejabberd/pulls
|
||||
[doc-repo]: https://github.com/processone/docs.ejabberd.im
|
||||
[developer-setup]: https://docs.ejabberd.im/developer/
|
||||
[cla]: https://www.process-one.net/resources/ejabberd-cla.pdf
|
||||
[license]: https://github.com/processone/ejabberd/blob/master/COPYING
|
||||
@@ -0,0 +1,37 @@
|
||||
# Contributors
|
||||
|
||||
We would like to thanks official ejabberd source code contributors:
|
||||
|
||||
- Sergey Abramyan
|
||||
- Badlop
|
||||
- Ludovic Bocquet
|
||||
- Emilio Bustos
|
||||
- Thiago Camargo
|
||||
- Juan Pablo Carlino
|
||||
- Paweł Chmielowski
|
||||
- Gabriel Gatu
|
||||
- Tsukasa Hamano
|
||||
- Konstantinos Kallas
|
||||
- Evgeny Khramtsov
|
||||
- Ben Langfeld
|
||||
- Peter Lemenkov
|
||||
- Anna Mukharram
|
||||
- Johan Oudinet
|
||||
- Pablo Polvorin
|
||||
- Mickaël Rémond
|
||||
- Matthias Rieber
|
||||
- Rafael Roemhild
|
||||
- Christophe Romain
|
||||
- Jérôme Sautret
|
||||
- Sonny Scroggin
|
||||
- Alexey Shchepin
|
||||
- Shelley Shyan
|
||||
- Radoslaw Szymczyszyn
|
||||
- Stu Tomlinson
|
||||
- Christian Ulrich
|
||||
- Holger Weiß
|
||||
|
||||
Please, if you think we are missing your contribution, do not hesitate to contact us at ProcessOne.
|
||||
In case you do not want to appear in this list, please, let us know as well.
|
||||
|
||||
Thanks !
|
||||
+10
-2
@@ -58,6 +58,9 @@ JSDIR = $(PRIVDIR)/js
|
||||
# /usr/lib/ejabberd/priv/sql
|
||||
SQLDIR = $(PRIVDIR)/sql
|
||||
|
||||
# /usr/lib/ejabberd/priv/lua
|
||||
LUADIR = $(PRIVDIR)/lua
|
||||
|
||||
# /var/lib/ejabberd/
|
||||
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
|
||||
|
||||
@@ -149,7 +152,7 @@ DEPS_FILES_FILTERED=$(filter-out $(BINARIES) deps/elixir/ebin/elixir.app,$(DEPS_
|
||||
DEPS_DIRS=$(sort deps/ $(foreach DEP,$(DEPS),deps/$(DEP)/) $(dir $(DEPS_FILES)))
|
||||
|
||||
MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/css/*.css priv/img/*.png priv/js/*.js priv/lib/* include/*.hrl COPYING))
|
||||
MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql)
|
||||
MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql priv/lua)
|
||||
|
||||
define DEP_VERSION_template
|
||||
DEP_$(1)_VERSION:=$(shell $(SED) -e '/vsn/!d;s/.*, *"/$(1)-/;s/".*//' $(2) 2>/dev/null)
|
||||
@@ -181,7 +184,10 @@ $(call TO_DEST,priv/sql/lite.sql): sql/lite.sql $(call TO_DEST,priv/sql)
|
||||
$(call TO_DEST,priv/bin/captcha.sh): tools/captcha.sh $(call TO_DEST,priv/bin)
|
||||
$(INSTALL) -m 755 $(O_USER) $< $@
|
||||
|
||||
copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql)
|
||||
$(call TO_DEST,priv/lua/redis_sm.lua): priv/lua/redis_sm.lua $(call TO_DEST,priv/lua)
|
||||
$(INSTALL) -m 644 $< $@
|
||||
|
||||
copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql priv/lua/redis_sm.lua)
|
||||
|
||||
.PHONY: $(call TO_DEST,$(DEPS_FILES) $(MAIN_DIRS) $(DEPS_DIRS))
|
||||
|
||||
@@ -283,6 +289,8 @@ uninstall-binary:
|
||||
rm -fr $(JSDIR)
|
||||
rm -f $(SQLDIR)/*.sql
|
||||
rm -fr $(SQLDIR)
|
||||
rm -fr $(LUADIR)/*.lua
|
||||
rm -fr $(LUADIR)
|
||||
rm -fr $(PRIVDIR)
|
||||
rm -fr $(EJABBERDDIR)
|
||||
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
ejabberd Community Edition
|
||||
==========================
|
||||
|
||||
[](https://travis-ci.org/processone/ejabberd) [](https://hex.pm/packages/ejabberd)
|
||||
|
||||
ejabberd is a distributed, fault-tolerant technology that allows the creation
|
||||
of large-scale instant messaging applications. The server can reliably support
|
||||
thousands of simultaneous users on a single node and has been designed to
|
||||
provide exceptional standards of fault tolerance. As an open source
|
||||
technology, based on industry-standards, ejabberd can be used to build bespoke
|
||||
solutions very cost effectively.
|
||||
|
||||
|
||||
Key Features
|
||||
------------
|
||||
|
||||
- **Cross-platform**
|
||||
ejabberd runs under Microsoft Windows and Unix-derived systems such as
|
||||
Linux, FreeBSD and NetBSD.
|
||||
|
||||
- **Distributed**
|
||||
You can run ejabberd on a cluster of machines and all of them will serve the
|
||||
same XMPP domain(s). When you need more capacity you can simply add a new
|
||||
cheap node to your cluster. Accordingly, you do not need to buy an expensive
|
||||
high-end machine to support tens of thousands concurrent users.
|
||||
|
||||
- **Fault-tolerant**
|
||||
You can deploy an ejabberd cluster so that all the information required for
|
||||
a properly working service will be replicated permanently on all nodes. This
|
||||
means that if one of the nodes crashes, the others will continue working
|
||||
without disruption. In addition, nodes also can be added or replaced ‘on
|
||||
the fly’.
|
||||
|
||||
- **Administrator-friendly**
|
||||
ejabberd is built on top of the Open Source Erlang. As a result you do not
|
||||
need to install an external database, an external web server, amongst others
|
||||
because everything is already included, and ready to run out of the box.
|
||||
Other administrator benefits include:
|
||||
- Comprehensive documentation.
|
||||
- Straightforward installers for Linux and Mac OS X.
|
||||
- Web administration.
|
||||
- Shared roster groups.
|
||||
- Command line administration tool.
|
||||
- Can integrate with existing authentication mechanisms.
|
||||
- Capability to send announce messages.
|
||||
|
||||
- **Internationalized**
|
||||
ejabberd leads in internationalization. Hence it is very well suited in a
|
||||
globalized world. Related features are:
|
||||
- Translated to 25 languages.
|
||||
- Support for IDNA.
|
||||
|
||||
- **Open Standards**
|
||||
ejabberd is the first Open Source Jabber server claiming to fully comply to
|
||||
the XMPP standard.
|
||||
- Fully XMPP-compliant.
|
||||
- XML-based protocol.
|
||||
- Many protocols supported.
|
||||
|
||||
|
||||
Additional Features
|
||||
-------------------
|
||||
|
||||
Moreover, ejabberd comes with a wide range of other state-of-the-art features:
|
||||
|
||||
- **Modularity**
|
||||
- Load only the modules you want.
|
||||
- Extend ejabberd with your own custom modules.
|
||||
|
||||
- **Security**
|
||||
- SASL and STARTTLS for c2s and s2s connections.
|
||||
- STARTTLS and Dialback s2s connections.
|
||||
- Web Admin accessible via HTTPS secure access.
|
||||
|
||||
- **Databases**
|
||||
- Internal database for fast deployment (Mnesia).
|
||||
- Native MySQL support.
|
||||
- Native PostgreSQL support.
|
||||
- ODBC data storage support.
|
||||
- Microsoft SQL Server support.
|
||||
|
||||
- **Authentication**
|
||||
- Internal authentication.
|
||||
- PAM, LDAP and ODBC.
|
||||
- External authentication script.
|
||||
|
||||
- **Others**
|
||||
- Support for virtual hosting.
|
||||
- Compressing XML streams with Stream Compression (XEP-0138).
|
||||
- Statistics via Statistics Gathering (XEP-0039).
|
||||
- IPv6 support both for c2s and s2s connections.
|
||||
- Multi-User Chat module with support for clustering and HTML logging.
|
||||
- Users Directory based on users vCards.
|
||||
- Publish-Subscribe component with support for Personal Eventing.
|
||||
- Support for web clients: HTTP Polling and HTTP Binding (BOSH).
|
||||
- Component support: interface with networks such as AIM, ICQ and MSN.
|
||||
|
||||
|
||||
Quickstart guide
|
||||
----------------
|
||||
|
||||
### 0. Requirements
|
||||
|
||||
To compile ejabberd you need:
|
||||
|
||||
- GNU Make.
|
||||
- GCC.
|
||||
- Libexpat 1.95 or higher.
|
||||
- Libyaml 0.1.4 or higher.
|
||||
- Erlang/OTP 17.5 or higher.
|
||||
- OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
|
||||
If your system splits packages in libraries and development headers, you must
|
||||
install the development packages also.
|
||||
|
||||
### 1. Compile and install on *nix systems
|
||||
|
||||
To compile ejabberd, execute the following commands. The first one is only
|
||||
necessary if your source tree didn't come with a `configure` script (In this
|
||||
case you need autoconf installed).
|
||||
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
|
||||
To install ejabberd, run this command with system administrator rights (root
|
||||
user):
|
||||
|
||||
sudo make install
|
||||
|
||||
These commands will:
|
||||
|
||||
- Install the configuration files in `/etc/ejabberd/`
|
||||
- Install ejabberd binary, header and runtime files in `/lib/ejabberd/`
|
||||
- Install the administration script: `/sbin/ejabberdctl`
|
||||
- Install ejabberd documentation in `/share/doc/ejabberd/`
|
||||
- Create a spool directory: `/var/lib/ejabberd/`
|
||||
- Create a directory for log files: `/var/log/ejabberd/`
|
||||
|
||||
|
||||
### 2. Start ejabberd
|
||||
|
||||
You can use the `ejabberdctl` command line administration script to
|
||||
start and stop ejabberd. For example:
|
||||
|
||||
ejabberdctl start
|
||||
|
||||
|
||||
For detailed information please refer to the ejabberd Installation and
|
||||
Operation Guide available online and in the `doc` directory of the source
|
||||
tarball.
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
In order to assist in the development of ejabberd, and particularly the
|
||||
execution of the test suite, a Vagrant environment is available at
|
||||
https://github.com/processone/ejabberd-vagrant-dev.
|
||||
|
||||
To start ejabberd in development mode from the repository directory, you can
|
||||
type a command like:
|
||||
|
||||
EJABBERD_CONFIG_PATH=ejabberd.yml erl -pa ebin -pa deps/*/ebin -pa test -pa deps/elixir/lib/*/ebin/ -s ejabberd
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- Documentation: http://docs.ejabberd.im
|
||||
- Community site: https://www.ejabberd.im
|
||||
- ejabberd commercial offering and support: https://www.process-one.net/en/ejabberd
|
||||
@@ -0,0 +1,174 @@
|
||||
ejabberd Community Edition
|
||||
==========================
|
||||
|
||||
[](https://travis-ci.org/processone/ejabberd) [](https://hex.pm/packages/ejabberd)
|
||||
|
||||
ejabberd is a distributed, fault-tolerant technology that allows the creation
|
||||
of large-scale instant messaging applications. The server can reliably support
|
||||
thousands of simultaneous users on a single node and has been designed to
|
||||
provide exceptional standards of fault tolerance. As an open source
|
||||
technology, based on industry-standards, ejabberd can be used to build bespoke
|
||||
solutions very cost effectively.
|
||||
|
||||
|
||||
Key Features
|
||||
------------
|
||||
|
||||
- **Cross-platform**
|
||||
ejabberd runs under Microsoft Windows and Unix-derived systems such as
|
||||
Linux, FreeBSD and NetBSD.
|
||||
|
||||
- **Distributed**
|
||||
You can run ejabberd on a cluster of machines and all of them will serve the
|
||||
same XMPP domain(s). When you need more capacity you can simply add a new
|
||||
cheap node to your cluster. Accordingly, you do not need to buy an expensive
|
||||
high-end machine to support tens of thousands concurrent users.
|
||||
|
||||
- **Fault-tolerant**
|
||||
You can deploy an ejabberd cluster so that all the information required for
|
||||
a properly working service will be replicated permanently on all nodes. This
|
||||
means that if one of the nodes crashes, the others will continue working
|
||||
without disruption. In addition, nodes also can be added or replaced ‘on
|
||||
the fly’.
|
||||
|
||||
- **Administrator-friendly**
|
||||
ejabberd is built on top of the Open Source Erlang. As a result you do not
|
||||
need to install an external database, an external web server, amongst others
|
||||
because everything is already included, and ready to run out of the box.
|
||||
Other administrator benefits include:
|
||||
- Comprehensive documentation.
|
||||
- Straightforward installers for Linux and Mac OS X.
|
||||
- Web administration.
|
||||
- Shared roster groups.
|
||||
- Command line administration tool.
|
||||
- Can integrate with existing authentication mechanisms.
|
||||
- Capability to send announce messages.
|
||||
|
||||
- **Internationalized**
|
||||
ejabberd leads in internationalization. Hence it is very well suited in a
|
||||
globalized world. Related features are:
|
||||
- Translated to 25 languages.
|
||||
- Support for IDNA.
|
||||
|
||||
- **Open Standards**
|
||||
ejabberd is the first Open Source Jabber server claiming to fully comply to
|
||||
the XMPP standard.
|
||||
- Fully XMPP-compliant.
|
||||
- XML-based protocol.
|
||||
- Many protocols supported.
|
||||
|
||||
|
||||
Additional Features
|
||||
-------------------
|
||||
|
||||
Moreover, ejabberd comes with a wide range of other state-of-the-art features:
|
||||
|
||||
- **Modularity**
|
||||
- Load only the modules you want.
|
||||
- Extend ejabberd with your own custom modules.
|
||||
|
||||
- **Security**
|
||||
- SASL and STARTTLS for c2s and s2s connections.
|
||||
- STARTTLS and Dialback s2s connections.
|
||||
- Web Admin accessible via HTTPS secure access.
|
||||
|
||||
- **Databases**
|
||||
- Internal database for fast deployment (Mnesia).
|
||||
- Native MySQL support.
|
||||
- Native PostgreSQL support.
|
||||
- ODBC data storage support.
|
||||
- Microsoft SQL Server support.
|
||||
|
||||
- **Authentication**
|
||||
- Internal authentication.
|
||||
- PAM, LDAP and ODBC.
|
||||
- External authentication script.
|
||||
|
||||
- **Others**
|
||||
- Support for virtual hosting.
|
||||
- Compressing XML streams with Stream Compression (XEP-0138).
|
||||
- Statistics via Statistics Gathering (XEP-0039).
|
||||
- IPv6 support both for c2s and s2s connections.
|
||||
- Multi-User Chat module with support for clustering and HTML logging.
|
||||
- Users Directory based on users vCards.
|
||||
- Publish-Subscribe component with support for Personal Eventing.
|
||||
- Support for web clients: HTTP Polling and HTTP Binding (BOSH).
|
||||
- Component support: interface with networks such as AIM, ICQ and MSN.
|
||||
|
||||
|
||||
Quickstart guide
|
||||
----------------
|
||||
|
||||
### 0. Requirements
|
||||
|
||||
To compile ejabberd you need:
|
||||
|
||||
- GNU Make.
|
||||
- GCC.
|
||||
- Libexpat ≥ 1.95.
|
||||
- Libyaml ≥ 0.1.4.
|
||||
- Erlang/OTP ≥ 19.0.
|
||||
- OpenSSL ≥ 1.0.0.
|
||||
- Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138). Optional.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
|
||||
If your system splits packages in libraries and development headers, you must
|
||||
install the development packages also.
|
||||
|
||||
### 1. Compile and install on *nix systems
|
||||
|
||||
To compile ejabberd, execute the following commands. The first one is only
|
||||
necessary if your source tree didn't come with a `configure` script (In this
|
||||
case you need autoconf installed).
|
||||
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
|
||||
To install ejabberd, run this command with system administrator rights (root
|
||||
user):
|
||||
|
||||
sudo make install
|
||||
|
||||
These commands will:
|
||||
|
||||
- Install the configuration files in `/etc/ejabberd/`
|
||||
- Install ejabberd binary, header and runtime files in `/lib/ejabberd/`
|
||||
- Install the administration script: `/sbin/ejabberdctl`
|
||||
- Install ejabberd documentation in `/share/doc/ejabberd/`
|
||||
- Create a spool directory: `/var/lib/ejabberd/`
|
||||
- Create a directory for log files: `/var/log/ejabberd/`
|
||||
|
||||
|
||||
### 2. Start ejabberd
|
||||
|
||||
You can use the `ejabberdctl` command line administration script to
|
||||
start and stop ejabberd. For example:
|
||||
|
||||
ejabberdctl start
|
||||
|
||||
|
||||
For detailed information please refer to the ejabberd Installation and
|
||||
Operation Guide available online and in the `doc` directory of the source
|
||||
tarball.
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
In order to assist in the development of ejabberd, and particularly the
|
||||
execution of the test suite, a Vagrant environment is available at
|
||||
https://github.com/processone/ejabberd-vagrant-dev.
|
||||
|
||||
To start ejabberd in development mode from the repository directory, you can
|
||||
type a command like:
|
||||
|
||||
EJABBERD_CONFIG_PATH=ejabberd.yml erl -pa ebin -pa deps/*/ebin -pa test -pa deps/elixir/lib/*/ebin/ -s ejabberd
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- Documentation: https://docs.ejabberd.im
|
||||
- Community site: https://www.ejabberd.im
|
||||
- ejabberd commercial offering and support: https://www.process-one.net/en/ejabberd
|
||||
@@ -1,14 +0,0 @@
|
||||
XmppAddr { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) id-on-xmppAddr(5) }
|
||||
|
||||
DEFINITIONS EXPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) 5 }
|
||||
|
||||
XmppAddr ::= UTF8String
|
||||
|
||||
END
|
||||
@@ -61,7 +61,6 @@ defmodule Ejabberd.ConfigFile do
|
||||
@opts [
|
||||
port: 5280,
|
||||
web_admin: true,
|
||||
http_poll: true,
|
||||
http_bind: true,
|
||||
captcha: true]
|
||||
end
|
||||
|
||||
@@ -1,666 +0,0 @@
|
||||
###
|
||||
### ejabberd configuration file
|
||||
###
|
||||
###
|
||||
|
||||
### The parameters used in this configuration file are explained in more detail
|
||||
### in the ejabberd Installation and Operation Guide.
|
||||
### Please consult the Guide in case of doubts, it is included with
|
||||
### your copy of ejabberd, and is also available online at
|
||||
### http://www.process-one.net/en/ejabberd/docs/
|
||||
|
||||
### The configuration file is written in YAML.
|
||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||
### However, ejabberd treats different literals as different types:
|
||||
###
|
||||
### - unquoted or single-quoted strings. They are called "atoms".
|
||||
### Example: dog, 'Jupiter', '3.14159', YELLOW
|
||||
###
|
||||
### - numeric literals. Example: 3, -45.0, .0
|
||||
###
|
||||
### - quoted or folded strings.
|
||||
### Examples of quoted string: "Lizzard", "orange".
|
||||
### Example of folded string:
|
||||
### > Art thou not Romeo,
|
||||
### and a Montague?
|
||||
|
||||
### =======
|
||||
### LOGGING
|
||||
|
||||
##
|
||||
## loglevel: Verbosity of log files generated by ejabberd.
|
||||
## 0: No ejabberd log at all (not recommended)
|
||||
## 1: Critical
|
||||
## 2: Error
|
||||
## 3: Warning
|
||||
## 4: Info
|
||||
## 5: Debug
|
||||
##
|
||||
loglevel: 4
|
||||
|
||||
##
|
||||
## rotation: Describe how to rotate logs. Either size and/or date can trigger
|
||||
## log rotation. Setting count to N keeps N rotated logs. Setting count to 0
|
||||
## does not disable rotation, it instead rotates the file and keeps no previous
|
||||
## versions around. Setting size to X rotate log when it reaches X bytes.
|
||||
## To disable rotation set the size to 0 and the date to ""
|
||||
## Date syntax is taken from the syntax newsyslog uses in newsyslog.conf.
|
||||
## Some examples:
|
||||
## $D0 rotate every night at midnight
|
||||
## $D23 rotate every day at 23:00 hr
|
||||
## $W0D23 rotate every week on Sunday at 23:00 hr
|
||||
## $W5D16 rotate every week on Friday at 16:00 hr
|
||||
## $M1D0 rotate on the first day of every month at midnight
|
||||
## $M5D6 rotate on every 5th day of the month at 6:00 hr
|
||||
##
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
|
||||
##
|
||||
## overload protection: If you want to limit the number of messages per second
|
||||
## allowed from error_logger, which is a good idea if you want to avoid a flood
|
||||
## of messages when system is overloaded, you can set a limit.
|
||||
## 100 is ejabberd's default.
|
||||
log_rate_limit: 100
|
||||
|
||||
##
|
||||
## watchdog_admins: Only useful for developers: if an ejabberd process
|
||||
## consumes a lot of memory, send live notifications to these XMPP
|
||||
## accounts.
|
||||
##
|
||||
## watchdog_admins:
|
||||
## - "bob@example.com"
|
||||
|
||||
|
||||
### ================
|
||||
### SERVED HOSTNAMES
|
||||
|
||||
##
|
||||
## hosts: Domains served by ejabberd.
|
||||
## You can define one or several, for example:
|
||||
## hosts:
|
||||
## - "example.net"
|
||||
## - "example.com"
|
||||
## - "example.org"
|
||||
##
|
||||
hosts:
|
||||
- "localhost"
|
||||
|
||||
##
|
||||
## route_subdomains: Delegate subdomains to other XMPP servers.
|
||||
## For example, if this ejabberd serves example.org and you want
|
||||
## to allow communication with an XMPP server called im.example.org.
|
||||
##
|
||||
## route_subdomains: s2s
|
||||
|
||||
### ===============
|
||||
### LISTENING PORTS
|
||||
|
||||
##
|
||||
## listen: The ports ejabberd will listen on, which service each is handled
|
||||
## by and what options to start it with.
|
||||
##
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
module: ejabberd_c2s
|
||||
##
|
||||
## If TLS is compiled in and you installed a SSL
|
||||
## certificate, specify the full path to the
|
||||
## file and uncomment these lines:
|
||||
##
|
||||
## certfile: "/path/to/ssl.pem"
|
||||
## starttls: true
|
||||
##
|
||||
## To enforce TLS encryption for client connections,
|
||||
## use this instead of the "starttls" option:
|
||||
##
|
||||
## starttls_required: true
|
||||
##
|
||||
## Custom OpenSSL options
|
||||
##
|
||||
## protocol_options:
|
||||
## - "no_sslv3"
|
||||
## - "no_tlsv1"
|
||||
max_stanza_size: 65536
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
-
|
||||
port: 5269
|
||||
module: ejabberd_s2s_in
|
||||
##
|
||||
## ejabberd_service: Interact with external components (transports, ...)
|
||||
##
|
||||
## -
|
||||
## port: 8888
|
||||
## module: ejabberd_service
|
||||
## access: all
|
||||
## shaper_rule: fast
|
||||
## ip: "127.0.0.1"
|
||||
## hosts:
|
||||
## "icq.example.org":
|
||||
## password: "secret"
|
||||
## "sms.example.org":
|
||||
## password: "secret"
|
||||
|
||||
##
|
||||
## ejabberd_stun: Handles STUN Binding requests
|
||||
##
|
||||
## -
|
||||
## port: 3478
|
||||
## transport: udp
|
||||
## module: ejabberd_stun
|
||||
|
||||
##
|
||||
## To handle XML-RPC requests that provide admin credentials:
|
||||
##
|
||||
## -
|
||||
## port: 4560
|
||||
## module: ejabberd_xmlrpc
|
||||
-
|
||||
port: 5280
|
||||
module: ejabberd_http
|
||||
## request_handlers:
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
web_admin: true
|
||||
http_poll: true
|
||||
http_bind: true
|
||||
## register: true
|
||||
captcha: true
|
||||
|
||||
##
|
||||
## s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
|
||||
## Allowed values are: false optional required required_trusted
|
||||
## You must specify a certificate file.
|
||||
##
|
||||
## s2s_use_starttls: optional
|
||||
|
||||
##
|
||||
## s2s_certfile: Specify a certificate file.
|
||||
##
|
||||
## s2s_certfile: "/path/to/ssl.pem"
|
||||
|
||||
## Custom OpenSSL options
|
||||
##
|
||||
## s2s_protocol_options:
|
||||
## - "no_sslv3"
|
||||
## - "no_tlsv1"
|
||||
|
||||
##
|
||||
## domain_certfile: Specify a different certificate for each served hostname.
|
||||
##
|
||||
## host_config:
|
||||
## "example.org":
|
||||
## domain_certfile: "/path/to/example_org.pem"
|
||||
## "example.com":
|
||||
## domain_certfile: "/path/to/example_com.pem"
|
||||
|
||||
##
|
||||
## S2S whitelist or blacklist
|
||||
##
|
||||
## Default s2s policy for undefined hosts.
|
||||
##
|
||||
## s2s_access: s2s
|
||||
|
||||
##
|
||||
## Outgoing S2S options
|
||||
##
|
||||
## Preferred address families (which to try first) and connect timeout
|
||||
## in milliseconds.
|
||||
##
|
||||
## outgoing_s2s_families:
|
||||
## - ipv4
|
||||
## - ipv6
|
||||
## outgoing_s2s_timeout: 10000
|
||||
|
||||
### ==============
|
||||
### AUTHENTICATION
|
||||
|
||||
##
|
||||
## auth_method: Method used to authenticate the users.
|
||||
## The default method is the internal.
|
||||
## If you want to use a different method,
|
||||
## comment this line and enable the correct ones.
|
||||
##
|
||||
auth_method: internal
|
||||
|
||||
##
|
||||
## Store the plain passwords or hashed for SCRAM:
|
||||
## auth_password_format: plain
|
||||
## auth_password_format: scram
|
||||
##
|
||||
## Define the FQDN if ejabberd doesn't detect it:
|
||||
## fqdn: "server3.example.com"
|
||||
|
||||
##
|
||||
## Authentication using external script
|
||||
## Make sure the script is executable by ejabberd.
|
||||
##
|
||||
## auth_method: external
|
||||
## extauth_program: "/path/to/authentication/script"
|
||||
|
||||
##
|
||||
## Authentication using ODBC
|
||||
## Remember to setup a database in the next section.
|
||||
##
|
||||
## auth_method: odbc
|
||||
|
||||
##
|
||||
## Authentication using PAM
|
||||
##
|
||||
## auth_method: pam
|
||||
## pam_service: "pamservicename"
|
||||
|
||||
##
|
||||
## Authentication using LDAP
|
||||
##
|
||||
## auth_method: ldap
|
||||
##
|
||||
## List of LDAP servers:
|
||||
## ldap_servers:
|
||||
## - "localhost"
|
||||
##
|
||||
## Encryption of connection to LDAP servers:
|
||||
## ldap_encrypt: none
|
||||
## ldap_encrypt: tls
|
||||
##
|
||||
## Port to connect to on LDAP servers:
|
||||
## ldap_port: 389
|
||||
## ldap_port: 636
|
||||
##
|
||||
## LDAP manager:
|
||||
## ldap_rootdn: "dc=example,dc=com"
|
||||
##
|
||||
## Password of LDAP manager:
|
||||
## ldap_password: "******"
|
||||
##
|
||||
## Search base of LDAP directory:
|
||||
## ldap_base: "dc=example,dc=com"
|
||||
##
|
||||
## LDAP attribute that holds user ID:
|
||||
## ldap_uids:
|
||||
## - "mail": "%u@mail.example.org"
|
||||
##
|
||||
## LDAP filter:
|
||||
## ldap_filter: "(objectClass=shadowAccount)"
|
||||
|
||||
##
|
||||
## Anonymous login support:
|
||||
## auth_method: anonymous
|
||||
## anonymous_protocol: sasl_anon | login_anon | both
|
||||
## allow_multiple_connections: true | false
|
||||
##
|
||||
## host_config:
|
||||
## "public.example.org":
|
||||
## auth_method: anonymous
|
||||
## allow_multiple_connections: false
|
||||
## anonymous_protocol: sasl_anon
|
||||
##
|
||||
## To use both anonymous and internal authentication:
|
||||
##
|
||||
## host_config:
|
||||
## "public.example.org":
|
||||
## auth_method:
|
||||
## - internal
|
||||
## - anonymous
|
||||
|
||||
### ==============
|
||||
### DATABASE SETUP
|
||||
|
||||
## ejabberd by default uses the internal Mnesia database,
|
||||
## so you do not necessarily need this section.
|
||||
## This section provides configuration examples in case
|
||||
## you want to use other database backends.
|
||||
## Please consult the ejabberd Guide for details on database creation.
|
||||
|
||||
##
|
||||
## MySQL server:
|
||||
##
|
||||
## odbc_type: mysql
|
||||
## odbc_server: "server"
|
||||
## odbc_database: "database"
|
||||
## odbc_username: "username"
|
||||
## odbc_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## odbc_port: 1234
|
||||
|
||||
##
|
||||
## PostgreSQL server:
|
||||
##
|
||||
## odbc_type: pgsql
|
||||
## odbc_server: "server"
|
||||
## odbc_database: "database"
|
||||
## odbc_username: "username"
|
||||
## odbc_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## odbc_port: 1234
|
||||
##
|
||||
## If you use PostgreSQL, have a large database, and need a
|
||||
## faster but inexact replacement for "select count(*) from users"
|
||||
##
|
||||
## pgsql_users_number_estimate: true
|
||||
|
||||
##
|
||||
## ODBC compatible or MSSQL server:
|
||||
##
|
||||
## odbc_type: odbc
|
||||
## odbc_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
|
||||
|
||||
##
|
||||
## Number of connections to open to the database for each virtual host
|
||||
##
|
||||
## odbc_pool_size: 10
|
||||
|
||||
##
|
||||
## Interval to make a dummy SQL request to keep the connections to the
|
||||
## database alive. Specify in seconds: for example 28800 means 8 hours
|
||||
##
|
||||
## odbc_keepalive_interval: undefined
|
||||
|
||||
### ===============
|
||||
### TRAFFIC SHAPERS
|
||||
|
||||
shaper:
|
||||
##
|
||||
## The "normal" shaper limits traffic speed to 1000 B/s
|
||||
##
|
||||
normal: 1000
|
||||
|
||||
##
|
||||
## The "fast" shaper limits traffic speed to 50000 B/s
|
||||
##
|
||||
fast: 50000
|
||||
|
||||
##
|
||||
## This option specifies the maximum number of elements in the queue
|
||||
## of the FSM. Refer to the documentation for details.
|
||||
##
|
||||
max_fsm_queue: 1000
|
||||
|
||||
###. ====================
|
||||
###' ACCESS CONTROL LISTS
|
||||
acl:
|
||||
##
|
||||
## The 'admin' ACL grants administrative privileges to XMPP accounts.
|
||||
## You can put here as many accounts as you want.
|
||||
##
|
||||
## admin:
|
||||
## user:
|
||||
## - "aleksey": "localhost"
|
||||
## - "ermine": "example.org"
|
||||
##
|
||||
## Blocked users
|
||||
##
|
||||
## blocked:
|
||||
## user:
|
||||
## - "baduser": "example.org"
|
||||
## - "test"
|
||||
|
||||
## Local users: don't modify this.
|
||||
##
|
||||
local:
|
||||
user_regexp: ""
|
||||
|
||||
##
|
||||
## More examples of ACLs
|
||||
##
|
||||
## jabberorg:
|
||||
## server:
|
||||
## - "jabber.org"
|
||||
## aleksey:
|
||||
## user:
|
||||
## - "aleksey": "jabber.ru"
|
||||
## test:
|
||||
## user_regexp: "^test"
|
||||
## user_glob: "test*"
|
||||
|
||||
##
|
||||
## Loopback network
|
||||
##
|
||||
loopback:
|
||||
ip:
|
||||
- "127.0.0.0/8"
|
||||
|
||||
##
|
||||
## Bad XMPP servers
|
||||
##
|
||||
## bad_servers:
|
||||
## server:
|
||||
## - "xmpp.zombie.org"
|
||||
## - "xmpp.spam.com"
|
||||
|
||||
##
|
||||
## Define specific ACLs in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## acl:
|
||||
## admin:
|
||||
## user:
|
||||
## - "bob-local": "localhost"
|
||||
|
||||
### ============
|
||||
### ACCESS RULES
|
||||
access:
|
||||
## Maximum number of simultaneous sessions allowed for a single user:
|
||||
max_user_sessions:
|
||||
all: 10
|
||||
## Maximum number of offline messages that users can have:
|
||||
max_user_offline_messages:
|
||||
admin: 5000
|
||||
all: 100
|
||||
## This rule allows access only for local users:
|
||||
local:
|
||||
local: allow
|
||||
## Only non-blocked users can use c2s connections:
|
||||
c2s:
|
||||
blocked: deny
|
||||
all: allow
|
||||
## For C2S connections, all users except admins use the "normal" shaper
|
||||
c2s_shaper:
|
||||
admin: none
|
||||
all: normal
|
||||
## All S2S connections use the "fast" shaper
|
||||
s2s_shaper:
|
||||
all: fast
|
||||
## Only admins can send announcement messages:
|
||||
announce:
|
||||
admin: allow
|
||||
## Only admins can use the configuration interface:
|
||||
configure:
|
||||
admin: allow
|
||||
## Admins of this server are also admins of the MUC service:
|
||||
muc_admin:
|
||||
admin: allow
|
||||
## Only accounts of the local ejabberd server can create rooms:
|
||||
muc_create:
|
||||
local: allow
|
||||
## All users are allowed to use the MUC service:
|
||||
muc:
|
||||
all: allow
|
||||
## Only accounts on the local ejabberd server can create Pubsub nodes:
|
||||
pubsub_createnode:
|
||||
local: allow
|
||||
## In-band registration allows registration of any possible username.
|
||||
## To disable in-band registration, replace 'allow' with 'deny'.
|
||||
register:
|
||||
all: allow
|
||||
## Only allow to register from localhost
|
||||
trusted_network:
|
||||
loopback: allow
|
||||
## Do not establish S2S connections with bad servers
|
||||
## s2s:
|
||||
## bad_servers: deny
|
||||
## all: allow
|
||||
|
||||
## By default the frequency of account registrations from the same IP
|
||||
## is limited to 1 account every 10 minutes. To disable, specify: infinity
|
||||
## registration_timeout: 600
|
||||
|
||||
##
|
||||
## Define specific Access Rules in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## access:
|
||||
## c2s:
|
||||
## admin: allow
|
||||
## all: deny
|
||||
## register:
|
||||
## all: deny
|
||||
|
||||
### ================
|
||||
### DEFAULT LANGUAGE
|
||||
|
||||
##
|
||||
## language: Default language used for server messages.
|
||||
##
|
||||
language: "en"
|
||||
|
||||
##
|
||||
## Set a different default language in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## language: "ru"
|
||||
|
||||
### =======
|
||||
### CAPTCHA
|
||||
|
||||
##
|
||||
## Full path to a script that generates the image.
|
||||
##
|
||||
## captcha_cmd: "/lib/ejabberd/priv/bin/captcha.sh"
|
||||
|
||||
##
|
||||
## Host for the URL and port where ejabberd listens for CAPTCHA requests.
|
||||
##
|
||||
## captcha_host: "example.org:5280"
|
||||
|
||||
##
|
||||
## Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
|
||||
##
|
||||
## captcha_limit: 5
|
||||
|
||||
### =======
|
||||
### MODULES
|
||||
|
||||
##
|
||||
## Modules enabled in all ejabberd virtual hosts.
|
||||
##
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
## mod_admin_extra: {}
|
||||
mod_announce: # recommends mod_adhoc
|
||||
access: announce
|
||||
mod_blocking: {} # requires mod_privacy
|
||||
mod_caps: {}
|
||||
mod_carboncopy: {}
|
||||
mod_client_state:
|
||||
queue_chat_states: true
|
||||
queue_presence: false
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
mod_disco: {}
|
||||
## mod_echo: {}
|
||||
mod_http_bind: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
## accesslog: "/var/log/ejabberd/access.log"
|
||||
mod_last: {}
|
||||
mod_muc:
|
||||
## host: "conference.@HOST@"
|
||||
access: muc
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
access_admin: muc_admin
|
||||
## mod_muc_log: {}
|
||||
mod_offline:
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
mod_ping: {}
|
||||
## mod_pres_counter:
|
||||
## count: 5
|
||||
## interval: 60
|
||||
mod_privacy: {}
|
||||
mod_private: {}
|
||||
## mod_proxy65: {}
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
## reduces resource comsumption, but XEP incompliant
|
||||
ignore_pep_from_offline: true
|
||||
## XEP compliant, but increases resource comsumption
|
||||
## ignore_pep_from_offline: false
|
||||
last_item_cache: false
|
||||
plugins:
|
||||
- "flat"
|
||||
- "hometree"
|
||||
- "pep" # pep requires mod_caps
|
||||
mod_register:
|
||||
##
|
||||
## Protect In-Band account registrations with CAPTCHA.
|
||||
##
|
||||
## captcha_protected: true
|
||||
|
||||
##
|
||||
## Set the minimum informational entropy for passwords.
|
||||
##
|
||||
## 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.
|
||||
|
||||
##
|
||||
## When a user registers, send a notification to
|
||||
## these XMPP accounts.
|
||||
##
|
||||
## registration_watchers:
|
||||
## - "admin1@example.org"
|
||||
|
||||
##
|
||||
## Only clients in the server machine can register accounts
|
||||
##
|
||||
ip_access: trusted_network
|
||||
|
||||
##
|
||||
## Local c2s or remote s2s users cannot register accounts
|
||||
##
|
||||
## access_from: deny
|
||||
|
||||
access: register
|
||||
mod_roster: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stats: {}
|
||||
mod_time: {}
|
||||
mod_vcard: {}
|
||||
mod_version: {}
|
||||
|
||||
##
|
||||
## Enable modules with custom options in a specific virtual host
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## modules:
|
||||
## mod_echo:
|
||||
## host: "mirror.localhost"
|
||||
|
||||
##
|
||||
## Enable modules management via ejabberdctl for installation and
|
||||
## uninstallation of public/private contributed modules
|
||||
## (enabled by default)
|
||||
##
|
||||
|
||||
allow_contrib_modules: true
|
||||
|
||||
### Local Variables:
|
||||
### mode: yaml
|
||||
### End:
|
||||
### vim: set filetype=yaml tabstop=8
|
||||
+80
-731
@@ -1,15 +1,16 @@
|
||||
###
|
||||
###' ejabberd configuration file
|
||||
### ejabberd configuration file
|
||||
###
|
||||
### The parameters used in this configuration file are explained at
|
||||
###
|
||||
### https://docs.ejabberd.im/admin/configuration
|
||||
###
|
||||
|
||||
### The parameters used in this configuration file are explained in more detail
|
||||
### in the ejabberd Installation and Operation Guide.
|
||||
### Please consult the Guide in case of doubts, it is included with
|
||||
### your copy of ejabberd, and is also available online at
|
||||
### http://www.process-one.net/en/ejabberd/docs/
|
||||
|
||||
### The configuration file is written in YAML.
|
||||
### *******************************************************
|
||||
### ******* !!! WARNING !!! *******
|
||||
### ******* YAML IS INDENTATION SENSITIVE *******
|
||||
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
||||
### *******************************************************
|
||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||
### However, ejabberd treats different literals as different types:
|
||||
###
|
||||
@@ -23,590 +24,78 @@
|
||||
### Example of folded string:
|
||||
### > Art thou not Romeo,
|
||||
### and a Montague?
|
||||
###
|
||||
|
||||
###. =======
|
||||
###' LOGGING
|
||||
|
||||
##
|
||||
## loglevel: Verbosity of log files generated by ejabberd.
|
||||
## 0: No ejabberd log at all (not recommended)
|
||||
## 1: Critical
|
||||
## 2: Error
|
||||
## 3: Warning
|
||||
## 4: Info
|
||||
## 5: Debug
|
||||
##
|
||||
loglevel: 4
|
||||
|
||||
##
|
||||
## rotation: Describe how to rotate logs. Either size and/or date can trigger
|
||||
## log rotation. Setting count to N keeps N rotated logs. Setting count to 0
|
||||
## does not disable rotation, it instead rotates the file and keeps no previous
|
||||
## versions around. Setting size to X rotate log when it reaches X bytes.
|
||||
## To disable rotation set the size to 0 and the date to ""
|
||||
## Date syntax is taken from the syntax newsyslog uses in newsyslog.conf.
|
||||
## Some examples:
|
||||
## $D0 rotate every night at midnight
|
||||
## $D23 rotate every day at 23:00 hr
|
||||
## $W0D23 rotate every week on Sunday at 23:00 hr
|
||||
## $W5D16 rotate every week on Friday at 16:00 hr
|
||||
## $M1D0 rotate on the first day of every month at midnight
|
||||
## $M5D6 rotate on every 5th day of the month at 6:00 hr
|
||||
##
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
|
||||
##
|
||||
## overload protection: If you want to limit the number of messages per second
|
||||
## allowed from error_logger, which is a good idea if you want to avoid a flood
|
||||
## of messages when system is overloaded, you can set a limit.
|
||||
## 100 is ejabberd's default.
|
||||
log_rate_limit: 100
|
||||
|
||||
###. ===============
|
||||
###' NODE PARAMETERS
|
||||
|
||||
##
|
||||
## net_ticktime: Specifies net_kernel tick time in seconds. This options must have
|
||||
## identical value on all nodes, and in most cases shouldn't be changed at all from
|
||||
## default value.
|
||||
##
|
||||
## net_ticktime: 60
|
||||
|
||||
###. ================
|
||||
###' SERVED HOSTNAMES
|
||||
|
||||
##
|
||||
## hosts: Domains served by ejabberd.
|
||||
## You can define one or several, for example:
|
||||
## hosts:
|
||||
## - "example.net"
|
||||
## - "example.com"
|
||||
## - "example.org"
|
||||
##
|
||||
hosts:
|
||||
- "localhost"
|
||||
|
||||
##
|
||||
## route_subdomains: Delegate subdomains to other XMPP servers.
|
||||
## For example, if this ejabberd serves example.org and you want
|
||||
## to allow communication with an XMPP server called im.example.org.
|
||||
##
|
||||
## route_subdomains: s2s
|
||||
loglevel: 4
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
log_rate_limit: 100
|
||||
|
||||
###. ============
|
||||
###' Certificates
|
||||
certfiles:
|
||||
- "/etc/letsencrypt/live/localhost/fullchain.pem"
|
||||
- "/etc/letsencrypt/live/localhost/privkey.pem"
|
||||
|
||||
## List all available PEM files containing certificates for your domains,
|
||||
## chains of certificates or certificate keys. Full chains will be built
|
||||
## automatically by ejabberd.
|
||||
##
|
||||
## certfiles:
|
||||
## - "/etc/letsencrypt/live/example.org/*.pem"
|
||||
## - "/etc/letsencrypt/live/example.com/*.pem"
|
||||
##
|
||||
## If your system provides only a single CA file (CentOS/FreeBSD):
|
||||
## ca_file: "/etc/ssl/certs/ca-bundle.pem"
|
||||
|
||||
###. =================
|
||||
###' TLS configuration
|
||||
|
||||
## Note that the following configuration is the default
|
||||
## configuration of the TLS driver, so you don't need to
|
||||
## uncomment it.
|
||||
##
|
||||
## define_macro:
|
||||
## 'TLS_CIPHERS': "HIGH:!aNULL:!eNULL:!3DES:@STRENGTH"
|
||||
## 'TLS_OPTIONS':
|
||||
## - "no_sslv3"
|
||||
## - "cipher_server_preference"
|
||||
## - "no_compression"
|
||||
## 'DH_FILE': "/path/to/dhparams.pem" # generated with: openssl dhparam -out dhparams.pem 2048
|
||||
##
|
||||
## c2s_dhfile: 'DH_FILE'
|
||||
## s2s_dhfile: 'DH_FILE'
|
||||
## c2s_ciphers: 'TLS_CIPHERS'
|
||||
## s2s_ciphers: 'TLS_CIPHERS'
|
||||
## c2s_protocol_options: 'TLS_OPTIONS'
|
||||
## s2s_protocol_options: 'TLS_OPTIONS'
|
||||
|
||||
###. ===============
|
||||
###' LISTENING PORTS
|
||||
|
||||
##
|
||||
## listen: The ports ejabberd will listen on, which service each is handled
|
||||
## by and what options to start it with.
|
||||
##
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
##
|
||||
## If TLS is compiled in and you installed a SSL
|
||||
## certificate, uncomment this line:
|
||||
##
|
||||
## starttls: true
|
||||
##
|
||||
## To enforce TLS encryption for client connections,
|
||||
## use this instead of the "starttls" option:
|
||||
##
|
||||
## starttls_required: true
|
||||
##
|
||||
## Stream compression
|
||||
##
|
||||
## zlib: true
|
||||
##
|
||||
max_stanza_size: 65536
|
||||
max_stanza_size: 262144
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
##
|
||||
## Direct-TLS for C2S (XEP-0368). A good practice is to forward
|
||||
## traffic from port 443 to this port, possibly multiplexing it
|
||||
## with HTTP using e.g. sslh [https://wiki.xmpp.org/web/Tech_pages/XEP-0368],
|
||||
## so modern clients can bypass restrictive firewalls (in airports, hotels, etc.).
|
||||
##
|
||||
## -
|
||||
## port: 5223
|
||||
## ip: "::"
|
||||
## module: ejabberd_c2s
|
||||
## tls: true
|
||||
## max_stanza_size: 65536
|
||||
## shaper: c2s_shaper
|
||||
## access: c2s
|
||||
starttls_required: true
|
||||
-
|
||||
port: 5269
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
max_stanza_size: 524288
|
||||
-
|
||||
port: 5280
|
||||
port: 5443
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/ws": ejabberd_http_ws
|
||||
"/bosh": mod_bosh
|
||||
"/api": mod_http_api
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
"/bosh": mod_bosh
|
||||
"/upload": mod_http_upload
|
||||
"/ws": ejabberd_http_ws
|
||||
web_admin: true
|
||||
## register: true
|
||||
captcha: true
|
||||
tls: true
|
||||
|
||||
##
|
||||
## ejabberd_service: Interact with external components (transports, ...)
|
||||
##
|
||||
## -
|
||||
## port: 8888
|
||||
## ip: "::"
|
||||
## module: ejabberd_service
|
||||
## access: all
|
||||
## shaper_rule: fast
|
||||
## ip: "127.0.0.1"
|
||||
## privilege_access:
|
||||
## roster: "both"
|
||||
## message: "outgoing"
|
||||
## presence: "roster"
|
||||
## delegations:
|
||||
## "urn:xmpp:mam:1":
|
||||
## filtering: ["node"]
|
||||
## "http://jabber.org/protocol/pubsub":
|
||||
## filtering: []
|
||||
## hosts:
|
||||
## "icq.example.org":
|
||||
## password: "secret"
|
||||
## "sms.example.org":
|
||||
## password: "secret"
|
||||
s2s_use_starttls: optional
|
||||
|
||||
##
|
||||
## ejabberd_stun: Handles STUN Binding requests
|
||||
##
|
||||
## -
|
||||
## port: 3478
|
||||
## transport: udp
|
||||
## module: ejabberd_stun
|
||||
|
||||
##
|
||||
## To handle XML-RPC requests that provide admin credentials:
|
||||
##
|
||||
## -
|
||||
## port: 4560
|
||||
## ip: "::"
|
||||
## module: ejabberd_xmlrpc
|
||||
## maxsessions: 10
|
||||
## timeout: 5000
|
||||
## access_commands:
|
||||
## admin:
|
||||
## commands: all
|
||||
## options: []
|
||||
|
||||
##
|
||||
## To enable secure http upload
|
||||
##
|
||||
## -
|
||||
## port: 5444
|
||||
## ip: "::"
|
||||
## module: ejabberd_http
|
||||
## request_handlers:
|
||||
## "": mod_http_upload
|
||||
## tls: true
|
||||
## protocol_options: 'TLS_OPTIONS'
|
||||
## dhfile: 'DH_FILE'
|
||||
## ciphers: 'TLS_CIPHERS'
|
||||
|
||||
## Disabling digest-md5 SASL authentication. digest-md5 requires plain-text
|
||||
## password storage (see auth_password_format option).
|
||||
## disable_sasl_mechanisms: "digest-md5"
|
||||
|
||||
###. ==================
|
||||
###' S2S GLOBAL OPTIONS
|
||||
|
||||
##
|
||||
## s2s_use_starttls: Enable STARTTLS for S2S connections.
|
||||
## Allowed values are: false, optional or required
|
||||
## You must specify 'certfiles' option
|
||||
##
|
||||
## s2s_use_starttls: optional
|
||||
|
||||
##
|
||||
## S2S whitelist or blacklist
|
||||
##
|
||||
## Default s2s policy for undefined hosts.
|
||||
##
|
||||
## s2s_access: s2s
|
||||
|
||||
##
|
||||
## Outgoing S2S options
|
||||
##
|
||||
## Preferred address families (which to try first) and connect timeout
|
||||
## in seconds.
|
||||
##
|
||||
## outgoing_s2s_families:
|
||||
## - ipv4
|
||||
## - ipv6
|
||||
## outgoing_s2s_timeout: 190
|
||||
|
||||
###. ==============
|
||||
###' AUTHENTICATION
|
||||
|
||||
##
|
||||
## auth_method: Method used to authenticate the users.
|
||||
## The default method is the internal.
|
||||
## If you want to use a different method,
|
||||
## comment this line and enable the correct ones.
|
||||
##
|
||||
auth_method: internal
|
||||
|
||||
##
|
||||
## Store the plain passwords or hashed for SCRAM:
|
||||
## auth_password_format: plain
|
||||
## auth_password_format: scram
|
||||
##
|
||||
## Define the FQDN if ejabberd doesn't detect it:
|
||||
## fqdn: "server3.example.com"
|
||||
|
||||
##
|
||||
## Authentication using external script
|
||||
## Make sure the script is executable by ejabberd.
|
||||
##
|
||||
## auth_method: external
|
||||
## extauth_program: "/path/to/authentication/script"
|
||||
|
||||
##
|
||||
## Authentication using SQL
|
||||
## Remember to setup a database in the next section.
|
||||
##
|
||||
## auth_method: sql
|
||||
|
||||
##
|
||||
## Authentication using PAM
|
||||
##
|
||||
## auth_method: pam
|
||||
## pam_service: "pamservicename"
|
||||
|
||||
##
|
||||
## Authentication using LDAP
|
||||
##
|
||||
## auth_method: ldap
|
||||
##
|
||||
## List of LDAP servers:
|
||||
## ldap_servers:
|
||||
## - "localhost"
|
||||
##
|
||||
## Encryption of connection to LDAP servers:
|
||||
## ldap_encrypt: none
|
||||
## ldap_encrypt: tls
|
||||
##
|
||||
## Port to connect to on LDAP servers:
|
||||
## ldap_port: 389
|
||||
## ldap_port: 636
|
||||
##
|
||||
## LDAP manager:
|
||||
## ldap_rootdn: "dc=example,dc=com"
|
||||
##
|
||||
## Password of LDAP manager:
|
||||
## ldap_password: "******"
|
||||
##
|
||||
## Search base of LDAP directory:
|
||||
## ldap_base: "dc=example,dc=com"
|
||||
##
|
||||
## LDAP attribute that holds user ID:
|
||||
## ldap_uids:
|
||||
## - "mail": "%u@mail.example.org"
|
||||
##
|
||||
## LDAP filter:
|
||||
## ldap_filter: "(objectClass=shadowAccount)"
|
||||
|
||||
##
|
||||
## Anonymous login support:
|
||||
## auth_method: anonymous
|
||||
## anonymous_protocol: sasl_anon | login_anon | both
|
||||
## allow_multiple_connections: true | false
|
||||
##
|
||||
## host_config:
|
||||
## "public.example.org":
|
||||
## auth_method: anonymous
|
||||
## allow_multiple_connections: false
|
||||
## anonymous_protocol: sasl_anon
|
||||
##
|
||||
## To use both anonymous and internal authentication:
|
||||
##
|
||||
## host_config:
|
||||
## "public.example.org":
|
||||
## auth_method:
|
||||
## - internal
|
||||
## - anonymous
|
||||
|
||||
###. ==============
|
||||
###' DATABASE SETUP
|
||||
|
||||
## ejabberd by default uses the internal Mnesia database,
|
||||
## so you do not necessarily need this section.
|
||||
## This section provides configuration examples in case
|
||||
## you want to use other database backends.
|
||||
## Please consult the ejabberd Guide for details on database creation.
|
||||
|
||||
##
|
||||
## MySQL server:
|
||||
##
|
||||
## sql_type: mysql
|
||||
## sql_server: "server"
|
||||
## sql_database: "database"
|
||||
## sql_username: "username"
|
||||
## sql_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## sql_port: 1234
|
||||
|
||||
##
|
||||
## PostgreSQL server:
|
||||
##
|
||||
## sql_type: pgsql
|
||||
## sql_server: "server"
|
||||
## sql_database: "database"
|
||||
## sql_username: "username"
|
||||
## sql_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## sql_port: 1234
|
||||
##
|
||||
## If you use PostgreSQL, have a large database, and need a
|
||||
## faster but inexact replacement for "select count(*) from users"
|
||||
##
|
||||
## pgsql_users_number_estimate: true
|
||||
|
||||
##
|
||||
## SQLite:
|
||||
##
|
||||
## sql_type: sqlite
|
||||
## sql_database: "/path/to/database.db"
|
||||
|
||||
##
|
||||
## ODBC compatible or MSSQL server:
|
||||
##
|
||||
## sql_type: odbc
|
||||
## sql_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
|
||||
|
||||
##
|
||||
## Number of connections to open to the database for each virtual host
|
||||
##
|
||||
## sql_pool_size: 10
|
||||
|
||||
##
|
||||
## Interval to make a dummy SQL request to keep the connections to the
|
||||
## database alive. Specify in seconds: for example 28800 means 8 hours
|
||||
##
|
||||
## sql_keepalive_interval: undefined
|
||||
|
||||
##
|
||||
## Use the new SQL schema
|
||||
##
|
||||
## new_sql_schema: true
|
||||
|
||||
##
|
||||
## A database can also can be used to store information from several
|
||||
## modules. To enable storage to the database, just make sure it is
|
||||
## setup above and set default_db: sql if you want to use SQL for
|
||||
## all modules.
|
||||
##
|
||||
## default_db: sql
|
||||
|
||||
###. ===============
|
||||
###' TRAFFIC SHAPERS
|
||||
|
||||
shaper:
|
||||
##
|
||||
## The "normal" shaper limits traffic speed to 1000 B/s
|
||||
##
|
||||
normal: 1000
|
||||
|
||||
##
|
||||
## The "fast" shaper limits traffic speed to 50000 B/s
|
||||
##
|
||||
fast: 50000
|
||||
|
||||
##
|
||||
## This option specifies the maximum number of elements in the queue
|
||||
## of the FSM. Refer to the documentation for details.
|
||||
##
|
||||
max_fsm_queue: 10000
|
||||
|
||||
###. ====================
|
||||
###' ACCESS CONTROL LISTS
|
||||
acl:
|
||||
##
|
||||
## The 'admin' ACL grants administrative privileges to XMPP accounts.
|
||||
## You can put here as many accounts as you want.
|
||||
##
|
||||
## admin:
|
||||
## user:
|
||||
## - "aleksey@localhost"
|
||||
## - "ermine@example.org"
|
||||
##
|
||||
## Blocked users
|
||||
##
|
||||
## blocked:
|
||||
## user:
|
||||
## - "baduser@example.org"
|
||||
## - "test"
|
||||
|
||||
## Local users: don't modify this.
|
||||
##
|
||||
local:
|
||||
user_regexp: ""
|
||||
|
||||
##
|
||||
## More examples of ACLs
|
||||
##
|
||||
## jabberorg:
|
||||
## server:
|
||||
## - "jabber.org"
|
||||
## aleksey:
|
||||
## user:
|
||||
## - "aleksey@jabber.ru"
|
||||
## test:
|
||||
## user_regexp: "^test"
|
||||
## user_glob: "test*"
|
||||
|
||||
##
|
||||
## Loopback network
|
||||
##
|
||||
loopback:
|
||||
ip:
|
||||
- "127.0.0.0/8"
|
||||
- "::1/128"
|
||||
- "::FFFF:127.0.0.1/128"
|
||||
|
||||
##
|
||||
## Bad XMPP servers
|
||||
##
|
||||
## bad_servers:
|
||||
## server:
|
||||
## - "xmpp.zombie.org"
|
||||
## - "xmpp.spam.com"
|
||||
|
||||
##
|
||||
## Define specific ACLs in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## acl:
|
||||
## admin:
|
||||
## user:
|
||||
## - "bob-local@localhost"
|
||||
|
||||
###. ============
|
||||
###' SHAPER RULES
|
||||
|
||||
shaper_rules:
|
||||
## Maximum number of simultaneous sessions allowed for a single user:
|
||||
max_user_sessions: 10
|
||||
## Maximum number of offline messages that users can have:
|
||||
max_user_offline_messages:
|
||||
- 5000: admin
|
||||
- 100
|
||||
## For C2S connections, all users except admins use the "normal" shaper
|
||||
c2s_shaper:
|
||||
- none: admin
|
||||
- normal
|
||||
## All S2S connections use the "fast" shaper
|
||||
s2s_shaper: fast
|
||||
|
||||
###. ============
|
||||
###' ACCESS RULES
|
||||
access_rules:
|
||||
## This rule allows access only for local users:
|
||||
local:
|
||||
- allow: local
|
||||
## Only non-blocked users can use c2s connections:
|
||||
c2s:
|
||||
- deny: blocked
|
||||
- allow
|
||||
## Only admins can send announcement messages:
|
||||
announce:
|
||||
- allow: admin
|
||||
## Only admins can use the configuration interface:
|
||||
configure:
|
||||
- allow: admin
|
||||
## Only accounts of the local ejabberd server can create rooms:
|
||||
muc_create:
|
||||
- allow: local
|
||||
## Only accounts on the local ejabberd server can create Pubsub nodes:
|
||||
pubsub_createnode:
|
||||
- allow: local
|
||||
## In-band registration allows registration of any possible username.
|
||||
## To disable in-band registration, replace 'allow' with 'deny'.
|
||||
register:
|
||||
- allow
|
||||
## Only allow to register from localhost
|
||||
trusted_network:
|
||||
- allow: loopback
|
||||
## Do not establish S2S connections with bad servers
|
||||
## If you enable this you also have to uncomment "s2s_access: s2s"
|
||||
## s2s:
|
||||
## - deny:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
## - deny:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
## - allow
|
||||
|
||||
## ===============
|
||||
## API PERMISSIONS
|
||||
## ===============
|
||||
##
|
||||
## This section allows you to define who and using what method
|
||||
## can execute commands offered by ejabberd.
|
||||
##
|
||||
## By default "console commands" section allow executing all commands
|
||||
## issued using ejabberdctl command, and "admin access" section allows
|
||||
## users in admin acl that connect from 127.0.0.1 to execute all
|
||||
## commands except start and stop with any available access method
|
||||
## (ejabberdctl, http-api, xmlrpc depending what is enabled on server).
|
||||
##
|
||||
## If you remove "console commands" there will be one added by
|
||||
## default allowing executing all commands, but if you just change
|
||||
## permissions in it, version from config file will be used instead
|
||||
## of default one.
|
||||
##
|
||||
api_permissions:
|
||||
"console commands":
|
||||
from:
|
||||
@@ -636,154 +125,71 @@ api_permissions:
|
||||
- "status"
|
||||
- "connected_users_number"
|
||||
|
||||
## By default the frequency of account registrations from the same IP
|
||||
## is limited to 1 account every 10 minutes. To disable, specify: infinity
|
||||
## registration_timeout: 600
|
||||
|
||||
##
|
||||
## Define specific Access Rules in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## access:
|
||||
## c2s:
|
||||
## - allow: admin
|
||||
## - deny
|
||||
## register:
|
||||
## - deny
|
||||
shaper:
|
||||
normal: 1000
|
||||
fast: 50000
|
||||
|
||||
###. ================
|
||||
###' DEFAULT LANGUAGE
|
||||
shaper_rules:
|
||||
max_user_sessions: 10
|
||||
max_user_offline_messages:
|
||||
- 5000: admin
|
||||
- 100
|
||||
c2s_shaper:
|
||||
- none: admin
|
||||
- normal
|
||||
s2s_shaper: fast
|
||||
|
||||
##
|
||||
## language: Default language used for server messages.
|
||||
##
|
||||
language: "en"
|
||||
|
||||
##
|
||||
## Set a different default language in a virtual host.
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## language: "ru"
|
||||
|
||||
###. =======
|
||||
###' CAPTCHA
|
||||
|
||||
##
|
||||
## Full path to a script that generates the image.
|
||||
##
|
||||
## captcha_cmd: "/lib/ejabberd/priv/bin/captcha.sh"
|
||||
|
||||
##
|
||||
## Host for the URL and port where ejabberd listens for CAPTCHA requests.
|
||||
##
|
||||
## captcha_host: "example.org:5280"
|
||||
|
||||
##
|
||||
## Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
|
||||
##
|
||||
## captcha_limit: 5
|
||||
|
||||
###. ====
|
||||
###' ACME
|
||||
##
|
||||
## In order to use the acme certificate acquiring through "Let's Encrypt"
|
||||
## an http listener has to be configured to listen to port 80 so that
|
||||
## the authorization challenges posed by "Let's Encrypt" can be solved.
|
||||
##
|
||||
## A simple way of doing this would be to add the following in the listening
|
||||
## section and to configure port forwarding from 80 to 5280 either via NAT
|
||||
## (for ipv4 only) or using frontends such as haproxy/nginx/sslh/etc.
|
||||
## -
|
||||
## port: 5280
|
||||
## ip: "::"
|
||||
## module: ejabberd_http
|
||||
|
||||
acme:
|
||||
|
||||
## A contact mail that the ACME Certificate Authority can contact in case of
|
||||
## an authorization issue, such as a server-initiated certificate revocation.
|
||||
## It is not mandatory to provide an email address but it is highly suggested.
|
||||
contact: "mailto:example-admin@example.com"
|
||||
|
||||
|
||||
## The ACME Certificate Authority URL.
|
||||
## This could either be:
|
||||
## - https://acme-v01.api.letsencrypt.org - (Default) for the production CA
|
||||
## - https://acme-staging.api.letsencrypt.org - for the staging CA
|
||||
## - http://localhost:4000 - for a local version of the CA
|
||||
ca_url: "https://acme-v01.api.letsencrypt.org"
|
||||
|
||||
###. =======
|
||||
###' MODULES
|
||||
|
||||
##
|
||||
## Modules enabled in all ejabberd virtual hosts.
|
||||
##
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
mod_admin_extra: {}
|
||||
mod_announce: # recommends mod_adhoc
|
||||
mod_announce:
|
||||
access: announce
|
||||
mod_blocking: {} # requires mod_privacy
|
||||
mod_avatar: {}
|
||||
mod_blocking: {}
|
||||
mod_bosh: {}
|
||||
mod_caps: {}
|
||||
mod_carboncopy: {}
|
||||
mod_client_state: {}
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
## mod_delegation: {} # for xep0356
|
||||
mod_configure: {}
|
||||
mod_disco: {}
|
||||
mod_echo: {}
|
||||
mod_bosh: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
## accesslog: "/var/log/ejabberd/access.log"
|
||||
## mod_http_upload:
|
||||
## # docroot: "@HOME@/upload"
|
||||
## put_url: "https://@HOST@:5444"
|
||||
## thumbnail: false # otherwise needs ejabberd to be compiled with libgd support
|
||||
## mod_http_upload_quota:
|
||||
## max_days: 30
|
||||
mod_fail2ban: {}
|
||||
mod_http_api: {}
|
||||
mod_http_upload:
|
||||
put_url: "https://@HOST@:5443/upload"
|
||||
mod_last: {}
|
||||
## XEP-0313: Message Archive Management
|
||||
## You might want to setup a SQL backend for MAM because the mnesia database is
|
||||
## limited to 2GB which might be exceeded on large servers
|
||||
## mod_mam: {} # for xep0313, mnesia is limited to 2GB, better use an SQL backend
|
||||
mod_mam:
|
||||
## Mnesia is limited to 2GB, better to use an SQL backend
|
||||
## For small servers SQLite is a good fit and is very easy
|
||||
## to configure. Uncomment this when you have SQL configured:
|
||||
## db_type: sql
|
||||
assume_mam_usage: true
|
||||
default: always
|
||||
mod_muc:
|
||||
## host: "conference.@HOST@"
|
||||
access:
|
||||
- allow
|
||||
access_admin:
|
||||
- allow: admin
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
default_room_options:
|
||||
mam: true
|
||||
mod_muc_admin: {}
|
||||
## mod_muc_log: {}
|
||||
## mod_multicast: {}
|
||||
mod_offline:
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
mod_ping: {}
|
||||
## mod_pres_counter:
|
||||
## count: 5
|
||||
## interval: 60
|
||||
mod_privacy: {}
|
||||
mod_private: {}
|
||||
## mod_proxy65: {}
|
||||
mod_proxy65:
|
||||
access: local
|
||||
max_connections: 5
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
## reduces resource comsumption, but XEP incompliant
|
||||
ignore_pep_from_offline: true
|
||||
## XEP compliant, but increases resource comsumption
|
||||
## ignore_pep_from_offline: false
|
||||
last_item_cache: false
|
||||
plugins:
|
||||
- "flat"
|
||||
- "hometree"
|
||||
- "pep" # pep requires mod_caps
|
||||
- "pep"
|
||||
force_node_config:
|
||||
## Avoid using OMEMO by default because it
|
||||
## introduces a lot of hard-to-track problems.
|
||||
## Comment out the following lines to enable OMEMO support
|
||||
## Change from "whitelist" to "open" to enable OMEMO support
|
||||
## See https://github.com/processone/ejabberd/issues/2425
|
||||
"eu.siacs.conversations.axolotl.*":
|
||||
access_model: whitelist
|
||||
## Avoid buggy clients to make their bookmarks public
|
||||
@@ -791,82 +197,25 @@ modules:
|
||||
access_model: whitelist
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
## mod_register:
|
||||
##
|
||||
## Protect In-Band account registrations with CAPTCHA.
|
||||
##
|
||||
## captcha_protected: true
|
||||
##
|
||||
## Set the minimum informational entropy for passwords.
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
## When a user registers, send a notification to
|
||||
## these XMPP accounts.
|
||||
##
|
||||
## registration_watchers:
|
||||
## - "admin1@example.org"
|
||||
##
|
||||
## Only clients in the server machine can register accounts
|
||||
##
|
||||
## ip_access: trusted_network
|
||||
##
|
||||
## Local c2s or remote s2s users cannot register accounts
|
||||
##
|
||||
## access_from: deny
|
||||
## access: register
|
||||
mod_roster: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stats: {}
|
||||
mod_time: {}
|
||||
mod_vcard:
|
||||
search: false
|
||||
mod_vcard_xupdate: {}
|
||||
mod_avatar: {}
|
||||
mod_version: {}
|
||||
mod_stream_mgmt: {}
|
||||
## Non-SASL Authentication (XEP-0078) is now disabled by default
|
||||
## because it's obsoleted and is used mostly by abandoned
|
||||
## client software
|
||||
## mod_legacy_auth: {}
|
||||
## The module for S2S dialback (XEP-0220). Please note that you cannot
|
||||
## rely solely on dialback if you want to federate with other servers,
|
||||
## because a lot of servers have dialback disabled and instead rely on
|
||||
## PKIX authentication. Make sure you have proper certificates installed
|
||||
## and check your accessibility at https://check.messaging.one/
|
||||
mod_register:
|
||||
## Only accept registration requests from the "trusted"
|
||||
## network (see access_rules section above).
|
||||
## Think twice before enabling registration from any
|
||||
## address. See the Jabber SPAM Manifesto for details:
|
||||
## https://github.com/ge0rg/jabber-spam-fighting-manifesto
|
||||
ip_access: trusted_network
|
||||
mod_roster:
|
||||
versioning: true
|
||||
mod_s2s_dialback: {}
|
||||
mod_http_api: {}
|
||||
mod_fail2ban: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stream_mgmt:
|
||||
resend_on_timeout: if_offline
|
||||
mod_vcard: {}
|
||||
mod_vcard_xupdate: {}
|
||||
mod_version:
|
||||
show_os: false
|
||||
|
||||
##
|
||||
## Enable modules with custom options in a specific virtual host
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## modules:
|
||||
## mod_echo:
|
||||
## host: "mirror.localhost"
|
||||
|
||||
##
|
||||
## Enable modules management via ejabberdctl for installation and
|
||||
## uninstallation of public/private contributed modules
|
||||
## (enabled by default)
|
||||
##
|
||||
|
||||
allow_contrib_modules: true
|
||||
|
||||
###.
|
||||
###'
|
||||
### Local Variables:
|
||||
### mode: yaml
|
||||
### End:
|
||||
### vim: set filetype=yaml tabstop=8 foldmarker=###',###. foldmethod=marker:
|
||||
### vim: set filetype=yaml tabstop=8
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-type matchspec_atom() :: '_' | '$1' | '$2' | '$3' | '$4'.
|
||||
-record(carboncopy, {us :: {binary(), binary()} | matchspec_atom(),
|
||||
resource :: binary() | matchspec_atom(),
|
||||
version :: binary() | matchspec_atom(),
|
||||
node = node() :: node() | matchspec_atom()}).
|
||||
|
||||
-define(CARBONCOPY_CACHE, carboncopy_cache).
|
||||
@@ -22,8 +22,6 @@
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(lqueue,
|
||||
{
|
||||
queue :: p1_queue:queue(),
|
||||
@@ -105,13 +103,13 @@
|
||||
access = {none,none,none,none} :: {atom(), atom(), atom(), atom()},
|
||||
jid = #jid{} :: jid(),
|
||||
config = #config{} :: config(),
|
||||
users = (?DICT):new() :: dict:dict(),
|
||||
subscribers = (?DICT):new() :: dict:dict(),
|
||||
subscriber_nicks = (?DICT):new() :: dict:dict(),
|
||||
users = #{} :: map(),
|
||||
subscribers = #{} :: map(),
|
||||
subscriber_nicks = #{} :: map(),
|
||||
last_voice_request_time = treap:empty() :: treap:treap(),
|
||||
robots = (?DICT):new() :: dict:dict(),
|
||||
nicks = (?DICT):new() :: dict:dict(),
|
||||
affiliations = (?DICT):new() :: dict:dict(),
|
||||
robots = #{} :: map(),
|
||||
nicks = #{} :: map(),
|
||||
affiliations = #{} :: map(),
|
||||
history :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = <<"">> :: binary(),
|
||||
|
||||
+5
-5
@@ -129,13 +129,13 @@
|
||||
id ,% :: mod_pubsub:nodeIdx(),
|
||||
parents = [] ,% :: [mod_pubsub:nodeId(),...],
|
||||
type = <<"flat">>,% :: binary(),
|
||||
owners = [] ,% :: [jlib:ljid(),...],
|
||||
owners = [] ,% :: [jid:ljid(),...],
|
||||
options = [] % :: mod_pubsub:nodeOptions()
|
||||
}).
|
||||
|
||||
-record(pubsub_state,
|
||||
{
|
||||
stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
|
||||
stateid ,% :: {jid:ljid(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
items = [] ,% :: [mod_pubsub:itemId(),...],
|
||||
affiliation = 'none',% :: mod_pubsub:affiliation(),
|
||||
@@ -146,8 +146,8 @@
|
||||
{
|
||||
itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
creation = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()},
|
||||
modification = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()},
|
||||
payload = [] % :: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
{
|
||||
nodeid ,% :: {binary(), mod_pubsub:nodeIdx()},
|
||||
itemid ,% :: mod_pubsub:itemId(),
|
||||
creation ,% :: {erlang:timestamp(), jlib:ljid()},
|
||||
creation ,% :: {erlang:timestamp(), jid:ljid()},
|
||||
payload % :: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(scram, {storedkey = <<"">> :: binary(),
|
||||
serverkey = <<"">> :: binary(),
|
||||
salt = <<"">> :: binary(),
|
||||
iterationcount = 0 :: integer()}).
|
||||
|
||||
-type scram() :: #scram{}.
|
||||
|
||||
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
|
||||
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "18.6.0",
|
||||
version: "18.12.0",
|
||||
description: description(),
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: ["lib"],
|
||||
@@ -29,7 +29,7 @@ defmodule Ejabberd.Mixfile do
|
||||
included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab,
|
||||
:fast_tls, :stringprep, :fast_xml, :xmpp,
|
||||
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2,
|
||||
:eimp, :base64url, :jose]
|
||||
:eimp, :base64url, :jose, :pkix]
|
||||
++ cond_apps()]
|
||||
end
|
||||
|
||||
@@ -58,7 +58,7 @@ defmodule Ejabberd.Mixfile do
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[{:lager, "~> 3.4.0"},
|
||||
[{:lager, "~> 3.6.0"},
|
||||
{:p1_utils, "~> 1.0"},
|
||||
{:fast_xml, "~> 1.1"},
|
||||
{:xmpp, "~> 1.2"},
|
||||
@@ -73,6 +73,7 @@ defmodule Ejabberd.Mixfile do
|
||||
{:jiffy, "~> 0.14.7"},
|
||||
{:p1_oauth2, "~> 0.6.1"},
|
||||
{:distillery, "~> 1.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
{:ex_doc, ">= 0.0.0", only: :dev},
|
||||
{:eimp, "~> 1.0"},
|
||||
{:base64url, "~> 0.0.1"},
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
%{
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.14", "e68d24789ff596a7cb4f08780f72a725f3f18e93dee486559b261df904234871", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.3", "b2f4fc34ec71ab4f1202a796f9290e068883b042319aa8c9aa45377ecac8597a", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.6", "087fc92daf7b03bac4aada8ea6063d9034d4b9088be24e050ff73323c8444a04", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.17", "0020e1036d7074d83a71be28b329ceb3e7f9d41cc5a8529b06c32ce4d8ee4995", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.5", "c6132d5c0152bdce6850fb6c942d58f1971b169b6965d908dc4e8767cfa51a95", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.9", "daf0d2904be3ef5e4128d946e158113cdb4d52555023746d29b83ce86b531f3c", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"epam": {:hex, :epam, "1.0.4", "2a5e40cbf9b2cf41df515782894c3b33c81b8ad32fff2fc847c3f725071dfaed", [:rebar3], [], "hexpm"},
|
||||
"eredis": {:hex, :eredis, "1.1.0", "8d8d74496f35216679b97726b75fb1c8715e99dd7f3ef9f9824a2264c3e0aac0", [:rebar3], [], "hexpm"},
|
||||
"esip": {:hex, :esip, "1.0.24", "c14efd0817012721dad3d1c36816e4d113adc86d185503f8c08f7faa11082018", [:rebar3], [{:fast_tls, "1.0.23", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.23", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"esip": {:hex, :esip, "1.0.27", "13a94542b659a9b3e4e013aedaf2f6a92de53d35945902d693657a67c6955b83", [:rebar3], [{:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.26", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ezlib": {:hex, :ezlib, "1.0.4", "2434e4bb549cb060d5ac02261ba48fbe1a69b2ae4e1bf7485a3b27b3f3ec618d", [:rebar3], [], "hexpm"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.23", "5fd44b84b60f1caeb82270aae842599997e2ef70a628c954e494a6613cc6f026", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.31", "7d85dd50533737adbaa95472c962ff8ad3d434ced1dbd04b83465edc2a4363b6", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.15", "55a8ff117e2bb44fda5ca96c53473799f864c865dc63a3b598cb626001207cab", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.26", "38d78859ca56e8600aca3ef73137582a279a280d71f7581c64a1eddbde38accb", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.34", "d76fc639d3607a44c4f0fb2dfdee1067b6c37b02b39706d8f067cb77eb2f6016", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.17", "e945ef64e0cb7c311c7b42804dbe32a24e13a2afc0ffe249b7e0f9f9ac08e176", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"},
|
||||
"hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"iconv": {:hex, :iconv, "1.0.8", "772a19153e9546b9f17206931ece321220feccfd0b3f5a5509dc92108326c2f9", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"iconv": {:hex, :iconv, "1.0.10", "fc7de75c0a1fbc1e4ed0c68637ae7464a5dd9eee81710fff26321922d144ecea", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [:rebar3], [], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"lager": {:hex, :lager, "3.6.7", "2fbf823944caa0fc10df5ec13f3f047524a249bb32f0d801b7900c9610264286", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.10", "455aaef8403be46752272206613e7a15467c014d40994b22fb54cde4d1ff7075", [:rebar3], [], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.12", "1f7b1a9f5d12c511848fec26bbefd09a21e1432eadb8982d9a8aceb9891a3cf2", [:rebar3], [], "hexpm"},
|
||||
"moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.6", "1fb48a907a7fe214a78be15a08f8ebfae2db424c4d9886891a298a395cc3afce", [:rebar3], [], "hexpm"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.8", "34ed5fe2f0e16a6ee5805c0c6c1d30ffbc4c5c9753197cdf384ee6e82c57b506", [:rebar3], [], "hexpm"},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.3", "fbd91ba86bd7f03d2a4f6e62affa86bab9930abfd6b473d61eefb148f246cd46", [:rebar3], [], "hexpm"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.6", "631004602b06ca6f55d759001f180185685c7097e583f3b0f7dd9b8e05ba5db1", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.12", "4304223a2b78d8031e980ae18d6b6d83f764cf2a91c779b2df7cbdfde73acbf8", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.13", "176577cafb54a8c2fdc0a7fc62b9a21ab0f66588f4062792cd9e65f3e500bfdb", [:rebar3], [], "hexpm"},
|
||||
"pkix": {:hex, :pkix, "1.0.0", "d88658eccc30227e929efa91c6ca6a4d2b4d40b4db3635ebd6ed9e246ecfcf82", [:rebar3], [], "hexpm"},
|
||||
"riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]},
|
||||
"sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.12", "3364897b9a376b2fb5e429944fd34ca0b562b44c9e5acf4e0299564371a6fbef", [:rebar3], [{:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.23", "79982f6a26dc65b58bb24082e8bbfba83a5228ddd6407753e9c3092fb45ba916", [:rebar3], [{:fast_tls, "1.0.23", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.2.1", "9c94ab556559dc6c3bf4963f756a02e9771dd220f5891d57fc30b76c3d71cb06", [:rebar3], [{:fast_xml, "1.1.31", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.12", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.12", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.14", "230a2d1c576bba168749d653fd6681166d02431ef07161a918444f3edb478ad0", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.26", "87b05229d0519f0db5c6b67b5c25ed3b79766beb96eba83d29bde4cae9e702e4", [:rebar3], [{:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.2.7", "79bbe73f4172c8a4ea3cf8ae81c85f395cc651aed446b4d2646f7e9f55e12d74", [:rebar3], [{:ezlib, "1.0.4", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.34", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.14", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
redis.replicate_commands()
|
||||
local cursor = redis.call('GET', KEYS[3]) or 0
|
||||
local scan_result = redis.call('HSCAN', KEYS[1], cursor, 'COUNT', ARGV[1])
|
||||
local newcursor = scan_result[1]
|
||||
local cursor = redis.call('SET', KEYS[3], newcursor)
|
||||
redis.call('EXPIRE', KEYS[3], 30)
|
||||
for key,value in ipairs(scan_result[2]) do
|
||||
local uskey, sidkey = string.match(value, '(.*)||(.*)')
|
||||
if uskey and sidkey then
|
||||
redis.call('HDEL', uskey, sidkey)
|
||||
redis.call('HDEL', KEYS[1], value)
|
||||
else
|
||||
redis.call('HDEL', KEYS[2], value)
|
||||
end
|
||||
end
|
||||
return newcursor
|
||||
+123
-121
@@ -152,7 +152,7 @@ msgstr "Agosto"
|
||||
|
||||
#: mod_pubsub:1842
|
||||
msgid "Automatic node creation is not enabled"
|
||||
msgstr ""
|
||||
msgstr "Criação automatizada de nós está desabilitada"
|
||||
|
||||
#: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617
|
||||
msgid "Backup"
|
||||
@@ -185,11 +185,11 @@ msgstr "Aniversário"
|
||||
|
||||
#: mod_legacy_auth:102
|
||||
msgid "Both the username and the resource are required"
|
||||
msgstr ""
|
||||
msgstr "Nome de usuário e recurso são necessários"
|
||||
|
||||
#: mod_proxy65_service:226
|
||||
msgid "Bytestream already activated"
|
||||
msgstr ""
|
||||
msgstr "Bytestream já foi ativado"
|
||||
|
||||
#: ejabberd_captcha:135
|
||||
msgid "CAPTCHA web page"
|
||||
@@ -201,11 +201,11 @@ msgstr "Tempo de CPU"
|
||||
|
||||
#: mod_privacy:334
|
||||
msgid "Cannot remove active list"
|
||||
msgstr ""
|
||||
msgstr "Não é possível remover uma lista ativa"
|
||||
|
||||
#: mod_privacy:341
|
||||
msgid "Cannot remove default list"
|
||||
msgstr ""
|
||||
msgstr "Não é possível remover uma lista padrão"
|
||||
|
||||
#: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353
|
||||
#: mod_register_web:361 mod_register_web:386
|
||||
@@ -383,7 +383,7 @@ msgstr "Exportar para arquivo texto"
|
||||
|
||||
#: mod_roster:180
|
||||
msgid "Duplicated groups are not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Grupos duplicados não são permitidos pela RFC6121"
|
||||
|
||||
#: mod_configure:1776
|
||||
msgid "Edit Properties"
|
||||
@@ -413,7 +413,7 @@ msgstr "Permitir criação de logs"
|
||||
|
||||
#: mod_push:252
|
||||
msgid "Enabling push without 'node' attribute is not supported"
|
||||
msgstr ""
|
||||
msgstr "Abilitar push sem o atributo 'node' não é suportado"
|
||||
|
||||
#: mod_irc:834
|
||||
msgid "Encoding for server ~b"
|
||||
@@ -503,15 +503,15 @@ msgstr ""
|
||||
|
||||
#: mod_delegation:275
|
||||
msgid "External component failure"
|
||||
msgstr ""
|
||||
msgstr "Falha de componente externo"
|
||||
|
||||
#: mod_delegation:283
|
||||
msgid "External component timeout"
|
||||
msgstr ""
|
||||
msgstr "Tempo esgotado à espera de componente externo"
|
||||
|
||||
#: mod_proxy65_service:218
|
||||
msgid "Failed to activate bytestream"
|
||||
msgstr ""
|
||||
msgstr "Falha ao ativar bytestream"
|
||||
|
||||
#: mod_muc_room:910
|
||||
msgid "Failed to extract JID from your voice request approval"
|
||||
@@ -519,19 +519,19 @@ msgstr "Não foi possível extrair o JID (Jabber ID) da requisição de voz"
|
||||
|
||||
#: mod_delegation:257
|
||||
msgid "Failed to map delegated namespace to external component"
|
||||
msgstr ""
|
||||
msgstr "Falha ao mapear namespace delegado ao componente externo"
|
||||
|
||||
#: mod_http_upload:602
|
||||
msgid "Failed to parse HTTP response"
|
||||
msgstr ""
|
||||
msgstr "Falha ao analisar resposta HTTP"
|
||||
|
||||
#: mod_irc:426
|
||||
msgid "Failed to parse chanserv"
|
||||
msgstr ""
|
||||
msgstr "Falha ao analisar chanserv"
|
||||
|
||||
#: mod_muc_room:3297
|
||||
msgid "Failed to process option '~s'"
|
||||
msgstr ""
|
||||
msgstr "Falha ao processar opção '~s'"
|
||||
|
||||
#: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103
|
||||
#: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172
|
||||
@@ -544,7 +544,7 @@ msgstr "Fevereiro"
|
||||
|
||||
#: mod_http_upload:555
|
||||
msgid "File larger than ~w bytes"
|
||||
msgstr ""
|
||||
msgstr "Arquivo maior que ~w bytes"
|
||||
|
||||
#: mod_vcard:437
|
||||
msgid ""
|
||||
@@ -610,7 +610,7 @@ msgstr "Máquina"
|
||||
|
||||
#: mod_s2s_dialback:325
|
||||
msgid "Host unknown"
|
||||
msgstr ""
|
||||
msgstr "Máquina desconhecida"
|
||||
|
||||
#: ejabberd_web_admin:2463
|
||||
msgid "IP"
|
||||
@@ -699,15 +699,15 @@ msgstr "Importar dados dos usuários de um diretório-fila jabberd14:"
|
||||
|
||||
#: xmpp_stream_in:983
|
||||
msgid "Improper 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' incorreto"
|
||||
|
||||
#: xmpp_stream_in:477
|
||||
msgid "Improper 'to' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'to' incorreto"
|
||||
|
||||
#: ejabberd_service:189
|
||||
msgid "Improper domain part of 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' contém domínio incorreto"
|
||||
|
||||
#: mod_muc_room:260
|
||||
msgid "Improper message type"
|
||||
@@ -715,11 +715,11 @@ msgstr "Tipo de mensagem incorreto"
|
||||
|
||||
#: ejabberd_web_admin:1586
|
||||
msgid "Incoming s2s Connections:"
|
||||
msgstr "Conexões que entram de s2s"
|
||||
msgstr "Conexões s2s de Entrada:"
|
||||
|
||||
#: mod_muc_room:3669 mod_register:187
|
||||
msgid "Incorrect CAPTCHA submit"
|
||||
msgstr ""
|
||||
msgstr "CAPTCHA submetido incorretamente"
|
||||
|
||||
#: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256
|
||||
#, fuzzy
|
||||
@@ -732,41 +732,41 @@ msgstr "Senha incorreta"
|
||||
|
||||
#: mod_irc:585
|
||||
msgid "Incorrect value in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto em formulário de dados"
|
||||
|
||||
#: mod_adhoc:276
|
||||
msgid "Incorrect value of 'action' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto do atributo 'action'"
|
||||
|
||||
#: mod_configure:1806
|
||||
msgid "Incorrect value of 'action' in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto de 'action' no formulário de dados"
|
||||
|
||||
#: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419
|
||||
#: mod_configure:1439
|
||||
msgid "Incorrect value of 'path' in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto de 'path' no formulário de dados"
|
||||
|
||||
#: mod_irc:331
|
||||
msgid "Incorrect value of 'type' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto do atributo 'type'"
|
||||
|
||||
#: mod_privilege:100
|
||||
msgid "Insufficient privilege"
|
||||
msgstr ""
|
||||
msgstr "Privilégio insuficiente"
|
||||
|
||||
#: mod_privilege:286
|
||||
msgid "Invalid 'from' attribute in forwarded message"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' inválido na mensagem reenviada"
|
||||
|
||||
#: mod_privilege:300
|
||||
msgid "Invalid <forwarded/> element"
|
||||
msgstr ""
|
||||
msgstr "Elemento <forwarded/> inválido"
|
||||
|
||||
#: mod_muc_room:3926
|
||||
#, fuzzy
|
||||
msgid "Invitations are not allowed in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Convites estão desabilitados nesta sala de conferência"
|
||||
|
||||
#: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046
|
||||
msgid ""
|
||||
@@ -786,11 +786,11 @@ msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\""
|
||||
|
||||
#: mod_muc_room:244
|
||||
msgid "It is not allowed to send private messages to the conference"
|
||||
msgstr "Impedir o envio de mensagens privadas para a sala"
|
||||
msgstr "Não é permitido enviar mensagens privadas para a sala de conferência"
|
||||
|
||||
#: mod_register_web:181 mod_register_web:189
|
||||
msgid "Jabber Account Registration"
|
||||
msgstr "Registros de Contas Jabber"
|
||||
msgstr "Registro de Contas Jabber"
|
||||
|
||||
#: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159
|
||||
#: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600
|
||||
@@ -805,11 +805,11 @@ msgstr "Janeiro"
|
||||
|
||||
#: mod_irc:665
|
||||
msgid "Join IRC channel"
|
||||
msgstr "Juntar-se ao canal IRC"
|
||||
msgstr "Entrar no canal IRC"
|
||||
|
||||
#: mod_irc:689
|
||||
msgid "Join the IRC channel here."
|
||||
msgstr "Aqui! Juntar-se ao canal IRC."
|
||||
msgstr "Entre no canal IRC aqui."
|
||||
|
||||
#: mod_irc:690
|
||||
msgid "Join the IRC channel in this Jabber ID: ~s"
|
||||
@@ -890,7 +890,7 @@ msgstr "Tornar sala pública possível de ser encontrada"
|
||||
#: mod_register:317
|
||||
#, fuzzy
|
||||
msgid "Malformed username"
|
||||
msgstr "Usuário IRC"
|
||||
msgstr "Nome de usuário inválido"
|
||||
|
||||
#: mod_muc_log:475
|
||||
msgid "March"
|
||||
@@ -910,7 +910,7 @@ msgstr "Membros:"
|
||||
|
||||
#: mod_muc_room:1833
|
||||
msgid "Membership is required to enter this room"
|
||||
msgstr "Necessitas ser membro desta sala para poder entrar"
|
||||
msgstr "É necessário ser membro desta sala para poder entrar"
|
||||
|
||||
#: mod_register_web:262
|
||||
msgid ""
|
||||
@@ -918,9 +918,9 @@ msgid ""
|
||||
"Jabber there isn't an automated way to recover your password if you forget "
|
||||
"it."
|
||||
msgstr ""
|
||||
"Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar "
|
||||
"seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se "
|
||||
"você a esquecer eventualmente."
|
||||
"Memorize a sua senha, ou anote-a em um papel, guardado em um local seguro. "
|
||||
"No Jabber, não há uma maneira automatizada de recuperar a sua senha, "
|
||||
"caso a esqueça."
|
||||
|
||||
#: ejabberd_web_admin:1954
|
||||
msgid "Memory"
|
||||
@@ -932,7 +932,7 @@ msgstr "Corpo da mensagem"
|
||||
|
||||
#: mod_privilege:291
|
||||
msgid "Message not found in forwarded payload"
|
||||
msgstr ""
|
||||
msgstr "Mensagem não encontrada em conteúdo encaminhado"
|
||||
|
||||
#: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102
|
||||
#: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171
|
||||
@@ -941,15 +941,15 @@ msgstr "Nome do meio"
|
||||
|
||||
#: mod_irc:704
|
||||
msgid "Missing 'channel' or 'server' in the data form"
|
||||
msgstr ""
|
||||
msgstr "Faltando 'channel' ou 'server' no formulário de dados"
|
||||
|
||||
#: xmpp_stream_in:990
|
||||
msgid "Missing 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Faltando atributo 'from'"
|
||||
|
||||
#: xmpp_stream_in:472 xmpp_stream_in:993
|
||||
msgid "Missing 'to' attribute"
|
||||
msgstr ""
|
||||
msgstr "Faltando atributo 'to'"
|
||||
|
||||
#: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798
|
||||
msgid "Moderator privileges required"
|
||||
@@ -965,7 +965,7 @@ msgstr "Módulo"
|
||||
|
||||
#: gen_iq_handler:153
|
||||
msgid "Module failed to handle the query"
|
||||
msgstr ""
|
||||
msgstr "Módulo falhou ao processar a consulta"
|
||||
|
||||
#: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595
|
||||
msgid "Modules"
|
||||
@@ -989,7 +989,7 @@ msgstr "Multicast"
|
||||
|
||||
#: mod_roster:195
|
||||
msgid "Multiple <item/> elements are not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Vários elementos <item/> não são permitidos pela RFC6121"
|
||||
|
||||
#: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115
|
||||
#: mod_vcard_sql:156 mod_vcard_sql:170
|
||||
@@ -1002,11 +1002,11 @@ msgstr "Nome:"
|
||||
|
||||
#: mod_muc_room:2696
|
||||
msgid "Neither 'jid' nor 'nick' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Nem o atributo 'jid' nem 'nick' foram encontrados"
|
||||
|
||||
#: mod_muc_room:2518 mod_muc_room:2701
|
||||
msgid "Neither 'role' nor 'affiliation' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Nem o atributo 'role' nem 'affiliation' foram encontrados"
|
||||
|
||||
#: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629
|
||||
msgid "Never"
|
||||
@@ -1031,41 +1031,41 @@ msgstr "O apelido ~s não existe na sala"
|
||||
|
||||
#: mod_configure:1496
|
||||
msgid "No 'access' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'access' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_configure:1455
|
||||
msgid "No 'acls' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'acls' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_muc_room:3075
|
||||
msgid "No 'affiliation' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'affiliation' não foi encontrado"
|
||||
|
||||
#: mod_muc_room:2505
|
||||
#, fuzzy
|
||||
msgid "No 'item' element found"
|
||||
msgstr "Nó não encontrado"
|
||||
msgstr "Elemento 'item' não foi encontrado"
|
||||
|
||||
#: mod_configure:1280
|
||||
msgid "No 'modules' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'modules' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_configure:1799
|
||||
msgid "No 'password' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'password' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_register:148
|
||||
msgid "No 'password' found in this query"
|
||||
msgstr ""
|
||||
msgstr "'password' não foi encontrado nesta consulta"
|
||||
|
||||
#: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413
|
||||
#: mod_configure:1433
|
||||
msgid "No 'path' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'path' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_muc_room:3922
|
||||
msgid "No 'to' attribute found in the invitation"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'to' não foi encontrado no convite"
|
||||
|
||||
#: ejabberd_web_admin:1767
|
||||
msgid "No Data"
|
||||
@@ -1073,7 +1073,7 @@ msgstr "Nenhum dado"
|
||||
|
||||
#: ejabberd_local:181
|
||||
msgid "No available resource found"
|
||||
msgstr ""
|
||||
msgstr "Nenhum recurso disponível foi encontrado"
|
||||
|
||||
#: mod_announce:575
|
||||
msgid "No body provided for announce message"
|
||||
@@ -1082,45 +1082,45 @@ msgstr "Nenhum corpo de texto fornecido para anunciar mensagem"
|
||||
#: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289
|
||||
#, fuzzy
|
||||
msgid "No data form found"
|
||||
msgstr "Nó não encontrado"
|
||||
msgstr "Formulário de dados não foi encontrado"
|
||||
|
||||
#: mod_disco:224 mod_vcard:282
|
||||
msgid "No features available"
|
||||
msgstr ""
|
||||
msgstr "Nenhuma funcionalidade disponível"
|
||||
|
||||
#: mod_adhoc:239
|
||||
msgid "No hook has processed this command"
|
||||
msgstr ""
|
||||
msgstr "Nenhum hook processou este comando"
|
||||
|
||||
#: mod_last:218
|
||||
msgid "No info about last activity found"
|
||||
msgstr ""
|
||||
msgstr "Não foi encontrada informação sobre última atividade"
|
||||
|
||||
#: mod_blocking:99
|
||||
msgid "No items found in this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum item encontrado nesta consulta"
|
||||
|
||||
#: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110
|
||||
#: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580
|
||||
#: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207
|
||||
msgid "No module is handling this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum módulo está processando esta consulta"
|
||||
|
||||
#: mod_pubsub:1541
|
||||
msgid "No node specified"
|
||||
msgstr ""
|
||||
msgstr "Nenhum nó especificado"
|
||||
|
||||
#: mod_pubsub:1426
|
||||
msgid "No pending subscriptions found"
|
||||
msgstr ""
|
||||
msgstr "Não foram encontradas subscrições"
|
||||
|
||||
#: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344
|
||||
msgid "No privacy list with this name found"
|
||||
msgstr ""
|
||||
msgstr "Nenhuma lista de privacidade encontrada com este nome"
|
||||
|
||||
#: mod_private:96
|
||||
msgid "No private data found in this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum dado privado encontrado nesta consulta"
|
||||
|
||||
#: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254
|
||||
#: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377
|
||||
@@ -1131,15 +1131,15 @@ msgstr "Nó não encontrado"
|
||||
|
||||
#: mod_disco:252 mod_vcard:265
|
||||
msgid "No services available"
|
||||
msgstr ""
|
||||
msgstr "Não há serviços disponíveis"
|
||||
|
||||
#: mod_stats:101
|
||||
msgid "No statistics found for this item"
|
||||
msgstr ""
|
||||
msgstr "Não foram encontradas estatísticas para este item"
|
||||
|
||||
#: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255
|
||||
msgid "Node already exists"
|
||||
msgstr ""
|
||||
msgstr "Nó já existe"
|
||||
|
||||
#: nodetree_tree_sql:99
|
||||
#, fuzzy
|
||||
@@ -1159,7 +1159,7 @@ msgstr "Nó ~p"
|
||||
|
||||
#: mod_vcard:385
|
||||
msgid "Nodeprep has failed"
|
||||
msgstr ""
|
||||
msgstr "Processo de identificação de nó falhou (nodeprep)"
|
||||
|
||||
#: ejabberd_web_admin:1837
|
||||
msgid "Nodes"
|
||||
@@ -1176,7 +1176,7 @@ msgstr "Não encontrado"
|
||||
|
||||
#: mod_disco:296 mod_disco:370 mod_last:159
|
||||
msgid "Not subscribed"
|
||||
msgstr ""
|
||||
msgstr "Não subscrito"
|
||||
|
||||
#: mod_muc_log:483
|
||||
msgid "November"
|
||||
@@ -1227,11 +1227,11 @@ msgstr "Usuários online"
|
||||
|
||||
#: mod_carboncopy:141
|
||||
msgid "Only <enable/> or <disable/> tags are allowed"
|
||||
msgstr ""
|
||||
msgstr "Apenas tags <enable/> ou <disable/> são permitidas"
|
||||
|
||||
#: mod_privacy:154
|
||||
msgid "Only <list/> element is allowed in this query"
|
||||
msgstr ""
|
||||
msgstr "Apenas elemento <list/> é permitido nesta consulta"
|
||||
|
||||
#: mod_mam:379
|
||||
msgid "Only members may query archives of this room"
|
||||
@@ -1255,11 +1255,11 @@ msgstr "Somente moderadores podem aprovar requisições de voz"
|
||||
|
||||
#: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989
|
||||
msgid "Only occupants are allowed to send messages to the conference"
|
||||
msgstr "Somente os ocupantes podem enviar mensagens à sala"
|
||||
msgstr "Somente os ocupantes podem enviar mensagens à sala de conferência"
|
||||
|
||||
#: mod_muc_room:457
|
||||
msgid "Only occupants are allowed to send queries to the conference"
|
||||
msgstr "Somente os ocupantes podem enviar consultas à sala"
|
||||
msgstr "Somente os ocupantes podem enviar consultas à sala de conferência"
|
||||
|
||||
#: mod_muc:422
|
||||
msgid "Only service administrators are allowed to send service messages"
|
||||
@@ -1282,11 +1282,11 @@ msgstr "Departamento/Unidade"
|
||||
|
||||
#: mod_configure:508
|
||||
msgid "Outgoing s2s Connections"
|
||||
msgstr "Conexões que partam de s2s"
|
||||
msgstr "Conexões s2s de Saída"
|
||||
|
||||
#: ejabberd_web_admin:1583
|
||||
msgid "Outgoing s2s Connections:"
|
||||
msgstr "Conexões que partem de s2s"
|
||||
msgstr "Conexões s2s de Saída"
|
||||
|
||||
#: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302
|
||||
#: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184
|
||||
@@ -1300,11 +1300,11 @@ msgstr "Pacote"
|
||||
|
||||
#: mod_irc:578
|
||||
msgid "Parse error"
|
||||
msgstr ""
|
||||
msgstr "Erro de análise de dados"
|
||||
|
||||
#: mod_configure:1299 mod_configure:1468 mod_configure:1513
|
||||
msgid "Parse failed"
|
||||
msgstr ""
|
||||
msgstr "Análise de dados falhou"
|
||||
|
||||
#: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126
|
||||
#: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036
|
||||
@@ -1384,7 +1384,7 @@ msgstr "Porta ~b"
|
||||
|
||||
#: mod_roster:173
|
||||
msgid "Possessing 'ask' attribute is not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Possuir atributo 'ask' não é permitido pela RFC6121"
|
||||
|
||||
#: ejabberd_web_admin:2464
|
||||
msgid "Protocol"
|
||||
@@ -1400,16 +1400,16 @@ msgstr "Publicação de Tópico"
|
||||
|
||||
#: node_dag:81
|
||||
msgid "Publishing items to collection node is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Publicar items em um nó de coleção não é permitido"
|
||||
|
||||
#: mod_muc_room:462
|
||||
msgid "Queries to the conference members are not allowed in this room"
|
||||
msgstr "Nesta sala não se permite consultas aos membros da sala"
|
||||
msgstr "Nesta sala de conferência, consultas aos membros não são permitidas"
|
||||
|
||||
#: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146
|
||||
#: mod_private:118 mod_roster:163 mod_sic:90
|
||||
msgid "Query to another users is forbidden"
|
||||
msgstr ""
|
||||
msgstr "Consultar a outro usuário é proibido"
|
||||
|
||||
#: mod_configure:889
|
||||
msgid "RAM and disc copy"
|
||||
@@ -1540,7 +1540,7 @@ msgstr "Lista de contatos"
|
||||
|
||||
#: mod_roster:334
|
||||
msgid "Roster module has failed"
|
||||
msgstr ""
|
||||
msgstr "O módulo Roster falhou"
|
||||
|
||||
#: mod_roster:968
|
||||
msgid "Roster of "
|
||||
@@ -1556,7 +1556,7 @@ msgstr "Nós em execução"
|
||||
|
||||
#: xmpp_stream_in:541 xmpp_stream_in:549
|
||||
msgid "SASL negotiation is not allowed in this state"
|
||||
msgstr ""
|
||||
msgstr "Negociação SASL não é permitida neste estado"
|
||||
|
||||
#: mod_muc_log:468
|
||||
msgid "Saturday"
|
||||
@@ -1564,12 +1564,12 @@ msgstr "Sábado"
|
||||
|
||||
#: mod_irc:582
|
||||
msgid "Scan error"
|
||||
msgstr ""
|
||||
msgstr "Erro de escaneamento"
|
||||
|
||||
#: mod_configure:1303 mod_configure:1472 mod_configure:1517
|
||||
#, fuzzy
|
||||
msgid "Scan failed"
|
||||
msgstr "O CAPTCHA é inválido."
|
||||
msgstr "O escaneamento falhou"
|
||||
|
||||
#: ejabberd_web_admin:2282
|
||||
msgid "Script check"
|
||||
@@ -1605,11 +1605,11 @@ msgstr "Setembro"
|
||||
|
||||
#: mod_irc_connection:648
|
||||
msgid "Server Connect Failed"
|
||||
msgstr ""
|
||||
msgstr "Conexão ao servidor falhou"
|
||||
|
||||
#: ejabberd_s2s:369
|
||||
msgid "Server connections to local subdomains are forbidden"
|
||||
msgstr ""
|
||||
msgstr "Conexões de servidor a subdomínios locais estão proibidas"
|
||||
|
||||
#: mod_irc:842
|
||||
msgid "Server ~b"
|
||||
@@ -1724,7 +1724,7 @@ msgstr "Subscrição"
|
||||
|
||||
#: mod_muc_room:3708
|
||||
msgid "Subscriptions are not allowed"
|
||||
msgstr ""
|
||||
msgstr "Subscrições não estão permitidas"
|
||||
|
||||
#: mod_muc_log:469
|
||||
msgid "Sunday"
|
||||
@@ -1748,15 +1748,15 @@ msgstr "A verificação do CAPTCHA falhou"
|
||||
|
||||
#: mod_muc_room:302
|
||||
msgid "The feature requested is not supported by the conference"
|
||||
msgstr ""
|
||||
msgstr "A funcionalidade solicitada não é suportada pela sala de conferência"
|
||||
|
||||
#: mod_register:308 mod_register:366
|
||||
msgid "The password contains unacceptable characters"
|
||||
msgstr ""
|
||||
msgstr "A senha contém caracteres proibidos"
|
||||
|
||||
#: mod_register:311 mod_register:370
|
||||
msgid "The password is too weak"
|
||||
msgstr "Senha considerada fraca'"
|
||||
msgstr "Senha considerada muito fraca"
|
||||
|
||||
#: mod_register_web:141
|
||||
msgid "The password of your Jabber account was successfully changed."
|
||||
@@ -1764,17 +1764,19 @@ msgstr "A senha da sua conta Jabber foi mudada com sucesso."
|
||||
|
||||
#: mod_register:160 mod_vcard:219
|
||||
msgid "The query is only allowed from local users"
|
||||
msgstr ""
|
||||
msgstr "Esta consulta só é permitida a partir de usuários locais"
|
||||
|
||||
#: mod_roster:203
|
||||
msgid "The query must not contain <item/> elements"
|
||||
msgstr ""
|
||||
msgstr "A consulta não pode conter elementos <item/>"
|
||||
|
||||
#: mod_privacy:280
|
||||
msgid ""
|
||||
"The stanza MUST contain only one <active/> element, one <default/> element, "
|
||||
"or one <list/> element"
|
||||
msgstr ""
|
||||
"A instância DEVE conter apenas um elemento <active/>, um elemento <default/>, "
|
||||
"ou um elemento <list/>"
|
||||
|
||||
#: mod_register_web:146
|
||||
msgid "There was an error changing the password: "
|
||||
@@ -1830,7 +1832,7 @@ msgstr "Para"
|
||||
|
||||
#: mod_register:215
|
||||
msgid "To register, visit ~s"
|
||||
msgstr ""
|
||||
msgstr "Para registrar, visite ~s"
|
||||
|
||||
#: mod_configure:709
|
||||
msgid "To ~s"
|
||||
@@ -1838,19 +1840,19 @@ msgstr "Para ~s"
|
||||
|
||||
#: ejabberd_oauth:439
|
||||
msgid "Token TTL"
|
||||
msgstr ""
|
||||
msgstr "Token TTL"
|
||||
|
||||
#: xmpp_stream_in:463
|
||||
msgid "Too long value of 'xml:lang' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor do atributo 'xml:lang' é demasiado longo"
|
||||
|
||||
#: mod_muc_room:2541 mod_muc_room:3081
|
||||
msgid "Too many <item/> elements"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de elementos <item/>"
|
||||
|
||||
#: mod_privacy:164
|
||||
msgid "Too many <list/> elements"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de elementos <list/>"
|
||||
|
||||
#: mod_muc_room:1924 mod_register:240
|
||||
msgid "Too many CAPTCHA requests"
|
||||
@@ -1859,20 +1861,20 @@ msgstr "Número excessivo de requisições para o CAPTCHA"
|
||||
#: mod_proxy65_service:223
|
||||
#, fuzzy
|
||||
msgid "Too many active bytestreams"
|
||||
msgstr "número excessivo de instâncias sem confirmação"
|
||||
msgstr "Número excessivo de bytestreams ativos"
|
||||
|
||||
#: mod_stream_mgmt:205
|
||||
msgid "Too many unacked stanzas"
|
||||
msgstr "número excessivo de instâncias sem confirmação"
|
||||
msgstr "Número excessivo de instâncias sem confirmação"
|
||||
|
||||
#: mod_muc_room:1802
|
||||
#, fuzzy
|
||||
msgid "Too many users in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Número excessivo de usuários nesta sala de conferência"
|
||||
|
||||
#: mod_register:355
|
||||
msgid "Too many users registered"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de usuários registrados"
|
||||
|
||||
#: mod_muc_admin:368
|
||||
msgid "Total rooms"
|
||||
@@ -1908,7 +1910,7 @@ msgstr "Impossível gerar um CAPTCHA"
|
||||
|
||||
#: ejabberd_service:120
|
||||
msgid "Unable to register route on existing local domain"
|
||||
msgstr ""
|
||||
msgstr "Não foi possível registrar rota no domínio local existente"
|
||||
|
||||
#: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241
|
||||
#: ejabberd_web_admin:253
|
||||
@@ -1917,7 +1919,7 @@ msgstr "Não Autorizado"
|
||||
|
||||
#: mod_announce:485 mod_configure:830 mod_configure:1758
|
||||
msgid "Unexpected action"
|
||||
msgstr ""
|
||||
msgstr "Ação inesperada"
|
||||
|
||||
#: mod_register_web:488
|
||||
msgid "Unregister"
|
||||
@@ -1929,11 +1931,11 @@ msgstr "Deletar conta Jabber"
|
||||
|
||||
#: mod_mam:526
|
||||
msgid "Unsupported <index/> element"
|
||||
msgstr ""
|
||||
msgstr "Elemento <index/> não suportado"
|
||||
|
||||
#: mod_mix:119
|
||||
msgid "Unsupported MIX query"
|
||||
msgstr ""
|
||||
msgstr "Consula MIX não suportada"
|
||||
|
||||
#: ejabberd_web_admin:1872 ejabberd_web_admin:2285
|
||||
msgid "Update"
|
||||
@@ -1979,7 +1981,7 @@ msgstr "Usuário"
|
||||
|
||||
#: ejabberd_oauth:428
|
||||
msgid "User (jid)"
|
||||
msgstr ""
|
||||
msgstr "Usuário (jid)"
|
||||
|
||||
#: mod_configure:308 mod_configure:505
|
||||
msgid "User Management"
|
||||
@@ -1987,11 +1989,11 @@ msgstr "Gerenciamento de Usuários"
|
||||
|
||||
#: mod_register:345
|
||||
msgid "User already exists"
|
||||
msgstr ""
|
||||
msgstr "Usuário já existe"
|
||||
|
||||
#: mod_echo:138
|
||||
msgid "User part of JID in 'from' is empty"
|
||||
msgstr ""
|
||||
msgstr "Parte do usuário do JID em 'from' está vazia"
|
||||
|
||||
#: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106
|
||||
#, fuzzy
|
||||
@@ -2000,7 +2002,7 @@ msgstr "Nó não encontrado"
|
||||
|
||||
#: mod_stream_mgmt:561 mod_stream_mgmt:583
|
||||
msgid "User session terminated"
|
||||
msgstr ""
|
||||
msgstr "Sessão de usuário terminada"
|
||||
|
||||
#: ejabberd_web_admin:1700
|
||||
msgid "User ~s"
|
||||
@@ -2029,7 +2031,7 @@ msgstr "Validar"
|
||||
#: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802
|
||||
#: mod_pubsub:895 mod_push:249
|
||||
msgid "Value 'get' of 'type' attribute is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Valor 'get' não permitido para atributo 'type'"
|
||||
|
||||
#: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270
|
||||
#: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504
|
||||
@@ -2038,20 +2040,20 @@ msgstr ""
|
||||
#: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93
|
||||
#: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62
|
||||
msgid "Value 'set' of 'type' attribute is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Valor 'set' não permitido para atributo 'type'"
|
||||
|
||||
#: pubsub_subscription:237 pubsub_subscription_sql:202
|
||||
msgid "Value of '~s' should be boolean"
|
||||
msgstr ""
|
||||
msgstr "Value de '~s' deveria ser um booleano"
|
||||
|
||||
#: pubsub_subscription:215 pubsub_subscription_sql:180
|
||||
msgid "Value of '~s' should be datetime string"
|
||||
msgstr ""
|
||||
msgstr "Valor de '~s' deveria ser data e hora"
|
||||
|
||||
#: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174
|
||||
#: pubsub_subscription_sql:192
|
||||
msgid "Value of '~s' should be integer"
|
||||
msgstr ""
|
||||
msgstr "Valor de '~s' deveria ser um inteiro"
|
||||
|
||||
#: ejabberd_web_admin:950
|
||||
msgid "Virtual Hosts"
|
||||
@@ -2071,7 +2073,7 @@ msgstr "Requisição de voz"
|
||||
|
||||
#: mod_muc_room:885
|
||||
msgid "Voice requests are disabled in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta sala de conferência"
|
||||
|
||||
#: mod_muc_log:465
|
||||
msgid "Wednesday"
|
||||
@@ -2087,7 +2089,7 @@ msgstr "Você foi banido desta sala"
|
||||
|
||||
#: mod_muc_room:1811
|
||||
msgid "You have joined too many conferences"
|
||||
msgstr ""
|
||||
msgstr "Você entrou em um número excessivo de salas de conferência"
|
||||
|
||||
#: mod_muc:777
|
||||
msgid "You must fill in field \"Nickname\" in the form"
|
||||
@@ -2379,7 +2381,7 @@ msgstr "~s's Fila de Mensagens Offline"
|
||||
#~ msgstr "Preencha campos para buscar usuários Jabber que concordem"
|
||||
|
||||
#~ msgid "Outgoing s2s Servers:"
|
||||
#~ msgstr "Servidores que partem de s2s"
|
||||
#~ msgstr "Servidores s2s de Saída"
|
||||
|
||||
#~ msgid "Delete"
|
||||
#~ msgstr "Eliminar"
|
||||
|
||||
+17
-22
@@ -18,23 +18,23 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager",
|
||||
{tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.12"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.14"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.23"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.12"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.31"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.2.1"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.15"}}},
|
||||
{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager", "3.6.7"}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.13"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.17"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.26"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.14"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.34"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.2.7"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.17"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.3"}}},
|
||||
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.0"}}},
|
||||
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.6"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.23"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.24"}}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.9"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.26"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.27"}}}},
|
||||
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
|
||||
{tag, "1.0.6"}}}},
|
||||
{tag, "1.0.8"}}}},
|
||||
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
|
||||
{tag, "1.1.6"}}}},
|
||||
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
|
||||
@@ -52,13 +52,11 @@
|
||||
{if_not_rebar3, {if_var_true, elixir, {rebar_elixir_plugin, ".*",
|
||||
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}},
|
||||
{if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv",
|
||||
{tag, "1.0.8"}}}},
|
||||
{tag, "1.0.10"}}}},
|
||||
{if_var_true, tools, {luerl, ".*", {git, "https://github.com/rvirding/luerl",
|
||||
{tag, "v0.3"}}}},
|
||||
{if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck",
|
||||
{tag, "0.8.4"}}}},
|
||||
{if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka",
|
||||
{tag, "1.0.5d"}}}},
|
||||
{tag, "0.8.12"}}}},
|
||||
{if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis",
|
||||
{tag, "v1.0.8"}}}}]}.
|
||||
|
||||
@@ -78,6 +76,7 @@
|
||||
epam,
|
||||
ezlib,
|
||||
eimp,
|
||||
pkix,
|
||||
iconv]}}.
|
||||
|
||||
{erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl",
|
||||
@@ -96,13 +95,9 @@
|
||||
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
|
||||
{if_var_match, db_type, mssql, {d, 'mssql'}},
|
||||
{if_var_true, elixir, {d, 'ELIXIR_ENABLED'}},
|
||||
{if_have_fun, {crypto, strong_rand_bytes, 1}, {d, 'STRONG_RAND_BYTES'}},
|
||||
{if_have_fun, {rand, uniform, 1}, {d, 'RAND_UNIFORM'}},
|
||||
{if_have_fun, {gb_sets, iterator_from, 2}, {d, 'GB_SETS_ITERATOR_FROM'}},
|
||||
{if_have_fun, {public_key, short_name_hash, 1}, {d, 'SHORT_NAME_HASH'}},
|
||||
{if_var_true, new_sql_schema, {d, 'NEW_SQL_SCHEMA'}},
|
||||
{if_var_true, hipe, native},
|
||||
{src_dirs, [asn1, src,
|
||||
{src_dirs, [src,
|
||||
{if_var_true, tools, tools},
|
||||
{if_var_true, elixir, include}]}]}.
|
||||
|
||||
|
||||
@@ -387,17 +387,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL,
|
||||
PRIMARY KEY (server_host, username, resource)
|
||||
);
|
||||
|
||||
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host, username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
@@ -357,16 +357,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username, resource);
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy (username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
+3
-16
@@ -134,9 +134,9 @@ CREATE TABLE [dbo].[muc_online_users] (
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host)
|
||||
CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server);
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[muc_room_subscribers] (
|
||||
@@ -529,21 +529,8 @@ CREATE TABLE [dbo].[bosh] (
|
||||
(
|
||||
[sid] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE TABLE [dbo].[carboncopy] (
|
||||
[username] [varchar] (255) NOT NULL,
|
||||
[resource] [varchar] (255) NOT NULL,
|
||||
[namespace] [varchar] (255) NOT NULL,
|
||||
[node] [varchar] (255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [carboncopy_ur] ON [carboncopy] (username, resource)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [carboncopy_user] ON [carboncopy] (username)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[push_session] (
|
||||
[username] [varchar] (255) NOT NULL,
|
||||
[timestamp] [bigint] NOT NULL,
|
||||
@@ -555,5 +542,5 @@ CREATE TABLE [dbo].[push_session] (
|
||||
CREATE UNIQUE CLUSTERED INDEX [i_push_usn] ON [push_session] (username, service, node)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [i_push_ut] ON [push_session] (username, timestamp)
|
||||
CREATE UNIQUE INDEX [i_push_ut] ON [push_session] (username, timestamp)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
+4
-15
@@ -90,7 +90,7 @@ CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp);
|
||||
CREATE TABLE spool (
|
||||
username varchar(191) NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
xml BLOB NOT NULL,
|
||||
xml mediumtext NOT NULL,
|
||||
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -104,8 +104,8 @@ CREATE TABLE archive (
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer varchar(191) NOT NULL,
|
||||
bare_peer varchar(191) NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
xml mediumtext NOT NULL,
|
||||
txt mediumtext,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
kind varchar(10),
|
||||
nick varchar(191),
|
||||
@@ -276,7 +276,7 @@ CREATE TABLE pubsub_item (
|
||||
publisher text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL
|
||||
payload mediumtext NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36));
|
||||
@@ -403,17 +403,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75));
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL,
|
||||
PRIMARY KEY (server_host(191), username(191), resource(191))
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host(191), username(75));
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
+4
-14
@@ -80,7 +80,7 @@ CREATE INDEX i_sr_user_grp ON sr_user(grp);
|
||||
|
||||
CREATE TABLE spool (
|
||||
username varchar(191) NOT NULL,
|
||||
xml BLOB NOT NULL,
|
||||
xml mediumtext NOT NULL,
|
||||
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -93,8 +93,8 @@ CREATE TABLE archive (
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer varchar(191) NOT NULL,
|
||||
bare_peer varchar(191) NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
xml mediumtext NOT NULL,
|
||||
txt mediumtext,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
kind varchar(10),
|
||||
nick varchar(191),
|
||||
@@ -253,7 +253,7 @@ CREATE TABLE pubsub_item (
|
||||
publisher text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL
|
||||
payload mediumtext NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36));
|
||||
@@ -373,16 +373,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75));
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username(75), resource(75));
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy (username(75));
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
@@ -156,13 +156,6 @@
|
||||
-- CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username);
|
||||
-- ALTER TABLE sm ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- ALTER TABLE carboncopy ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
-- DROP INDEX i_carboncopy_ur;
|
||||
-- DROP INDEX i_carboncopy_user;
|
||||
-- ALTER TABLE carboncopy ADD PRIMARY KEY (server_host, username, resource);
|
||||
-- CREATE INDEX i_carboncopy_sh_user ON carboncopy USING btree (server_host, username);
|
||||
-- ALTER TABLE carboncopy ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
|
||||
CREATE TABLE users (
|
||||
username text NOT NULL,
|
||||
@@ -555,17 +548,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL,
|
||||
PRIMARY KEY (server_host, username, resource)
|
||||
);
|
||||
|
||||
CREATE INDEX i_carboncopy_sh_user ON carboncopy USING btree (server_host, username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
-10
@@ -377,16 +377,6 @@ CREATE TABLE bosh (
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy USING btree (username, resource);
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy USING btree (username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ normalize_spec(Spec) ->
|
||||
{ok, Net, Mask} ->
|
||||
{ip, {Net, Mask}};
|
||||
error ->
|
||||
?INFO_MSG("Invalid network address: ~p", [S]),
|
||||
?WARNING_MSG("Invalid network address: ~p", [S]),
|
||||
none
|
||||
end;
|
||||
BadVal ->
|
||||
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Cyrus SASL-like library
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/0, register_mechanism/3, listmech/1,
|
||||
server_new/7, server_start/3, server_step/2,
|
||||
get_mech/1, format_error/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-record(sasl_mechanism,
|
||||
{mechanism = <<"">> :: mechanism() | '$1',
|
||||
module :: atom(),
|
||||
password_type = plain :: password_type() | '$2'}).
|
||||
|
||||
-type(mechanism() :: binary()).
|
||||
-type(mechanisms() :: [mechanism(),...]).
|
||||
-type(password_type() :: plain | digest | scram).
|
||||
-type sasl_property() :: {username, binary()} |
|
||||
{authzid, binary()} |
|
||||
{mechanism, binary()} |
|
||||
{auth_module, atom()}.
|
||||
-type sasl_return() :: {ok, [sasl_property()]} |
|
||||
{ok, [sasl_property()], binary()} |
|
||||
{continue, binary(), sasl_state()} |
|
||||
{error, atom(), binary()}.
|
||||
|
||||
-type(sasl_mechanism() :: #sasl_mechanism{}).
|
||||
-type error_reason() :: cyrsasl_digest:error_reason() |
|
||||
cyrsasl_oauth:error_reason() |
|
||||
cyrsasl_plain:error_reason() |
|
||||
cyrsasl_scram:error_reason() |
|
||||
unsupported_mechanism | nodeprep_failed |
|
||||
empty_username | aborted.
|
||||
-record(sasl_state,
|
||||
{
|
||||
service,
|
||||
myname,
|
||||
realm,
|
||||
get_password,
|
||||
check_password,
|
||||
check_password_digest,
|
||||
mech_name = <<"">>,
|
||||
mech_mod,
|
||||
mech_state
|
||||
}).
|
||||
-type sasl_state() :: #sasl_state{}.
|
||||
-export_type([mechanism/0, mechanisms/0, sasl_mechanism/0, error_reason/0,
|
||||
sasl_state/0, sasl_return/0, sasl_property/0]).
|
||||
|
||||
-callback start(list()) -> any().
|
||||
-callback stop() -> any().
|
||||
-callback mech_new(binary(), fun(), fun(), fun()) -> any().
|
||||
-callback mech_step(any(), binary()) -> sasl_return().
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ets:new(sasl_mechanism,
|
||||
[named_table, public,
|
||||
{keypos, #sasl_mechanism.mechanism}]),
|
||||
cyrsasl_plain:start([]),
|
||||
cyrsasl_digest:start([]),
|
||||
cyrsasl_scram:start([]),
|
||||
cyrsasl_anonymous:start([]),
|
||||
cyrsasl_oauth:start([]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
cyrsasl_plain:stop(),
|
||||
cyrsasl_digest:stop(),
|
||||
cyrsasl_scram:stop(),
|
||||
cyrsasl_anonymous:stop(),
|
||||
cyrsasl_oauth:stop().
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec format_error(mechanism() | sasl_state(), error_reason()) -> {atom(), binary()}.
|
||||
format_error(_, unsupported_mechanism) ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>};
|
||||
format_error(_, nodeprep_failed) ->
|
||||
{'bad-protocol', <<"Nodeprep failed">>};
|
||||
format_error(_, empty_username) ->
|
||||
{'bad-protocol', <<"Empty username">>};
|
||||
format_error(_, aborted) ->
|
||||
{'aborted', <<"Aborted">>};
|
||||
format_error(#sasl_state{mech_mod = Mod}, Reason) ->
|
||||
Mod:format_error(Reason);
|
||||
format_error(Mech, Reason) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Mod}] ->
|
||||
Mod:format_error(Reason);
|
||||
[] ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>}
|
||||
end.
|
||||
|
||||
-spec register_mechanism(Mechanim :: mechanism(), Module :: module(),
|
||||
PasswordType :: password_type()) -> any().
|
||||
|
||||
register_mechanism(Mechanism, Module, PasswordType) ->
|
||||
ets:insert(sasl_mechanism,
|
||||
#sasl_mechanism{mechanism = Mechanism, module = Module,
|
||||
password_type = PasswordType}).
|
||||
|
||||
check_credentials(_State, Props) ->
|
||||
User = proplists:get_value(authzid, Props, <<>>),
|
||||
case jid:nodeprep(User) of
|
||||
error -> {error, nodeprep_failed};
|
||||
<<"">> -> {error, empty_username};
|
||||
_LUser -> ok
|
||||
end.
|
||||
|
||||
-spec listmech(Host ::binary()) -> Mechanisms::mechanisms().
|
||||
|
||||
listmech(Host) ->
|
||||
ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1',
|
||||
password_type = '$2', _ = '_'},
|
||||
case catch ejabberd_auth:store_type(Host) of
|
||||
external -> [{'==', '$2', plain}];
|
||||
scram -> [{'/=', '$2', digest}];
|
||||
{'EXIT', {undef, [{Module, store_type, []} | _]}} ->
|
||||
?WARNING_MSG("~p doesn't implement the function store_type/0",
|
||||
[Module]),
|
||||
[];
|
||||
_Else -> []
|
||||
end,
|
||||
['$1']}]).
|
||||
|
||||
-spec server_new(binary(), binary(), binary(), term(),
|
||||
fun(), fun(), fun()) -> sasl_state().
|
||||
server_new(Service, ServerFQDN, UserRealm, _SecFlags,
|
||||
GetPassword, CheckPassword, CheckPasswordDigest) ->
|
||||
#sasl_state{service = Service, myname = ServerFQDN,
|
||||
realm = UserRealm, get_password = GetPassword,
|
||||
check_password = CheckPassword,
|
||||
check_password_digest = CheckPasswordDigest}.
|
||||
|
||||
-spec server_start(sasl_state(), mechanism(), binary()) -> sasl_return().
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case lists:member(Mech,
|
||||
listmech(State#sasl_state.myname))
|
||||
of
|
||||
true ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} =
|
||||
Module:mech_new(State#sasl_state.myname,
|
||||
State#sasl_state.get_password,
|
||||
State#sasl_state.check_password,
|
||||
State#sasl_state.check_password_digest),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_name = Mech,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ -> {error, unsupported_mechanism, <<"">>}
|
||||
end;
|
||||
false -> {error, unsupported_mechanism, <<"">>}
|
||||
end.
|
||||
|
||||
-spec server_step(sasl_state(), binary()) -> sasl_return().
|
||||
server_step(State, ClientIn) ->
|
||||
Module = State#sasl_state.mech_mod,
|
||||
MechState = State#sasl_state.mech_state,
|
||||
case Module:mech_step(MechState, ClientIn) of
|
||||
{ok, Props} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props};
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{ok, Props, ServerOut} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props, ServerOut};
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{continue, ServerOut, NewMechState} ->
|
||||
{continue, ServerOut, State#sasl_state{mech_state = NewMechState}};
|
||||
{error, Error, Username} ->
|
||||
{error, Error, Username};
|
||||
{error, Error} ->
|
||||
{error, Error, <<"">>}
|
||||
end.
|
||||
|
||||
-spec get_mech(sasl_state()) -> binary().
|
||||
get_mech(#sasl_state{mech_name = Mech}) ->
|
||||
Mech.
|
||||
@@ -1,50 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_anonymous.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : ANONYMOUS SASL mechanism
|
||||
%%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_anonymous).
|
||||
|
||||
-protocol({xep, 175, '1.2'}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {server = <<"">> :: binary()}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"ANONYMOUS">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{server = Host}}.
|
||||
|
||||
mech_step(#state{}, _ClientIn) ->
|
||||
User = iolist_to_binary([randoms:get_string(),
|
||||
integer_to_binary(p1_time_compat:unique_integer([positive]))]),
|
||||
{ok, [{username, User},
|
||||
{authzid, User},
|
||||
{auth_module, ejabberd_auth_anonymous}]}.
|
||||
@@ -1,270 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_digest.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : DIGEST-MD5 SASL mechanism
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_digest).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2,
|
||||
parse/1, format_error/1, opt_type/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-type get_password_fun() :: fun((binary()) -> {false, any()} |
|
||||
{binary(), atom()}).
|
||||
-type check_password_fun() :: fun((binary(), binary(), binary(), binary(),
|
||||
fun((binary()) -> binary())) ->
|
||||
{boolean(), any()} |
|
||||
false).
|
||||
-type error_reason() :: parser_failed | invalid_digest_uri |
|
||||
not_authorized | unexpected_response.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
-record(state, {step = 1 :: 1 | 3 | 5,
|
||||
nonce = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
authzid = <<"">> :: binary(),
|
||||
get_password :: get_password_fun(),
|
||||
check_password :: check_password_fun(),
|
||||
auth_module :: atom(),
|
||||
host = <<"">> :: binary(),
|
||||
hostfqdn = [] :: [binary()]}).
|
||||
|
||||
start(_Opts) ->
|
||||
Fqdn = get_local_fqdn(),
|
||||
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||
digest).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(invalid_digest_uri) ->
|
||||
{'bad-protocol', <<"Invalid digest URI">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(unexpected_response) ->
|
||||
{'bad-protocol', <<"Unexpected response">>}.
|
||||
|
||||
mech_new(Host, GetPassword, _CheckPassword,
|
||||
CheckPasswordDigest) ->
|
||||
{ok,
|
||||
#state{step = 1, nonce = randoms:get_string(),
|
||||
host = Host, hostfqdn = get_local_fqdn(),
|
||||
get_password = GetPassword,
|
||||
check_password = CheckPasswordDigest}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
<<"nonce=\"", Nonce/binary,
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>,
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State,
|
||||
ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
bad -> {error, parser_failed};
|
||||
KeyVals ->
|
||||
DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
|
||||
case is_digesturi_valid(DigestURI, State#state.host,
|
||||
State#state.hostfqdn)
|
||||
of
|
||||
false ->
|
||||
?DEBUG("User login not authorized because digest-uri "
|
||||
"seems invalid: ~p (checking for Host "
|
||||
"~p, FQDN ~p)",
|
||||
[DigestURI, State#state.host, State#state.hostfqdn]),
|
||||
{error, invalid_digest_uri, UserName};
|
||||
true ->
|
||||
AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} -> {error, not_authorized, UserName};
|
||||
{Passwd, AuthModule} ->
|
||||
case (State#state.check_password)(UserName, UserName, <<"">>,
|
||||
proplists:get_value(<<"response">>, KeyVals, <<>>),
|
||||
fun (PW) ->
|
||||
response(KeyVals,
|
||||
UserName,
|
||||
PW,
|
||||
Nonce,
|
||||
AuthzId,
|
||||
<<"AUTHENTICATE">>)
|
||||
end)
|
||||
of
|
||||
{true, _} ->
|
||||
RspAuth = response(KeyVals, UserName, Passwd, Nonce,
|
||||
AuthzId, <<"">>),
|
||||
{continue, <<"rspauth=", RspAuth/binary>>,
|
||||
State#state{step = 5, auth_module = AuthModule,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
false -> {error, not_authorized, UserName};
|
||||
{false, _} -> {error, not_authorized, UserName}
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5, auth_module = AuthModule,
|
||||
username = UserName, authzid = AuthzId},
|
||||
<<"">>) ->
|
||||
{ok,
|
||||
[{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]),
|
||||
{error, unexpected_response}.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
parse1([$= | Cs], S, Ts) ->
|
||||
parse2(Cs, lists:reverse(S), "", Ts);
|
||||
parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts);
|
||||
parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts);
|
||||
parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) -> lists:reverse(T);
|
||||
parse1([], _S, _T) -> bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, Val, Ts);
|
||||
parse2([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse2([], _, _, _) -> bad.
|
||||
|
||||
parse3([$" | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse3([$\\, C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([], _, _, _) -> bad.
|
||||
|
||||
parse4([$, | Cs], Key, Val, Ts) ->
|
||||
parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]);
|
||||
parse4([$\s | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse4([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse4([], Key, Val, Ts) ->
|
||||
%% @doc Check if the digest-uri is valid.
|
||||
%% RFC-2831 allows to provide the IP address in Host,
|
||||
%% however ejabberd doesn't allow that.
|
||||
%% If the service (for example jabber.example.org)
|
||||
%% is provided by several hosts (being one of them server3.example.org),
|
||||
%% then acceptable digest-uris would be:
|
||||
%% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and
|
||||
%% xmpp/jabber.example.org
|
||||
%% The last version is not actually allowed by the RFC, but implemented by popular clients
|
||||
parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]).
|
||||
|
||||
is_digesturi_valid(DigestURICase, JabberDomain,
|
||||
JabberFQDN) ->
|
||||
DigestURI = stringprep:tolower(DigestURICase),
|
||||
case catch str:tokens(DigestURI, <<"/">>) of
|
||||
[<<"xmpp">>, Host] ->
|
||||
IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
|
||||
(Host == JabberDomain) or IsHostFqdn;
|
||||
[<<"xmpp">>, Host, ServName] ->
|
||||
IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
|
||||
(ServName == JabberDomain) and IsHostFqdn;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
is_host_fqdn(_Host, []) ->
|
||||
false;
|
||||
is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn ->
|
||||
true;
|
||||
is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn ->
|
||||
is_host_fqdn(Host, FqdnTail).
|
||||
|
||||
get_local_fqdn() ->
|
||||
case ejabberd_config:get_option(fqdn) of
|
||||
undefined ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
{ok, {hostent, Fqdn, _, _, _, _}} = inet:gethostbyname(Hostname),
|
||||
[list_to_binary(Fqdn)];
|
||||
Fqdn ->
|
||||
Fqdn
|
||||
end.
|
||||
|
||||
hex(S) ->
|
||||
str:to_hexlist(S).
|
||||
|
||||
proplists_get_bin_value(Key, Pairs, Default) ->
|
||||
case proplists:get_value(Key, Pairs, Default) of
|
||||
L when is_list(L) ->
|
||||
list_to_binary(L);
|
||||
L2 ->
|
||||
L2
|
||||
end.
|
||||
|
||||
response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
A2Prefix) ->
|
||||
Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>),
|
||||
CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>),
|
||||
DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>),
|
||||
QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>),
|
||||
MD5Hash = erlang:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
Passwd/binary>>),
|
||||
A1 = case AuthzId of
|
||||
<<"">> ->
|
||||
<<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary>>;
|
||||
_ ->
|
||||
<<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary, ":",
|
||||
AuthzId/binary>>
|
||||
end,
|
||||
A2 = case QOP of
|
||||
<<"auth">> ->
|
||||
<<A2Prefix/binary, ":", DigestURI/binary>>;
|
||||
_ ->
|
||||
<<A2Prefix/binary, ":", DigestURI/binary,
|
||||
":00000000000000000000000000000000">>
|
||||
end,
|
||||
T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary,
|
||||
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
-spec opt_type(fqdn) -> fun((binary() | [binary()]) -> [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(fqdn) ->
|
||||
fun(FQDN) when is_binary(FQDN) ->
|
||||
[FQDN];
|
||||
(FQDNs) when is_list(FQDNs) ->
|
||||
[iolist_to_binary(FQDN) || FQDN <- FQDNs]
|
||||
end;
|
||||
opt_type(_) -> [fqdn].
|
||||
@@ -1,104 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_oauth.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : X-OAUTH2 SASL mechanism
|
||||
%%% Created : 17 Sep 2015 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_oauth).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {host}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid token">>}.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Token] ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, State#state.host, [<<"sasl_auth">>], Token) of
|
||||
true ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, ejabberd_oauth}]};
|
||||
_ ->
|
||||
{error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[<<"">>, UserMaybeDomain, Token] ->
|
||||
case parse_domain(UserMaybeDomain) of
|
||||
%% <NUL>login@domain<NUL>pwd
|
||||
[User, _Domain] -> [User, User, Token];
|
||||
%% <NUL>login<NUL>pwd
|
||||
[User] -> [User, User, Token]
|
||||
end;
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzId, User, Token] ->
|
||||
case parse_domain(AuthzId) of
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzUser, _Domain] -> [AuthzUser, User, Token];
|
||||
%% login<NUL>login<NUL>pwd
|
||||
[AuthzUser] -> [AuthzUser, User, Token]
|
||||
end;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
parse1([0 | Cs], S, T) ->
|
||||
parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
|
||||
%parse1([], [], T) ->
|
||||
% lists:reverse(T);
|
||||
parse1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
parse_domain1([$@ | Cs], S, T) ->
|
||||
parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse_domain1([C | Cs], S, T) ->
|
||||
parse_domain1(Cs, [C | S], T);
|
||||
parse_domain1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
@@ -1,94 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_plain.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : PLAIN SASL mechanism
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_plain).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {check_password}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>}.
|
||||
|
||||
mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Password] ->
|
||||
case (State#state.check_password)(User, AuthzId, Password) of
|
||||
{true, AuthModule} ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, AuthModule}]};
|
||||
_ -> {error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[<<"">>, UserMaybeDomain, Password] ->
|
||||
case parse_domain(UserMaybeDomain) of
|
||||
%% <NUL>login@domain<NUL>pwd
|
||||
[User, _Domain] -> [User, User, Password];
|
||||
%% <NUL>login<NUL>pwd
|
||||
[User] -> [User, User, Password]
|
||||
end;
|
||||
[AuthzId, User, Password] ->
|
||||
case parse_domain(AuthzId) of
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzUser, _Domain] -> [AuthzUser, User, Password];
|
||||
%% login<NUL>login<NUL>pwd
|
||||
[AuthzUser] -> [AuthzUser, User, Password]
|
||||
end;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
parse(S) ->
|
||||
binary:split(S, <<0>>, [global]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
parse_domain1([$@ | Cs], S, T) ->
|
||||
parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse_domain1([C | Cs], S, T) ->
|
||||
parse_domain1(Cs, [C | S], T);
|
||||
parse_domain1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
@@ -1,249 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_scram.erl
|
||||
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%% Purpose : SASL SCRAM authentication
|
||||
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_scram).
|
||||
|
||||
-author('stephen.roettger@googlemail.com').
|
||||
|
||||
-protocol({rfc, 5802}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, format_error/1]).
|
||||
|
||||
-include("scram.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state,
|
||||
{step = 2 :: 2 | 4,
|
||||
stored_key = <<"">> :: binary(),
|
||||
server_key = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
auth_module :: module(),
|
||||
get_password :: fun((binary()) ->
|
||||
{false | ejabberd_auth:password(), module()}),
|
||||
auth_message = <<"">> :: binary(),
|
||||
client_nonce = <<"">> :: binary(),
|
||||
server_nonce = <<"">> :: binary()}).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
-define(NONCE_LENGTH, 16).
|
||||
|
||||
-type error_reason() :: unsupported_extension | bad_username |
|
||||
not_authorized | saslprep_failed |
|
||||
parser_failed | bad_attribute |
|
||||
nonce_mismatch | bad_channel_binding.
|
||||
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"SCRAM-SHA-1">>, ?MODULE,
|
||||
scram).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(unsupported_extension) ->
|
||||
{'bad-protocol', <<"Unsupported extension">>};
|
||||
format_error(bad_username) ->
|
||||
{'invalid-authzid', <<"Malformed username">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(saslprep_failed) ->
|
||||
{'not-authorized', <<"SASLprep failed">>};
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(bad_attribute) ->
|
||||
{'bad-protocol', <<"Malformed or unexpected attribute">>};
|
||||
format_error(nonce_mismatch) ->
|
||||
{'bad-protocol', <<"Nonce mismatch">>};
|
||||
format_error(bad_channel_binding) ->
|
||||
{'bad-protocol', <<"Invalid channel binding">>}.
|
||||
|
||||
mech_new(_Host, GetPassword, _CheckPassword,
|
||||
_CheckPasswordDigest) ->
|
||||
{ok, #state{step = 2, get_password = GetPassword}}.
|
||||
|
||||
mech_step(#state{step = 2} = State, ClientIn) ->
|
||||
case re:split(ClientIn, <<",">>, [{return, binary}]) of
|
||||
[_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _]
|
||||
when ExtensionAttribute /= <<"">> ->
|
||||
{error, unsupported_extension};
|
||||
[CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _]
|
||||
when (CBind == <<"y">>) or (CBind == <<"n">>) ->
|
||||
case parse_attribute(UserNameAttribute) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
{_, EscapedUserName} ->
|
||||
case unescape_username(EscapedUserName) of
|
||||
error -> {error, bad_username};
|
||||
UserName ->
|
||||
case parse_attribute(ClientNonceAttribute) of
|
||||
{$r, ClientNonce} ->
|
||||
{Pass, AuthModule} = (State#state.get_password)(UserName),
|
||||
LPass = if is_binary(Pass) -> jid:resourceprep(Pass);
|
||||
true -> Pass
|
||||
end,
|
||||
if Pass == false ->
|
||||
{error, not_authorized, UserName};
|
||||
LPass == error ->
|
||||
{error, saslprep_failed, UserName};
|
||||
true ->
|
||||
{StoredKey, ServerKey, Salt, IterationCount} =
|
||||
if is_record(Pass, scram) ->
|
||||
{base64:decode(Pass#scram.storedkey),
|
||||
base64:decode(Pass#scram.serverkey),
|
||||
base64:decode(Pass#scram.salt),
|
||||
Pass#scram.iterationcount};
|
||||
true ->
|
||||
TempSalt =
|
||||
randoms:bytes(?SALT_LENGTH),
|
||||
SaltedPassword =
|
||||
scram:salted_password(Pass,
|
||||
TempSalt,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT),
|
||||
{scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
scram:server_key(SaltedPassword),
|
||||
TempSalt,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT}
|
||||
end,
|
||||
ClientFirstMessageBare =
|
||||
str:substr(ClientIn,
|
||||
str:str(ClientIn, <<"n=">>)),
|
||||
ServerNonce =
|
||||
base64:encode(randoms:bytes(?NONCE_LENGTH)),
|
||||
ServerFirstMessage =
|
||||
iolist_to_binary(
|
||||
["r=",
|
||||
ClientNonce,
|
||||
ServerNonce,
|
||||
",", "s=",
|
||||
base64:encode(Salt),
|
||||
",", "i=",
|
||||
integer_to_list(IterationCount)]),
|
||||
{continue, ServerFirstMessage,
|
||||
State#state{step = 4, stored_key = StoredKey,
|
||||
server_key = ServerKey,
|
||||
auth_module = AuthModule,
|
||||
auth_message =
|
||||
<<ClientFirstMessageBare/binary,
|
||||
",", ServerFirstMessage/binary>>,
|
||||
client_nonce = ClientNonce,
|
||||
server_nonce = ServerNonce,
|
||||
username = UserName}}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end
|
||||
end
|
||||
end;
|
||||
_Else -> {error, parser_failed}
|
||||
end;
|
||||
mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
case str:tokens(ClientIn, <<",">>) of
|
||||
[GS2ChannelBindingAttribute, NonceAttribute,
|
||||
ClientProofAttribute] ->
|
||||
case parse_attribute(GS2ChannelBindingAttribute) of
|
||||
{$c, CVal} ->
|
||||
ChannelBindingSupport = try binary:first(base64:decode(CVal))
|
||||
catch _:badarg -> 0
|
||||
end,
|
||||
if (ChannelBindingSupport == $n)
|
||||
or (ChannelBindingSupport == $y) ->
|
||||
Nonce = <<(State#state.client_nonce)/binary,
|
||||
(State#state.server_nonce)/binary>>,
|
||||
case parse_attribute(NonceAttribute) of
|
||||
{$r, CompareNonce} when CompareNonce == Nonce ->
|
||||
case parse_attribute(ClientProofAttribute) of
|
||||
{$p, ClientProofB64} ->
|
||||
ClientProof = try base64:decode(ClientProofB64)
|
||||
catch _:badarg -> <<>>
|
||||
end,
|
||||
AuthMessage = iolist_to_binary(
|
||||
[State#state.auth_message,
|
||||
",",
|
||||
str:substr(ClientIn, 1,
|
||||
str:str(ClientIn, <<",p=">>)
|
||||
- 1)]),
|
||||
ClientSignature =
|
||||
scram:client_signature(State#state.stored_key,
|
||||
AuthMessage),
|
||||
ClientKey = scram:client_key(ClientProof,
|
||||
ClientSignature),
|
||||
CompareStoredKey = scram:stored_key(ClientKey),
|
||||
if CompareStoredKey == State#state.stored_key ->
|
||||
ServerSignature =
|
||||
scram:server_signature(State#state.server_key,
|
||||
AuthMessage),
|
||||
{ok, [{username, State#state.username},
|
||||
{auth_module, State#state.auth_module},
|
||||
{authzid, State#state.username}],
|
||||
<<"v=",
|
||||
(base64:encode(ServerSignature))/binary>>};
|
||||
true -> {error, not_authorized, State#state.username}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
{$r, _} -> {error, nonce_mismatch};
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
true -> {error, bad_channel_binding}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
parse_attribute(<<Name, $=, Val/binary>>) when Val /= <<>> ->
|
||||
case is_alpha(Name) of
|
||||
true -> {Name, Val};
|
||||
false -> {error, bad_attribute}
|
||||
end;
|
||||
parse_attribute(_) ->
|
||||
{error, bad_attribute}.
|
||||
|
||||
unescape_username(<<"">>) -> <<"">>;
|
||||
unescape_username(EscapedUsername) ->
|
||||
Pos = str:str(EscapedUsername, <<"=">>),
|
||||
if Pos == 0 -> EscapedUsername;
|
||||
true ->
|
||||
Start = str:substr(EscapedUsername, 1, Pos - 1),
|
||||
End = str:substr(EscapedUsername, Pos),
|
||||
EndLen = byte_size(End),
|
||||
if EndLen < 3 -> error;
|
||||
true ->
|
||||
case str:substr(End, 1, 3) of
|
||||
<<"=2C">> ->
|
||||
<<Start/binary, ",",
|
||||
(unescape_username(str:substr(End, 4)))/binary>>;
|
||||
<<"=3D">> ->
|
||||
<<Start/binary, "=",
|
||||
(unescape_username(str:substr(End, 4)))/binary>>;
|
||||
_Else -> error
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
is_alpha(Char) when Char >= $a, Char =< $z -> true;
|
||||
is_alpha(Char) when Char >= $A, Char =< $Z -> true;
|
||||
is_alpha(_) -> false.
|
||||
+2
-2
@@ -38,7 +38,7 @@
|
||||
-protocol({xep, 270, '1.0'}).
|
||||
|
||||
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
|
||||
get_pid_file/0, check_app/1, module_name/1]).
|
||||
get_pid_file/0, check_app/1, module_name/1, is_loaded/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -143,7 +143,7 @@ exit_or_halt(Reason, StartFlag) ->
|
||||
end.
|
||||
|
||||
sleep(N) ->
|
||||
timer:sleep(randoms:uniform(N)).
|
||||
timer:sleep(p1_rand:uniform(N)).
|
||||
|
||||
get_module_file(App, Mod) ->
|
||||
BaseName = atom_to_list(Mod),
|
||||
|
||||
+20
-15
@@ -3,7 +3,8 @@
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% ejabberdctl commands
|
||||
-export([get_certificates/1,
|
||||
-export([get_commands_spec/0,
|
||||
get_certificates/1,
|
||||
renew_certificates/0,
|
||||
list_certificates/1,
|
||||
revoke_certificate/1]).
|
||||
@@ -119,7 +120,7 @@ get_commands_spec() ->
|
||||
args = [],
|
||||
result = {certificates, string}},
|
||||
#ejabberd_commands{name = list_certificates, tags = [acme],
|
||||
desc = "Lists all curently handled certificates and "
|
||||
desc = "Lists all currently handled certificates and "
|
||||
"their respective domains in {plain|verbose} format",
|
||||
module = ?MODULE, function = list_certificates,
|
||||
args_desc = ["Whether to print the whole certificate "
|
||||
@@ -150,7 +151,8 @@ get_certificates(Domains) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, get_certificates}
|
||||
end;
|
||||
false ->
|
||||
@@ -242,7 +244,8 @@ get_certificate(CAUrl, DomainName, PrivateKey) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, DomainName, get_certificate}
|
||||
end.
|
||||
|
||||
@@ -381,7 +384,8 @@ renew_certificates() ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, get_certificates}
|
||||
end.
|
||||
|
||||
@@ -446,7 +450,8 @@ list_certificates(Verbose) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, list_certificates}
|
||||
end;
|
||||
false ->
|
||||
@@ -488,7 +493,8 @@ format_certificate(DataCert, Verbose) ->
|
||||
end
|
||||
catch
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
fail_format_certificate(DomainName)
|
||||
end.
|
||||
|
||||
@@ -613,7 +619,8 @@ revoke_certificates(DomainOrFile) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, revoke_certificate}
|
||||
end.
|
||||
|
||||
@@ -1105,7 +1112,7 @@ save_certificate({ok, DomainName, Cert}) ->
|
||||
%% that there is no certificate saved if it cannot be added in
|
||||
%% certificate persistent storage
|
||||
write_cert(CertificateFile, Cert, DomainName),
|
||||
ok = ejabberd_pkix:add_certfile(CertificateFile),
|
||||
{ok, _} = ejabberd_pkix:add_certfile(CertificateFile),
|
||||
DataCert = #data_cert{
|
||||
domain = DomainName,
|
||||
pem = Cert,
|
||||
@@ -1117,7 +1124,8 @@ save_certificate({ok, DomainName, Cert}) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, DomainName, saving}
|
||||
end.
|
||||
|
||||
@@ -1214,15 +1222,12 @@ generate_key() ->
|
||||
%% Option Parsing Code
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
-spec opt_type(acme) -> fun((acme_config()) -> (acme_config()));
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(acme) ->
|
||||
fun(L) ->
|
||||
lists:map(
|
||||
fun({ca_url, URL}) ->
|
||||
URL1 = binary_to_list(URL),
|
||||
{ok, _} = http_uri:parse(URL1),
|
||||
{ca_url, URL1};
|
||||
{ca_url, misc:try_url(URL)};
|
||||
({contact, Contact}) ->
|
||||
[<<_, _/binary>>, <<_, _/binary>>] =
|
||||
binary:split(Contact, <<":">>),
|
||||
|
||||
@@ -275,6 +275,8 @@ get_commands_spec() ->
|
||||
|
||||
#ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak],
|
||||
desc = "Import data from Prosody",
|
||||
longdesc = "Note: this method requires ejabberd compiled with optional tools support "
|
||||
"and package must provide optional luerl dependency.",
|
||||
module = prosody2ejabberd, function = from_dir,
|
||||
args_desc = ["Full path to the Prosody data directory"],
|
||||
args_example = ["/var/lib/prosody/datadump/"],
|
||||
|
||||
@@ -55,6 +55,7 @@ start(normal, _Args) ->
|
||||
ejabberd_system_monitor:start(),
|
||||
register_elixir_config_hooks(),
|
||||
ejabberd_cluster:wait_for_sync(infinity),
|
||||
ejabberd_hooks:run(ejabberd_started, []),
|
||||
{T2, _} = statistics(wall_clock),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||
[ejabberd_config:get_version(),
|
||||
@@ -76,6 +77,7 @@ start(_, _) ->
|
||||
%% This function is called when an application is about to be stopped,
|
||||
%% before shutting down the processes of the application.
|
||||
prep_stop(State) ->
|
||||
ejabberd_hooks:run(ejabberd_stopping, []),
|
||||
ejabberd_listener:stop_listeners(),
|
||||
ejabberd_sm:stop(),
|
||||
gen_mod:stop_modules(),
|
||||
@@ -150,6 +152,7 @@ start_apps() ->
|
||||
crypto:start(),
|
||||
ejabberd:start_app(sasl),
|
||||
ejabberd:start_app(ssl),
|
||||
ejabberd:start_app(pkix),
|
||||
ejabberd:start_app(p1_utils),
|
||||
ejabberd:start_app(fast_yaml),
|
||||
ejabberd:start_app(fast_tls),
|
||||
|
||||
+49
-17
@@ -41,7 +41,8 @@
|
||||
get_password_s/2, get_password_with_authmodule/2,
|
||||
user_exists/2, user_exists_in_other_modules/3,
|
||||
remove_user/2, remove_user/3, plain_password_required/1,
|
||||
store_type/1, entropy/1, backend_type/1, password_format/1]).
|
||||
store_type/1, entropy/1, backend_type/1, password_format/1,
|
||||
which_users_exists/1]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
@@ -139,7 +140,7 @@ handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
||||
NewModules = auth_modules(Host),
|
||||
start(Host, NewModules -- OldModules),
|
||||
stop(Host, OldModules -- NewModules),
|
||||
reload(Host, lists_intersection(OldModules, NewModules)),
|
||||
reload(Host, misc:intersection(OldModules, NewModules)),
|
||||
maps:put(Host, NewModules, Acc)
|
||||
end, HostModules, ejabberd_config:get_myhosts()),
|
||||
init_cache(NewHostModules),
|
||||
@@ -245,7 +246,9 @@ check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGe
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, false, auth_modules(LServer))
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
-spec set_password(binary(), binary(), password()) -> ok | {error, atom()}.
|
||||
@@ -409,6 +412,47 @@ user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) ->
|
||||
maybe
|
||||
end.
|
||||
|
||||
-spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}).
|
||||
which_users_exists(USPairs) ->
|
||||
ByServer = lists:foldl(
|
||||
fun({User, Server}, Dict) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
case gb_trees:lookup(LServer, Dict) of
|
||||
none ->
|
||||
gb_trees:insert(LServer, gb_sets:singleton(LUser), Dict);
|
||||
{value, Set} ->
|
||||
gb_trees:update(LServer, gb_sets:add(LUser, Set), Dict)
|
||||
end
|
||||
end, gb_trees:empty(), USPairs),
|
||||
Set = lists:foldl(
|
||||
fun({LServer, UsersSet}, Results) ->
|
||||
UsersList = gb_sets:to_list(UsersSet),
|
||||
lists:foldl(
|
||||
fun(M, Results2) ->
|
||||
try M:which_users_exists(LServer, UsersList) of
|
||||
{error, _} ->
|
||||
Results2;
|
||||
Res ->
|
||||
gb_sets:union(
|
||||
gb_sets:from_list([{U, LServer} || U <- Res]),
|
||||
Results2)
|
||||
catch
|
||||
_:undef ->
|
||||
lists:foldl(
|
||||
fun(U, R2) ->
|
||||
case user_exists(U, LServer) of
|
||||
true ->
|
||||
gb_sets:add({U, LServer}, R2);
|
||||
_ ->
|
||||
R2
|
||||
end
|
||||
end, Results2, UsersList)
|
||||
end
|
||||
end, Results, auth_modules(LServer))
|
||||
end, gb_sets:empty(), gb_trees:to_list(ByServer)),
|
||||
gb_sets:to_list(Set).
|
||||
|
||||
-spec remove_user(binary(), binary()) -> ok.
|
||||
remove_user(User, Server) ->
|
||||
case validate_credentials(User, Server) of
|
||||
@@ -693,7 +737,7 @@ password_to_scram(Password) ->
|
||||
password_to_scram(#scram{} = Password, _IterationCount) ->
|
||||
Password;
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = randoms:bytes(?SALT_LENGTH),
|
||||
Salt = p1_rand:bytes(?SALT_LENGTH),
|
||||
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
|
||||
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
ServerKey = scram:server_key(SaltedPassword),
|
||||
@@ -834,12 +878,6 @@ validate_credentials(User, Server, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
lists_intersection(L1, L2) ->
|
||||
lists:filter(
|
||||
fun(E) ->
|
||||
lists:member(E, L2)
|
||||
end, L1).
|
||||
|
||||
import_info() ->
|
||||
[{<<"users">>, 3}].
|
||||
|
||||
@@ -855,13 +893,7 @@ import(Server, {sql, _}, riak, <<"users">>, Fields) ->
|
||||
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
|
||||
ok.
|
||||
|
||||
-spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]);
|
||||
(auth_password_format) -> fun((plain | scram) -> plain | scram);
|
||||
(auth_use_cache) -> fun((boolean()) -> boolean());
|
||||
(auth_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(auth_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(auth_cache_size) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(auth_method) ->
|
||||
fun (V) when is_list(V) ->
|
||||
lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
use_cache/1,
|
||||
allow_anonymous/1,
|
||||
is_sasl_anonymous_enabled/1,
|
||||
is_login_anonymous_enabled/1,
|
||||
@@ -60,6 +61,9 @@ stop(Host) ->
|
||||
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
|
||||
?MODULE, unregister_connection, 100).
|
||||
|
||||
use_cache(_) ->
|
||||
false.
|
||||
|
||||
%% Return true if anonymous is allowed for host or false otherwise
|
||||
allow_anonymous(Host) ->
|
||||
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
|
||||
@@ -173,10 +177,7 @@ plain_password_required(_) ->
|
||||
store_type(_) ->
|
||||
external.
|
||||
|
||||
-spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean());
|
||||
(anonymous_protocol) -> fun((sasl_anon | login_anon | both) ->
|
||||
sasl_anon | login_anon | both);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(allow_multiple_connections) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(anonymous_protocol) ->
|
||||
|
||||
@@ -118,9 +118,11 @@ opt_type(extauth_instances) ->
|
||||
fun (V) when is_integer(V), V > 0 -> V end;
|
||||
opt_type(extauth_program) ->
|
||||
fun (V) -> binary_to_list(iolist_to_binary(V)) end;
|
||||
opt_type(extauth_pool_name) ->
|
||||
fun (V) -> iolist_to_binary(V) end;
|
||||
opt_type(extauth_pool_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
opt_type(_) ->
|
||||
[extauth_program, extauth_pool_size,
|
||||
[extauth_program, extauth_pool_size, extauth_pool_name,
|
||||
%% Deprecated:
|
||||
extauth_cache, extauth_instances].
|
||||
|
||||
@@ -362,10 +362,7 @@ parse_options(Host) ->
|
||||
sfilter = SearchFilter, lfilter = LocalFilter,
|
||||
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
||||
|
||||
-spec opt_type(ldap_dn_filter) -> fun(([{binary(), binary()}]) ->
|
||||
[{binary(), binary()}]);
|
||||
(ldap_local_filter) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ldap_dn_filter) ->
|
||||
fun ([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
|
||||
@@ -82,9 +82,7 @@ get_pam_service(Host) ->
|
||||
get_pam_userinfotype(Host) ->
|
||||
ejabberd_config:get_option({pam_userinfotype, Host}, username).
|
||||
|
||||
-spec opt_type(pam_service) -> fun((binary()) -> binary());
|
||||
(pam_userinfotype) -> fun((username | jid) -> username | jid);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(pam_service) -> fun iolist_to_binary/1;
|
||||
opt_type(pam_userinfotype) ->
|
||||
fun (username) -> username;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||
get_users/2, count_users/2, get_password/2,
|
||||
remove_user/2, store_type/1, plain_password_required/1,
|
||||
convert_to_scram/1, opt_type/1, export/1]).
|
||||
convert_to_scram/1, opt_type/1, export/1, which_users_exists/2]).
|
||||
|
||||
-include("scram.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -247,6 +247,32 @@ users_number(LServer, [{prefix, Prefix}])
|
||||
users_number(LServer, []) ->
|
||||
users_number(LServer).
|
||||
|
||||
which_users_exists(LServer, LUsers) when length(LUsers) =< 100 ->
|
||||
try ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(username)s from users where username in %(LUsers)ls")) of
|
||||
{selected, Matching} ->
|
||||
[U || {U} <- Matching];
|
||||
{error, _} = E ->
|
||||
E
|
||||
catch _:B ->
|
||||
{error, B}
|
||||
end;
|
||||
which_users_exists(LServer, LUsers) ->
|
||||
{First, Rest} = lists:split(100, LUsers),
|
||||
case which_users_exists(LServer, First) of
|
||||
{error, _} = E ->
|
||||
E;
|
||||
V ->
|
||||
case which_users_exists(LServer, Rest) of
|
||||
{error, _} = E2 ->
|
||||
E2;
|
||||
V2 ->
|
||||
V ++ V2
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
convert_to_scram(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
if
|
||||
@@ -324,8 +350,7 @@ export(_Server) ->
|
||||
[]
|
||||
end}].
|
||||
|
||||
-spec opt_type(pgsql_users_number_estimate) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(pgsql_users_number_estimate) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(_) -> [pgsql_users_number_estimate].
|
||||
|
||||
+71
-101
@@ -23,20 +23,18 @@
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_bosh).
|
||||
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(p1_fsm).
|
||||
-protocol({xep, 124, '1.11'}).
|
||||
-protocol({xep, 206, '1.4'}).
|
||||
|
||||
-behaviour(p1_fsm).
|
||||
|
||||
%% API
|
||||
-export([start/2, start/3, start_link/3]).
|
||||
|
||||
-export([send_xml/2, setopts/2, controlling_process/2,
|
||||
migrate/3, become_controller/2,
|
||||
reset_stream/1, change_shaper/2, monitor/1, close/1,
|
||||
reset_stream/1, change_shaper/2, close/1,
|
||||
sockname/1, peername/1, process_request/3, send/2,
|
||||
change_controller/2]).
|
||||
get_transport/1, get_owner/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, wait_for_session/2, wait_for_session/3,
|
||||
@@ -88,7 +86,7 @@
|
||||
sid = <<"">> :: binary(),
|
||||
el_ibuf :: p1_queue:queue(),
|
||||
el_obuf :: p1_queue:queue(),
|
||||
shaper_state = none :: shaper:shaper(),
|
||||
shaper_state = none :: ejabberd_shaper:shaper(),
|
||||
c2s_pid :: pid() | undefined,
|
||||
xmpp_ver = <<"">> :: binary(),
|
||||
inactivity_timer :: reference() | undefined,
|
||||
@@ -167,22 +165,12 @@ setopts({http_bind, FsmRef, _IP}, Opts) ->
|
||||
|
||||
controlling_process(_Socket, _Pid) -> ok.
|
||||
|
||||
become_controller(FsmRef, C2SPid) ->
|
||||
p1_fsm:send_all_state_event(FsmRef,
|
||||
{become_controller, C2SPid}).
|
||||
|
||||
change_controller({http_bind, FsmRef, _IP}, C2SPid) ->
|
||||
become_controller(FsmRef, C2SPid).
|
||||
|
||||
reset_stream({http_bind, _FsmRef, _IP} = Socket) ->
|
||||
Socket.
|
||||
|
||||
change_shaper({http_bind, FsmRef, _IP}, Shaper) ->
|
||||
p1_fsm:send_all_state_event(FsmRef, {change_shaper, Shaper}).
|
||||
|
||||
monitor({http_bind, FsmRef, _IP}) ->
|
||||
erlang:monitor(process, FsmRef).
|
||||
|
||||
close({http_bind, FsmRef, _IP}) ->
|
||||
catch p1_fsm:sync_send_all_state_event(FsmRef,
|
||||
close).
|
||||
@@ -191,10 +179,11 @@ sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}.
|
||||
|
||||
peername({http_bind, _FsmRef, IP}) -> {ok, IP}.
|
||||
|
||||
migrate(FsmRef, Node, After) when node(FsmRef) == node() ->
|
||||
catch erlang:send_after(After, FsmRef, {migrate, Node});
|
||||
migrate(_FsmRef, _Node, _After) ->
|
||||
ok.
|
||||
get_transport(_Socket) ->
|
||||
http_bind.
|
||||
|
||||
get_owner({http_bind, FsmRef, _IP}) ->
|
||||
FsmRef.
|
||||
|
||||
process_request(Data, IP, Type) ->
|
||||
Opts1 = ejabberd_c2s_config:get_c2s_limits(),
|
||||
@@ -281,7 +270,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
Opts1 = ejabberd_c2s_config:get_c2s_limits(),
|
||||
Opts2 = [{xml_socket, true} | Opts1],
|
||||
Shaper = none,
|
||||
ShaperState = shaper:new(Shaper),
|
||||
ShaperState = ejabberd_shaper:new(Shaper),
|
||||
Socket = make_socket(self(), IP),
|
||||
XMPPVer = get_attr('xmpp:version', Attrs),
|
||||
XMPPDomain = get_attr(to, Attrs),
|
||||
@@ -295,30 +284,26 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
buf_new(XMPPDomain)),
|
||||
Opts2}
|
||||
end,
|
||||
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
|
||||
[{receiver, self()}|Opts]),
|
||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
||||
mod_bosh, max_inactivity),
|
||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
|
||||
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
||||
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
||||
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
||||
max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain),
|
||||
inactivity_timeout = Inactivity,
|
||||
shaped_receivers = ShapedReceivers,
|
||||
shaper_state = ShaperState},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
mod_bosh:open_session(SID, self()),
|
||||
{ok, wait_for_session, NewState};
|
||||
init([StateName, State]) ->
|
||||
mod_bosh:open_session(State#state.sid, self()),
|
||||
case State#state.c2s_pid of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
NewSocket = make_socket(self(), State#state.ip),
|
||||
C2SPid ! {change_socket, NewSocket},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
{ok, StateName, NewState};
|
||||
_ -> {stop, normal}
|
||||
case ejabberd_c2s:start({?MODULE, Socket}, [{receiver, self()}|Opts]) of
|
||||
{ok, C2SPid} ->
|
||||
ejabberd_c2s:accept(C2SPid),
|
||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
||||
mod_bosh, max_inactivity),
|
||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
|
||||
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
||||
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
||||
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
||||
max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain),
|
||||
inactivity_timeout = Inactivity,
|
||||
shaped_receivers = ShapedReceivers,
|
||||
shaper_state = ShaperState},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
mod_bosh:open_session(SID, self()),
|
||||
{ok, wait_for_session, NewState};
|
||||
{error, Reason} ->
|
||||
{stop, Reason};
|
||||
ignore ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
wait_for_session(_Event, State) ->
|
||||
@@ -355,7 +340,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
||||
{'xmlns:stream', ?NS_STREAM}, {from, State#state.host}
|
||||
| Polling]},
|
||||
{ShaperState, _} =
|
||||
shaper:update(State#state.shaper_state, Req#body.size),
|
||||
ejabberd_shaper:update(State#state.shaper_state, Req#body.size),
|
||||
State1 = State#state{wait_timeout = Wait,
|
||||
prev_rid = RID, prev_key = NewKey,
|
||||
prev_poll = PollTime, shaper_state = ShaperState,
|
||||
@@ -365,15 +350,22 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
||||
{State3, RespEls} = get_response_els(State2),
|
||||
State4 = stop_inactivity_timer(State3),
|
||||
case RespEls of
|
||||
[] ->
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers}};
|
||||
_ ->
|
||||
reply_next_state(State4, Resp#body{els = RespEls}, RID,
|
||||
From)
|
||||
[{xmlstreamstart, _, _} = El1] ->
|
||||
OutBuf = buf_in([El1], State4#state.el_obuf),
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers, el_obuf = OutBuf}};
|
||||
[] ->
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers}};
|
||||
_ ->
|
||||
reply_next_state(State4, Resp#body{els = RespEls}, RID,
|
||||
From)
|
||||
end;
|
||||
wait_for_session(_Event, _From, State) ->
|
||||
?ERROR_MSG("unexpected sync event in 'wait_for_session': ~p",
|
||||
@@ -393,7 +385,7 @@ active(#body{attrs = Attrs, size = Size} = Req, From,
|
||||
"~p~n** State: ~p",
|
||||
[Req, From, State]),
|
||||
{ShaperState, Pause} =
|
||||
shaper:update(State#state.shaper_state, Size),
|
||||
ejabberd_shaper:update(State#state.shaper_state, Size),
|
||||
State1 = State#state{shaper_state = ShaperState},
|
||||
if Pause > 0 ->
|
||||
TRef = start_shaper_timer(Pause),
|
||||
@@ -404,7 +396,7 @@ active(#body{attrs = Attrs, size = Size} = Req, From,
|
||||
{next_state, active,
|
||||
State2#state{shaped_receivers = Q}}
|
||||
catch error:full ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
RID = get_attr(rid, Attrs),
|
||||
reply_stop(State1,
|
||||
#body{http_reason = <<"Too many requests">>,
|
||||
@@ -518,15 +510,13 @@ active1(#body{attrs = Attrs} = Req, From, State) ->
|
||||
end
|
||||
end.
|
||||
|
||||
handle_event({become_controller, C2SPid}, StateName,
|
||||
handle_event({activate, C2SPid}, StateName,
|
||||
State) ->
|
||||
State1 = route_els(State#state{c2s_pid = C2SPid}),
|
||||
{next_state, StateName, State1};
|
||||
handle_event({change_shaper, Shaper}, StateName,
|
||||
State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{next_state, StateName,
|
||||
State#state{shaper_state = NewShaperState}};
|
||||
{next_state, StateName, State#state{shaper_state = Shaper}};
|
||||
handle_event(_Event, StateName, State) ->
|
||||
?ERROR_MSG("unexpected event in '~s': ~p",
|
||||
[StateName, _Event]),
|
||||
@@ -554,7 +544,7 @@ handle_sync_event({send_xml, El}, _From, StateName,
|
||||
State2 = case p1_queue:out(State1#state.shaped_receivers)
|
||||
of
|
||||
{{value, {TRef, From, Body}}, Q} ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
p1_fsm:send_event(self(), {Body, From}),
|
||||
State1#state{shaped_receivers = Q};
|
||||
_ -> State1
|
||||
@@ -574,7 +564,8 @@ handle_sync_event(_Event, _From, StateName, State) ->
|
||||
|
||||
handle_info({timeout, TRef, wait_timeout}, StateName,
|
||||
#state{wait_timer = TRef} = State) ->
|
||||
{next_state, StateName, drop_holding_receiver(State)};
|
||||
State2 = State#state{wait_timer = undefined},
|
||||
{next_state, StateName, drop_holding_receiver(State2)};
|
||||
handle_info({timeout, TRef, inactive}, _StateName,
|
||||
#state{inactivity_timer = TRef} = State) ->
|
||||
{stop, normal, State};
|
||||
@@ -592,24 +583,11 @@ handle_info({timeout, TRef, shaper_timeout}, StateName,
|
||||
{stop, normal, State};
|
||||
_ -> {next_state, StateName, State}
|
||||
end;
|
||||
handle_info({migrate, Node}, StateName, State) ->
|
||||
if Node /= node() ->
|
||||
NewState = bounce_receivers(State, migrated),
|
||||
{migrate, NewState,
|
||||
{Node, ?MODULE, start, [StateName, NewState]}, 0};
|
||||
true -> {next_state, StateName, State}
|
||||
end;
|
||||
handle_info(_Info, StateName, State) ->
|
||||
?ERROR_MSG("unexpected info:~n** Msg: ~p~n** StateName: ~p",
|
||||
[_Info, StateName]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
terminate({migrated, ClonePid}, _StateName, State) ->
|
||||
?INFO_MSG("Migrating session \"~s\" (c2s_pid = "
|
||||
"~p) to ~p on node ~p",
|
||||
[State#state.sid, State#state.c2s_pid, ClonePid,
|
||||
node(ClonePid)]),
|
||||
mod_bosh:close_session(State#state.sid);
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
mod_bosh:close_session(State#state.sid),
|
||||
case State#state.c2s_pid of
|
||||
@@ -693,7 +671,8 @@ drop_holding_receiver(State, RID) ->
|
||||
State1#state.receivers),
|
||||
State2 = State1#state{receivers = Receivers},
|
||||
do_reply(State2, From, Body, RID);
|
||||
none -> State
|
||||
none ->
|
||||
restart_inactivity_timer(State)
|
||||
end.
|
||||
|
||||
do_reply(State, From, Body, RID) ->
|
||||
@@ -711,7 +690,7 @@ do_reply(State, From, Body, RID) ->
|
||||
Responses2 = gb_trees:insert(RID, Body, Responses1),
|
||||
State#state{responses = Responses2}.
|
||||
|
||||
bounce_receivers(State, Reason) ->
|
||||
bounce_receivers(State, _Reason) ->
|
||||
Receivers = gb_trees:to_list(State#state.receivers),
|
||||
ShapedReceivers = lists:map(fun ({_, From,
|
||||
#body{attrs = Attrs} = Body}) ->
|
||||
@@ -719,18 +698,13 @@ bounce_receivers(State, Reason) ->
|
||||
{RID, {From, Body}}
|
||||
end,
|
||||
p1_queue:to_list(State#state.shaped_receivers)),
|
||||
lists:foldl(fun ({RID, {From, Body}}, AccState) ->
|
||||
NewBody = if Reason == closed ->
|
||||
#body{http_reason =
|
||||
<<"Session closed">>,
|
||||
attrs =
|
||||
[{type, <<"terminate">>},
|
||||
{condition,
|
||||
<<"other-request">>}]};
|
||||
Reason == migrated ->
|
||||
Body#body{http_reason =
|
||||
<<"Session migrated">>}
|
||||
end,
|
||||
lists:foldl(fun ({RID, {From, _Body}}, AccState) ->
|
||||
NewBody = #body{http_reason =
|
||||
<<"Session closed">>,
|
||||
attrs =
|
||||
[{type, <<"terminate">>},
|
||||
{condition,
|
||||
<<"other-request">>}]},
|
||||
do_reply(AccState, From, NewBody, RID)
|
||||
end,
|
||||
State, Receivers ++ ShapedReceivers).
|
||||
@@ -984,7 +958,7 @@ http_error(Status, Reason, Type) ->
|
||||
end,
|
||||
{Status, Reason, ?HEADER(CType), <<"">>}.
|
||||
|
||||
make_sid() -> str:sha(randoms:get_string()).
|
||||
make_sid() -> str:sha(p1_rand:get_string()).
|
||||
|
||||
-compile({no_auto_import, [{min, 2}]}).
|
||||
|
||||
@@ -1037,12 +1011,8 @@ buf_out(Buf, I, Els) ->
|
||||
{empty, _} -> buf_out(Buf, 0, Els)
|
||||
end.
|
||||
|
||||
cancel_timer(TRef) when is_reference(TRef) ->
|
||||
p1_fsm:cancel_timer(TRef);
|
||||
cancel_timer(_) -> false.
|
||||
|
||||
restart_timer(TRef, Timeout, Msg) ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
erlang:start_timer(timer:seconds(Timeout), self(), Msg).
|
||||
|
||||
restart_inactivity_timer(#state{inactivity_timeout =
|
||||
@@ -1059,7 +1029,7 @@ restart_inactivity_timer(#state{inactivity_timer =
|
||||
|
||||
stop_inactivity_timer(#state{inactivity_timer = TRef} =
|
||||
State) ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
State#state{inactivity_timer = undefined}.
|
||||
|
||||
restart_wait_timer(#state{wait_timer = TRef,
|
||||
@@ -1069,13 +1039,13 @@ restart_wait_timer(#state{wait_timer = TRef,
|
||||
State#state{wait_timer = NewTRef}.
|
||||
|
||||
stop_wait_timer(#state{wait_timer = TRef} = State) ->
|
||||
cancel_timer(TRef), State#state{wait_timer = undefined}.
|
||||
misc:cancel_timer(TRef), State#state{wait_timer = undefined}.
|
||||
|
||||
start_shaper_timer(Timeout) ->
|
||||
erlang:start_timer(Timeout, self(), shaper_timeout).
|
||||
|
||||
make_random_jid(Host) ->
|
||||
User = randoms:get_string(),
|
||||
jid:make(User, Host, randoms:get_string()).
|
||||
User = p1_rand:get_string(),
|
||||
jid:make(User, Host, p1_rand:get_string()).
|
||||
|
||||
make_socket(Pid, IP) -> {http_bind, Pid, IP}.
|
||||
|
||||
+95
-113
@@ -22,20 +22,20 @@
|
||||
-module(ejabberd_c2s).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(ejabberd_config).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-protocol({rfc, 6121}).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([start/2, start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
%% ejabberd_config callbacks
|
||||
-export([opt_type/1, listen_opt_type/1, transform_listen_option/2]).
|
||||
-export([opt_type/1, transform_listen_option/2]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
||||
-export([tls_options/1, tls_required/1, tls_enabled/1,
|
||||
compress_methods/1, bind/2, sasl_mechanisms/2,
|
||||
get_password_fun/1, check_password_fun/1, check_password_digest_fun/1,
|
||||
get_password_fun/2, check_password_fun/2, check_password_digest_fun/2,
|
||||
unauthenticated_stream_features/1, authenticated_stream_features/1,
|
||||
handle_stream_start/2, handle_stream_end/2,
|
||||
handle_unauthenticated_packet/2, handle_authenticated_packet/2,
|
||||
@@ -49,7 +49,7 @@
|
||||
-export([get_presence/1, set_presence/2, resend_presence/1, resend_presence/2,
|
||||
open_session/1, call/3, cast/2, send/2, close/1, close/2, stop/1,
|
||||
reply/2, copy_state/2, set_timeout/2, route/2,
|
||||
host_up/1, host_down/1]).
|
||||
host_up/1, host_down/1, send_ws_ping/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -61,26 +61,18 @@
|
||||
-export_type([state/0]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% xmpp_socket API
|
||||
%%% ejabberd_listener API
|
||||
%%%===================================================================
|
||||
start(SockData, Opts) ->
|
||||
case proplists:get_value(supervisor, Opts, true) of
|
||||
true ->
|
||||
case supervisor:start_child(ejabberd_c2s_sup, [SockData, Opts]) of
|
||||
{ok, undefined} -> ignore;
|
||||
Res -> Res
|
||||
end;
|
||||
_ ->
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts))
|
||||
end.
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Common API
|
||||
@@ -104,7 +96,7 @@ get_presence(Ref) ->
|
||||
set_presence(Ref, Pres) ->
|
||||
call(Ref, {set_presence, Pres}, 1000).
|
||||
|
||||
-spec resend_presence(pid()) -> ok.
|
||||
-spec resend_presence(pid()) -> boolean().
|
||||
resend_presence(Pid) ->
|
||||
resend_presence(Pid, undefined).
|
||||
|
||||
@@ -145,6 +137,11 @@ send_error(#{lserver := LServer} = State, Pkt, Err) ->
|
||||
{Pkt1, State1} -> xmpp_stream_in:send_error(State1, Pkt1, Err)
|
||||
end.
|
||||
|
||||
-spec send_ws_ping(pid()) -> ok;
|
||||
(state()) -> state().
|
||||
send_ws_ping(Ref) ->
|
||||
xmpp_stream_in:send_ws_ping(Ref).
|
||||
|
||||
-spec route(pid(), term()) -> boolean().
|
||||
route(Pid, Term) ->
|
||||
ejabberd_cluster:send(Pid, Term).
|
||||
@@ -272,7 +269,6 @@ process_terminated(#{sid := SID, socket := Socket,
|
||||
State1 = case maps:is_key(pres_last, State) of
|
||||
true ->
|
||||
Pres = #presence{type = unavailable,
|
||||
status = xmpp:mk_text(Status),
|
||||
from = JID,
|
||||
to = jid:remove_resource(JID)},
|
||||
ejabberd_sm:close_session_unset_presence(SID, U, S, R,
|
||||
@@ -339,9 +335,6 @@ tls_options(#{lserver := LServer, tls_options := DefaultOpts,
|
||||
tls_required(#{tls_required := TLSRequired}) ->
|
||||
TLSRequired.
|
||||
|
||||
tls_verify(#{tls_verify := TLSVerify}) ->
|
||||
TLSVerify.
|
||||
|
||||
tls_enabled(#{tls_enabled := TLSEnabled,
|
||||
tls_required := TLSRequired,
|
||||
tls_verify := TLSVerify}) ->
|
||||
@@ -358,25 +351,41 @@ unauthenticated_stream_features(#{lserver := LServer}) ->
|
||||
authenticated_stream_features(#{lserver := LServer}) ->
|
||||
ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]).
|
||||
|
||||
sasl_mechanisms(Mechs, #{lserver := LServer}) ->
|
||||
sasl_mechanisms(Mechs, #{lserver := LServer} = State) ->
|
||||
Type = ejabberd_auth:store_type(LServer),
|
||||
Mechs1 = ejabberd_config:get_option({disable_sasl_mechanisms, LServer}, []),
|
||||
Mechs2 = case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer) of
|
||||
true -> Mechs1;
|
||||
false -> [<<"ANONYMOUS">>|Mechs1]
|
||||
end,
|
||||
Mechs -- Mechs2.
|
||||
%% I re-created it from cyrsasl ets magic, but I think it's wrong
|
||||
%% TODO: need to check before 18.09 release
|
||||
lists:filter(
|
||||
fun(<<"ANONYMOUS">>) ->
|
||||
ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer);
|
||||
(<<"DIGEST-MD5">>) -> Type == plain;
|
||||
(<<"SCRAM-SHA-1">>) -> Type /= external;
|
||||
(<<"PLAIN">>) -> true;
|
||||
(<<"X-OAUTH2">>) -> true;
|
||||
(<<"EXTERNAL">>) -> maps:get(tls_verify, State, false);
|
||||
(_) -> false
|
||||
end, Mechs -- Mechs1).
|
||||
|
||||
get_password_fun(#{lserver := LServer}) ->
|
||||
get_password_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U) ->
|
||||
ejabberd_auth:get_password_with_authmodule(U, LServer)
|
||||
end.
|
||||
|
||||
check_password_fun(#{lserver := LServer}) ->
|
||||
check_password_fun(<<"X-OAUTH2">>, #{lserver := LServer}) ->
|
||||
fun(User, _AuthzId, Token) ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, LServer, [<<"sasl_auth">>], Token) of
|
||||
true -> {true, ejabberd_oauth};
|
||||
_ -> {false, ejabberd_oauth}
|
||||
end
|
||||
end;
|
||||
check_password_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U, AuthzId, P) ->
|
||||
ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P)
|
||||
end.
|
||||
|
||||
check_password_digest_fun(#{lserver := LServer}) ->
|
||||
check_password_digest_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U, AuthzId, P, D, DG) ->
|
||||
ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG)
|
||||
end.
|
||||
@@ -404,8 +413,8 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
||||
{ok, State2};
|
||||
deny ->
|
||||
ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]),
|
||||
?INFO_MSG("(~s) Forbidden c2s session for ~s",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
?WARNING_MSG("(~s) Forbidden c2s session for ~s",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
Txt = <<"Access denied by service policy">>,
|
||||
{error, xmpp:err_not_allowed(Txt, Lang), State}
|
||||
end
|
||||
@@ -440,12 +449,12 @@ handle_auth_success(User, Mech, AuthModule,
|
||||
handle_auth_failure(User, Mech, Reason,
|
||||
#{socket := Socket,
|
||||
ip := IP, lserver := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed c2s ~s authentication ~sfrom ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech,
|
||||
if User /= <<"">> -> ["for ", User, "@", LServer, " "];
|
||||
true -> ""
|
||||
end,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
?WARNING_MSG("(~s) Failed c2s ~s authentication ~sfrom ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech,
|
||||
if User /= <<"">> -> ["for ", User, "@", LServer, " "];
|
||||
true -> ""
|
||||
end,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
ejabberd_hooks:run_fold(c2s_auth_result, LServer, State, [false, User]).
|
||||
|
||||
handle_unbinded_packet(Pkt, #{lserver := LServer} = State) ->
|
||||
@@ -629,7 +638,7 @@ route_probe_reply(From, #{jid := To,
|
||||
Subscription = get_subscription(To, From),
|
||||
if IsAnotherResource orelse
|
||||
Subscription == both orelse Subscription == from ->
|
||||
Packet = xmpp_util:add_delay_info(LastPres, To, TS),
|
||||
Packet = misc:add_delay_info(LastPres, To, TS),
|
||||
case privacy_check_packet(State, Packet, out) of
|
||||
deny ->
|
||||
ok;
|
||||
@@ -881,7 +890,7 @@ bounce_message_queue() ->
|
||||
-spec new_uniq_id() -> binary().
|
||||
new_uniq_id() ->
|
||||
iolist_to_binary(
|
||||
[randoms:get_string(),
|
||||
[p1_rand:get_string(),
|
||||
integer_to_binary(p1_time_compat:unique_integer([positive]))]).
|
||||
|
||||
-spec get_conn_type(state()) -> c2s | c2s_tls | c2s_compressed | websocket |
|
||||
@@ -920,7 +929,7 @@ change_shaper(#{shaper := ShaperName, ip := IP, lserver := LServer,
|
||||
Shaper = acl:access_matches(ShaperName,
|
||||
#{usr => jid:split(JID), ip => IP},
|
||||
LServer),
|
||||
xmpp_stream_in:change_shaper(State, Shaper).
|
||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||
|
||||
-spec format_reason(state(), term()) -> binary().
|
||||
format_reason(#{stop_reason := Reason}, _) ->
|
||||
@@ -934,7 +943,7 @@ format_reason(_, {shutdown, _}) ->
|
||||
format_reason(_, _) ->
|
||||
<<"internal server error">>.
|
||||
|
||||
-spec get_certfile(binary()) -> file:filename_all().
|
||||
-spec get_certfile(binary()) -> file:filename_all() | undefined.
|
||||
get_certfile(LServer) ->
|
||||
case ejabberd_pkix:get_certfile(LServer) of
|
||||
{ok, CertFile} ->
|
||||
@@ -948,15 +957,7 @@ get_certfile(LServer) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-type resource_conflict() :: setresource | closeold | closenew | acceptnew.
|
||||
-spec opt_type(c2s_ciphers) -> fun((binary()) -> binary());
|
||||
(c2s_dhfile) -> fun((binary()) -> binary());
|
||||
(c2s_cafile) -> fun((binary()) -> binary());
|
||||
(c2s_protocol_options) -> fun(([binary()]) -> binary());
|
||||
(c2s_tls_compression) -> fun((boolean()) -> boolean());
|
||||
(resource_conflict) -> fun((resource_conflict()) -> resource_conflict());
|
||||
(disable_sasl_mechanisms) -> fun((binary() | [binary()]) -> [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(c2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(c2s_dhfile) -> fun misc:try_read_file/1;
|
||||
opt_type(c2s_cafile) -> fun misc:try_read_file/1;
|
||||
@@ -982,73 +983,54 @@ opt_type(_) ->
|
||||
c2s_protocol_options, c2s_tls_compression, resource_conflict,
|
||||
disable_sasl_mechanisms].
|
||||
|
||||
-spec listen_opt_type(access) -> fun((any()) -> any());
|
||||
(shaper) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(starttls) -> fun((boolean()) -> boolean());
|
||||
(tls_verify) -> fun((boolean()) -> boolean());
|
||||
(zlib) -> fun((boolean()) -> boolean());
|
||||
(supervisor) -> fun((boolean()) -> boolean());
|
||||
(max_stanza_size) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((timeout()) -> timeout());
|
||||
(stream_management) -> fun((boolean()) -> boolean());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
||||
File
|
||||
end;
|
||||
listen_opt_type(ciphers) -> opt_type(c2s_ciphers);
|
||||
listen_opt_type(dhfile) -> opt_type(c2s_dhfile);
|
||||
listen_opt_type(cafile) -> opt_type(c2s_cafile);
|
||||
listen_opt_type(protocol_options) -> opt_type(c2s_protocol_options);
|
||||
listen_opt_type(tls_compression) -> opt_type(c2s_tls_compression);
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(starttls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(starttls_required) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(tls_verify) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(zlib) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
listen_opt_type(zlib) ->
|
||||
fun(true) ->
|
||||
ejabberd:start_app(ezlib),
|
||||
true;
|
||||
(false) ->
|
||||
false
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(stream_management) ->
|
||||
?ERROR_MSG("listening option 'stream_management' is ignored: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
fun(B) when is_boolean(B) ->
|
||||
?ERROR_MSG("Listening option 'stream_management' is ignored: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
B
|
||||
end;
|
||||
listen_opt_type(O) ->
|
||||
StreamOpts = mod_stream_mgmt:mod_options(ejabberd_config:get_myname()),
|
||||
case lists:keyfind(O, 1, StreamOpts) of
|
||||
false ->
|
||||
[access, shaper, certfile, ciphers, dhfile, cafile,
|
||||
protocol_options, tls, tls_compression, starttls,
|
||||
starttls_required, tls_verify, zlib, max_fsm_queue,
|
||||
backlog, inet, inet6, accept_interval];
|
||||
_ ->
|
||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
||||
"option from mod_stream_mgmt module", [O, O]),
|
||||
mod_stream_mgmt:mod_opt_type(O)
|
||||
MgmtOpts = mod_stream_mgmt:mod_options(ejabberd_config:get_myname()),
|
||||
case lists:keymember(O, 1, MgmtOpts) of
|
||||
true ->
|
||||
fun(V) ->
|
||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
||||
"option from mod_stream_mgmt module", [O, O]),
|
||||
(mod_stream_mgmt:mod_opt_type(O))(V)
|
||||
end
|
||||
end.
|
||||
|
||||
listen_options() ->
|
||||
[{access, all},
|
||||
{shaper, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{starttls, false},
|
||||
{starttls_required, false},
|
||||
{tls_verify, false},
|
||||
{zlib, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000}|
|
||||
mod_stream_mgmt:mod_options(ejabberd_config:get_myname())].
|
||||
|
||||
@@ -89,7 +89,7 @@ mk_field(Type, Var, Value) ->
|
||||
create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
Id = <<(p1_rand:get_string())/binary>>,
|
||||
JID = jid:encode(From),
|
||||
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
|
||||
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type,
|
||||
@@ -120,7 +120,7 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
Id = <<(p1_rand:get_string())/binary>>,
|
||||
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
|
||||
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
|
||||
HelpTxt = translate:translate(Lang,
|
||||
@@ -128,10 +128,10 @@ create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
||||
"visit the web page.">>),
|
||||
Imageurl = get_url(<<Id/binary, "/image">>),
|
||||
NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++
|
||||
[#xdata_field{type = fixed, values = [HelpTxt]},
|
||||
[#xdata_field{type = fixed, var = <<"captcha-fallback-text">>, values = [HelpTxt]},
|
||||
#xdata_field{type = hidden, var = <<"captchahidden">>,
|
||||
values = [<<"workaround-for-psi">>]},
|
||||
#xdata_field{type = 'text-single', var = <<"url">>,
|
||||
#xdata_field{type = 'text-single', var = <<"captcha-fallback-url">>,
|
||||
label = translate:translate(
|
||||
Lang, <<"CAPTCHA web page">>),
|
||||
values = [Imageurl]},
|
||||
@@ -230,7 +230,6 @@ process_iq(#iq{type = get, lang = Lang} = IQ) ->
|
||||
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
|
||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||
process_iq(#iq{lang = Lang} = IQ) ->
|
||||
?INFO_MSG("IQ = ~p", [IQ]),
|
||||
Txt = <<"No module is handling this query">>,
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
|
||||
@@ -376,7 +375,7 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
create_image() -> create_image(undefined).
|
||||
|
||||
create_image(Limiter) ->
|
||||
Key = str:substr(randoms:get_string(), 1, 6),
|
||||
Key = str:substr(p1_rand:get_string(), 1, 6),
|
||||
create_image(Limiter, Key).
|
||||
|
||||
create_image(Limiter, Key) ->
|
||||
@@ -517,11 +516,7 @@ recv_data(Port, TRef, Buf) ->
|
||||
end.
|
||||
|
||||
return(Port, TRef, Result) ->
|
||||
case erlang:cancel_timer(TRef) of
|
||||
false ->
|
||||
receive {timeout, TRef, _} -> ok after 0 -> ok end;
|
||||
_ -> ok
|
||||
end,
|
||||
misc:cancel_timer(TRef),
|
||||
catch port_close(Port),
|
||||
Result.
|
||||
|
||||
@@ -561,7 +556,7 @@ check_captcha(Id, ProvidedKey) ->
|
||||
case ets:lookup(captcha, Id) of
|
||||
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
|
||||
ets:delete(captcha, Id),
|
||||
erlang:cancel_timer(Tref),
|
||||
misc:cancel_timer(Tref),
|
||||
if ValidKey == ProvidedKey ->
|
||||
callback(captcha_succeed, Pid, Args),
|
||||
captcha_valid;
|
||||
@@ -595,10 +590,7 @@ callback(_, _, _) ->
|
||||
now_priority() ->
|
||||
-p1_time_compat:system_time(micro_seconds).
|
||||
|
||||
-spec opt_type(captcha_cmd) -> fun((binary()) -> binary());
|
||||
(captcha_host) -> fun((binary()) -> binary());
|
||||
(captcha_limit) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(captcha_cmd) ->
|
||||
fun (FileName) ->
|
||||
F = iolist_to_binary(FileName), if F /= <<"">> -> F end
|
||||
|
||||
@@ -619,9 +619,7 @@ permission_addon() ->
|
||||
[{access, ejabberd_config:get_option(commands_admin_access, none)}],
|
||||
{get_exposed_commands(), []}}}].
|
||||
|
||||
-spec opt_type(commands_admin_access) -> fun((any()) -> any());
|
||||
(commands) -> fun((list()) -> list());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(commands_admin_access) -> fun acl:access_rules_validator/1;
|
||||
opt_type(commands) ->
|
||||
fun(V) when is_list(V) -> V end;
|
||||
|
||||
@@ -459,7 +459,8 @@ generate_md_output(File, RegExp, Languages) ->
|
||||
end, Cmds2),
|
||||
Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3],
|
||||
Langs = binary:split(Languages, <<",">>, [global]),
|
||||
Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>,
|
||||
Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: Administration API\norder: 40\n"
|
||||
"// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---">>,
|
||||
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4),
|
||||
{ok, Fh} = file:open(File, [write]),
|
||||
io:format(Fh, "~s~s", [Header, Out]),
|
||||
|
||||
+66
-30
@@ -34,7 +34,7 @@
|
||||
prepare_opt_val/4, transform_options/1, collect_options/1,
|
||||
convert_to_yaml/1, convert_to_yaml/2, v_db/2,
|
||||
env_binary_to_list/2, opt_type/1, may_hide_data/1,
|
||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
|
||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1, v_host/1, v_hosts/1,
|
||||
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
||||
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
||||
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
||||
@@ -57,9 +57,10 @@
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_config.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
-callback opt_type(atom()) -> function() | [atom()].
|
||||
-callback opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
-type bad_option() :: invalid_option | unknown_option.
|
||||
|
||||
-spec start() -> ok | {error, bad_option()}.
|
||||
@@ -75,7 +76,7 @@ start() ->
|
||||
UnixTime = p1_time_compat:system_time(seconds),
|
||||
SharedKey = case erlang:get_cookie() of
|
||||
nocookie ->
|
||||
str:sha(randoms:get_string());
|
||||
str:sha(p1_rand:get_string());
|
||||
Cookie ->
|
||||
str:sha(misc:atom_to_binary(Cookie))
|
||||
end,
|
||||
@@ -115,7 +116,7 @@ start(Hosts, Opts) ->
|
||||
UnixTime = p1_time_compat:system_time(seconds),
|
||||
SharedKey = case erlang:get_cookie() of
|
||||
nocookie ->
|
||||
str:sha(randoms:get_string());
|
||||
str:sha(p1_rand:get_string());
|
||||
Cookie ->
|
||||
str:sha(misc:atom_to_binary(Cookie))
|
||||
end,
|
||||
@@ -782,8 +783,24 @@ set_opts(State) ->
|
||||
fun(#local_config{key = Key, value = Val}) ->
|
||||
{Key, Val}
|
||||
end, Opts)),
|
||||
set_fqdn(),
|
||||
set_log_level().
|
||||
|
||||
set_fqdn() ->
|
||||
FQDNs = case get_option(fqdn, []) of
|
||||
[] ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
case inet:gethostbyname(Hostname) of
|
||||
{ok, #hostent{h_name = FQDN}} ->
|
||||
[iolist_to_binary(FQDN)];
|
||||
{error, _} ->
|
||||
[]
|
||||
end;
|
||||
Domains ->
|
||||
Domains
|
||||
end,
|
||||
xmpp:set_config([{fqdn, FQDNs}]).
|
||||
|
||||
set_log_level() ->
|
||||
Level = get_option(loglevel, 4),
|
||||
ejabberd_logger:set(Level).
|
||||
@@ -954,6 +971,33 @@ v_dbs_mods(Mod) ->
|
||||
(atom_to_binary(M, utf8))/binary>>, utf8)
|
||||
end, v_dbs(Mod)).
|
||||
|
||||
-spec v_host(binary()) -> binary().
|
||||
v_host(Host) ->
|
||||
hd(v_hosts([Host])).
|
||||
|
||||
-spec v_hosts([binary()]) -> [binary()].
|
||||
v_hosts(Hosts) ->
|
||||
ServerHosts = get_myhosts(),
|
||||
lists:foldr(
|
||||
fun(Host, Acc) ->
|
||||
case lists:member(Host, ServerHosts) of
|
||||
true ->
|
||||
?ERROR_MSG("Failed to reuse route ~s because it's "
|
||||
"already registered on a virtual host",
|
||||
[Host]),
|
||||
erlang:error(badarg);
|
||||
false ->
|
||||
case lists:member(Host, Acc) of
|
||||
true ->
|
||||
?ERROR_MSG("Host ~s is defined multiple times",
|
||||
[Host]),
|
||||
erlang:error(badarg);
|
||||
false ->
|
||||
[Host|Acc]
|
||||
end
|
||||
end
|
||||
end, [], Hosts).
|
||||
|
||||
-spec default_db(module()) -> atom().
|
||||
default_db(Module) ->
|
||||
default_db(global, Module).
|
||||
@@ -1034,14 +1078,15 @@ validate_opts(#state{opts = Opts} = State, ModOpts) ->
|
||||
NewVal ->
|
||||
In#local_config{value = NewVal}
|
||||
catch {invalid_syntax, Error} ->
|
||||
?ERROR_MSG("Invalid value '~p' for "
|
||||
"option '~s': ~s",
|
||||
[Val, Opt, Error]),
|
||||
?ERROR_MSG("Invalid value for "
|
||||
"option '~s' (~s): ~s",
|
||||
[Opt, Error,
|
||||
misc:format_val({yaml, Val})]),
|
||||
erlang:error(invalid_option);
|
||||
_:_ ->
|
||||
?ERROR_MSG("Invalid value '~p' for "
|
||||
"option '~s'",
|
||||
[Val, Opt]),
|
||||
_:R when R /= undef ->
|
||||
?ERROR_MSG("Invalid value for "
|
||||
"option '~s': ~s",
|
||||
[Opt, misc:format_val({yaml, Val})]),
|
||||
erlang:error(invalid_option)
|
||||
end;
|
||||
_ ->
|
||||
@@ -1085,7 +1130,7 @@ get_version() ->
|
||||
-spec get_myhosts() -> [binary()].
|
||||
|
||||
get_myhosts() ->
|
||||
get_option(hosts).
|
||||
get_option(hosts, [<<"localhost">>]).
|
||||
|
||||
-spec get_myname() -> binary().
|
||||
|
||||
@@ -1245,7 +1290,7 @@ transform_terms(Terms) ->
|
||||
ejabberd_s2s,
|
||||
ejabberd_listener,
|
||||
ejabberd_sql_sup,
|
||||
shaper,
|
||||
ejabberd_shaper,
|
||||
ejabberd_s2s_out,
|
||||
acl,
|
||||
ejabberd_config],
|
||||
@@ -1394,22 +1439,7 @@ emit_deprecation_warning(Module, NewModule) ->
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
-spec opt_type(hide_sensitive_log_data) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([binary()]) -> [binary()]);
|
||||
(language) -> fun((binary()) -> binary());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(default_db) -> fun((atom()) -> atom());
|
||||
(default_ram_db) -> fun((atom()) -> atom());
|
||||
(loglevel) -> fun((0..5) -> 0..5);
|
||||
(queue_dir) -> fun((binary()) -> binary());
|
||||
(queue_type) -> fun((ram | file) -> ram | file);
|
||||
(use_cache) -> fun((boolean()) -> boolean());
|
||||
(cache_size) -> fun((timeout()) -> timeout());
|
||||
(cache_missed) -> fun((boolean()) -> boolean());
|
||||
(cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(shared_key) -> fun((binary()) -> binary());
|
||||
(node_start) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(hide_sensitive_log_data) ->
|
||||
fun (H) when is_boolean(H) -> H end;
|
||||
opt_type(hosts) ->
|
||||
@@ -1452,10 +1482,16 @@ opt_type(node_start) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
opt_type(validate_stream) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(fqdn) ->
|
||||
fun(Domain) when is_binary(Domain) ->
|
||||
[Domain];
|
||||
(Domains) ->
|
||||
[iolist_to_binary(Domain) || Domain <- Domains]
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
||||
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
||||
use_cache, cache_size, cache_missed, cache_life_time,
|
||||
use_cache, cache_size, cache_missed, cache_life_time, fqdn,
|
||||
shared_key, node_start, validate_stream, negotiation_timeout].
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
|
||||
@@ -168,15 +168,15 @@ process(["status"], _Version) ->
|
||||
{InternalStatus, ProvidedStatus} = init:get_status(),
|
||||
print("The node ~p is ~p with status: ~p~n",
|
||||
[node(), InternalStatus, ProvidedStatus]),
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
case lists:keymember(ejabberd, 1, application:which_applications()) of
|
||||
false ->
|
||||
EjabberdLogPath = ejabberd_logger:get_log_path(),
|
||||
print("ejabberd is not running in that node~n"
|
||||
"Check for error messages: ~s~n"
|
||||
"or other files in that directory.~n", [EjabberdLogPath]),
|
||||
?STATUS_ERROR;
|
||||
{value, {_, _, Version}} ->
|
||||
print("ejabberd ~s is running in that node~n", [Version]),
|
||||
true ->
|
||||
print("ejabberd ~s is running in that node~n", [ejabberd_config:get_version()]),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
@@ -874,8 +874,7 @@ print(Format, Args) ->
|
||||
%% ["aaaa bbb ccc"].
|
||||
|
||||
|
||||
-spec opt_type(ejabberdctl_access_commands) -> fun((list()) -> list());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ejabberdctl_access_commands) ->
|
||||
fun (V) when is_list(V) -> V end;
|
||||
opt_type(_) -> [ejabberdctl_access_commands].
|
||||
|
||||
@@ -380,11 +380,12 @@ safe_apply(Hook, Module, Function, Args) ->
|
||||
apply(Module, Function, Args)
|
||||
end
|
||||
catch E:R when E /= exit; R /= normal ->
|
||||
St = get_stacktrace(),
|
||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n"
|
||||
"** Reason = ~p~n"
|
||||
"** Arguments = ~p",
|
||||
[Hook, Module, Function, length(Args),
|
||||
{E, R, get_stacktrace()}, Args]),
|
||||
{E, R, St}, Args]),
|
||||
'EXIT'
|
||||
end.
|
||||
|
||||
|
||||
+89
-94
@@ -24,15 +24,16 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_http).
|
||||
|
||||
-behaviour(ejabberd_listener).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% External exports
|
||||
-export([start/2, start_link/2, become_controller/1,
|
||||
socket_type/0, receive_headers/1, recv_file/2,
|
||||
transform_listen_option/2, listen_opt_type/1]).
|
||||
-export([start/2, start_link/2,
|
||||
accept/1, receive_headers/1, recv_file/2,
|
||||
transform_listen_option/2, listen_opt_type/1,
|
||||
listen_options/0]).
|
||||
|
||||
-export([init/2, opt_type/1]).
|
||||
|
||||
@@ -68,7 +69,8 @@
|
||||
default_host,
|
||||
custom_headers,
|
||||
trail = <<>>,
|
||||
addr_re
|
||||
addr_re,
|
||||
sock_peer_name = none
|
||||
}).
|
||||
|
||||
-define(XHTML_DOCTYPE,
|
||||
@@ -101,6 +103,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||
TLSOpts1 = lists:filter(fun ({ciphers, _}) -> true;
|
||||
({dhfile, _}) -> true;
|
||||
({cafile, _}) -> true;
|
||||
({protocol_options, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
@@ -141,6 +144,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
true -> [{[], ejabberd_xmlrpc}];
|
||||
false -> []
|
||||
end,
|
||||
SockPeer = proplists:get_value(sock_peer_name, Opts, none),
|
||||
DefinedHandlers = proplists:get_value(request_handlers, Opts, []),
|
||||
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
|
||||
Admin ++ Bind ++ XMLRPC,
|
||||
@@ -157,6 +161,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
custom_headers = CustomHeaders,
|
||||
options = Opts,
|
||||
request_handlers = RequestHandlers,
|
||||
sock_peer_name = SockPeer,
|
||||
addr_re = RE},
|
||||
try receive_headers(State) of
|
||||
V -> V
|
||||
@@ -164,12 +169,9 @@ init({SockMod, Socket}, Opts) ->
|
||||
{error, _} -> State
|
||||
end.
|
||||
|
||||
become_controller(_Pid) ->
|
||||
accept(_Pid) ->
|
||||
ok.
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
|
||||
send_text(_State, none) ->
|
||||
ok;
|
||||
send_text(State, Text) ->
|
||||
@@ -202,8 +204,8 @@ send_file(State, Fd, Size, FileName) ->
|
||||
end
|
||||
catch _:{case_clause, {error, Why}} ->
|
||||
if Why /= closed ->
|
||||
?INFO_MSG("Failed to read ~s: ~s",
|
||||
[FileName, file_format_error(Why)]),
|
||||
?WARNING_MSG("Failed to read ~s: ~s",
|
||||
[FileName, file_format_error(Why)]),
|
||||
exit(normal);
|
||||
true ->
|
||||
ok
|
||||
@@ -412,11 +414,11 @@ extract_path_query(#state{request_method = Method,
|
||||
when Method =:= 'GET' orelse
|
||||
Method =:= 'HEAD' orelse
|
||||
Method =:= 'DELETE' orelse Method =:= 'OPTIONS' ->
|
||||
case catch url_decode_q_split(Path) of
|
||||
{'EXIT', _} -> {State, false};
|
||||
{NPath, Query} ->
|
||||
LPath = normalize_path([NPE
|
||||
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|
||||
case catch url_decode_q_split_normalize(Path) of
|
||||
{'EXIT', Error} ->
|
||||
?DEBUG("Error decoding URL '~p': ~p", [Path, Error]),
|
||||
{State, false};
|
||||
{LPath, Query} ->
|
||||
LQuery = case catch parse_urlencoded(Query) of
|
||||
{'EXIT', _Reason} -> [];
|
||||
LQ -> LQ
|
||||
@@ -430,11 +432,11 @@ extract_path_query(#state{request_method = Method,
|
||||
sockmod = _SockMod,
|
||||
socket = _Socket} = State)
|
||||
when (Method =:= 'POST' orelse Method =:= 'PUT') andalso Len>0 ->
|
||||
case catch url_decode_q_split(Path) of
|
||||
{'EXIT', _} -> {State, false};
|
||||
{NPath, _Query} ->
|
||||
LPath = normalize_path(
|
||||
[NPE || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|
||||
case catch url_decode_q_split_normalize(Path) of
|
||||
{'EXIT', Error} ->
|
||||
?DEBUG("Error decoding URL '~p': ~p", [Path, Error]),
|
||||
{State, false};
|
||||
{LPath, _Query} ->
|
||||
case Method of
|
||||
'PUT' ->
|
||||
{State, {LPath, [], Trail}};
|
||||
@@ -464,6 +466,7 @@ process_request(#state{request_method = Method,
|
||||
request_version = Version,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
sock_peer_name = SockPeer,
|
||||
options = Options,
|
||||
request_host = Host,
|
||||
request_port = Port,
|
||||
@@ -482,13 +485,17 @@ process_request(#state{request_method = Method,
|
||||
{State2, false} ->
|
||||
{State2, make_bad_request(State)};
|
||||
{State2, {LPath, LQuery, Data}} ->
|
||||
PeerName =
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:peername(Socket);
|
||||
_ ->
|
||||
SockMod:peername(Socket)
|
||||
end,
|
||||
PeerName = case SockPeer of
|
||||
none ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:peername(Socket);
|
||||
_ ->
|
||||
SockMod:peername(Socket)
|
||||
end;
|
||||
{_, Peer} ->
|
||||
{ok, Peer}
|
||||
end,
|
||||
IPHere = case PeerName of
|
||||
{ok, V} -> V;
|
||||
{error, _} = E -> throw(E)
|
||||
@@ -574,7 +581,7 @@ is_ipchain_trusted(UserIPs, Masks) ->
|
||||
lists:any(
|
||||
fun({Mask, MaskLen}) ->
|
||||
acl:ip_matches_mask(IP2, Mask, MaskLen)
|
||||
end, [{{127,0,0,1}, 8} | Masks]);
|
||||
end, Masks);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
@@ -598,35 +605,37 @@ recv_file(#request{length = Len, data = Trail,
|
||||
sockmod = SockMod, socket = Socket}, Path) ->
|
||||
case file:open(Path, [write, exclusive, raw]) of
|
||||
{ok, Fd} ->
|
||||
case file:write(Fd, Trail) of
|
||||
ok ->
|
||||
NewLen = max(0, Len - byte_size(Trail)),
|
||||
case do_recv_file(NewLen, SockMod, Socket, Fd) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, _} = Err ->
|
||||
file:delete(Path),
|
||||
Err
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
file:delete(Path),
|
||||
Err
|
||||
end;
|
||||
Res = case file:write(Fd, Trail) of
|
||||
ok ->
|
||||
NewLen = max(0, Len - byte_size(Trail)),
|
||||
do_recv_file(NewLen, SockMod, Socket, Fd);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end,
|
||||
file:close(Fd),
|
||||
case Res of
|
||||
ok -> ok;
|
||||
{error, _} -> file:delete(Path)
|
||||
end,
|
||||
Res;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
do_recv_file(0, _SockMod, _Socket, Fd) ->
|
||||
file:close(Fd);
|
||||
do_recv_file(0, _SockMod, _Socket, _Fd) ->
|
||||
ok;
|
||||
do_recv_file(Len, SockMod, Socket, Fd) ->
|
||||
ChunkLen = min(Len, ?RECV_BUF),
|
||||
try
|
||||
{ok, Data} = SockMod:recv(Socket, ChunkLen, timer:seconds(30)),
|
||||
ok = file:write(Fd, Data),
|
||||
do_recv_file(Len-size(Data), SockMod, Socket, Fd)
|
||||
catch _:{badmatch, {error, _} = Err} ->
|
||||
file:close(Fd),
|
||||
Err
|
||||
case SockMod:recv(Socket, ChunkLen, timer:seconds(30)) of
|
||||
{ok, Data} ->
|
||||
case file:write(Fd, Data) of
|
||||
ok ->
|
||||
do_recv_file(Len-size(Data), SockMod, Socket, Fd);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
{error, _} ->
|
||||
{error, closed}
|
||||
end.
|
||||
|
||||
make_headers(State, Status, Reason, Headers, Data) ->
|
||||
@@ -723,6 +732,12 @@ file_format_error(Reason) ->
|
||||
Text -> Text
|
||||
end.
|
||||
|
||||
url_decode_q_split_normalize(Path) ->
|
||||
{NPath, Query} = url_decode_q_split(Path),
|
||||
LPath = normalize_path([NPE
|
||||
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|
||||
{LPath, Query}.
|
||||
|
||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||
% is distributed under the following license:
|
||||
%
|
||||
@@ -947,8 +962,7 @@ transform_listen_option({request_handlers, Hs}, Opts) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec opt_type(trusted_proxies) -> fun((all | [binary()]) -> all | [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(trusted_proxies) ->
|
||||
fun (all) -> all;
|
||||
(TPs) -> lists:filtermap(
|
||||
@@ -961,40 +975,13 @@ opt_type(trusted_proxies) ->
|
||||
end;
|
||||
opt_type(_) -> [trusted_proxies].
|
||||
|
||||
-spec listen_opt_type(tls) -> fun((boolean()) -> boolean());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(captcha) -> fun((boolean()) -> boolean());
|
||||
(register) -> fun((boolean()) -> boolean());
|
||||
(web_admin) -> fun((boolean()) -> boolean());
|
||||
(http_bind) -> fun((boolean()) -> boolean());
|
||||
(xmlrpc) -> fun((boolean()) -> boolean());
|
||||
(request_handlers) -> fun(([{binary(), atom()}]) ->
|
||||
[{binary(), atom()}]);
|
||||
(default_host) -> fun((binary()) -> binary());
|
||||
(custom_headers) -> fun(([{binary(), binary()}]) ->
|
||||
[{binary(), binary()}]);
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
||||
File
|
||||
end;
|
||||
listen_opt_type(ciphers) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(dhfile) ->
|
||||
fun misc:try_read_file/1;
|
||||
listen_opt_type(protocol_options) ->
|
||||
fun(Options) -> str:join(Options, <<"|">>) end;
|
||||
listen_opt_type(tls_compression) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(captcha) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(register) ->
|
||||
@@ -1021,15 +1008,23 @@ listen_opt_type(request_handlers) ->
|
||||
end} || {Path, Mod} <- Hs2]
|
||||
end;
|
||||
listen_opt_type(default_host) ->
|
||||
fun(A) -> A end;
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(custom_headers) ->
|
||||
fun expand_custom_headers/1;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
%% TODO
|
||||
fun(A) -> A end.
|
||||
fun expand_custom_headers/1.
|
||||
|
||||
listen_options() ->
|
||||
[{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{captcha, false},
|
||||
{register, false},
|
||||
{web_admin, false},
|
||||
{http_bind, false},
|
||||
{xmlrpc, false},
|
||||
{request_handlers, []},
|
||||
{default_host, undefined},
|
||||
{custom_headers, []}].
|
||||
|
||||
+47
-57
@@ -23,19 +23,17 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-module(ejabberd_http_ws).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('ecestari@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(p1_fsm).
|
||||
|
||||
-export([start/1, start_link/1, init/1, handle_event/3,
|
||||
handle_sync_event/4, code_change/4, handle_info/3,
|
||||
terminate/3, send_xml/2, setopts/2, sockname/1,
|
||||
peername/1, controlling_process/2, become_controller/2,
|
||||
monitor/1, reset_stream/1, close/1, change_shaper/2,
|
||||
socket_handoff/3, opt_type/1]).
|
||||
peername/1, controlling_process/2, get_owner/1,
|
||||
reset_stream/1, close/1, change_shaper/2,
|
||||
socket_handoff/3, get_transport/1, opt_type/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -54,8 +52,8 @@
|
||||
timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(),
|
||||
timer = make_ref() :: reference(),
|
||||
input = [] :: list(),
|
||||
waiting_input = false :: false | pid(),
|
||||
last_receiver = self() :: pid(),
|
||||
active = false :: boolean(),
|
||||
c2s_pid :: pid(),
|
||||
ws :: {#ws{}, pid()},
|
||||
rfc_compilant = undefined :: boolean() | undefined}).
|
||||
|
||||
@@ -104,15 +102,9 @@ peername({http_ws, _FsmRef, IP}) -> {ok, IP}.
|
||||
|
||||
controlling_process(_Socket, _Pid) -> ok.
|
||||
|
||||
become_controller(FsmRef, C2SPid) ->
|
||||
p1_fsm:send_all_state_event(FsmRef, {activate, C2SPid}).
|
||||
|
||||
close({http_ws, FsmRef, _IP}) ->
|
||||
catch p1_fsm:sync_send_all_state_event(FsmRef, close).
|
||||
|
||||
monitor({http_ws, FsmRef, _IP}) ->
|
||||
erlang:monitor(process, FsmRef).
|
||||
|
||||
reset_stream({http_ws, _FsmRef, _IP} = Socket) ->
|
||||
Socket.
|
||||
|
||||
@@ -120,6 +112,12 @@ change_shaper({http_ws, _FsmRef, _IP}, _Shaper) ->
|
||||
%% TODO???
|
||||
ok.
|
||||
|
||||
get_transport(_Socket) ->
|
||||
websocket.
|
||||
|
||||
get_owner({http_ws, FsmRef, _IP}) ->
|
||||
FsmRef.
|
||||
|
||||
socket_handoff(LocalPath, Request, Opts) ->
|
||||
ejabberd_websocket:socket_handoff(LocalPath, Request, Opts, ?MODULE, fun get_human_html_xmlel/0).
|
||||
|
||||
@@ -145,31 +143,34 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
||||
Socket = {http_ws, self(), IP},
|
||||
?DEBUG("Client connected through websocket ~p",
|
||||
[Socket]),
|
||||
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
|
||||
[{receiver, self()}|Opts]),
|
||||
Timer = erlang:start_timer(WSTimeout, self(), []),
|
||||
{ok, loop,
|
||||
#state{socket = Socket, timeout = WSTimeout,
|
||||
timer = Timer, ws = WS,
|
||||
ping_interval = PingInterval}}.
|
||||
|
||||
handle_event({activate, From}, StateName, StateData) ->
|
||||
case StateData#state.input of
|
||||
[] ->
|
||||
{next_state, StateName,
|
||||
StateData#state{waiting_input = From}};
|
||||
Input ->
|
||||
Receiver = From,
|
||||
lists:foreach(fun(I) when is_binary(I)->
|
||||
Receiver ! {tcp, StateData#state.socket, I};
|
||||
(I2) ->
|
||||
Receiver ! {tcp, StateData#state.socket, [I2]}
|
||||
end, Input),
|
||||
{next_state, StateName,
|
||||
StateData#state{input = [], waiting_input = false,
|
||||
last_receiver = Receiver}}
|
||||
case ejabberd_c2s:start({?MODULE, Socket}, [{receiver, self()}|Opts]) of
|
||||
{ok, C2SPid} ->
|
||||
ejabberd_c2s:accept(C2SPid),
|
||||
Timer = erlang:start_timer(WSTimeout, self(), []),
|
||||
{ok, loop,
|
||||
#state{socket = Socket, timeout = WSTimeout,
|
||||
timer = Timer, ws = WS, c2s_pid = C2SPid,
|
||||
ping_interval = PingInterval}};
|
||||
{error, Reason} ->
|
||||
{stop, Reason};
|
||||
ignore ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
handle_event({activate, From}, StateName, State) ->
|
||||
State1 = case State#state.input of
|
||||
[] -> State#state{active = true};
|
||||
Input ->
|
||||
lists:foreach(
|
||||
fun(I) when is_binary(I)->
|
||||
From ! {tcp, State#state.socket, I};
|
||||
(I2) ->
|
||||
From ! {tcp, State#state.socket, [I2]}
|
||||
end, Input),
|
||||
State#state{active = false, input = []}
|
||||
end,
|
||||
{next_state, StateName, State1#state{c2s_pid = From}}.
|
||||
|
||||
handle_sync_event({send_xml, Packet}, _From, StateName,
|
||||
#state{ws = {_, WsPid}, rfc_compilant = R} = StateData) ->
|
||||
Packet2 = case {case R of undefined -> true; V -> V end, Packet} of
|
||||
@@ -233,14 +234,13 @@ handle_info(closed, _StateName, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
handle_info({received, Packet}, StateName, StateDataI) ->
|
||||
{StateData, Parsed} = parse(StateDataI, Packet),
|
||||
SD = case StateData#state.waiting_input of
|
||||
SD = case StateData#state.active of
|
||||
false ->
|
||||
Input = StateData#state.input ++ if is_binary(Parsed) -> [Parsed]; true -> Parsed end,
|
||||
StateData#state{input = Input};
|
||||
Receiver ->
|
||||
Receiver ! {tcp, StateData#state.socket, Parsed},
|
||||
setup_timers(StateData#state{waiting_input = false,
|
||||
last_receiver = Receiver})
|
||||
true ->
|
||||
StateData#state.c2s_pid ! {tcp, StateData#state.socket, Parsed},
|
||||
setup_timers(StateData#state{active = false})
|
||||
end,
|
||||
{next_state, StateName, SD};
|
||||
handle_info(PingPong, StateName, StateData) when PingPong == ping orelse
|
||||
@@ -256,7 +256,7 @@ handle_info({timeout, Timer, _}, StateName,
|
||||
#state{ping_timer = Timer, ws = {_, WsPid}} = StateData) ->
|
||||
case StateData#state.pong_expected of
|
||||
false ->
|
||||
cancel_timer(StateData#state.ping_timer),
|
||||
misc:cancel_timer(StateData#state.ping_timer),
|
||||
PingTimer = erlang:start_timer(StateData#state.ping_interval,
|
||||
self(), []),
|
||||
WsPid ! {ping, <<>>},
|
||||
@@ -273,19 +273,13 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
terminate(_Reason, _StateName, StateData) ->
|
||||
case StateData#state.waiting_input of
|
||||
false -> ok;
|
||||
Receiver ->
|
||||
?DEBUG("C2S Pid : ~p", [Receiver]),
|
||||
Receiver ! {tcp_closed, StateData#state.socket}
|
||||
end,
|
||||
ok.
|
||||
StateData#state.c2s_pid ! {tcp_closed, StateData#state.socket}.
|
||||
|
||||
setup_timers(StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
misc:cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(StateData#state.timeout,
|
||||
self(), []),
|
||||
cancel_timer(StateData#state.ping_timer),
|
||||
misc:cancel_timer(StateData#state.ping_timer),
|
||||
PingTimer = case StateData#state.ping_interval of
|
||||
0 -> StateData#state.ping_timer;
|
||||
V -> erlang:start_timer(V, self(), [])
|
||||
@@ -293,10 +287,6 @@ setup_timers(StateData) ->
|
||||
StateData#state{timer = Timer, ping_timer = PingTimer,
|
||||
pong_expected = false}.
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive {timeout, Timer, _} -> ok after 0 -> ok end.
|
||||
|
||||
get_human_html_xmlel() ->
|
||||
Heading = <<"ejabberd ", (misc:atom_to_binary(?MODULE))/binary>>,
|
||||
#xmlel{name = <<"html">>,
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_idna.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Support for IDNA (RFC3490)
|
||||
%%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_idna).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([domain_utf8_to_ascii/1,
|
||||
domain_ucs2_to_ascii/1,
|
||||
utf8_to_ucs2/1]).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-endif.
|
||||
|
||||
-spec domain_utf8_to_ascii(binary()) -> false | binary().
|
||||
|
||||
domain_utf8_to_ascii(Domain) ->
|
||||
domain_ucs2_to_ascii(utf8_to_ucs2(Domain)).
|
||||
|
||||
utf8_to_ucs2(S) ->
|
||||
utf8_to_ucs2(binary_to_list(S), "").
|
||||
|
||||
utf8_to_ucs2([], R) -> lists:reverse(R);
|
||||
utf8_to_ucs2([C | S], R) when C < 128 ->
|
||||
utf8_to_ucs2(S, [C | R]);
|
||||
utf8_to_ucs2([C1, C2 | S], R) when C1 < 224 ->
|
||||
utf8_to_ucs2(S, [C1 band 31 bsl 6 bor C2 band 63 | R]);
|
||||
utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 ->
|
||||
utf8_to_ucs2(S,
|
||||
[C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63
|
||||
| R]).
|
||||
|
||||
-spec domain_ucs2_to_ascii(list()) -> false | binary().
|
||||
|
||||
domain_ucs2_to_ascii(Domain) ->
|
||||
case catch domain_ucs2_to_ascii1(Domain) of
|
||||
{'EXIT', _Reason} -> false;
|
||||
Res -> iolist_to_binary(Res)
|
||||
end.
|
||||
|
||||
domain_ucs2_to_ascii1(Domain) ->
|
||||
Parts = string:tokens(Domain,
|
||||
[46, 12290, 65294, 65377]),
|
||||
ASCIIParts = lists:map(fun (P) -> to_ascii(P) end,
|
||||
Parts),
|
||||
string:strip(lists:flatmap(fun (P) -> [$. | P] end,
|
||||
ASCIIParts),
|
||||
left, $.).
|
||||
|
||||
%% Domain names are already nameprep'ed in ejabberd, so we skiping this step
|
||||
to_ascii(Name) ->
|
||||
false = lists:any(fun (C)
|
||||
when (0 =< C) and (C =< 44) or
|
||||
(46 =< C) and (C =< 47)
|
||||
or (58 =< C) and (C =< 64)
|
||||
or (91 =< C) and (C =< 96)
|
||||
or (123 =< C) and (C =< 127) ->
|
||||
true;
|
||||
(_) -> false
|
||||
end,
|
||||
Name),
|
||||
case Name of
|
||||
[H | _] when H /= $- -> true = lists:last(Name) /= $-
|
||||
end,
|
||||
ASCIIName = case lists:any(fun (C) -> C > 127 end, Name)
|
||||
of
|
||||
true ->
|
||||
true = case Name of
|
||||
"xn--" ++ _ -> false;
|
||||
_ -> true
|
||||
end,
|
||||
"xn--" ++ punycode_encode(Name);
|
||||
false -> Name
|
||||
end,
|
||||
L = length(ASCIIName),
|
||||
true = (1 =< L) and (L =< 63),
|
||||
ASCIIName.
|
||||
|
||||
%%% PUNYCODE (RFC3492)
|
||||
|
||||
-define(BASE, 36).
|
||||
|
||||
-define(TMIN, 1).
|
||||
|
||||
-define(TMAX, 26).
|
||||
|
||||
-define(SKEW, 38).
|
||||
|
||||
-define(DAMP, 700).
|
||||
|
||||
-define(INITIAL_BIAS, 72).
|
||||
|
||||
-define(INITIAL_N, 128).
|
||||
|
||||
punycode_encode(Input) ->
|
||||
N = (?INITIAL_N),
|
||||
Delta = 0,
|
||||
Bias = (?INITIAL_BIAS),
|
||||
Basic = lists:filter(fun (C) -> C =< 127 end, Input),
|
||||
NonBasic = lists:filter(fun (C) -> C > 127 end, Input),
|
||||
L = length(Input),
|
||||
B = length(Basic),
|
||||
SNonBasic = lists:usort(NonBasic),
|
||||
Output1 = if B > 0 -> Basic ++ "-";
|
||||
true -> ""
|
||||
end,
|
||||
Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N,
|
||||
Delta, Bias, ""),
|
||||
Output1 ++ Output2.
|
||||
|
||||
punycode_encode1(Input, [M | SNonBasic], B, H, L, N,
|
||||
Delta, Bias, Out)
|
||||
when H < L ->
|
||||
Delta1 = Delta + (M - N) * (H + 1),
|
||||
% let n = m
|
||||
{NewDelta, NewBias, NewH, NewOut} = lists:foldl(fun (C,
|
||||
{ADelta, ABias, AH,
|
||||
AOut}) ->
|
||||
if C < M ->
|
||||
{ADelta + 1,
|
||||
ABias, AH,
|
||||
AOut};
|
||||
C == M ->
|
||||
NewOut =
|
||||
punycode_encode_delta(ADelta,
|
||||
ABias,
|
||||
AOut),
|
||||
NewBias =
|
||||
adapt(ADelta,
|
||||
H +
|
||||
1,
|
||||
H
|
||||
==
|
||||
B),
|
||||
{0, NewBias,
|
||||
AH + 1,
|
||||
NewOut};
|
||||
true ->
|
||||
{ADelta,
|
||||
ABias, AH,
|
||||
AOut}
|
||||
end
|
||||
end,
|
||||
{Delta1, Bias, H, Out},
|
||||
Input),
|
||||
punycode_encode1(Input, SNonBasic, B, NewH, L, M + 1,
|
||||
NewDelta + 1, NewBias, NewOut);
|
||||
punycode_encode1(_Input, _SNonBasic, _B, _H, _L, _N,
|
||||
_Delta, _Bias, Out) ->
|
||||
lists:reverse(Out).
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out) ->
|
||||
punycode_encode_delta(Delta, Bias, Out, ?BASE).
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out, K) ->
|
||||
T = if K =< Bias -> ?TMIN;
|
||||
K >= Bias + (?TMAX) -> ?TMAX;
|
||||
true -> K - Bias
|
||||
end,
|
||||
if Delta < T -> [codepoint(Delta) | Out];
|
||||
true ->
|
||||
C = T + (Delta - T) rem ((?BASE) - T),
|
||||
punycode_encode_delta((Delta - T) div ((?BASE) - T),
|
||||
Bias, [codepoint(C) | Out], K + (?BASE))
|
||||
end.
|
||||
|
||||
adapt(Delta, NumPoints, FirstTime) ->
|
||||
Delta1 = if FirstTime -> Delta div (?DAMP);
|
||||
true -> Delta div 2
|
||||
end,
|
||||
Delta2 = Delta1 + Delta1 div NumPoints,
|
||||
adapt1(Delta2, 0).
|
||||
|
||||
adapt1(Delta, K) ->
|
||||
if Delta > ((?BASE) - (?TMIN)) * (?TMAX) div 2 ->
|
||||
adapt1(Delta div ((?BASE) - (?TMIN)), K + (?BASE));
|
||||
true ->
|
||||
K +
|
||||
((?BASE) - (?TMIN) + 1) * Delta div (Delta + (?SKEW))
|
||||
end.
|
||||
|
||||
codepoint(C) ->
|
||||
if (0 =< C) and (C =< 25) -> C + 97;
|
||||
(26 =< C) and (C =< 35) -> C + 22
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Unit tests
|
||||
%%%===================================================================
|
||||
-ifdef(TEST).
|
||||
|
||||
acsii_test() ->
|
||||
?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)).
|
||||
|
||||
utf8_test() ->
|
||||
?assertEqual(
|
||||
<<"xn--d1acufc.xn--p1ai">>,
|
||||
domain_utf8_to_ascii(
|
||||
<<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)).
|
||||
|
||||
-endif.
|
||||
+1
-1
@@ -49,7 +49,7 @@ start_link() ->
|
||||
-spec route(iq(), atom() | pid(), term(), non_neg_integer()) -> ok.
|
||||
route(#iq{type = T} = IQ, Proc, Ctx, Timeout) when T == set; T == get ->
|
||||
Expire = current_time() + Timeout,
|
||||
Rnd = randoms:get_string(),
|
||||
Rnd = p1_rand:get_string(),
|
||||
ID = encode_id(Expire, Rnd),
|
||||
ets:insert(?MODULE, {{Expire, Rnd}, Proc, Ctx}),
|
||||
gen_server:cast(?MODULE, {restart_timer, Expire}),
|
||||
|
||||
+644
-524
File diff suppressed because it is too large
Load Diff
@@ -71,8 +71,9 @@ start_link() ->
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
-spec route_iq(iq(), function()) -> ok.
|
||||
|
||||
+16
-1
@@ -143,10 +143,14 @@ do_start(Level) ->
|
||||
LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024),
|
||||
LogRotateCount = get_integer_env(log_rotate_count, 1),
|
||||
LogRateLimit = get_integer_env(log_rate_limit, 100),
|
||||
ConsoleLevel = case get_lager_version() >= "3.6.0" of
|
||||
true -> [{level, Level}];
|
||||
false -> Level
|
||||
end,
|
||||
application:set_env(lager, error_logger_hwm, LogRateLimit),
|
||||
application:set_env(
|
||||
lager, handlers,
|
||||
[{lager_console_backend, Level},
|
||||
[{lager_console_backend, ConsoleLevel},
|
||||
{lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate},
|
||||
{count, LogRotateCount}, {size, LogRotateSize}]},
|
||||
{lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate},
|
||||
@@ -214,6 +218,10 @@ set(LogLevel) when is_integer(LogLevel) ->
|
||||
ok
|
||||
end, gen_event:which_handlers(lager_event))
|
||||
end,
|
||||
case LogLevel of
|
||||
5 -> xmpp:set_config([{debug, true}]);
|
||||
_ -> xmpp:set_config([{debug, false}])
|
||||
end,
|
||||
{module, lager};
|
||||
set({_LogLevel, _}) ->
|
||||
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
|
||||
@@ -248,3 +256,10 @@ get_lager_handlers() ->
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
get_lager_version() ->
|
||||
Apps = application:loaded_applications(),
|
||||
case lists:keyfind(lager, 1, Apps) of
|
||||
{_, _, Vsn} -> Vsn;
|
||||
false -> "0.0.0"
|
||||
end.
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
config_reloaded/0,
|
||||
opt_type/1]).
|
||||
|
||||
-export([oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
-export([get_commands_spec/0,
|
||||
oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
@@ -645,14 +646,7 @@ logo() ->
|
||||
<<>>
|
||||
end.
|
||||
|
||||
-spec opt_type(oauth_expire) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(oauth_access) -> fun((any()) -> any());
|
||||
(oauth_db_type) -> fun((atom()) -> atom());
|
||||
(oauth_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(oauth_cache_size) -> fun((timeout()) -> timeout());
|
||||
(oauth_use_cache) -> fun((boolean()) -> boolean());
|
||||
(oauth_cache_misse) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(oauth_expire) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(oauth_access) ->
|
||||
|
||||
@@ -92,8 +92,7 @@ path(Path) ->
|
||||
<<Base/binary, "/", Path/binary>>.
|
||||
|
||||
|
||||
-spec opt_type(ext_api_path_oauth) -> fun((binary()) -> binary());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ext_api_path_oauth) ->
|
||||
fun (X) -> iolist_to_binary(X) end;
|
||||
opt_type(_) -> [ext_api_path_oauth].
|
||||
|
||||
+35
-18
@@ -435,7 +435,7 @@ process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El,
|
||||
{<<"query">>, ?NS_PRIVATE} ->
|
||||
process_private(xmpp:decode(El), State);
|
||||
{<<"vCard">>, ?NS_VCARD} ->
|
||||
process_vcard(El, State);
|
||||
process_vcard(xmpp:decode(El), State);
|
||||
{<<"offline-messages">>, NS} ->
|
||||
Msgs = [xmpp:decode(E, NS, [ignore_els]) || E <- Els],
|
||||
process_offline_msgs(Msgs, State);
|
||||
@@ -475,30 +475,47 @@ process_roster(RosterQuery, State = #state{user = U, server = S}) ->
|
||||
-spec process_privacy(privacy_query(), state()) -> {ok, state()} | {error, _}.
|
||||
process_privacy(#privacy_query{lists = Lists,
|
||||
default = Default,
|
||||
active = Active} = PrivacyQuery,
|
||||
active = Active},
|
||||
State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
from = JID, to = JID, sub_els = [PrivacyQuery]},
|
||||
case mod_privacy:process_iq(IQ) of
|
||||
#iq{type = error} = ResIQ ->
|
||||
#stanza_error{reason = Reason} = xmpp:get_error(ResIQ),
|
||||
if Reason == 'item-not-found', Lists == [],
|
||||
Active == undefined, Default /= undefined ->
|
||||
if Lists /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{lists = Lists});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if Active /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{active = Active});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if Default /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{default = Default});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{ok, State}.
|
||||
|
||||
process_privacy2(JID, PQ) ->
|
||||
case mod_privacy:process_iq(#iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID,
|
||||
sub_els = [PQ]}) of
|
||||
#iq{type = error} = ResIQ ->
|
||||
#stanza_error{reason = Reason} = xmpp:get_error(ResIQ),
|
||||
if Reason /= 'item-not-found' ->
|
||||
%% Failed to set default list because there is no
|
||||
%% list with such name. We shouldn't stop here.
|
||||
{ok, State};
|
||||
true ->
|
||||
stop("Failed to write privacy: ~p", [Reason])
|
||||
end;
|
||||
_ ->
|
||||
{ok, State}
|
||||
end.
|
||||
stop("Failed to write default privacy: ~p", [Reason]);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec process_private(private(), state()) -> {ok, state()} | {error, _}.
|
||||
process_private(Private, State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
IQ = #iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID, sub_els = [Private]},
|
||||
case mod_private:process_sm_iq(IQ) of
|
||||
#iq{type = result} ->
|
||||
@@ -510,7 +527,7 @@ process_private(Private, State = #state{user = U, server = S}) ->
|
||||
-spec process_vcard(xmlel(), state()) -> {ok, state()} | {error, _}.
|
||||
process_vcard(El, State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
IQ = #iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID, sub_els = [El]},
|
||||
case mod_vcard:process_sm_iq(IQ) of
|
||||
#iq{type = result} ->
|
||||
|
||||
+293
-818
File diff suppressed because it is too large
Load Diff
@@ -71,7 +71,7 @@ get_spec(Host) ->
|
||||
|
||||
-spec config_reloaded() -> ok.
|
||||
config_reloaded() ->
|
||||
lists:foreach(fun start_host/1, ejabberd_config:get_myhosts()).
|
||||
lists:foreach(fun reload_host/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
-spec start_host(binary()) -> ok.
|
||||
start_host(Host) ->
|
||||
@@ -96,6 +96,10 @@ stop_host(Host) ->
|
||||
supervisor:delete_child(?MODULE, SupName),
|
||||
ok.
|
||||
|
||||
-spec reload_host(binary()) -> ok.
|
||||
reload_host(Host) ->
|
||||
ejabberd_sql_sup:reload(Host).
|
||||
|
||||
%% Returns {true, App} if we have configured sql for the given host
|
||||
needs_sql(Host) ->
|
||||
LHost = jid:nameprep(Host),
|
||||
@@ -108,9 +112,7 @@ needs_sql(Host) ->
|
||||
undefined -> false
|
||||
end.
|
||||
|
||||
-type sql_type() :: mysql | pgsql | sqlite | mssql | odbc.
|
||||
-spec opt_type(sql_type) -> fun((sql_type()) -> sql_type());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
|
||||
+41
-4
@@ -33,10 +33,10 @@
|
||||
%% API
|
||||
-export([start_link/1, get_proc/1, get_connection/1, q/1, qp/1, format_error/1]).
|
||||
%% Commands
|
||||
-export([multi/1, get/1, set/2, del/1,
|
||||
-export([multi/1, get/1, set/2, del/1, info/1,
|
||||
sadd/2, srem/2, smembers/1, sismember/2, scard/1,
|
||||
hget/2, hset/3, hdel/2, hlen/1, hgetall/1, hkeys/1,
|
||||
subscribe/1, publish/2]).
|
||||
subscribe/1, publish/2, script_load/1, evalsha/3]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
@@ -61,6 +61,9 @@
|
||||
-type redis_reply() :: binary() | [binary()].
|
||||
-type redis_command() :: [binary()].
|
||||
-type redis_pipeline() :: [redis_command()].
|
||||
-type redis_info() :: server | clients | memory | persistence |
|
||||
stats | replication | cpu | commandstats |
|
||||
cluster | keyspace | default | all.
|
||||
-type state() :: #state{}.
|
||||
|
||||
-export_type([error_reason/0]).
|
||||
@@ -316,6 +319,40 @@ publish(Channel, Data) ->
|
||||
tr_enq(Cmd, Stack)
|
||||
end.
|
||||
|
||||
-spec script_load(iodata()) -> {ok, binary()} | redis_error().
|
||||
script_load(Data) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
q([<<"SCRIPT">>, <<"LOAD">>, Data]);
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
-spec evalsha(binary(), [iodata()], [iodata()]) -> {ok, binary()} | redis_error().
|
||||
evalsha(SHA, Keys, Args) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
q([<<"EVALSHA">>, SHA, length(Keys)|Keys ++ Args]);
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
-spec info(redis_info()) -> {ok, [{atom(), binary()}]} | redis_error().
|
||||
info(Type) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
case q([<<"INFO">>, misc:atom_to_binary(Type)]) of
|
||||
{ok, Info} ->
|
||||
Lines = binary:split(Info, <<"\r\n">>, [global]),
|
||||
KVs = [binary:split(Line, <<":">>) || Line <- Lines],
|
||||
{ok, [{misc:binary_to_atom(K), V} || [K, V] <- KVs]};
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
@@ -437,7 +474,7 @@ connect(#state{num = Num}) ->
|
||||
erlang:error(Why)
|
||||
end
|
||||
catch _:Reason ->
|
||||
Timeout = randoms:uniform(
|
||||
Timeout = p1_rand:uniform(
|
||||
min(10, ejabberd_redis_sup:get_pool_size())),
|
||||
?ERROR_MSG("Redis connection #~p at ~s:~p has failed: ~p; "
|
||||
"reconnecting in ~p seconds",
|
||||
@@ -502,7 +539,7 @@ log_error(Cmd, Reason) ->
|
||||
|
||||
-spec get_rnd_id() -> pos_integer().
|
||||
get_rnd_id() ->
|
||||
randoms:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
||||
p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
||||
|
||||
-spec get_result([{error, atom() | binary()} | {ok, iodata()}]) ->
|
||||
{ok, [redis_reply()]} | {error, binary()}.
|
||||
|
||||
@@ -126,14 +126,7 @@ get_pool_size() ->
|
||||
iolist_to_list(IOList) ->
|
||||
binary_to_list(iolist_to_binary(IOList)).
|
||||
|
||||
-spec opt_type(redis_connect_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(redis_db) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(redis_password) -> fun((binary()) -> binary());
|
||||
(redis_port) -> fun((0..65535) -> 0..65535);
|
||||
(redis_server) -> fun((binary()) -> binary());
|
||||
(redis_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(redis_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(redis_connect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(redis_db) ->
|
||||
|
||||
@@ -36,7 +36,7 @@ exec({ReM, ReF, ReA}, {RgM, RgF, RgA}) ->
|
||||
-spec run(binary(), binary()) -> match | nomatch | {error, any()}.
|
||||
|
||||
run(String, Regexp) ->
|
||||
case exec({re, run, [String, Regexp, [{capture, none}]]},
|
||||
case exec({re, run, [String, Regexp, [{capture, none}, unicode]]},
|
||||
{regexp, first_match, [binary_to_list(String),
|
||||
binary_to_list(Regexp)]})
|
||||
of
|
||||
|
||||
@@ -162,7 +162,7 @@ get_pids() ->
|
||||
[ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())].
|
||||
|
||||
get_random_pid() ->
|
||||
I = randoms:round_robin(get_pool_size()) + 1,
|
||||
I = p1_rand:round_robin(get_pool_size()) + 1,
|
||||
ejabberd_riak:get_proc(I).
|
||||
|
||||
transform_options(Opts) ->
|
||||
@@ -173,14 +173,7 @@ transform_options({riak_server, {S, P}}, Opts) ->
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec opt_type(riak_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(riak_port) -> fun((0..65535) -> 0..65535);
|
||||
(riak_server) -> fun((binary()) -> binary());
|
||||
(riak_start_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(riak_cacertfile) -> fun((binary()) -> binary());
|
||||
(riak_username) -> fun((binary()) -> binary());
|
||||
(riak_password) -> fun((binary()) -> binary());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(riak_pool_size) ->
|
||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
||||
opt_type(riak_port) ->
|
||||
|
||||
+4
-12
@@ -91,8 +91,9 @@ start_link() ->
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
|
||||
@@ -479,7 +480,7 @@ cache_opts() ->
|
||||
end,
|
||||
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||
|
||||
-spec clean_cache(node()) -> ok.
|
||||
-spec clean_cache(node()) -> non_neg_integer().
|
||||
clean_cache(Node) ->
|
||||
ets_cache:filter(
|
||||
?ROUTES_CACHE,
|
||||
@@ -498,16 +499,7 @@ clean_cache(Node) ->
|
||||
clean_cache() ->
|
||||
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
|
||||
|
||||
-type domain_balancing() :: random | source | destination |
|
||||
bare_source | bare_destination.
|
||||
-spec opt_type(domain_balancing) -> fun((domain_balancing()) -> domain_balancing());
|
||||
(domain_balancing_component_number) -> fun((pos_integer()) -> pos_integer());
|
||||
(router_db_type) -> fun((atom()) -> atom());
|
||||
(router_use_cache) -> fun((boolean()) -> boolean());
|
||||
(router_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(router_cache_size) -> fun((timeout()) -> timeout());
|
||||
(router_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(domain_balancing) ->
|
||||
fun (random) -> random;
|
||||
(source) -> source;
|
||||
|
||||
@@ -122,10 +122,11 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) ->
|
||||
catch _:{bad_node, _} ->
|
||||
[];
|
||||
E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to decode row from 'route' table:~n"
|
||||
"Row = ~p~n"
|
||||
"Domain = ~s~n"
|
||||
"Reason = ~p",
|
||||
[Row, Domain, {E, {R, erlang:get_stacktrace()}}]),
|
||||
[Row, Domain, {E, {R, St}}]),
|
||||
[]
|
||||
end.
|
||||
|
||||
+9
-15
@@ -95,8 +95,9 @@ start_link() ->
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
clean_temporarily_blocked_table() ->
|
||||
@@ -707,19 +708,7 @@ get_s2s_state(S2sPid) ->
|
||||
end,
|
||||
[{s2s_pid, S2sPid} | Infos].
|
||||
|
||||
-type use_starttls() :: boolean() | optional | required | required_trusted.
|
||||
-spec opt_type(route_subdomains) -> fun((s2s | local) -> s2s | local);
|
||||
(s2s_access) -> fun((any()) -> any());
|
||||
(s2s_ciphers) -> fun((binary()) -> binary());
|
||||
(s2s_dhfile) -> fun((binary()) -> binary());
|
||||
(s2s_cafile) -> fun((binary()) -> binary());
|
||||
(s2s_protocol_options) -> fun(([binary()]) -> binary());
|
||||
(s2s_tls_compression) -> fun((boolean()) -> boolean());
|
||||
(s2s_use_starttls) -> fun((use_starttls()) -> use_starttls());
|
||||
(s2s_zlib) -> fun((boolean()) -> boolean());
|
||||
(s2s_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(route_subdomains) ->
|
||||
fun (s2s) -> s2s;
|
||||
(local) -> local
|
||||
@@ -749,7 +738,12 @@ opt_type(s2s_use_starttls) ->
|
||||
required_trusted
|
||||
end;
|
||||
opt_type(s2s_zlib) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
fun(true) ->
|
||||
ejabberd:start_app(ezlib),
|
||||
true;
|
||||
(false) ->
|
||||
false
|
||||
end;
|
||||
opt_type(s2s_timeout) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> timer:seconds(I);
|
||||
(infinity) -> infinity;
|
||||
|
||||
+46
-80
@@ -21,17 +21,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_s2s_in).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([listen_opt_type/1]).
|
||||
-export([start/2, start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
||||
compress_methods/1,
|
||||
-export([tls_options/1, tls_required/1, tls_enabled/1, compress_methods/1,
|
||||
unauthenticated_stream_features/1, authenticated_stream_features/1,
|
||||
handle_stream_start/2, handle_stream_end/2,
|
||||
handle_stream_established/1, handle_auth_success/4,
|
||||
@@ -54,16 +51,8 @@
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
start(SockData, Opts) ->
|
||||
case proplists:get_value(supervisor, Opts, true) of
|
||||
true ->
|
||||
case supervisor:start_child(ejabberd_s2s_in_sup, [SockData, Opts]) of
|
||||
{ok, undefined} -> ignore;
|
||||
Res -> Res
|
||||
end;
|
||||
_ ->
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts))
|
||||
end.
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
@@ -78,8 +67,8 @@ close(Ref, Reason) ->
|
||||
stop(Ref) ->
|
||||
xmpp_stream_in:stop(Ref).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
-spec send(pid(), xmpp_element()) -> ok;
|
||||
(state(), xmpp_element()) -> state().
|
||||
@@ -132,25 +121,30 @@ reject_unauthenticated_packet(State, _Pkt) ->
|
||||
Err = xmpp:serr_not_authorized(),
|
||||
send(State, Err).
|
||||
|
||||
process_closed(State, _Reason) ->
|
||||
process_closed(#{server := LServer} = State, Reason) ->
|
||||
RServer = case State of
|
||||
#{remote_server := Name} ->
|
||||
Name;
|
||||
#{ip := IP} ->
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))
|
||||
end,
|
||||
?INFO_MSG("Closing inbound s2s connection ~s -> ~s: ~s",
|
||||
[RServer, LServer, xmpp_stream_out:format_error(Reason)]),
|
||||
stop(State).
|
||||
|
||||
%%%===================================================================
|
||||
%%% xmpp_stream_in callbacks
|
||||
%%%===================================================================
|
||||
tls_options(#{tls_options := TLSOpts, server_host := LServer}) ->
|
||||
tls_options(#{tls_options := TLSOpts, lserver := LServer}) ->
|
||||
ejabberd_s2s:tls_options(LServer, TLSOpts).
|
||||
|
||||
tls_required(#{server_host := LServer}) ->
|
||||
tls_required(#{lserver := LServer}) ->
|
||||
ejabberd_s2s:tls_required(LServer).
|
||||
|
||||
tls_verify(#{server_host := LServer}) ->
|
||||
ejabberd_s2s:tls_verify(LServer).
|
||||
|
||||
tls_enabled(#{server_host := LServer}) ->
|
||||
tls_enabled(#{lserver := LServer}) ->
|
||||
ejabberd_s2s:tls_enabled(LServer).
|
||||
|
||||
compress_methods(#{server_host := LServer}) ->
|
||||
compress_methods(#{lserver := LServer}) ->
|
||||
case ejabberd_s2s:zlib_enabled(LServer) of
|
||||
true -> [<<"zlib">>];
|
||||
false -> []
|
||||
@@ -187,23 +181,23 @@ handle_auth_success(RServer, Mech, _AuthModule,
|
||||
?INFO_MSG("(~s) Accepted inbound s2s ~s authentication ~s -> ~s (~s)",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
State1 = case ejabberd_s2s:allow_host(ServerHost, RServer) of
|
||||
State1 = case ejabberd_s2s:allow_host(LServer, RServer) of
|
||||
true ->
|
||||
AuthDomains1 = sets:add_element(RServer, AuthDomains),
|
||||
State0 = change_shaper(State, RServer),
|
||||
State0#{auth_domains => AuthDomains1};
|
||||
false ->
|
||||
State
|
||||
end,
|
||||
end,
|
||||
ejabberd_hooks:run_fold(s2s_in_auth_result, ServerHost, State1, [true, RServer]).
|
||||
|
||||
handle_auth_failure(RServer, Mech, Reason,
|
||||
#{socket := Socket, ip := IP,
|
||||
server_host := ServerHost,
|
||||
lserver := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
?WARNING_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
ejabberd_hooks:run_fold(s2s_in_auth_result,
|
||||
ServerHost, State, [false, RServer]).
|
||||
|
||||
@@ -227,7 +221,7 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}} = State) ->
|
||||
case Pkt1 of
|
||||
drop -> ok;
|
||||
_ -> ejabberd_router:route(Pkt1)
|
||||
end,
|
||||
end,
|
||||
State2;
|
||||
{error, Err} ->
|
||||
send(State, Err)
|
||||
@@ -255,9 +249,9 @@ init([State, Opts]) ->
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
auth_domains => sets:new(),
|
||||
@@ -333,7 +327,7 @@ check_to(#jid{lserver = LServer}, _State) ->
|
||||
ejabberd_router:is_my_route(LServer).
|
||||
|
||||
-spec set_idle_timeout(state()) -> state().
|
||||
set_idle_timeout(#{server_host := LServer,
|
||||
set_idle_timeout(#{lserver := LServer,
|
||||
established := true} = State) ->
|
||||
Timeout = ejabberd_s2s:get_idle_timeout(LServer),
|
||||
xmpp_stream_in:set_timeout(State, Timeout);
|
||||
@@ -344,52 +338,24 @@ set_idle_timeout(State) ->
|
||||
change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
||||
RServer) ->
|
||||
Shaper = acl:match_rule(ServerHost, ShaperName, jid:make(RServer)),
|
||||
xmpp_stream_in:change_shaper(State, Shaper).
|
||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||
|
||||
-spec listen_opt_type(shaper) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(supervisor) -> fun((boolean()) -> boolean());
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(ciphers) -> ejabberd_s2s:opt_type(s2s_ciphers);
|
||||
listen_opt_type(dhfile) -> ejabberd_s2s:opt_type(s2s_dhfile);
|
||||
listen_opt_type(cafile) -> ejabberd_s2s:opt_type(s2s_cafile);
|
||||
listen_opt_type(protocol_options) -> ejabberd_s2s:opt_type(s2s_protocol_options);
|
||||
listen_opt_type(tls_compression) -> ejabberd_s2s:opt_type(s2s_tls_compression);
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
|
||||
tls_compression, tls, max_fsm_queue, backlog, inet, inet6,
|
||||
accept_interval].
|
||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
||||
File
|
||||
end.
|
||||
|
||||
listen_options() ->
|
||||
[{shaper, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000}].
|
||||
|
||||
+12
-18
@@ -138,9 +138,9 @@ host_down(Host) ->
|
||||
process_auth_result(#{server := LServer, remote_server := RServer} = State,
|
||||
{false, Reason}) ->
|
||||
Delay = get_delay(),
|
||||
?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: "
|
||||
"authentication failed; bouncing for ~p seconds",
|
||||
[LServer, RServer, Delay]),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: "
|
||||
"authentication failed; bouncing for ~p seconds",
|
||||
[LServer, RServer, Delay]),
|
||||
State1 = State#{on_route => bounce, stop_reason => Reason},
|
||||
State2 = close(State1),
|
||||
State3 = bounce_queue(State2),
|
||||
@@ -157,9 +157,9 @@ process_closed(#{server := LServer, remote_server := RServer,
|
||||
process_closed(#{server := LServer, remote_server := RServer} = State,
|
||||
Reason) ->
|
||||
Delay = get_delay(),
|
||||
?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; "
|
||||
"bouncing for ~p seconds",
|
||||
[LServer, RServer, format_error(Reason), Delay]),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; "
|
||||
"bouncing for ~p seconds",
|
||||
[LServer, RServer, format_error(Reason), Delay]),
|
||||
State1 = State#{on_route => bounce},
|
||||
State2 = bounce_queue(State1),
|
||||
xmpp_stream_out:set_timeout(State2, timer:seconds(Delay)).
|
||||
@@ -223,10 +223,10 @@ handle_auth_failure(Mech, Reason,
|
||||
remote_server := RServer,
|
||||
server_host := ServerHost,
|
||||
server := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
xmpp_stream_out:format_error(Reason)]),
|
||||
?WARNING_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
xmpp_stream_out:format_error(Reason)]),
|
||||
ejabberd_hooks:run_fold(s2s_out_auth_result, ServerHost, State, [{false, Reason}]).
|
||||
|
||||
handle_packet(Pkt, #{server_host := ServerHost} = State) ->
|
||||
@@ -375,7 +375,7 @@ mk_bounce_error(_Lang, _State) ->
|
||||
-spec get_delay() -> non_neg_integer().
|
||||
get_delay() ->
|
||||
MaxDelay = ejabberd_config:get_option(s2s_max_retry_delay, 300),
|
||||
randoms:uniform(MaxDelay).
|
||||
p1_rand:uniform(MaxDelay).
|
||||
|
||||
-spec set_idle_timeout(state()) -> state().
|
||||
set_idle_timeout(#{on_route := send, server := LServer} = State) ->
|
||||
@@ -443,13 +443,7 @@ maybe_report_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
|
||||
maybe_report_huge_timeout(_, _) ->
|
||||
ok.
|
||||
|
||||
-spec opt_type(outgoing_s2s_families) -> fun(([ipv4|ipv6]) -> [inet|inet6]);
|
||||
(outgoing_s2s_port) -> fun((0..65535) -> 0..65535);
|
||||
(outgoing_s2s_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_dns_retries) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(s2s_dns_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_max_retry_delay) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(outgoing_s2s_families) ->
|
||||
fun(Families) ->
|
||||
lists:map(
|
||||
|
||||
+43
-69
@@ -21,20 +21,19 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_service).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-protocol({xep, 114, '1.6'}).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0, close/1, close/2]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([listen_opt_type/1, transform_listen_option/2]).
|
||||
-export([start/2, start_link/2, accept/1]).
|
||||
-export([listen_opt_type/1, listen_options/0, transform_listen_option/2]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_info/2, terminate/2, code_change/3]).
|
||||
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
|
||||
handle_authenticated_packet/2, get_password_fun/1, tls_options/1]).
|
||||
%% API
|
||||
-export([send/2]).
|
||||
-export([send/2, close/1, close/2]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -53,8 +52,8 @@ start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
-spec send(pid(), xmpp_element()) -> ok;
|
||||
(state(), xmpp_element()) -> state().
|
||||
@@ -79,7 +78,8 @@ tls_options(#{tls_options := TLSOptions}) ->
|
||||
|
||||
init([State, Opts]) ->
|
||||
Access = proplists:get_value(access, Opts, all),
|
||||
Shaper = proplists:get_value(shaper_rule, Opts, none),
|
||||
Shaper = proplists:get_value(shaper, Opts,
|
||||
proplists:get_value(shaper_rule, Opts, none)),
|
||||
GlobalPassword = proplists:get_value(password, Opts, random_password()),
|
||||
HostOpts = proplists:get_value(hosts, Opts, [{global, GlobalPassword}]),
|
||||
HostOpts1 = lists:map(
|
||||
@@ -101,7 +101,7 @@ init([State, Opts]) ->
|
||||
end,
|
||||
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
||||
State1 = xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)),
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
State3 = State2#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
@@ -146,10 +146,10 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
{ok, Password} ->
|
||||
{Password, undefined};
|
||||
error ->
|
||||
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
||||
"external component from ~s",
|
||||
[xmpp_socket:pp(Socket), RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
?WARNING_MSG("(~s) Domain ~s is unconfigured for "
|
||||
"external component from ~s",
|
||||
[xmpp_socket:pp(Socket), RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
{false, undefined}
|
||||
end
|
||||
end.
|
||||
@@ -177,11 +177,11 @@ handle_auth_success(_, Mech, _,
|
||||
handle_auth_failure(_, Mech, Reason,
|
||||
#{remote_server := RemoteServer,
|
||||
socket := Socket, ip := IP} = State) ->
|
||||
?INFO_MSG("(~s) Failed external component ~s authentication "
|
||||
"for ~s from ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
Reason]),
|
||||
?WARNING_MSG("(~s) Failed external component ~s authentication "
|
||||
"for ~s from ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
Reason]),
|
||||
State.
|
||||
|
||||
handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
|
||||
@@ -261,7 +261,7 @@ check_from(From, #{host_opts := HostOpts}) ->
|
||||
dict:is_key(Server, HostOpts).
|
||||
|
||||
random_password() ->
|
||||
str:sha(randoms:bytes(20)).
|
||||
str:sha(p1_rand:bytes(20)).
|
||||
|
||||
transform_listen_option({hosts, Hosts, O}, Opts) ->
|
||||
case lists:keyfind(hosts, 1, Opts) of
|
||||
@@ -281,39 +281,12 @@ transform_listen_option({host, Host, Os}, Opts) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec listen_opt_type(access) -> fun((any()) -> any());
|
||||
(shaper_rule) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(check_from) -> fun((boolean()) -> boolean());
|
||||
(password) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
||||
[{binary(), binary() | undefined}]);
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
listen_opt_type(shaper_rule) ->
|
||||
fun(V) ->
|
||||
?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
|
||||
"is renamed to 'shaper'", [?MODULE]),
|
||||
acl:shaper_rules_validator(V)
|
||||
end;
|
||||
listen_opt_type(ciphers) -> fun iolist_to_binary/1;
|
||||
listen_opt_type(dhfile) -> fun misc:try_read_file/1;
|
||||
listen_opt_type(cafile) -> fun misc:try_read_file/1;
|
||||
listen_opt_type(protocol_options) ->
|
||||
fun(Options) -> str:join(Options, <<"|">>) end;
|
||||
listen_opt_type(tls_compression) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(password) -> fun iolist_to_binary/1;
|
||||
listen_opt_type(hosts) ->
|
||||
@@ -328,21 +301,22 @@ listen_opt_type(hosts) ->
|
||||
end, HostOpts)
|
||||
end;
|
||||
listen_opt_type(global_routes) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I) -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
|
||||
protocol_options, tls_compression, password, hosts, check_from,
|
||||
max_fsm_queue, global_routes, backlog, inet, inet6, accept_interval].
|
||||
fun(B) when is_boolean(B) -> B end.
|
||||
|
||||
listen_options() ->
|
||||
[{access, all},
|
||||
{shaper, none},
|
||||
{shaper_rule, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000},
|
||||
{password, undefined},
|
||||
{hosts, []},
|
||||
{check_from, true},
|
||||
{global_routes, true}].
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : shaper.erl
|
||||
%%% File : ejabberd_shaper.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Functions to control connections traffic
|
||||
%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
@@ -23,7 +23,7 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(shaper).
|
||||
-module(ejabberd_shaper).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(ejabberd_config).
|
||||
@@ -39,19 +39,13 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(maxrate, {maxrate = 0 :: integer(),
|
||||
burst_size = 0 :: integer(),
|
||||
acquired_credit = 0 :: integer(),
|
||||
lasttime = 0 :: integer()}).
|
||||
|
||||
-record(shaper, {name :: {atom(), global},
|
||||
maxrate :: integer(),
|
||||
burst_size :: integer()}).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-type shaper() :: none | #maxrate{}.
|
||||
|
||||
-type shaper() :: none | p1_shaper:state().
|
||||
-export_type([shaper/0]).
|
||||
|
||||
-spec start_link() -> {ok, pid()} | {error, any()}.
|
||||
@@ -84,7 +78,6 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec load_from_config() -> ok | {error, any()}.
|
||||
|
||||
load_from_config() ->
|
||||
Shapers = ejabberd_config:get_option(shaper, []),
|
||||
case mnesia:transaction(
|
||||
@@ -105,7 +98,6 @@ load_from_config() ->
|
||||
end.
|
||||
|
||||
-spec get_max_rate(atom()) -> none | non_neg_integer().
|
||||
|
||||
get_max_rate(none) ->
|
||||
none;
|
||||
get_max_rate(Name) ->
|
||||
@@ -122,29 +114,18 @@ new(none) ->
|
||||
new(Name) ->
|
||||
case ets:lookup(shaper, {Name, global}) of
|
||||
[#shaper{maxrate = R, burst_size = B}] ->
|
||||
|
||||
#maxrate{maxrate = R, burst_size = B,
|
||||
acquired_credit = B,
|
||||
lasttime = p1_time_compat:system_time(micro_seconds)};
|
||||
p1_shaper:new(R, B);
|
||||
[] ->
|
||||
none
|
||||
end.
|
||||
|
||||
-spec update(shaper(), integer()) -> {shaper(), integer()}.
|
||||
|
||||
update(none, _Size) -> {none, 0};
|
||||
update(#maxrate{maxrate = MR, burst_size = BS,
|
||||
acquired_credit = AC, lasttime = L} = State, Size) ->
|
||||
Now = p1_time_compat:system_time(micro_seconds),
|
||||
AC2 = min(BS, AC + (MR*(Now - L) div 1000000) - Size),
|
||||
|
||||
Pause = if AC2 >= 0 -> 0;
|
||||
true -> -1000*AC2 div MR
|
||||
end,
|
||||
?DEBUG("MaxRate=~p, BurstSize=~p, AcquiredCredit=~p, Size=~p, NewAcquiredCredit=~p, Pause=~p",
|
||||
[MR, BS, AC, Size, AC2, Pause]),
|
||||
{State#maxrate{acquired_credit = AC2, lasttime = Now},
|
||||
Pause}.
|
||||
update(Shaper, Size) ->
|
||||
Result = p1_shaper:update(Shaper, Size),
|
||||
?DEBUG("Shaper update:~n~s =>~n~s",
|
||||
[p1_shaper:pp(Shaper), p1_shaper:pp(Result)]),
|
||||
Result.
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
@@ -166,7 +147,6 @@ transform_options({shaper, List}, Opts) when is_list(List) ->
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt | Opts].
|
||||
|
||||
-spec opt_type(shaper) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(shaper) -> fun(V) -> V end;
|
||||
opt_type(_) -> [shaper].
|
||||
+23
-27
@@ -24,25 +24,28 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_sip).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-ifndef(SIP).
|
||||
-include("logger.hrl").
|
||||
-export([socket_type/0, start/2, listen_opt_type/1]).
|
||||
log_error() ->
|
||||
?CRITICAL_MSG("ejabberd is not compiled with SIP support", []).
|
||||
socket_type() ->
|
||||
log_error(),
|
||||
raw.
|
||||
listen_opt_type(_) ->
|
||||
log_error(),
|
||||
[].
|
||||
-export([accept/1, start/2, start_link/2, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
"ejabberd is not compiled with SIP support",
|
||||
[?MODULE]),
|
||||
erlang:error(sip_not_compiled).
|
||||
accept(_) ->
|
||||
fail().
|
||||
listen_options() ->
|
||||
fail().
|
||||
start(_, _) ->
|
||||
log_error(),
|
||||
{error, sip_not_compiled}.
|
||||
fail().
|
||||
start_link(_, _) ->
|
||||
fail().
|
||||
-else.
|
||||
%% API
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
|
||||
socket_type/0, listen_opt_type/1]).
|
||||
start_link/2, accept/1, listen_options/0]).
|
||||
|
||||
|
||||
%%%===================================================================
|
||||
@@ -62,8 +65,11 @@ udp_recv(Sock, Addr, Port, Data, Opts) ->
|
||||
start(Opaque, Opts) ->
|
||||
esip_socket:start(Opaque, Opts).
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
start_link({gen_tcp, Sock}, Opts) ->
|
||||
esip_socket:start_link(Sock, Opts).
|
||||
|
||||
accept(_) ->
|
||||
ok.
|
||||
|
||||
set_certfile(Opts) ->
|
||||
case lists:keymember(certfile, 1, Opts) of
|
||||
@@ -83,19 +89,9 @@ set_certfile(Opts) ->
|
||||
end
|
||||
end.
|
||||
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
%% We cannot deprecate the option for now:
|
||||
%% I think SIP clients are too stupid to set SNI
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[tls, certfile, accept_interval].
|
||||
listen_options() ->
|
||||
[{tls, false},
|
||||
{certfile, undefined}].
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
|
||||
+90
-93
@@ -50,8 +50,6 @@
|
||||
set_presence/7,
|
||||
unset_presence/6,
|
||||
close_session_unset_presence/5,
|
||||
set_offline_info/5,
|
||||
get_offline_info/4,
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
get_vh_session_list/1,
|
||||
@@ -68,6 +66,8 @@
|
||||
get_session_sids/2,
|
||||
get_user_info/2,
|
||||
get_user_info/3,
|
||||
set_user_info/5,
|
||||
del_user_info/4,
|
||||
get_user_ip/3,
|
||||
get_max_user_sessions/2,
|
||||
get_all_pids/0,
|
||||
@@ -78,8 +78,7 @@
|
||||
host_down/1,
|
||||
make_sid/0,
|
||||
clean_cache/1,
|
||||
config_reloaded/0,
|
||||
is_online/1
|
||||
config_reloaded/0
|
||||
]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@@ -142,9 +141,10 @@ route(Packet) ->
|
||||
Packet1 ->
|
||||
try do_route(Packet1), ok
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet1),
|
||||
{E, {R, erlang:get_stacktrace()}}])
|
||||
{E, {R, St}}])
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -210,14 +210,14 @@ get_user_resources(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = online(get_sessions(Mod, LUser, LServer)),
|
||||
Ss = get_sessions(Mod, LUser, LServer),
|
||||
[element(3, S#session.usr) || S <- clean_session_list(Ss)].
|
||||
|
||||
-spec get_user_present_resources(binary(), binary()) -> [tuple()].
|
||||
|
||||
get_user_present_resources(LUser, LServer) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = online(get_sessions(Mod, LUser, LServer)),
|
||||
Ss = get_sessions(Mod, LUser, LServer),
|
||||
[{S#session.priority, element(3, S#session.usr)}
|
||||
|| S <- clean_session_list(Ss), is_integer(S#session.priority)].
|
||||
|
||||
@@ -228,7 +228,7 @@ get_user_ip(User, Server, Resource) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case online(get_sessions(Mod, LUser, LServer, LResource)) of
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] ->
|
||||
undefined;
|
||||
Ss ->
|
||||
@@ -241,7 +241,7 @@ get_user_info(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = online(get_sessions(Mod, LUser, LServer)),
|
||||
Ss = get_sessions(Mod, LUser, LServer),
|
||||
[{LResource, [{node, node(Pid)}, {ts, Ts}, {pid, Pid},
|
||||
{priority, Priority} | Info]}
|
||||
|| #session{usr = {_, _, LResource},
|
||||
@@ -256,7 +256,7 @@ get_user_info(User, Server, Resource) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case online(get_sessions(Mod, LUser, LServer, LResource)) of
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] ->
|
||||
offline;
|
||||
Ss ->
|
||||
@@ -268,6 +268,44 @@ get_user_info(User, Server, Resource) ->
|
||||
|Session#session.info]
|
||||
end.
|
||||
|
||||
-spec set_user_info(binary(), binary(), binary(), atom(), term()) -> ok | {error, any()}.
|
||||
set_user_info(User, Server, Resource, Key, Val) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] -> {error, notfound};
|
||||
Ss ->
|
||||
lists:foldl(
|
||||
fun(#session{sid = {_, Pid},
|
||||
info = Info} = Session, _) when Pid == self() ->
|
||||
Info1 = lists:keystore(Key, 1, Info, {Key, Val}),
|
||||
set_session(Session#session{info = Info1});
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, {error, not_owner}, Ss)
|
||||
end.
|
||||
|
||||
-spec del_user_info(binary(), binary(), binary(), atom()) -> ok | {error, any()}.
|
||||
del_user_info(User, Server, Resource, Key) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] -> {error, notfound};
|
||||
Ss ->
|
||||
lists:foldl(
|
||||
fun(#session{sid = {_, Pid},
|
||||
info = Info} = Session, _) when Pid == self() ->
|
||||
Info1 = lists:keydelete(Key, 1, Info),
|
||||
set_session(Session#session{info = Info1});
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, {error, not_owner}, Ss)
|
||||
end.
|
||||
|
||||
-spec set_presence(sid(), binary(), binary(), binary(),
|
||||
prio(), presence(), info()) -> ok.
|
||||
|
||||
@@ -315,7 +353,7 @@ get_session_sid(User, Server, Resource) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case online(get_sessions(Mod, LUser, LServer, LResource)) of
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] ->
|
||||
none;
|
||||
Ss ->
|
||||
@@ -329,43 +367,15 @@ get_session_sids(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
Sessions = online(get_sessions(Mod, LUser, LServer)),
|
||||
Sessions = get_sessions(Mod, LUser, LServer),
|
||||
[SID || #session{sid = SID} <- Sessions].
|
||||
|
||||
-spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok.
|
||||
|
||||
set_offline_info(SID, User, Server, Resource, Info) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]).
|
||||
|
||||
-spec get_offline_info(erlang:timestamp(), binary(), binary(),
|
||||
binary()) -> none | info().
|
||||
|
||||
get_offline_info(Time, User, Server, Resource) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[#session{sid = {Time, _}, info = Info}] ->
|
||||
case proplists:get_bool(offline, Info) of
|
||||
true ->
|
||||
Info;
|
||||
false ->
|
||||
none
|
||||
end;
|
||||
_ ->
|
||||
none
|
||||
end.
|
||||
|
||||
-spec dirty_get_sessions_list() -> [ljid()].
|
||||
|
||||
dirty_get_sessions_list() ->
|
||||
lists:flatmap(
|
||||
fun(Mod) ->
|
||||
[S#session.usr || S <- online(get_sessions(Mod))]
|
||||
[S#session.usr || S <- get_sessions(Mod)]
|
||||
end, get_sm_backends()).
|
||||
|
||||
-spec dirty_get_my_sessions_list() -> [#session{}].
|
||||
@@ -373,7 +383,7 @@ dirty_get_sessions_list() ->
|
||||
dirty_get_my_sessions_list() ->
|
||||
lists:flatmap(
|
||||
fun(Mod) ->
|
||||
[S || S <- online(get_sessions(Mod)),
|
||||
[S || S <- get_sessions(Mod),
|
||||
node(element(2, S#session.sid)) == node()]
|
||||
end, get_sm_backends()).
|
||||
|
||||
@@ -382,14 +392,14 @@ dirty_get_my_sessions_list() ->
|
||||
get_vh_session_list(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
[S#session.usr || S <- online(get_sessions(Mod, LServer))].
|
||||
[S#session.usr || S <- get_sessions(Mod, LServer)].
|
||||
|
||||
-spec get_all_pids() -> [pid()].
|
||||
|
||||
get_all_pids() ->
|
||||
lists:flatmap(
|
||||
fun(Mod) ->
|
||||
[element(2, S#session.sid) || S <- online(get_sessions(Mod))]
|
||||
[element(2, S#session.sid) || S <- get_sessions(Mod)]
|
||||
end, get_sm_backends()).
|
||||
|
||||
-spec get_vh_session_number(binary()) -> non_neg_integer().
|
||||
@@ -397,7 +407,7 @@ get_all_pids() ->
|
||||
get_vh_session_number(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
length(online(get_sessions(Mod, LServer))).
|
||||
length(get_sessions(Mod, LServer)).
|
||||
|
||||
%% Why the hell do we have so many similar kicks?
|
||||
c2s_handle_info(#{lang := Lang} = State, replaced) ->
|
||||
@@ -426,15 +436,22 @@ config_reloaded() ->
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
init_cache(),
|
||||
lists:foreach(fun(Mod) -> Mod:init() end, get_sm_backends()),
|
||||
clean_cache(),
|
||||
gen_iq_handler:start(?MODULE),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}}.
|
||||
case lists:foldl(
|
||||
fun(Mod, ok) -> Mod:init();
|
||||
(_, Err) -> Err
|
||||
end, ok, get_sm_backends()) of
|
||||
ok ->
|
||||
clean_cache(),
|
||||
gen_iq_handler:start(?MODULE),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}};
|
||||
{error, Why} ->
|
||||
{stop, Why}
|
||||
end.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
@@ -506,9 +523,13 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
|
||||
LResource = jid:resourceprep(Resource),
|
||||
US = {LUser, LServer},
|
||||
USR = {LUser, LServer, LResource},
|
||||
set_session(#session{sid = SID, usr = USR, us = US,
|
||||
priority = Priority, info = Info}).
|
||||
|
||||
-spec set_session(#session{}) -> ok | {error, any()}.
|
||||
set_session(#session{us = {LUser, LServer}} = Session) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
case Mod:set_session(#session{sid = SID, usr = USR, us = US,
|
||||
priority = Priority, info = Info}) of
|
||||
case Mod:set_session(Session) of
|
||||
ok ->
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
@@ -571,16 +592,6 @@ delete_session(Mod, #session{usr = {LUser, LServer, _}} = Session) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec online([#session{}]) -> [#session{}].
|
||||
|
||||
online(Sessions) ->
|
||||
lists:filter(fun is_online/1, Sessions).
|
||||
|
||||
-spec is_online(#session{}) -> boolean().
|
||||
|
||||
is_online(#session{info = Info}) ->
|
||||
not proplists:get_bool(offline, Info).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
-spec do_route(jid(), term()) -> any().
|
||||
do_route(#jid{lresource = <<"">>} = To, Term) ->
|
||||
@@ -592,7 +603,7 @@ do_route(To, Term) ->
|
||||
?DEBUG("broadcasting ~p to ~s", [Term, jid:encode(To)]),
|
||||
{U, S, R} = jid:tolower(To),
|
||||
Mod = get_sm_backend(S),
|
||||
case online(get_sessions(Mod, U, S, R)) of
|
||||
case get_sessions(Mod, U, S, R) of
|
||||
[] ->
|
||||
?DEBUG("dropping broadcast to unavailable resourse: ~p", [Term]);
|
||||
Ss ->
|
||||
@@ -623,7 +634,7 @@ do_route(#presence{to = To, type = T} = Packet)
|
||||
ejabberd_c2s:route(Pid, {route, Packet1});
|
||||
(_) ->
|
||||
ok
|
||||
end, online(get_sessions(Mod, LUser, LServer)));
|
||||
end, get_sessions(Mod, LUser, LServer));
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
@@ -652,7 +663,7 @@ do_route(Packet) ->
|
||||
To = xmpp:get_to(Packet),
|
||||
{LUser, LServer, LResource} = jid:tolower(To),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case online(get_sessions(Mod, LUser, LServer, LResource)) of
|
||||
case get_sessions(Mod, LUser, LServer, LResource) of
|
||||
[] ->
|
||||
case Packet of
|
||||
#message{type = T} when T == chat; T == normal ->
|
||||
@@ -700,8 +711,8 @@ route_message(#message{to = To, type = Type} = Packet) ->
|
||||
(P >= 0) and (Type == headline) ->
|
||||
LResource = jid:resourceprep(R),
|
||||
Mod = get_sm_backend(LServer),
|
||||
case online(get_sessions(Mod, LUser, LServer,
|
||||
LResource)) of
|
||||
case get_sessions(Mod, LUser, LServer,
|
||||
LResource) of
|
||||
[] ->
|
||||
ok; % Race condition
|
||||
Ss ->
|
||||
@@ -772,13 +783,9 @@ check_for_sessions_to_replace(User, Server, Resource) ->
|
||||
check_existing_resources(LUser, LServer, LResource) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = get_sessions(Mod, LUser, LServer, LResource),
|
||||
{OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss),
|
||||
lists:foreach(fun(S) ->
|
||||
delete_session(Mod, S)
|
||||
end, OfflineSs),
|
||||
if OnlineSs == [] -> ok;
|
||||
if Ss == [] -> ok;
|
||||
true ->
|
||||
SIDs = [SID || #session{sid = SID} <- OnlineSs],
|
||||
SIDs = [SID || #session{sid = SID} <- Ss],
|
||||
MaxSID = lists:max(SIDs),
|
||||
lists:foreach(fun ({_, Pid} = S) when S /= MaxSID ->
|
||||
ejabberd_c2s:route(Pid, replaced);
|
||||
@@ -798,22 +805,17 @@ get_resource_sessions(User, Server, Resource) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LResource = jid:resourceprep(Resource),
|
||||
Mod = get_sm_backend(LServer),
|
||||
[S#session.sid || S <- online(get_sessions(Mod, LUser, LServer, LResource))].
|
||||
[S#session.sid || S <- get_sessions(Mod, LUser, LServer, LResource)].
|
||||
|
||||
-spec check_max_sessions(binary(), binary()) -> ok | replaced.
|
||||
check_max_sessions(LUser, LServer) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = get_sessions(Mod, LUser, LServer),
|
||||
{OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss),
|
||||
MaxSessions = get_max_user_sessions(LUser, LServer),
|
||||
if length(OnlineSs) =< MaxSessions -> ok;
|
||||
if length(Ss) =< MaxSessions -> ok;
|
||||
true ->
|
||||
#session{sid = {_, Pid}} = lists:min(OnlineSs),
|
||||
#session{sid = {_, Pid}} = lists:min(Ss),
|
||||
ejabberd_c2s:route(Pid, replaced)
|
||||
end,
|
||||
if length(OfflineSs) =< MaxSessions -> ok;
|
||||
true ->
|
||||
delete_session(Mod, lists:min(OfflineSs))
|
||||
end.
|
||||
|
||||
%% Get the user_max_session setting
|
||||
@@ -835,7 +837,7 @@ get_max_user_sessions(LUser, Host) ->
|
||||
|
||||
force_update_presence({LUser, LServer}) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = online(get_sessions(Mod, LUser, LServer)),
|
||||
Ss = get_sessions(Mod, LUser, LServer),
|
||||
lists:foreach(fun (#session{sid = {_, Pid}}) ->
|
||||
ejabberd_c2s:resend_presence(Pid)
|
||||
end,
|
||||
@@ -890,7 +892,7 @@ cache_opts() ->
|
||||
end,
|
||||
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||
|
||||
-spec clean_cache(node()) -> ok.
|
||||
-spec clean_cache(node()) -> non_neg_integer().
|
||||
clean_cache(Node) ->
|
||||
ets_cache:filter(
|
||||
?SM_CACHE,
|
||||
@@ -1004,12 +1006,7 @@ kick_user(User, Server, Resource) ->
|
||||
make_sid() ->
|
||||
{p1_time_compat:unique_timestamp(), self()}.
|
||||
|
||||
-spec opt_type(sm_db_type) -> fun((atom()) -> atom());
|
||||
(sm_use_cache) -> fun((boolean()) -> boolean());
|
||||
(sm_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(sm_cache_size) -> fun((timeout()) -> timeout());
|
||||
(sm_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
opt_type(O) when O == sm_use_cache; O == sm_cache_missed ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
|
||||
+79
-28
@@ -27,12 +27,11 @@
|
||||
-define(GEN_SERVER, p1_server).
|
||||
-endif.
|
||||
-behaviour(?GEN_SERVER).
|
||||
|
||||
-behaviour(ejabberd_sm).
|
||||
|
||||
-export([init/0, set_session/1, delete_session/1,
|
||||
get_sessions/0, get_sessions/1, get_sessions/2,
|
||||
cache_nodes/1]).
|
||||
cache_nodes/1, clean_table/1, clean_table/0]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
@@ -41,6 +40,7 @@
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(SM_KEY, <<"ejabberd:sm">>).
|
||||
-define(MIN_REDIS_VERSION, <<"3.2.0">>).
|
||||
-record(state, {}).
|
||||
|
||||
%%%===================================================================
|
||||
@@ -70,10 +70,14 @@ set_session(Session) ->
|
||||
SIDKey = sid_to_key(Session#session.sid),
|
||||
ServKey = server_to_key(element(2, Session#session.us)),
|
||||
USSIDKey = us_sid_to_key(Session#session.us, Session#session.sid),
|
||||
NodeHostKey = node_host_to_key(node(), element(2, Session#session.us)),
|
||||
case ejabberd_redis:multi(
|
||||
fun() ->
|
||||
ejabberd_redis:hset(USKey, SIDKey, T),
|
||||
ejabberd_redis:hset(ServKey, USSIDKey, T),
|
||||
ejabberd_redis:hset(NodeHostKey,
|
||||
<<USKey/binary, "||", SIDKey/binary>>,
|
||||
USSIDKey),
|
||||
ejabberd_redis:publish(
|
||||
?SM_KEY, term_to_binary({delete, Session#session.us}))
|
||||
end) of
|
||||
@@ -89,10 +93,13 @@ delete_session(#session{sid = SID} = Session) ->
|
||||
SIDKey = sid_to_key(SID),
|
||||
ServKey = server_to_key(element(2, Session#session.us)),
|
||||
USSIDKey = us_sid_to_key(Session#session.us, SID),
|
||||
NodeHostKey = node_host_to_key(node(), element(2, Session#session.us)),
|
||||
case ejabberd_redis:multi(
|
||||
fun() ->
|
||||
ejabberd_redis:hdel(USKey, [SIDKey]),
|
||||
ejabberd_redis:hdel(ServKey, [USSIDKey]),
|
||||
ejabberd_redis:hdel(NodeHostKey,
|
||||
[<<USKey/binary, "||", SIDKey/binary>>]),
|
||||
ejabberd_redis:publish(
|
||||
?SM_KEY,
|
||||
term_to_binary({delete, Session#session.us}))
|
||||
@@ -136,8 +143,10 @@ get_sessions(LUser, LServer) ->
|
||||
%%%===================================================================
|
||||
init([]) ->
|
||||
ejabberd_redis:subscribe([?SM_KEY]),
|
||||
clean_table(),
|
||||
{ok, #state{}}.
|
||||
case clean_table() of
|
||||
ok -> {ok, #state{}};
|
||||
{error, Why} -> {stop, Why}
|
||||
end.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
@@ -168,10 +177,10 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
us_to_key({LUser, LServer}) ->
|
||||
<<"ejabberd:sm:", LUser/binary, "@", LServer/binary>>.
|
||||
<<(?SM_KEY)/binary, ":", LUser/binary, "@", LServer/binary>>.
|
||||
|
||||
server_to_key(LServer) ->
|
||||
<<"ejabberd:sm:", LServer/binary>>.
|
||||
<<(?SM_KEY)/binary, ":", LServer/binary>>.
|
||||
|
||||
us_sid_to_key(US, SID) ->
|
||||
term_to_binary({US, SID}).
|
||||
@@ -179,33 +188,75 @@ us_sid_to_key(US, SID) ->
|
||||
sid_to_key(SID) ->
|
||||
term_to_binary(SID).
|
||||
|
||||
node_session_deletion_cursor(Node, Host) ->
|
||||
NodeName = node_host_to_key(Node, Host),
|
||||
<<NodeName/binary, ":deletioncursor">>.
|
||||
|
||||
node_host_to_key(Node, Host) when is_atom(Node) ->
|
||||
NodeBin = atom_to_binary(node(), utf8),
|
||||
node_host_to_key(NodeBin, Host);
|
||||
node_host_to_key(NodeBin, Host) ->
|
||||
HostKey = server_to_key(Host),
|
||||
<<HostKey/binary, ":node:", NodeBin/binary>>.
|
||||
|
||||
decode_session_list(Vals) ->
|
||||
[binary_to_term(Val) || {_, Val} <- Vals].
|
||||
|
||||
clean_table() ->
|
||||
?DEBUG("Cleaning Redis SM table...", []),
|
||||
clean_table(node()).
|
||||
|
||||
clean_table(Node) when is_atom(Node) ->
|
||||
clean_table(atom_to_binary(Node, utf8));
|
||||
clean_table(Node) ->
|
||||
?DEBUG("Cleaning Redis SM table... ", []),
|
||||
try
|
||||
lists:foreach(
|
||||
fun(LServer) ->
|
||||
ServKey = server_to_key(LServer),
|
||||
{ok, Vals} = ejabberd_redis:hkeys(ServKey),
|
||||
{ok, _} =
|
||||
ejabberd_redis:multi(
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun(USSIDKey) ->
|
||||
{US, SID} = binary_to_term(USSIDKey),
|
||||
if node(element(2, SID)) == node() ->
|
||||
USKey = us_to_key(US),
|
||||
SIDKey = sid_to_key(SID),
|
||||
ejabberd_redis:hdel(ServKey, [USSIDKey]),
|
||||
ejabberd_redis:hdel(USKey, [SIDKey]);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Vals)
|
||||
end)
|
||||
fun(Host) ->
|
||||
ok = clean_node_sessions(Node, Host)
|
||||
end, ejabberd_sm:get_vh_by_backend(?MODULE))
|
||||
catch _:{badmatch, {error, _}} ->
|
||||
?ERROR_MSG("failed to clean redis c2s sessions", [])
|
||||
catch _:{badmatch, {error, _} = Err} ->
|
||||
?ERROR_MSG("Failed to clean Redis SM table", []),
|
||||
Err
|
||||
end.
|
||||
|
||||
clean_node_sessions(Node, Host) ->
|
||||
case load_script() of
|
||||
{ok, SHA} ->
|
||||
clean_node_sessions(Node, Host, SHA);
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
clean_node_sessions(Node, Host, SHA) ->
|
||||
Keys = [node_host_to_key(Node, Host),
|
||||
server_to_key(Host),
|
||||
node_session_deletion_cursor(Node, Host)],
|
||||
case ejabberd_redis:evalsha(SHA, Keys, [1000]) of
|
||||
{ok, <<"0">>} ->
|
||||
ok;
|
||||
{ok, _Cursor} ->
|
||||
clean_node_sessions(Node, Host, SHA);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
load_script() ->
|
||||
case misc:read_lua("redis_sm.lua") of
|
||||
{ok, Data} ->
|
||||
case ejabberd_redis:info(server) of
|
||||
{ok, Info} ->
|
||||
case proplists:get_value(redis_version, Info) of
|
||||
V when V >= ?MIN_REDIS_VERSION ->
|
||||
ejabberd_redis:script_load(Data);
|
||||
V ->
|
||||
?CRITICAL_MSG("Unsupported Redis version: ~s. "
|
||||
"The version must be ~s or above",
|
||||
[V, ?MIN_REDIS_VERSION]),
|
||||
{error, unsupported_redis_version}
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
@@ -55,7 +55,7 @@ init() ->
|
||||
ok;
|
||||
Err ->
|
||||
?ERROR_MSG("failed to clean 'sm' table: ~p", [Err]),
|
||||
Err
|
||||
{error, db_failure}
|
||||
end;
|
||||
(_, Err) ->
|
||||
Err
|
||||
|
||||
+84
-52
@@ -37,12 +37,12 @@
|
||||
sql_query_t/1,
|
||||
sql_transaction/2,
|
||||
sql_bloc/2,
|
||||
abort/1,
|
||||
restart/1,
|
||||
use_new_schema/0,
|
||||
sql_query_to_iolist/1,
|
||||
abort/1,
|
||||
restart/1,
|
||||
use_new_schema/0,
|
||||
sql_query_to_iolist/1,
|
||||
escape/1,
|
||||
standard_escape/1,
|
||||
standard_escape/1,
|
||||
escape_like/1,
|
||||
escape_like_arg/1,
|
||||
escape_like_arg_circumflex/1,
|
||||
@@ -55,7 +55,8 @@
|
||||
freetds_config/0,
|
||||
odbcinst_config/0,
|
||||
init_mssql/1,
|
||||
keep_alive/2]).
|
||||
keep_alive/2,
|
||||
to_list/2]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, handle_event/3, handle_sync_event/4,
|
||||
@@ -136,7 +137,7 @@ start_link(Host, StartInterval) ->
|
||||
-spec sql_query(binary(), sql_query()) -> sql_query_result().
|
||||
|
||||
sql_query(Host, Query) ->
|
||||
check_error(sql_call(Host, {sql_query, Query}), Query).
|
||||
sql_call(Host, {sql_query, Query}).
|
||||
|
||||
%% SQL transaction based on a list of queries
|
||||
%% This function automatically
|
||||
@@ -172,10 +173,16 @@ sql_call(Host, Msg) ->
|
||||
end.
|
||||
|
||||
keep_alive(Host, PID) ->
|
||||
sync_send_event(PID,
|
||||
case sync_send_event(PID,
|
||||
{sql_cmd, {sql_query, ?KEEPALIVE_QUERY},
|
||||
p1_time_compat:monotonic_time(milli_seconds)},
|
||||
query_timeout(Host)).
|
||||
query_timeout(Host)) of
|
||||
{selected,_,[[<<"1">>]]} ->
|
||||
ok;
|
||||
_Err ->
|
||||
?ERROR_MSG("keep alive query failed, closing connection: ~p", [_Err]),
|
||||
sync_send_event(PID, force_timeout, query_timeout(Host))
|
||||
end.
|
||||
|
||||
sync_send_event(Pid, Msg, Timeout) ->
|
||||
try p1_fsm:sync_send_event(Pid, Msg, Timeout)
|
||||
@@ -252,6 +259,10 @@ to_bool(true) -> true;
|
||||
to_bool(1) -> true;
|
||||
to_bool(_) -> false.
|
||||
|
||||
to_list(EscapeFun, Val) ->
|
||||
Escaped = lists:join(<<",">>, lists:map(EscapeFun, Val)),
|
||||
[<<"(">>, Escaped, <<")">>].
|
||||
|
||||
encode_term(Term) ->
|
||||
escape(list_to_binary(
|
||||
erl_prettypr:format(erl_syntax:abstract(Term),
|
||||
@@ -335,10 +346,10 @@ connecting(connect, #state{host = Host} = State) ->
|
||||
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",
|
||||
[State#state.db_type, Reason,
|
||||
State#state.start_interval div 1000]),
|
||||
?WARNING_MSG("~p connection failed:~n** Reason: ~p~n** "
|
||||
"Retry after: ~p seconds",
|
||||
[State#state.db_type, Reason,
|
||||
State#state.start_interval div 1000]),
|
||||
p1_fsm:send_event_after(State#state.start_interval,
|
||||
connect),
|
||||
{next_state, connecting, State}
|
||||
@@ -389,6 +400,8 @@ session_established(Request, {Who, _Ref}, State) ->
|
||||
session_established({sql_cmd, Command, From, Timestamp},
|
||||
State) ->
|
||||
run_sql_cmd(Command, From, State, Timestamp);
|
||||
session_established(force_timeout, State) ->
|
||||
{stop, timeout, State};
|
||||
session_established(Event, State) ->
|
||||
?WARNING_MSG("unexpected event in 'session_established': ~p",
|
||||
[Event]),
|
||||
@@ -580,18 +593,19 @@ sql_query_internal(#sql_query{} = Query) ->
|
||||
sqlite ->
|
||||
sqlite_sql_query(Query)
|
||||
end
|
||||
catch
|
||||
Class:Reason ->
|
||||
catch exit:{timeout, _} ->
|
||||
{error, <<"timed out">>};
|
||||
exit:{killed, _} ->
|
||||
{error, <<"killed">>};
|
||||
exit:{normal, _} ->
|
||||
{error, <<"terminated unexpectedly">>};
|
||||
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;
|
||||
check_error(Res, Query);
|
||||
sql_query_internal(F) when is_function(F) ->
|
||||
case catch execute_fun(F) of
|
||||
{'EXIT', Reason} -> {error, Reason};
|
||||
@@ -616,17 +630,12 @@ sql_query_internal(Query) ->
|
||||
[Query], self(),
|
||||
[{timeout, QueryTimeout - 1000},
|
||||
{result_type, binary}])),
|
||||
%% ?INFO_MSG("MySQL, Received result~n~p~n", [R]),
|
||||
R;
|
||||
sqlite ->
|
||||
Host = State#state.host,
|
||||
sqlite_to_odbc(Host, sqlite3:sql_exec(sqlite_db(Host), Query))
|
||||
end,
|
||||
case Res of
|
||||
{error, <<"No SQL-driver information available.">>} ->
|
||||
{updated, 0};
|
||||
_Else -> Res
|
||||
end.
|
||||
check_error(Res, Query).
|
||||
|
||||
select_sql_query(Queries, State) ->
|
||||
select_sql_query(
|
||||
@@ -745,14 +754,23 @@ sql_query_to_iolist(SQLQuery) ->
|
||||
generic_sql_query_format(SQLQuery).
|
||||
|
||||
%% Generate the OTP callback return tuple depending on the driver result.
|
||||
abort_on_driver_error({error, <<"query timed out">>} =
|
||||
Reply,
|
||||
abort_on_driver_error({error,
|
||||
<<"query timed out">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, timeout, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"Failed sending data on socket", _/binary>>} =
|
||||
Reply,
|
||||
<<"Failed sending data on socket", _/binary>>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, closed, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"SQL connection failed">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, timeout, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"Communication link failure">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, closed, get(?STATE_KEY)};
|
||||
@@ -768,6 +786,7 @@ odbc_connect(SQLServer, Timeout) ->
|
||||
ejabberd:start_app(odbc),
|
||||
odbc:connect(binary_to_list(SQLServer),
|
||||
[{scrollable_cursors, off},
|
||||
{extended_errors, on},
|
||||
{tuple_row, off},
|
||||
{timeout, Timeout},
|
||||
{binary_strings, on}]).
|
||||
@@ -1029,6 +1048,7 @@ init_mssql(Host) ->
|
||||
FreeTDS = io_lib:fwrite("[~s]~n"
|
||||
"\thost = ~s~n"
|
||||
"\tport = ~p~n"
|
||||
"\tclient charset = UTF-8~n"
|
||||
"\ttds version = 7.1~n",
|
||||
[Host, Server, Port]),
|
||||
ODBCINST = io_lib:fwrite("[freetds]~n"
|
||||
@@ -1093,37 +1113,49 @@ query_timeout(LServer) ->
|
||||
timer:seconds(
|
||||
ejabberd_config:get_option({sql_query_timeout, LServer}, 60)).
|
||||
|
||||
%% ***IMPORTANT*** This error format requires extended_errors turned on.
|
||||
extended_error({"08S01", _, Reason}) ->
|
||||
% TCP Provider: The specified network name is no longer available
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"08001", _, Reason}) ->
|
||||
% Login timeout expired
|
||||
?DEBUG("ODBC Connect Timeout: ~s", [Reason]),
|
||||
<<"SQL connection failed">>;
|
||||
extended_error({"IMC01", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"IMC06", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({Code, _, Reason}) ->
|
||||
?DEBUG("ODBC Error ~s: ~s", [Code, Reason]),
|
||||
iolist_to_binary(Reason);
|
||||
extended_error(Error) ->
|
||||
Error.
|
||||
|
||||
check_error({error, Why} = Err, _Query) when Why == killed ->
|
||||
Err;
|
||||
check_error({error, Why} = Err, #sql_query{} = Query) ->
|
||||
check_error({error, Why}, #sql_query{} = Query) ->
|
||||
Err = extended_error(Why),
|
||||
?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) ->
|
||||
[Query#sql_query.hash, Query#sql_query.loc, Err]),
|
||||
{error, Err};
|
||||
check_error({error, Why}, Query) ->
|
||||
Err = extended_error(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 '~s' failed: ~p", [SQuery, Err]);
|
||||
_ ->
|
||||
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Why])
|
||||
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Err])
|
||||
end,
|
||||
Err;
|
||||
{error, Err};
|
||||
check_error(Result, _Query) ->
|
||||
Result.
|
||||
|
||||
-spec opt_type(sql_database) -> fun((binary()) -> binary());
|
||||
(sql_keepalive_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_password) -> fun((binary()) -> binary());
|
||||
(sql_port) -> fun((0..65535) -> 0..65535);
|
||||
(sql_server) -> fun((binary()) -> binary());
|
||||
(sql_username) -> fun((binary()) -> binary());
|
||||
(sql_ssl) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_verify) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_certfile) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_cafile) -> fun((boolean()) -> boolean());
|
||||
(sql_query_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_connect_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_database) -> fun iolist_to_binary/1;
|
||||
opt_type(sql_keepalive_interval) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
@@ -1135,7 +1167,7 @@ opt_type(sql_username) -> fun iolist_to_binary/1;
|
||||
opt_type(sql_ssl) -> fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(sql_ssl_verify) -> fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(sql_ssl_certfile) -> fun ejabberd_pkix:try_certfile/1;
|
||||
opt_type(sql_ssl_cafile) -> fun misc:try_read_file/1;
|
||||
opt_type(sql_ssl_cafile) -> fun ejabberd_pkix:try_certfile/1;
|
||||
opt_type(sql_query_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(sql_connect_timeout) ->
|
||||
|
||||
@@ -306,6 +306,20 @@ parse1([$%, $( | S], Acc, State) ->
|
||||
false ->
|
||||
append_string("0=0", State3)
|
||||
end;
|
||||
{list, InternalType} ->
|
||||
Convert = erl_syntax:application(
|
||||
erl_syntax:atom(ejabberd_sql),
|
||||
erl_syntax:atom(to_list),
|
||||
[erl_syntax:record_access(
|
||||
erl_syntax:variable(?ESCAPE_VAR),
|
||||
erl_syntax:atom(?ESCAPE_RECORD),
|
||||
erl_syntax:atom(InternalType)),
|
||||
erl_syntax:variable(Name)]),
|
||||
State2#state{'query' = [{var, Var} | State2#state.'query'],
|
||||
args = [Convert | State2#state.args],
|
||||
params = [Var | State2#state.params],
|
||||
param_pos = State2#state.param_pos + 1,
|
||||
used_vars = [Name | State2#state.used_vars]};
|
||||
_ ->
|
||||
Convert =
|
||||
erl_syntax:application(
|
||||
@@ -335,6 +349,19 @@ parse_name(S, IsArg, State) ->
|
||||
parse_name([], _Acc, _Depth, _IsArg, State) ->
|
||||
throw({error, State#state.loc,
|
||||
"expected ')', found end of string"});
|
||||
parse_name([$), $l, T | S], Acc, 0, true, State) ->
|
||||
Type = case T of
|
||||
$d -> {list, integer};
|
||||
$s -> {list, string};
|
||||
$b -> {list, boolean};
|
||||
_ ->
|
||||
throw({error, State#state.loc,
|
||||
["unknown type specifier 'l", T, "'"]})
|
||||
end,
|
||||
{lists:reverse(Acc), Type, S, State};
|
||||
parse_name([$), $l, T | _], _Acc, 0, false, State) ->
|
||||
throw({error, State#state.loc,
|
||||
["list type 'l", T, "' is not allowed for outputs"]});
|
||||
parse_name([$), T | S], Acc, 0, IsArg, State) ->
|
||||
Type =
|
||||
case T of
|
||||
|
||||
+46
-25
@@ -31,21 +31,19 @@
|
||||
|
||||
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
||||
get_pids/1, get_random_pid/1, transform_options/1,
|
||||
opt_type/1]).
|
||||
reload/1, opt_type/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
-define(PGSQL_PORT, 5432).
|
||||
|
||||
-define(MYSQL_PORT, 3306).
|
||||
|
||||
-define(DEFAULT_POOL_SIZE, 10).
|
||||
|
||||
-define(DEFAULT_SQL_START_INTERVAL, 30).
|
||||
|
||||
-define(CONNECT_TIMEOUT, 500).
|
||||
|
||||
-record(sql_pool, {host, pid}).
|
||||
-record(sql_pool, {host :: binary(),
|
||||
pid :: pid()}).
|
||||
|
||||
start_link(Host) ->
|
||||
ejabberd_mnesia:create(?MODULE, sql_pool,
|
||||
@@ -59,9 +57,6 @@ start_link(Host) ->
|
||||
?MODULE, [Host]).
|
||||
|
||||
init([Host]) ->
|
||||
StartInterval = ejabberd_config:get_option(
|
||||
{sql_start_interval, Host},
|
||||
?DEFAULT_SQL_START_INTERVAL),
|
||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
||||
PoolSize = get_pool_size(Type, Host),
|
||||
case Type of
|
||||
@@ -72,16 +67,37 @@ init([Host]) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{ok, {{one_for_one, PoolSize * 10, 1},
|
||||
[child_spec(I, Host) || I <- lists:seq(1, PoolSize)]}}.
|
||||
|
||||
{ok,
|
||||
{{one_for_one, PoolSize * 10, 1},
|
||||
lists:map(fun (I) ->
|
||||
{I,
|
||||
{ejabberd_sql, start_link,
|
||||
[Host, StartInterval * 1000]},
|
||||
transient, 2000, worker, [?MODULE]}
|
||||
end,
|
||||
lists:seq(1, PoolSize))}}.
|
||||
reload(Host) ->
|
||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
||||
NewPoolSize = get_pool_size(Type, Host),
|
||||
OldPoolSize = ets:select_count(
|
||||
sql_pool,
|
||||
ets:fun2ms(
|
||||
fun(#sql_pool{host = H}) when H == Host ->
|
||||
true
|
||||
end)),
|
||||
reload(Host, NewPoolSize, OldPoolSize).
|
||||
|
||||
reload(Host, NewPoolSize, OldPoolSize) ->
|
||||
Sup = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
if NewPoolSize == OldPoolSize ->
|
||||
ok;
|
||||
NewPoolSize > OldPoolSize ->
|
||||
lists:foreach(
|
||||
fun(I) ->
|
||||
Spec = child_spec(I, Host),
|
||||
supervisor:start_child(Sup, Spec)
|
||||
end, lists:seq(OldPoolSize+1, NewPoolSize));
|
||||
OldPoolSize > NewPoolSize ->
|
||||
lists:foreach(
|
||||
fun(I) ->
|
||||
supervisor:terminate_child(Sup, I),
|
||||
supervisor:delete_child(Sup, I)
|
||||
end, lists:seq(NewPoolSize+1, OldPoolSize))
|
||||
end.
|
||||
|
||||
get_pids(Host) ->
|
||||
Rs = mnesia:dirty_read(sql_pool, Host),
|
||||
@@ -91,7 +107,7 @@ get_random_pid(Host) ->
|
||||
case get_pids(Host) of
|
||||
[] -> none;
|
||||
Pids ->
|
||||
I = randoms:round_robin(length(Pids)) + 1,
|
||||
I = p1_rand:round_robin(length(Pids)) + 1,
|
||||
lists:nth(I, Pids)
|
||||
end.
|
||||
|
||||
@@ -123,6 +139,13 @@ get_pool_size(SQLType, Host) ->
|
||||
end,
|
||||
PoolSize.
|
||||
|
||||
child_spec(I, Host) ->
|
||||
StartInterval = ejabberd_config:get_option(
|
||||
{sql_start_interval, Host},
|
||||
?DEFAULT_SQL_START_INTERVAL),
|
||||
{I, {ejabberd_sql, start_link, [Host, timer:seconds(StartInterval)]},
|
||||
transient, 2000, worker, [?MODULE]}.
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
||||
@@ -167,7 +190,7 @@ check_sqlite_db(Host) ->
|
||||
ok
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("Failed open sqlite database, reason ~p", [Reason])
|
||||
?WARNING_MSG("Failed open sqlite database, reason ~p", [Reason])
|
||||
end.
|
||||
|
||||
create_sqlite_tables(DB) ->
|
||||
@@ -180,8 +203,8 @@ create_sqlite_tables(DB) ->
|
||||
[ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
|
||||
ok = sqlite3:sql_exec(DB, "commit");
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("Failed to read SQLite schema file: ~s",
|
||||
[file:format_error(Reason)])
|
||||
?WARNING_MSG("Failed to read SQLite schema file: ~s",
|
||||
[file:format_error(Reason)])
|
||||
end.
|
||||
|
||||
read_lines(Fd, File, Acc) ->
|
||||
@@ -212,9 +235,7 @@ read_lines(Fd, File, Acc) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec opt_type(sql_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_start_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_pool_size) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(sql_start_interval) ->
|
||||
|
||||
+38
-38
@@ -24,27 +24,29 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_stun).
|
||||
|
||||
-behaviour(ejabberd_listener).
|
||||
-protocol({rfc, 5766}).
|
||||
-protocol({xep, 176, '1.0'}).
|
||||
|
||||
-ifndef(STUN).
|
||||
-include("logger.hrl").
|
||||
-export([socket_type/0, start/2, listen_opt_type/1]).
|
||||
log_error() ->
|
||||
?CRITICAL_MSG("ejabberd is not compiled with STUN/TURN support", []).
|
||||
socket_type() ->
|
||||
log_error(),
|
||||
raw.
|
||||
listen_opt_type(_) ->
|
||||
log_error(),
|
||||
[].
|
||||
-export([accept/1, start/2, start_link/2, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
"ejabberd is not compiled with STUN/TURN support",
|
||||
[?MODULE]),
|
||||
erlang:error(stun_not_compiled).
|
||||
accept(_) ->
|
||||
fail().
|
||||
listen_options() ->
|
||||
fail().
|
||||
start(_, _) ->
|
||||
log_error(),
|
||||
{error, sip_not_compiled}.
|
||||
fail().
|
||||
start_link(_, _) ->
|
||||
fail().
|
||||
-else.
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
|
||||
socket_type/0, listen_opt_type/1]).
|
||||
start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -65,8 +67,11 @@ udp_recv(Socket, Addr, Port, Packet, Opts) ->
|
||||
start(Opaque, Opts) ->
|
||||
stun:start(Opaque, Opts).
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
start_link({gen_tcp, Sock}, Opts) ->
|
||||
stun:start_link(Sock, Opts).
|
||||
|
||||
accept(_Pid) ->
|
||||
ok.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
@@ -107,7 +112,7 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
MaxRate = shaper:get_max_rate(Shaper),
|
||||
MaxRate = ejabberd_shaper:get_max_rate(Shaper),
|
||||
Opts1 = Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} |
|
||||
lists:keydelete(shaper, 1, Opts)],
|
||||
set_certfile(Opts1).
|
||||
@@ -138,27 +143,16 @@ listen_opt_type(turn_ip) ->
|
||||
{ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)),
|
||||
Addr
|
||||
end;
|
||||
listen_opt_type(shaper) ->
|
||||
fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(auth_type) ->
|
||||
fun(anonymous) -> anonymous;
|
||||
(user) -> user
|
||||
end;
|
||||
listen_opt_type(auth_realm) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
%% We cannot deprecate the option for now:
|
||||
%% I think STUN/TURN clients are too stupid to set SNI
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(turn_min_port) ->
|
||||
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
|
||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
||||
listen_opt_type(turn_max_port) ->
|
||||
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
|
||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
||||
listen_opt_type(turn_max_allocations) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
@@ -170,13 +164,19 @@ listen_opt_type(turn_max_permissions) ->
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(server_name) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
|
||||
turn_max_port, turn_max_allocations, turn_max_permissions,
|
||||
server_name, backlog, accept_interval].
|
||||
fun iolist_to_binary/1.
|
||||
|
||||
listen_options() ->
|
||||
[{shaper, none},
|
||||
{use_turn, false},
|
||||
{turn_ip, undefined},
|
||||
{auth_type, user},
|
||||
{auth_realm, undefined},
|
||||
{tls, false},
|
||||
{certfile, undefined},
|
||||
{turn_min_port, 49152},
|
||||
{turn_max_port, 65535},
|
||||
{turn_max_allocations, 10},
|
||||
{turn_max_permissions, 10},
|
||||
{server_name, <<"ejabberd">>}].
|
||||
-endif.
|
||||
|
||||
@@ -39,21 +39,20 @@ init([]) ->
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[worker(ejabberd_hooks),
|
||||
worker(ejabberd_cluster),
|
||||
worker(cyrsasl),
|
||||
worker(translate),
|
||||
worker(ejabberd_access_permissions),
|
||||
worker(ejabberd_ctl),
|
||||
worker(ejabberd_commands),
|
||||
worker(ejabberd_admin),
|
||||
supervisor(ejabberd_listener),
|
||||
worker(ejabberd_pkix),
|
||||
worker(ejabberd_acme),
|
||||
supervisor(ejabberd_listener),
|
||||
worker(ejabberd_s2s),
|
||||
simple_supervisor(ejabberd_s2s_in),
|
||||
simple_supervisor(ejabberd_s2s_out),
|
||||
simple_supervisor(ejabberd_service),
|
||||
worker(acl),
|
||||
worker(shaper),
|
||||
worker(ejabberd_shaper),
|
||||
supervisor(ejabberd_backend_sup),
|
||||
supervisor(ejabberd_rdbms),
|
||||
supervisor(ejabberd_riak_sup),
|
||||
|
||||
@@ -88,7 +88,7 @@ handle_event({set_alarm, {system_memory_high_watermark, _}}, State) ->
|
||||
handle_overload(State),
|
||||
{ok, restart_timer(State)};
|
||||
handle_event({clear_alarm, system_memory_high_watermark}, State) ->
|
||||
cancel_timer(State#state.tref),
|
||||
misc:cancel_timer(State#state.tref),
|
||||
{ok, State#state{tref = undefined}};
|
||||
handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
|
||||
case proc_stat(Pid, get_app_pids()) of
|
||||
@@ -220,23 +220,10 @@ proc_stat(Pid, AppPids) ->
|
||||
|
||||
-spec restart_timer(#state{}) -> #state{}.
|
||||
restart_timer(State) ->
|
||||
cancel_timer(State#state.tref),
|
||||
misc:cancel_timer(State#state.tref),
|
||||
TRef = erlang:start_timer(?CHECK_INTERVAL, self(), handle_overload),
|
||||
State#state{tref = TRef}.
|
||||
|
||||
-spec cancel_timer(reference()) -> ok.
|
||||
cancel_timer(undefined) ->
|
||||
ok;
|
||||
cancel_timer(TRef) ->
|
||||
case erlang:cancel_timer(TRef) of
|
||||
false ->
|
||||
receive {timeout, TRef, _} -> ok
|
||||
after 0 -> ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec format_apps(dict:dict()) -> io:data().
|
||||
format_apps(Apps) ->
|
||||
AppList = lists:reverse(lists:keysort(2, dict:to_list(Apps))),
|
||||
|
||||
@@ -166,7 +166,7 @@ process([<<"doc">>, LocalFile], _Request) ->
|
||||
"documentation with the environment variable "
|
||||
"EJABBERD_DOC_PATH. Check the ejabberd "
|
||||
"Guide for more information.">>,
|
||||
?INFO_MSG("Problem '~p' accessing the local Guide file ~s", [Error, Help]),
|
||||
?WARNING_MSG("Problem '~p' accessing the local Guide file ~s", [Error, Help]),
|
||||
case Error of
|
||||
eacces -> {403, [], <<"Forbidden", Help/binary>>};
|
||||
enoent -> {307, [{<<"Location">>, <<"http://docs.ejabberd.im/admin/guide/configuration/">>}], <<"Not found", Help/binary>>};
|
||||
@@ -1873,7 +1873,7 @@ get_node(Host, Node, [<<"modules">>], Query, Lang)
|
||||
Modules, Query)
|
||||
of
|
||||
submitted -> ok;
|
||||
{'EXIT', Reason} -> ?INFO_MSG("~p~n", [Reason]), error;
|
||||
{'EXIT', Reason} -> ?ERROR_MSG("~p~n", [Reason]), error;
|
||||
_ -> nothing
|
||||
end,
|
||||
NewModules = lists:sort(ejabberd_cluster:call(Node, gen_mod,
|
||||
@@ -2242,8 +2242,9 @@ make_netprot_html(NetProt) ->
|
||||
[<<"tcp">>, <<"udp">>]))).
|
||||
|
||||
get_port_data(PortIP, Opts) ->
|
||||
{Port, IPT, IPS, _IPV, NetProt, OptsClean} =
|
||||
{Port, IPT, _IPV, NetProt, OptsClean} =
|
||||
ejabberd_listener:parse_listener_portip(PortIP, Opts),
|
||||
IPS = misc:ip_to_list(IPT),
|
||||
SPort = integer_to_binary(Port),
|
||||
SSPort = list_to_binary(
|
||||
lists:map(fun (N) ->
|
||||
@@ -2694,8 +2695,7 @@ make_menu_item(item, 3, URI, Name, Lang) ->
|
||||
%%%==================================
|
||||
|
||||
|
||||
-spec opt_type(access_readonly) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(access_readonly) -> fun acl:access_rules_validator/1;
|
||||
opt_type(_) -> [access_readonly].
|
||||
|
||||
|
||||
+12
-17
@@ -31,11 +31,12 @@
|
||||
%%% TODO: commands strings should be strings without ~n
|
||||
|
||||
-module(ejabberd_xmlrpc).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-author('badlop@process-one.net').
|
||||
|
||||
-export([start/2, handler/2, process/2, socket_type/0,
|
||||
transform_listen_option/2, listen_opt_type/1]).
|
||||
-export([start/2, start_link/2, handler/2, process/2, accept/1,
|
||||
transform_listen_option/2, listen_opt_type/1, listen_options/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
@@ -190,7 +191,11 @@
|
||||
start({gen_tcp = _SockMod, Socket}, Opts) ->
|
||||
ejabberd_http:start({gen_tcp, Socket}, [{xmlrpc, true}|Opts]).
|
||||
|
||||
socket_type() -> raw.
|
||||
start_link({gen_tcp = _SockMod, Socket}, Opts) ->
|
||||
ejabberd_http:start_link({gen_tcp, Socket}, [{xmlrpc, true}|Opts]).
|
||||
|
||||
accept(Pid) ->
|
||||
ejabberd_http:accept(Pid).
|
||||
|
||||
%% -----------------------------
|
||||
%% HTTP interface
|
||||
@@ -575,17 +580,7 @@ listen_opt_type(access_commands) ->
|
||||
{<<"ejabberd_xmlrpc compatibility shim">>,
|
||||
{[?MODULE], [{access, Ac}], Commands}}
|
||||
end, lists:flatten(Opts))
|
||||
end;
|
||||
listen_opt_type(maxsessions) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(timeout) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(accept_interval) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[access_commands, maxsessions, timeout, backlog, inet, inet6,
|
||||
accept_interval].
|
||||
end.
|
||||
|
||||
listen_options() ->
|
||||
[{access_commands, []}].
|
||||
|
||||
@@ -216,6 +216,10 @@ prepare_output(FileName, normal) when is_list(FileName) ->
|
||||
case file:open(FileName, [write, raw]) of
|
||||
{ok, Fd} ->
|
||||
Fd;
|
||||
{error, eacces} ->
|
||||
exit({"Not enough permission to the file or path", FileName});
|
||||
{error, enoent} ->
|
||||
exit({"Path does not exist", FileName});
|
||||
Err ->
|
||||
exit(Err)
|
||||
end;
|
||||
|
||||
+11
-16
@@ -659,7 +659,7 @@ handle_info({Tag, _Socket, Data}, connecting, S)
|
||||
{next_state, connecting, S};
|
||||
handle_info({Tag, _Socket, Data}, wait_bind_response, S)
|
||||
when Tag == tcp; Tag == ssl ->
|
||||
cancel_timer(S#eldap.bind_timer),
|
||||
misc:cancel_timer(S#eldap.bind_timer),
|
||||
case catch recvd_wait_bind_response(Data, S) of
|
||||
bound -> dequeue_commands(S);
|
||||
{fail_bind, Reason} ->
|
||||
@@ -847,14 +847,14 @@ recvd_packet(Pkt, S) ->
|
||||
if Reason == success; Reason == sizeLimitExceeded ->
|
||||
{Res, Ref} = polish(Result_so_far),
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
{reply,
|
||||
#eldap_search_result{entries = Res,
|
||||
referrals = Ref},
|
||||
From, S#eldap{dict = New_dict}};
|
||||
true ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
{reply, {error, Reason}, From,
|
||||
S#eldap{dict = New_dict}}
|
||||
end;
|
||||
@@ -863,37 +863,37 @@ recvd_packet(Pkt, S) ->
|
||||
{ok, S#eldap{dict = New_dict}};
|
||||
{addRequest, {addResponse, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{delRequest, {delResponse, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{modifyRequest, {modifyResponse, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{modDNRequest, {modDNResponse, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{bindRequest, {bindResponse, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_bind_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{extendedReq, {extendedResp, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
Reply = check_extended_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{OtherName, OtherResult} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
{reply,
|
||||
{error, {invalid_result, OtherName, OtherResult}},
|
||||
From, S#eldap{dict = New_dict}}
|
||||
@@ -968,16 +968,11 @@ check_id(_, _) -> throw({error, wrong_bind_id}).
|
||||
%% General Helpers
|
||||
%%-----------------------------------------------------------------------
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive {timeout, Timer, _} -> ok after 0 -> ok end.
|
||||
|
||||
|
||||
close_and_retry(S, Timeout) ->
|
||||
catch (S#eldap.sockmod):close(S#eldap.fd),
|
||||
Queue = dict:fold(fun (_Id,
|
||||
[{Timer, Command, From, _Name} | _], Q) ->
|
||||
cancel_timer(Timer),
|
||||
misc:cancel_timer(Timer),
|
||||
queue:in_r({Command, From}, Q);
|
||||
(_, _, Q) -> Q
|
||||
end,
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ start_link(Name, Hosts, Backups, Port, Rootdn, Passwd,
|
||||
of
|
||||
{ok, Pid} -> pg2:join(PoolName, Pid);
|
||||
Err ->
|
||||
?INFO_MSG("Err = ~p", [Err]),
|
||||
?ERROR_MSG("Err = ~p", [Err]),
|
||||
error
|
||||
end
|
||||
end,
|
||||
|
||||
+1
-18
@@ -333,24 +333,7 @@ collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
|
||||
collect_parts_bit([],Acc,Uacc) ->
|
||||
list_to_binary([Uacc|lists:reverse(Acc)]).
|
||||
|
||||
-type deref_aliases() :: never | searching | finding | always.
|
||||
-type uids() :: binary() | {binary()} | {binary(), binary()}.
|
||||
-spec opt_type(deref_aliases) -> fun((deref_aliases()) -> deref_aliases());
|
||||
(ldap_backups) -> fun(([binary()]) -> [binary()]);
|
||||
(ldap_base) -> fun((binary()) -> binary());
|
||||
(ldap_deref_aliases) -> fun((deref_aliases()) -> deref_aliases());
|
||||
(ldap_encrypt) -> fun((tls | starttls | none) -> tls | starttls | none);
|
||||
(ldap_password) -> fun((binary()) -> binary());
|
||||
(ldap_port) -> fun((0..65535) -> 0..65535);
|
||||
(ldap_rootdn) -> fun((binary()) -> binary());
|
||||
(ldap_servers) -> fun(([binary()]) -> [binary()]);
|
||||
(ldap_tls_certfile) -> fun((binary()) -> string());
|
||||
(ldap_tls_cacertfile) -> fun((binary()) -> string());
|
||||
(ldap_tls_depth) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(ldap_tls_verify) -> fun((hard | soft | false) -> hard | soft | false);
|
||||
(ldap_filter) -> fun((binary()) -> binary());
|
||||
(ldap_uids) -> fun((uids()) -> uids());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(deref_aliases) ->
|
||||
fun(unspecified) -> unspecified;
|
||||
(never) -> never;
|
||||
|
||||
+1
-2
@@ -680,8 +680,7 @@ format({Key, Val}) when is_binary(Val) ->
|
||||
format({Key, Val}) -> % TODO: improve Yaml parsing
|
||||
{Key, Val}.
|
||||
|
||||
-spec opt_type(allow_contrib_modules) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(allow_contrib_modules) ->
|
||||
fun (false) -> false;
|
||||
(no) -> false;
|
||||
|
||||
+7
-2
@@ -82,7 +82,12 @@ prog_name(Host) ->
|
||||
|
||||
-spec pool_name(binary()) -> atom().
|
||||
pool_name(Host) ->
|
||||
list_to_atom("extauth_pool_" ++ binary_to_list(Host)).
|
||||
case ejabberd_config:get_option({extauth_pool_name, Host}) of
|
||||
undefined ->
|
||||
list_to_atom("extauth_pool_" ++ binary_to_list(Host));
|
||||
Name ->
|
||||
list_to_atom("extauth_pool_" ++ binary_to_list(Name))
|
||||
end.
|
||||
|
||||
-spec worker_name(atom(), integer()) -> atom().
|
||||
worker_name(Pool, N) ->
|
||||
@@ -186,7 +191,7 @@ call_port(Server, Args, Timeout) ->
|
||||
StartTime = p1_time_compat:monotonic_time(milli_seconds),
|
||||
Pool = pool_name(Server),
|
||||
PoolSize = pool_size(Server),
|
||||
I = randoms:round_robin(PoolSize),
|
||||
I = p1_rand:round_robin(PoolSize),
|
||||
Cmd = str:join(Args, <<":">>),
|
||||
do_call(Cmd, I, I + PoolSize, Pool, PoolSize,
|
||||
StartTime + Timeout, StartTime).
|
||||
|
||||
@@ -114,8 +114,9 @@ process_iq(_Host, Module, Function, IQ) ->
|
||||
ignore ->
|
||||
ok
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to process iq:~n~s~nReason = ~p",
|
||||
[xmpp:pp(IQ), {E, {R, erlang:get_stacktrace()}}]),
|
||||
[xmpp:pp(IQ), {E, {R, St}}]),
|
||||
Txt = <<"Module failed to handle the query">>,
|
||||
Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang),
|
||||
ejabberd_router:route_error(IQ, Err)
|
||||
@@ -153,8 +154,7 @@ transform_module_options(Opts) ->
|
||||
Opt
|
||||
end, Opts).
|
||||
|
||||
-spec opt_type(iqdisc) -> fun((any()) -> no_queue);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(iqdisc) -> fun check_type/1;
|
||||
opt_type(_) -> [iqdisc].
|
||||
|
||||
|
||||
+16
-11
@@ -67,7 +67,7 @@
|
||||
-type opts() :: [{atom(), any()}].
|
||||
-type db_type() :: atom().
|
||||
|
||||
-callback start(binary(), opts()) -> ok | {ok, pid()}.
|
||||
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
-callback stop(binary()) -> any().
|
||||
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
|
||||
-callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
@@ -546,12 +546,14 @@ validate_opts(Host, Module, Opts0) ->
|
||||
[Module, Opt]),
|
||||
module_error(ErrTxt);
|
||||
_:{invalid_option, Opt, Val} ->
|
||||
ErrTxt = io_lib:format("Invalid value '~p' for option '~s' of "
|
||||
"module '~s'", [Val, Opt, Module]),
|
||||
ErrTxt = io_lib:format("Invalid value for option '~s' of "
|
||||
"module ~s: ~s",
|
||||
[Opt, Module, misc:format_val({yaml, Val})]),
|
||||
module_error(ErrTxt);
|
||||
_:{invalid_option, Opt, Val, Reason} ->
|
||||
ErrTxt = io_lib:format("Invalid value '~p' for option '~s' of "
|
||||
"module '~s': ~s", [Val, Opt, Module, Reason]),
|
||||
ErrTxt = io_lib:format("Invalid value for option '~s' of "
|
||||
"module ~s (~s): ~s",
|
||||
[Opt, Module, Reason, misc:format_val({yaml, Val})]),
|
||||
module_error(ErrTxt);
|
||||
_:{unknown_option, Opt, []} ->
|
||||
ErrTxt = io_lib:format("Unknown option '~s' of module '~s': "
|
||||
@@ -618,7 +620,7 @@ validate_opt(Opt, Val, VFun) ->
|
||||
NewVal -> [{Opt, NewVal}]
|
||||
catch {invalid_syntax, Error} ->
|
||||
err_invalid_option(Opt, Val, Error);
|
||||
_:_ ->
|
||||
_:R when R /= undef ->
|
||||
err_invalid_option(Opt, Val)
|
||||
end.
|
||||
|
||||
@@ -728,10 +730,14 @@ format_module_error(Module, Fun, Arity, Opts, Class, Reason, St) ->
|
||||
"it doesn't export ~s/~B callback: "
|
||||
"is it really an ejabberd module?",
|
||||
[Fun, Module, Fun, Arity]);
|
||||
{error, {bad_return, Module, {error, _} = Err}} ->
|
||||
io_lib:format("Failed to ~s module ~s: ~s",
|
||||
[Fun, Module, misc:format_val(Err)]);
|
||||
{error, {bad_return, Module, Ret}} ->
|
||||
io_lib:format("Module ~s returned unexpected value from "
|
||||
"~s/~B: ~p; this is either not an ejabberd "
|
||||
"module or it implements ejabbed API incorrectly",
|
||||
io_lib:format("Module ~s returned unexpected value from ~s/~B:~n"
|
||||
"** Error: ~p~n"
|
||||
"** Hint: this is either not an ejabberd module "
|
||||
"or it implements ejabbed API incorrectly",
|
||||
[Module, Fun, Arity, Ret]);
|
||||
_ ->
|
||||
io_lib:format("Internal error of module ~s has "
|
||||
@@ -925,8 +931,7 @@ is_opt_list(L) when is_list(L) ->
|
||||
is_opt_list(_) ->
|
||||
false.
|
||||
|
||||
-spec opt_type(modules) -> fun(([{atom(), list()}]) -> [{atom(), list()}]);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(modules) ->
|
||||
fun(Mods) ->
|
||||
lists:map(
|
||||
|
||||
+1
-2
@@ -111,7 +111,6 @@ process_xdb(User, Server,
|
||||
xdb_data(_User, _Server, {xmlcdata, _CData}) -> ok;
|
||||
xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
|
||||
From = jid:make(User, Server),
|
||||
LUser = From#jid.luser,
|
||||
LServer = From#jid.lserver,
|
||||
case fxml:get_attr_s(<<"xmlns">>, Attrs) of
|
||||
?NS_AUTH ->
|
||||
@@ -142,7 +141,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
|
||||
(_) -> true
|
||||
end, Attrs),
|
||||
catch mod_private:set_data(
|
||||
LUser, LServer,
|
||||
From,
|
||||
[{XMLNS, El#xmlel{attrs = NewAttrs}}]);
|
||||
_ ->
|
||||
?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS])
|
||||
|
||||
+149
-4
@@ -28,14 +28,17 @@
|
||||
-module(misc).
|
||||
|
||||
%% API
|
||||
-export([tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
|
||||
-export([add_delay_info/3, add_delay_info/4,
|
||||
unwrap_carbon/1, unwrap_mucsub_message/1, is_standalone_chat_state/1,
|
||||
tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
|
||||
hex_to_bin/1, hex_to_base64/1, url_encode/1, expand_keyword/3,
|
||||
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
|
||||
l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
|
||||
now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
|
||||
compile_exprs/2, join_atoms/2, try_read_file/1, get_descr/2,
|
||||
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0,
|
||||
read_css/1, read_img/1, read_js/1]).
|
||||
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
|
||||
read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1,
|
||||
intersection/2, format_val/1, cancel_timer/1]).
|
||||
|
||||
%% Deprecated functions
|
||||
-export([decode_base64/1, encode_base64/1]).
|
||||
@@ -43,11 +46,81 @@
|
||||
{encode_base64, 1}]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
-spec add_delay_info(stanza(), jid(), erlang:timestamp()) -> stanza().
|
||||
add_delay_info(Stz, From, Time) ->
|
||||
add_delay_info(Stz, From, Time, <<"">>).
|
||||
|
||||
-spec add_delay_info(stanza(), jid(), erlang:timestamp(), binary()) -> stanza().
|
||||
add_delay_info(Stz, From, Time, Desc) ->
|
||||
NewDelay = #delay{stamp = Time, from = From, desc = Desc},
|
||||
case xmpp:get_subtag(Stz, #delay{stamp = {0,0,0}}) of
|
||||
#delay{from = OldFrom} when is_record(OldFrom, jid) ->
|
||||
case jid:tolower(From) == jid:tolower(OldFrom) of
|
||||
true ->
|
||||
Stz;
|
||||
false ->
|
||||
xmpp:append_subtags(Stz, [NewDelay])
|
||||
end;
|
||||
_ ->
|
||||
xmpp:append_subtags(Stz, [NewDelay])
|
||||
end.
|
||||
|
||||
-spec unwrap_carbon(stanza()) -> xmpp_element().
|
||||
unwrap_carbon(#message{} = Msg) ->
|
||||
try
|
||||
case xmpp:get_subtag(Msg, #carbons_sent{forwarded = #forwarded{}}) of
|
||||
#carbons_sent{forwarded = #forwarded{sub_els = [El]}} ->
|
||||
xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
|
||||
_ ->
|
||||
case xmpp:get_subtag(Msg, #carbons_received{
|
||||
forwarded = #forwarded{}}) of
|
||||
#carbons_received{forwarded = #forwarded{sub_els = [El]}} ->
|
||||
xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
|
||||
_ ->
|
||||
Msg
|
||||
end
|
||||
end
|
||||
catch _:{xmpp_codec, _} ->
|
||||
Msg
|
||||
end;
|
||||
unwrap_carbon(Stanza) -> Stanza.
|
||||
|
||||
-spec unwrap_mucsub_message(xmpp_element()) -> message() | false.
|
||||
unwrap_mucsub_message(#message{} = OuterMsg) ->
|
||||
case xmpp:get_subtag(OuterMsg, #ps_event{}) of
|
||||
#ps_event{
|
||||
items = #ps_items{
|
||||
node = Node,
|
||||
items = [
|
||||
#ps_item{
|
||||
sub_els = [#message{} = InnerMsg]} | _]}}
|
||||
when Node == ?NS_MUCSUB_NODES_MESSAGES;
|
||||
Node == ?NS_MUCSUB_NODES_SUBJECT ->
|
||||
InnerMsg;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
unwrap_mucsub_message(_Packet) ->
|
||||
false.
|
||||
|
||||
-spec is_standalone_chat_state(stanza()) -> boolean().
|
||||
is_standalone_chat_state(Stanza) ->
|
||||
case unwrap_carbon(Stanza) of
|
||||
#message{body = [], subject = [], sub_els = Els} ->
|
||||
IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT],
|
||||
Stripped = [El || El <- Els,
|
||||
not lists:member(xmpp:get_ns(El), IgnoreNS)],
|
||||
Stripped == [];
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
-spec tolower(binary()) -> binary().
|
||||
tolower(B) ->
|
||||
iolist_to_binary(tolower_s(binary_to_list(B))).
|
||||
@@ -203,7 +276,7 @@ compile_exprs(Mod, Exprs) ->
|
||||
|
||||
-spec join_atoms([atom()], binary()) -> binary().
|
||||
join_atoms(Atoms, Sep) ->
|
||||
str:join([io_lib:format("~p", [A]) || A <- Atoms], Sep).
|
||||
str:join([io_lib:format("~p", [A]) || A <- lists:sort(Atoms)], Sep).
|
||||
|
||||
%% @doc Checks if the file is readable and converts its name to binary.
|
||||
%% Fails with `badarg` otherwise. The function is intended for usage
|
||||
@@ -219,6 +292,29 @@ try_read_file(Path) ->
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
|
||||
%% @doc Checks if the URL is valid HTTP(S) URL and converts its name to binary.
|
||||
%% Fails with `badarg` otherwise. The function is intended for usage
|
||||
%% in configuration validators only.
|
||||
-spec try_url(binary() | string()) -> binary().
|
||||
try_url(URL0) ->
|
||||
URL = case URL0 of
|
||||
V when is_binary(V) -> binary_to_list(V);
|
||||
_ -> URL0
|
||||
end,
|
||||
case http_uri:parse(URL) of
|
||||
{ok, {Scheme, _, _, _, _, _}} when Scheme /= http, Scheme /= https ->
|
||||
?ERROR_MSG("Unsupported URI scheme: ~s", [URL]),
|
||||
erlang:error(badarg);
|
||||
{ok, {_, _, Host, _, _, _}} when Host == ""; Host == <<"">> ->
|
||||
?ERROR_MSG("Invalid URL: ~s", [URL]),
|
||||
erlang:error(badarg);
|
||||
{ok, _} ->
|
||||
iolist_to_binary(URL);
|
||||
{error, _} ->
|
||||
?ERROR_MSG("Invalid URL: ~s", [URL]),
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
|
||||
-spec css_dir() -> file:filename().
|
||||
css_dir() ->
|
||||
get_dir("css").
|
||||
@@ -239,6 +335,10 @@ msgs_dir() ->
|
||||
sql_dir() ->
|
||||
get_dir("sql").
|
||||
|
||||
-spec lua_dir() -> file:filename().
|
||||
lua_dir() ->
|
||||
get_dir("lua").
|
||||
|
||||
-spec read_css(file:filename()) -> {ok, binary()} | {error, file:posix()}.
|
||||
read_css(File) ->
|
||||
read_file(filename:join(css_dir(), File)).
|
||||
@@ -251,12 +351,57 @@ read_img(File) ->
|
||||
read_js(File) ->
|
||||
read_file(filename:join(js_dir(), File)).
|
||||
|
||||
-spec read_lua(file:filename()) -> {ok, binary()} | {error, file:posix()}.
|
||||
read_lua(File) ->
|
||||
read_file(filename:join(lua_dir(), File)).
|
||||
|
||||
-spec get_descr(binary(), binary()) -> binary().
|
||||
get_descr(Lang, Text) ->
|
||||
Desc = translate:translate(Lang, Text),
|
||||
Copyright = ejabberd_config:get_copyright(),
|
||||
<<Desc/binary, $\n, Copyright/binary>>.
|
||||
|
||||
-spec intersection(list(), list()) -> list().
|
||||
intersection(L1, L2) ->
|
||||
lists:filter(
|
||||
fun(E) ->
|
||||
lists:member(E, L2)
|
||||
end, L1).
|
||||
|
||||
-spec format_val(any()) -> iodata().
|
||||
format_val({yaml, S}) when is_integer(S); is_binary(S); is_atom(S) ->
|
||||
format_val(S);
|
||||
format_val({yaml, YAML}) ->
|
||||
S = try fast_yaml:encode(YAML)
|
||||
catch _:_ -> YAML
|
||||
end,
|
||||
format_val(S);
|
||||
format_val(I) when is_integer(I) ->
|
||||
integer_to_list(I);
|
||||
format_val(B) when is_atom(B) ->
|
||||
erlang:atom_to_binary(B, utf8);
|
||||
format_val(Term) ->
|
||||
S = try iolist_to_binary(Term)
|
||||
catch _:_ -> list_to_binary(io_lib:format("~p", [Term]))
|
||||
end,
|
||||
case binary:match(S, <<"\n">>) of
|
||||
nomatch -> S;
|
||||
_ -> [io_lib:nl(), S]
|
||||
end.
|
||||
|
||||
-spec cancel_timer(reference()) -> ok.
|
||||
cancel_timer(TRef) when is_reference(TRef) ->
|
||||
case erlang:cancel_timer(TRef) of
|
||||
false ->
|
||||
receive {timeout, TRef, _} -> ok
|
||||
after 0 -> ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
cancel_timer(_) ->
|
||||
ok.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user