Compare commits
374 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18f6b02065 | |||
| 33544569fb | |||
| 529ab22a79 | |||
| 9f6afab09c | |||
| 2137c03da1 | |||
| 58fe9d3345 | |||
| deb5e45bff | |||
| bd1df027c6 | |||
| 20290cab01 | |||
| adcf2d5c4e | |||
| 8faafc5b0d | |||
| 370093afde | |||
| 32d3d1626b | |||
| d4d28e038b | |||
| d6a076dae8 | |||
| 2cacf21d51 | |||
| 0874b93e7c | |||
| ba9094a089 | |||
| bfebcebeb7 | |||
| 1c0b99e162 | |||
| f850bcbbc9 | |||
| 7bc879c6f4 | |||
| f7d532f2f7 | |||
| dfbf32ecba | |||
| 17abbf3d82 | |||
| dfbfd90f8c | |||
| 84c1cf8033 | |||
| 5a0cfe7e2b | |||
| 714dce84db | |||
| b807b21a59 | |||
| 4f4c8eb61e | |||
| 3952888f94 | |||
| 9b145385af | |||
| 897b46c31d | |||
| 72299ab078 | |||
| e7ab83b612 | |||
| 07cf6f09b8 | |||
| 252ee6228b | |||
| cf6264f507 | |||
| 4f1ececbd1 | |||
| 70bf5b4eda | |||
| f59a979f7c | |||
| 05e3893f60 | |||
| 7d7621c67c | |||
| 449e56ed52 | |||
| aded966370 | |||
| dac1f328d7 | |||
| 90ea752046 | |||
| e4b3bd5005 | |||
| abcbcd1f2c | |||
| 18fd67b311 | |||
| b116957982 | |||
| a1f20a5bc0 | |||
| e0d14c3b8d | |||
| 1548a18b5e | |||
| 339fa6e41b | |||
| db0962804d | |||
| 677d8b1a29 | |||
| 850218c2df | |||
| 1c89914382 | |||
| fe40a9c5f6 | |||
| bc0ae5a017 | |||
| 91ab7e029b | |||
| c3a88c713b | |||
| fd52f2cb7d | |||
| f91eb52890 | |||
| ca59a7f027 | |||
| 6dea2d2307 | |||
| 650b2802b5 | |||
| 6ddc66db9f | |||
| 792512459d | |||
| 0359e345b0 | |||
| a09c319357 | |||
| 36166aa40e | |||
| a25c9c5df3 | |||
| 6e3a9ac4fd | |||
| f4c74c147b | |||
| 0edda6150b | |||
| bd43505db9 | |||
| a2e1d2030a | |||
| 4c8b034874 | |||
| b47c50145d | |||
| b9f4daca46 | |||
| 606c207e21 | |||
| d9c01e8ed9 | |||
| 3e9c9fc750 | |||
| fbcb9bb154 | |||
| 6242fd2bb8 | |||
| 1c566057f8 | |||
| aa59d8f1f0 | |||
| 6dd35923b0 | |||
| b9bbe19d4c | |||
| 44b2002504 | |||
| faf422202f | |||
| eb884c80d0 | |||
| b83dd9f954 | |||
| fd828c3e9b | |||
| 79648ce853 | |||
| ddc94a8c96 | |||
| aa413d63da | |||
| 548be4cf3d | |||
| 0e5b0b43fa | |||
| 8258f5940b | |||
| c890b17834 | |||
| f0a848ef45 | |||
| 20d66e6736 | |||
| 731c9b86e0 | |||
| 955343f6aa | |||
| 1b73ac5118 | |||
| 9ee8191939 | |||
| f2231a2282 | |||
| 3e24408710 | |||
| 3e67200d64 | |||
| 6374b5b1e7 | |||
| 26ac75bdc9 | |||
| 1d1496a667 | |||
| e34eebb5ad | |||
| 776930fa06 | |||
| 38af7c85a6 | |||
| 99b7a285d7 | |||
| 19e20f75d7 | |||
| b8d6aee1ce | |||
| 48b768b5b0 | |||
| bd604945c9 | |||
| 0a59ebe405 | |||
| 32eaa01929 | |||
| baf9363bdf | |||
| 12aaa0125b | |||
| cb7cd9abb8 | |||
| ca701e1675 | |||
| 9b88fd6646 | |||
| 76ca7ae7f0 | |||
| 1ab223568d | |||
| 043effc3ce | |||
| 4fd1a8ba63 | |||
| 36303fb4be | |||
| 62357b9589 | |||
| 808029b868 | |||
| cfd377b98e | |||
| 6eeb355a22 | |||
| a0e2e943b4 | |||
| 78931d8efa | |||
| 0d2a8cd04f | |||
| 97087eb3b9 | |||
| 6eeef62ce4 | |||
| 0ccff15599 | |||
| 325fb8caef | |||
| 18ef908759 | |||
| 1f1d2bd5f5 | |||
| 0c484369c9 | |||
| 100f2e9a13 | |||
| 510fd8cf73 | |||
| 080922a3de | |||
| cb7d8c8ead | |||
| c47252aea1 | |||
| f96074057c | |||
| dfaeb3bc88 | |||
| c31f59e326 | |||
| 7c2b9eaf97 | |||
| 79f5251d69 | |||
| 917e8640c2 | |||
| c4bc0e7252 | |||
| 1f16e4783c | |||
| 7f3a5066c6 | |||
| 6f3713a67d | |||
| abfb4a2841 | |||
| f672fd0824 | |||
| 60b36beda8 | |||
| c4289095e0 | |||
| 31757116fc | |||
| 2f68733708 | |||
| dccaff0544 | |||
| 184ec38510 | |||
| 2d59efb515 | |||
| 195d22d906 | |||
| ab80513755 | |||
| 8a116411bb | |||
| bd3889b6ec | |||
| 400fb69f15 | |||
| 9da45d40c7 | |||
| 23db206ea1 | |||
| 964b7b6b67 | |||
| 9c5f34794a | |||
| eca7588abf | |||
| a15d583d4d | |||
| e03c453c78 | |||
| 641dc7d695 | |||
| 0a1b0498a6 | |||
| a6858a6ce4 | |||
| f4beeb1706 | |||
| 75298b4c27 | |||
| 011464e6ac | |||
| c53e8012b2 | |||
| fe04d57284 | |||
| 0f3bd782c4 | |||
| 91cf9194d8 | |||
| e2dbad6242 | |||
| 84d4a1619b | |||
| 16fae4d117 | |||
| 76f7548935 | |||
| 2d08dcf11a | |||
| 7af47b9dbe | |||
| 437da38342 | |||
| 801ee586b7 | |||
| 6ff3fda14b | |||
| cfca2b502a | |||
| e1a4ae8264 | |||
| 19826858a4 | |||
| 0c2677bc50 | |||
| d1c1902687 | |||
| a5230c46c2 | |||
| 95ce77f80d | |||
| a93f4f7750 | |||
| ac87749d55 | |||
| 50b747041f | |||
| 36df8c9035 | |||
| 58a5ed9cdc | |||
| 5ea909885e | |||
| 1f2c9b7971 | |||
| c13940e1de | |||
| 8e5297f4c3 | |||
| 3278f019cb | |||
| e2c3925b7d | |||
| 8a0ccfc401 | |||
| c29b2fda99 | |||
| c75b7b2b12 | |||
| c754c91ad1 | |||
| 6952324509 | |||
| 4b5ef8f2ce | |||
| d87fff1a4c | |||
| 3024bb0cbf | |||
| f8dd973373 | |||
| 972440c2ca | |||
| b14899d41a | |||
| d2d8a09b4a | |||
| 1ea09b09a2 | |||
| 071c0a1afe | |||
| 4ed00c3d1f | |||
| c9ff370278 | |||
| 8ab8da82c4 | |||
| 4b6a42f539 | |||
| 8a251ccafe | |||
| 550363cd52 | |||
| 367353100b | |||
| 5113d28bb4 | |||
| c8df607173 | |||
| c8033833f9 | |||
| cf4f0dbe6d | |||
| 8c4884d665 | |||
| 26f6eebaa9 | |||
| ac84267b22 | |||
| 5030f35558 | |||
| d7930d7f82 | |||
| 42ddc297fe | |||
| e82db8cc7f | |||
| 10d43c7cc6 | |||
| d539fd28c1 | |||
| 12e00c57f9 | |||
| 0c95cf7e61 | |||
| d106f741d9 | |||
| 6c96157d1b | |||
| a42a012f94 | |||
| 7e90d6cf92 | |||
| e19ac27803 | |||
| c100cbedd1 | |||
| 4c2e7e38a1 | |||
| fa22b23435 | |||
| c3c23a04f6 | |||
| ae6545989e | |||
| baeb3d076e | |||
| 0ac5684bf0 | |||
| 715cc5ea3b | |||
| d9e2931ed9 | |||
| 1531541331 | |||
| b7e02cc42e | |||
| 3ab90c9d3a | |||
| 1b7948f50a | |||
| fe23dbd76e | |||
| 21d2f4efab | |||
| f66f049ef3 | |||
| 49dd83e731 | |||
| 9cd2d5e0cf | |||
| fab705dc13 | |||
| a84f8bc9ef | |||
| 2477b735bf | |||
| 8db328af60 | |||
| bc2280be70 | |||
| a28aaa1fdd | |||
| afa0f4d403 | |||
| 0e5b930b22 | |||
| 7d8f8a7e99 | |||
| a7924adee9 | |||
| d4ec7a2f01 | |||
| e0fab19345 | |||
| 4646a5dbb8 | |||
| 1567592ac7 | |||
| 3b735c269c | |||
| 5bdef05f12 | |||
| 077626c40a | |||
| c544384c83 | |||
| d56522c8a0 | |||
| b83e77ca21 | |||
| 361400691d | |||
| ba9247c530 | |||
| 91d22109c8 | |||
| cb702a770d | |||
| db059bdfaf | |||
| 2291a6afea | |||
| af7fe0c21e | |||
| 7ce3291603 | |||
| 07e870271e | |||
| a5f8aeb6da | |||
| 1777ecd15a | |||
| d4d45f3a50 | |||
| 9900be3d68 | |||
| b88372c313 | |||
| e4aab7f749 | |||
| 42cbd0c1c4 | |||
| 9318678e08 | |||
| bd992bcb87 | |||
| 01d2bcfd92 | |||
| 0bfda4e628 | |||
| a2f659a2c6 | |||
| 9a04a60c7d | |||
| 7238ab6f05 | |||
| dd954ef1c2 | |||
| f1b8853339 | |||
| 479a2adaae | |||
| dd3c939025 | |||
| 767e7d65ef | |||
| 5feb20afd0 | |||
| 9038bbd3e7 | |||
| 506fbbe7f7 | |||
| 08f1f55e0c | |||
| 5ea9a0ddb7 | |||
| d4e0fab06b | |||
| 217aaf78f9 | |||
| 2ee788e0bf | |||
| f0f4b2719c | |||
| 406a31c8be | |||
| c32332898b | |||
| 4bbf6684b4 | |||
| 9476d8a2c3 | |||
| 6ac46c6171 | |||
| 6ae8b9c4d6 | |||
| 39119192a1 | |||
| 25e4038623 | |||
| f58d03c12e | |||
| 58590cf08d | |||
| cce57310b0 | |||
| 516d369174 | |||
| 7fc1207661 | |||
| 951808658d | |||
| cd9e1621f9 | |||
| 650966ce2c | |||
| 2a99af8cce | |||
| 3ba0061ada | |||
| 705c5b4c1c | |||
| 6ae1f62469 | |||
| 1c00a9713d | |||
| 5e7d8868c0 | |||
| 55dbdf5dba | |||
| 2ff291899d | |||
| 2d375e0429 | |||
| 4669ae0a81 | |||
| c2362db03d | |||
| 1405e9d375 | |||
| 84c4d75735 | |||
| 41aa693896 | |||
| 44b282474a | |||
| 70cfcb1008 | |||
| fa28c8baf6 | |||
| 584fa98564 | |||
| 214c62bf9c |
+38
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# You can add personal rules in your file .git/info/exclude
|
||||
*.swp
|
||||
*~
|
||||
/contrib/extract_translations/extract_translations.beam
|
||||
/doc/*.aux
|
||||
/doc/*.haux
|
||||
/doc/*.html
|
||||
/doc/*.htoc
|
||||
/doc/*.idx
|
||||
/doc/*.ilg
|
||||
/doc/*.ind
|
||||
/doc/*.log
|
||||
/doc/*.out
|
||||
/doc/*.pdf
|
||||
/doc/*.toc
|
||||
/doc/contributed_modules.tex
|
||||
/doc/version.tex
|
||||
/src/*.beam
|
||||
/src/*.so
|
||||
/src/*.so.dSYM
|
||||
/src/*/*.beam
|
||||
/src/*/Makefile
|
||||
/src/Makefile
|
||||
/src/XmppAddr.asn1db
|
||||
/src/XmppAddr.erl
|
||||
/src/XmppAddr.hrl
|
||||
/src/aclocal.m4
|
||||
/src/autom4te.cache
|
||||
/src/config.log
|
||||
/src/config.status
|
||||
/src/ejabberd.init
|
||||
/src/ejabberdctl.example
|
||||
/src/eldap/ELDAPv3.asn1db
|
||||
/src/eldap/ELDAPv3.erl
|
||||
/src/eldap/ELDAPv3.hrl
|
||||
/src/eldap/eldap_filter_yecc.erl
|
||||
/src/epam
|
||||
@@ -9,10 +9,9 @@ To compile ejabberd you need:
|
||||
- GNU Make
|
||||
- GCC
|
||||
- Libexpat 1.95 or higher
|
||||
- Erlang/OTP R10B-9 or higher. The recommended version is R12B-5.
|
||||
Support for R13 is experimental.
|
||||
- OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL
|
||||
encryption. Optional, highly recommended.
|
||||
- Erlang/OTP R10B-9 or higher. Recommended: R12B-5 and R13B04.
|
||||
Avoid R14A and R14B.
|
||||
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support
|
||||
(XEP-0138). Optional.
|
||||
- Erlang mysql library. Optional. MySQL authentication/storage.
|
||||
@@ -21,7 +20,7 @@ To compile ejabberd you need:
|
||||
- GNU Iconv 1.8 or higher, for the IRC Transport
|
||||
(mod_irc). Optional. Not needed on systems with GNU Libc.
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
- exmpp 0.9.2 or higher. Optional. For import/export XEP-0227 files.
|
||||
- exmpp 0.9.6 or higher. Optional. For import/export XEP-0227 files.
|
||||
|
||||
|
||||
1. Compile and install on *nix systems
|
||||
|
||||
+6
-3
@@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.1.3 Developers Guide
|
||||
<TITLE>Ejabberd 2.1.7 Developers Guide
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@@ -49,7 +49,7 @@ TD P{margin:0px;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic dev.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.3 Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.7 Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
|
||||
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
@@ -170,7 +170,7 @@ manager.</P><!--TOC subsection Local Router-->
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it’s content.</P><!--TOC subsection Session Manager-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc6">3.3</A>  Session Manager</H3><!--SEC END --><P>This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
packet must be sent via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.</P><!--TOC subsection S2S Manager-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc7">3.4</A>  S2S Manager</H3><!--SEC END --><P>This module routes packets to other XMPP servers. First, it checks if an
|
||||
@@ -194,6 +194,9 @@ operation are as follows:
|
||||
auth:User:Server:Password (check if a username/password pair is correct)
|
||||
</LI><LI CLASS="li-itemize">isuser:User:Server (check if it’s a valid user)
|
||||
</LI><LI CLASS="li-itemize">setpass:User:Server:Password (set user’s password)
|
||||
</LI><LI CLASS="li-itemize">tryregister:User:Server:Password (try to register an account)
|
||||
</LI><LI CLASS="li-itemize">removeuser:User:Server (remove this account)
|
||||
</LI><LI CLASS="li-itemize">removeuser3:User:Server:Password (remove this account if the password is correct)
|
||||
</LI></UL>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-itemize">write to stdout: AABB
|
||||
|
||||
+4
-1
@@ -141,7 +141,7 @@ session manager, else it is processed depending on it's content.
|
||||
\subsection{Session Manager}
|
||||
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
packet must be sent via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.
|
||||
|
||||
@@ -176,6 +176,9 @@ That script is supposed to do theses actions, in an infinite loop:
|
||||
\item auth:User:Server:Password (check if a username/password pair is correct)
|
||||
\item isuser:User:Server (check if it's a valid user)
|
||||
\item setpass:User:Server:Password (set user's password)
|
||||
\item tryregister:User:Server:Password (try to register an account)
|
||||
\item removeuser:User:Server (remove this account)
|
||||
\item removeuser3:User:Server:Password (remove this account if the password is correct)
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\item write to stdout: AABB
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.1.3 Feature Sheet
|
||||
<TITLE>Ejabberd 2.1.7 Feature Sheet
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@@ -50,7 +50,7 @@ SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic features.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.3 Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.7 Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
<A HREF="mailto:s.devrieze@pandora.be"><TT>mailto:s.devrieze@pandora.be</TT></A><BR>
|
||||
<A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
|
||||
+582
-185
File diff suppressed because it is too large
Load Diff
+661
-92
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,80 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.4
|
||||
|
||||
ejabberd 2.1.4 is the fourth release in ejabberd 2.1.x branch,
|
||||
and includes many small bugfixes and improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.4
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
This is the full list of changes:
|
||||
|
||||
* Authentication
|
||||
- Extauth: Optionally cache extauth users in mnesia (EJAB-641)
|
||||
- LDAP: Allow inband password change (EJAB-199)
|
||||
- LDAP: Extensible match support (EJAB-722)
|
||||
- LDAP: New option ldap_tls_verify is added (EJAB-1229)
|
||||
- PAM: New option pam_userinfotype to provide username or JID (EJAB-652)
|
||||
|
||||
* HTTP
|
||||
- Add xml default content type
|
||||
- Don't show HTTP request in logs, because reveals password (EJAB-1231)
|
||||
- Move HTTP session timeout log from warning level to info
|
||||
- New Access rule webadmin_view for read-only
|
||||
|
||||
* HTTP-Bind (BOSH)
|
||||
- Change max inactivity from 30 to 120 seconds
|
||||
- Export functions to facilitate prebinding methods
|
||||
- Use dirty_delete when removing the session
|
||||
- Remove an unneeded delay of 100 milliseconds
|
||||
|
||||
* Pubsub, PEP and Caps
|
||||
- Enforce pubsub#presence_based_delivery (EJAB-1221)
|
||||
- Enforce pubsub#show_values subscription option (EJAB-1096)
|
||||
- Fix error code when unsubscribing from a non-existent node
|
||||
- Fix to send node notifications (EJAB-1225)
|
||||
- Full support for XEP-0115 v1.5 (EJAB-1223)(EJAB-1189)
|
||||
- Make last_item_cache feature to be cluster aware
|
||||
- Prevent orphaned pubsub node (EJAB-1233)
|
||||
- Send created node notifications
|
||||
|
||||
* Other
|
||||
- Bounce messages when closing c2s session
|
||||
- Bugfixes when handling Service Discovery to contacts (EJAB-1207)
|
||||
- Compilation of ejabberd_debug.erl is now optional
|
||||
- Don't send error stanza as reply to error stanza (EJAB-930)
|
||||
- Don't store blocked messages in offline queue
|
||||
- Reduce verbosity of log when captcha_cmd is checked but not configured
|
||||
- Use a standard method to get a random seed (EJAB-1229)
|
||||
- Commands: new update_list and update to update modified modules (EJAB-1237)
|
||||
- Localization: Updated most translations
|
||||
- MUC: Refactor code to reduce calls to get_affiliation and get_role
|
||||
- ODBC: Add created_at column also to PostgreSQL schema
|
||||
- Vcard: Automatic vcard avatar addition in presence
|
||||
|
||||
|
||||
Upgrading From previous ejabberd releases:
|
||||
|
||||
- If you use PostgreSQL, maybe you want to add the column created_at
|
||||
to several tables. This is only a suggestion; ejabberd doesn't use
|
||||
that column. Add it to your existing database executing those SQL
|
||||
statements:
|
||||
|
||||
ALTER TABLE users ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE rosterusers ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE spool ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE vcard ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE privacy_list ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE privacy_storage ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.5
|
||||
|
||||
ejabberd 2.1.5 is the fifth release in ejabberd 2.1.x branch,
|
||||
and includes several minor bugfixes and a few improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.4
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
This is the full list of changes:
|
||||
|
||||
* Authentication
|
||||
- Extauth: Support parallel script running (EJAB-1280)
|
||||
- mod_register: Return Registered element when account exists
|
||||
|
||||
* ejabberdctl
|
||||
- Fix print of command result that contains ~
|
||||
- Fix problem when FIREWALL_WINDOW options for erl kernel were used
|
||||
- Fix typo in update_list command (EJAB-1237)
|
||||
- Some systems delete the lock dir; in such case don't use flock at all
|
||||
- The command Update now returns meaningful message and exit-status (EJAB-1237)
|
||||
|
||||
* HTTP-Bind (BOSH)
|
||||
- Don't say v1.2 in the Bind HTTP page
|
||||
- New optional BOSH connection attribute process-delay (EJAB-1257)
|
||||
|
||||
* MUC
|
||||
- Document the mod_muc option captcha_protected
|
||||
- Now admins are able to see private rooms in disco (EJAB-1269)
|
||||
- Show some more room options in the log file
|
||||
|
||||
* ODBC
|
||||
- Correct handling of SQL boolean types (EJAB-1275)
|
||||
- Discard too old queued requests (the caller has already got a timeout)
|
||||
- Fixes wrong SQL escaping when --enable-full-xml is set
|
||||
- Use ets insead of asking supervisor in ejabberd_odbc_sup:get_pids/1
|
||||
|
||||
* Pubsub, PEP and Caps
|
||||
- Enforce disco features results (EJAB-1033, EJAB-1228, EJAB-1238)
|
||||
- Support all the hash functions required by Caps XEP-0115
|
||||
|
||||
* Requirements
|
||||
- Fixed support for Erlang R12; which doesn't support: true andalso ok
|
||||
- Support OTP R14A by using public_key library instead of old ssl (EJAB-953)
|
||||
- Requirement of OpenSSL increased from 0.9.6 to 0.9.8
|
||||
- OpenSSL is now required, not optional
|
||||
|
||||
* Other
|
||||
- Don't ask for client certificate when using tls (EJAB-1267)
|
||||
- Fix typo in --enable-transient_supervisors
|
||||
- Fix privacy check when serving local Last (EJAB-1271)
|
||||
- Inform client that SSL session caching is disabled
|
||||
- New configure option: --enable-nif
|
||||
- Use driver allocator in C files for reflecting memory in erlang:memory(system)
|
||||
- Debug: New p1_prof compiled with: make debugtools=true
|
||||
- Debug: Added functions to collect stats about queues, memory, reductions etc
|
||||
- HTTP: Log error if request has ambiguous Host header (EJAB-1261)
|
||||
- Logs: When logging s2s out connection attempt or success, log if TLS is used
|
||||
- Shared Rosters: When account is deleted, delete also member of stored rosters
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.6
|
||||
|
||||
ejabberd 2.1.6 is the sixth release in ejabberd 2.1.x branch,
|
||||
and includes a lot of bugfixes and improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.6
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
Some of the changes are:
|
||||
|
||||
* Account register
|
||||
- mod_register: New ip_access option restricts which IPs can register (EJAB-915)
|
||||
- mod_register: Default configuration allows registrations only from localhost
|
||||
- mod_register: New password_strength for entropy check (EJAB-1326)
|
||||
- mod_register: New captcha_protected option to require CAPTCHA (EJAB-1262)
|
||||
- mod_register_web: New module, with CAPTCHA support (EJAB-471)
|
||||
|
||||
* BOSH
|
||||
- Don't loop when there is nothing after a stream start (EJAB-1358)
|
||||
- Fix http-bind supervisor to support multiple vhosts (EJAB-1321)
|
||||
- Support to restart http-bind (EJAB-1318)
|
||||
- Support for X-Forwarded-For HTTP header (EJAB-1356)
|
||||
|
||||
* Erlang/OTP compatibility
|
||||
- R11: Fix detection of Erlang R11 and older (EJAB-1287)
|
||||
- R12B5: Fix compatibility in ejabberd_http_bind.erl (EJAB-1343)
|
||||
- R14A: Make xml.c correctly compile (EJAB-1288)
|
||||
- R14A, R14B: Disapprove the use of R14A and R14B due to the rwlock bug
|
||||
- R14B: Use pg2 from this version in systems with older ones (EJAB-1349)
|
||||
|
||||
* Listeners
|
||||
- Bind listener ports early and start accepting connections later
|
||||
- Fix a leak of ejabberd_receiver processes
|
||||
- Speed up ejabberd_s2s:is_service/2, allow_host/2 (EJAB-1319)
|
||||
- S2S: New option to require encryption (EJAB-495)
|
||||
- S2S: New option to reject connection if untrusted certificate (EJAB-464)
|
||||
- S2S: Include From attribute in the stream header of outgoing S2S connections
|
||||
- S2S: Fix domain_certfile tlsopts modifications for S2S connections (EJAB-1086)
|
||||
|
||||
* Pubsub/PEP/Caps
|
||||
- Fix pubsub cross domain eventing (EJAB-1340)
|
||||
- Use one_queue IQ discipline by default
|
||||
- Implement lifetime for broken hashes
|
||||
- New CAPS processing
|
||||
|
||||
* ODBC
|
||||
- Increase maximum restart strategy to handle some SQL timeouts
|
||||
- Support PostgreSQL 9.0 (EJAB-1359)
|
||||
- Use MEDIUMTEXT type for vcard avatars in MySQL schema (EJAB-1252)
|
||||
|
||||
* Miscellanea:
|
||||
- mod_shared_roster_ldap: New Shared Roster Groups using LDAP information
|
||||
- mod_privacy: Fix to allow block by group and subscription again
|
||||
- Support timezone West of UTC (EJAB-1301)
|
||||
- Support to change loglevel per module at runtime (EJAB-225)
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.7
|
||||
|
||||
ejabberd 2.1.7 is the eighth release in ejabberd 2.1.x branch,
|
||||
and includes a lot of bugfixes and improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.7
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
* BOSH and Web
|
||||
- Clarify error message when BOSH query is sent to non-running module
|
||||
- Keep the order of stanzas when BOSH sends several (EJAB-1374)
|
||||
- Show configuration for HTTPS http_bind
|
||||
- Support as read-only HTTP method not only GET, also HEAD
|
||||
- The responses to HEAD must have empty Body
|
||||
|
||||
* CAPTCHA
|
||||
- If the port number isn't listener, then specify the protocol (EJAB-1418)
|
||||
- New CAPTCHA limit
|
||||
- New CAPTCHA whitelist support
|
||||
- Only check system at startup if option is enabled
|
||||
- Provide HTTPS URL in CAPTCHA form when listener has 'tls' option (EJAB-1406)
|
||||
- Show captcha_limit option in the example config
|
||||
- Support more captcha_host value formats (EJAB-1418)
|
||||
- Throw error when captcha fails at server start, not later at runtime
|
||||
- captcha_host must have the port number to get protocol (EJAB-1418)
|
||||
|
||||
* Core ejabberd
|
||||
- Disable all entity expansions (EJAB-1451)
|
||||
- Do not accept XML with undefined prefixes (EJAB-680)
|
||||
- Make jlib:ip_to_list safe to use
|
||||
- Make sure 'closed' event is correctly processed on every state
|
||||
- New route_iq/5 accepting Timeout (EJAB-1398)
|
||||
- Take into consideration internal queue length when sorting processes queues
|
||||
- Use route instead of send_element to go through standard workflow
|
||||
|
||||
* Erlang/OTP compatibility
|
||||
- Remove Type and Spec, backport list comprehensions, so R12B-5 can compile
|
||||
- Tweak pg2_backport.erl to work with Erlang older than R13A (EJAB-1349)
|
||||
|
||||
* ODBC
|
||||
- Don't let presence-in privacy rule block a presence subscription (EJAB-255)
|
||||
- Escape user input in mod_privacy_odbc (EJAB-1442)
|
||||
- Try to improve support for roster_version in MSSQL (EJAB-1437)
|
||||
|
||||
* Pubsub/PEP/Caps
|
||||
- Apply filtered notification to PEP last items (EJAB-1456)
|
||||
- Fix empty pubsub payload check
|
||||
- Owner can delete any items from its own node (EJAB-1445)
|
||||
- Pubsub node maxitem forced to 0 if non persistent node (EJAB-1434)
|
||||
- Reorganize the push_item function, and handle version not_found (EJAB-1420)
|
||||
|
||||
* Scripts
|
||||
- ejabberd.init: Several fixes and improvements
|
||||
- ejabberdctl: Escape output from ctlexec() to erl script (EJAB-1399)
|
||||
- ejabberdctl: Fix bashism and mimic master branch (EJAB-1404)
|
||||
- ejabberdctl: Fix space between INET_DIST_INTERFACE (EJAB-1416)
|
||||
- ejabberdctl: New DIST_USE_INTERFACE restricts IP of erlang listen (EJAB-1404)
|
||||
- ejabberdctl: New ERL_EPMD_ADDRESS that works since Erlang/OTP R14B03
|
||||
- extauth: Fix delayed response of timeout was reused for next login (EJAB-1385)
|
||||
- extauth: Forward old messages to newly spawned extauth process (EJAB-1385)
|
||||
- extauth: If script crashes, ejabberd should restart it (EJAB-1428)
|
||||
|
||||
* XEP support
|
||||
- mod_blocking: New XEP-0191 Simple Communications Blocking (EJAB-695)
|
||||
- No need to inform that XEP-0237 is optional; clarified in XEP version 1.2
|
||||
|
||||
* Miscellanea:
|
||||
- If a module start fails during server start, stop erlang (EJAB-1446)
|
||||
- New Indonesian translation (EJAB-1407)
|
||||
- LDAP: Note that ejabberd works with CGP LDAP server
|
||||
- S2S: Handle Tigase's unexpected version=1.0 (EJAB-1379)
|
||||
- mod_irc: Send presence unavailable to the departing occupant (EJAB-1417)
|
||||
- mod_last: Allow user to query his own Last activity
|
||||
- mod_muc: Do not decrease MUC admin's role/affiliation
|
||||
- mod_muc: Send jid attribute when occupant is banned (EJAB-1432)
|
||||
- mod_offline: Change c2s state before offline messages resending
|
||||
- mod_ping: Use iqdisc no_queue by default (EJAB-1435)
|
||||
- mod_pres_counter: Prevent subscription flood (EJAB-1388)
|
||||
- mod_register Access now also controls account unregistrations
|
||||
- mod_register: Clarify more the expected content of welcome_message option
|
||||
- mod_shared_roster: Fix support for anonymous accounts in @all@ (EJAB-1264)
|
||||
- mod_shared_roster: New @online@ directive (EJAB-1391)
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.8
|
||||
|
||||
ejabberd 2.1.8 is the ninth release in ejabberd 2.1.x branch,
|
||||
and includes a PubSub regression bugfix.
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The change is:
|
||||
|
||||
- Fix issue on PubSub preventing publication of items (EJAB-1457)
|
||||
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
% ejabberd version (automatically generated).
|
||||
\newcommand{\version}{2.1.3}
|
||||
\newcommand{\version}{2.1.7}
|
||||
|
||||
Regular → Executable
+16
@@ -42,6 +42,22 @@ while(1)
|
||||
# password is null. Return 1 if the user $user\@$domain exitst.
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'tryregister' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'removeuser' and do
|
||||
{
|
||||
# password is null. Return 1 if the user $user\@$domain exitst.
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'removeuser3' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
};
|
||||
my $out = pack "nn",2,$result ? 1 : 0;
|
||||
syswrite STDOUT,$out;
|
||||
|
||||
+24
-5
@@ -30,15 +30,17 @@ else
|
||||
INIT_USER=$(INSTALLUSER)
|
||||
endif
|
||||
|
||||
EFLAGS += @ERLANG_SSL39@ -pa .
|
||||
EFLAGS += @ERLANG_SSLVER@ -pa .
|
||||
ERLANG_CFLAGS += @ERLANG_SSLVER@
|
||||
|
||||
# make debug=true to compile Erlang module with debug informations.
|
||||
ifdef debug
|
||||
EFLAGS+=+debug_info +export_all
|
||||
endif
|
||||
|
||||
ifdef ejabberd_debug
|
||||
EFLAGS+=-Dejabberd_debug
|
||||
DEBUGTOOLS = p1_prof.erl
|
||||
ifdef debugtools
|
||||
SOURCES+=$(DEBUGTOOLS)
|
||||
endif
|
||||
|
||||
ifeq (@hipe@, true)
|
||||
@@ -53,10 +55,20 @@ ifeq (@full_xml@, true)
|
||||
EFLAGS+=-DFULL_XML_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq (@nif@, true)
|
||||
EFLAGS+=-DNIF
|
||||
ERLSHLIBS=xml.so
|
||||
endif
|
||||
|
||||
ifeq (@transient_supervisors@, false)
|
||||
EFLAGS+=-DNO_TRANSIENT_SUPERVISORS
|
||||
endif
|
||||
|
||||
ifeq (@md2@, true)
|
||||
EFLAGS+=-DHAVE_MD2
|
||||
ERLANG_CFLAGS += -DHAVE_MD2
|
||||
endif
|
||||
|
||||
INSTALL_EPAM=
|
||||
ifeq (@pam@, pam)
|
||||
INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR)
|
||||
@@ -66,10 +78,11 @@ prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ stringprep stun @tls@ @odbc@ @ejabberd_zlib@
|
||||
ERLSHLIBS = expat_erl.so
|
||||
ERLSHLIBS += expat_erl.so
|
||||
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
|
||||
SOURCES_ALL = $(wildcard *.erl)
|
||||
SOURCES = $(filter-out $(ERLBEHAVS),$(SOURCES_ALL))
|
||||
SOURCES_MISC = $(ERLBEHAVS) $(DEBUGTOOLS)
|
||||
SOURCES += $(filter-out $(SOURCES_MISC),$(SOURCES_ALL))
|
||||
ERLBEHAVBEAMS = $(ERLBEHAVS:.erl=.beam)
|
||||
BEAMS = $(SOURCES:.erl=.beam)
|
||||
|
||||
@@ -195,6 +208,7 @@ install: all
|
||||
sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Binary Erlang files
|
||||
install -d $(BEAMDIR)
|
||||
@@ -247,9 +261,14 @@ install: all
|
||||
#
|
||||
# Documentation
|
||||
install -d $(DOCDIR)
|
||||
install -m 644 ../doc/dev.html $(DOCDIR)
|
||||
install -m 644 ../doc/guide.html $(DOCDIR)
|
||||
install -m 644 ../doc/*.png $(DOCDIR)
|
||||
install -m 644 ../doc/*.txt $(DOCDIR)
|
||||
[ -f ../doc/guide.pdf ] \
|
||||
&& install -m 644 ../doc/guide.pdf $(DOCDIR) \
|
||||
|| echo "No ../doc/guide.pdf was built"
|
||||
install -m 644 ../COPYING $(DOCDIR)
|
||||
|
||||
uninstall: uninstall-binary
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
AC_DEFUN(AM_WITH_EXPAT,
|
||||
AC_DEFUN([AM_WITH_EXPAT],
|
||||
[ AC_ARG_WITH(expat,
|
||||
[AC_HELP_STRING([--with-expat=PREFIX], [prefix where EXPAT is installed])])
|
||||
|
||||
@@ -32,7 +32,7 @@ AC_DEFUN(AM_WITH_EXPAT,
|
||||
AC_SUBST(EXPAT_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_ZLIB,
|
||||
AC_DEFUN([AM_WITH_ZLIB],
|
||||
[ AC_ARG_WITH(zlib,
|
||||
[AC_HELP_STRING([--with-zlib=PREFIX], [prefix where zlib is installed])])
|
||||
|
||||
@@ -68,7 +68,7 @@ if test x"$ejabberd_zlib" != x; then
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_PAM,
|
||||
AC_DEFUN([AM_WITH_PAM],
|
||||
[ AC_ARG_WITH(pam,
|
||||
[AC_HELP_STRING([--with-pam=PREFIX], [prefix where PAM is installed])])
|
||||
if test x"$pam" != x; then
|
||||
@@ -103,7 +103,7 @@ if test x"$pam" != x; then
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_ERLANG,
|
||||
AC_DEFUN([AM_WITH_ERLANG],
|
||||
[ AC_ARG_WITH(erlang,
|
||||
[AC_HELP_STRING([--with-erlang=PREFIX], [path to erlc and erl])])
|
||||
|
||||
@@ -121,7 +121,6 @@ AC_DEFUN(AM_WITH_ERLANG,
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([[start/0]]).
|
||||
-include_lib("ssl/include/ssl_pkix.hrl").
|
||||
|
||||
start() ->
|
||||
EIDirS = code:lib_dir("erl_interface") ++ "\n",
|
||||
@@ -130,11 +129,13 @@ start() ->
|
||||
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
|
||||
halt().
|
||||
|
||||
-[ifdef]('id-pkix').
|
||||
ssldef() -> "-DSSL39\n".
|
||||
-else.
|
||||
ssldef() -> "\n".
|
||||
-endif.
|
||||
ssldef() ->
|
||||
OTP = (catch erlang:system_info(otp_release)),
|
||||
if
|
||||
OTP >= "R14" -> "-DSSL40\n";
|
||||
OTP >= "R12" -> "-DSSL39\n";
|
||||
true -> "\n"
|
||||
end.
|
||||
|
||||
%% return physical architecture based on OS/Processor
|
||||
archname() ->
|
||||
@@ -184,7 +185,7 @@ _EOF
|
||||
# Second line
|
||||
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
|
||||
# Third line
|
||||
ERLANG_SSL39=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
ERLANG_SSLVER=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
# End line
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
@@ -193,12 +194,12 @@ _EOF
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
AC_SUBST(ERLANG_SSL39)
|
||||
AC_SUBST(ERLANG_SSLVER)
|
||||
AC_SUBST(ERLC)
|
||||
AC_SUBST(ERL)
|
||||
])
|
||||
|
||||
AC_DEFUN(AC_MOD_ENABLE,
|
||||
AC_DEFUN([AC_MOD_ENABLE],
|
||||
[
|
||||
$1=
|
||||
make_$1=
|
||||
@@ -308,7 +309,7 @@ size_t iconv();
|
||||
])
|
||||
|
||||
dnl <openssl>
|
||||
AC_DEFUN(AM_WITH_OPENSSL,
|
||||
AC_DEFUN([AM_WITH_OPENSSL],
|
||||
[ AC_ARG_WITH(openssl,
|
||||
[AC_HELP_STRING([--with-openssl=PREFIX], [prefix where OPENSSL is installed])])
|
||||
unset SSL_LIBS;
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
|
||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -0,0 +1,604 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : cache_tab.erl
|
||||
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% Description : Caching key-value table
|
||||
%%%
|
||||
%%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2011 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., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(cache_tab).
|
||||
|
||||
-define(GEN_SERVER, gen_server).
|
||||
|
||||
-behaviour(?GEN_SERVER).
|
||||
|
||||
%% API
|
||||
-export([start_link/4, new/2, delete/1, delete/3, lookup/3,
|
||||
insert/4, info/2, tab2list/1, setopts/2,
|
||||
dirty_lookup/3, dirty_insert/4, dirty_delete/3,
|
||||
all/0, test/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(state, {tab = treap:empty(),
|
||||
name,
|
||||
size = 0,
|
||||
owner,
|
||||
max_size,
|
||||
life_time,
|
||||
warn,
|
||||
hits = 0,
|
||||
miss = 0,
|
||||
procs_num,
|
||||
cache_missed,
|
||||
lru,
|
||||
shrink_size}).
|
||||
|
||||
-define(PROCNAME, ?MODULE).
|
||||
-define(CALL_TIMEOUT, 60000).
|
||||
|
||||
%% Defaults
|
||||
-define(MAX_SIZE, 1000).
|
||||
-define(WARN, true).
|
||||
-define(CACHE_MISSED, true).
|
||||
-define(LRU, true).
|
||||
-define(LIFETIME, 600). %% 10 minutes
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
start_link(Proc, Tab, Opts, Owner) ->
|
||||
?GEN_SERVER:start_link(
|
||||
{local, Proc}, ?MODULE, [Tab, Opts, get_proc_num(), Owner], []).
|
||||
|
||||
new(Tab, Opts) ->
|
||||
Res = lists:flatmap(
|
||||
fun(Proc) ->
|
||||
Spec = {{Tab, Proc},
|
||||
{?MODULE, start_link,
|
||||
[Proc, Tab, Opts, self()]},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
case supervisor:start_child(cache_tab_sup, Spec) of
|
||||
{ok, _Pid} ->
|
||||
[ok];
|
||||
R ->
|
||||
[R]
|
||||
end
|
||||
end, get_all_procs(Tab)),
|
||||
case lists:filter(fun(ok) -> false; (_) -> true end, Res) of
|
||||
[] ->
|
||||
ok;
|
||||
Err ->
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
delete(Tab) ->
|
||||
lists:foreach(
|
||||
fun(Proc) ->
|
||||
supervisor:terminate_child(cache_tab_sup, {Tab, Proc}),
|
||||
supervisor:delete_child(cache_tab_sup, {Tab, Proc})
|
||||
end, get_all_procs(Tab)).
|
||||
|
||||
delete(Tab, Key, F) ->
|
||||
?GEN_SERVER:call(
|
||||
get_proc_by_hash(Tab, Key), {delete, Key, F}, ?CALL_TIMEOUT).
|
||||
|
||||
dirty_delete(Tab, Key, F) ->
|
||||
F(),
|
||||
?GEN_SERVER:call(
|
||||
get_proc_by_hash(Tab, Key), {cache_delete, Key}, ?CALL_TIMEOUT).
|
||||
|
||||
lookup(Tab, Key, F) ->
|
||||
?GEN_SERVER:call(
|
||||
get_proc_by_hash(Tab, Key), {lookup, Key, F}, ?CALL_TIMEOUT).
|
||||
|
||||
dirty_lookup(Tab, Key, F) ->
|
||||
Proc = get_proc_by_hash(Tab, Key),
|
||||
case ?GEN_SERVER:call(Proc, {cache_lookup, Key}, ?CALL_TIMEOUT) of
|
||||
{ok, '$cached_mismatch'} ->
|
||||
error;
|
||||
{ok, Val} ->
|
||||
{ok, Val};
|
||||
_ ->
|
||||
{Result, NewVal} = case F() of
|
||||
{ok, Val} ->
|
||||
{{ok, Val}, Val};
|
||||
_ ->
|
||||
{error, '$cached_mismatch'}
|
||||
end,
|
||||
?GEN_SERVER:call(
|
||||
Proc, {cache_insert, Key, NewVal}, ?CALL_TIMEOUT),
|
||||
Result
|
||||
end.
|
||||
|
||||
insert(Tab, Key, Val, F) ->
|
||||
?GEN_SERVER:call(
|
||||
get_proc_by_hash(Tab, Key), {insert, Key, Val, F}, ?CALL_TIMEOUT).
|
||||
|
||||
dirty_insert(Tab, Key, Val, F) ->
|
||||
F(),
|
||||
?GEN_SERVER:call(
|
||||
get_proc_by_hash(Tab, Key), {cache_insert, Key, Val}, ?CALL_TIMEOUT).
|
||||
|
||||
info(Tab, Info) ->
|
||||
case lists:map(
|
||||
fun(Proc) ->
|
||||
?GEN_SERVER:call(Proc, {info, Info}, ?CALL_TIMEOUT)
|
||||
end, get_all_procs(Tab)) of
|
||||
Res when Info == size ->
|
||||
{ok, lists:sum(Res)};
|
||||
Res when Info == all ->
|
||||
{ok, Res};
|
||||
Res when Info == ratio ->
|
||||
{H, M} = lists:foldl(
|
||||
fun({Hits, Miss}, {HitsAcc, MissAcc}) ->
|
||||
{HitsAcc + Hits, MissAcc + Miss}
|
||||
end, {0, 0}, Res),
|
||||
{ok, [{hits, H}, {miss, M}]};
|
||||
_ ->
|
||||
{error, badarg}
|
||||
end.
|
||||
|
||||
setopts(Tab, Opts) ->
|
||||
lists:foreach(
|
||||
fun(Proc) ->
|
||||
?GEN_SERVER:call(Proc, {setopts, Opts}, ?CALL_TIMEOUT)
|
||||
end, get_all_procs(Tab)).
|
||||
|
||||
tab2list(Tab) ->
|
||||
lists:flatmap(
|
||||
fun(Proc) ->
|
||||
?GEN_SERVER:call(Proc, tab2list, ?CALL_TIMEOUT)
|
||||
end, get_all_procs(Tab)).
|
||||
|
||||
all() ->
|
||||
lists:usort(
|
||||
[Tab || {{Tab, _}, _, _, _} <- supervisor:which_children(cache_tab_sup)]).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
init([Tab, Opts, N, Pid]) ->
|
||||
State = #state{procs_num = N,
|
||||
owner = Pid,
|
||||
name = Tab},
|
||||
{ok, do_setopts(State, Opts)}.
|
||||
|
||||
handle_call({lookup, Key, F}, _From, #state{tab = T} = State) ->
|
||||
CleanPrio = clean_priority(State#state.life_time),
|
||||
case treap:lookup(Key, T) of
|
||||
{ok, Prio, Val} when (State#state.lru == true) or (Prio =< CleanPrio) ->
|
||||
Hits = State#state.hits,
|
||||
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
||||
case Val of
|
||||
'$cached_mismatch' ->
|
||||
{reply, error, NewState};
|
||||
_ ->
|
||||
{reply, {ok, Val}, NewState}
|
||||
end;
|
||||
_ ->
|
||||
case catch F() of
|
||||
{ok, Val} ->
|
||||
Miss = State#state.miss,
|
||||
NewState = treap_insert(Key, Val, State),
|
||||
{reply, {ok, Val}, NewState#state{miss = Miss + 1}};
|
||||
{'EXIT', Reason} ->
|
||||
print_error(lookup, [Key], Reason, State),
|
||||
{reply, error, State};
|
||||
_ ->
|
||||
Miss = State#state.miss,
|
||||
NewState = State#state{miss = Miss + 1},
|
||||
if State#state.cache_missed ->
|
||||
{reply, error,
|
||||
treap_insert(Key, '$cached_mismatch', NewState)};
|
||||
true ->
|
||||
{reply, error, NewState}
|
||||
end
|
||||
end
|
||||
end;
|
||||
handle_call({cache_lookup, Key}, _From, #state{tab = T} = State) ->
|
||||
CleanPrio = clean_priority(State#state.life_time),
|
||||
case treap:lookup(Key, T) of
|
||||
{ok, Prio, Val} when (State#state.lru == true) or (Prio =< CleanPrio) ->
|
||||
Hits = State#state.hits,
|
||||
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
||||
{reply, {ok, Val}, NewState};
|
||||
_ ->
|
||||
Miss = State#state.miss,
|
||||
NewState = State#state{miss = Miss + 1},
|
||||
{reply, error, NewState}
|
||||
end;
|
||||
handle_call({insert, Key, Val, F}, _From, #state{tab = T} = State) ->
|
||||
case treap:lookup(Key, T) of
|
||||
{ok, _Prio, Val} ->
|
||||
{reply, ok, treap_update(Key, Val, State)};
|
||||
_ ->
|
||||
case catch F() of
|
||||
{'EXIT', Reason} ->
|
||||
print_error(insert, [Key, Val], Reason, State),
|
||||
{reply, ok, State};
|
||||
_ ->
|
||||
{reply, ok, treap_insert(Key, Val, State)}
|
||||
end
|
||||
end;
|
||||
handle_call({cache_insert, _, '$cached_mismatch'}, _From,
|
||||
#state{cache_missed = false} = State) ->
|
||||
{reply, ok, State};
|
||||
handle_call({cache_insert, Key, Val}, _From, State) ->
|
||||
{reply, ok, treap_insert(Key, Val, State)};
|
||||
handle_call({delete, Key, F}, _From, State) ->
|
||||
NewState = treap_delete(Key, State),
|
||||
case catch F() of
|
||||
{'EXIT', Reason} ->
|
||||
print_error(delete, [Key], Reason, State);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{reply, ok, NewState};
|
||||
handle_call({cache_delete, Key}, _From, State) ->
|
||||
NewState = treap_delete(Key, State),
|
||||
{reply, ok, NewState};
|
||||
handle_call({info, Info}, _From, State) ->
|
||||
Res = case Info of
|
||||
size ->
|
||||
State#state.size;
|
||||
ratio ->
|
||||
{State#state.hits, State#state.miss};
|
||||
all ->
|
||||
[{max_size, State#state.max_size},
|
||||
{life_time, State#state.life_time},
|
||||
{shrink_size, State#state.shrink_size},
|
||||
{size, State#state.size},
|
||||
{owner, State#state.owner},
|
||||
{hits, State#state.hits},
|
||||
{miss, State#state.miss},
|
||||
{cache_missed, State#state.cache_missed},
|
||||
{lru, State#state.lru},
|
||||
{warn, State#state.warn}];
|
||||
_ ->
|
||||
badarg
|
||||
end,
|
||||
{reply, Res, State};
|
||||
handle_call(tab2list, _From, #state{tab = T} = State) ->
|
||||
Res = treap:fold(
|
||||
fun({Key, _, Val}, Acc) ->
|
||||
[{Key, Val}|Acc]
|
||||
end, [], T),
|
||||
{reply, Res, State};
|
||||
handle_call({setopts, Opts}, _From, State) ->
|
||||
{reply, ok, do_setopts(State, Opts)};
|
||||
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) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
do_setopts(#state{procs_num = N} = State, Opts) ->
|
||||
MaxSize = case {proplists:get_value(max_size, Opts),
|
||||
State#state.max_size} of
|
||||
{MS, _} when is_integer(MS), MS > 0 ->
|
||||
round(MS/N);
|
||||
{unlimited, _} ->
|
||||
unlimited;
|
||||
{_, undefined} ->
|
||||
round(?MAX_SIZE/N);
|
||||
{_, MS} ->
|
||||
MS
|
||||
end,
|
||||
LifeTime = case {proplists:get_value(life_time, Opts),
|
||||
State#state.life_time} of
|
||||
{LT, _} when is_integer(LT), LT > 0 ->
|
||||
LT*1000*1000;
|
||||
{unlimited, _} ->
|
||||
unlimited;
|
||||
{_, undefined} ->
|
||||
?LIFETIME*1000*1000;
|
||||
{_, LT} ->
|
||||
LT
|
||||
end,
|
||||
ShrinkSize = case {proplists:get_value(shrink_size, Opts),
|
||||
State#state.shrink_size} of
|
||||
{SS, _} when is_integer(SS), SS > 0 ->
|
||||
round(SS/N);
|
||||
_ when is_integer(MaxSize) ->
|
||||
round(MaxSize/2);
|
||||
_ ->
|
||||
unlimited
|
||||
end,
|
||||
Warn = case {proplists:get_value(warn, Opts),
|
||||
State#state.warn} of
|
||||
{true, _} ->
|
||||
true;
|
||||
{false, _} ->
|
||||
false;
|
||||
{_, undefined} ->
|
||||
?WARN;
|
||||
{_, W} ->
|
||||
W
|
||||
end,
|
||||
CacheMissed = case proplists:get_value(
|
||||
cache_missed, Opts, State#state.cache_missed) of
|
||||
false ->
|
||||
false;
|
||||
true ->
|
||||
true;
|
||||
_ ->
|
||||
?CACHE_MISSED
|
||||
end,
|
||||
LRU = case proplists:get_value(
|
||||
lru, Opts, State#state.lru) of
|
||||
false ->
|
||||
false;
|
||||
true ->
|
||||
true;
|
||||
_ ->
|
||||
?LRU
|
||||
end,
|
||||
State#state{max_size = MaxSize,
|
||||
warn = Warn,
|
||||
life_time = LifeTime,
|
||||
cache_missed = CacheMissed,
|
||||
lru = LRU,
|
||||
shrink_size = ShrinkSize}.
|
||||
|
||||
get_proc_num() ->
|
||||
erlang:system_info(logical_processors).
|
||||
|
||||
get_proc_by_hash(Tab, Term) ->
|
||||
N = erlang:phash2(Term, get_proc_num()) + 1,
|
||||
get_proc(Tab, N).
|
||||
|
||||
get_proc(Tab, N) ->
|
||||
list_to_atom(atom_to_list(?PROCNAME) ++ "_" ++
|
||||
atom_to_list(Tab) ++ "_" ++ integer_to_list(N)).
|
||||
|
||||
get_all_procs(Tab) ->
|
||||
[get_proc(Tab, N) || N <- lists:seq(1, get_proc_num())].
|
||||
|
||||
now_priority() ->
|
||||
{MSec, Sec, USec} = now(),
|
||||
-((MSec*1000000 + Sec)*1000000 + USec).
|
||||
|
||||
clean_priority(LifeTime) ->
|
||||
if is_integer(LifeTime) ->
|
||||
now_priority() + LifeTime;
|
||||
true ->
|
||||
unlimited
|
||||
end.
|
||||
|
||||
treap_update(Key, Val, #state{tab = T, lru = LRU} = State) ->
|
||||
if LRU ->
|
||||
Priority = now_priority(),
|
||||
NewT = treap:insert(Key, Priority, Val, T),
|
||||
State#state{tab = NewT};
|
||||
true ->
|
||||
State
|
||||
end.
|
||||
|
||||
treap_insert(Key, Val, State) ->
|
||||
State1 = clean_treap(State),
|
||||
#state{size = Size} = State2 = shrink_treap(State1),
|
||||
T = State2#state.tab,
|
||||
case treap:lookup(Key, T) of
|
||||
{ok, _, Val} ->
|
||||
treap_update(Key, Val, State2);
|
||||
{ok, _, _} ->
|
||||
NewT = treap:insert(Key, now_priority(), Val, T),
|
||||
State2#state{tab = NewT};
|
||||
_ ->
|
||||
NewT = treap:insert(Key, now_priority(), Val, T),
|
||||
State2#state{tab = NewT, size = Size+1}
|
||||
end.
|
||||
|
||||
treap_delete(Key, #state{tab = T, size = Size} = State) ->
|
||||
case treap:lookup(Key, T) of
|
||||
{ok, _, _} ->
|
||||
NewT = treap:delete(Key, T),
|
||||
clean_treap(State#state{tab = NewT, size = Size-1});
|
||||
_ ->
|
||||
State
|
||||
end.
|
||||
|
||||
clean_treap(#state{tab = T, size = Size, life_time = LifeTime} = State) ->
|
||||
if is_integer(LifeTime) ->
|
||||
Priority = now_priority(),
|
||||
{Cleaned, NewT} = clean_treap(T, Priority + LifeTime, 0),
|
||||
State#state{size = Size - Cleaned, tab = NewT};
|
||||
true ->
|
||||
State
|
||||
end.
|
||||
|
||||
clean_treap(Treap, CleanPriority, N) ->
|
||||
case treap:is_empty(Treap) of
|
||||
true ->
|
||||
{N, Treap};
|
||||
false ->
|
||||
{_Key, Priority, _Value} = treap:get_root(Treap),
|
||||
if Priority > CleanPriority ->
|
||||
clean_treap(treap:delete_root(Treap), CleanPriority, N+1);
|
||||
true ->
|
||||
{N, Treap}
|
||||
end
|
||||
end.
|
||||
|
||||
shrink_treap(#state{tab = T,
|
||||
max_size = MaxSize,
|
||||
shrink_size = ShrinkSize,
|
||||
warn = Warn,
|
||||
size = Size} = State) when Size >= MaxSize ->
|
||||
if Warn ->
|
||||
?WARNING_MSG("shrinking table:~n"
|
||||
"** Table: ~p~n"
|
||||
"** Processes Number: ~p~n"
|
||||
"** Max Size: ~p items~n"
|
||||
"** Shrink Size: ~p items~n"
|
||||
"** Life Time: ~p microseconds~n"
|
||||
"** Hits/Miss: ~p/~p~n"
|
||||
"** Owner: ~p~n"
|
||||
"** Cache Missed: ~p~n"
|
||||
"** Instruction: you have to tune cacheing options"
|
||||
" if this message repeats too frequently",
|
||||
[State#state.name, State#state.procs_num,
|
||||
MaxSize, ShrinkSize, State#state.life_time,
|
||||
State#state.hits, State#state.miss,
|
||||
State#state.owner, State#state.cache_missed]);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{Shrinked, NewT} = shrink_treap(T, ShrinkSize, 0),
|
||||
State#state{tab = NewT, size = Size - Shrinked};
|
||||
shrink_treap(State) ->
|
||||
State.
|
||||
|
||||
shrink_treap(T, ShrinkSize, ShrinkSize) ->
|
||||
{ShrinkSize, T};
|
||||
shrink_treap(T, ShrinkSize, N) ->
|
||||
case treap:is_empty(T) of
|
||||
true ->
|
||||
{N, T};
|
||||
false ->
|
||||
shrink_treap(treap:delete_root(T), ShrinkSize, N+1)
|
||||
end.
|
||||
|
||||
print_error(Operation, Args, Reason, State) ->
|
||||
?ERROR_MSG("callback failed:~n"
|
||||
"** Tab: ~p~n"
|
||||
"** Owner: ~p~n"
|
||||
"** Operation: ~p~n"
|
||||
"** Args: ~p~n"
|
||||
"** Reason: ~p",
|
||||
[State#state.name, State#state.owner,
|
||||
Operation, Args, Reason]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Tests
|
||||
%%--------------------------------------------------------------------
|
||||
-define(lookup, dirty_lookup).
|
||||
-define(delete, dirty_delete).
|
||||
-define(insert, dirty_insert).
|
||||
%%-define(lookup, lookup).
|
||||
%%-define(delete, delete).
|
||||
%%-define(insert, insert).
|
||||
|
||||
test() ->
|
||||
LifeTime = 2,
|
||||
ok = new(test_tbl, [{life_time, LifeTime}, {max_size, unlimited}]),
|
||||
check([]),
|
||||
ok = ?insert(test_tbl, "key", "value", fun() -> ok end),
|
||||
check([{"key", "value"}]),
|
||||
{ok, "value"} = ?lookup(test_tbl, "key", fun() -> error end),
|
||||
check([{"key", "value"}]),
|
||||
io:format("** waiting for ~p seconds to check if LRU works fine...~n",
|
||||
[LifeTime+1]),
|
||||
timer:sleep(timer:seconds(LifeTime+1)),
|
||||
ok = ?insert(test_tbl, "key1", "value1", fun() -> ok end),
|
||||
check([{"key1", "value1"}]),
|
||||
ok = ?delete(test_tbl, "key1", fun() -> ok end),
|
||||
{ok, "value"} = ?lookup(test_tbl, "key", fun() -> {ok, "value"} end),
|
||||
check([{"key", "value"}]),
|
||||
ok = ?delete(test_tbl, "key", fun() -> ok end),
|
||||
check([]),
|
||||
%% io:format("** testing buggy callbacks...~n"),
|
||||
%% delete(test_tbl, "key", fun() -> erlang:error(badarg) end),
|
||||
%% insert(test_tbl, "key", "val", fun() -> erlang:error(badarg) end),
|
||||
%% lookup(test_tbl, "key", fun() -> erlang:error(badarg) end),
|
||||
check([]),
|
||||
delete(test_tbl),
|
||||
test1().
|
||||
|
||||
test1() ->
|
||||
MaxSize = 10,
|
||||
ok = new(test_tbl, [{max_size, MaxSize}, {shrink_size, 1}, {warn, false}]),
|
||||
lists:foreach(
|
||||
fun(N) ->
|
||||
ok = ?insert(test_tbl, N, N, fun() -> ok end)
|
||||
end, lists:seq(1, MaxSize*get_proc_num())),
|
||||
{ok, MaxSize} = info(test_tbl, size),
|
||||
delete(test_tbl),
|
||||
test2().
|
||||
|
||||
test2() ->
|
||||
LifeTime = 2,
|
||||
ok = new(test_tbl, [{life_time, LifeTime},
|
||||
{max_size, unlimited},
|
||||
{lru, false}]),
|
||||
check([]),
|
||||
ok = ?insert(test_tbl, "key", "value", fun() -> ok end),
|
||||
{ok, "value"} = ?lookup(test_tbl, "key", fun() -> error end),
|
||||
check([{"key", "value"}]),
|
||||
io:format("** waiting for ~p seconds to check if non-LRU works fine...~n",
|
||||
[LifeTime+1]),
|
||||
timer:sleep(timer:seconds(LifeTime+1)),
|
||||
error = ?lookup(test_tbl, "key", fun() -> error end),
|
||||
check([{"key", '$cached_mismatch'}]),
|
||||
ok = ?insert(test_tbl, "key", "value1", fun() -> ok end),
|
||||
check([{"key", "value1"}]),
|
||||
delete(test_tbl),
|
||||
io:format("** testing speed, this may take a while...~n"),
|
||||
test3(1000),
|
||||
test3(10000),
|
||||
test3(100000),
|
||||
test3(1000000).
|
||||
|
||||
test3(Iter) ->
|
||||
ok = new(test_tbl, [{max_size, unlimited}, {life_time, unlimited}]),
|
||||
L = lists:seq(1, Iter),
|
||||
T1 = now(),
|
||||
lists:foreach(
|
||||
fun(N) ->
|
||||
ok = ?insert(test_tbl, N, N, fun() -> ok end)
|
||||
end, L),
|
||||
io:format("** average insert (size = ~p): ~p usec~n",
|
||||
[Iter, round(timer:now_diff(now(), T1)/Iter)]),
|
||||
T2 = now(),
|
||||
lists:foreach(
|
||||
fun(N) ->
|
||||
{ok, N} = ?lookup(test_tbl, N, fun() -> ok end)
|
||||
end, L),
|
||||
io:format("** average lookup (size = ~p): ~p usec~n",
|
||||
[Iter, round(timer:now_diff(now(), T2)/Iter)]),
|
||||
{ok, Iter} = info(test_tbl, size),
|
||||
delete(test_tbl).
|
||||
|
||||
check(List) ->
|
||||
Size = length(List),
|
||||
{ok, Size} = info(test_tbl, size),
|
||||
List = tab2list(test_tbl).
|
||||
@@ -0,0 +1,53 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : cache_tab_sup.erl
|
||||
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% Description : Cache tables supervisor
|
||||
%%%
|
||||
%%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2011 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., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(cache_tab_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
%%====================================================================
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
|
||||
|
||||
%%====================================================================
|
||||
%% Supervisor callbacks
|
||||
%%====================================================================
|
||||
init([]) ->
|
||||
{ok, {{one_for_one,10,1}, []}}.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
Vendored
+413
-311
File diff suppressed because it is too large
Load Diff
+14
-2
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.53)
|
||||
AC_INIT(ejabberd.erl, version, [ejabberd@process-one.net], [ejabberd])
|
||||
AC_INIT(ejabberd, m4_esyscmd([grep -o -E "\{vsn,.\".*\"\}" ejabberd.app | cut -d \" -f 2 | tr -d '\n']), [ejabberd@process-one.net], [ejabberd])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@@ -81,7 +81,7 @@ AC_ARG_ENABLE(transient_supervisors,
|
||||
[case "${enableval}" in
|
||||
yes) transient_supervisors=true ;;
|
||||
no) transient_supervisors=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-transient_supervisors) ;;
|
||||
esac],[transient_supervisors=true])
|
||||
AC_SUBST(transient_supervisors)
|
||||
|
||||
@@ -94,6 +94,15 @@ AC_ARG_ENABLE(full_xml,
|
||||
esac],[full_xml=false])
|
||||
AC_SUBST(full_xml)
|
||||
|
||||
AC_ARG_ENABLE(nif,
|
||||
[AC_HELP_STRING([--enable-nif], [replace some functions with C equivalents. Requires Erlang R13B04 or higher (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) nif=true ;;
|
||||
no) nif=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-nif) ;;
|
||||
esac],[nif=false])
|
||||
AC_SUBST(nif)
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
@@ -139,6 +148,9 @@ if test "$ENABLEUSER" != ""; then
|
||||
AC_SUBST([INSTALLUSER], [$ENABLEUSER])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(openssl/md2.h, md2=true, md2=false)
|
||||
AC_SUBST(md2)
|
||||
|
||||
AC_CANONICAL_SYSTEM
|
||||
#AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target")
|
||||
#AC_SUBST(target_os)
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+2
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
{application, ejabberd,
|
||||
[{description, "ejabberd"},
|
||||
{vsn, "2.1.3"},
|
||||
{vsn, "2.1.7"},
|
||||
{modules, [acl,
|
||||
adhoc,
|
||||
configure,
|
||||
@@ -115,6 +115,7 @@
|
||||
nodetree_virtual,
|
||||
p1_fsm,
|
||||
p1_mnesia,
|
||||
p1_prof,
|
||||
randoms,
|
||||
sha,
|
||||
shaper,
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
captcha,
|
||||
http_bind,
|
||||
http_poll,
|
||||
%%register,
|
||||
web_admin
|
||||
]}
|
||||
|
||||
@@ -169,10 +170,10 @@
|
||||
|
||||
%%
|
||||
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
|
||||
%% Allowed values are: true or false.
|
||||
%% Allowed values are: false optional required required_trusted
|
||||
%% You must specify a certificate file.
|
||||
%%
|
||||
%%{s2s_use_starttls, true}.
|
||||
%%{s2s_use_starttls, optional}.
|
||||
|
||||
%%
|
||||
%% s2s_certfile: Specify a certificate file.
|
||||
@@ -474,10 +475,14 @@
|
||||
%%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
|
||||
|
||||
%%
|
||||
%% Host part of the URL sent to the user.
|
||||
%% 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
|
||||
@@ -489,6 +494,7 @@
|
||||
[
|
||||
{mod_adhoc, []},
|
||||
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
|
||||
{mod_blocking,[]}, % requires mod_privacy
|
||||
{mod_caps, []},
|
||||
{mod_configure,[]}, % requires mod_adhoc
|
||||
{mod_disco, []},
|
||||
@@ -510,6 +516,7 @@
|
||||
%%{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,[]},
|
||||
@@ -521,6 +528,16 @@
|
||||
{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.
|
||||
@@ -534,8 +551,26 @@
|
||||
%%
|
||||
%%{registration_watchers, ["admin1@example.org"]},
|
||||
|
||||
%%
|
||||
%% Only clients in the server machine can register accounts
|
||||
%%
|
||||
{ip_access, [{allow, "127.0.0.0/8"},
|
||||
{deny, "0.0.0.0/0"}]},
|
||||
|
||||
%%
|
||||
%% Local c2s or remote s2s users cannot register accounts
|
||||
%%
|
||||
%%{access_from, deny},
|
||||
|
||||
{access, register}
|
||||
]},
|
||||
%%{mod_register_web, [
|
||||
%%
|
||||
%% When a user registers, send a notification to
|
||||
%% these XMPP accounts.
|
||||
%%
|
||||
%%{registration_watchers, ["admin1@example.org"]}
|
||||
%% ]},
|
||||
{mod_roster, []},
|
||||
%%{mod_service_log,[]},
|
||||
{mod_shared_roster,[]},
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
#! /bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: ejabberd
|
||||
# Required-Start: $remote_fs $network $named $time
|
||||
# Required-Stop: $remote_fs $network $named $time
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Starts ejabberd jabber server
|
||||
# Description: Starts ejabberd jabber server, an XMPP
|
||||
# compliant server written in Erlang.
|
||||
### END INIT INFO
|
||||
|
||||
# chkconfig: 2345 90 10
|
||||
# description: ejabberd XMPP server
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
DIR=@ctlscriptpath@
|
||||
CTL="$DIR"/ejabberdctl
|
||||
@@ -32,14 +46,17 @@ case "$1" in
|
||||
su - $USER -c "$CTL stopped"
|
||||
echo "done."
|
||||
;;
|
||||
|
||||
status)
|
||||
test -x "$CTL" || exit 0
|
||||
echo "Getting ejabberd status..."
|
||||
su - $USER -c "$CTL status"
|
||||
;;
|
||||
force-reload|restart)
|
||||
"$0" stop
|
||||
"$0" start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload}"
|
||||
echo "Usage: $0 {start|stop|restart|force-reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
|
||||
+37
-3
@@ -5,7 +5,7 @@
|
||||
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -31,6 +31,8 @@
|
||||
%% Server
|
||||
status/0, reopen_log/0,
|
||||
stop_kindly/2, send_service_message_all_mucs/2,
|
||||
%% Erlang
|
||||
update_list/0, update/1,
|
||||
%% Accounts
|
||||
register/3, unregister/2,
|
||||
registered_users/1,
|
||||
@@ -95,6 +97,17 @@ commands() ->
|
||||
{leveldesc, string}
|
||||
]}}},
|
||||
|
||||
#ejabberd_commands{name = update_list, tags = [server],
|
||||
desc = "List modified modules that can be updated",
|
||||
module = ?MODULE, function = update_list,
|
||||
args = [],
|
||||
result = {modules, {list, {module, string}}}},
|
||||
#ejabberd_commands{name = update, tags = [server],
|
||||
desc = "Update the given module, or use the keyword: all",
|
||||
module = ?MODULE, function = update,
|
||||
args = [{module, string}],
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = register, tags = [accounts],
|
||||
desc = "Register a user",
|
||||
module = ?MODULE, function = register,
|
||||
@@ -150,7 +163,7 @@ commands() ->
|
||||
|
||||
#ejabberd_commands{name = set_master, tags = [mnesia],
|
||||
desc = "Set master node of the clustered Mnesia tables",
|
||||
longdesc = "If you provie as nodename \"self\", this "
|
||||
longdesc = "If you provide as nodename \"self\", this "
|
||||
"node will be set as its own master.",
|
||||
module = ?MODULE, function = set_master,
|
||||
args = [{nodename, string}], result = {res, restuple}},
|
||||
@@ -276,6 +289,27 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
end,
|
||||
?MYHOSTS).
|
||||
|
||||
%%%
|
||||
%%% ejabberd_update
|
||||
%%%
|
||||
|
||||
update_list() ->
|
||||
{ok, _Dir, UpdatedBeams, _Script, _LowLevelScript, _Check} =
|
||||
ejabberd_update:update_info(),
|
||||
[atom_to_list(Beam) || Beam <- UpdatedBeams].
|
||||
|
||||
update("all") ->
|
||||
[update_module(ModStr) || ModStr <- update_list()];
|
||||
update(ModStr) ->
|
||||
update_module(ModStr).
|
||||
|
||||
update_module(ModuleNameString) ->
|
||||
ModuleName = list_to_atom(ModuleNameString),
|
||||
case ejabberd_update:update([ModuleName]) of
|
||||
{ok, Res} -> {ok, io_lib:format("Updated: ~p", [Res])};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Account management
|
||||
%%%
|
||||
@@ -283,7 +317,7 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
register(User, Host, Password) ->
|
||||
case ejabberd_auth:try_register(User, Host, Password) of
|
||||
{atomic, ok} ->
|
||||
{ok, io_lib:format("User ~s@~s succesfully registered", [User, Host])};
|
||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||
{atomic, exists} ->
|
||||
String = io_lib:format("User ~s@~s already registered at node ~p",
|
||||
[User, Host, node()]),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -46,6 +46,7 @@ start(normal, _Args) ->
|
||||
db_init(),
|
||||
sha:start(),
|
||||
stringprep_sup:start_link(),
|
||||
xml:start(),
|
||||
start(),
|
||||
translate:start(),
|
||||
acl:start(),
|
||||
@@ -56,6 +57,8 @@ start(normal, _Args) ->
|
||||
ejabberd_config:start(),
|
||||
ejabberd_check:config(),
|
||||
connect_nodes(),
|
||||
%% Loading ASN.1 driver explicitly to avoid races in LDAP
|
||||
catch asn1rt:load_driver(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_rdbms:start(),
|
||||
ejabberd_auth:start(),
|
||||
@@ -85,7 +88,7 @@ prep_stop(State) ->
|
||||
stop(_State) ->
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
|
||||
delete_pid_file(),
|
||||
ejabberd_debug:stop(),
|
||||
%%ejabberd_debug:stop(),
|
||||
ok.
|
||||
|
||||
|
||||
|
||||
+36
-3
@@ -5,7 +5,7 @@
|
||||
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -49,7 +49,8 @@
|
||||
is_user_exists_in_other_modules/3,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/1
|
||||
plain_password_required/1,
|
||||
entropy/1
|
||||
]).
|
||||
|
||||
-export([auth_modules/1]).
|
||||
@@ -251,7 +252,16 @@ get_password_with_authmodule(User, Server) ->
|
||||
is_user_exists(User, Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:is_user_exists(User, Server)
|
||||
case M:is_user_exists(User, Server) of
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("The authentication module ~p returned an "
|
||||
"error~nwhen checking user ~p in server ~p~n"
|
||||
"Error message: ~p",
|
||||
[M, User, Server, Error]),
|
||||
false;
|
||||
Else ->
|
||||
Else
|
||||
end
|
||||
end, auth_modules(Server)).
|
||||
|
||||
%% Check if the user exists in all authentications module except the module
|
||||
@@ -309,6 +319,29 @@ remove_user(User, Server, Password) ->
|
||||
end,
|
||||
R.
|
||||
|
||||
%% @spec (IOList) -> non_negative_float()
|
||||
%% @doc Calculate informational entropy.
|
||||
entropy(IOList) ->
|
||||
case binary_to_list(iolist_to_binary(IOList)) of
|
||||
"" ->
|
||||
0.0;
|
||||
S ->
|
||||
Set = lists:foldl(
|
||||
fun(C, [Digit, Printable, LowLetter, HiLetter, Other]) ->
|
||||
if C >= $a, C =< $z ->
|
||||
[Digit, Printable, 26, HiLetter, Other];
|
||||
C >= $0, C =< $9 ->
|
||||
[9, Printable, LowLetter, HiLetter, Other];
|
||||
C >= $A, C =< $Z ->
|
||||
[Digit, Printable, LowLetter, 26, Other];
|
||||
C >= 16#21, C =< 16#7e ->
|
||||
[Digit, 33, LowLetter, HiLetter, Other];
|
||||
true ->
|
||||
[Digit, Printable, LowLetter, HiLetter, 128]
|
||||
end
|
||||
end, [0, 0, 0, 0, 0], S),
|
||||
length(S) * math:log(lists:sum(Set))/math:log(2)
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -143,6 +143,7 @@ register_connection(SID, #jid{luser = LUser, lserver = LServer}, Info) ->
|
||||
AuthModule = xml:get_attr_s(auth_module, Info),
|
||||
case AuthModule == ?MODULE of
|
||||
true ->
|
||||
ejabberd_hooks:run(register_user, LServer, [LUser, LServer]),
|
||||
US = {LUser, LServer},
|
||||
mnesia:sync_dirty(
|
||||
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
||||
@@ -215,8 +216,8 @@ try_register(_User, _Server, _Password) ->
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
get_vh_registered_users(Server) ->
|
||||
[{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)].
|
||||
|
||||
|
||||
%% Return password of permanent user or false for anonymous users
|
||||
|
||||
+236
-19
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -35,6 +35,9 @@
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
@@ -43,45 +46,87 @@
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
extauth:start(
|
||||
Host, ejabberd_config:get_local_option({extauth_program, Host})),
|
||||
ok.
|
||||
case check_cache_last_options(Host) of
|
||||
cache ->
|
||||
ok = ejabberd_auth_internal:start(Host);
|
||||
no_cache ->
|
||||
ok
|
||||
end.
|
||||
|
||||
check_cache_last_options(Server) ->
|
||||
%% if extauth_cache is enabled, then a mod_last module must also be enabled
|
||||
case get_cache_option(Server) of
|
||||
false -> no_cache;
|
||||
{true, _CacheTime} ->
|
||||
case get_mod_last_configured(Server) of
|
||||
no_mod_last ->
|
||||
?ERROR_MSG("In host ~p extauth is used, extauth_cache is enabled but "
|
||||
"mod_last is not enabled.", [Server]),
|
||||
no_cache;
|
||||
_ -> cache
|
||||
end
|
||||
end.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso Password /= "".
|
||||
case get_cache_option(Server) of
|
||||
false -> check_password_extauth(User, Server, Password);
|
||||
{true, CacheTime} -> check_password_cache(User, Server, Password, CacheTime)
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
case extauth:set_password(User, Server, Password) of
|
||||
true -> ok;
|
||||
true -> set_password_internal(User, Server, Password),
|
||||
ok;
|
||||
_ -> {error, unknown_problem}
|
||||
end.
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
try_register(User, Server, Password) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> try_register_extauth(User, Server, Password);
|
||||
{true, _CacheTime} -> try_register_external_cache(User, Server, Password)
|
||||
end.
|
||||
|
||||
%% TODO
|
||||
%% Return the list of all users handled by external
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
ejabberd_auth_internal:dirty_get_registered_users().
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
get_vh_registered_users(Server) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users(Server).
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
get_vh_registered_users(Server, Data) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users(Server, Data).
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
get_vh_registered_users_number(Server) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users_number(Server).
|
||||
|
||||
get_vh_registered_users_number(Server, Data) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users_number(Server, Data).
|
||||
|
||||
%% The password can only be returned if cache is enabled, cached info exists and is fresh enough.
|
||||
get_password(User, Server) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, CacheTime} -> get_password_cache(User, Server, CacheTime)
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case get_password(User, Server) of
|
||||
false -> [];
|
||||
Other -> Other
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
@@ -91,9 +136,181 @@ is_user_exists(User, Server) ->
|
||||
_:Error -> {error, Error}
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
remove_user(User, Server) ->
|
||||
case extauth:remove_user(User, Server) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_internal:remove_user(User, Server)
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
remove_user(User, Server, Password) ->
|
||||
case extauth:remove_user(User, Server, Password) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_internal:remove_user(User, Server, Password)
|
||||
end
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Extauth cache management
|
||||
%%%
|
||||
|
||||
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
||||
get_cache_option(Host) ->
|
||||
case ejabberd_config:get_local_option({extauth_cache, Host}) of
|
||||
CacheTime when is_integer(CacheTime) -> {true, CacheTime};
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
check_password_extauth(User, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso Password /= "".
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
try_register_extauth(User, Server, Password) ->
|
||||
extauth:try_register(User, Server, Password).
|
||||
|
||||
check_password_cache(User, Server, Password, CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online ->
|
||||
check_password_internal(User, Server, Password);
|
||||
never ->
|
||||
check_password_external_cache(User, Server, Password);
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled but mod_last is not enabled in that host", []),
|
||||
check_password_external_cache(User, Server, Password);
|
||||
TimeStamp ->
|
||||
%% If last access exists, compare last access with cache refresh time
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
%% If no need to refresh, check password against Mnesia
|
||||
true ->
|
||||
case check_password_internal(User, Server, Password) of
|
||||
%% If password valid in Mnesia, accept it
|
||||
true ->
|
||||
true;
|
||||
%% Else (password nonvalid in Mnesia), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, Server, Password)
|
||||
end;
|
||||
%% Else (need to refresh), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, Server, Password)
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_internal(User, Server) ->
|
||||
ejabberd_auth_internal:get_password(User, Server).
|
||||
|
||||
%% @spec (User, Server, CacheTime) -> false | Password::string()
|
||||
get_password_cache(User, Server, CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online ->
|
||||
get_password_internal(User, Server);
|
||||
never ->
|
||||
false;
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled but mod_last is not enabled in that host", []),
|
||||
false;
|
||||
TimeStamp ->
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
true ->
|
||||
get_password_internal(User, Server);
|
||||
false ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%% Check the password using extauth; if success then cache it
|
||||
check_password_external_cache(User, Server, Password) ->
|
||||
case check_password_extauth(User, Server, Password) of
|
||||
true ->
|
||||
set_password_internal(User, Server, Password), true;
|
||||
false ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% Try to register using extauth; if success then cache it
|
||||
try_register_external_cache(User, Server, Password) ->
|
||||
case try_register_extauth(User, Server, Password) of
|
||||
{atomic, ok} = R ->
|
||||
set_password_internal(User, Server, Password),
|
||||
R;
|
||||
_ -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
check_password_internal(User, Server, Password) ->
|
||||
ejabberd_auth_internal:check_password(User, Server, Password).
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
||||
set_password_internal(User, Server, Password) ->
|
||||
ejabberd_auth_internal:set_password(User, Server, Password).
|
||||
|
||||
%% @spec (TimeLast, CacheTime) -> true | false
|
||||
%% TimeLast = online | never | integer()
|
||||
%% CacheTime = integer() | false
|
||||
is_fresh_enough(online, _CacheTime) ->
|
||||
true;
|
||||
is_fresh_enough(never, _CacheTime) ->
|
||||
false;
|
||||
is_fresh_enough(TimeStampLast, CacheTime) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
Now = MegaSecs * 1000000 + Secs,
|
||||
(TimeStampLast + CacheTime > Now).
|
||||
|
||||
%% @spec (User, Server) -> online | never | mod_last_required | TimeStamp::integer()
|
||||
%% Code copied from mod_configure.erl
|
||||
%% Code copied from web/ejabberd_web_admin.erl
|
||||
%% TODO: Update time format to XEP-0202: Entity Time
|
||||
get_last_access(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
_US = {User, Server},
|
||||
case get_last_info(User, Server) of
|
||||
mod_last_required ->
|
||||
mod_last_required;
|
||||
not_found ->
|
||||
never;
|
||||
{ok, Timestamp, _Status} ->
|
||||
Timestamp
|
||||
end;
|
||||
_ ->
|
||||
online
|
||||
end.
|
||||
%% @spec (User, Server) -> {ok, Timestamp, Status} | not_found | mod_last_required
|
||||
get_last_info(User, Server) ->
|
||||
case get_mod_last_enabled(Server) of
|
||||
mod_last -> mod_last:get_last_info(User, Server);
|
||||
mod_last_odbc -> mod_last_odbc:get_last_info(User, Server);
|
||||
no_mod_last -> mod_last_required
|
||||
end.
|
||||
|
||||
%% @spec (Server) -> mod_last | mod_last_odbc | no_mod_last
|
||||
get_mod_last_enabled(Server) ->
|
||||
ML = gen_mod:is_loaded(Server, mod_last),
|
||||
MLO = gen_mod:is_loaded(Server, mod_last_odbc),
|
||||
case {ML, MLO} of
|
||||
{true, _} -> mod_last;
|
||||
{false, true} -> mod_last_odbc;
|
||||
{false, false} -> no_mod_last
|
||||
end.
|
||||
|
||||
get_mod_last_configured(Server) ->
|
||||
ML = is_configured(Server, mod_last),
|
||||
MLO = is_configured(Server, mod_last_odbc),
|
||||
case {ML, MLO} of
|
||||
{true, _} -> mod_last;
|
||||
{false, true} -> mod_last_odbc;
|
||||
{false, false} -> no_mod_last
|
||||
end.
|
||||
|
||||
is_configured(Host, Module) ->
|
||||
lists:keymember(Module, 1, ejabberd_config:get_local_option({modules, Host})).
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+25
-17
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -66,7 +66,7 @@
|
||||
servers,
|
||||
backups,
|
||||
port,
|
||||
encrypt,
|
||||
tls_options,
|
||||
dn,
|
||||
password,
|
||||
base,
|
||||
@@ -119,19 +119,19 @@ terminate(_Reason, _State) ->
|
||||
init(Host) ->
|
||||
State = parse_options(Host),
|
||||
eldap_pool:start_link(State#state.eldap_id,
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.encrypt),
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.tls_options),
|
||||
eldap_pool:start_link(State#state.bind_eldap_id,
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.encrypt),
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.tls_options),
|
||||
{ok, State}.
|
||||
|
||||
plain_password_required() ->
|
||||
@@ -153,8 +153,14 @@ check_password(User, Server, Password) ->
|
||||
check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
set_password(User, Server, Password) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
case find_user_dn(User, State) of
|
||||
false ->
|
||||
{error, user_not_found};
|
||||
DN ->
|
||||
eldap_pool:modify_passwd(State#state.eldap_id, DN, Password)
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {error, not_allowed}
|
||||
try_register(_User, _Server, _Password) ->
|
||||
@@ -367,6 +373,7 @@ parse_options(Host) ->
|
||||
Backups -> Backups
|
||||
end,
|
||||
LDAPEncrypt = ejabberd_config:get_local_option({ldap_encrypt, Host}),
|
||||
LDAPTLSVerify = ejabberd_config:get_local_option({ldap_tls_verify, Host}),
|
||||
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
|
||||
undefined -> case LDAPEncrypt of
|
||||
tls -> ?LDAPS_PORT;
|
||||
@@ -411,7 +418,8 @@ parse_options(Host) ->
|
||||
servers = LDAPServers,
|
||||
backups = LDAPBackups,
|
||||
port = LDAPPort,
|
||||
encrypt = LDAPEncrypt,
|
||||
tls_options = [{encrypt, LDAPEncrypt},
|
||||
{tls_verify, LDAPTLSVerify}],
|
||||
dn = RootDN,
|
||||
password = Password,
|
||||
base = LDAPBase,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -60,7 +60,11 @@ check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
|
||||
check_password(User, Host, Password) ->
|
||||
Service = get_pam_service(Host),
|
||||
case catch epam:authenticate(Service, User, Password) of
|
||||
UserInfo = case get_pam_userinfotype(Host) of
|
||||
username -> User;
|
||||
jid -> User++"@"++Host
|
||||
end,
|
||||
case catch epam:authenticate(Service, UserInfo, Password) of
|
||||
true -> true;
|
||||
_ -> false
|
||||
end.
|
||||
@@ -84,7 +88,11 @@ get_password_s(_User, _Server) ->
|
||||
%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
|
||||
is_user_exists(User, Host) ->
|
||||
Service = get_pam_service(Host),
|
||||
case catch epam:acct_mgmt(Service, User) of
|
||||
UserInfo = case get_pam_userinfotype(Host) of
|
||||
username -> User;
|
||||
jid -> User++"@"++Host
|
||||
end,
|
||||
case catch epam:acct_mgmt(Service, UserInfo) of
|
||||
true -> true;
|
||||
_ -> false
|
||||
end.
|
||||
@@ -106,3 +114,8 @@ get_pam_service(Host) ->
|
||||
undefined -> "ejabberd";
|
||||
Service -> Service
|
||||
end.
|
||||
get_pam_userinfotype(Host) ->
|
||||
case ejabberd_config:get_local_option({pam_userinfotype, Host}) of
|
||||
undefined -> username;
|
||||
Type -> Type
|
||||
end.
|
||||
|
||||
+323
-221
@@ -5,7 +5,7 @@
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -40,6 +40,11 @@
|
||||
send_element/2,
|
||||
socket_type/0,
|
||||
get_presence/1,
|
||||
get_aux_field/2,
|
||||
set_aux_field/3,
|
||||
del_aux_field/2,
|
||||
get_subscription/2,
|
||||
broadcast/4,
|
||||
get_subscribed/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
@@ -55,7 +60,9 @@
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
terminate/3,
|
||||
print_state/1
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@@ -94,6 +101,7 @@
|
||||
conn = unknown,
|
||||
auth_module = unknown,
|
||||
ip,
|
||||
aux_fields = [],
|
||||
lang}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
@@ -152,6 +160,39 @@ socket_type() ->
|
||||
get_presence(FsmRef) ->
|
||||
?GEN_FSM:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
|
||||
|
||||
get_aux_field(Key, #state{aux_fields = Opts}) ->
|
||||
case lists:keysearch(Key, 1, Opts) of
|
||||
{value, {_, Val}} ->
|
||||
{ok, Val};
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
set_aux_field(Key, Val, #state{aux_fields = Opts} = State) ->
|
||||
Opts1 = lists:keydelete(Key, 1, Opts),
|
||||
State#state{aux_fields = [{Key, Val}|Opts1]}.
|
||||
|
||||
del_aux_field(Key, #state{aux_fields = Opts} = State) ->
|
||||
Opts1 = lists:keydelete(Key, 1, Opts),
|
||||
State#state{aux_fields = Opts1}.
|
||||
|
||||
get_subscription(From = #jid{}, StateData) ->
|
||||
get_subscription(jlib:jid_tolower(From), StateData);
|
||||
get_subscription(LFrom, StateData) ->
|
||||
LBFrom = setelement(3, LFrom, ""),
|
||||
F = ?SETS:is_element(LFrom, StateData#state.pres_f) orelse
|
||||
?SETS:is_element(LBFrom, StateData#state.pres_f),
|
||||
T = ?SETS:is_element(LFrom, StateData#state.pres_t) orelse
|
||||
?SETS:is_element(LBFrom, StateData#state.pres_t),
|
||||
if F and T -> both;
|
||||
F -> from;
|
||||
T -> to;
|
||||
true -> none
|
||||
end.
|
||||
|
||||
broadcast(FsmRef, Type, From, Packet) ->
|
||||
FsmRef ! {broadcast, Type, From, Packet}.
|
||||
|
||||
stop(FsmRef) ->
|
||||
?GEN_FSM:send_event(FsmRef, closed).
|
||||
|
||||
@@ -194,8 +235,8 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
%% Check if IP is blacklisted:
|
||||
case is_ip_blacklisted(IP) of
|
||||
true ->
|
||||
?INFO_MSG("Connection attempt from blacklisted IP: ~s",
|
||||
[jlib:ip_to_list(IP)]),
|
||||
?INFO_MSG("Connection attempt from blacklisted IP: ~s (~w)",
|
||||
[jlib:ip_to_list(IP), IP]),
|
||||
{stop, normal};
|
||||
false ->
|
||||
Socket1 =
|
||||
@@ -245,7 +286,19 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
Server = jlib:nameprep(xml:get_attr_s("to", Attrs)),
|
||||
case lists:member(Server, ?MYHOSTS) of
|
||||
true ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
Lang = case xml:get_attr_s("xml:lang", Attrs) of
|
||||
Lang1 when length(Lang1) =< 35 ->
|
||||
%% As stated in BCP47, 4.4.1:
|
||||
%% Protocols or specifications that
|
||||
%% specify limited buffer sizes for
|
||||
%% language tags MUST allow for
|
||||
%% language tags of at least 35 characters.
|
||||
Lang1;
|
||||
_ ->
|
||||
%% Do not store long language tag to
|
||||
%% avoid possible DoS/flood attacks
|
||||
""
|
||||
end,
|
||||
change_shaper(StateData, jlib:make_jid("", Server, "")),
|
||||
case xml:get_attr_s("version", Attrs) of
|
||||
"1.0" ->
|
||||
@@ -315,10 +368,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs}] ++
|
||||
ejabberd_hooks:run_fold(
|
||||
c2s_stream_features,
|
||||
Server,
|
||||
[], [])}),
|
||||
ejabberd_hooks:run_fold(
|
||||
c2s_stream_features,
|
||||
Server,
|
||||
[], [Server])}),
|
||||
fsm_next_state(wait_for_feature_request,
|
||||
StateData#state{
|
||||
server = Server,
|
||||
@@ -327,11 +380,20 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
_ ->
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
RosterVersioningFeature = ejabberd_hooks:run_fold(roster_get_versioning_feature, Server, [], [Server]),
|
||||
StreamFeatures = [{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []} | RosterVersioningFeature],
|
||||
RosterVersioningFeature =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_versioning_feature,
|
||||
Server, [], [Server]),
|
||||
StreamFeatures =
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []}]
|
||||
++ RosterVersioningFeature
|
||||
++ ejabberd_hooks:run_fold(
|
||||
c2s_stream_features,
|
||||
Server,
|
||||
[], [Server]),
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
@@ -616,7 +678,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
Socket = StateData#state.socket,
|
||||
TLSSocket = (StateData#state.sockmod):starttls(
|
||||
Socket, TLSOpts,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{socket = TLSSocket,
|
||||
@@ -639,7 +701,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
Socket = StateData#state.socket,
|
||||
ZlibSocket = (StateData#state.sockmod):compress(
|
||||
Socket,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "compressed",
|
||||
[{"xmlns", ?NS_COMPRESS}], []})),
|
||||
fsm_next_state(wait_for_stream,
|
||||
@@ -989,7 +1051,9 @@ session_established2(El, StateData) ->
|
||||
end;
|
||||
"iq" ->
|
||||
case jlib:iq_query_info(NewEl) of
|
||||
#iq{xmlns = ?NS_PRIVACY} = IQ ->
|
||||
#iq{xmlns = Xmlns} = IQ
|
||||
when Xmlns == ?NS_PRIVACY;
|
||||
Xmlns == ?NS_BLOCKING ->
|
||||
process_privacy_iq(
|
||||
FromJID, ToJID, IQ, StateData);
|
||||
_ ->
|
||||
@@ -997,8 +1061,7 @@ session_established2(El, StateData) ->
|
||||
user_send_packet,
|
||||
Server,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(
|
||||
FromJID, ToJID, NewEl),
|
||||
check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),
|
||||
StateData
|
||||
end;
|
||||
"message" ->
|
||||
@@ -1092,35 +1155,39 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
{Pass, NewAttrs, NewState} =
|
||||
case Name of
|
||||
"presence" ->
|
||||
State = ejabberd_hooks:run_fold(
|
||||
c2s_presence_in, StateData#state.server,
|
||||
StateData,
|
||||
[{From, To, Packet}]),
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"probe" ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
LBFrom = jlib:jid_remove_resource(LFrom),
|
||||
NewStateData =
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_a) orelse
|
||||
LFrom, State#state.pres_a) orelse
|
||||
?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_a) of
|
||||
LBFrom, State#state.pres_a) of
|
||||
true ->
|
||||
StateData;
|
||||
State;
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_f) of
|
||||
LFrom, State#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LFrom,
|
||||
StateData#state.pres_a),
|
||||
StateData#state{pres_a = A};
|
||||
State#state.pres_a),
|
||||
State#state{pres_a = A};
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_f) of
|
||||
LBFrom, State#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LBFrom,
|
||||
StateData#state.pres_a),
|
||||
StateData#state{pres_a = A};
|
||||
State#state.pres_a),
|
||||
State#state{pres_a = A};
|
||||
false ->
|
||||
StateData
|
||||
State
|
||||
end
|
||||
end
|
||||
end,
|
||||
@@ -1128,66 +1195,59 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
{false, Attrs, NewStateData};
|
||||
"error" ->
|
||||
NewA = remove_element(jlib:jid_tolower(From),
|
||||
StateData#state.pres_a),
|
||||
{true, Attrs, StateData#state{pres_a = NewA}};
|
||||
State#state.pres_a),
|
||||
{true, Attrs, State#state{pres_a = NewA}};
|
||||
"invisible" ->
|
||||
Attrs1 = lists:keydelete("type", 1, Attrs),
|
||||
{true, [{"type", "unavailable"} | Attrs1], StateData};
|
||||
{true, [{"type", "unavailable"} | Attrs1], State};
|
||||
"subscribe" ->
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
SRes = is_privacy_allow(State, From, To, Packet, in),
|
||||
{SRes, Attrs, State};
|
||||
"subscribed" ->
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
SRes = is_privacy_allow(State, From, To, Packet, in),
|
||||
{SRes, Attrs, State};
|
||||
"unsubscribe" ->
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
SRes = is_privacy_allow(State, From, To, Packet, in),
|
||||
{SRes, Attrs, State};
|
||||
"unsubscribed" ->
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
SRes = is_privacy_allow(State, From, To, Packet, in),
|
||||
{SRes, Attrs, State};
|
||||
_ ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in]) of
|
||||
case privacy_check_packet(State, From, To, Packet, in) of
|
||||
allow ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
LBFrom = jlib:jid_remove_resource(LFrom),
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_a) orelse
|
||||
LFrom, State#state.pres_a) orelse
|
||||
?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_a) of
|
||||
LBFrom, State#state.pres_a) of
|
||||
true ->
|
||||
{true, Attrs, StateData};
|
||||
{true, Attrs, State};
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_f) of
|
||||
LFrom, State#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LFrom,
|
||||
StateData#state.pres_a),
|
||||
State#state.pres_a),
|
||||
{true, Attrs,
|
||||
StateData#state{pres_a = A}};
|
||||
State#state{pres_a = A}};
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_f) of
|
||||
LBFrom, State#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LBFrom,
|
||||
StateData#state.pres_a),
|
||||
State#state.pres_a),
|
||||
{true, Attrs,
|
||||
StateData#state{pres_a = A}};
|
||||
State#state{pres_a = A}};
|
||||
false ->
|
||||
{true, Attrs, StateData}
|
||||
{true, Attrs, State}
|
||||
end
|
||||
end
|
||||
end;
|
||||
deny ->
|
||||
{false, Attrs, StateData}
|
||||
{false, Attrs, State}
|
||||
end
|
||||
end;
|
||||
"broadcast" ->
|
||||
@@ -1225,67 +1285,52 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
send_element(StateData, PrivPushEl),
|
||||
{false, Attrs, StateData#state{privacy_list = NewPL}}
|
||||
end;
|
||||
[{blocking, What}] ->
|
||||
route_blocking(What, StateData),
|
||||
{false, Attrs, StateData};
|
||||
_ ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
"iq" ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = ?NS_VCARD} when (To#jid.luser == "") or (To#jid.lresource == "") ->
|
||||
Host = StateData#state.server,
|
||||
case ets:lookup(sm_iqtable, {?NS_VCARD, Host}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end,
|
||||
{false, Attrs, StateData};
|
||||
#iq{} ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in]) of
|
||||
allow ->
|
||||
{true, Attrs, StateData};
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
#iq{xmlns = ?NS_LAST} ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
LBFrom = jlib:jid_remove_resource(LFrom),
|
||||
HasFromSub = (?SETS:is_element(LFrom, StateData#state.pres_f) orelse ?SETS:is_element(LBFrom, StateData#state.pres_f))
|
||||
andalso is_privacy_allow(StateData, To, From, {xmlelement, "presence", [], []}, out),
|
||||
case HasFromSub of
|
||||
true ->
|
||||
case privacy_check_packet(StateData, From, To, Packet, in) of
|
||||
allow ->
|
||||
{true, Attrs, StateData};
|
||||
deny ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
_ ->
|
||||
{true, Attrs, StateData}
|
||||
end;
|
||||
"message" ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in]) of
|
||||
allow ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
feature_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.pres_last,
|
||||
{From, To, Packet},
|
||||
in]) of
|
||||
IQ when (is_record(IQ, iq)) or (IQ == reply) ->
|
||||
case privacy_check_packet(StateData, From, To, Packet, in) of
|
||||
allow ->
|
||||
{true, Attrs, StateData};
|
||||
deny ->
|
||||
deny when is_record(IQ, iq) ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
{false, Attrs, StateData};
|
||||
deny when IQ == reply ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
IQ when (IQ == invalid) or (IQ == not_iq) ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
"message" ->
|
||||
case privacy_check_packet(StateData, From, To, Packet, in) of
|
||||
allow ->
|
||||
{true, Attrs, StateData};
|
||||
deny ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
@@ -1330,10 +1375,53 @@ handle_info(system_shutdown, StateName, StateData) ->
|
||||
ok
|
||||
end,
|
||||
{stop, normal, StateData};
|
||||
handle_info({force_update_presence, LUser}, StateName,
|
||||
#state{user = LUser, server = LServer} = StateData) ->
|
||||
NewStateData =
|
||||
case StateData#state.pres_last of
|
||||
{xmlelement, "presence", _Attrs, _Els} ->
|
||||
PresenceEl = ejabberd_hooks:run_fold(
|
||||
c2s_update_presence,
|
||||
LServer,
|
||||
StateData#state.pres_last,
|
||||
[LUser, LServer]),
|
||||
StateData2 = StateData#state{pres_last = PresenceEl},
|
||||
presence_update(StateData2#state.jid,
|
||||
PresenceEl,
|
||||
StateData2),
|
||||
StateData2;
|
||||
_ ->
|
||||
StateData
|
||||
end,
|
||||
{next_state, StateName, NewStateData};
|
||||
handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
|
||||
Recipients = ejabberd_hooks:run_fold(
|
||||
c2s_broadcast_recipients, StateData#state.server,
|
||||
[],
|
||||
[StateData, Type, From, Packet]),
|
||||
lists:foreach(
|
||||
fun(USR) ->
|
||||
ejabberd_router:route(
|
||||
From, jlib:make_jid(USR), Packet)
|
||||
end, lists:usort(Recipients)),
|
||||
fsm_next_state(StateName, StateData);
|
||||
handle_info(Info, StateName, StateData) ->
|
||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||
fsm_next_state(StateName, StateData).
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
|
||||
State#state{pres_t = {pres_t, ?SETS:size(T)},
|
||||
pres_f = {pres_f, ?SETS:size(F)},
|
||||
pres_a = {pres_a, ?SETS:size(A)},
|
||||
pres_i = {pres_i, ?SETS:size(I)}
|
||||
}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
@@ -1392,7 +1480,8 @@ terminate(_Reason, StateName, StateData) ->
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_i, Packet)
|
||||
end
|
||||
end;
|
||||
end,
|
||||
bounce_messages();
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
@@ -1408,15 +1497,19 @@ change_shaper(StateData, JID) ->
|
||||
StateData#state.shaper, JID),
|
||||
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
|
||||
|
||||
send_text(StateData, Text) when StateData#state.xml_socket ->
|
||||
?DEBUG("Send Text on stream = ~p", [lists:flatten(Text)]),
|
||||
(StateData#state.sockmod):send_xml(StateData#state.socket,
|
||||
{xmlstreamraw, Text});
|
||||
send_text(StateData, Text) ->
|
||||
?DEBUG("Send XML on stream = ~p", [lists:flatten(Text)]),
|
||||
?DEBUG("Send XML on stream = ~p", [Text]),
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) when StateData#state.xml_socket ->
|
||||
(StateData#state.sockmod):send_xml(StateData#state.socket,
|
||||
{xmlstreamelement, El});
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
send_header(StateData, Server, Version, Lang)
|
||||
when StateData#state.xml_socket ->
|
||||
@@ -1547,14 +1640,7 @@ process_presence_probe(From, To, StateData) ->
|
||||
[jlib:timestamp_to_xml(Timestamp, utc, To, ""),
|
||||
%% TODO: Delete the next line once XEP-0091 is Obsolete
|
||||
jlib:timestamp_to_xml(Timestamp)]),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{To, From, Packet},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, To, From, Packet, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -1652,37 +1738,33 @@ presence_update(From, Packet, StateData) ->
|
||||
StateData#state.pres_invis,
|
||||
?DEBUG("from unavail = ~p~n", [FromUnavail]),
|
||||
NewState =
|
||||
NewStateData = StateData#state{pres_last = Packet,
|
||||
pres_invis = false,
|
||||
pres_timestamp = Timestamp},
|
||||
if
|
||||
FromUnavail ->
|
||||
ejabberd_hooks:run(user_available_hook,
|
||||
StateData#state.server,
|
||||
[StateData#state.jid]),
|
||||
NewStateData#state.server,
|
||||
[NewStateData#state.jid]),
|
||||
if NewPriority >= 0 ->
|
||||
resend_offline_messages(StateData),
|
||||
resend_subscription_requests(StateData);
|
||||
resend_offline_messages(NewStateData),
|
||||
resend_subscription_requests(NewStateData);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
presence_broadcast_first(
|
||||
From, StateData#state{pres_last = Packet,
|
||||
pres_invis = false,
|
||||
pres_timestamp = Timestamp
|
||||
}, Packet);
|
||||
presence_broadcast_first(From, NewStateData, Packet);
|
||||
true ->
|
||||
presence_broadcast_to_trusted(StateData,
|
||||
presence_broadcast_to_trusted(NewStateData,
|
||||
From,
|
||||
StateData#state.pres_f,
|
||||
StateData#state.pres_a,
|
||||
NewStateData#state.pres_f,
|
||||
NewStateData#state.pres_a,
|
||||
Packet),
|
||||
if OldPriority < 0, NewPriority >= 0 ->
|
||||
resend_offline_messages(StateData);
|
||||
resend_offline_messages(NewStateData);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
StateData#state{pres_last = Packet,
|
||||
pres_invis = false,
|
||||
pres_timestamp = Timestamp
|
||||
}
|
||||
NewStateData
|
||||
end,
|
||||
NewState
|
||||
end.
|
||||
@@ -1749,44 +1831,35 @@ presence_track(From, To, Packet, StateData) ->
|
||||
end.
|
||||
|
||||
check_privacy_route(From, StateData, FromRoute, To, Packet) ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, To, Packet, out) of
|
||||
deny ->
|
||||
Lang = StateData#state.lang,
|
||||
ErrText = "Your active privacy list has denied the routing of this stanza.",
|
||||
Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ok;
|
||||
allow ->
|
||||
ejabberd_router:route(FromRoute, To, Packet)
|
||||
end.
|
||||
|
||||
privacy_check_packet(StateData, From, To, Packet, Dir) ->
|
||||
ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
Dir]).
|
||||
|
||||
%% Check if privacy rules allow this delivery
|
||||
is_privacy_allow(From, To, Packet, PrivacyList) ->
|
||||
User = To#jid.user,
|
||||
Server = To#jid.server,
|
||||
allow == ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, Server,
|
||||
allow,
|
||||
[User,
|
||||
Server,
|
||||
PrivacyList,
|
||||
{From, To, Packet},
|
||||
in]).
|
||||
is_privacy_allow(StateData, From, To, Packet, Dir) ->
|
||||
allow == privacy_check_packet(StateData, From, To, Packet, Dir).
|
||||
|
||||
presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
lists:foreach(fun(JID) ->
|
||||
FJID = jlib:make_jid(JID),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, FJID, Packet, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -1800,14 +1873,7 @@ presence_broadcast_to_trusted(StateData, From, T, A, Packet) ->
|
||||
case ?SETS:is_element(JID, T) of
|
||||
true ->
|
||||
FJID = jlib:make_jid(JID),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, FJID, Packet, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -1838,14 +1904,7 @@ presence_broadcast_first(From, StateData, Packet) ->
|
||||
As = ?SETS:fold(
|
||||
fun(JID, A) ->
|
||||
FJID = jlib:make_jid(JID),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, FJID, Packet, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -1900,14 +1959,7 @@ roster_change(IJID, ISubscription, StateData) ->
|
||||
if
|
||||
Cond1 ->
|
||||
?DEBUG("C1: ~p~n", [LIJID]),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, P},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, To, P, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -1922,14 +1974,7 @@ roster_change(IJID, ISubscription, StateData) ->
|
||||
?DEBUG("C2: ~p~n", [LIJID]),
|
||||
PU = {xmlelement, "presence",
|
||||
[{"type", "unavailable"}], []},
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
[StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, PU},
|
||||
out]) of
|
||||
case privacy_check_packet(StateData, From, To, PU, out) of
|
||||
deny ->
|
||||
ok;
|
||||
allow ->
|
||||
@@ -2007,25 +2052,16 @@ process_privacy_iq(From, To,
|
||||
NewStateData.
|
||||
|
||||
|
||||
resend_offline_messages(#state{user = User,
|
||||
server = Server,
|
||||
privacy_list = PrivList} = StateData) ->
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook,
|
||||
Server,
|
||||
[],
|
||||
[User, Server]) of
|
||||
resend_offline_messages(StateData) ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
resend_offline_messages_hook, StateData#state.server,
|
||||
[],
|
||||
[StateData#state.user, StateData#state.server]) of
|
||||
Rs when is_list(Rs) ->
|
||||
lists:foreach(
|
||||
fun({route,
|
||||
From, To, {xmlelement, Name, Attrs, Els} = Packet}) ->
|
||||
Pass = case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, Server,
|
||||
allow,
|
||||
[User,
|
||||
Server,
|
||||
PrivList,
|
||||
{From, To, Packet},
|
||||
in]) of
|
||||
From, To, {xmlelement, _Name, _Attrs, _Els} = Packet}) ->
|
||||
Pass = case privacy_check_packet(StateData, From, To, Packet, in) of
|
||||
allow ->
|
||||
true;
|
||||
deny ->
|
||||
@@ -2033,16 +2069,18 @@ resend_offline_messages(#state{user = User,
|
||||
end,
|
||||
if
|
||||
Pass ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
send_element(StateData, FixedPacket),
|
||||
ejabberd_hooks:run(user_receive_packet,
|
||||
StateData#state.server,
|
||||
[StateData#state.jid,
|
||||
From, To, FixedPacket]);
|
||||
%% Attrs2 = jlib:replace_from_to_attrs(
|
||||
%% jlib:jid_to_string(From),
|
||||
%% jlib:jid_to_string(To),
|
||||
%% Attrs),
|
||||
%% FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
%% Use route instead of send_element to go through standard workflow
|
||||
ejabberd_router:route(From, To, Packet);
|
||||
%% send_element(StateData, FixedPacket),
|
||||
%% ejabberd_hooks:run(user_receive_packet,
|
||||
%% StateData#state.server,
|
||||
%% [StateData#state.jid,
|
||||
%% From, To, FixedPacket]);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
@@ -2078,7 +2116,17 @@ get_statustag(Presence) ->
|
||||
end.
|
||||
|
||||
process_unauthenticated_stanza(StateData, El) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
NewEl = case xml:get_tag_attr_s("xml:lang", El) of
|
||||
"" ->
|
||||
case StateData#state.lang of
|
||||
"" -> El;
|
||||
Lang ->
|
||||
xml:replace_tag_attr("xml:lang", Lang, El)
|
||||
end;
|
||||
_ ->
|
||||
El
|
||||
end,
|
||||
case jlib:iq_query_info(NewEl) of
|
||||
#iq{} = IQ ->
|
||||
Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq,
|
||||
StateData#state.server,
|
||||
@@ -2185,6 +2233,60 @@ fsm_limit_opts(Opts) ->
|
||||
end
|
||||
end.
|
||||
|
||||
bounce_messages() ->
|
||||
receive
|
||||
{route, From, To, El} ->
|
||||
ejabberd_router:route(From, To, El),
|
||||
bounce_messages()
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% XEP-0191
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
route_blocking(What, StateData) ->
|
||||
SubEl =
|
||||
case What of
|
||||
{block, JIDs} ->
|
||||
{xmlelement, "block",
|
||||
[{"xmlns", ?NS_BLOCKING}],
|
||||
lists:map(
|
||||
fun(JID) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", jlib:jid_to_string(JID)}],
|
||||
[]}
|
||||
end, JIDs)};
|
||||
{unblock, JIDs} ->
|
||||
{xmlelement, "unblock",
|
||||
[{"xmlns", ?NS_BLOCKING}],
|
||||
lists:map(
|
||||
fun(JID) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", jlib:jid_to_string(JID)}],
|
||||
[]}
|
||||
end, JIDs)};
|
||||
unblock_all ->
|
||||
{xmlelement, "unblock",
|
||||
[{"xmlns", ?NS_BLOCKING}], []}
|
||||
end,
|
||||
PrivPushIQ =
|
||||
#iq{type = set, xmlns = ?NS_BLOCKING,
|
||||
id = "push",
|
||||
sub_el = [SubEl]},
|
||||
PrivPushEl =
|
||||
jlib:replace_from_to(
|
||||
jlib:jid_remove_resource(
|
||||
StateData#state.jid),
|
||||
StateData#state.jid,
|
||||
jlib:iq_to_xml(PrivPushIQ)),
|
||||
send_element(StateData, PrivPushEl),
|
||||
%% No need to replace active privacy list here,
|
||||
%% blocking pushes are always accompanied by
|
||||
%% Privacy List pushes
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% JID Set memory footprint reduction code
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
%%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+222
-46
@@ -5,7 +5,7 @@
|
||||
%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -36,7 +36,8 @@
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-export([create_captcha/6, build_captcha_html/2, check_captcha/2,
|
||||
process_reply/1, process/2, is_feature_available/0]).
|
||||
process_reply/1, process/2, is_feature_available/0,
|
||||
create_captcha_x/5, create_captcha_x/6]).
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
@@ -48,8 +49,9 @@
|
||||
|
||||
-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")).
|
||||
-define(CAPTCHA_LIFETIME, 120000). % two minutes
|
||||
-define(LIMIT_PERIOD, 60*1000*1000). % one minute
|
||||
|
||||
-record(state, {}).
|
||||
-record(state, {limits = treap:empty()}).
|
||||
-record(captcha, {id, pid, key, tref, args}).
|
||||
|
||||
-define(T(S),
|
||||
@@ -71,11 +73,12 @@
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
create_captcha(Id, SID, From, To, Lang, Args)
|
||||
when is_list(Id), is_list(Lang), is_list(SID),
|
||||
create_captcha(SID, From, To, Lang, Limiter, Args)
|
||||
when is_list(Lang), is_list(SID),
|
||||
is_record(From, jid), is_record(To, jid) ->
|
||||
case create_image() of
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = randoms:get_string(),
|
||||
B64Image = jlib:encode_base64(binary_to_list(Image)),
|
||||
JID = jlib:jid_to_string(From),
|
||||
CID = "sha1+" ++ sha:sha(Image) ++ "@bob.xmpp.org",
|
||||
@@ -91,7 +94,8 @@ create_captcha(Id, SID, From, To, Lang, Args)
|
||||
?VFIELD("hidden", "challenge", {xmlcdata, Id}),
|
||||
?VFIELD("hidden", "sid", {xmlcdata, SID}),
|
||||
{xmlelement, "field", [{"var", "ocr"}, {"label", ?CAPTCHA_TEXT(Lang)}],
|
||||
[{xmlelement, "media", [{"xmlns", ?NS_MEDIA}],
|
||||
[{xmlelement, "required", [], []},
|
||||
{xmlelement, "media", [{"xmlns", ?NS_MEDIA}],
|
||||
[{xmlelement, "uri", [{"type", Type}],
|
||||
[{xmlcdata, "cid:" ++ CID}]}]}]}]}]},
|
||||
BodyString1 = translate:translate(Lang, "Your messages to ~s are being blocked. To unblock them, visit ~s"),
|
||||
@@ -104,12 +108,61 @@ create_captcha(Id, SID, From, To, Lang, Args)
|
||||
case ?T(mnesia:write(#captcha{id=Id, pid=self(), key=Key,
|
||||
tref=Tref, args=Args})) of
|
||||
ok ->
|
||||
{ok, [Body, OOB, Captcha, Data]};
|
||||
_Err ->
|
||||
error
|
||||
{ok, Id, [Body, OOB, Captcha, Data]};
|
||||
Err ->
|
||||
{error, Err}
|
||||
end;
|
||||
_Err ->
|
||||
error
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls, []).
|
||||
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls, TailEls) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = randoms:get_string(),
|
||||
B64Image = jlib:encode_base64(binary_to_list(Image)),
|
||||
CID = "sha1+" ++ sha:sha(Image) ++ "@bob.xmpp.org",
|
||||
Data = {xmlelement, "data",
|
||||
[{"xmlns", ?NS_BOB}, {"cid", CID},
|
||||
{"max-age", "0"}, {"type", Type}],
|
||||
[{xmlcdata, B64Image}]},
|
||||
HelpTxt = translate:translate(
|
||||
Lang,
|
||||
"If you don't see the CAPTCHA image here, "
|
||||
"visit the web page."),
|
||||
Imageurl = get_url(Id ++ "/image"),
|
||||
Captcha =
|
||||
{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
|
||||
[?VFIELD("hidden", "FORM_TYPE", {xmlcdata, ?NS_CAPTCHA}) | HeadEls] ++
|
||||
[{xmlelement, "field", [{"type", "fixed"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, HelpTxt}]}]},
|
||||
{xmlelement, "field", [{"type", "hidden"}, {"var", "captchahidden"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, "workaround-for-psi"}]}]},
|
||||
{xmlelement, "field",
|
||||
[{"type", "text-single"},
|
||||
{"label", translate:translate(Lang, "CAPTCHA web page")},
|
||||
{"var", "url"}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Imageurl}]}]},
|
||||
?VFIELD("hidden", "from", {xmlcdata, jlib:jid_to_string(To)}),
|
||||
?VFIELD("hidden", "challenge", {xmlcdata, Id}),
|
||||
?VFIELD("hidden", "sid", {xmlcdata, SID}),
|
||||
{xmlelement, "field", [{"var", "ocr"}, {"label", ?CAPTCHA_TEXT(Lang)}],
|
||||
[{xmlelement, "required", [], []},
|
||||
{xmlelement, "media", [{"xmlns", ?NS_MEDIA}],
|
||||
[{xmlelement, "uri", [{"type", Type}],
|
||||
[{xmlcdata, "cid:" ++ CID}]}]}]}] ++ TailEls},
|
||||
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
|
||||
case ?T(mnesia:write(#captcha{id=Id, key=Key, tref=Tref})) of
|
||||
ok ->
|
||||
{ok, [Captcha, Data]};
|
||||
Err ->
|
||||
{error, Err}
|
||||
end;
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
%% @spec (Id::string(), Lang::string()) -> {FormEl, {ImgEl, TextEl, IdEl, KeyEl}} | captcha_not_found
|
||||
@@ -155,10 +208,18 @@ check_captcha(Id, ProvidedKey) ->
|
||||
mnesia:delete({captcha, Id}),
|
||||
erlang:cancel_timer(Tref),
|
||||
if StoredKey == ProvidedKey ->
|
||||
Pid ! {captcha_succeed, Args},
|
||||
if is_pid(Pid) ->
|
||||
Pid ! {captcha_succeed, Args};
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
captcha_valid;
|
||||
true ->
|
||||
Pid ! {captcha_failed, Args},
|
||||
if is_pid(Pid) ->
|
||||
Pid ! {captcha_failed, Args};
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
captcha_non_valid
|
||||
end;
|
||||
_ ->
|
||||
@@ -166,24 +227,32 @@ check_captcha(Id, ProvidedKey) ->
|
||||
end).
|
||||
|
||||
|
||||
process_reply({xmlelement, "captcha", _, _} = El) ->
|
||||
process_reply({xmlelement, _, _, _} = El) ->
|
||||
case xml:get_subtag(El, "x") of
|
||||
false ->
|
||||
{error, malformed};
|
||||
Xdata ->
|
||||
Fields = jlib:parse_xdata_submit(Xdata),
|
||||
case {proplists:get_value("challenge", Fields),
|
||||
proplists:get_value("ocr", Fields)} of
|
||||
case catch {proplists:get_value("challenge", Fields),
|
||||
proplists:get_value("ocr", Fields)} of
|
||||
{[Id|_], [OCR|_]} ->
|
||||
?T(case mnesia:read(captcha, Id, write) of
|
||||
[#captcha{pid=Pid, args=Args, key=Key, tref=Tref}] ->
|
||||
mnesia:delete({captcha, Id}),
|
||||
erlang:cancel_timer(Tref),
|
||||
if OCR == Key ->
|
||||
Pid ! {captcha_succeed, Args},
|
||||
if is_pid(Pid) ->
|
||||
Pid ! {captcha_succeed, Args};
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
ok;
|
||||
true ->
|
||||
Pid ! {captcha_failed, Args},
|
||||
if is_pid(Pid) ->
|
||||
Pid ! {captcha_failed, Args};
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{error, bad_match}
|
||||
end;
|
||||
_ ->
|
||||
@@ -208,16 +277,19 @@ process(_Handlers, #request{method='GET', lang=Lang, path=[_, Id]}) ->
|
||||
ejabberd_web:error(not_found)
|
||||
end;
|
||||
|
||||
process(_Handlers, #request{method='GET', path=[_, Id, "image"]}) ->
|
||||
process(_Handlers, #request{method='GET', path=[_, Id, "image"], ip = IP}) ->
|
||||
{Addr, _Port} = IP,
|
||||
case mnesia:dirty_read(captcha, Id) of
|
||||
[#captcha{key=Key}] ->
|
||||
case create_image(Key) of
|
||||
case create_image(Addr, Key) of
|
||||
{ok, Type, _, Img} ->
|
||||
{200,
|
||||
[{"Content-Type", Type},
|
||||
{"Cache-Control", "no-cache"},
|
||||
{"Last-Modified", httpd_util:rfc1123_date()}],
|
||||
Img};
|
||||
{error, limit} ->
|
||||
ejabberd_web:error(not_allowed);
|
||||
_ ->
|
||||
ejabberd_web:error(not_found)
|
||||
end;
|
||||
@@ -256,6 +328,20 @@ init([]) ->
|
||||
check_captcha_setup(),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call({is_limited, Limiter, RateLimit}, _From, State) ->
|
||||
NowPriority = now_priority(),
|
||||
CleanPriority = NowPriority + ?LIMIT_PERIOD,
|
||||
Limits = clean_treap(State#state.limits, CleanPriority),
|
||||
case treap:lookup(Limiter, Limits) of
|
||||
{ok, _, Rate} when Rate >= RateLimit ->
|
||||
{reply, true, State#state{limits = Limits}};
|
||||
{ok, Priority, Rate} ->
|
||||
NewLimits = treap:insert(Limiter, Priority, Rate+1, Limits),
|
||||
{reply, false, State#state{limits = NewLimits}};
|
||||
_ ->
|
||||
NewLimits = treap:insert(Limiter, NowPriority, 1, Limits),
|
||||
{reply, false, State#state{limits = NewLimits}}
|
||||
end;
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, bad_request, State}.
|
||||
|
||||
@@ -266,7 +352,11 @@ handle_info({remove_id, Id}, State) ->
|
||||
?DEBUG("captcha ~p timed out", [Id]),
|
||||
_ = ?T(case mnesia:read(captcha, Id, write) of
|
||||
[#captcha{args=Args, pid=Pid}] ->
|
||||
Pid ! {captcha_failed, Args},
|
||||
if is_pid(Pid) ->
|
||||
Pid ! {captcha_failed, Args};
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
mnesia:delete({captcha, Id});
|
||||
_ ->
|
||||
ok
|
||||
@@ -293,11 +383,22 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%% Reason = atom()
|
||||
%%--------------------------------------------------------------------
|
||||
create_image() ->
|
||||
create_image(undefined).
|
||||
|
||||
create_image(Limiter) ->
|
||||
%% Six numbers from 1 to 9.
|
||||
Key = string:substr(randoms:get_string(), 1, 6),
|
||||
create_image(Key).
|
||||
create_image(Limiter, Key).
|
||||
|
||||
create_image(Key) ->
|
||||
create_image(Limiter, Key) ->
|
||||
case is_limited(Limiter) of
|
||||
true ->
|
||||
{error, limit};
|
||||
false ->
|
||||
do_create_image(Key)
|
||||
end.
|
||||
|
||||
do_create_image(Key) ->
|
||||
FileName = get_prog_name(),
|
||||
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
|
||||
case cmd(Cmd) of
|
||||
@@ -327,20 +428,82 @@ get_prog_name() ->
|
||||
case ejabberd_config:get_local_option(captcha_cmd) of
|
||||
FileName when is_list(FileName) ->
|
||||
FileName;
|
||||
_ ->
|
||||
?CRITICAL_MSG("The option captcha_cmd is not configured, but some "
|
||||
Value when (Value == undefined) or (Value == "") ->
|
||||
?DEBUG("The option captcha_cmd is not configured, but some "
|
||||
"module wants to use the CAPTCHA feature.", []),
|
||||
throw({error, option_not_configured_captcha_cmd})
|
||||
false
|
||||
end.
|
||||
|
||||
get_url(Str) ->
|
||||
case ejabberd_config:get_local_option(captcha_host) of
|
||||
Host when is_list(Host) ->
|
||||
CaptchaHost = ejabberd_config:get_local_option(captcha_host),
|
||||
case string:tokens(CaptchaHost, ":") of
|
||||
[Host] ->
|
||||
"http://" ++ Host ++ "/captcha/" ++ Str;
|
||||
["http"++_ = TransferProt, Host] ->
|
||||
TransferProt ++ ":" ++ Host ++ "/captcha/" ++ Str;
|
||||
[Host, PortString] ->
|
||||
TransferProt = atom_to_list(get_transfer_protocol(PortString)),
|
||||
TransferProt ++ "://" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
|
||||
[TransferProt, Host, PortString] ->
|
||||
TransferProt ++ ":" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
|
||||
_ ->
|
||||
"http://" ++ ?MYNAME ++ "/captcha/" ++ Str
|
||||
end.
|
||||
|
||||
get_transfer_protocol(PortString) ->
|
||||
PortNumber = list_to_integer(PortString),
|
||||
PortListeners = get_port_listeners(PortNumber),
|
||||
get_captcha_transfer_protocol(PortListeners).
|
||||
|
||||
get_port_listeners(PortNumber) ->
|
||||
AllListeners = ejabberd_config:get_local_option(listen),
|
||||
lists:filter(
|
||||
fun({{Port, _Ip, _Netp}, _Module1, _Opts1}) when Port == PortNumber ->
|
||||
true;
|
||||
(_) ->
|
||||
false
|
||||
end,
|
||||
AllListeners).
|
||||
|
||||
get_captcha_transfer_protocol([]) ->
|
||||
throw("The port number mentioned in captcha_host is not "
|
||||
"a ejabberd_http listener with 'captcha' option. "
|
||||
"Change the port number or specify http:// in that option.");
|
||||
get_captcha_transfer_protocol([{{_Port, _Ip, tcp}, ejabberd_http, Opts}
|
||||
| Listeners]) ->
|
||||
case lists:member(captcha, Opts) of
|
||||
true ->
|
||||
case lists:member(tls, Opts) of
|
||||
true ->
|
||||
https;
|
||||
false ->
|
||||
http
|
||||
end;
|
||||
false ->
|
||||
get_captcha_transfer_protocol(Listeners)
|
||||
end;
|
||||
get_captcha_transfer_protocol([_ | Listeners]) ->
|
||||
get_captcha_transfer_protocol(Listeners).
|
||||
|
||||
is_limited(undefined) ->
|
||||
false;
|
||||
is_limited(Limiter) ->
|
||||
case ejabberd_config:get_local_option(captcha_limit) of
|
||||
Int when is_integer(Int), Int > 0 ->
|
||||
case catch gen_server:call(?MODULE, {is_limited, Limiter, Int},
|
||||
5000) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
false;
|
||||
Err ->
|
||||
?ERROR_MSG("Call failed: ~p", [Err]),
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: cmd(Cmd) -> Data | {error, Reason}
|
||||
%% Cmd = string()
|
||||
@@ -389,28 +552,41 @@ return(Port, TRef, Result) ->
|
||||
catch port_close(Port),
|
||||
Result.
|
||||
|
||||
is_feature_enabled() ->
|
||||
try get_prog_name() of
|
||||
Prog when is_list(Prog) -> true
|
||||
catch
|
||||
_:_ -> false
|
||||
end.
|
||||
|
||||
is_feature_available() ->
|
||||
case is_feature_enabled() of
|
||||
false -> false;
|
||||
true ->
|
||||
case create_image() of
|
||||
{ok, _, _, _} -> true;
|
||||
_Error -> false
|
||||
end
|
||||
case get_prog_name() of
|
||||
Prog when is_list(Prog) -> true;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
check_captcha_setup() ->
|
||||
case is_feature_enabled() andalso not is_feature_available() of
|
||||
case is_feature_available() of
|
||||
true ->
|
||||
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
|
||||
"but it can't generate images.", []);
|
||||
case create_image() of
|
||||
{ok, _, _, _} ->
|
||||
ok;
|
||||
_Err ->
|
||||
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
|
||||
"but it can't generate images.", []),
|
||||
throw({error, captcha_cmd_enabled_but_fails})
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
clean_treap(Treap, CleanPriority) ->
|
||||
case treap:is_empty(Treap) of
|
||||
true ->
|
||||
Treap;
|
||||
false ->
|
||||
{_Key, Priority, _Value} = treap:get_root(Treap),
|
||||
if
|
||||
Priority > CleanPriority ->
|
||||
clean_treap(treap:delete_root(Treap), CleanPriority);
|
||||
true ->
|
||||
Treap
|
||||
end
|
||||
end.
|
||||
|
||||
now_priority() ->
|
||||
{MSec, Sec, USec} = now(),
|
||||
-((MSec*1000000 + Sec)*1000000 + USec).
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -215,7 +215,7 @@ exit_or_halt(ExitText) ->
|
||||
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
|
||||
[] ->
|
||||
timer:sleep(1000),
|
||||
halt(ExitText);
|
||||
halt(string:substr(ExitText, 1, 199));
|
||||
[_] ->
|
||||
exit(ExitText)
|
||||
end.
|
||||
@@ -431,6 +431,8 @@ process_term(Term, State) ->
|
||||
add_option(captcha_cmd, Cmd, State);
|
||||
{captcha_host, Host} ->
|
||||
add_option(captcha_host, Host, State);
|
||||
{captcha_limit, Limit} ->
|
||||
add_option(captcha_limit, Limit, State);
|
||||
{ejabberdctl_access_commands, ACs} ->
|
||||
add_option(ejabberdctl_access_commands, ACs, State);
|
||||
{loglevel, Loglevel} ->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -201,8 +201,7 @@ process(Args) ->
|
||||
case String of
|
||||
[] -> ok;
|
||||
_ ->
|
||||
io:format(String),
|
||||
io:format("\n")
|
||||
io:format("~s~n", [String])
|
||||
end,
|
||||
Code.
|
||||
|
||||
@@ -742,6 +741,10 @@ print_usage_command(Cmd, C, MaxC, ShCode) ->
|
||||
|
||||
?PRINT(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []).
|
||||
|
||||
format_usage_ctype(Type, _Indentation)
|
||||
when (Type==atom) or (Type==integer) or (Type==string) or (Type==rescode) or (Type==restuple)->
|
||||
io_lib:format("~p", [Type]);
|
||||
|
||||
format_usage_ctype({Name, Type}, _Indentation)
|
||||
when (Type==atom) or (Type==integer) or (Type==string) or (Type==rescode) or (Type==restuple)->
|
||||
io_lib:format("~p::~p", [Name, Type]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_debug.erl
|
||||
%%% Author : Mickael Remond
|
||||
%%% Purpose : ejabberd's application callback module
|
||||
%%% Created : 6 may 2009 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 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., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_debug).
|
||||
|
||||
-export([eprof_start/0, fprof_start/0, stop/0]).
|
||||
-export([pids/0]).
|
||||
|
||||
eprof_start() ->
|
||||
eprof:start(),
|
||||
eprof:profile(pids()).
|
||||
|
||||
fprof_start() ->
|
||||
fprof:trace([start, {file, "/tmp/fprof"}, {procs, pids()}]).
|
||||
|
||||
%% Stop all profilers
|
||||
stop() ->
|
||||
catch eprof:stop(),
|
||||
catch fprof:stop(),
|
||||
ok.
|
||||
|
||||
pids() ->
|
||||
lists:zf(
|
||||
fun(Pid) ->
|
||||
case process_info(Pid) of
|
||||
ProcessInfo when is_list(ProcessInfo) ->
|
||||
CurrentFunction = current_function(ProcessInfo),
|
||||
InitialCall = initial_call(ProcessInfo),
|
||||
RegisteredName = registered_name(ProcessInfo),
|
||||
Ancestor = ancestor(ProcessInfo),
|
||||
filter_pid(Pid, CurrentFunction, InitialCall, RegisteredName, Ancestor);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
processes()).
|
||||
|
||||
current_function(ProcessInfo) ->
|
||||
{value, {_, {CurrentFunction, _,_}}} =
|
||||
lists:keysearch(current_function, 1, ProcessInfo),
|
||||
atom_to_list(CurrentFunction).
|
||||
|
||||
initial_call(ProcessInfo) ->
|
||||
{value, {_, {InitialCall, _,_}}} =
|
||||
lists:keysearch(initial_call, 1, ProcessInfo),
|
||||
atom_to_list(InitialCall).
|
||||
|
||||
registered_name(ProcessInfo) ->
|
||||
case lists:keysearch(registered_name, 1, ProcessInfo) of
|
||||
{value, {_, Name}} when is_atom(Name) -> atom_to_list(Name);
|
||||
_ -> ""
|
||||
end.
|
||||
|
||||
ancestor(ProcessInfo) ->
|
||||
{value, {_, Dictionary}} = lists:keysearch(dictionary, 1, ProcessInfo),
|
||||
case lists:keysearch('$ancestors', 1, Dictionary) of
|
||||
{value, {_, [Ancestor|_T]}} when is_atom(Ancestor) ->
|
||||
atom_to_list(Ancestor);
|
||||
_ ->
|
||||
""
|
||||
end.
|
||||
|
||||
filter_pid(Pid, "ejabberd" ++ _, _InitialCall, _RegisteredName, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, "ejabberd" ++ _, _RegisteredName, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, "ejabberd"++_, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, "stringprep"++_, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, _RegisteredName, "ejabberd"++_) ->
|
||||
{true, Pid};
|
||||
filter_pid(_Pid, _CurrentFunction, _InitialCall, _RegisteredName, _Ancestor) ->
|
||||
false.
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+81
-32
@@ -5,7 +5,7 @@
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -48,8 +48,42 @@ start_link() ->
|
||||
|
||||
|
||||
init(_) ->
|
||||
ets:new(listen_sockets, [named_table, public]),
|
||||
bind_tcp_ports(),
|
||||
{ok, {{one_for_one, 10, 1}, []}}.
|
||||
|
||||
bind_tcp_ports() ->
|
||||
case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
ignore;
|
||||
Ls ->
|
||||
lists:foreach(
|
||||
fun({Port, Module, Opts}) ->
|
||||
ModuleRaw = strip_frontend(Module),
|
||||
case ModuleRaw:socket_type() of
|
||||
independent -> ok;
|
||||
_ ->
|
||||
bind_tcp_port(Port, Module, Opts)
|
||||
end
|
||||
end, Ls)
|
||||
end.
|
||||
|
||||
bind_tcp_port(PortIP, Module, RawOpts) ->
|
||||
try check_listener_options(RawOpts) of
|
||||
ok ->
|
||||
{Port, IPT, IPS, IPV, Proto, OptsClean} = parse_listener_portip(PortIP, RawOpts),
|
||||
{_Opts, SockOpts} = prepare_opts(IPT, IPV, OptsClean),
|
||||
case Proto of
|
||||
udp -> ok;
|
||||
_ ->
|
||||
ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS),
|
||||
ets:insert(listen_sockets, {PortIP, ListenSocket})
|
||||
end
|
||||
catch
|
||||
throw:{error, Error} ->
|
||||
?ERROR_MSG(Error, [])
|
||||
end.
|
||||
|
||||
start_listeners() ->
|
||||
case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
@@ -99,15 +133,7 @@ start_dependent(Port, Module, Opts) ->
|
||||
|
||||
init(PortIP, Module, RawOpts) ->
|
||||
{Port, IPT, IPS, IPV, Proto, OptsClean} = parse_listener_portip(PortIP, RawOpts),
|
||||
%% The first inet|inet6 and the last {ip, _} work,
|
||||
%% so overriding those in Opts
|
||||
Opts = [IPV | OptsClean] ++ [{ip, IPT}],
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
({backlog, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
{Opts, SockOpts} = prepare_opts(IPT, IPV, OptsClean),
|
||||
if Proto == udp ->
|
||||
init_udp(PortIP, Module, Opts, SockOpts, Port, IPS);
|
||||
true ->
|
||||
@@ -128,28 +154,39 @@ init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
end.
|
||||
|
||||
init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
SockOpts2 = try erlang:system_info(otp_release) >= "R13B" of
|
||||
true -> [{send_timeout_close, true} | SockOpts];
|
||||
false -> SockOpts
|
||||
catch
|
||||
_:_ -> []
|
||||
end,
|
||||
Res = gen_tcp:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true},
|
||||
{send_timeout, ?TCP_SEND_TIMEOUT},
|
||||
{keepalive, true} |
|
||||
SockOpts2]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
%% Inform my parent that this port was opened succesfully
|
||||
proc_lib:init_ack({ok, self()}),
|
||||
%% And now start accepting connection attempts
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
socket_error(Reason, PortIP, Module, SockOpts, Port, IPS)
|
||||
ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS),
|
||||
%% Inform my parent that this port was opened succesfully
|
||||
proc_lib:init_ack({ok, self()}),
|
||||
%% And now start accepting connection attempts
|
||||
accept(ListenSocket, Module, Opts).
|
||||
|
||||
listen_tcp(PortIP, Module, SockOpts, Port, IPS) ->
|
||||
case ets:lookup(listen_sockets, PortIP) of
|
||||
[{PortIP, ListenSocket}] ->
|
||||
?INFO_MSG("Reusing listening port for ~p", [Port]),
|
||||
ets:delete(listen_sockets, Port),
|
||||
ListenSocket;
|
||||
_ ->
|
||||
SockOpts2 = try erlang:system_info(otp_release) >= "R13B" of
|
||||
true -> [{send_timeout_close, true} | SockOpts];
|
||||
false -> SockOpts
|
||||
catch
|
||||
_:_ -> []
|
||||
end,
|
||||
Res = gen_tcp:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true},
|
||||
{send_timeout, ?TCP_SEND_TIMEOUT},
|
||||
{keepalive, true} |
|
||||
SockOpts2]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
ListenSocket;
|
||||
{error, Reason} ->
|
||||
socket_error(Reason, PortIP, Module, SockOpts, Port, IPS)
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (PortIP, Opts) -> {Port, IPT, IPS, IPV, OptsClean}
|
||||
@@ -194,6 +231,18 @@ parse_listener_portip(PortIP, Opts) ->
|
||||
end,
|
||||
{Port, IPT, IPS, IPV, Proto, OptsClean}.
|
||||
|
||||
prepare_opts(IPT, IPV, OptsClean) ->
|
||||
%% The first inet|inet6 and the last {ip, _} work,
|
||||
%% so overriding those in Opts
|
||||
Opts = [IPV | OptsClean] ++ [{ip, IPT}],
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
({backlog, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
{Opts, SockOpts}.
|
||||
|
||||
add_proto(Port, Opts) when is_integer(Port) ->
|
||||
{Port, get_proto(Opts)};
|
||||
add_proto({Port, Proto}, _Opts) when is_atom(Proto) ->
|
||||
|
||||
+19
-5
@@ -5,7 +5,7 @@
|
||||
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -34,10 +34,12 @@
|
||||
|
||||
-export([route/3,
|
||||
route_iq/4,
|
||||
route_iq/5,
|
||||
process_iq_reply/3,
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
register_iq_response_handler/4,
|
||||
register_iq_response_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
unregister_iq_response_handler/2,
|
||||
refresh_iq_handlers/0,
|
||||
@@ -123,19 +125,31 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
route_iq(From, To, #iq{type = Type} = IQ, F) when is_function(F) ->
|
||||
route_iq(From, To, IQ, F) ->
|
||||
route_iq(From, To, IQ, F, undefined).
|
||||
|
||||
route_iq(From, To, #iq{type = Type} = IQ, F, Timeout) when is_function(F) ->
|
||||
Packet = if Type == set; Type == get ->
|
||||
ID = randoms:get_string(),
|
||||
Host = From#jid.lserver,
|
||||
register_iq_response_handler(Host, ID, undefined, F),
|
||||
register_iq_response_handler(Host, ID, undefined, F, Timeout),
|
||||
jlib:iq_to_xml(IQ#iq{id = ID});
|
||||
true ->
|
||||
jlib:iq_to_xml(IQ)
|
||||
end,
|
||||
ejabberd_router:route(From, To, Packet).
|
||||
|
||||
register_iq_response_handler(_Host, ID, Module, Function) ->
|
||||
TRef = erlang:start_timer(?IQ_TIMEOUT, ejabberd_local, ID),
|
||||
register_iq_response_handler(Host, ID, Module, Function) ->
|
||||
register_iq_response_handler(Host, ID, Module, Function, undefined).
|
||||
|
||||
register_iq_response_handler(_Host, ID, Module, Function, Timeout0) ->
|
||||
Timeout = case Timeout0 of
|
||||
undefined ->
|
||||
?IQ_TIMEOUT;
|
||||
N when is_integer(N), N > 0 ->
|
||||
N
|
||||
end,
|
||||
TRef = erlang:start_timer(Timeout, ejabberd_local, ID),
|
||||
mnesia:dirty_write(#iq_response{id = ID,
|
||||
module = Module,
|
||||
function = Function,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 23 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -151,6 +151,19 @@ write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
|
||||
F = add_node("ERROR: ~p - ~p~n", Pid),
|
||||
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
|
||||
end;
|
||||
write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
|
||||
T = write_time(Time, "WARNING REPORT"),
|
||||
S = format_report(Rep),
|
||||
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
|
||||
write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
|
||||
T = write_time(Time, "WARNING REPORT"),
|
||||
case catch io_lib:format(add_node(Format,Pid), Args) of
|
||||
S when is_list(S) ->
|
||||
file:write(Fd, io_lib:format(T ++ S, []));
|
||||
_ ->
|
||||
F = add_node("ERROR: ~p - ~p~n", Pid),
|
||||
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
|
||||
end;
|
||||
write_event(_, _) ->
|
||||
ok.
|
||||
|
||||
|
||||
+118
-52
@@ -9,7 +9,7 @@
|
||||
%%% Created : 29 Nov 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -31,32 +31,59 @@
|
||||
-module(ejabberd_loglevel).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-export([set/1, get/0]).
|
||||
-export([set/1,
|
||||
get/0,
|
||||
set_custom/2,
|
||||
clear_custom/0,
|
||||
clear_custom/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-define(LOGMODULE, "error_logger").
|
||||
|
||||
%% Error levels:
|
||||
-define(LOG_LEVELS,[ {0, no_log, "No log"}
|
||||
,{1, critical, "Critical"}
|
||||
,{2, error, "Error"}
|
||||
,{3, warning, "Warning"}
|
||||
,{4, info, "Info"}
|
||||
,{5, debug, "Debug"}
|
||||
]).
|
||||
-record(loglevel, {ordinal,
|
||||
name,
|
||||
description,
|
||||
function = no_log,
|
||||
event_type = no_log,
|
||||
msg_prefix = no_log}).
|
||||
|
||||
-define(LOG_LEVELS,
|
||||
[#loglevel{ordinal = 0, name = no_log, description = "No log"},
|
||||
#loglevel{ordinal = 1, name = critical, description = "Critical",
|
||||
function = critical_msg, event_type = error, msg_prefix = "C"},
|
||||
#loglevel{ordinal = 2, name = error, description = "Error",
|
||||
function = error_msg, event_type = error, msg_prefix = "E"},
|
||||
#loglevel{ordinal = 3, name = warning, description = "Warning",
|
||||
function = warning_msg, event_type = warning_msg, msg_prefix = "W"},
|
||||
#loglevel{ordinal = 4, name = info, description = "Info",
|
||||
function = info_msg, event_type = info_msg, msg_prefix = "I"},
|
||||
#loglevel{ordinal = 5, name = debug, description = "Debug",
|
||||
function = debug_msg, event_type = info_msg, msg_prefix = "D"}]).
|
||||
|
||||
%% @type level() = integer() | atom().
|
||||
|
||||
%% @spec () -> {DefaultLevelOrdinal::integer(), [{Module::atom(), LevelOrdinal::integer()}]}
|
||||
%% @doc Get the default and all custom levels
|
||||
get() ->
|
||||
Level = ejabberd_logger:get(),
|
||||
case lists:keysearch(Level, 1, ?LOG_LEVELS) of
|
||||
{value, Result} -> Result;
|
||||
_ -> erlang:error({no_such_loglevel, Level})
|
||||
{DefaultLevel, _CustomLevels} = ejabberd_logger:get(),
|
||||
case lists:keysearch(DefaultLevel, #loglevel.ordinal, ?LOG_LEVELS) of
|
||||
{value, Result = #loglevel{}} ->
|
||||
{Result#loglevel.ordinal, Result#loglevel.name, Result#loglevel.description};
|
||||
_ ->
|
||||
erlang:error({no_such_loglevel, DefaultLevel})
|
||||
end.
|
||||
|
||||
|
||||
set(LogLevel) when is_atom(LogLevel) ->
|
||||
set(level_to_integer(LogLevel));
|
||||
set(Loglevel) when is_integer(Loglevel) ->
|
||||
%% @spec (DefaultLevel::level() | {DefaultLevel::level(), [{Module::atom(), Level::level()}]}) ->
|
||||
%% {module, ejabberd_logger}
|
||||
%% @doc Set the default and all custom levels
|
||||
set(DefaultLevel) when is_atom(DefaultLevel) orelse is_integer(DefaultLevel) ->
|
||||
set({DefaultLevel, []});
|
||||
set({DefaultLevel, CustomLevels}) when is_list(CustomLevels) ->
|
||||
DefaultInt = level_to_integer(DefaultLevel),
|
||||
CustomInts = [level_to_integer(C) || C <- CustomLevels],
|
||||
Loglevel = {DefaultInt, CustomInts},
|
||||
try
|
||||
{Mod,Code} = dynamic_compile:from_string(ejabberd_logger_src(Loglevel)),
|
||||
code:load_binary(Mod, ?LOGMODULE ++ ".erl", Code)
|
||||
@@ -64,11 +91,45 @@ set(Loglevel) when is_integer(Loglevel) ->
|
||||
Type:Error -> ?CRITICAL_MSG("Error compiling logger (~p): ~p~n", [Type, Error])
|
||||
end;
|
||||
set(_) ->
|
||||
exit("Loglevel must be an integer").
|
||||
exit("Invalid loglevel format").
|
||||
|
||||
%% @spec (Module::atom(), CustomLevel::level()) -> ok
|
||||
%% @doc Set a custom level
|
||||
set_custom(Module, Level) ->
|
||||
{DefaultLevel, CustomLevels} = ejabberd_logger:get(),
|
||||
case lists:keysearch(Module, 1, CustomLevels) of
|
||||
{value, {Module, Level}} ->
|
||||
ok;
|
||||
{value, _} ->
|
||||
set({DefaultLevel, lists:keyreplace(Module, 1, CustomLevels, {Module, Level})});
|
||||
_ ->
|
||||
set({DefaultLevel, [{Module, Level} | CustomLevels]})
|
||||
end.
|
||||
|
||||
%% @spec () -> ok
|
||||
%% @doc Clear all custom levels
|
||||
clear_custom() ->
|
||||
{DefaultLevel, _CustomLevels} = ejabberd_logger:get(),
|
||||
set({DefaultLevel, []}).
|
||||
|
||||
%% @spec (Module::atom()) -> ok
|
||||
%% @doc Clear a custom level
|
||||
clear_custom(Module) ->
|
||||
{DefaultLevel, CustomLevels} = ejabberd_logger:get(),
|
||||
case lists:keysearch(Module, 1, CustomLevels) of
|
||||
{value, _} ->
|
||||
set({DefaultLevel, lists:keydelete(Module, 1, CustomLevels)});
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
level_to_integer(Level) when is_integer(Level) ->
|
||||
Level;
|
||||
level_to_integer({Module, Level}) ->
|
||||
{Module, level_to_integer(Level)};
|
||||
level_to_integer(Level) ->
|
||||
case lists:keysearch(Level, 2, ?LOG_LEVELS) of
|
||||
{value, {Int, Level, _Desc}} -> Int;
|
||||
case lists:keysearch(Level, #loglevel.name, ?LOG_LEVELS) of
|
||||
{value, #loglevel{ordinal = Int}} -> Int;
|
||||
_ -> erlang:error({no_such_loglevel, Level})
|
||||
end.
|
||||
|
||||
@@ -77,7 +138,12 @@ level_to_integer(Level) ->
|
||||
%% This allows to dynamically change log level while keeping a
|
||||
%% very efficient code.
|
||||
ejabberd_logger_src(Loglevel) ->
|
||||
L = integer_to_list(Loglevel),
|
||||
lists:flatten([header_src(),
|
||||
get_src(Loglevel),
|
||||
[log_src(Loglevel, LevelSpec) || LevelSpec <- ?LOG_LEVELS],
|
||||
notify_src()]).
|
||||
|
||||
header_src() ->
|
||||
"-module(ejabberd_logger).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
@@ -87,42 +153,42 @@ ejabberd_logger_src(Loglevel) ->
|
||||
error_msg/4,
|
||||
critical_msg/4,
|
||||
get/0]).
|
||||
".
|
||||
|
||||
get() -> "++ L ++".
|
||||
get_src(Loglevel) ->
|
||||
io_lib:format("get() -> ~w.
|
||||
", [Loglevel]).
|
||||
|
||||
%% Helper functions
|
||||
debug_msg(Module, Line, Format, Args) when " ++ L ++ " >= 5 ->
|
||||
notify(info_msg,
|
||||
\"D(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line]++Args);
|
||||
debug_msg(_,_,_,_) -> ok.
|
||||
log_src(_Loglevel, #loglevel{function = no_log}) ->
|
||||
[];
|
||||
log_src({DefaultLevel, [{Module, Level} | Tail]}, Spec = #loglevel{ordinal = MinLevel})
|
||||
when Level < MinLevel andalso MinLevel =< DefaultLevel ->
|
||||
[atom_to_list(Spec#loglevel.function), "(", atom_to_list(Module), ", _, _, _) -> ok;
|
||||
", log_src({DefaultLevel, Tail}, Spec)];
|
||||
log_src({DefaultLevel, [{Module, Level} | Tail]}, Spec = #loglevel{ordinal = MinLevel})
|
||||
when DefaultLevel < MinLevel andalso MinLevel =< Level ->
|
||||
[atom_to_list(Spec#loglevel.function), "(", atom_to_list(Module), " = Module, Line, Format, Args) ->",
|
||||
log_notify_src(Spec), ";
|
||||
", log_src({DefaultLevel, Tail}, Spec)];
|
||||
log_src({DefaultLevel, [_Head | Tail]}, Spec = #loglevel{}) ->
|
||||
log_src({DefaultLevel, Tail}, Spec);
|
||||
log_src({DefaultLevel, []}, Spec = #loglevel{ordinal = MinLevel})
|
||||
when DefaultLevel < MinLevel ->
|
||||
[atom_to_list(Spec#loglevel.function), "(_, _, _, _) -> ok.
|
||||
"];
|
||||
log_src({_DefaultLevel, []}, Spec = #loglevel{}) ->
|
||||
[atom_to_list(Spec#loglevel.function), "(Module, Line, Format, Args) ->",
|
||||
log_notify_src(Spec), ".
|
||||
"].
|
||||
|
||||
info_msg(Module, Line, Format, Args) when " ++ L ++ " >= 4 ->
|
||||
notify(info_msg,
|
||||
\"I(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line]++Args);
|
||||
info_msg(_,_,_,_) -> ok.
|
||||
|
||||
warning_msg(Module, Line, Format, Args) when " ++ L ++ " >= 3 ->
|
||||
notify(error,
|
||||
\"W(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line]++Args);
|
||||
warning_msg(_,_,_,_) -> ok.
|
||||
|
||||
error_msg(Module, Line, Format, Args) when " ++ L ++ " >= 2 ->
|
||||
notify(error,
|
||||
\"E(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line]++Args);
|
||||
error_msg(_,_,_,_) -> ok.
|
||||
|
||||
critical_msg(Module, Line, Format, Args) when " ++ L ++ " >= 1 ->
|
||||
notify(error,
|
||||
\"C(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line]++Args);
|
||||
critical_msg(_,_,_,_) -> ok.
|
||||
log_notify_src(Spec = #loglevel{}) ->
|
||||
["notify(", atom_to_list(Spec#loglevel.event_type), ",
|
||||
\"", Spec#loglevel.msg_prefix, "(~p:~p:~p) : \"++Format++\"~n\",
|
||||
[self(), Module, Line | Args])"].
|
||||
|
||||
notify_src() ->
|
||||
%% Distribute the message to the Erlang error logger
|
||||
notify(Type, Format, Args) ->
|
||||
"notify(Type, Format, Args) ->
|
||||
LoggerMsg = {Type, group_leader(), {self(), Format, Args}},
|
||||
gen_event:notify(error_logger, LoggerMsg).
|
||||
".
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 1 Nov 2006 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -40,6 +40,12 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-ifdef(SSL40).
|
||||
-define(PG2, pg2).
|
||||
-else.
|
||||
-define(PG2, pg2_backport).
|
||||
-endif.
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%====================================================================
|
||||
@@ -54,20 +60,20 @@ start_link() ->
|
||||
|
||||
join(Name) ->
|
||||
PG = {?MODULE, Name},
|
||||
pg2:create(PG),
|
||||
pg2:join(PG, whereis(?MODULE)).
|
||||
?PG2:create(PG),
|
||||
?PG2:join(PG, whereis(?MODULE)).
|
||||
|
||||
leave(Name) ->
|
||||
PG = {?MODULE, Name},
|
||||
pg2:leave(PG, whereis(?MODULE)).
|
||||
?PG2:leave(PG, whereis(?MODULE)).
|
||||
|
||||
get_members(Name) ->
|
||||
PG = {?MODULE, Name},
|
||||
[node(P) || P <- pg2:get_members(PG)].
|
||||
[node(P) || P <- ?PG2:get_members(PG)].
|
||||
|
||||
get_closest_node(Name) ->
|
||||
PG = {?MODULE, Name},
|
||||
node(pg2:get_closest_pid(PG)).
|
||||
node(?PG2:get_closest_pid(PG)).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
|
||||
+28
-18
@@ -5,7 +5,7 @@
|
||||
%%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -153,8 +153,8 @@ process_elements(Elements,State) ->
|
||||
process_element(El=#xmlel{name=user, ns=_XMLNS},
|
||||
State=#parsing_state{host=Host}) ->
|
||||
case add_user(El,Host) of
|
||||
{error, _Other} -> error;
|
||||
_ -> ok
|
||||
ok -> ok;
|
||||
{error, _Other} -> error
|
||||
end,
|
||||
State;
|
||||
|
||||
@@ -196,34 +196,44 @@ process_element(El,State) ->
|
||||
add_user(El, Domain) ->
|
||||
User = exmpp_xml:get_attribute(El,name,none),
|
||||
Password = exmpp_xml:get_attribute(El,password,none),
|
||||
add_user(El, Domain, ?BTL(User), ?BTL(Password)).
|
||||
add_user(El, Domain, User, Password).
|
||||
|
||||
%% @spec (El::xmlel(), Domain::string(), User::string(), Password::string())
|
||||
%% -> ok | {atomic, exists} | {error, not_allowed}
|
||||
%% @spec (El::xmlel(), Domain::string(), User::binary(), Password::binary() | none)
|
||||
%% -> ok | {error, ErrorText::string()}
|
||||
%% @doc Add a new user to the database.
|
||||
%% If user already exists, it will be only updated.
|
||||
add_user(El, Domain, User, Password) ->
|
||||
add_user(El, Domain, UserBinary, none) ->
|
||||
User = ?BTL(UserBinary),
|
||||
io:format("Account ~s@~s will not be created, updating it...~n",
|
||||
[User, Domain]),
|
||||
io:format(""),
|
||||
populate_user_with_elements(El, Domain, User),
|
||||
ok;
|
||||
add_user(El, Domain, UserBinary, PasswordBinary) ->
|
||||
User = ?BTL(UserBinary),
|
||||
Password = ?BTL(PasswordBinary),
|
||||
case create_user(User,Password,Domain) of
|
||||
ok ->
|
||||
ok = exmpp_xml:foreach(
|
||||
fun(_,Child) ->
|
||||
populate_user(User,Domain,Child)
|
||||
end,
|
||||
El),
|
||||
populate_user_with_elements(El, Domain, User),
|
||||
ok;
|
||||
{atomic, exists} ->
|
||||
io:format("Account ~s@~s already exists, updating it...~n",
|
||||
[User, Domain]),
|
||||
io:format(""),
|
||||
ok = exmpp_xml:foreach(
|
||||
fun(_,Child) ->
|
||||
populate_user(User,Domain,Child)
|
||||
end,
|
||||
El);
|
||||
populate_user_with_elements(El, Domain, User),
|
||||
ok;
|
||||
{error, Other} ->
|
||||
?ERROR_MSG("Error adding user ~s@~s: ~p~n", [User, Domain, Other])
|
||||
?ERROR_MSG("Error adding user ~s@~s: ~p~n", [User, Domain, Other]),
|
||||
{error, Other}
|
||||
end.
|
||||
|
||||
populate_user_with_elements(El, Domain, User) ->
|
||||
exmpp_xml:foreach(
|
||||
fun (_,Child) ->
|
||||
populate_user(User,Domain,Child)
|
||||
end,
|
||||
El).
|
||||
|
||||
%% @spec (User::string(), Password::string(), Domain::string())
|
||||
%% -> ok | {atomic, exists} | {error, not_allowed}
|
||||
%% @doc Create a new user
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 10 Nov 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+13
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
%% API
|
||||
-export([route/3,
|
||||
route_error/4,
|
||||
register_route/1,
|
||||
register_route/2,
|
||||
register_routes/1,
|
||||
@@ -72,6 +73,17 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Route the error packet only if the originating packet is not an error itself.
|
||||
%% RFC3920 9.3.1
|
||||
route_error(From, To, ErrPacket, OrigPacket) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = OrigPacket,
|
||||
case "error" == xml:get_attr_s("type", Attrs) of
|
||||
false ->
|
||||
route(From, To, ErrPacket);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_route(Domain) ->
|
||||
register_route(Domain, undefined).
|
||||
|
||||
|
||||
+20
-19
@@ -5,7 +5,7 @@
|
||||
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -37,6 +37,7 @@
|
||||
get_connections_pids/1,
|
||||
try_register/1,
|
||||
remove_connection/3,
|
||||
find_connection/2,
|
||||
dirty_get_connections/0,
|
||||
allow_host/2,
|
||||
incoming_s2s_number/0,
|
||||
@@ -408,22 +409,21 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber,
|
||||
is_service(From, To) ->
|
||||
LFromDomain = From#jid.lserver,
|
||||
case ejabberd_config:get_local_option({route_subdomains, LFromDomain}) of
|
||||
s2s -> % bypass RFC 3920 10.3
|
||||
false;
|
||||
_ ->
|
||||
LDstDomain = To#jid.lserver,
|
||||
P = fun(Domain) -> is_subdomain(LDstDomain, Domain) end,
|
||||
lists:any(P, ?MYHOSTS)
|
||||
s2s -> % bypass RFC 3920 10.3
|
||||
false;
|
||||
_ ->
|
||||
Hosts = ?MYHOSTS,
|
||||
P = fun(ParentDomain) -> lists:member(ParentDomain, Hosts) end,
|
||||
lists:any(P, parent_domains(To#jid.lserver))
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: is_subdomain(Domain1, Domain2) -> true | false
|
||||
%% Description: Return true if Domain1 (a string representing an
|
||||
%% internet domain name) is a subdomain (or the same domain) of
|
||||
%% Domain2
|
||||
%% --------------------------------------------------------------------
|
||||
is_subdomain(Domain1, Domain2) ->
|
||||
lists:suffix(string:tokens(Domain2, "."), string:tokens(Domain1, ".")).
|
||||
parent_domains(Domain) ->
|
||||
lists:foldl(
|
||||
fun(Label, []) ->
|
||||
[Label];
|
||||
(Label, [Head | Tail]) ->
|
||||
[Label ++ "." ++ Head, Head | Tail]
|
||||
end, [], lists:reverse(string:tokens(Domain, "."))).
|
||||
|
||||
send_element(Pid, El) ->
|
||||
Pid ! {send_element, El}.
|
||||
@@ -486,10 +486,11 @@ update_tables() ->
|
||||
|
||||
%% Check if host is in blacklist or white list
|
||||
allow_host(MyServer, S2SHost) ->
|
||||
case lists:filter(
|
||||
fun(Host) ->
|
||||
is_subdomain(MyServer, Host)
|
||||
end, ?MYHOSTS) of
|
||||
Hosts = ?MYHOSTS,
|
||||
case lists:dropwhile(
|
||||
fun(ParentDomain) ->
|
||||
not lists:member(ParentDomain, Hosts)
|
||||
end, parent_domains(MyServer)) of
|
||||
[MyHost|_] ->
|
||||
allow_host1(MyHost, S2SHost);
|
||||
[] ->
|
||||
|
||||
+82
-30
@@ -5,7 +5,7 @@
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -48,6 +48,11 @@
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-ifdef(SSL40).
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
-define(PKIXEXPLICIT, 'OTP-PUB-KEY').
|
||||
-define(PKIXIMPLICIT, 'OTP-PUB-KEY').
|
||||
-else.
|
||||
-ifdef(SSL39).
|
||||
-include_lib("ssl/include/ssl_pkix.hrl").
|
||||
-define(PKIXEXPLICIT, 'OTP-PKIX').
|
||||
@@ -58,6 +63,7 @@
|
||||
-define(PKIXEXPLICIT, 'PKIX1Explicit88').
|
||||
-define(PKIXIMPLICIT, 'PKIX1Implicit88').
|
||||
-endif.
|
||||
-endif.
|
||||
-include("XmppAddr.hrl").
|
||||
|
||||
-define(DICT, dict).
|
||||
@@ -68,7 +74,10 @@
|
||||
shaper,
|
||||
tls = false,
|
||||
tls_enabled = false,
|
||||
tls_required = false,
|
||||
tls_certverify = false,
|
||||
tls_options = [],
|
||||
server,
|
||||
authenticated = false,
|
||||
auth_domain,
|
||||
connections = ?DICT:new(),
|
||||
@@ -144,12 +153,16 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
{value, {_, S}} -> S;
|
||||
_ -> none
|
||||
end,
|
||||
StartTLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
undefined ->
|
||||
false;
|
||||
UseStartTLS ->
|
||||
UseStartTLS
|
||||
end,
|
||||
{StartTLS, TLSRequired, TLSCertverify} = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
UseTls when (UseTls==undefined) or (UseTls==false) ->
|
||||
{false, false, false};
|
||||
UseTls when (UseTls==true) or (UseTls==optional) ->
|
||||
{true, false, false};
|
||||
required ->
|
||||
{true, true, false};
|
||||
required_trusted ->
|
||||
{true, true, true}
|
||||
end,
|
||||
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||
undefined ->
|
||||
[];
|
||||
@@ -164,6 +177,8 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
shaper = Shaper,
|
||||
tls = StartTLS,
|
||||
tls_enabled = false,
|
||||
tls_required = TLSRequired,
|
||||
tls_certverify = TLSCertverify,
|
||||
tls_options = TLSOpts,
|
||||
timer = Timer}}.
|
||||
|
||||
@@ -177,8 +192,9 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs),
|
||||
xml:get_attr_s("xmlns:db", Attrs),
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||
{"jabber:server", _, true} when
|
||||
{"jabber:server", _, Server, true} when
|
||||
StateData#state.tls and (not StateData#state.authenticated) ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
SASL =
|
||||
@@ -186,16 +202,18 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
StateData#state.tls_enabled ->
|
||||
case (StateData#state.sockmod):get_peer_certificate(
|
||||
StateData#state.socket) of
|
||||
{ok, _Cert} ->
|
||||
case (StateData#state.sockmod):get_verify_result(
|
||||
StateData#state.socket) of
|
||||
{ok, Cert} ->
|
||||
case (StateData#state.sockmod):get_verify_result(StateData#state.socket) of
|
||||
0 ->
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, "EXTERNAL"}]}]}];
|
||||
_ ->
|
||||
[]
|
||||
CertVerifyRes ->
|
||||
case StateData#state.tls_certverify of
|
||||
true -> {error_cert_verif, CertVerifyRes, Cert};
|
||||
false -> []
|
||||
end
|
||||
end;
|
||||
error ->
|
||||
[]
|
||||
@@ -206,21 +224,44 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
StartTLS = if
|
||||
StateData#state.tls_enabled ->
|
||||
[];
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}]
|
||||
(not StateData#state.tls_enabled) and (not StateData#state.tls_required) ->
|
||||
[{xmlelement, "starttls", [{"xmlns", ?NS_TLS}], []}];
|
||||
(not StateData#state.tls_enabled) and StateData#state.tls_required ->
|
||||
[{xmlelement, "starttls", [{"xmlns", ?NS_TLS}],
|
||||
[{xmlelement, "required", [], []}]
|
||||
}]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
SASL ++ StartTLS}),
|
||||
{next_state, wait_for_feature_request, StateData};
|
||||
{"jabber:server", _, true} when
|
||||
case SASL of
|
||||
{error_cert_verif, CertVerifyResult, Certificate} ->
|
||||
CertError = tls:get_cert_verify_string(CertVerifyResult, Certificate),
|
||||
RemoteServer = xml:get_attr_s("from", Attrs),
|
||||
?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)", [StateData#state.server, RemoteServer, CertError]),
|
||||
send_text(StateData, xml:element_to_string(?SERRT_POLICY_VIOLATION("en", CertError))),
|
||||
{atomic, Pid} = ejabberd_s2s:find_connection(jlib:make_jid("", Server, ""), jlib:make_jid("", RemoteServer, "")),
|
||||
ejabberd_s2s_out:stop_connection(Pid),
|
||||
|
||||
{stop, normal, StateData};
|
||||
_ ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
SASL ++ StartTLS ++
|
||||
ejabberd_hooks:run_fold(
|
||||
s2s_stream_features,
|
||||
Server,
|
||||
[], [Server])}),
|
||||
{next_state, wait_for_feature_request, StateData#state{server = Server}}
|
||||
end;
|
||||
{"jabber:server", _, Server, true} when
|
||||
StateData#state.authenticated ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{xmlelement, "stream:features", [],
|
||||
ejabberd_hooks:run_fold(
|
||||
s2s_stream_features,
|
||||
Server,
|
||||
[], [Server])}),
|
||||
{next_state, stream_established, StateData};
|
||||
{"jabber:server", "jabber:server:dialback", _} ->
|
||||
{"jabber:server", "jabber:server:dialback", _Server, _} ->
|
||||
send_text(StateData, ?STREAM_HEADER("")),
|
||||
{next_state, stream_established, StateData};
|
||||
_ ->
|
||||
@@ -251,15 +292,26 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
SockMod == gen_tcp ->
|
||||
?DEBUG("starttls", []),
|
||||
Socket = StateData#state.socket,
|
||||
TLSOpts = StateData#state.tls_options,
|
||||
TLSOpts = case ejabberd_config:get_local_option(
|
||||
{domain_certfile,
|
||||
StateData#state.server}) of
|
||||
undefined ->
|
||||
StateData#state.tls_options;
|
||||
CertFile ->
|
||||
[{certfile, CertFile} |
|
||||
lists:keydelete(
|
||||
certfile, 1,
|
||||
StateData#state.tls_options)]
|
||||
end,
|
||||
TLSSocket = (StateData#state.sockmod):starttls(
|
||||
Socket, TLSOpts,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
tls_enabled = true,
|
||||
tls_options = TLSOpts
|
||||
}};
|
||||
{?NS_SASL, "auth"} when TLSEnabled ->
|
||||
Mech = xml:get_attr_s("mechanism", Attrs),
|
||||
@@ -349,11 +401,11 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
LFrom = jlib:nameprep(From),
|
||||
%% Checks if the from domain is allowed and if the to
|
||||
%% domain is handled by this server:
|
||||
case {ejabberd_s2s:allow_host(To, From),
|
||||
case {ejabberd_s2s:allow_host(LTo, LFrom),
|
||||
lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
|
||||
{true, true} ->
|
||||
ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
|
||||
ejabberd_s2s_out:start(To, From,
|
||||
ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
|
||||
ejabberd_s2s_out:start(LTo, LFrom,
|
||||
{verify, self(),
|
||||
Key, StateData#state.streamid}),
|
||||
Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
|
||||
@@ -609,7 +661,7 @@ send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
|
||||
change_shaper(StateData, Host, JID) ->
|
||||
|
||||
+85
-26
@@ -5,7 +5,7 @@
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -44,6 +44,7 @@
|
||||
wait_for_features/2,
|
||||
wait_for_auth_result/2,
|
||||
wait_for_starttls_proceed/2,
|
||||
relay_to_bridge/2,
|
||||
reopen_socket/2,
|
||||
wait_before_retry/2,
|
||||
stream_established/2,
|
||||
@@ -51,6 +52,7 @@
|
||||
handle_sync_event/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
print_state/1,
|
||||
code_change/4,
|
||||
test_get_addr_port/1,
|
||||
get_addr_port/1]).
|
||||
@@ -64,13 +66,14 @@
|
||||
tls = false,
|
||||
tls_required = false,
|
||||
tls_enabled = false,
|
||||
tls_options = [],
|
||||
tls_options = [connect],
|
||||
authenticated = false,
|
||||
db_enabled = true,
|
||||
try_auth = true,
|
||||
myname, server, queue,
|
||||
delay_to_retry = undefined_delay,
|
||||
new = false, verify = false,
|
||||
bridge,
|
||||
timer}).
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
@@ -105,6 +108,7 @@
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"xmlns='jabber:server' "
|
||||
"xmlns:db='jabber:server:dialback' "
|
||||
"from='~s' "
|
||||
"to='~s'~s>"
|
||||
).
|
||||
|
||||
@@ -135,7 +139,7 @@ start_connection(Pid) ->
|
||||
p1_fsm:send_event(Pid, init).
|
||||
|
||||
stop_connection(Pid) ->
|
||||
p1_fsm:send_event(Pid, stop).
|
||||
p1_fsm:send_event(Pid, closed).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from p1_fsm
|
||||
@@ -151,16 +155,18 @@ stop_connection(Pid) ->
|
||||
init([From, Server, Type]) ->
|
||||
process_flag(trap_exit, true),
|
||||
?DEBUG("started: ~p", [{From, Server, Type}]),
|
||||
TLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
undefined ->
|
||||
false;
|
||||
UseStartTLS ->
|
||||
UseStartTLS
|
||||
{TLS, TLSRequired} = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
UseTls when (UseTls==undefined) or (UseTls==false) ->
|
||||
{false, false};
|
||||
UseTls when (UseTls==true) or (UseTls==optional) ->
|
||||
{true, false};
|
||||
UseTls when (UseTls==required) or (UseTls==required_trusted) ->
|
||||
{true, true}
|
||||
end,
|
||||
UseV10 = TLS,
|
||||
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||
undefined ->
|
||||
[];
|
||||
[connect];
|
||||
CertFile ->
|
||||
[{certfile, CertFile}, connect]
|
||||
end,
|
||||
@@ -174,6 +180,7 @@ init([From, Server, Type]) ->
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, open_socket, #state{use_v10 = UseV10,
|
||||
tls = TLS,
|
||||
tls_required = TLSRequired,
|
||||
tls_options = TLSOpts,
|
||||
queue = queue:new(),
|
||||
myname = From,
|
||||
@@ -191,7 +198,8 @@ init([From, Server, Type]) ->
|
||||
open_socket(init, StateData) ->
|
||||
log_s2s_out(StateData#state.new,
|
||||
StateData#state.myname,
|
||||
StateData#state.server),
|
||||
StateData#state.server,
|
||||
StateData#state.tls),
|
||||
?DEBUG("open_socket: ~p", [{StateData#state.myname,
|
||||
StateData#state.server,
|
||||
StateData#state.new,
|
||||
@@ -220,16 +228,27 @@ open_socket(init, StateData) ->
|
||||
tls_enabled = false,
|
||||
streamid = new_id()},
|
||||
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
[StateData#state.myname, StateData#state.server,
|
||||
Version])),
|
||||
{next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
|
||||
{error, _Reason} ->
|
||||
?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
wait_before_reconnect(StateData)
|
||||
%%{stop, normal, StateData}
|
||||
case ejabberd_hooks:run_fold(find_s2s_bridge,
|
||||
undefined,
|
||||
[StateData#state.myname,
|
||||
StateData#state.server]) of
|
||||
{Mod, Fun, Type} ->
|
||||
?INFO_MSG("found a bridge to ~s for: ~s -> ~s",
|
||||
[Type, StateData#state.myname,
|
||||
StateData#state.server]),
|
||||
NewStateData = StateData#state{bridge={Mod, Fun}},
|
||||
{next_state, relay_to_bridge, NewStateData};
|
||||
_ ->
|
||||
wait_before_reconnect(StateData)
|
||||
end
|
||||
end;
|
||||
open_socket(stop, StateData) ->
|
||||
open_socket(closed, StateData) ->
|
||||
?INFO_MSG("s2s connection: ~s -> ~s (stopped in open socket)",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
{stop, normal, StateData};
|
||||
@@ -297,6 +316,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
{"jabber:server", "jabber:server:dialback", true} when
|
||||
StateData#state.use_v10 ->
|
||||
{next_state, wait_for_features, StateData, ?FSMTIMEOUT};
|
||||
%% Clause added to handle Tigase's workaround for an old ejabberd bug:
|
||||
{"jabber:server", "jabber:server:dialback", true} when
|
||||
not StateData#state.use_v10 ->
|
||||
send_db_request(StateData);
|
||||
{"jabber:server", "", true} when StateData#state.use_v10 ->
|
||||
{next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT};
|
||||
{NSProvided, DB, _} ->
|
||||
@@ -336,16 +359,21 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
case is_verify_res(El) of
|
||||
{result, To, From, Id, Type} ->
|
||||
?DEBUG("recv result: ~p", [{From, To, Id, Type}]),
|
||||
case Type of
|
||||
"valid" ->
|
||||
case {Type, StateData#state.tls_enabled, StateData#state.tls_required} of
|
||||
{"valid", Enabled, Required} when (Enabled==true) or (Required==false) ->
|
||||
send_queue(StateData, StateData#state.queue),
|
||||
?INFO_MSG("Connection established: ~s -> ~s",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
?INFO_MSG("Connection established: ~s -> ~s with TLS=~p",
|
||||
[StateData#state.myname, StateData#state.server, StateData#state.tls_enabled]),
|
||||
ejabberd_hooks:run(s2s_connect_hook,
|
||||
[StateData#state.myname,
|
||||
StateData#state.server]),
|
||||
{next_state, stream_established,
|
||||
StateData#state{queue = queue:new()}};
|
||||
{"valid", Enabled, Required} when (Enabled==false) and (Required==true) ->
|
||||
%% TODO: bounce packets
|
||||
?INFO_MSG("Closing s2s connection: ~s -> ~s (TLS is required but unavailable)",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
{stop, normal, StateData};
|
||||
_ ->
|
||||
%% TODO: bounce packets
|
||||
?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid dialback key)",
|
||||
@@ -538,7 +566,7 @@ wait_for_auth_result({xmlstreamelement, El}, StateData) ->
|
||||
ejabberd_socket:reset_stream(StateData#state.socket),
|
||||
send_text(StateData,
|
||||
io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
[StateData#state.myname, StateData#state.server,
|
||||
" version='1.0'"])),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
@@ -606,7 +634,7 @@ wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
||||
Socket = StateData#state.socket,
|
||||
TLSOpts = case ejabberd_config:get_local_option(
|
||||
{domain_certfile,
|
||||
StateData#state.server}) of
|
||||
StateData#state.myname}) of
|
||||
undefined ->
|
||||
StateData#state.tls_options;
|
||||
CertFile ->
|
||||
@@ -618,11 +646,12 @@ wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
||||
TLSSocket = ejabberd_socket:starttls(Socket, TLSOpts),
|
||||
NewStateData = StateData#state{socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
tls_enabled = true,
|
||||
tls_options = TLSOpts
|
||||
},
|
||||
send_text(NewStateData,
|
||||
io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
[StateData#state.myname, StateData#state.server,
|
||||
" version='1.0'"])),
|
||||
{next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
|
||||
_ ->
|
||||
@@ -675,6 +704,15 @@ reopen_socket(closed, StateData) ->
|
||||
wait_before_retry(_Event, StateData) ->
|
||||
{next_state, wait_before_retry, StateData, ?FSMTIMEOUT}.
|
||||
|
||||
relay_to_bridge(stop, StateData) ->
|
||||
wait_before_reconnect(StateData);
|
||||
relay_to_bridge(closed, StateData) ->
|
||||
?INFO_MSG("relay to bridge: ~s -> ~s (closed)",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
{stop, normal, StateData};
|
||||
relay_to_bridge(_Event, StateData) ->
|
||||
{next_state, relay_to_bridge, StateData}.
|
||||
|
||||
stream_established({xmlstreamelement, El}, StateData) ->
|
||||
?DEBUG("s2S stream established", []),
|
||||
case is_verify_res(El) of
|
||||
@@ -825,6 +863,19 @@ handle_info({send_element, El}, StateName, StateData) ->
|
||||
wait_before_retry ->
|
||||
bounce_element(El, ?ERR_REMOTE_SERVER_NOT_FOUND),
|
||||
{next_state, StateName, StateData};
|
||||
relay_to_bridge ->
|
||||
%% In this state we relay all outbound messages
|
||||
%% to a foreign protocol bridge such as SMTP, SIP, etc.
|
||||
{Mod, Fun} = StateData#state.bridge,
|
||||
?DEBUG("relaying stanza via ~p:~p/1", [Mod, Fun]),
|
||||
case catch Mod:Fun(El) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("Error while relaying to bridge: ~p", [Reason]),
|
||||
bounce_element(El, ?ERR_INTERNAL_SERVER_ERROR),
|
||||
wait_before_reconnect(StateData);
|
||||
_ ->
|
||||
{next_state, StateName, StateData}
|
||||
end;
|
||||
_ ->
|
||||
Q = queue:in(El, StateData#state.queue),
|
||||
{next_state, StateName, StateData#state{queue = Q},
|
||||
@@ -875,6 +926,14 @@ terminate(Reason, StateName, StateData) ->
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) ->
|
||||
State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -883,7 +942,7 @@ send_text(StateData, Text) ->
|
||||
ejabberd_socket:send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
send_queue(StateData, Q) ->
|
||||
case queue:out(Q) of
|
||||
@@ -1142,10 +1201,10 @@ outgoing_s2s_timeout() ->
|
||||
|
||||
%% Human readable S2S logging: Log only new outgoing connections as INFO
|
||||
%% Do not log dialback
|
||||
log_s2s_out(false, _, _) -> ok;
|
||||
log_s2s_out(false, _, _, _) -> ok;
|
||||
%% Log new outgoing connections:
|
||||
log_s2s_out(_, Myname, Server) ->
|
||||
?INFO_MSG("Trying to open s2s connection: ~s -> ~s",[Myname, Server]).
|
||||
log_s2s_out(_, Myname, Server, Tls) ->
|
||||
?INFO_MSG("Trying to open s2s connection: ~s -> ~s with TLS=~p", [Myname, Server, Tls]).
|
||||
|
||||
%% Calculate timeout depending on which state we are in:
|
||||
%% Can return integer > 0 | infinity
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -47,7 +47,8 @@
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
terminate/3,
|
||||
print_state/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@@ -346,12 +347,15 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
Text = xml:element_to_binary({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text);
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route_error(To, From, Err, Packet)
|
||||
end,
|
||||
{next_state, StateName, StateData};
|
||||
handle_info(Info, StateName, StateData) ->
|
||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
|
||||
@@ -374,6 +378,14 @@ terminate(Reason, StateName, StateData) ->
|
||||
(StateData#state.sockmod):close(StateData#state.socket),
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) ->
|
||||
State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -382,7 +394,7 @@ send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
||||
+20
-4
@@ -5,7 +5,7 @@
|
||||
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -47,6 +47,7 @@
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
force_update_presence/1,
|
||||
connected_users/0,
|
||||
connected_users_number/0,
|
||||
user_resources/2,
|
||||
@@ -572,9 +573,14 @@ route_message(From, To, Packet) ->
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
case is_privacy_allow(From, To, Packet) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
@@ -711,6 +717,16 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
force_update_presence({LUser, _LServer} = US) ->
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
ok;
|
||||
Ss ->
|
||||
lists:foreach(fun(#session{sid = {_, Pid}}) ->
|
||||
Pid ! {force_update_presence, LUser}
|
||||
end, Ss)
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% ejabberd commands
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -87,7 +87,13 @@ start(Module, SockMod, Socket, Opts) ->
|
||||
end,
|
||||
ReceiverMod:become_controller(Receiver, Pid);
|
||||
{error, _Reason} ->
|
||||
SockMod:close(Socket)
|
||||
SockMod:close(Socket),
|
||||
case ReceiverMod of
|
||||
ejabberd_receiver ->
|
||||
ReceiverMod:close(Receiver);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end;
|
||||
independent ->
|
||||
ok;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -177,6 +177,13 @@ init([]) ->
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
CacheTabSupervisor =
|
||||
{cache_tab_sup,
|
||||
{cache_tab_sup, start_link, []},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[cache_tab_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
NodeGroups,
|
||||
@@ -196,6 +203,7 @@ init([]) ->
|
||||
IQSupervisor,
|
||||
STUNSupervisor,
|
||||
FrontendSocketSupervisor,
|
||||
CacheTabSupervisor,
|
||||
Listener]}}.
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 21 Mar 2007 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+12
-5
@@ -5,7 +5,7 @@
|
||||
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -84,7 +84,7 @@ update_info() ->
|
||||
update_info(Dir, Files) ->
|
||||
Beams = lists:sort(get_beams(Files)),
|
||||
UpdatedBeams = get_updated_beams(Beams),
|
||||
?INFO_MSG("beam files: ~p~n", [UpdatedBeams]),
|
||||
?DEBUG("beam files: ~p~n", [UpdatedBeams]),
|
||||
{Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams),
|
||||
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}.
|
||||
|
||||
@@ -124,14 +124,21 @@ get_current_version(Module) ->
|
||||
%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
|
||||
build_script(Dir, UpdatedBeams) ->
|
||||
Script = make_script(UpdatedBeams),
|
||||
?INFO_MSG("script: ~p~n", [Script]),
|
||||
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
|
||||
?INFO_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
Check =
|
||||
release_handler_1:check_script(
|
||||
LowLevelScript,
|
||||
[{ejabberd, "", filename:join(Dir, "..")}]),
|
||||
?INFO_MSG("check: ~p~n", [Check]),
|
||||
case Check of
|
||||
ok ->
|
||||
?DEBUG("script: ~p~n", [Script]),
|
||||
?DEBUG("low level script: ~p~n", [LowLevelScript]),
|
||||
?DEBUG("check: ~p~n", [Check]);
|
||||
_ ->
|
||||
?ERROR_MSG("script: ~p~n", [Script]),
|
||||
?ERROR_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
?ERROR_MSG("check: ~p~n", [Check])
|
||||
end,
|
||||
{Script, LowLevelScript, Check}.
|
||||
|
||||
%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
* ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -32,6 +32,15 @@ typedef struct {
|
||||
z_stream *i_stream;
|
||||
} ejabberd_zlib_data;
|
||||
|
||||
static void* zlib_alloc(void* data, unsigned int items, unsigned int size)
|
||||
{
|
||||
return (void*) driver_alloc(items*size);
|
||||
}
|
||||
|
||||
static void zlib_free(void* data, void* addr)
|
||||
{
|
||||
driver_free(addr);
|
||||
}
|
||||
|
||||
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
@@ -39,18 +48,18 @@ static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
|
||||
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
|
||||
d->port = port;
|
||||
|
||||
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
d->d_stream = (z_stream *)driver_alloc(sizeof(z_stream));
|
||||
|
||||
d->d_stream->zalloc = (alloc_func)0;
|
||||
d->d_stream->zfree = (free_func)0;
|
||||
d->d_stream->zalloc = zlib_alloc;
|
||||
d->d_stream->zfree = zlib_free;
|
||||
d->d_stream->opaque = (voidpf)0;
|
||||
|
||||
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
|
||||
|
||||
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
d->i_stream = (z_stream *)driver_alloc(sizeof(z_stream));
|
||||
|
||||
d->i_stream->zalloc = (alloc_func)0;
|
||||
d->i_stream->zfree = (free_func)0;
|
||||
d->i_stream->zalloc = zlib_alloc;
|
||||
d->i_stream->zfree = zlib_free;
|
||||
d->i_stream->opaque = (voidpf)0;
|
||||
|
||||
inflateInit(d->i_stream);
|
||||
@@ -65,10 +74,10 @@ static void ejabberd_zlib_drv_stop(ErlDrvData handle)
|
||||
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
|
||||
|
||||
deflateEnd(d->d_stream);
|
||||
free(d->d_stream);
|
||||
driver_free(d->d_stream);
|
||||
|
||||
inflateEnd(d->i_stream);
|
||||
free(d->i_stream);
|
||||
driver_free(d->i_stream);
|
||||
|
||||
driver_free((char *)handle);
|
||||
}
|
||||
|
||||
@@ -50,12 +50,39 @@
|
||||
#
|
||||
#FIREWALL_WINDOW=
|
||||
|
||||
#.
|
||||
#' INET_DIST_INTERFACE: IP address where this Erlang node listens other nodes
|
||||
#
|
||||
# This communication is used by ejabberdctl command line tool,
|
||||
# and in a cluster of several ejabberd nodes.
|
||||
# Notice that the IP address must be specified in the Erlang syntax.
|
||||
#
|
||||
# Default: {127,0,0,1}
|
||||
#
|
||||
INET_DIST_INTERFACE={127,0,0,1}
|
||||
|
||||
#.
|
||||
#' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections
|
||||
#
|
||||
# IMPORTANT: This option works only in Erlang/OTP R14B03 and newer.
|
||||
#
|
||||
# This environment variable may be set to a comma-separated
|
||||
# list of IP addresses, in which case the epmd daemon
|
||||
# will listen only on the specified address(es) and on the
|
||||
# loopback address (which is implicitly added to the list if it
|
||||
# has not been specified). The default behaviour is to listen on
|
||||
# all available IP addresses.
|
||||
#
|
||||
# Default: 0.0.0.0
|
||||
#
|
||||
#ERL_EPMD_ADDRESS=127.0.0.1
|
||||
|
||||
#.
|
||||
#' ERL_PROCESSES: Maximum number of Erlang processes
|
||||
#
|
||||
# Erlang consumes a lot of lightweight processes. If there is a lot of activity
|
||||
# on ejabberd so that the maximum number of processes is reached, people will
|
||||
# experiment greater latency times. As these processes are implemented in
|
||||
# experience greater latency times. As these processes are implemented in
|
||||
# Erlang, and therefore not related to the operating system processes, you do
|
||||
# not have to worry about allowing a huge number of them.
|
||||
#
|
||||
|
||||
@@ -76,13 +76,15 @@ fi
|
||||
NAME=-name
|
||||
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
|
||||
|
||||
if [ "$FIREWALL_WINDOW" = "" ] ; then
|
||||
KERNEL_OPTS=""
|
||||
else
|
||||
KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
KERNEL_OPTS=""
|
||||
if [ "$FIREWALL_WINDOW" != "" ] ; then
|
||||
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
fi
|
||||
if [ "$INET_DIST_INTERFACE" != "" ] ; then
|
||||
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${INET_DIST_INTERFACE}\""
|
||||
fi
|
||||
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS $KERNEL_OPTS"
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
|
||||
# define additional environment variables
|
||||
if [ "$EJABBERDDIR" = "" ]; then
|
||||
@@ -114,6 +116,10 @@ HOME=$SPOOLDIR
|
||||
# create the home dir with the proper user if doesn't exist, because it stores cookie file
|
||||
[ -d $HOME ] || $EXEC_CMD "mkdir -p $HOME"
|
||||
|
||||
# Change to a directory readable by INSTALLUSER to
|
||||
# prevent "File operation error: eacces." messages
|
||||
cd $HOME
|
||||
|
||||
# export global variables
|
||||
export EJABBERD_CONFIG_PATH
|
||||
export EJABBERD_MSGS_PATH
|
||||
@@ -123,6 +129,7 @@ export EJABBERD_BIN_PATH
|
||||
export EJABBERD_DOC_PATH
|
||||
export EJABBERD_PID_PATH
|
||||
export ERL_CRASH_DUMP
|
||||
export ERL_EPMD_ADDRESS
|
||||
export ERL_INETRC
|
||||
export ERL_MAX_PORTS
|
||||
export ERL_MAX_ETS_TABLES
|
||||
@@ -141,6 +148,7 @@ start ()
|
||||
-noinput -detached \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd \
|
||||
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
@@ -174,6 +182,7 @@ debug ()
|
||||
$NAME debug-${TTY}-${ERLANG_NODE} \
|
||||
-remsh $ERLANG_NODE \
|
||||
-hidden \
|
||||
$KERNEL_OPTS \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
|
||||
@@ -203,6 +212,7 @@ live ()
|
||||
$NAME $ERLANG_NODE \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
@@ -236,7 +246,7 @@ ctl ()
|
||||
MAXCONNID=100
|
||||
CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
|
||||
FLOCK='/usr/bin/flock'
|
||||
if [ ! -x "$FLOCK" ] ; then
|
||||
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
|
||||
JOT='/usr/bin/jot'
|
||||
if [ ! -x "$JOT" ] ; then
|
||||
# no flock or jot, simply invoke ctlexec()
|
||||
@@ -305,12 +315,21 @@ ctlexec ()
|
||||
{
|
||||
CONN_NAME=$1; shift
|
||||
COMMAND=$@
|
||||
$EXEC_CMD "$ERL \
|
||||
|
||||
CTLEXEC="$ERL \
|
||||
$NAME ${CONN_NAME} \
|
||||
-noinput \
|
||||
-hidden \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd_ctl -extra $ERLANG_NODE"
|
||||
|
||||
# quote input from the command line
|
||||
for i in $COMMAND; do
|
||||
CTLEXEC="$CTLEXEC '$i'";
|
||||
done
|
||||
|
||||
$EXEC_CMD "$CTLEXEC"
|
||||
}
|
||||
|
||||
# display ctl usage
|
||||
|
||||
+4
-5
@@ -5,7 +5,7 @@
|
||||
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -141,8 +141,7 @@ export_offline(Server, Output) ->
|
||||
TimeStamp))]},
|
||||
XML =
|
||||
ejabberd_odbc:escape(
|
||||
lists:flatten(
|
||||
xml:element_to_string(NewPacket))),
|
||||
xml:element_to_binary(NewPacket)),
|
||||
["insert into spool(username, xml) "
|
||||
"values ('", Username, "', '",
|
||||
XML,
|
||||
@@ -176,7 +175,7 @@ export_vcard(Server, Output) ->
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SVCARD = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(VCARD))),
|
||||
xml:element_to_binary(VCARD)),
|
||||
["delete from vcard where username='", Username, "';"
|
||||
"insert into vcard(username, vcard) "
|
||||
"values ('", Username, "', '", SVCARD, "');"];
|
||||
@@ -260,7 +259,7 @@ export_private_storage(Server, Output) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LXMLNS = ejabberd_odbc:escape(XMLNS),
|
||||
SData = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(Data))),
|
||||
xml:element_to_binary(Data)),
|
||||
odbc_queries:set_private_data_sql(Username, LXMLNS, SData);
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
|
||||
@@ -286,6 +286,16 @@ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||
responseName [10] LDAPOID OPTIONAL,
|
||||
response [11] OCTET STRING OPTIONAL }
|
||||
|
||||
passwdModifyOID LDAPOID ::= "1.3.6.1.4.1.4203.1.11.1"
|
||||
|
||||
PasswdModifyRequestValue ::= SEQUENCE {
|
||||
userIdentity [0] OCTET STRING OPTIONAL,
|
||||
oldPasswd [1] OCTET STRING OPTIONAL,
|
||||
newPasswd [2] OCTET STRING OPTIONAL }
|
||||
|
||||
PasswdModifyResponseValue ::= SEQUENCE {
|
||||
genPasswd [0] OCTET STRING OPTIONAL }
|
||||
|
||||
END
|
||||
|
||||
|
||||
|
||||
+12
-4
@@ -1,4 +1,4 @@
|
||||
# $Id$
|
||||
# $Id: Makefile.in 2842 2009-12-29 19:10:52Z badlop $
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
@@ -11,6 +11,7 @@ ASN_FLAGS = -bber_bin +optimize +driver
|
||||
ERLANG_CFLAGS = @ERLANG_CFLAGS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
EFLAGS += @ERLANG_SSLVER@
|
||||
EFLAGS += -I ..
|
||||
EFLAGS += -pz ..
|
||||
|
||||
@@ -20,18 +21,23 @@ ifdef debug
|
||||
endif
|
||||
|
||||
OUTDIR = ..
|
||||
SOURCES = $(wildcard *.erl) ELDAPv3.erl
|
||||
SOURCES = $(wildcard *.erl) ELDAPv3.erl eldap_filter_yecc.erl
|
||||
BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam))
|
||||
|
||||
|
||||
all: $(BEAMS) ELDAPv3.beam
|
||||
all: $(BEAMS) ELDAPv3.beam eldap_filter_yecc.beam
|
||||
|
||||
ELDAPv3.beam: ELDAPv3.erl
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
@ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
eldap_filter_yecc.beam: eldap_filter_yecc.erl
|
||||
|
||||
eldap_filter_yecc.erl: eldap_filter_yecc.yrl
|
||||
@ERLC@ -W $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl eldap_filter_yecc.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
clean:
|
||||
@@ -39,6 +45,8 @@ clean:
|
||||
rm -f ELDAPv3.erl
|
||||
rm -f ELDAPv3.hrl
|
||||
rm -f ELDAPv3.beam
|
||||
rm -f eldap_filter_yecc.erl
|
||||
rm -f eldap_filter_yecc.beam
|
||||
rm -f $(BEAMS)
|
||||
|
||||
distclean: clean
|
||||
|
||||
@@ -4,7 +4,7 @@ include ..\Makefile.inc
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OUTDIR = ..
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam ..\eldap_filter_yecc.beam
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
@@ -15,11 +15,16 @@ Clean :
|
||||
-@erase ELDAPv3.erl
|
||||
-@erase ELDAPv3.hrl
|
||||
-@erase ELDAPv3.beam
|
||||
-@erase eldap_filter_yecc.erl
|
||||
-@erase eldap_filter_yecc.beam
|
||||
-@erase $(BEAMS)
|
||||
|
||||
ELDAPv3.erl : ELDAPv3.asn
|
||||
erlc $(ASN_FLAGS) -W $(EFLAGS) ELDAPv3.asn
|
||||
|
||||
eldap_filter_yecc.erl: eldap_filter_yecc.yrl
|
||||
erlc -W eldap_filter_yecc.yrl
|
||||
|
||||
$(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl
|
||||
|
||||
@@ -34,3 +39,6 @@ $(OUTDIR)\eldap_utils.beam : eldap_utils.erl
|
||||
|
||||
$(OUTDIR)\eldap_pool.beam : eldap_pool.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap_pool.erl
|
||||
|
||||
$(OUTDIR)\eldap_filter_yecc.beam : eldap_filter_yecc.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap_filter_yecc.erl
|
||||
|
||||
+91
-24
@@ -33,8 +33,11 @@
|
||||
|
||||
%%% Modified by Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
%%% Modified by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%% Modified by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% Implemented queue for bind() requests to prevent pending binds.
|
||||
%%% Implemented extensibleMatch/2 function.
|
||||
%%% Implemented LDAP Extended Operations (currently only Password Modify
|
||||
%%% is supported - RFC 3062).
|
||||
|
||||
%%% Modified by Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% Improve error case handling
|
||||
@@ -71,9 +74,9 @@
|
||||
|
||||
-export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
|
||||
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
|
||||
approxMatch/2,search/2,substrings/2,present/1,
|
||||
approxMatch/2,search/2,substrings/2,present/1,extensibleMatch/2,
|
||||
'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
|
||||
mod_replace/2, add/3, delete/2, modify_dn/5, bind/3]).
|
||||
mod_replace/2, add/3, delete/2, modify_dn/5, modify_passwd/3, bind/3]).
|
||||
-export([get_status/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
@@ -127,9 +130,10 @@ start_link(Name) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
|
||||
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd, Encrypt) ->
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd, Opts) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd, Encrypt}, []).
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE,
|
||||
{Hosts, Port, Rootdn, Passwd, Opts}, []).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Get status of connection.
|
||||
@@ -239,6 +243,10 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
|
||||
{modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)},
|
||||
?CALL_TIMEOUT).
|
||||
|
||||
modify_passwd(Handle, DN, Passwd) when is_list(DN), is_list(Passwd) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(
|
||||
Handle1, {modify_passwd, DN, Passwd}, ?CALL_TIMEOUT).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Bind.
|
||||
@@ -374,6 +382,29 @@ substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
|
||||
{substrings,#'SubstringFilter'{type = Type,
|
||||
substrings = Ss}}.
|
||||
|
||||
%%%
|
||||
%%% extensibleMatch filter.
|
||||
%%% FIXME: Describe the purpose of this filter.
|
||||
%%%
|
||||
%%% Value ::= string( <attribute> )
|
||||
%%% Opts ::= listof( {matchingRule, Str} | {type, Str} | {dnAttributes, true} )
|
||||
%%%
|
||||
%%% Example: extensibleMatch("Fred", [{matchingRule, "1.2.3.4.5"}, {type, "cn"}]).
|
||||
%%%
|
||||
extensibleMatch(Value, Opts) when is_list(Value), is_list(Opts) ->
|
||||
MRA = #'MatchingRuleAssertion'{matchValue=Value},
|
||||
{extensibleMatch, extensibleMatch_opts(Opts, MRA)}.
|
||||
|
||||
extensibleMatch_opts([{matchingRule, Rule} | Opts], MRA) when is_list(Rule) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{matchingRule=Rule});
|
||||
extensibleMatch_opts([{type, Desc} | Opts], MRA) when is_list(Desc) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{type=Desc});
|
||||
extensibleMatch_opts([{dnAttributes, true} | Opts], MRA) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{dnAttributes=true});
|
||||
extensibleMatch_opts([_ | Opts], MRA) ->
|
||||
extensibleMatch_opts(Opts, MRA);
|
||||
extensibleMatch_opts([], MRA) ->
|
||||
MRA.
|
||||
|
||||
get_handle(Pid) when is_pid(Pid) -> Pid;
|
||||
get_handle(Atom) when is_atom(Atom) -> Atom;
|
||||
@@ -393,15 +424,18 @@ get_handle(Name) when is_list(Name) -> list_to_atom("eldap_" ++ Name).
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
case get_config() of
|
||||
{ok, Hosts, Rootdn, Passwd, Encrypt} ->
|
||||
init({Hosts, Rootdn, Passwd, Encrypt});
|
||||
{ok, Hosts, Rootdn, Passwd, Opts} ->
|
||||
init({Hosts, Rootdn, Passwd, Opts});
|
||||
{error, Reason} ->
|
||||
{stop, Reason}
|
||||
end;
|
||||
init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
|
||||
init({Hosts, Port, Rootdn, Passwd, Opts}) ->
|
||||
catch ssl:start(),
|
||||
{X1,X2,X3} = erlang:now(),
|
||||
ssl:seed(integer_to_list(X1) ++ integer_to_list(X2) ++ integer_to_list(X3)),
|
||||
ssl:seed(randoms:get_string()),
|
||||
Encrypt = case proplists:get_value(encrypt, Opts) of
|
||||
tls -> tls;
|
||||
_ -> none
|
||||
end,
|
||||
PortTemp = case Port of
|
||||
undefined ->
|
||||
case Encrypt of
|
||||
@@ -414,7 +448,14 @@ init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
|
||||
end;
|
||||
PT -> PT
|
||||
end,
|
||||
TLSOpts = [verify_none],
|
||||
TLSOpts = case proplists:get_value(tls_verify, Opts) of
|
||||
soft ->
|
||||
[{verify, 1}];
|
||||
hard ->
|
||||
[{verify, 2}];
|
||||
_ ->
|
||||
[{verify, 0}]
|
||||
end,
|
||||
{ok, connecting, #eldap{hosts = Hosts,
|
||||
port = PortTemp,
|
||||
rootdn = Rootdn,
|
||||
@@ -671,6 +712,16 @@ gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) ->
|
||||
deleteoldrdn = DelOldRDN,
|
||||
newSuperior = NewSup}};
|
||||
|
||||
gen_req({modify_passwd, DN, Passwd}) ->
|
||||
{ok, ReqVal} = asn1rt:encode(
|
||||
'ELDAPv3', 'PasswdModifyRequestValue',
|
||||
#'PasswdModifyRequestValue'{
|
||||
userIdentity = DN,
|
||||
newPasswd = Passwd}),
|
||||
{extendedReq,
|
||||
#'ExtendedRequest'{requestName = ?passwdModifyOID,
|
||||
requestValue = list_to_binary(ReqVal)}};
|
||||
|
||||
gen_req({bind, RootDN, Passwd}) ->
|
||||
{bindRequest,
|
||||
#'BindRequest'{version = ?LDAP_VERSION,
|
||||
@@ -745,6 +796,11 @@ recvd_packet(Pkt, S) ->
|
||||
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),
|
||||
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),
|
||||
@@ -769,6 +825,15 @@ check_bind_reply(#'BindResponse'{resultCode = Reason}, _From) ->
|
||||
check_bind_reply(Other, _From) ->
|
||||
{error, Other}.
|
||||
|
||||
%% TODO: process reply depending on requestName:
|
||||
%% this requires BER-decoding of #'ExtendedResponse'.response
|
||||
check_extended_reply(#'ExtendedResponse'{resultCode = success}, _From) ->
|
||||
ok;
|
||||
check_extended_reply(#'ExtendedResponse'{resultCode = Reason}, _From) ->
|
||||
{error, Reason};
|
||||
check_extended_reply(Other, _From) ->
|
||||
{error, Other}.
|
||||
|
||||
get_op_rec(Id, Dict) ->
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Timer, _Command, From, Name}|Res]} ->
|
||||
@@ -904,7 +969,7 @@ connect_bind(S) ->
|
||||
tls ->
|
||||
SockMod = ssl,
|
||||
SslOpts = [{packet, asn1}, {active, true}, {keepalive, true},
|
||||
binary],
|
||||
binary | S#eldap.tls_options],
|
||||
ssl:connect(Host, S#eldap.port, SslOpts);
|
||||
%% starttls -> %% TODO: Implement STARTTLS;
|
||||
_ ->
|
||||
@@ -973,6 +1038,8 @@ v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};
|
||||
v_filter({approxMatch,AV}) -> {approxMatch,AV};
|
||||
v_filter({present,A}) -> {present,A};
|
||||
v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S};
|
||||
v_filter({extensibleMatch, S}) when is_record(S, 'MatchingRuleAssertion') ->
|
||||
{extensibleMatch, S};
|
||||
v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).
|
||||
|
||||
v_modifications(Mods) ->
|
||||
@@ -1018,8 +1085,8 @@ get_config() ->
|
||||
case file:consult(File) of
|
||||
{ok, Entries} ->
|
||||
case catch parse(Entries) of
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Encrypt} ->
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Encrypt};
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Opts} ->
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Opts};
|
||||
{error, Reason} ->
|
||||
{error, Reason};
|
||||
{'EXIT', Reason} ->
|
||||
@@ -1035,7 +1102,7 @@ parse(Entries) ->
|
||||
get_integer(port, Entries),
|
||||
get_list(rootdn, Entries),
|
||||
get_list(passwd, Entries),
|
||||
get_atom(encrypt, Entries)}.
|
||||
get_list(options, Entries)}.
|
||||
|
||||
get_integer(Key, List) ->
|
||||
case lists:keysearch(Key, 1, List) of
|
||||
@@ -1057,15 +1124,15 @@ get_list(Key, List) ->
|
||||
throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
end.
|
||||
|
||||
get_atom(Key, List) ->
|
||||
case lists:keysearch(Key, 1, List) of
|
||||
{value, {Key, Value}} when is_atom(Value) ->
|
||||
Value;
|
||||
{value, {Key, _Value}} ->
|
||||
throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
|
||||
false ->
|
||||
throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
end.
|
||||
%% get_atom(Key, List) ->
|
||||
%% case lists:keysearch(Key, 1, List) of
|
||||
%% {value, {Key, Value}} when is_atom(Value) ->
|
||||
%% Value;
|
||||
%% {value, {Key, _Value}} ->
|
||||
%% throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
|
||||
%% false ->
|
||||
%% throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
%% end.
|
||||
|
||||
get_hosts(Key, List) ->
|
||||
lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A),
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+65
-190
@@ -3,10 +3,10 @@
|
||||
%%% Purpose: Converts String Representation of
|
||||
%%% LDAP Search Filter (RFC 2254)
|
||||
%%% to eldap's representation of filter
|
||||
%%% Author: Evgeniy Khramtsov <xramtsov@gmail.com>
|
||||
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -24,19 +24,17 @@
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(eldap_filter).
|
||||
|
||||
%%%======================
|
||||
%%% Export functions
|
||||
%%%======================
|
||||
%% TODO: remove this when new regexp module will be used
|
||||
-compile({nowarn_deprecated_function, {regexp, sub, 3}}).
|
||||
|
||||
-export([parse/1,
|
||||
parse/2,
|
||||
do_sub/2
|
||||
]).
|
||||
-export([parse/1, parse/2, do_sub/2]).
|
||||
|
||||
%%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Arity: parse/1
|
||||
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} |
|
||||
%%% {error, bad_filter}
|
||||
@@ -47,15 +45,15 @@
|
||||
%%% to eldap's representation of filter.
|
||||
%%%
|
||||
%%% Example:
|
||||
%%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))").
|
||||
%%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))").
|
||||
%%%
|
||||
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
|
||||
%%% {present,"mail"}]}}
|
||||
%%%-------------------------------------------------------------------------
|
||||
parse(RFC2254_Filter) ->
|
||||
parse(RFC2254_Filter, []).
|
||||
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
|
||||
%%% {present,"mail"}]}}
|
||||
%%%-------------------------------------------------------------------
|
||||
parse(L) when is_list(L) ->
|
||||
parse(L, []).
|
||||
|
||||
%%%-------------------------------------------------------------------------
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Arity: parse/2
|
||||
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) ->
|
||||
%%% {ok, EldapFilter} |
|
||||
@@ -81,135 +79,53 @@ parse(RFC2254_Filter) ->
|
||||
%%% {equalityMatch,{'AttributeValueAssertion',
|
||||
%%% "jid",
|
||||
%%% "xramtsov@gmail.com"}}]}}
|
||||
%%%--------------------------------------------------------------------------
|
||||
parse(RFC2254_Filter, ListOfSubValues) ->
|
||||
case catch convert_filter(parse_filter(RFC2254_Filter), ListOfSubValues) of
|
||||
[EldapFilter] when is_tuple(EldapFilter) ->
|
||||
{ok, EldapFilter};
|
||||
{regexp, Error} ->
|
||||
{error, Error};
|
||||
_ ->
|
||||
{error, bad_filter}
|
||||
%%%-------------------------------------------------------------------
|
||||
parse(L, SList) when is_list(L), is_list(SList) ->
|
||||
case catch eldap_filter_yecc:parse(scan(L, SList)) of
|
||||
{error, {_, _, Msg}} ->
|
||||
{error, Msg};
|
||||
{ok, Result} ->
|
||||
{ok, Result};
|
||||
{regexp, Err} ->
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
%%%==========================
|
||||
%%% Internal functions
|
||||
%%%==========================
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
-define(do_scan(L), scan(Rest, [], [{L, 1} | check(Buf, S) ++ Result], L, S)).
|
||||
|
||||
%%%----------------------
|
||||
%%% split/1,4
|
||||
%%%----------------------
|
||||
split(Filter) ->
|
||||
split(Filter, 0, [], []).
|
||||
scan(L, SList) ->
|
||||
scan(L, "", [], undefined, SList).
|
||||
|
||||
split([], _, _, Result) ->
|
||||
Result;
|
||||
scan("=*)" ++ Rest, Buf, Result, '(', S) ->
|
||||
scan(Rest, [], [{')', 1}, {'=*', 1} | check(Buf, S) ++ Result], ')', S);
|
||||
scan(":dn" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':dn');
|
||||
scan(":=" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':=');
|
||||
scan(":=" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':=');
|
||||
scan(":=" ++ Rest, Buf, Result, ':', S) -> ?do_scan(':=');
|
||||
scan("~=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('~=');
|
||||
scan(">=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('>=');
|
||||
scan("<=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('<=');
|
||||
scan("=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('=');
|
||||
scan(":" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':');
|
||||
scan(":" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':');
|
||||
scan("&" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('&');
|
||||
scan("|" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('|');
|
||||
scan("!" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('!');
|
||||
scan("*" ++ Rest, Buf, Result, '*', S) -> ?do_scan('*');
|
||||
scan("*" ++ Rest, Buf, Result, '=', S) -> ?do_scan('*');
|
||||
scan("(" ++ Rest, Buf, Result, _, S) -> ?do_scan('(');
|
||||
scan(")" ++ Rest, Buf, Result, _, S) -> ?do_scan(')');
|
||||
scan([Letter | Rest], Buf, Result, PreviosAtom, S) ->
|
||||
scan(Rest, [Letter|Buf], Result, PreviosAtom, S);
|
||||
scan([], Buf, Result, _, S) ->
|
||||
lists:reverse(check(Buf, S) ++ Result).
|
||||
|
||||
split([H|T], Num, Rest, Result) ->
|
||||
NewNum = case H of
|
||||
$( -> Num + 1;
|
||||
$) -> Num - 1;
|
||||
_ -> Num
|
||||
end,
|
||||
if
|
||||
NewNum == 0 ->
|
||||
X = Rest++[H],
|
||||
LenX = length(X),
|
||||
if
|
||||
LenX > 2 ->
|
||||
split(T, 0, [], Result ++ [lists:sublist(X, 2, LenX-2)]);
|
||||
true ->
|
||||
split(T, 0, Rest, Result)
|
||||
end;
|
||||
true ->
|
||||
split(T, NewNum, Rest++[H], Result)
|
||||
end.
|
||||
|
||||
%%%-----------------------
|
||||
%%% parse_filter/1
|
||||
%%%-----------------------
|
||||
parse_filter(Filter) ->
|
||||
case Filter of
|
||||
[$! | T] ->
|
||||
{'not', parse_filter(T)};
|
||||
[$| | T] ->
|
||||
{'or', parse_filter(T)};
|
||||
[$& | T] ->
|
||||
{'and', parse_filter(T)};
|
||||
[$( | _] ->
|
||||
parse_filter(split(Filter));
|
||||
[List | _] when is_list(List) ->
|
||||
[parse_filter(X) || X <- Filter];
|
||||
_ ->
|
||||
Filter
|
||||
end.
|
||||
|
||||
%%%--------------------
|
||||
%%% convert_filter/2
|
||||
%%%--------------------
|
||||
convert_filter({'not', [Val | _]}, Replace) ->
|
||||
eldap:'not'(convert_filter(Val, Replace));
|
||||
|
||||
convert_filter({'or', Vals}, Replace) ->
|
||||
eldap:'or'([convert_filter(X, Replace) || X <- Vals]);
|
||||
|
||||
convert_filter({'and', Vals}, Replace) ->
|
||||
eldap:'and'([convert_filter(X, Replace) || X <- Vals]);
|
||||
|
||||
convert_filter([H|_] = Filter, Replace) when is_integer(H) ->
|
||||
parse_attr(Filter, Replace);
|
||||
|
||||
convert_filter(Filter, Replace) when is_list(Filter) ->
|
||||
[convert_filter(X, Replace) || X <- Filter].
|
||||
|
||||
%%%-----------------
|
||||
%%% parse_attr/2,3
|
||||
%%%-----------------
|
||||
parse_attr(Attr, ListOfSubValues) ->
|
||||
{Action, [_|_] = Name, [_|_] = Value} = split_attribute(Attr),
|
||||
parse_attr(Action, {Name, Value}, ListOfSubValues).
|
||||
|
||||
parse_attr(approx, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:approxMatch(Name, NewValue);
|
||||
|
||||
parse_attr(greater, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:greaterOrEqual(Name, NewValue);
|
||||
|
||||
parse_attr(less, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:lessOrEqual(Name, NewValue);
|
||||
|
||||
parse_attr(equal, {Name, Value}, ListOfSubValues) ->
|
||||
{ok, RegSList} = regexp:split(remove_extra_asterisks(Value), "[*]"),
|
||||
Pattern = case [do_sub(X, ListOfSubValues) || X <- RegSList] of
|
||||
[Head | Tail] when Tail /= [] ->
|
||||
{Head, lists:sublist(Tail, length(Tail)-1), lists:last(Tail)};
|
||||
R ->
|
||||
R
|
||||
end,
|
||||
case Pattern of
|
||||
[V] ->
|
||||
eldap:equalityMatch(Name, V);
|
||||
{[], [], []} ->
|
||||
eldap:present(Name);
|
||||
{"", Any, ""} ->
|
||||
eldap:substrings(Name, [{any, X} || X<-Any]);
|
||||
{H, Any, ""} ->
|
||||
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]);
|
||||
{"", Any, T} ->
|
||||
eldap:substrings(Name, [{any, X} || X<-Any]++[{final, T}]);
|
||||
{H, Any, T} ->
|
||||
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]++[{final, T}])
|
||||
end;
|
||||
|
||||
parse_attr(_, _, _) ->
|
||||
false.
|
||||
|
||||
%%%--------------------
|
||||
%%% do_sub/2,3
|
||||
%%%--------------------
|
||||
check([], _) ->
|
||||
[];
|
||||
check(Buf, S) ->
|
||||
[{str, 1, do_sub(lists:reverse(Buf), S)}].
|
||||
|
||||
-define(MAX_RECURSION, 100).
|
||||
|
||||
@@ -234,9 +150,9 @@ do_sub(S, {RegExp, New}, Iter) ->
|
||||
{ok, NewS, _} when Iter =< ?MAX_RECURSION ->
|
||||
do_sub(NewS, {RegExp, New}, Iter+1);
|
||||
{ok, _, _} when Iter > ?MAX_RECURSION ->
|
||||
throw({regexp, max_substitute_recursion});
|
||||
erlang:error(max_substitute_recursion);
|
||||
_ ->
|
||||
throw({regexp, bad_regexp})
|
||||
erlang:error(bad_regexp)
|
||||
end;
|
||||
|
||||
do_sub(S, {_, _, N}, _) when N<1 ->
|
||||
@@ -251,52 +167,11 @@ do_sub(S, {RegExp, New, Times}, Iter) ->
|
||||
{ok, NewS, _} ->
|
||||
NewS;
|
||||
_ ->
|
||||
throw({regexp, bad_regexp})
|
||||
erlang:error(bad_regexp)
|
||||
end.
|
||||
|
||||
remove_extra_asterisks(String) ->
|
||||
{Res, _} = lists:foldl(
|
||||
fun(X, {Acc, Last}) ->
|
||||
case X of
|
||||
$* when Last==$* ->
|
||||
{Acc, X};
|
||||
_ ->
|
||||
{Acc ++ [X], X}
|
||||
end
|
||||
end,
|
||||
{"", ""}, String),
|
||||
Res.
|
||||
|
||||
replace_amps(String) ->
|
||||
lists:foldl(
|
||||
fun(X, Acc) ->
|
||||
if
|
||||
X == $& ->
|
||||
Acc ++ "\\&";
|
||||
true ->
|
||||
Acc ++ [X]
|
||||
end
|
||||
end,
|
||||
"", String).
|
||||
|
||||
split_attribute(String) ->
|
||||
split_attribute(String, "", $0).
|
||||
|
||||
split_attribute([], _, _) ->
|
||||
{error, "", ""};
|
||||
|
||||
split_attribute([H|Tail], Acc, Last) ->
|
||||
case H of
|
||||
$= when Last==$> ->
|
||||
{greater, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$< ->
|
||||
{less, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$~ ->
|
||||
{approx, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$: ->
|
||||
{equal, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= ->
|
||||
{equal, Acc, Tail};
|
||||
_ ->
|
||||
split_attribute(Tail, Acc++[H], H)
|
||||
end.
|
||||
lists:flatmap(
|
||||
fun($&) -> "\\&";
|
||||
(Chr) -> [Chr]
|
||||
end, String).
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
Nonterminals
|
||||
filter filtercomp filterlist item
|
||||
simple present substring extensible
|
||||
initial any final matchingrule xattr
|
||||
attr value.
|
||||
|
||||
Terminals str
|
||||
'(' ')' '&' '|' '!' '=' '~=' '>=' '<=' '=*' '*' ':dn' ':' ':='.
|
||||
|
||||
Rootsymbol filter.
|
||||
|
||||
filter -> '(' filtercomp ')': '$2'.
|
||||
filtercomp -> '&' filterlist: 'and'('$2').
|
||||
filtercomp -> '|' filterlist: 'or'('$2').
|
||||
filtercomp -> '!' filter: 'not'('$2').
|
||||
filtercomp -> item: '$1'.
|
||||
filterlist -> filter: '$1'.
|
||||
filterlist -> filter filterlist: flatten(['$1', '$2']).
|
||||
|
||||
item -> simple: '$1'.
|
||||
item -> present: '$1'.
|
||||
item -> substring: '$1'.
|
||||
item -> extensible: '$1'.
|
||||
|
||||
simple -> attr '=' value: equal('$1', '$3').
|
||||
simple -> attr '~=' value: approx('$1', '$3').
|
||||
simple -> attr '>=' value: greater('$1', '$3').
|
||||
simple -> attr '<=' value: less('$1', '$3').
|
||||
|
||||
present -> attr '=*': present('$1').
|
||||
|
||||
substring -> attr '=' initial '*' any: substrings('$1', ['$3', '$5']).
|
||||
substring -> attr '=' '*' any final: substrings('$1', ['$4', '$5']).
|
||||
substring -> attr '=' initial '*' any final: substrings('$1', ['$3', '$5', '$6']).
|
||||
substring -> attr '=' '*' any: substrings('$1', ['$4']).
|
||||
any -> any value '*': 'any'('$1', '$2').
|
||||
any -> '$empty': [].
|
||||
initial -> value: initial('$1').
|
||||
final -> value: final('$1').
|
||||
|
||||
extensible -> xattr ':dn' ':' matchingrule ':=' value: extensible('$6', ['$1', '$4']).
|
||||
extensible -> xattr ':' matchingrule ':=' value: extensible('$5', ['$1', '$3']).
|
||||
extensible -> xattr ':dn' ':=' value: extensible('$4', ['$1']).
|
||||
extensible -> xattr ':=' value: extensible('$3', ['$1']).
|
||||
extensible -> ':dn' ':' matchingrule ':=' value: extensible('$5', ['$3']).
|
||||
extensible -> ':' matchingrule ':=' value: extensible('$4', ['$2']).
|
||||
xattr -> value: xattr('$1').
|
||||
matchingrule -> value: matchingrule('$1').
|
||||
|
||||
attr -> str: value_of('$1').
|
||||
value -> str: value_of('$1').
|
||||
|
||||
Erlang code.
|
||||
|
||||
'and'(Value) -> eldap:'and'(Value).
|
||||
'or'(Value) -> eldap:'or'(Value).
|
||||
'not'(Value) -> eldap:'not'(Value).
|
||||
equal(Desc, Value) -> eldap:equalityMatch(Desc, Value).
|
||||
approx(Desc, Value) -> eldap:approxMatch(Desc, Value).
|
||||
greater(Desc, Value) -> eldap:greaterOrEqual(Desc, Value).
|
||||
less(Desc, Value) -> eldap:lessOrEqual(Desc, Value).
|
||||
present(Value) -> eldap:present(Value).
|
||||
extensible(Value, Opts) -> eldap:extensibleMatch(Value, Opts).
|
||||
substrings(Desc, ValueList) -> eldap:substrings(Desc, flatten(ValueList)).
|
||||
initial(Value) -> {initial, Value}.
|
||||
final(Value) -> {final, Value}.
|
||||
'any'(Token, Value) -> [Token, {any, Value}].
|
||||
xattr(Value) -> {type, Value}.
|
||||
matchingrule(Value) -> {matchingRule, Value}.
|
||||
value_of(Token) -> element(3, Token).
|
||||
flatten(List) -> lists:flatten(List).
|
||||
+28
-14
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Nov 2006 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -31,11 +31,18 @@
|
||||
-export([
|
||||
start_link/7,
|
||||
bind/3,
|
||||
search/2
|
||||
search/2,
|
||||
modify_passwd/3
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-ifdef(SSL40).
|
||||
-define(PG2, pg2).
|
||||
-else.
|
||||
-define(PG2, pg2_backport).
|
||||
-endif.
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
@@ -45,26 +52,33 @@ bind(PoolName, DN, Passwd) ->
|
||||
search(PoolName, Opts) ->
|
||||
do_request(PoolName, {search, [Opts]}).
|
||||
|
||||
start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Encrypt) ->
|
||||
modify_passwd(PoolName, DN, Passwd) ->
|
||||
do_request(PoolName, {modify_passwd, [DN, Passwd]}).
|
||||
|
||||
start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Opts) ->
|
||||
PoolName = make_id(Name),
|
||||
pg2:create(PoolName),
|
||||
lists:foreach(fun(Host) ->
|
||||
ID = erlang:ref_to_list(make_ref()),
|
||||
case catch eldap:start_link(ID, [Host|Backups], Port, Rootdn, Passwd, Encrypt) of
|
||||
{ok, Pid} ->
|
||||
pg2:join(PoolName, Pid);
|
||||
_ ->
|
||||
error
|
||||
end
|
||||
end, Hosts).
|
||||
?PG2:create(PoolName),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ID = erlang:ref_to_list(make_ref()),
|
||||
case catch eldap:start_link(ID, [Host|Backups], Port,
|
||||
Rootdn, Passwd, Opts) of
|
||||
{ok, Pid} ->
|
||||
?PG2:join(PoolName, Pid);
|
||||
_ ->
|
||||
error
|
||||
end
|
||||
end, Hosts).
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
do_request(Name, {F, Args}) ->
|
||||
case pg2:get_closest_pid(make_id(Name)) of
|
||||
case ?PG2:get_closest_pid(make_id(Name)) of
|
||||
Pid when is_pid(Pid) ->
|
||||
case catch apply(eldap, F, [Pid | Args]) of
|
||||
{'EXIT', {timeout, _}} ->
|
||||
?ERROR_MSG("LDAP request failed: timed out", []);
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("LDAP request failed: eldap:~p(~p)~nReason: ~p",
|
||||
[F, Args, Reason]),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+77
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
* ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -35,12 +35,41 @@
|
||||
#define PARSE_FINAL_COMMAND 1
|
||||
|
||||
ei_x_buff event_buf;
|
||||
ei_x_buff xmlns_buf;
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
XML_Parser parser;
|
||||
} expat_data;
|
||||
|
||||
static XML_Memory_Handling_Suite ms = {driver_alloc, driver_realloc, driver_free};
|
||||
|
||||
void encode_name(const XML_Char *name)
|
||||
{
|
||||
char *name_start;
|
||||
char *prefix_start;
|
||||
char *buf;
|
||||
int name_len, prefix_len, buf_len;
|
||||
|
||||
if ((name_start = strchr(name, '\n'))) {
|
||||
if ((prefix_start = strchr(name_start+1, '\n'))) {
|
||||
name_len = prefix_start - name_start;
|
||||
prefix_len = strlen(prefix_start+1);
|
||||
buf_len = prefix_len + name_len;
|
||||
buf = driver_alloc(buf_len);
|
||||
memcpy(buf, prefix_start+1, prefix_len);
|
||||
memcpy(buf+prefix_len, name_start, name_len);
|
||||
buf[prefix_len] = ':';
|
||||
ei_x_encode_string_len(&event_buf, buf, buf_len);
|
||||
driver_free(buf);
|
||||
} else {
|
||||
ei_x_encode_string(&event_buf, name_start+1);
|
||||
};
|
||||
} else {
|
||||
ei_x_encode_string(&event_buf, name);
|
||||
}
|
||||
}
|
||||
|
||||
void *erlXML_StartElementHandler(expat_data *d,
|
||||
const XML_Char *name,
|
||||
const XML_Char **atts)
|
||||
@@ -51,7 +80,10 @@ void *erlXML_StartElementHandler(expat_data *d,
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_START);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_string(&event_buf, name);
|
||||
encode_name(name);
|
||||
ei_x_append(&event_buf, &xmlns_buf);
|
||||
ei_x_free(&xmlns_buf);
|
||||
ei_x_new(&xmlns_buf);
|
||||
|
||||
for (i = 0; atts[i]; i += 2) {}
|
||||
|
||||
@@ -62,7 +94,7 @@ void *erlXML_StartElementHandler(expat_data *d,
|
||||
for (i = 0; atts[i]; i += 2)
|
||||
{
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_string(&event_buf, atts[i]);
|
||||
encode_name(atts[i]);
|
||||
ei_x_encode_string(&event_buf, atts[i+1]);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +110,7 @@ void *erlXML_EndElementHandler(expat_data *d,
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_END);
|
||||
ei_x_encode_string(&event_buf, name);
|
||||
encode_name(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -93,12 +125,45 @@ void *erlXML_CharacterDataHandler(expat_data *d,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *erlXML_StartNamespaceDeclHandler(expat_data *d,
|
||||
const XML_Char *prefix,
|
||||
const XML_Char *uri)
|
||||
{
|
||||
int prefix_len;
|
||||
char *buf;
|
||||
|
||||
/* From the expat documentation:
|
||||
"For a default namespace declaration (xmlns='...'),
|
||||
the prefix will be null ...
|
||||
... The URI will be null for the case where
|
||||
the default namespace is being unset."
|
||||
|
||||
FIXME: I'm not quite sure what all that means */
|
||||
if (uri == NULL)
|
||||
return NULL;
|
||||
|
||||
ei_x_encode_list_header(&xmlns_buf, 1);
|
||||
ei_x_encode_tuple_header(&xmlns_buf, 2);
|
||||
if (prefix) {
|
||||
prefix_len = strlen(prefix);
|
||||
buf = driver_alloc(7 + prefix_len);
|
||||
strcpy(buf, "xmlns:");
|
||||
strcpy(buf+6, prefix);
|
||||
ei_x_encode_string(&xmlns_buf, buf);
|
||||
driver_free(buf);
|
||||
} else {
|
||||
ei_x_encode_string(&xmlns_buf, "xmlns");
|
||||
};
|
||||
ei_x_encode_string(&xmlns_buf, uri);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
|
||||
d->port = port;
|
||||
d->parser = XML_ParserCreate("UTF-8");
|
||||
d->parser = XML_ParserCreate_MM("UTF-8", &ms, "\n");
|
||||
XML_SetUserData(d->parser, d);
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
@@ -110,6 +175,11 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
||||
XML_SetCharacterDataHandler(
|
||||
d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler);
|
||||
|
||||
XML_SetStartNamespaceDeclHandler(
|
||||
d->parser, (XML_StartNamespaceDeclHandler) erlXML_StartNamespaceDeclHandler);
|
||||
XML_SetReturnNSTriplet(d->parser, 1);
|
||||
|
||||
XML_SetDefaultHandler(d->parser, NULL);
|
||||
|
||||
return (ErlDrvData)d;
|
||||
}
|
||||
@@ -136,6 +206,7 @@ static int expat_erl_control(ErlDrvData drv_data,
|
||||
case PARSE_COMMAND:
|
||||
case PARSE_FINAL_COMMAND:
|
||||
ei_x_new_with_version(&event_buf);
|
||||
ei_x_new(&xmlns_buf);
|
||||
res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND);
|
||||
|
||||
if(!res)
|
||||
@@ -159,6 +230,7 @@ static int expat_erl_control(ErlDrvData drv_data,
|
||||
memcpy(b->orig_bytes, event_buf.buff, size);
|
||||
|
||||
ei_x_free(&event_buf);
|
||||
ei_x_free(&xmlns_buf);
|
||||
|
||||
*rbuf = (char *)b;
|
||||
return size;
|
||||
|
||||
+83
-17
@@ -5,7 +5,7 @@
|
||||
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,8 +27,15 @@
|
||||
-module(extauth).
|
||||
-author('leifj@it.su.se').
|
||||
|
||||
-export([start/2, stop/1, init/2,
|
||||
check_password/3, set_password/3, is_user_exists/2]).
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
init/2,
|
||||
check_password/3,
|
||||
set_password/3,
|
||||
try_register/3,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
is_user_exists/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -36,16 +43,36 @@
|
||||
-define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000
|
||||
|
||||
start(Host, ExtPrg) ->
|
||||
spawn(?MODULE, init, [Host, ExtPrg]).
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
start_instance(get_process_name(Host, This), ExtPrg)
|
||||
end,
|
||||
lists:seq(0, get_instances(Host)-1)
|
||||
).
|
||||
|
||||
init(Host, ExtPrg) ->
|
||||
register(gen_mod:get_module_proc(Host, eauth), self()),
|
||||
start_instance(ProcessName, ExtPrg) ->
|
||||
spawn(?MODULE, init, [ProcessName, ExtPrg]).
|
||||
|
||||
restart_instance(ProcessName, ExtPrg) ->
|
||||
unregister(ProcessName),
|
||||
start_instance(ProcessName, ExtPrg).
|
||||
|
||||
init(ProcessName, ExtPrg) ->
|
||||
register(ProcessName, self()),
|
||||
process_flag(trap_exit,true),
|
||||
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||
loop(Port, ?INIT_TIMEOUT).
|
||||
loop(Port, ?INIT_TIMEOUT, ProcessName, ExtPrg).
|
||||
|
||||
stop(Host) ->
|
||||
gen_mod:get_module_proc(Host, eauth) ! stop.
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
get_process_name(Host, This) ! stop
|
||||
end,
|
||||
lists:seq(0, get_instances(Host)-1)
|
||||
).
|
||||
|
||||
get_process_name(Host, Integer) ->
|
||||
gen_mod:get_module_proc(lists:append([Host, integer_to_list(Integer)]), eauth).
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
call_port(Server, ["auth", User, Server, Password]).
|
||||
@@ -56,31 +83,59 @@ is_user_exists(User, Server) ->
|
||||
set_password(User, Server, Password) ->
|
||||
call_port(Server, ["setpass", User, Server, Password]).
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case call_port(Server, ["tryregister", User, Server, Password]) of
|
||||
true -> {atomic, ok};
|
||||
false -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
call_port(Server, ["removeuser", User, Server]).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
call_port(Server, ["removeuser3", User, Server, Password]).
|
||||
|
||||
call_port(Server, Msg) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
gen_mod:get_module_proc(LServer, eauth) ! {call, self(), Msg},
|
||||
ProcessName = get_process_name(LServer, random_instance(get_instances(LServer))),
|
||||
ProcessName ! {call, self(), Msg},
|
||||
receive
|
||||
{eauth,Result} ->
|
||||
Result
|
||||
end.
|
||||
|
||||
loop(Port, Timeout) ->
|
||||
random_instance(MaxNum) ->
|
||||
{A1,A2,A3} = now(),
|
||||
random:seed(A1, A2, A3),
|
||||
random:uniform(MaxNum) - 1.
|
||||
|
||||
get_instances(Server) ->
|
||||
case ejabberd_config:get_local_option({extauth_instances, Server}) of
|
||||
Num when is_integer(Num) -> Num;
|
||||
_ -> 1
|
||||
end.
|
||||
|
||||
loop(Port, Timeout, ProcessName, ExtPrg) ->
|
||||
receive
|
||||
{call, Caller, Msg} ->
|
||||
Port ! {self(), {command, encode(Msg)}},
|
||||
port_command(Port, encode(Msg)),
|
||||
receive
|
||||
{Port, {data, Data}} ->
|
||||
?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]),
|
||||
Caller ! {eauth, decode(Data)};
|
||||
Caller ! {eauth, decode(Data)},
|
||||
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg);
|
||||
{Port, Other} ->
|
||||
?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]),
|
||||
Caller ! {eauth, false}
|
||||
Caller ! {eauth, false},
|
||||
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg)
|
||||
after
|
||||
Timeout ->
|
||||
?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
|
||||
Caller ! {eauth, false}
|
||||
end,
|
||||
loop(Port, ?CALL_TIMEOUT);
|
||||
Caller ! {eauth, false},
|
||||
Pid = restart_instance(ProcessName, ExtPrg),
|
||||
flush_buffer_and_forward_messages(Pid),
|
||||
exit(port_terminated)
|
||||
end;
|
||||
stop ->
|
||||
Port ! {self(), close},
|
||||
receive
|
||||
@@ -88,10 +143,21 @@ loop(Port, Timeout) ->
|
||||
exit(normal)
|
||||
end;
|
||||
{'EXIT', Port, Reason} ->
|
||||
?CRITICAL_MSG("~p ~n", [Reason]),
|
||||
?CRITICAL_MSG("extauth script has exitted abruptly with reason '~p'", [Reason]),
|
||||
Pid = restart_instance(ProcessName, ExtPrg),
|
||||
flush_buffer_and_forward_messages(Pid),
|
||||
exit(port_terminated)
|
||||
end.
|
||||
|
||||
flush_buffer_and_forward_messages(Pid) ->
|
||||
receive
|
||||
Message ->
|
||||
Pid ! Message,
|
||||
flush_buffer_and_forward_messages(Pid)
|
||||
after 0 ->
|
||||
true
|
||||
end.
|
||||
|
||||
join(List, Sep) ->
|
||||
lists:foldl(fun(A, "") -> A;
|
||||
(A, Acc) -> Acc ++ Sep ++ A
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+19
-6
@@ -5,7 +5,7 @@
|
||||
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -66,15 +66,28 @@ start_module(Host, Module, Opts) ->
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}),
|
||||
case catch Module:start(Host, Opts) of
|
||||
{'EXIT', Reason} ->
|
||||
try Module:start(Host, Opts)
|
||||
catch Class:Reason ->
|
||||
del_module_mnesia(Host, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p",
|
||||
[Module, Host, Opts, Class, Reason]),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
case is_app_running(ejabberd) of
|
||||
true ->
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace());
|
||||
false ->
|
||||
?CRITICAL_MSG("ejabberd initialization was aborted because a module start failed.", []),
|
||||
timer:sleep(3000),
|
||||
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199))
|
||||
end
|
||||
end.
|
||||
|
||||
is_app_running(AppName) ->
|
||||
%% Use a high timeout to prevent a false positive in a high load system
|
||||
Timeout = 15000,
|
||||
lists:keymember(AppName, 1, application:which_applications(Timeout)).
|
||||
|
||||
%% @doc Stop the module in a host, and forget its configuration.
|
||||
stop_module(Host, Module) ->
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
%%% Created : 2 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+14
-3
@@ -5,7 +5,7 @@
|
||||
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -364,6 +364,8 @@ get_iq_namespace({xmlelement, Name, _Attrs, Els}) when Name == "iq" ->
|
||||
get_iq_namespace(_) ->
|
||||
"".
|
||||
|
||||
%% @spec (xmlelement()) -> iq() | reply | invalid | not_iq
|
||||
|
||||
iq_query_info(El) ->
|
||||
iq_info_internal(El, request).
|
||||
|
||||
@@ -455,6 +457,8 @@ parse_xdata_submit(El) ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"submit" ->
|
||||
lists:reverse(parse_xdata_fields(Els, []));
|
||||
"form" -> %% This is a workaround to accept Psi's wrong forms
|
||||
lists:reverse(parse_xdata_fields(Els, []));
|
||||
_ ->
|
||||
invalid
|
||||
end.
|
||||
@@ -557,7 +561,7 @@ rsm_encode_count(Count, Arr)->
|
||||
i2l(I) when is_integer(I) -> integer_to_list(I);
|
||||
i2l(L) when is_list(L) -> L.
|
||||
|
||||
%% Timezone = utc | {Hours, Minutes}
|
||||
%% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes}
|
||||
%% Hours = integer()
|
||||
%% Minutes = integer()
|
||||
timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}, Timezone) ->
|
||||
@@ -568,6 +572,8 @@ timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}, Timezone) ->
|
||||
Timezone_string =
|
||||
case Timezone of
|
||||
utc -> "Z";
|
||||
{Sign, {TZh, TZm}} ->
|
||||
io_lib:format("~s~2..0w:~2..0w", [Sign, TZh, TZm]);
|
||||
{TZh, TZm} ->
|
||||
Sign = case TZh >= 0 of
|
||||
true -> "+";
|
||||
@@ -793,5 +799,10 @@ e(X) -> exit({bad_encode_base64_token, X}).
|
||||
%% Convert Erlang inet IP to list
|
||||
ip_to_list({IP, _Port}) ->
|
||||
ip_to_list(IP);
|
||||
ip_to_list({_,_,_,_,_,_,_,_} = Ipv6Address) ->
|
||||
inet_parse:ntoa(Ipv6Address);
|
||||
%% This function clause could use inet_parse too:
|
||||
ip_to_list({A,B,C,D}) ->
|
||||
lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D])).
|
||||
lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D]));
|
||||
ip_to_list(IP) ->
|
||||
lists:flatten(io_lib:format("~w", [IP])).
|
||||
|
||||
+5
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -22,6 +22,7 @@
|
||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
||||
-define(NS_VCARD, "vcard-temp").
|
||||
-define(NS_VCARD_UPDATE, "vcard-temp:x:update").
|
||||
-define(NS_AUTH, "jabber:iq:auth").
|
||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
||||
-define(NS_REGISTER, "jabber:iq:register").
|
||||
@@ -29,6 +30,7 @@
|
||||
-define(NS_ROSTER, "jabber:iq:roster").
|
||||
-define(NS_ROSTER_VER, "urn:xmpp:features:rosterver").
|
||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
||||
-define(NS_BLOCKING, "urn:xmpp:blocking").
|
||||
-define(NS_PRIVATE, "jabber:iq:private").
|
||||
-define(NS_VERSION, "jabber:iq:version").
|
||||
-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete
|
||||
@@ -140,6 +142,8 @@
|
||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
||||
?STANZA_ERROR("401", "cancel", "unexpected-request")).
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user