Compare commits

..

53 Commits

Author SHA1 Message Date
Badlop 3c9ac026ed Create tag for ejabberd 2.1.1.
SVN Revision: 2809
2009-12-17 16:48:35 +00:00
Badlop 402a8c0d35 Prepare 2.1.1 release
SVN Revision: 2808
2009-12-14 14:15:45 +00:00
Badlop d24449488c Print command's reply and newline only when there's something to print.
SVN Revision: 2806
2009-12-11 20:04:36 +00:00
Badlop b9e57a341d Added to ejabberdctl an experimental variable EJABBERD_BYPASS_WARNINGS
SVN Revision: 2804
2009-12-11 19:42:59 +00:00
Badlop 79be929d96 Add support for serving a Unique Room Name (thanks to Pablo Polvorin)(EJAB-1121)
SVN Revision: 2802
2009-12-11 12:30:41 +00:00
Badlop 65aed47873 Show Last Activity menu item in Webadmin only if vhost has mod_last (EJAB-1031)
SVN Revision: 2799
2009-12-09 17:47:02 +00:00
Badlop 8ac16a0ec1 Call in WebAdmin the proper mod_last backend module (EJAB-1031)
SVN Revision: 2798
2009-12-09 17:46:51 +00:00
Badlop 3250c24ff1 This is ejabberd 2.1.x branch, not 2.1.0 tag.
SVN Revision: 2796
2009-12-09 10:09:35 +00:00
Badlop 88b6849e69 Fix erl -setcookie argument in cluster setup (thanks to Jan Koum)
SVN Revision: 2795
2009-12-09 10:09:19 +00:00
Badlop e3b4c99b00 Apply a forgotten part of the original patch (EJAB-1119)
SVN Revision: 2792
2009-12-08 17:32:46 +00:00
Badlop 1f5bf8fbc0 Fix memory and port leak when TLS is enabled in HTTP (thanks to Brian Cully)(EJAB-1119)
SVN Revision: 2790
2009-12-07 17:33:02 +00:00
Badlop f044d6ed28 Fix bug that couldn't connect if starttls_required and zlib were set (EJAB-1118)
SVN Revision: 2789
2009-12-07 15:54:25 +00:00
Christophe Romain 3aa242e2c7 prevent sending duplicated last items if several resources online (EJAB-1116)
SVN Revision: 2786
2009-12-06 23:18:17 +00:00
Badlop 8983cba129 Typo in mod_pubsub_odbc breaks Service Discovery and more (EJAB-1115)
SVN Revision: 2784
2009-12-04 18:35:14 +00:00
Badlop bd97c40f92 Call ejabberd_router:route/3 instead of sending a message (EJAB-1114)
SVN Revision: 2782
2009-12-04 16:31:25 +00:00
Badlop 5a006b977d Small relax the limitation in the mod_pubsub_odbc host option.
SVN Revision: 2781
2009-12-04 16:08:31 +00:00
Badlop 3658da3ae8 Explain limitation in the mod_pubsub_odbc host option.
SVN Revision: 2777
2009-12-03 22:53:56 +00:00
Badlop 2265f52c34 Fix warning of unused variable
SVN Revision: 2776
2009-12-03 22:53:39 +00:00
Badlop 7b244652f1 Use os:getenv to get HOME; if variable is undefined use default values.
SVN Revision: 2773
2009-12-03 15:59:01 +00:00
Badlop 325e67f4cb Use old regexp instead re in 2.x for pre-R12B-4 compatibility.
SVN Revision: 2771
2009-12-02 23:26:28 +00:00
Badlop 6ce660b9c2 IQ sent to resource not connected, return service-unavailable instead of recipient-unavailable (EJAB-1045)
SVN Revision: 2768
2009-12-01 20:00:33 +00:00
Badlop 575e07a986 Forward IQ vCard queries of room occupants to bare JID, not full (EJAB-1045)
SVN Revision: 2767
2009-12-01 20:00:15 +00:00
Badlop 20d1011194 Don't handle internally an IQ vCard if destination is full JID (EJAB-1045)
SVN Revision: 2766
2009-12-01 19:59:59 +00:00
Badlop 3df5277728 Move get_group_name outside the loop (thanks to mikekaganski)
SVN Revision: 2762
2009-11-26 14:46:24 +00:00
Badlop ea4f379bd5 Fix explanation of option content_types. Reorder options.
SVN Revision: 2755
2009-11-23 19:11:37 +00:00
Badlop 51594b6011 Generate main XML file also when exporting only a vhost (EJAB-1098)
SVN Revision: 2753
2009-11-23 18:34:36 +00:00
Badlop 58425ba45d Show informative webpage when browsing the HTTP-Poll page (EJAB-1106)
SVN Revision: 2751
2009-11-23 15:21:05 +00:00
Badlop 7fd3d67ff3 Change captcha.sh to not depend on bash (thanks to Konstantin Khomoutov)(EJAB-1105)
SVN Revision: 2749
2009-11-23 12:43:15 +00:00
Badlop 1716b090f9 Option to define custom HTTP headers in mod_http_fileserver (EJAB-612)
SVN Revision: 2747
2009-11-23 12:00:46 +00:00
Badlop e5e777694d Explain that the example captcha.sh requires Bash because uses RANDOM feature.
SVN Revision: 2745
2009-11-17 15:49:05 +00:00
Badlop 6cf3ae8050 Include IP address, time and module in IBR admin message (thanks to Tsukasa Hamano)
SVN Revision: 2744
2009-11-17 11:14:31 +00:00
Badlop 26c572b7e7 The default value of option ldap_filter is: undefined.
SVN Revision: 2741
2009-11-13 20:16:41 +00:00
Evgeniy Khramtsov 311bf4dbb1 fixes allow_host/2 on subdomains. added hook s2s_allow_host: the hook should return deny or allow
SVN Revision: 2738
2009-11-13 16:19:46 +00:00
Badlop 1511a5dc82 Fix EDoc errors
SVN Revision: 2735
2009-11-06 18:26:06 +00:00
Badlop 041d947e83 Fix bug that forgot listener options when using R12 or older.
SVN Revision: 2734
2009-11-06 18:24:54 +00:00
Christophe Romain 2007b3d496 typo fix that breaks pep
SVN Revision: 2731
2009-11-06 11:01:40 +00:00
Christophe Romain 09e847b97d fix root node deletion match
SVN Revision: 2729
2009-11-05 17:36:15 +00:00
Badlop b9f911554c Fix ProcessOne name in comments.
SVN Revision: 2724
2009-11-04 20:14:22 +00:00
Badlop 24323e8b3b Extend explanation of ldap_encrypt tls (thanks to Konstantin Khomoutov)
SVN Revision: 2723
2009-11-04 20:12:54 +00:00
Badlop 1a5eb4f73d Fix disco on root node, add commands to disco (thanks to Brian Cully)(EJAB-1094)
SVN Revision: 2722
2009-11-04 19:54:15 +00:00
Badlop 3930715712 Send authorization messages outside of get-pending transaction (thanks to Brian Cully)(EJAB-1090)
SVN Revision: 2721
2009-11-04 19:52:33 +00:00
Badlop 64bc97d471 Send authorization messages outside of get-pending transaction (thanks to Brian Cully)(EJAB-1090)
SVN Revision: 2719
2009-11-04 17:38:51 +00:00
Badlop 4aec8dda48 Extend explanation of mod_pubsub "default" rename to "hometree".
SVN Revision: 2718
2009-11-04 09:42:54 +00:00
Badlop 9192c75e1a Fix stringprep Bidi checking (thanks to Alexey Shchepin)(EJAB-1087)
SVN Revision: 2717
2009-11-01 12:15:34 +00:00
Christophe Romain 512d701ef8 minor bugfix, thanks to brian Cully (EJAB-1089)
SVN Revision: 2713
2009-10-27 14:26:22 +00:00
Christophe Romain ef3dd090c7 backport previous patch to pubsub odbc
SVN Revision: 2711
2009-10-27 14:13:18 +00:00
Christophe Romain 3d5fe6a337 fix disco bugs, thanks to Brian Cully (EJAB-1088)
SVN Revision: 2710
2009-10-27 14:07:35 +00:00
Badlop 0906a68c27 Mention in Release Notes the three changes in SQL databases (EJAB-1078)
SVN Revision: 2707
2009-10-23 17:32:12 +00:00
Badlop 798853abb6 Fix mistake that disallowed s2s_out connections when using Erlang R12 or older.
SVN Revision: 2694
2009-10-21 21:11:09 +00:00
Badlop 5e3fdbeeed Mention the change in nodename slash separation in release notes.
SVN Revision: 2689
2009-10-20 15:39:57 +00:00
Badlop 04545f2668 Create branch for ejabberd 2.1.x release line.
SVN Revision: 2688
2009-10-20 15:28:48 +00:00
Christophe Romain f383511e0d add release_notes_2.0.1
SVN Revision: 1338
2008-05-19 22:35:01 +00:00
Jérôme Sautret 2f65188edb Created ejabberd 2.1.X branch.
SVN Revision: 1274
2008-04-03 13:38:34 +00:00
343 changed files with 62896 additions and 61995 deletions
-38
View File
@@ -1,38 +0,0 @@
#
# You can add personal rules in your file .git/info/exclude
*.swp
*~
/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/configure
/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
+11 -12
View File
@@ -8,15 +8,20 @@ Quickstart guide
To compile ejabberd you need:
- GNU Make
- GCC
- Erlang/OTP R12B-5 or higher. Recommended: R12B-5, R13B04 and R14B01.
Avoid R14A and R14B.
- exmpp 0.9.6 or higher
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
- 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.
- Zlib 1.2.3 or higher, for Stream Compression support
(XEP-0138). Optional.
- Erlang mysql library. Optional. MySQL authentication/storage.
- Erlang pgsql library. Optional. PostgreSQL authentication/storage.
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
- ESASL library. Optional. For SASL GSSAPI authentication.
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
- GNU Iconv 1.8 or higher, for the IRC Transport
(mod_irc). Optional. Not needed on systems with GNU Libc.
- ImageMagicks Convert program. Optional. For CAPTCHA challenges.
- exmpp 0.9.1 or higher. Optional. For import/export XEP-0227 files.
1. Compile and install on *nix systems
@@ -25,12 +30,6 @@ To compile ejabberd, go to the directory src/ and execute the commands:
./configure
make
If you get an error like:
./configure: No such file or directory
the solution is to first execute:
aclocal
autoconf
To install ejabberd, run this command with system administrator rights
(root user):
@@ -281,14 +281,14 @@ build_additional_translators(List) ->
List).
print_translation(File, Line, Str, StrT) ->
StrQ = re:replace(Str, "\\\"", "\\\\\\\"", [global, {return, list}]),
StrTQ = re:replace(StrT, "\\\"", "\\\\\\\"", [global, {return, list}]),
{ok, StrQ, _} = regexp:gsub(Str, "\"", "\\\""),
{ok, StrTQ, _} = regexp:gsub(StrT, "\"", "\\\""),
io:format("#: ~s:~p~nmsgid \"~s\"~nmsgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
print_translation_obsolete(Str, StrT) ->
File = "unknown.erl",
Line = 1,
StrQ = re:replace(Str, "\\\"", "\\\\\\\"", [global, {return, list}]),
StrTQ = re:replace(StrT, "\\\"", "\\\\\\\"", [global, {return, list}]),
{ok, StrQ, _} = regexp:gsub(Str, "\"", "\\\""),
{ok, StrTQ, _} = regexp:gsub(StrT, "\"", "\\\""),
io:format("#: ~s:~p~n#~~ msgid \"~s\"~n#~~ msgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
@@ -16,7 +16,7 @@ prepare_dirs ()
EJA_SRC_DIR=$EJA_DIR/src/
EJA_MSGS_DIR=$EJA_SRC_DIR/msgs/
EXTRACT_DIR=$EJA_DIR/tools/extract_translations/
EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/
EXTRACT_ERL=$EXTRACT_DIR/extract_translations.erl
EXTRACT_BEAM=$EXTRACT_DIR/extract_translations.beam
@@ -97,7 +97,7 @@ extract_lang_all ()
done
cd $MSGS_DIR
REVISION=`git describe --always`
REVISION=`svn info | grep "^Rev" | head -1 | awk '{print $2}'`
zip $HOME/ejabberd-langs-$REVISION.zip *.translate;
rm *.translate
+2 -1
View File
@@ -1,3 +1,5 @@
# $Id$
SHELL = /bin/bash
CONTRIBUTED_MODULES = ""
@@ -35,7 +37,6 @@ clean:
rm -f *.out
rm -f *.pdf
rm -f *.toc
rm -f version.tex
[ ! -f contributed_modules.tex ] || rm contributed_modules.tex
distclean: clean
+1 -1
View File
@@ -15,4 +15,4 @@ clean:
docs:
erl -noshell -run edoc_run application \
"'$(APPNAME)'" '"$(SRCDIR)"' '[{dir,"$(DOCDIR)"},{packages, false},{todo,false},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"$(DOCDIR)/overview.edoc"}]' -s init stop
"'$(APPNAME)'" '"$(SRCDIR)"' '[{dir,"$(DOCDIR)"},{packages, false},{todo,true},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"$(DOCDIR)/overview.edoc"}]' -s init stop
+10
View File
@@ -0,0 +1,10 @@
@author Mickael Remond <mickael.remond@process-one.net>
[http://www.process-one.net/]
@copyright 2007 ProcessOne
@version {@vsn}, {@date} {@time}
@title ejabberd Development API Documentation
@doc
== Introduction ==
TODO: Insert content from Jerome documentation.
@@ -1,25 +1,3 @@
div.navbar table tr td a[target="_parent"] {
background: transparent url("ejabberd-devdoc.png") no-repeat;
display: block;
font-size: 0%;
top: 0;
left: 0;
padding: 0;
margin: 0;
width: 192px;
height: 42px;
}
div.navbar table tr td a img {
border: 0;
padding: 0 0 0 10;
float: right;
}
p i {
display: none;
}
html, body {
font-family: Verdana, sans-serif;
color: #000;
@@ -27,20 +5,19 @@ html, body {
}
h1 {
font-size: 20px;
color: #4a5389;
border-bottom: solid 1px #000;
}
h2 {
font-size: 18px;
font-size: 24px;
text-align: right;
color: #4a5389;
border-bottom: 1px solid #000;
}
h3 {
font-size: 16px;
font-size: 18px;
color: #900;
}
+410
View File
@@ -0,0 +1,410 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD>
<TITLE>Ejabberd 2.1.1 Developers Guide
</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<META name="GENERATOR" content="hevea 1.10">
<STYLE type="text/css">
.li-itemize{margin:1ex 0ex;}
.li-enumerate{margin:1ex 0ex;}
.dd-description{margin:0ex 0ex 1ex 4ex;}
.dt-description{margin:0ex;}
.toc{list-style:none;}
.thefootnotes{text-align:left;margin:0ex;}
.dt-thefootnotes{margin:0em;}
.dd-thefootnotes{margin:0em 0em 0em 2em;}
.footnoterule{margin:1em auto 1em 0px;width:50%;}
.caption{padding-left:2ex; padding-right:2ex; margin-left:auto; margin-right:auto}
.title{margin:2ex auto;text-align:center}
.center{text-align:center;margin-left:auto;margin-right:auto;}
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
DIV TABLE{margin-left:inherit;margin-right:inherit;}
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
TD P{margin:0px;}
.boxed{border:1px solid black}
.textboxed{border:1px solid black}
.vbar{border:none;width:2px;background-color:black;}
.hbar{border:none;height:2px;width:100%;background-color:black;}
.hfill{border:none;height:1px;width:200%;background-color:black;}
.vdisplay{border-collapse:separate;border-spacing:2px;width:auto; empty-cells:show; border:2px solid red;}
.vdcell{white-space:nowrap;padding:0px;width:auto; border:2px solid green;}
.display{border-collapse:separate;border-spacing:2px;width:auto; border:none;}
.dcell{white-space:nowrap;padding:0px;width:auto; border:none;}
.dcenter{margin:0ex auto;}
.vdcenter{border:solid #FF8000 2px; margin:0ex auto;}
.minipage{text-align:left; margin-left:0em; margin-right:auto;}
.marginpar{border:solid thin black; width:20%; text-align:left;}
.marginparleft{float:left; margin-left:0ex; margin-right:1ex;}
.marginparright{float:right; margin-left:1ex; margin-right:0ex;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
.part{margin:2ex auto;text-align:center}
</STYLE>
</HEAD>
<BODY >
<!--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.1 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">
<IMG SRC="logo.png" ALT="logo.png">
</DIV><BLOCKQUOTE CLASS="quotation"><I>I can thoroughly recommend ejabberd for ease of setup &#X2013;
Kevin Smith, Current maintainer of the Psi project</I></BLOCKQUOTE><!--TOC section Contents-->
<H2 CLASS="section"><!--SEC ANCHOR -->Contents</H2><!--SEC END --><UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc1">1&#XA0;&#XA0;Key Features</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc2">2&#XA0;&#XA0;Additional Features</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc3">3&#XA0;&#XA0;How it Works</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc4">3.1&#XA0;&#XA0;Router</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc5">3.2&#XA0;&#XA0;Local Router</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc6">3.3&#XA0;&#XA0;Session Manager</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc7">3.4&#XA0;&#XA0;S2S Manager</A>
</LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc8">4&#XA0;&#XA0;Authentication</A>
<UL CLASS="toc">
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc9">4.0.1&#XA0;&#XA0;External</A>
</LI></UL>
</UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc10">5&#XA0;&#XA0;XML Representation</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc11">6&#XA0;&#XA0;Module <TT>xml</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc12">7&#XA0;&#XA0;Module <TT>xml_stream</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc13">8&#XA0;&#XA0;Modules</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc14">8.1&#XA0;&#XA0;Module gen_iq_handler</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc15">8.2&#XA0;&#XA0;Services</A>
</LI></UL>
</LI></UL><P>Introduction
<A NAME="intro"></A></P><P><TT>ejabberd</TT> is a free and open source instant messaging server written in <A HREF="http://www.erlang.org/">Erlang/OTP</A>.</P><P><TT>ejabberd</TT> is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication.</P><P><TT>ejabberd</TT> is designed to be a rock-solid and feature rich XMPP server.</P><P><TT>ejabberd</TT> is suitable for small deployments, whether they need to be scalable or not, as well as extremely big deployments.</P><!--TOC section Key Features-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc1">1</A>&#XA0;&#XA0;Key Features</H2><!--SEC END --><P>
<A NAME="keyfeatures"></A>
</P><P><TT>ejabberd</TT> is:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
Cross-platform: <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.</LI><LI CLASS="li-itemize">Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.</LI><LI CLASS="li-itemize">Fault-tolerant: You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced &#X2018;on the fly&#X2019;.</LI><LI CLASS="li-itemize">Administrator Friendly: <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Comprehensive documentation.
</LI><LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows. </LI><LI CLASS="li-itemize">Web Administration.
</LI><LI CLASS="li-itemize">Shared Roster Groups.
</LI><LI CLASS="li-itemize">Command line administration tool. </LI><LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
</LI><LI CLASS="li-itemize">Capability to send announce messages.
</LI></UL></LI><LI CLASS="li-itemize">Internationalized: <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Translated to 25 languages. </LI><LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
</LI></UL></LI><LI CLASS="li-itemize">Open Standards: <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
<UL CLASS="itemize"><LI CLASS="li-itemize">
Fully XMPP compliant.
</LI><LI CLASS="li-itemize">XML-based protocol.
</LI><LI CLASS="li-itemize"><A HREF="http://www.ejabberd.im/protocols">Many protocols supported</A>.
</LI></UL></LI></UL><!--TOC section Additional Features-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc2">2</A>&#XA0;&#XA0;Additional Features</H2><!--SEC END --><P>
<A NAME="addfeatures"></A>
</P><P>Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
Modular
<UL CLASS="itemize"><LI CLASS="li-itemize">
Load only the modules you want.
</LI><LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
</LI></UL>
</LI><LI CLASS="li-itemize">Security
<UL CLASS="itemize"><LI CLASS="li-itemize">
SASL and STARTTLS for c2s and s2s connections.
</LI><LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
</LI><LI CLASS="li-itemize">Web Admin accessible via HTTPS secure access.
</LI></UL>
</LI><LI CLASS="li-itemize">Databases
<UL CLASS="itemize"><LI CLASS="li-itemize">
Internal database for fast deployment (Mnesia).
</LI><LI CLASS="li-itemize">Native MySQL support.
</LI><LI CLASS="li-itemize">Native PostgreSQL support.
</LI><LI CLASS="li-itemize">ODBC data storage support.
</LI><LI CLASS="li-itemize">Microsoft SQL Server support. </LI></UL>
</LI><LI CLASS="li-itemize">Authentication
<UL CLASS="itemize"><LI CLASS="li-itemize">
Internal Authentication.
</LI><LI CLASS="li-itemize">PAM, LDAP and ODBC. </LI><LI CLASS="li-itemize">External Authentication script.
</LI></UL>
</LI><LI CLASS="li-itemize">Others
<UL CLASS="itemize"><LI CLASS="li-itemize">
Support for virtual hosting.
</LI><LI CLASS="li-itemize">Compressing XML streams with Stream Compression (<A HREF="http://www.xmpp.org/extensions/xep-0138.html">XEP-0138</A>).
</LI><LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.xmpp.org/extensions/xep-0039.html">XEP-0039</A>).
</LI><LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
</LI><LI CLASS="li-itemize"><A HREF="http://www.xmpp.org/extensions/xep-0045.html">Multi-User Chat</A> module with support for clustering and HTML logging. </LI><LI CLASS="li-itemize">Users Directory based on users vCards.
</LI><LI CLASS="li-itemize"><A HREF="http://www.xmpp.org/extensions/xep-0060.html">Publish-Subscribe</A> component with support for <A HREF="http://www.xmpp.org/extensions/xep-0163.html">Personal Eventing via Pubsub</A>.
</LI><LI CLASS="li-itemize">Support for web clients: <A HREF="http://www.xmpp.org/extensions/xep-0025.html">HTTP Polling</A> and <A HREF="http://www.xmpp.org/extensions/xep-0206.html">HTTP Binding (BOSH)</A> services.
</LI><LI CLASS="li-itemize">IRC transport.
</LI><LI CLASS="li-itemize">Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
</LI></UL>
</LI></UL><!--TOC section How it Works-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc3">3</A>&#XA0;&#XA0;How it Works</H2><!--SEC END --><P>
<A NAME="howitworks"></A></P><P>A XMPP domain is served by one or more <TT>ejabberd</TT> nodes. These nodes can
be run on different machines that are connected via a network. They all must
have the ability to connect to port 4369 of all another nodes, and must have
the same magic cookie (see Erlang/OTP documentation, in other words the file
<TT>~ejabberd/.erlang.cookie</TT> must be the same on all nodes). This is
needed because all nodes exchange information about connected users, S2S
connections, registered services, etc&#X2026;</P><P>Each <TT>ejabberd</TT> node have following modules:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
router;
</LI><LI CLASS="li-itemize">local router.
</LI><LI CLASS="li-itemize">session manager;
</LI><LI CLASS="li-itemize">S2S manager;
</LI></UL><!--TOC subsection Router-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc4">3.1</A>&#XA0;&#XA0;Router</H3><!--SEC END --><P>This module is the main router of XMPP packets on each node. It routes
them based on their destinations domains. It has two tables: local and global
routes. First, domain of packet destination searched in local table, and if it
found, then the packet is routed to appropriate process. If no, then it
searches in global table, and is routed to the appropriate <TT>ejabberd</TT> node or
process. If it does not exists in either tables, then it sent to the S2S
manager.</P><!--TOC subsection Local Router-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc5">3.2</A>&#XA0;&#XA0;Local Router</H3><!--SEC END --><P>This module routes packets which have a destination domain equal to this server
name. If destination JID has a non-empty user part, then it routed to the
session manager, else it is processed depending on it&#X2019;s content.</P><!--TOC subsection Session Manager-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc6">3.3</A>&#XA0;&#XA0;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
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>&#XA0;&#XA0;S2S Manager</H3><!--SEC END --><P>This module routes packets to other XMPP servers. First, it checks if an
open S2S connection from the domain of the packet source to the domain of
packet destination already exists. If it is open on another node, then it
routes the packet to S2S manager on that node, if it is open on this node, then
it is routed to the process that serves this connection, and if a connection
does not exist, then it is opened and registered.</P><!--TOC section Authentication-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc8">4</A>&#XA0;&#XA0;Authentication</H2><!--SEC END --><!--TOC subsubsection External-->
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A NAME="htoc9">4.0.1</A>&#XA0;&#XA0;External</H4><!--SEC END --><P>
<A NAME="externalauth"></A>
</P><P>The external authentication script follows
<A HREF="http://www.erlang.org/doc/tutorial/c_portdriver.html">the erlang port driver API</A>.</P><P>That script is supposed to do theses actions, in an infinite loop:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
read from stdin: AABBBBBBBBB.....
<UL CLASS="itemize"><LI CLASS="li-itemize">
A: 2 bytes of length data (a short in network byte order)
</LI><LI CLASS="li-itemize">B: a string of length found in A that contains operation in plain text
operation are as follows:
<UL CLASS="itemize"><LI CLASS="li-itemize">
auth:User:Server:Password (check if a username/password pair is correct)
</LI><LI CLASS="li-itemize">isuser:User:Server (check if it&#X2019;s a valid user)
</LI><LI CLASS="li-itemize">setpass:User:Server:Password (set user&#X2019;s password)
</LI></UL>
</LI></UL>
</LI><LI CLASS="li-itemize">write to stdout: AABB
<UL CLASS="itemize"><LI CLASS="li-itemize">
A: the number 2 (coded as a short, which is bytes length of following result)
</LI><LI CLASS="li-itemize">B: the result code (coded as a short), should be 1 for success/valid, or 0 for failure/invalid
</LI></UL>
</LI></UL><P>Example python script
</P><PRE CLASS="verbatim">#!/usr/bin/python
import sys
from struct import *
def from_ejabberd():
input_length = sys.stdin.read(2)
(size,) = unpack('&gt;h', input_length)
return sys.stdin.read(size).split(':')
def to_ejabberd(bool):
answer = 0
if bool:
answer = 1
token = pack('&gt;hh', 2, answer)
sys.stdout.write(token)
sys.stdout.flush()
def auth(username, server, password):
return True
def isuser(username, server):
return True
def setpass(username, server, password):
return True
while True:
data = from_ejabberd()
success = False
if data[0] == "auth":
success = auth(data[1], data[2], data[3])
elif data[0] == "isuser":
success = isuser(data[1], data[2])
elif data[0] == "setpass":
success = setpass(data[1], data[2], data[3])
to_ejabberd(success)
</PRE><!--TOC section XML Representation-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc10">5</A>&#XA0;&#XA0;XML Representation</H2><!--SEC END --><P>
<A NAME="xmlrepr"></A></P><P>Each XML stanza is represented as the following tuple:
</P><PRE CLASS="verbatim">XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
Name = string()
Attrs = [Attr]
Attr = {Key, Val}
Key = string()
Val = string()
ElementOrCDATA = XMLElement | CDATA
CDATA = {xmlcdata, string()}
</PRE><P>E.&#XA0;g. this stanza:
</P><PRE CLASS="verbatim">&lt;message to='test@conference.example.org' type='groupchat'&gt;
&lt;body&gt;test&lt;/body&gt;
&lt;/message&gt;
</PRE><P>is represented as the following structure:
</P><PRE CLASS="verbatim">{xmlelement, "message",
[{"to", "test@conference.example.org"},
{"type", "groupchat"}],
[{xmlelement, "body",
[],
[{xmlcdata, "test"}]}]}}
</PRE><!--TOC section Module <TT>xml</TT>-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc11">6</A>&#XA0;&#XA0;Module <TT>xml</TT></H2><!--SEC END --><P>
<A NAME="xmlmod"></A></P><DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>element_to_string(El) -&gt; string()</CODE>
<PRE CLASS="verbatim">El = XMLElement
</PRE>Returns string representation of XML stanza <TT>El</TT>.</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>crypt(S) -&gt; string()</CODE>
<PRE CLASS="verbatim">S = string()
</PRE>Returns string which correspond to <TT>S</TT> with encoded XML special
characters.</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>remove_cdata(ECList) -&gt; EList</CODE>
<PRE CLASS="verbatim">ECList = [ElementOrCDATA]
EList = [XMLElement]
</PRE><TT>EList</TT> is a list of all non-CDATA elements of ECList.</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>get_path_s(El, Path) -&gt; Res</CODE>
<PRE CLASS="verbatim">El = XMLElement
Path = [PathItem]
PathItem = PathElem | PathAttr | PathCDATA
PathElem = {elem, Name}
PathAttr = {attr, Name}
PathCDATA = cdata
Name = string()
Res = string() | XMLElement
</PRE>If <TT>Path</TT> is empty, then returns <TT>El</TT>. Else sequentially
consider elements of <TT>Path</TT>. Each element is one of:
<DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>{elem, Name}</CODE> <TT>Name</TT> is name of subelement of
<TT>El</TT>, if such element exists, then this element considered in
following steps, else returns empty string.
</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>{attr, Name}</CODE> If <TT>El</TT> have attribute <TT>Name</TT>, then
returns value of this attribute, else returns empty string.
</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>cdata</CODE> Returns CDATA of <TT>El</TT>.
</DD></DL></DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description">TODO:
<PRE CLASS="verbatim"> get_cdata/1, get_tag_cdata/1
get_attr/2, get_attr_s/2
get_tag_attr/2, get_tag_attr_s/2
get_subtag/2
</PRE></DD></DL><!--TOC section Module <TT>xml_stream</TT>-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc12">7</A>&#XA0;&#XA0;Module <TT>xml_stream</TT></H2><!--SEC END --><P>
<A NAME="xmlstreammod"></A></P><DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>parse_element(Str) -&gt; XMLElement | {error, Err}</CODE>
<PRE CLASS="verbatim">Str = string()
Err = term()
</PRE>Parses <TT>Str</TT> using XML parser, returns either parsed element or error
tuple.
</DD></DL><!--TOC section Modules-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc13">8</A>&#XA0;&#XA0;Modules</H2><!--SEC END --><P>
<A NAME="emods"></A></P><!--TOC subsection Module gen_iq_handler-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc14">8.1</A>&#XA0;&#XA0;Module gen_iq_handler</H3><!--SEC END --><P>
<A NAME="geniqhandl"></A></P><P>The module <CODE>gen_iq_handler</CODE> allows to easily write handlers for IQ packets
of particular XML namespaces that addressed to server or to users bare JIDs.</P><P>In this module the following functions are defined:
</P><DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>add_iq_handler(Component, Host, NS, Module, Function, Type)</CODE>
<PRE CLASS="verbatim">Component = Module = Function = atom()
Host = NS = string()
Type = no_queue | one_queue | parallel
</PRE>Registers function <CODE>Module:Function</CODE> as handler for IQ packets on
virtual host <CODE>Host</CODE> that contain child of namespace <CODE>NS</CODE> in
<CODE>Component</CODE>. Queueing discipline is <CODE>Type</CODE>. There are at least
two components defined:
<DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>ejabberd_local</CODE> Handles packets that addressed to server JID;
</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>ejabberd_sm</CODE> Handles packets that addressed to users bare JIDs.
</DD></DL>
</DD><DT CLASS="dt-description"></DT><DD CLASS="dd-description"><CODE>remove_iq_handler(Component, Host, NS)</CODE>
<PRE CLASS="verbatim">Component = atom()
Host = NS = string()
</PRE>Removes IQ handler on virtual host <CODE>Host</CODE> for namespace <CODE>NS</CODE> from
<CODE>Component</CODE>.
</DD></DL><P>Handler function must have the following type:
</P><DL CLASS="description"><DT CLASS="dt-description">
</DT><DD CLASS="dd-description"><CODE>Module:Function(From, To, IQ)</CODE>
<PRE CLASS="verbatim">From = To = jid()
</PRE></DD></DL><PRE CLASS="verbatim">-module(mod_cputime).
-behaviour(gen_mod).
-export([start/2,
stop/1,
process_local_iq/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-define(NS_CPUTIME, "ejabberd:cputime").
start(Host, Opts) -&gt;
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_CPUTIME,
?MODULE, process_local_iq, IQDisc).
stop(Host) -&gt;
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -&gt;
case Type of
set -&gt;
{iq, ID, error, XMLNS,
[SubEl, ?ERR_NOT_ALLOWED]};
get -&gt;
CPUTime = element(1, erlang:statistics(runtime))/1000,
SCPUTime = lists:flatten(io_lib:format("~.3f", CPUTime)),
{iq, ID, result, XMLNS,
[{xmlelement, "query",
[{"xmlns", ?NS_CPUTIME}],
[{xmlelement, "cputime", [], [{xmlcdata, SCPUTime}]}]}]}
end.
</PRE><!--TOC subsection Services-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc15">8.2</A>&#XA0;&#XA0;Services</H3><!--SEC END --><P>
<A NAME="services"></A></P><PRE CLASS="verbatim">-module(mod_echo).
-behaviour(gen_mod).
-export([start/2, init/1, stop/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
start(Host, Opts) -&gt;
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost])).
init(Host) -&gt;
ejabberd_router:register_local_route(Host),
loop(Host).
loop(Host) -&gt;
receive
{route, From, To, Packet} -&gt;
ejabberd_router:route(To, From, Packet),
loop(Host);
stop -&gt;
ejabberd_router:unregister_route(Host),
ok;
_ -&gt;
loop(Host)
end.
stop(Host) -&gt;
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
</PRE><!--CUT END -->
<!--HTMLFOOT-->
<!--ENDHTML-->
<!--FOOTER-->
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://hevea.inria.fr/index.html"><EM>H</EM><EM><FONT SIZE=2><sup>E</sup></FONT></EM><EM>V</EM><EM><FONT SIZE=2><sup>E</sup></FONT></EM><EM>A</EM></A><EM>.</EM></BLOCKQUOTE></BODY>
</HTML>
+5 -4
View File
@@ -37,20 +37,24 @@
\newcommand{\modecho}{\module{mod\_echo}}
\newcommand{\modirc}{\module{mod\_irc}}
\newcommand{\modlast}{\module{mod\_last}}
\newcommand{\modlastodbc}{\module{mod\_last\_odbc}}
\newcommand{\modmuc}{\module{mod\_muc}}
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
\newcommand{\modoffline}{\module{mod\_offline}}
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
\newcommand{\modprivacy}{\module{mod\_privacy}}
\newcommand{\modprivate}{\module{mod\_private}}
\newcommand{\modpubsub}{\module{mod\_pubsub}}
\newcommand{\modregister}{\module{mod\_register}}
\newcommand{\modroster}{\module{mod\_roster}}
\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}}
\newcommand{\modservicelog}{\module{mod\_service\_log}}
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
\newcommand{\modstats}{\module{mod\_stats}}
\newcommand{\modtime}{\module{mod\_time}}
\newcommand{\modvcard}{\module{mod\_vcard}}
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
\newcommand{\modversion}{\module{mod\_version}}
%% Title page
@@ -137,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 sent via presence table. If this resource is connected to
packet must be sended 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.
@@ -172,9 +176,6 @@ 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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 B

-19
View File
@@ -1,19 +0,0 @@
.comment {color: brown}
.export {color: darkorchid}
.import {color: darkorchid}
.string {color: maroon}
.builtin {color: dodgerblue}
.guard {color: mediumturquoise}
.function {color: blue}
.variable {color: green}
.macro {color: mediumpurple}
.record {color: orangered}
.call {color: purple}
.attribute {color: firebrick}
.error{color: red}
.l {
font-style: normal;
font-weight: normal;
text-decoration: none;
color: gray;
}
-356
View File
@@ -1,356 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : escobar_hiliter.erl
%%% Author : Mats Cronqvist <mats.cronqvist@gmail.com>
%%% Description :
%%%
%%% Created : 6 Jun 2005 by Mats Cronqvist <mats.cronqvist@gmail.com>
%%%
%%%
%%% Escobar, Copyright (c) 2005-2009 Mats Cronqvist
%%%
%%% MIT License:
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
%%% of this software and associated documentation files (the "Software"), to deal
%%% in the Software without restriction, including without limitation the rights
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%%% copies of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%%% THE SOFTWARE.
%%%
%%%-------------------------------------------------------------------
-module(escobar_hilite).
-export([file/1,tree/1,string/1,out/2]).
-define(LOG(T),escobar:log(process_info(self()),T)).
-import(erl_syntax,
[get_ann/1,add_ann/2,subtrees/1,update_tree/2,type/1,get_pos/1,
application/2,application_arguments/1,application_operator/1,
arity_qualifier_argument/1,arity_qualifier_body/1,
atom_name/1,atom_value/1,
attribute/2,attribute_name/1,attribute_arguments/1,
clause/3,clause_patterns/1,clause_guard/1,clause_body/1,
comment/2,comment_text/1,comment_padding/1,
function/2,function_name/1,function_clauses/1,function_arity/1,
integer_literal/1,
list/1,list_elements/1,
macro/2,macro_name/1,macro_arguments/1,
module_qualifier_body/1, module_qualifier_argument/1,
string_value/1,string_literal/1,
variable_literal/1,variable_name/1,
record_access/3, record_access_argument/1,
record_access_field/1,record_access_type/1,
record_expr/3, record_expr_argument/1,
record_expr_fields/1, record_expr_type/1,
record_index_expr/2, record_index_expr_field/1,
record_index_expr_type/1,
form_list/1,
get_precomments/1,get_postcomments/1,has_comments/1,
copy_comments/2,remove_comments/1]).
-import(prettypr,
[above/2,follow/2,beside/2,empty/0,
null_text/1,break/1,floating/3,text/1,floating/1]).
-import(lists,[flatten/1,duplicate/2,keysearch/3,member/2,usort/1,reverse/1]).
-import(filename,[join/1,basename/1]).
out(File,HtmlString) ->
{ok,FD}=file:open(File,[write]),
try
io:fwrite(FD,"<html>",[]),
io:fwrite(FD,"<link rel=\"stylesheet\" type=\"text/css\"",[]),
io:fwrite(FD,"href=\"escobar.css\"></link><body><pre>",[]),
io:fwrite(FD,"~s",[HtmlString]),
io:fwrite(FD,"</pre></body></html>",[])
after
file:close(FD)
end.
string(Str) ->
tree(form_list(scan_and_parse(Str,1,[]))).
scan_and_parse([],_Line,Forms) -> reverse(Forms);
scan_and_parse(Text,Line,Forms) ->
{done,{ok,Toks,NLine},Cont} = erl_scan:tokens([],Text,Line),
{ok,Form} = erl_parse:parse_form(Toks),
scan_and_parse(Cont,NLine,[Form|Forms]).
file(FileName) ->
case filelib:is_regular(FileName) of
true ->
case filename:extension(FileName) of
".beam"-> tree(get_tree_beam(FileName));
".erl" -> tree(get_comm_tree_erl(FileName));
".hrl" -> tree(get_comm_tree_erl(FileName));
X -> erlang:error({unknown_extension,X})
end;
false->
erlang:error({no_file,FileName})
end.
get_comm_tree_erl(Filename) ->
Comms = erl_comment_scan:file(Filename),
Forms = get_forms_erl(Filename),
erl_recomment:recomment_forms(Forms,Comms).
get_tree_beam(Filename) ->
case beam_lib:chunks(Filename,["Abst"]) of
{ok,{_,[{"Abst",AChunk}]}} ->
{_,Forms} = binary_to_term(AChunk),
form_list(Forms);
_ ->
erlang:error({no_debuginfo,Filename})
end.
get_forms_erl(Filename) ->
{ok,Fs} = epp_dodger:parse_file(Filename,[{no_fail, true}]),
Fs.
%%% # tree
%%% turn a syntax tree into html by annotating and pretty-printing
%%% with a hook function
tree(Tree) ->
pout(ann(type(Tree),Tree)).
%%lists:foldl(fun(Form,Acc) -> [pout(ann(Form))|Acc] end, [], Tree).
pout(Form) ->
erl_prettypr:format(Form,[{hook,fun tag/3},{paper,90},{ribbon,650}]).
%%% ## formatting
%%% ### 'tag' - the format hook function
tag(Node,Ctxt,Cont) ->
Tags = get_ann(Node),
case member(has_comment,Tags) of
true ->
PreC = get_precomments(Node),
PostC = get_postcomments(Node),
Nod = remove_comments(Node),
Doc0 = tagit(Tags--[has_comment],Cont(Nod,Ctxt)),
postcomment(precomment(Doc0,PreC),PostC);
false ->
Doc0 = Cont(Node,Ctxt),
tagit(Tags,Doc0)
end.
tagit([],Doc0) ->
Doc0;
tagit(["binary"],{beside,_,{beside,Doc,_}}) ->
beside(floating(text("&lt;&lt;")),beside(Doc,floating(text("&gt;&gt;"))));
tagit([Tag],Doc0) ->
beside(null_text(Tag),beside(Doc0,null_text(etag(Tag)))).
etag("<"++Tag) -> "</"++hd(string:tokens(Tag," "))++">".
%%%### comment stuff
precomment(Doc,PreC) ->
above(floating(break(stack(PreC)), -1, -1), Doc).
postcomment(Doc,PostC) ->
beside(Doc, floating(break(stack(PostC)), 1, 0)).
stack([]) -> empty();
stack([Comm|Comms]) ->
Doc = maybe_pad(stack_comment_lines(comment_text(Comm)),Comm),
case Comms of
[] -> Doc;
_ -> above(Doc, stack(Comms))
end.
maybe_pad(Doc,Comm) ->
case comment_padding(Comm) of
I when is_integer(I), 0 < I -> beside(text(duplicate(I,$ )), Doc);
_ -> Doc
end.
%%% stolen with pride from erl_prettypr
%%% Stack lines of text above each other and prefix each string in
%%% the list with a single `%' character.
stack_comment_lines([S | Ss]) ->
D = tagit([dehtml('span', [{class,comment}])],text("%"++debracket(S))),
case Ss of
[] -> D;
_ -> above(D, stack_comment_lines(Ss))
end;
stack_comment_lines([]) ->
empty().
%%% annotate nodes that should be hilited
%%% the annotation is put on the subtree that should be marked up
%%% the annotation is;
%%% has_comments|Markup
%%% if a node already has an annotation the new one is dropped, except
%%% if either the new or the old one is has_comments
ann(binary,Tree) ->
new_tree(Tree,add_anno("binary",Tree));
ann(application,Tree) ->
Op = application_operator(Tree),
Args = application_arguments(Tree),
new_tree(Tree,application(add_anno(mu(application,Tree),Op),Args));
ann(attribute,Tree) ->
Name = attribute_name(Tree),
case atom_value(Name) of
export ->
AQs = list_elements(hd(attribute_arguments(Tree))),
Args = [list([add_anno(mu(export,Tree),AQ) || AQ <- AQs])];
import ->
[ImportMod,ImportFAs] = attribute_arguments(Tree),
AQs = list_elements(ImportFAs),
Args = [ImportMod,list([add_anno(mu(import,Tree),AQ) || AQ <- AQs])];
define ->
[Macro|Rest] = attribute_arguments(Tree),
case type(Macro) of
application ->
Op = application_operator(Macro),
As = application_arguments(Macro),
Args = [application(add_anno(mu(macro,Tree),Op),As)|Rest];
_ ->
Args = [add_anno(mu(macro,Tree),Macro)|Rest]
end;
record ->
[Rec|Rest] = attribute_arguments(Tree),
Args = [add_anno(mu(record,Tree),Rec)|Rest];
_ ->
Args = attribute_arguments(Tree)
end,
new_tree(Tree,attribute(add_anno(mu(attribute,Tree),Name),Args));
ann(record_access,Tree) ->
Arg = record_access_argument(Tree),
Type = record_access_type(Tree),
Field = record_access_field(Tree),
new_tree(Tree,record_access(Arg,add_anno(mu(record,Tree),Type),Field));
ann(record_expr,Tree) ->
Arg = record_expr_argument(Tree),
Type = record_expr_type(Tree),
Fields = record_expr_fields(Tree),
new_tree(Tree,record_expr(Arg,add_anno(mu(record,Tree),Type),Fields));
ann(record_index_expr,Tree) ->
Type = record_index_expr_type(Tree),
Field = record_index_expr_field(Tree),
new_tree(Tree,record_index_expr(add_anno(mu(record,Tree),Type),Field));
ann(function,Tree) ->
Name = function_name(Tree),
Clauses = function_clauses(Tree),
new_tree(Tree,function(add_anno(mu(function,Tree),Name),Clauses));
ann(macro,Tree) ->
Name = macro_name(Tree),
Args = macro_arguments(Tree),
new_tree(Tree,macro(add_anno(mu(macro,Tree),Name), Args));
ann(string,OTree) ->
Tree = erl_syntax:string(debracket(string_value(OTree))),
new_tree(OTree,add_anno(mu(string,OTree),Tree));
ann(variable,Tree) ->
new_tree(Tree,add_anno(mu(variable,Tree),Tree));
ann(text,Tree) ->
new_tree(Tree,add_anno(mu(error,Tree),Tree));
ann(comment,OTree) ->
Pad = comment_padding(OTree),
Text = [debracket(S) || S <- comment_text(OTree)],
Tree = comment(Pad,Text),
new_tree(OTree,add_anno(mu(comment,OTree),Tree));
ann(_Typ,Tree) ->
new_tree(Tree,Tree).
new_tree(OTree,NTree) ->
Tree =
case has_comments(OTree) of
true -> add_ann(has_comment,copy_comments(OTree,NTree));
false -> NTree
end,
SubTrees = subtrees(Tree),
case [[ann(type(SubT),SubT) || SubT<-Group] || Group<-SubTrees] of
[] -> Tree;
NSubtrees -> update_tree(Tree,NSubtrees)
end.
debracket([]) -> [];
debracket([$>|Str]) -> "&gt;"++debracket(Str);
debracket([$<|Str]) -> "&lt;"++debracket(Str);
debracket([C|Str]) -> [C|debracket(Str)].
add_anno(nil,Tree) -> Tree;
add_anno(Ann,Tree) ->
case get_ann(Tree) of
[] -> add_ann(Ann,Tree);
[has_comment] -> add_ann(Ann,Tree);
_OAnn -> Tree
end.
%%%### the markups
mu(application,Node) ->
Op = application_operator(Node),
Ar = length(application_arguments(Node)),
case type(Op) of
%% variable ->
%% dehtml('span', [{class,variable}]);
atom ->
case is_guard_or_builtin(atom_value(Op),Ar) of
guard -> dehtml('span', [{class,guard}]);
builtin->dehtml('span', [{class,builtin}]);
neither->dehtml('span', [{class,call}])
end;
module_qualifier ->
Mod = module_qualifier_argument(Op),
Fun = module_qualifier_body(Op),
case {type(Mod),type(Fun)} of
{atom,atom} ->
case atom_value(Mod) of
erlang -> dehtml('span', [{class,builtin}]);
_ -> dehtml('span', [{class,call}])
end;
_ ->
nil
end;
_ ->
nil
end;
mu(function = Class, {tree, function, _, {function, _, [{tree, clause, _, {clause, Vars, _, _}} | _]}}) ->
dehtml('span', [{class,Class}, {arity,length(Vars)}]);
mu(Class,_Node) ->
dehtml('span', [{class,Class}]).
dehtml(Tag,Atts) ->
flatten([$<,str(Tag),$ ,[[str(A),"=\"",str(V),"\" "]||{A,V}<-Atts],$>]).
str(I) when is_integer(I) -> integer_to_list(I);
str(A) when is_atom(A) -> atom_to_list(A);
str(L) when is_list(L) -> L.
is_guard_or_builtin(atom,1) ->guard;
is_guard_or_builtin(binary,1) ->guard;
is_guard_or_builtin(constant,1) ->guard;
is_guard_or_builtin(float,1) ->guard;
is_guard_or_builtin(function,1) ->guard;
is_guard_or_builtin(function,2) ->guard;
is_guard_or_builtin(integer,1) ->guard;
is_guard_or_builtin(list,1) ->guard;
is_guard_or_builtin(number,1) ->guard;
is_guard_or_builtin(pid,1) ->guard;
is_guard_or_builtin(port,1) ->guard;
is_guard_or_builtin(reference,1) ->guard;
is_guard_or_builtin(tuple,1) ->guard;
is_guard_or_builtin(record,2) ->guard;
is_guard_or_builtin(record,3) ->guard;
is_guard_or_builtin(F,A) ->
case erlang:function_exported(erlang,F,A) orelse
erlang:is_builtin(erlang,F,A) of
true -> builtin;
false-> neither
end.
-54
View File
@@ -1,54 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : escobar_run.erl
%%% Author : Badlop <badlop@process-one.net>
%%% Purpose : Frontend to run Escobar
%%% Created : 16 Apr 2008 by Badlop <badlop@process-one.net>
%%%-------------------------------------------------------------------
-module(escobar_run).
%%% Download ejabberd_hilite.erl from http://code.google.com/p/erl-escobar/
%%% Example calls:
%%% escobar_run:file("escobar_run.erl", "../ejascobar/").
%%% escobar_run:dir(".", "../ejascobar/").
%%% escobar_run:dir(".", "../doc/api/").
%%% find ./ -type f -name '*.html' -exec sed -i 's/class="function" >\([a-z0-9]*\)</class="function" id="\1">\1</;' {} \;
-export([file/2, file/1, dir/1]).
file([F, OutDir]) ->
file(F, OutDir).
file(F, OutDir) ->
String = escobar_hilite:file(F),
FB = filename:basename(F),
FilenameHTML = filename:join(OutDir, FB ++ ".html"),
escobar_hilite:out(FilenameHTML, String).
dir([SrcDir, OutDir]) ->
SrcDirAbs = filename:absname(SrcDir),
OutDirAbs = filename:absname(OutDir),
Files = get_files([SrcDirAbs]),
lists:foreach(
fun(F) ->
case filename:extension(F) of
".erl" ->
file(F, OutDirAbs);
".hrl" ->
file(F, OutDirAbs);
_ ->
ok
end
end,
Files).
get_files([]) ->
[];
get_files([FHead | FTail]) ->
case catch file:list_dir(FHead) of
{ok, Files} ->
FilesHead = [filename:join(FHead, FilesN) || FilesN <- Files],
get_files(FilesHead ++ FTail);
{error, enotdir} ->
[FHead] ++ get_files(FTail)
end.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

-522
View File
@@ -1,522 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : funrelg.erl
%%% Author : Badlop <badlop@process-one.net>
%%% Purpose : Function Relation Graph
%%% Created : 3 Apr 2007 by Badlop <badlop@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 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(funrelg).
-author('badlop@process-one.net').
-export([dir/1, file/1, g/0, g/1, gc/0, gc/1, d/0]).
-record(mfa, {mod, func, arity}).
-record(arcs, {id, from, to, occ}).
%% The functions that are not in export and don't call anybody are not drawn
%% The calls to this module that use Mod:Func are drawn as external calls
%%-----------------------------
%% Module handlers
%%-----------------------------
dir([SrcDir, OutDir]) ->
SrcDirAbs = filename:absname(SrcDir),
OutDirAbs = filename:absname(OutDir),
Files = get_files([SrcDirAbs]),
lists:foreach(
fun(F) ->
file(F, OutDirAbs)
end,
Files).
file([SrcDir, OutDir]) ->
SrcFilAbs = filename:absname(SrcDir),
OutDirAbs = filename:absname(OutDir),
file(SrcFilAbs, OutDirAbs).
file(SrcFile, OutDir) ->
case {filename:extension(SrcFile), filename:basename(SrcFile)} of
%% The file must by *.erl, and the first character must be a-z
{".erl", [FirstChar | _]} when (FirstChar >= 97) and (FirstChar =< 122) ->
make_file(SrcFile, OutDir),
d();
_ ->
ok
end.
get_files([]) ->
[];
get_files([FHead | FTail]) ->
case catch file:list_dir(FHead) of
{ok, Files} ->
FilesHead = [filename:join(FHead, FilesN) || FilesN <- Files],
get_files(FilesHead ++ FTail);
{error, enotdir} ->
[FHead] ++ get_files(FTail)
end.
make_file(File, OutDir) ->
FB = filename:basename(File),
FileDot = filename:join(OutDir, FB ++ ".dot"),
Text = gc(File),
{ok,FO}=file:open(FileDot,[write]),
try
io:fwrite(FO,"~s",[Text])
after
file:close(FO)
end,
FileSvg = filename:join(OutDir, filename:basename(File, ".erl") ++ ".svg"),
case os:cmd("dot -Tsvg " ++ FileDot ++ " -o " ++ FileSvg) of
"" -> ok;
ShellResult ->
io:format("Trying to run 'dot', we got this result:~n ~s~n"
"Remember that you need to have Graphviz 'dot' installed.~n", [ShellResult])
end,
ok.
%%====================================================================
%% Internal functions
%%====================================================================
g() ->
g("a.erl").
g(Filename) ->
gc(Filename),
halt().
gc() ->
gc("a.erl").
gc(Filename) ->
{ok, File} = epp_dodger:parse_file(Filename),
ets:new(arcs, [set, public, named_table, {keypos, 2}]),
ets:new(mfa_conversion, [set, public, named_table, {keypos, 1}]),
ets:new(counters, [set, public, named_table]),
ets:insert(counters, {arcs_id, 0}),
ModuleName = get_module_name(File),
Exports = lists:usort(get_exports(File)),
Functions = get_functions(File),
FunctionsParsed = lists:usort(parse_functions(Functions)), % side effects: stores on ets
Privates = FunctionsParsed -- Exports,
%%Externals = get_externals(),
MFAs = get_func_calls(),
AppModules1 = os:cmd("ls -1 ../doc/devdoc/*.html | tr \".\" \" \" | tr \"/\" \" \" | awk '{print $3}'"),
AppModules = [list_to_atom(Str) || Str <- string:tokens(AppModules1, [10])],
ExternalTypes1 = [mfa_to_externaltype(MFA, ModuleName, AppModules, FunctionsParsed) || MFA <- MFAs],
ExternalTypes = lists:usort(ExternalTypes1),
AppExternals = [Function || {Type, Function} <- ExternalTypes, Type == app],
ExmppExternals = [Function || {Type, Function} <- ExternalTypes, Type == exmpp],
OTPExternals = [Function || {Type, Function} <- ExternalTypes, Type == other],
textize(ModuleName, Exports, Privates, AppExternals, ExmppExternals, OTPExternals).
get_arcs_id() ->
ets:update_counter(counters, arcs_id, 1).
insert_arc_normal(FromFunc, FromArity, ToFunc, ToArity) ->
From = #mfa{mod = -1, func = FromFunc, arity = FromArity},
To = #mfa{mod = -1, func = ToFunc, arity = ToArity},
insert_arc(From, To).
insert_arc_external(FromFunc, FromArity, ToMod, ToFunc, ToArity) ->
From = #mfa{mod = -1, func = FromFunc, arity = FromArity},
To = #mfa{mod = ToMod, func = ToFunc, arity = ToArity},
insert_arc(From, To).
insert_arc(From, To) ->
Match = #arcs{
from = From,
to = To,
id='$1',
_='_'},
Select = [{Match, [], ['$1']}],
Ids = ets:select(arcs, Select),
case Ids of
[] ->
ets:insert(arcs,
#arcs{
id = get_arcs_id(),
from = From,
to = To,
occ = 1
}
);
[Id] ->
[Arc] = ets:lookup(arcs, Id),
Arc2 = Arc#arcs{occ = Arc#arcs.occ+1},
ets:insert(arcs, Arc2)
end.
read_arc(Id) ->
ets:lookup(arcs, Id).
d() ->
ets:delete(arcs),
ets:delete(mfa_conversion),
ets:delete(counters).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Get
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_module_name(File) ->
Attributes = get_elements(attribute, File),
get_attribute(module, Attributes).
get_exports(File) ->
Attributes = get_elements(attribute, File),
Exports = get_attribute(export, Attributes),
parse_export(Exports).
get_functions(File) ->
get_elements(function, File).
get_elements(Type, L) -> get_elements(Type, L, []).
get_elements(_, [], Res) -> Res;
get_elements(Type, [{tree, Type, _, A} | L], Res) ->
get_elements(Type, L, Res++[A]);
get_elements(Type, [_ | L], Res) ->
get_elements(Type, L, Res).
get_attribute(_, []) -> [];
get_attribute(Name, [{attribute, {tree, atom, _, Name}, [Al]} | As]) ->
case Name of
module ->
{tree, atom, _, Mn} = Al,
Mn;
export ->
{tree, list, _, {list, Exports, _}} = Al,
Exports++get_attribute(Name, As);
_ -> ok
end;
get_attribute(Name, [_ | As]) ->
get_attribute(Name, As).
parse_export(Ex) ->
lists:foldl(
fun(Aq, Res) ->
{tree, arity_qualifier, _, {arity_qualifier, Atom, Inte}} = Aq,
{tree, atom, _, An} = Atom,
{tree, integer, _, In} = Inte,
Res++[{An, In}]
end,
[],
Ex).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Parse
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse_functions(Functions) ->
lists:map(
fun(E) ->
{function, {_, _, _, Fn}, Clauses} = E,
Arity = get_function_arity(Clauses),
parse_clauses(Fn, Clauses),
{Fn, Arity}
end,
Functions).
parse_clauses(Fn, Clauses) ->
lists:foreach(
fun(E) ->
{tree, clause, L, C} = E,
{attr, _Ln, _, _} = L,
{clause, Parameters, _, Contents} = C,
Fa = length(Parameters),
parse_contents({Fn, Fa}, Contents)
end,
Clauses).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% OLD
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse_contents(F, Contents) ->
{_, Res} = lists:foldl(
fun(C, {Fu, R}) ->
{Fu, R++parse_content(F, C)}
end,
{F, []},
Contents),
Res.
parse_content(F, {tree, application, L, A}) ->
{attr, _Ln, _, _} = L,
parse_application(F, A);
parse_content(F, {tree, list, _, {list, [L], Ls}}) ->
RL = parse_content(F, L),
RL++parse_content(F, Ls);
parse_content(F, {tree, tuple, _, T}) ->
parse_contents(F, T);
parse_content(F, {tree, match_expr, _, M}) ->
{match_expr, _, Value} = M,
parse_content(F, Value);
parse_content(F, {tree, try_expr, _, T}) ->
{try_expr, As, Bs, Cs, Ds} = T,
RAs = parse_contents(F, As),
RBs = parse_contents(F, Bs),
RDs = parse_contents(F, Ds),
RAs++RBs++RDs++parse_contents(F, Cs);
parse_content(F, {tree, block_expr, _, Ts}) ->
parse_contents(F, Ts);
parse_content(F, {tree, catch_expr, _, C}) ->
parse_content(F, C);
parse_content(F, {tree, case_expr, _, C}) ->
{case_expr, Case, Cases} = C,
R2 = parse_content(F, Case),
R2++parse_contents(F, Cases);
parse_content(F, {tree, if_expr, _, Cases}) ->
parse_contents(F, Cases);
parse_content(F, {tree, disjunction, _, Cases}) ->
parse_contents(F, Cases);
parse_content(F, {tree, conjunction, _, Cases}) ->
parse_contents(F, Cases);
parse_content(F, {tree, list_comp, _, L}) ->
{list_comp, A, Bs} = L,
RA = parse_content(F, A),
RA++parse_contents(F, Bs);
parse_content(F, {tree, generator, _, G}) ->
{generator, _, Gen} = G,
parse_content(F, Gen);
parse_content(F, {tree, clause, _, C}) ->
{clause, Clause, When, Second} = C,
R2 = parse_contents(F, Clause),
R3 = parse_content(F, When),
R2++R3++parse_contents(F, Second);
parse_content(F, {tree, fun_expr, _, E}) ->
parse_contents(F, E);
parse_content(F, {tree, record_expr, _, E}) ->
{record_expr, _, _, Records} = E,
parse_contents(F, Records);
parse_content(F, {tree, record_field, _, R}) ->
{record_field, F1, F2} = R,
R2 = parse_content(F, F1),
R2++parse_content(F, F2);
parse_content(F, {tree, receive_expr, _, E}) ->
{receive_expr, E1, _, _} = E,
parse_contents(F, E1);
parse_content(F, {tree, infix_expr, _, I}) ->
{infix_expr, _Operator, T1, T2} = I,
R = parse_content(F, T1),
R++parse_content(F, T2);
parse_content(_F, {tree, prefix_expr, _, _P}) ->
[];
parse_content(_F, {tree, class_qualifier, _, _}) -> [];
parse_content(_F, {tree, implicit_fun, _, _}) -> [];
parse_content(_F, {tree, record_access, _, _}) -> [];
parse_content(_F, {tree, record_index_expr, _, _}) -> [];
parse_content(_F, {tree, macro, _, _}) -> [];
parse_content(_F, {tree, binary, _, _}) -> [];
parse_content(_F, {tree, binary_generator, _, _}) -> [];
parse_content(_F, {tree, binary_comp, _, _}) -> [];
parse_content(_F, {integer, _, _I}) -> [];
parse_content(_F, {float, _, _I}) -> [];
parse_content(_F, {string, _, _S}) -> [];
parse_content(_F, {char, _, _S}) -> [];
parse_content(_F, {atom, _, _A}) -> [];
parse_content(_F, {var, _, _V}) -> [];
parse_content(_F, {nil, _}) -> [];
parse_content(_F, none) -> [];
parse_content(_F, C) -> io:format("Unknown content: ~p~n", [C]), [].
parse_application({Fn, Fa}, {application, {atom, _, Name}, Valores}) ->
Arity = length(Valores),
insert_arc_normal(Fn, Fa, Name, Arity),
Ra = [{arc_normal, Fn, Name}],
Ra++parse_contents({Fn, Fa}, Valores);
parse_application({Fn, Fa}, {application, {tree, module_qualifier, _, M}, Tree2}) ->
case M of
{module_qualifier, {_, _, Tf1}, {_, _, Tf2}} ->
ToArity = length(Tree2),
insert_arc_external(Fn, Fa, Tf1, Tf2, ToArity),
parse_contents({Fn, Fa}, Tree2);
_Other ->
%%io:format("Unknown application module_qualifier: ~p~n", [M]),
parse_contents({Fn, Fa}, Tree2)
end;
parse_application({Fn, Fa}, {application, {tree, record_access, _, _}, Tree2}) ->
parse_contents({Fn, Fa}, Tree2);
parse_application({Fn, Fa}, {application, {var, _, _}, Tree2}) ->
parse_contents({Fn, Fa}, Tree2);
parse_application({Fn, Fa}, {application, Other, Tree2}) ->
io:format("Unknown application tree: ~p~n", [Other]),
parse_contents({Fn, Fa}, Tree2).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Textize
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
textize(ModuleName, Exports, Privates, AppExternals, ExmppExternals, OTPExternals) ->
ExportsS = textize_exports(Exports),
PrivatesS = textize_exports(Privates),
AppExternalsS = textize_externals(AppExternals),
ExmppExternalsS = textize_externals(ExmppExternals),
OTPExternalsS = textize_externals(OTPExternals),
Arcs = textize_arcs(),
io_lib:format(
"digraph ~p {~n"++
" label = \"Module ~p\";~n"++
" labelloc = \"t\";~n"++
" fontname = Helvetica;~n"++
" rankdir = LR;~n"++
" ratio = fill;~n"++
%%" node [shape = box, style = filled, color = olivedrab1, fontsize=40];~s~n"++
%%" node [shape = parallelogram, style = filled, color = rosybrown1, fontsize=40];~s~n"++
%%" node [shape = ellipse, style = filled, color = slategray1, peripheries=3, fontsize=40];~n"++
" node [shape = box, color=darkgreen, fillcolor=Honeydew, style=filled, href=\"PRI~p.html#\\N\"];~s~n"++
" node [shape = box, color=darkgreen, fillcolor=PaleGreen, style=filled, href=\"EXP~p.html#\\N\"];~s~n"++
" node [shape = box, color=darkgreen, fillcolor=Aquamarine, style=filled, href=\"APP\\N\"];~s~n"++
" node [shape = box, color=darkblue, fillcolor=LightSkyBlue,style=filled, href=\"EXM\\N\"];~s~n"++
" node [shape = box, color=darkred, fillcolor=PeachPuff, style=filled, href=\"OTP\\N\"];~s~n"++
" node [shape = box];~n"++
"~s"++
"}~n",
[ModuleName, ModuleName,
ModuleName, PrivatesS,
ModuleName, ExportsS,
AppExternalsS,
ExmppExternalsS,
OTPExternalsS,
Arcs]).
textize_exports(Exports) ->
lists:foldl(
fun({Func, Arity}, R) ->
string:concat(R, io_lib:format(" \"~p/~p\";", [Func, Arity]))
end,
"",
Exports).
textize_externals(Externals) ->
lists:foldl(
fun({Mod, Func, Arity}, R) ->
string:concat(R, io_lib:format(" \"~p:~p/~p\";", [Mod, Func, Arity]));
({Func, Arity}, R) ->
string:concat(R, io_lib:format(" \"~p/~p\";", [Func, Arity]))
end,
"",
Externals).
textize_arcs() ->
Id = ets:first(arcs),
textize_arcs(Id, []).
textize_arcs('$end_of_table', Res) -> Res;
textize_arcs(Id, Res) ->
[Arc] = read_arc(Id),
From = mfa_prepare(Arc#arcs.from),
To = mfa_prepare(Arc#arcs.to),
Edge_attrs = check_loop(From, To)++check_occu(Arc#arcs.occ)++check_external(Arc#arcs.to),
Res2 = io_lib:format(" ~s -> ~s~s;~n", [From, To, Edge_attrs]),
Id_next = ets:next(arcs, Id),
textize_arcs(Id_next, string:concat(Res2, Res)).
get_func_calls() ->
Id = ets:first(arcs),
get_func_calls(Id, []).
get_func_calls('$end_of_table', Res) -> Res;
get_func_calls(Id, Res) ->
[Arc] = read_arc(Id),
MFA = Arc#arcs.to,
Call = MFA,
Id_next = ets:next(arcs, Id),
get_func_calls(Id_next, [Call | Res]).
mfa_prepare(MFAinitial) ->
MFA = try_mfa_conversion(MFAinitial),
Mod = case MFA#mfa.mod of
-1 -> "\"";
M -> io_lib:format("\"~p:", [M])
end,
Func = io_lib:format("~p", [MFA#mfa.func]),
Arity = case MFA#mfa.arity of
-1 -> "\"";
A -> io_lib:format("/~p\"", [A])
end,
string:concat(Mod, string:concat(Func, Arity)).
%% noexternal | app | exmpp | other
mfa_to_externaltype(MFA, ModuleName, AppModules, Functions) ->
case MFA of
{mfa, -1, Fu, Ar} ->
case {is_internal, lists:member({Fu, Ar}, Functions)} of
{is_internal, true} ->
{noexternal, {Fu, Ar}};
{is_internal, false} ->
store_mfa_conversion(MFA, {mfa, -1, Fu, Ar}),
{other, {Fu, Ar}}
end;
{mfa, Mo, Fu, Ar} when Mo == ModuleName ->
store_mfa_conversion(MFA, {mfa, -1, Fu, Ar}),
{noexternal, {Fu, Ar}};
{mfa, Mo, Fu, Ar} ->
Type = case {is_app, lists:member(Mo, AppModules)} of
{is_app, true} -> app;
{is_app, false} ->
case string:str(atom_to_list(Mo), "exmpp") of
0 -> other;
N when is_integer(N) -> exmpp
end
end,
{Type, {Mo, Fu, Ar}}
end.
get_function_arity([{tree, clause, _, {clause, Vars, _, _}} | _]) ->
length(Vars);
get_function_arity(E) ->
io:format("Unknown function arity: ~p~n", [E]).
store_mfa_conversion(MFA1, MFA2) ->
ets:insert(mfa_conversion, {MFA1, MFA2}).
try_mfa_conversion(MFA) ->
case ets:lookup(mfa_conversion, MFA) of
[{MFA,MFA2}] ->
MFA2;
[] ->
MFA
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Utils
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%check_loop(I, I) -> " [color = sandybrown]";
check_loop(I, I) -> "";
check_loop(_, _) -> "".
check_occu(1) -> "";
check_occu(Occ) -> io_lib:format(" [label=\"~p\"]", [Occ]).
check_external(To) ->
case To#mfa.mod of
-1 -> " [style = bold]";
_ -> ""
end.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

+132
View File
@@ -0,0 +1,132 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD>
<TITLE>Ejabberd 2.1.1 Feature Sheet
</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<META name="GENERATOR" content="hevea 1.10">
<STYLE type="text/css">
.li-itemize{margin:1ex 0ex;}
.li-enumerate{margin:1ex 0ex;}
.dd-description{margin:0ex 0ex 1ex 4ex;}
.dt-description{margin:0ex;}
.toc{list-style:none;}
.thefootnotes{text-align:left;margin:0ex;}
.dt-thefootnotes{margin:0em;}
.dd-thefootnotes{margin:0em 0em 0em 2em;}
.footnoterule{margin:1em auto 1em 0px;width:50%;}
.caption{padding-left:2ex; padding-right:2ex; margin-left:auto; margin-right:auto}
.title{margin:2ex auto;text-align:center}
.center{text-align:center;margin-left:auto;margin-right:auto;}
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
DIV TABLE{margin-left:inherit;margin-right:inherit;}
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
TD P{margin:0px;}
.boxed{border:1px solid black}
.textboxed{border:1px solid black}
.vbar{border:none;width:2px;background-color:black;}
.hbar{border:none;height:2px;width:100%;background-color:black;}
.hfill{border:none;height:1px;width:200%;background-color:black;}
.vdisplay{border-collapse:separate;border-spacing:2px;width:auto; empty-cells:show; border:2px solid red;}
.vdcell{white-space:nowrap;padding:0px;width:auto; border:2px solid green;}
.display{border-collapse:separate;border-spacing:2px;width:auto; border:none;}
.dcell{white-space:nowrap;padding:0px;width:auto; border:none;}
.dcenter{margin:0ex auto;}
.vdcenter{border:solid #FF8000 2px; margin:0ex auto;}
.minipage{text-align:left; margin-left:0em; margin-right:auto;}
.marginpar{border:solid thin black; width:20%; text-align:left;}
.marginparleft{float:left; margin-left:0ex; margin-right:1ex;}
.marginparright{float:right; margin-left:1ex; margin-right:0ex;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
.part{margin:2ex auto;text-align:center}
SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
</STYLE>
</HEAD>
<BODY >
<!--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.1 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">
<IMG SRC="logo.png" ALT="logo.png">
</DIV><BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I can thoroughly recommend ejabberd for ease of setup &#X2013;
Kevin Smith, Current maintainer of the Psi project</I></FONT></BLOCKQUOTE><P>Introduction
<A NAME="intro"></A></P><BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. &#X2014;
Joeri</I></FONT></BLOCKQUOTE><P><TT>ejabberd</TT> is a <B><FONT SIZE=4><FONT COLOR="#001376">free and open source</FONT></FONT></B> instant messaging server written in <A HREF="http://www.erlang.org/">Erlang/OTP</A>.</P><P><TT>ejabberd</TT> is <B><FONT SIZE=4><FONT COLOR="#001376">cross-platform</FONT></FONT></B>, distributed, fault-tolerant, and based on open standards to achieve real-time communication.</P><P><TT>ejabberd</TT> is designed to be a <B><FONT SIZE=4><FONT COLOR="#001376">rock-solid and feature rich</FONT></FONT></B> XMPP server.</P><P><TT>ejabberd</TT> is suitable for small deployments, whether they need to be <B><FONT SIZE=4><FONT COLOR="#001376">scalable</FONT></FONT></B> or not, as well as extremely big deployments.</P><!--TOC section Key Features-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc1"></A>Key Features</H2><!--SEC END --><P>
<A NAME="keyfeatures"></A>
</P><BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>Erlang seems to be tailor-made for writing stable, robust servers. &#X2014;
Peter Saint-Andr&#XE9;, Executive Director of the Jabber Software Foundation</I></FONT></BLOCKQUOTE><P><TT>ejabberd</TT> is:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
<B><FONT SIZE=4><FONT COLOR="#001376">Cross-platform:</FONT></FONT></B> <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.</LI><LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Distributed:</FONT></FONT></B> You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.</LI><LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Fault-tolerant:</FONT></FONT></B> You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced &#X2018;on the fly&#X2019;.</LI><LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Administrator Friendly:</FONT></FONT></B> <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Comprehensive documentation.
</LI><LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows. </LI><LI CLASS="li-itemize">Web Administration.
</LI><LI CLASS="li-itemize">Shared Roster Groups.
</LI><LI CLASS="li-itemize">Command line administration tool. </LI><LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
</LI><LI CLASS="li-itemize">Capability to send announce messages.
</LI></UL></LI><LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Internationalized:</FONT></FONT></B> <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Translated to 25 languages. </LI><LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
</LI></UL></LI><LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Open Standards:</FONT></FONT></B> <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
<UL CLASS="itemize"><LI CLASS="li-itemize">
Fully XMPP compliant.
</LI><LI CLASS="li-itemize">XML-based protocol.
</LI><LI CLASS="li-itemize"><A HREF="http://www.ejabberd.im/protocols">Many protocols supported</A>.
</LI></UL></LI></UL><!--TOC section Additional Features-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc2"></A>Additional Features</H2><!--SEC END --><P>
<A NAME="addfeatures"></A>
</P><BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>ejabberd is making inroads to solving the "buggy incomplete server" problem &#X2014;
Justin Karneges, Founder of the Psi and the Delta projects</I></FONT></BLOCKQUOTE><P>Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
Modular
<UL CLASS="itemize"><LI CLASS="li-itemize">
Load only the modules you want.
</LI><LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
</LI></UL>
</LI><LI CLASS="li-itemize">Security
<UL CLASS="itemize"><LI CLASS="li-itemize">
SASL and STARTTLS for c2s and s2s connections.
</LI><LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
</LI><LI CLASS="li-itemize">Web Admin accessible via HTTPS secure access.
</LI></UL>
</LI><LI CLASS="li-itemize">Databases
<UL CLASS="itemize"><LI CLASS="li-itemize">
Internal database for fast deployment (Mnesia).
</LI><LI CLASS="li-itemize">Native MySQL support.
</LI><LI CLASS="li-itemize">Native PostgreSQL support.
</LI><LI CLASS="li-itemize">ODBC data storage support.
</LI><LI CLASS="li-itemize">Microsoft SQL Server support. </LI></UL>
</LI><LI CLASS="li-itemize">Authentication
<UL CLASS="itemize"><LI CLASS="li-itemize">
Internal Authentication.
</LI><LI CLASS="li-itemize">PAM, LDAP and ODBC. </LI><LI CLASS="li-itemize">External Authentication script.
</LI></UL>
</LI><LI CLASS="li-itemize">Others
<UL CLASS="itemize"><LI CLASS="li-itemize">
Support for virtual hosting.
</LI><LI CLASS="li-itemize">Compressing XML streams with Stream Compression (<A HREF="http://www.xmpp.org/extensions/xep-0138.html">XEP-0138</A>).
</LI><LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.xmpp.org/extensions/xep-0039.html">XEP-0039</A>).
</LI><LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
</LI><LI CLASS="li-itemize"><A HREF="http://www.xmpp.org/extensions/xep-0045.html">Multi-User Chat</A> module with support for clustering and HTML logging. </LI><LI CLASS="li-itemize">Users Directory based on users vCards.
</LI><LI CLASS="li-itemize"><A HREF="http://www.xmpp.org/extensions/xep-0060.html">Publish-Subscribe</A> component with support for <A HREF="http://www.xmpp.org/extensions/xep-0163.html">Personal Eventing via Pubsub</A>.
</LI><LI CLASS="li-itemize">Support for web clients: <A HREF="http://www.xmpp.org/extensions/xep-0025.html">HTTP Polling</A> and <A HREF="http://www.xmpp.org/extensions/xep-0206.html">HTTP Binding (BOSH)</A> services.
</LI><LI CLASS="li-itemize">IRC transport.
</LI><LI CLASS="li-itemize">Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
</LI></UL>
</LI></UL><!--CUT END -->
<!--HTMLFOOT-->
<!--ENDHTML-->
<!--FOOTER-->
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://hevea.inria.fr/index.html"><EM>H</EM><EM><FONT SIZE=2><sup>E</sup></FONT></EM><EM>V</EM><EM><FONT SIZE=2><sup>E</sup></FONT></EM><EM>A</EM></A><EM>.</EM></BLOCKQUOTE></BODY>
</HTML>
+3987
View File
File diff suppressed because it is too large Load Diff
+438 -1219
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -68,7 +68,7 @@ Peter Saint-Andr\'e, Executive Director of the Jabber Software Foundation}
\item \marking{Internationalized:} \ejabberd{} leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
\begin{itemize}
\item Translated to 24 languages. %%\improved{}
\item Translated to 25 languages. %%\improved{}
\item Support for \footahref{http://www.ietf.org/rfc/rfc3490.txt}{IDNA}.
\end{itemize}
@@ -127,6 +127,7 @@ Moreover, \ejabberd{} comes with a wide range of other state-of-the-art features
\item Users Directory based on users vCards.
\item \txepref{0060}{Publish-Subscribe} component with support for \txepref{0163}{Personal Eventing via Pubsub}.
\item Support for web clients: \txepref{0025}{HTTP Polling} and \txepref{0206}{HTTP Binding (BOSH)} services.
\item IRC transport.
\item Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
\end{itemize}
\end{itemize}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

-51
View File
@@ -1,51 +0,0 @@
Release Notes
ejabberd 2.1.2
ejabberd 2.1.2 is the second bugfix release in ejabberd 2.1.x branch.
ejabberd 2.1.2 includes several bugfixes.
More details of those fixes can be retrieved from:
http://redir.process-one.net/ejabberd-2.1.2
The new code can be downloaded from ejabberd download page:
http://www.process-one.net/en/ejabberd/
The major changes are:
* Core
- Close sessions that were half connected
- Fix SASL PLAIN authentication message for RFC4616 compliance
- Fix support for old Erlang/OTP R10 and R11
- Return proper error (not 'conflict') when register is forbidden by ACL
- When ejabberd stops, send stream close to clients
* ejabberdctl
- Check for EGID in ejabberdctl command
- Command to stop ejabberd informing users, with grace period
- If there's a problem in config file, display config lines and stop node
* MUC
- Kick occupants with reason when room is stopped due to MUC shutdown
- Write in room log when a room is created, destroyed, started, stopped
* PubSub and PEP
- Don't call gen_server on internal event (improves performance and scalability)
- Fix duplicate SHIM header in Pubsub message
- Notification messages of Pubsub node config change contained a SHIM header
- SubID SHIM header missing in Pubsub message with multiple
subscriptions on the same node
- PEP: last published item not sent from unavailable users when the
subscription is implicit (XEP-0115)
- pep_mapping not working due to Node type mismatch
* WebAdmin
- If big offline message queue, show only subset on WebAdmin
- Support in user list page of WebAdmin when mod_offline is disabled
Bug reports
You can officially report bugs on ProcessOne support site:
http://support.process-one.net/
-91
View File
@@ -1,91 +0,0 @@
Release Notes
ejabberd 2.1.3
ejabberd 2.1.3 is the third release in ejabberd 2.1.x branch.
ejabberd 2.1.3 includes many bugfixes, and some improvements.
More details of those fixes can be retrieved from:
http://redir.process-one.net/ejabberd-2.1.3
The new code can be downloaded from ejabberd download page:
http://www.process-one.net/en/ejabberd/
This is the full list of changes:
* Client connections
- Avoid 'invalid' value in iq record
- Avoid resending stream:error stanzas on terminate (EJAB-1180)
- Close also legacy sessions that were half connected (EJAB-1165)
- iq_query_info/1 now returns 'invalid' if XMLNS is invalid
- New ejabberd_c2s option support: max_fsm_queue
- Rewrite mnesia counter functions to use dirty_update_counter (EJAB-1177)
- Run user_receive_packet also when sending offline messages (EJAB-1193)
- Use p1_fsm behaviour in c2s FSM (EJAB-1173)
* Clustering
- Fix cluster race condition in route read
- New command to set master Mnesia node
- Use mnesia:async_dirty when cleaning table from failed node
* Documentation
- Add quotes in documentation of some erl arguments (EJAB-1191)
- Add option access_from (EJAB-1187)
- Add option max_fsm_queue (EJAB-1185)
- Fix documentation installation, no need for executable permission (EJAB-1170)
- Fix typo in EJABBERD_BIN_PATH (EJAB-891)
- Fix typos in example config comments (EJAB-1192)
* ejabberdctl
- Support concurrent connections with bound connection names
- Add support for Jot in ctl and TTY in debug
- Support help command names with old - characters
- Fix to really use the variable ERL_PROCESSES
* Erlang compatibility
- Don't call queue:filter/2 to keep compatibility with older Erlang versions
- Use alternative of file:read_line/1 to not require R13B02
* HTTP
- Add new debugging hook to the http receiving process
- Allow a request_handler to serve a file in root of HTTP
* HTTP-Bind (BOSH)
- Cross-domain HTTP-Bind support (EJAB-1168)
- Hibernate http-bind process after handling a request
- Reduce verbosity of HTTP Binding log messages
* LDAP
- Document ldap_dn_filter, fetch only needed attributes in search (EJAB-1204)
- Use "%u" pattern as default for ldap_uids (EJAB-1203)
* Localization
- Fix German translation (EJAB-1195)
- Fix Russian translation
* ODBC
- Fix MSSQL support, which was broken (EJAB-1201)
- Improved SQL reconnect behaviour
* Pubsub, PEP and Caps
- Add extended stanza addressing 'replyto' on PEP (EJAB-1198)
- Add pubsub#purge_offline (EJAB-1186)
- Fix pubsub#title option (EJAB-1190)
- Fix remove_user for node subscriptions (EJAB-1172)
- Optimizations in mod_caps
* Other
- mod_register: Add new acl access_from, default is to deny
- mod_sic: new module for the experimental XEP-0279 Server IP Check (EJAB-1205)
- PIEFXIS: Catch errors when exporting to PIEFXIS file (EJAB-1178)
- Proxy65: new option "hostname" (EJAB-838)
- Roster: Fix resending authorization problem
- Shared Roster Groups: get contacts nickname from vcard (EJAB-114)
- S2S: Improved s2s connections clean up (EJAB-1202)
Bug reports
You can officially report bugs on ProcessOne support site:
http://support.process-one.net/
-80
View File
@@ -1,80 +0,0 @@
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/
-70
View File
@@ -1,70 +0,0 @@
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.5
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/
-67
View File
@@ -1,67 +0,0 @@
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/
-108
View File
@@ -1,108 +0,0 @@
******************************************************************************
* These are the preliminary release notes of ejabberd 3.0.0.
*
* The download page for preliminary releases is:
* http://download.process-one.net/ejabberd/
*
* WARNING!!! PRELIMINARY DATABASE SCHEMA !!!
* The database schema may change before ejabberd 3.0.0 is released,
* and no migration code will be developed for this preliminary schema.
* The only supported migrations are
* from any ejabberd 0.9.0 ... 2.1.x to the final 3.0.0.
* Don't use this preliminary ejabberd release for a production server.
* You can test this release with a blank database or with a copy of your
* production database, but don't let your users connect to this copy
* because their changes may get lost when you upgrade to the final 3.0.0.
******************************************************************************
Release Notes
ejabberd 3.0.0
ejabberd 3.0.0 is the first release in the brand new 3.x.x branch,
and includes several minor bugfixes and a few improvements.
Read more details about the changes in:
http://redir.process-one.net/ejabberd-3.0.0
Download the source code and installers from:
http://www.process-one.net/en/ejabberd/
ejabberd 3.0.0 includes three major changes:
* exmpp is now extensively used in ejabberd for parsing stanzas, and many more.
* gen_storage (abbreviated GS) provides a database abstraction layer,
which supports storage in Mnesia and in ODBC databases.
Several ejabberd modules use GS, like mod_roster and ejabberd_auth_storage.
The schema of the tables stored by those modules have changed.
ejabberd automatically creates mnesia and ODBC tables,
and migrates them from a previous ejabberd version.
* Massive Hosting (abbreviated MH) is preliminary implemented in ejabberd,
but it is still incomplete, untested and undocumented.
New option static_modules.
This is a more detailed list of changes since ejabberd 2.1.x releases:
* Requirements
- Erlang/OTP R12B-5 or higher is required
- exmpp 0.9.6 or higher is required, not optional
- ESASL library is optional, for SASL GSSAPI authentication
- Libexpat not required by ejabberd, it uses exmpp now (EJAB-1111)
- GNU Iconv not required, because mod_irc isn't included anymore (EJAB-954)
- Only database migration from ejabberd 0.9.0 up to 2.1.x is supported
- ejabberd modules developed for previous releases need to be upgraded to exmpp
* Configuration
- New option clusterid
- New option that can't be used yet: static_modules
- ejabberd_auth_storage: new option auth_storage
- mod_muc_log: option spam_prevention is now link_nofollow (EJAB-1141)
- mod_roster: new access option (EJAB-72)
- S2S: allow definition of local address for outgoing connections (EJAB-418)
* Database
- New gen_storage that provides a database storage abstraction layer (EJAB-1102)
- mod_*_odbc are obsolete, use the normal ones with the option {backend, odbc}
- New ejabberd_auth_storage that supports internal and odbc databases
* XEP support
- Removed support for the deprecated XEP-0018 Invisible Presence (EJAB-810)
- Update http_bind to XEP-0124 1.10 and XEP-0206 1.3
- mod_multicast: service for XEP-0033: Extended Stanza Addressing (EJAB-265)
- New ejabberd router for multicast packets (XEP-0033) (EJAB-329)
- ejabberd_c2s uses XEP-0033 if mod_multicast is enabled (EJAB-267)
- mod_muc uses XEP-0033 if mod_multicast is enabled (EJAB-266)
* Pubsub/PEP/Caps
- Allow distinguish between leafs and items (EJAB-1027)
- Enforce disco features results (EJAB-1033, EJAB-1228, EJAB-1238)
- Enforce pubsub#presence_based_delivery (EJAB-1221)
- Improve get_caps while still waiting for initial presence (EJAB-934)
- Make nodetree_tree implicitly create parent node if required (EJAB-944)
- Only use binary() instead of string() in Host and ServerHost (EJAB-1244)
- Rename NodeId (pubsub_node.id) bindings to Nidx (EJAB-1000)
- node_flat becomes default; it has the code instead of hometree (EJAB-1077)
- pubsub#notify_sub node option (EJAB-1230)
- pubsub#type node config and payload namespace (EJAB-1083)
* Miscellanea
- Bugfix handling Service Discovery to contacts (EJAB-1207)
- Bugfix: external contacts were offline after unavailable presence (EJAB-943)
- Don't offer SASL auth before doing TLS if TLS is required
- Experimental support for GSSAPI auth, requires esasl library (EJAB-831)
- Replaced the full ejabberd_zlib into a simple exmpp_compress interface (EJAB-991)
- Use binaries instead of lists where possible (EJAB-17)
- Webadmin: provide list of registered pids/ports
- Windows ejabberdctl.cmd only handles up to 4 arguments (EJAB-1216)
- ejabberdctl: when starting, check it isn't already running
Bug reports
You can officially report bugs on ProcessOne support site:
http://support.process-one.net/
+2
View File
@@ -0,0 +1,2 @@
% ejabberd version (automatically generated).
\newcommand{\version}{2.1.1}
-16
View File
@@ -42,22 +42,6 @@ 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;
+75
View File
@@ -0,0 +1,75 @@
#!/bin/sh
#
# PROVIDE: ejabberd
# REQUIRE: DAEMON
# KEYWORD: shutdown
#
HOME=/usr/pkg/jabber D=/usr/pkg/jabber/ejabberd export HOME
name="ejabberd"
rcvar=$name
if [ -r /etc/rc.conf ]
then
. /etc/rc.conf
else
eval ${rcvar}=YES
fi
# $flags from environment overrides ${rcvar}_flags
if [ -n "${flags}" ]
then
eval ${rcvar}_flags="${flags}"
fi
checkyesno()
{
eval _value=\$${1}
case $_value in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;;
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;;
*)
echo "\$${1} is not set properly."
return 1
;;
esac
}
cmd=${1:-start}
case ${cmd} in
force*)
cmd=${cmd#force}
eval ${rcvar}=YES
;;
esac
if checkyesno ${rcvar}
then
else
exit 0
fi
case ${cmd} in
start)
if [ -x $D/src ]; then
echo "Starting ${name}."
cd $D/src
ERL_MAX_PORTS=32000 export ERL_MAX_PORTS
ulimit -n $ERL_MAX_PORTS
su jabber -c "/usr/pkg/bin/erl -sname ejabberd -s ejabberd -heart -detached -sasl sasl_error_logger '{file, \"ejabberd-sasl.log\"}' &" \
1>/dev/null 2>&1
fi
;;
stop)
echo "rpc:call('ejabberd@`hostname -s`', init, stop, [])." | \
su jabber -c "/usr/pkg/bin/erl -sname ejabberdstop"
;;
restart)
echo "rpc:call('ejabberd@`hostname -s`', init, restart, [])." | \
su jabber -c "/usr/pkg/bin/erl -sname ejabberdrestart"
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
+81
View File
@@ -0,0 +1,81 @@
#!/bin/sh
echo '1. fetch, compile, and install erlang'
if [ ! pkg_info erlang 1>/dev/null 2>&1 ]; then
cd /usr/pkgsrc/lang/erlang
make fetch-list|sh
make
make install
fi
if pkg_info erlang | grep -q erlang-9.1nb1; then
else
echo "erlang-9.1nb1 not installed" 1>&2
exit 1
fi
echo '2. install crypt_drv.so'
if [ ! -d /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib ] ; then
mkdir -p /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
fi
if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/crypto_drv.so ]; then
cp work/otp*/lib/crypto/priv/*/*/crypto_drv.so \
/usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
fi
echo '3. compile and install elibcrypto.so'
if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/elibcrypto.so ]; then
cd /usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/crypto/c_src
ld -r -u CRYPTO_set_mem_functions -u MD5 -u MD5_Init -u MD5_Update \
-u MD5_Final -u SHA1 -u SHA1_Init -u SHA1_Update -u SHA1_Final \
-u des_set_key -u des_ncbc_encrypt -u des_ede3_cbc_encrypt \
-L/usr/lib -lcrypto -o ../priv/obj/i386--netbsdelf/elibcrypto.o
cc -shared \
-L/usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/erl_interface/obj/i386--netbsdelf \
-o ../priv/obj/i386--netbsdelf/elibcrypto.so \
../priv/obj/i386--netbsdelf/elibcrypto.o -L/usr/lib -lcrypto
cp ../priv/obj/i386--netbsdelf/elibcrypto.so \
/usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
fi
echo '4. compile and install ssl_esock'
if [ ! -f /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/bin/ssl_esock ]; then
cd /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/obj/
make
fi
echo '5. initial ejabberd configuration'
cd /usr/pkg/jabber/ejabberd/src
./configure
echo '6. edit ejabberd Makefiles'
for M in Makefile mod_*/Makefile; do
if [ ! -f $M.orig ]; then
mv $M $M.orig
sed -e s%/usr/local%/usr/pkg%g < $M.orig > $M
fi
done
echo '7. compile ejabberd'
gmake
for A in mod_irc mod_muc mod_pubsub; do
(cd $A; gmake)
done
echo ''
echo 'now edit ejabberd.cfg'
echo ''
echo 'to start ejabberd: erl -sname ejabberd -s ejabberd'
+66
View File
@@ -0,0 +1,66 @@
% jabber.dbc.mtview.ca.us
override_acls.
{acl, admin, {user, "mrose", "jabber.dbc.mtview.ca.us"}}.
{access, announce, [{allow, admin},
{deny, all}]}.
{access, c2s, [{deny, blocked},
{allow, all}]}.
{access, c2s_shaper, [{none, admin},
{normal, all}]}.
{access, configure, [{allow, admin},
{deny, all}]}.
{access, disco_admin, [{allow, admin},
{deny, all}]}.
{access, muc_admin, [{allow, admin},
{deny, all}]}.
{access, register, [{deny, all}]}.
{access, s2s_shaper, [{fast, all}]}.
{auth_method, internal}.
{host, "jabber.dbc.mtview.ca.us"}.
{outgoing_s2s_port, 5269}.
{shaper, normal, {maxrate, 1000}}.
{shaper, fast, {maxrate, 50000}}.
{welcome_message, none}.
{listen, [{5222, ejabberd_c2s,
[{access, c2s},
{shaper, c2s_shaper}]},
{5223, ejabberd_c2s,
[{access, c2s},
{shaper, c2s_shaper},
{ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]},
{5269, ejabberd_s2s_in,
[{shaper, s2s_shaper}]}]}.
{modules, [
{mod_register, []},
{mod_roster, []},
{mod_privacy, []},
{mod_configure, []},
{mod_disco, []},
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
{mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]},
{mod_private, []},
% {mod_irc, []},
{mod_muc, []},
{mod_pubsub, []},
{mod_time, []},
{mod_last, []},
{mod_version, []}
]}.
% Local Variables:
% mode: erlang
% End:
@@ -0,0 +1,77 @@
<!-- aim-transport.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/aim-transport-error.log</file>
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<format>%d %h %s</format>
<file>/var/log/jabber/aim-transport-record.log</file>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file> <!-- This file is part of jabberd-1.4.x. -->
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id='aim.SERVER.COM'>
<!-- aim-transport configuration. -->
<aimtrans xmlns='jabber:config:aimtrans'>
<vCard>
<FN>AIM/ICQ Transport</FN>
<DESC>This is the AIM/ICQ Transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://aim-transport.jabberstudio.org/</URL>
</vCard>
<charset>cp1252</charset>
</aimtrans>
<!-- aim-transport module. -->
<load>
<aim_transport>/usr/local/lib/jabber/aim-transport.so</aim_transport>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="icq-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5233</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/aim-transport.pid</pidfile>
</jabber>
+136
View File
@@ -0,0 +1,136 @@
<!-- ile.xml -->
<config>
<jabber>
<server>127.0.0.1</server>
<port>5238</port>
<secret>SECRET</secret>
<service>ile.SERVER.COM</service>
<connectsleep>7</connectsleep> <!-- seconds to wait if we get disconnected -->
<language>en</language>
<vCard>
<FN>I Love Email</FN>
<DESC>With this service you can receive email notifications.
Security warning: Be careful when using this. Your password will travel in clear from your client to your jabber server if you don't use SSL and it will probably travel in clear from the jabber server to your email server. Use with care. This shouldn't be an issue in your Intranet, but it is if you use an ILE installed in a foreign jabber server.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://ile.jabberstudio.org/</URL>
</vCard>
</jabber>
<debug>
<file>/var/log/jabber/ile.log</file>
<level>1</level> <!-- man Net::Jabber::Debug -->
</debug>
<mail>
<checkinterval>10</checkinterval> <!-- in minutes -->
<timeout>20</timeout> <!-- timeout for IMAP/POP connection, in seconds -->
</mail>
<files>
<users>/var/spool/jabber/ile.SERVER.COM/users.db</users>
<passwords>/var/spool/jabber/ile.SERVER.COM/passwords.db</passwords>
<hosts>/var/spool/jabber/ile.SERVER.COM/hosts.db</hosts>
<types>/var/spool/jabber/ile.SERVER.COM/types.db</types>
<notifyxa>/var/spool/jabber/ile.SERVER.COM/notifyxa.db</notifyxa>
<notifydnd>/var/spool/jabber/ile.SERVER.COM/notifydnd.db</notifydnd>
<urls>/var/spool/jabber/ile.SERVER.COM/urls.db</urls>
</files>
<form>
<en>
<instructions>Please fill in the fields,according to your email account settings and notification preferences</instructions>
<title>ILE: Email notification service</title>
<email_options>Email account settings</email_options>
<user>Username</user>
<pass>Password</pass>
<host>Hostname</host>
<type>Type</type>
<newmail>You have received NUM email messages since last time I checked, which was CHECKINTERVAL minutes ago.</newmail>
<errorcheck>There was an error while trying to check mail for ACCOUNT.</errorcheck>
<notify_options>Notification Options</notify_options>
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Login to ACCOUNT</webmail_login>
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
</en>
<es>
<instructions>Por favor, rellene los campos del formulario.</instructions>
<title>ILE: Servicio de notificación de correo</title>
<email_options>Configuración de la cuenta de correo</email_options>
<user>Usuario</user>
<pass>Clave</pass>
<host>Host</host>
<type>Tipo</type>
<newmail>Ha recibido NUM email(s) desde la última comprobación que fue hace CHECKINTERVAL minutos</newmail>
<errorcheck>Ha habido un error en la comprobación del correo para la cuenta ACCOUNT.</errorcheck>
<notify_options>Opciones de notificación</notify_options>
<notifyxa>Notificar incluso si muy ausente (XA)</notifyxa>
<notifydnd>Notificar incluso si no molestar (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Leer correo de ACCOUNT</webmail_login>
<iledesc>ILE: un notificador de nuevo email - http://ile.jabberstudio.org</iledesc>
</es>
<ca>
<instructions>Ompli els camps del formulari.</instructions>
<title>ILE: Servei de notificació de nou email</title>
<email_options>Dades del compte de mail</email_options>
<user>Usuari</user>
<pass>Clau</pass>
<host>Host</host>
<type>Tipus</type>
<newmail>Ha rebut NUM email(s) des de la última comprobació que va ser fa CHECKINTERVAL minuts.</newmail>
<errorcheck>S'ha produit un error en la comprobació del correu per al compte ACCOUNT.</errorcheck>
<notify_options>Opcions de notificació</notify_options>
<notifyxa>Notificar si molt absent (XA)</notifyxa>
<notifydnd>Notificar si no molestar (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Llegir correu de ACCOUNT</webmail_login>
<iledesc>ILE: un notificador de nou email - http://ile.jabberstudio.org</iledesc>
</ca>
<ro>
<!-- Contributed by Adrian Rappa -->
<instructions>Va rog completati urmatoarele campuri</instructions>
<title>I Love Email: new email notification service</title>
<email_options>Email account settings</email_options>
<user>Nume utilizator</user>
<pass>Parola</pass>
<host>Nume gazda</host>
<type>Tip</type>
<newmail>Ati primit NUM mesaj(e) de la ultima verificare, care a fost acum CHECKINTERVAL minute.</newmail>
<errorcheck>A fost eroare in timp ce incercam sa verific posta pentru ACCOUNT.</errorcheck>
<notify_options>Notification Options</notify_options>
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Login to ACCOUNT</webmail_login>
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
</ro>
<nl>
<!-- Contributed by Sander Devrieze -->
<instructions>Vul volgende velden in.</instructions>
<title>ILE: Dienst voor e-mailnotificaties</title>
<email_options>Instellingen van e-mailaccount</email_options>
<user>Gebruikersnaam</user>
<pass>Wachtwoord</pass>
<host>Inkomende mailserver</host>
<type>Type verbinding</type>
<newmail>U hebt NUM berichten ontvangen sinds CHECKINTERVAL minuten geleden.</newmail>
<errorcheck>Fout tijdens controle op nieuwe e-mails bij ACCOUNT. ILE zal deze account niet meer opnieuw controleren tot u uw registratiegegevens wijzigt of opnieuw aanmeldt.</errorcheck>
<notify_options>Notificatie-instellingen</notify_options>
<notifyxa>Notificeer ook in de status Niet Beschikbaar (XA)</notifyxa>
<notifydnd>Notificeer ook in de status Niet Storen (DND)</notifydnd>
<webmail_url>URL van webmail</webmail_url>
<webmail_login>Aanmelden op ACCOUNT</webmail_login>
<iledesc>ILE: een dienst om e-mailnotificaties te ontvangen: http://ile.jabberstudio.org</iledesc>
</nl>
</form>
</config>
@@ -0,0 +1,149 @@
<jggtrans>
<service jid="gg.SERVER.COM"/>
<!-- This connects the jabber-gg-transport process to ejabberd. -->
<connect id="gglinker">
<ip>127.0.0.1</ip>
<port>5237</port>
<secret>SECRET</secret>
</connect>
<register>
<!-- This tag contains the message displayed to users at registration time.
You can use <p/> and/or <br/> to break lines. Multiple spaces and newlines
are converted to just one, so formatting of config file doesn't really matter. -->
<instructions>
Fill in your GG number (after "username")
and password to register on the transport.
<p/>To change your information in the GaduGadu directory you need to fill in the other fields.
<p/>To remove registration you need to leave the form blank.
</instructions>
</register>
<search>
<!-- This tag contains the message displayed to users at search time. -->
<instructions>
To search people:<br/>
First fill in surname or family name, nickname, city, birthyear or range of birthyears (eg. 1950-1960)
and gender (you may fill in more fields at once).<br/>
or<br/>
Fill in phone number<br/>
or<br/>
Fill in the GG number of the person you are searching.
</instructions>
</search>
<gateway>
<!-- This is message, that may be displayed to user when adding gg contact. -->
<desc>Please fill in the GaduGadu number of the person you want to add.</desc>
<!-- And this is the prompt for GG number. -->
<prompt>GG Nummer</prompt>
</gateway>
<vCard>
<FN>Gadu-Gadu Transport</FN>
<DESC>This is the Gadu-Gadu Transport.</DESC>
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
<URL>http://www.jabberstudio.org/projects/jabber-gg-transport/</URL>
</vCard>
<!-- Default user locale (language).
Empty means system locale setting,
no (or commented-out) <default_locale> tag means no translations. -->
<!-- <default_locale>pl_PL</default_locale> -->
<!-- Logger configuration.
You may configure one logger of type "syslog" and/or one of type "file".
You may also not configure logging at all. -->
<log type="syslog" facility="local0"/>
<log type="file">/var/log/jabber/jabber-gg-transport.log</log>
<!-- Uncomment this, if you want proxy to be used for Gadu-Gadu connection. -->
<!--
<proxy>
<ip>127.0.0.1</ip>
<port>8080</port>
</proxy>
-->
<!-- You can change these values according to your needs. -->
<conn_timeout>60</conn_timeout>
<ping_interval>10</ping_interval>
<!-- Gadu-Gadu server doesn't seem to answer pings anymore :-(
So let's give it 10 year :-) -->
<pong_timeout>315360000</pong_timeout>
<!-- This time after disconnection from Gadu-Gadu server the transport
will try to connect again. -->
<reconnect>300</reconnect>
<!-- How long to wait before restart, after jabber server connection is broken
negative value means, that jggtrans should terminate. -->
<restart_timeout>60</restart_timeout>
<!-- Delay between the unavailable presence is received from user and loggin out
from Gadu-Gadu - for nice <presence type="invisible"/> support. -->
<disconnect_delay>5</disconnect_delay>
<!-- list of Gadu-Gadu servers to use.
<hub/> means "use GG hub to find server"
<server/> tag should contain server address and may contain "port"
attribute with port number. When TLS is available (supported by libgadu)
it will be used unless "tls" attribute is set to "no". Please notice,
that not all servers will accept TLS connections.
Servers (including hub) are tried in order as they appear in <servers/>
element.
A reasonable default server list is hardcoded in jggtrans.
-->
<!--
<servers>
<hub/>
<server port="443">217.17.41.90</server>
<server tls="no">217.17.41.85</server>
<server tls="no">217.17.41.88</server>
</servers>
-->
<!-- Spool directory. This is the place, where user info will be stored. -->
<!-- Be careful about permissions - users' Gadu-Gadu passwords are stored there. -->
<spool>/var/spool/jabber/gg.SERVER.COM/</spool>
<!-- Where to store pid file. This tag is optional. -->
<pidfile>/var/run/jabber/jabber-gg-transport.pid</pidfile>
<!-- jid allowed to do some administrative task (eg. discovering online users).
May be used multiple times. -->
<admin>GG_TRANSPORT_ADMIN@SERVER.COM</admin>
<!-- ACL gives detailed access control to the transport. -->
<acl>
<!-- Example entries: -->
<allow who="admin@SERVER.COM" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
<!-- will allow statistics gathering to admin@SERVER.COM -->
<deny who="*" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
<!-- will deny statistics gathering for anybody else -->
<!-- <allow who="*@SERVER.COM"/> -->
<!-- will allow anything else to users from "SERVER.COM" -->
<!-- <deny what="iq/query?xmlns=jabber:x:register"/> -->
<!-- will deny registration for all other users -->
<!-- <allow what="presence"/> -->
<!-- allow presence from anybody -->
<!-- <allow what="iq"/> -->
<!-- allow iq from anybody -->
<!-- <allow what="message"/> -->
<!-- allow message from anybody -->
<!-- <deny/> -->
<!-- will deny anything else -->
</acl>
</jggtrans>
+128
View File
@@ -0,0 +1,128 @@
<!-- jit.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<file>/var/log/jabber/jit-error</file> <!-- WPJabber logs with date. -->
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<file>/var/log/jabber/jit-record</file> <!-- WPJabber logs with date. -->
</log>
<!--
ejabberd do not provide XDB services.
xdb_file-jit.so (the renamed xdb_file.so from WPJabber) is
loaded in to handle all XDB requests.
Read also the documentation in xdb_file/README from the JIT package.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/xdb_file.so</xdb_file> <!-- The xdb_file.so from WPJabber/JIT. -->
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="icq.SERVER.COM">
<!--
Replace SERVER.COM with the same as above to enable sms.
-->
<host>sms.icq.SERVER.COM</host>
<!-- JIT configuration. -->
<icqtrans xmlns="jabber:config:icqtrans">
<sms>
<host>sms.icq.SERVER.COM</host>
<!-- Status of virtual "sms-contacts". -->
<show>away</show>
<status/>
</sms>
<instructions>Fill in your UIN and password.</instructions>
<search>Search ICQ users.</search>
<vCard>
<FN>ICQ Transport (JIT)</FN>
<DESC>This is the Jabber ICQ Transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://jit.jabberstudio.org/</URL>
</vCard>
<!-- Hashtable for users. -->
<prime>3907</prime>
<!-- Send messages from ICQ as chat to Jabber clients. -->
<chat/>
<!-- Enable this for ICQ web presence. -->
<web/>
<!--
If you don't want jabber:x:data forms
in reg and search uncomment this tag
(Not recomended).
-->
<no_xdata/>
<!--
This tag is necessary when using ejabberd.
In this way JIT will have its own contact list.
-->
<own_roster/>
<!--
When present, this tag will tell JIT not to try to
get the user's roster (which will take a bit of time
to fail in scenarios described above).
-->
<no_jabber_roster/>
<!-- File with stats. -->
<user_count_file>/var/spool/jabber/jit-count</user_count_file>
<!--
Interval beetween checking sessions: ping, messages, acks.
-->
<session_check>5</session_check>
<!-- Reconnect retries. -->
<reconnects>5</reconnects>
<!--
Time in sec when session can be inactive, 0=disabled.
-->
<session_timeout>18000</session_timeout>
<charset>windows-1252</charset>
<server>
<host port="5190">login.icq.com</host>
</server>
</icqtrans>
<!-- JIT module. -->
<load>
<icqtrans>/usr/local/lib/jabber/jit.so</icqtrans>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="icq-linker">
<host>SERVER.COM</host>
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5234</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/jit.pid</pidfile>
</jabber>
@@ -0,0 +1,118 @@
<!-- msn-transport.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/msn-transport-error.log</file>
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<format>%d %h %s</format>
<file>/var/log/jabber/msn-transport-record.log</file>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="msn.SERVER.COM">
<!-- msn-transport configuration. -->
<msntrans xmlns="jabber:config:msntrans">
<instructions>Fill in your MSN account and password (eg: user1@hotmail.com). A nickname is optional.</instructions>
<vCard>
<FN>MSN Transport</FN>
<DESC>This is the MSN Transport.</DESC>
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
<URL>http://msn-transport.jabberstudio.org/</URL>
</vCard>
<!--
Conference support allows you to create groupchat rooms on the
msn-transport and invite MSN users to join.
-->
<conference id="conference.msn.SERVER.COM">
<!--
This will make MSN transport invite you to a special groupchat
room when more then one user joins a normal one-on-one session.
Joining this room will make MSN transport "switch" the session
into groupchat mode. If you ignore it, MSN transport will
continue to send the messages as one-on-one chats.
-->
<invite>More than one user entered this chat session. Enter this room to switch to groupchat modus.</invite>
<notice>
<join> is available</join>
<leave> has leaved the room</leave>
</notice>
</conference>
<!-- Enable Hotmail inbox notification. -->
<headlines/>
<!--
Enable fancy friendly names
If the user enters a nickname upon registration, and the user has
a status message, their MSN friendly name will be "nickname - status message".
If the user does not enter a nickname on registration, but they do have
a status message, their friendly name will just be their status message.
If the user did enter a nickname on registration, but they have a blank status message,
then their friendly name will just be the registered nickname.
If the user did not enter a nickname on registration, and they have a blank status message,
their nickname will just be the username portion of their JID.
If the above chosen friendly name is too long, then it will be truncated and "..." placed
at the end. MSN only supports friendly names of 128 characters, so this is unavoidable.
If this is disabled, then the registered nick is always sent as the MSN friendly name,
or if that is blank, the username portion of their JID is sent instead.
-->
<fancy_friendly/>
</msntrans>
<!-- msn-transport module. -->
<load>
<msntrans>/usr/local/lib/jabber/msn-transport.so</msntrans>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="msn-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5235</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/msn-transport.pid</pidfile>
</jabber>
@@ -0,0 +1,86 @@
<!-- yahoo-transport-2.xml -->
<jabber>
<!--
You need to add the elogger entry here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/yahoo-transport-2-error.log</file>
<stderr/>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="yahoo.SERVER.COM">
<!-- yahoo-transport-2 configuration. -->
<config xmlns="jabber:config:yahoo">
<vCard>
<NAME>Yahoo! Transport</NAME>
<FN>vCard not implemented in current version</FN>
<DESC>This is the Yahoo! transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://yahoo-transport-2.jabberstudio.org/</URL>
</vCard>
<instructions>Fill in your YAHOO! Messenger username and password to register on this transport.</instructions>
<server>scs.msg.yahoo.com</server>
<port>5050</port>
<!--
The character map. This provides character set translation from UTF-8
to the indicated character map. See the man page for 'iconv' for available
character maps on your platform. CP1252 is the standard Windows character
set.
-->
<charmap>CP1252</charmap>
<!--
When this element exists, the transport will send new mail notifications as
well as a count of unread messages when the user initially logs in.
-->
<newmail/>
</config>
<!-- yahoo-transport-2 module. -->
<load>
<yahoo_transport>/usr/local/lib/jabber/yahoo-transport-2.so</yahoo_transport>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="yahoo-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5236</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/yahoo-transport-2.pid</pidfile>
</jabber>
+45
View File
@@ -0,0 +1,45 @@
#!/bin/sh
#########################################################
#
# aim-transport -- script to start aim-transport.
#
#########################################################
DAEMON=/usr/local/sbin/jabberd-aim-transport
CONF=/etc/jabber/aim-transport.xml
NAME=jabberd-aim-transport
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+43
View File
@@ -0,0 +1,43 @@
#!/bin/sh
#########################################################
#
# ile -- script to start ILE.
#
#########################################################
DAEMON=/usr/local/sbin/ile.pl
NAME=ile.pl
CONF=/etc/jabber/ile.xml
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
echo "Not implemented yet. Starting in normal mode"
$0 start
;;
start)
test -f $DAEMON || exit 0
echo "Starting $NAME."
$DAEMON $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|status|restart}"
exit 1
esac
@@ -0,0 +1,47 @@
#!/bin/sh
#########################################################
#
# jabber-gg-transport -- script to start jabber-gg-transport.
#
#########################################################
DAEMON=/usr/local/sbin/jggtrans
CONF=/etc/jabber/jabber-gg-transport.xml
NAME=jggtrans
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
rm /var/run/jabber/jabber-gg-transport.pid
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+45
View File
@@ -0,0 +1,45 @@
#!/bin/sh
#########################################################
#
# jit -- script to start JIT.
#
#########################################################
DAEMON=/usr/local/sbin/wpjabber-jit
CONF=/etc/jabber/jit.xml
NAME=wpjabber-jit
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+50
View File
@@ -0,0 +1,50 @@
#!/bin/sh
#########################################################
#
# msn-transport -- script to start MSN Transport.
#
#########################################################
DAEMON=/usr/local/sbin/jabberd-msn-transport
CONF=/etc/jabber/msn-transport.xml
NAME=jabberd-msn-transport
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
strace)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in strace mode."
strace -o /opt/ejabberd/var/log/jabber/strace.log $DAEMON -H $HOME -c $CONF &
;;
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+45
View File
@@ -0,0 +1,45 @@
#!/bin/sh
##############################################################
#
# yahoo-transport-2 -- script to start Yahoo-transport-2.
#
#############################################################
DAEMON=/usr/local/sbin/jabberd-yahoo-transport-2
CONF=/etc/jabber/yahoo-transport-2.xml
NAME=jabberd-yahoo-transport-2
HOME=/etc/jabber/
USER=ejabberd
#############################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+29 -129
View File
@@ -1,11 +1,15 @@
# $Id$
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
EXPAT_CFLAGS = @EXPAT_CFLAGS@
ERLANG_CFLAGS= @ERLANG_CFLAGS@
EXPAT_LIBS = @EXPAT_LIBS@
ERLANG_LIBS = @ERLANG_LIBS@
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
@@ -26,16 +30,15 @@ else
INIT_USER=$(INSTALLUSER)
endif
EFLAGS += @ERLANG_SSLVER@ -pa .
EFLAGS += @ERLANG_SSL39@ -pa .
# make debug=true to compile Erlang module with debug informations.
ifdef debug
EFLAGS+=+debug_info +export_all
EFLAGS+=+debug_info
endif
DEBUGTOOLS = p1_prof.erl etop_tr.erl
ifdef debugtools
SOURCES+=$(DEBUGTOOLS)
ifdef ejabberd_debug
EFLAGS+=-Dejabberd_debug
endif
ifeq (@hipe@, true)
@@ -54,30 +57,19 @@ 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)
endif
ifeq (@flash_hack@, true)
ERLC_FLAGS+=-DENABLE_FLASH_HACK
CPPFLAGS+=-DENABLE_FLASH_HACK
endif
prefix = @prefix@
exec_prefix = @exec_prefix@
SUBDIRS = stun @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ @tls@ @odbc@
ERLSHLIBS =
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ stringprep stun @tls@ @odbc@ @ejabberd_zlib@
ERLSHLIBS = expat_erl.so
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
SOURCES_ALL = $(wildcard *.erl)
SOURCES_MISC = $(ERLBEHAVS) $(DEBUGTOOLS)
SOURCES += $(filter-out $(SOURCES_MISC),$(SOURCES_ALL))
SOURCES = $(filter-out $(ERLBEHAVS),$(SOURCES_ALL))
ERLBEHAVBEAMS = $(ERLBEHAVS:.erl=.beam)
BEAMS = $(SOURCES:.erl=.beam)
@@ -118,9 +110,6 @@ MSGSDIR = $(PRIVDIR)/msgs
# /var/lib/ejabberd/
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
# /var/lock/ejabberdctl
CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl
# /var/lib/ejabberd/.erlang.cookie
COOKIEFILE = $(SPOOLDIR)/.erlang.cookie
@@ -149,7 +138,7 @@ all-recursive: $(ERLBEHAVBEAMS)
all-recursive install-recursive uninstall-recursive \
clean-recursive distclean-recursive devdoc-recursive \
clean-recursive distclean-recursive \
mostlyclean-recursive maintainer-clean-recursive:
@subdirs="$(SUBDIRS)"; for subdir in $$subdirs; do \
target=`echo $@|sed 's,-recursive,,'`; \
@@ -163,15 +152,17 @@ mostlyclean-recursive maintainer-clean-recursive:
@ERLC@ -W $(EFLAGS) $*.erl
$(ERLSHLIBS): %.so: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LIBS) \
$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) \
$(subst ../,,$(subst .so,.c,$@)) \
$(EXPAT_LIBS) \
$(EXPAT_CFLAGS) \
$(ERLANG_LIBS) \
$(ERLANG_CFLAGS) \
-o $@ \
$(DYNAMIC_LIB_CFLAGS)
translations:
../tools/extract_translations/prepare-translation.sh -updateall
../contrib/extract_translations/prepare-translation.sh -updateall
install: all
#
@@ -201,7 +192,6 @@ 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)
@@ -242,11 +232,6 @@ install: all
chmod -R 750 $(SPOOLDIR)
[ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; }
#
# ejabberdctl lock directory
install -d -m 750 $(O_USER) $(CTLLOCKDIR)
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT)
chmod -R 750 $(CTLLOCKDIR)
#
# Log directory
install -d -m 750 $(O_USER) $(LOGDIR)
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(LOGDIR) >$(CHOWN_OUTPUT)
@@ -254,16 +239,9 @@ install: all
#
# Documentation
install -d $(DOCDIR)
[ -f ../doc/guide.html ] \
&& install -m 644 ../doc/dev.html $(DOCDIR) \
&& install -m 644 ../doc/guide.html $(DOCDIR) \
&& install -m 644 ../doc/*.png $(DOCDIR) \
|| echo "No ../doc/guide.html was built"
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)
install ../doc/guide.html $(DOCDIR)
install ../doc/*.png $(DOCDIR)
install ../doc/*.txt $(DOCDIR)
uninstall: uninstall-binary
@@ -287,10 +265,9 @@ uninstall-all: uninstall-binary
rm -rf $(ETCDIR)
rm -rf $(EJABBERDDIR)
rm -rf $(SPOOLDIR)
rm -rf $(CTLLOCKDIR)
rm -rf $(LOGDIR)
clean: clean-recursive clean-local clean-devdoc
clean: clean-recursive clean-local
clean-local:
rm -f *.beam $(ERLSHLIBS) epam ejabberdctl.example
@@ -300,6 +277,7 @@ distclean: distclean-recursive clean-local
rm -f config.status
rm -f config.log
rm -f Makefile
[ ! -f ../ChangeLog ] || rm -f ../ChangeLog
TAGS:
etags *.erl
@@ -309,89 +287,11 @@ Makefile: Makefile.in
dialyzer: $(BEAMS)
@dialyzer -c .
## Devdoc definitions
SRCDIR=.
DDTDIR=.
DEVDOCDIR=../doc/devdoc
DEVDOC_ERLS = $(wildcard ../doc/devdoc/*.erl)
DEVDOC_BEAMS = $(DEVDOC_ERLS:.erl=.beam)
APPNAME = ejabberd
VSN = $(shell sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ./ejabberd.app)
.PHONY = all
HTMLS = $(SOURCES:%.erl=../doc/devdoc/%.html)
ERLHTMLS = $(SOURCES:%.erl=../doc/devdoc/%.erl.html)
SVGS = $(SOURCES:%.erl=../doc/devdoc/%.svg)
EDOCINDEX = $(DEVDOCDIR)/index.html
## Devdoc rules
devdoc: compile-devdoc devdoc-root devdoc-recursive
$(devdoc-customize)
$(devdoc-move)
devdoc-root: $(EDOCINDEX) $(HTMLS) $(ERLHTMLS) $(SVGS)
define devdoc-customize
find $(DDTDIR) -type f -name '*.erl.html' -exec sed -i 's/<span class="attribute" >module<\/span>(\([A-Za-z0-9_]*\))/<span class="attribute" >module<\/span>(<a href="\1.html">\1<\/a>)/g;' {} \;
find $(DDTDIR) -type f -name '*.erl.html' -exec sed -i 's/arity="\([0-9]*\)" >\([A-Za-z0-9_]*\)</><a class="function" id="\2-\1" href="EDOCFILENAME#\2-\1">\2<\/a></g;' {} \;
find $(DDTDIR) -type f -name '*.erl.html' -exec sed -i 's/class="export" >\([A-Za-z0-9_]*\)\/\([0-9]*\)</class="export" ><a href="#\1-\2">\1\/\2<\/a></g;' {} \;
for fn in *.erl.html; do \
sed -i 's/EDOCFILENAME/'$${fn%.erl.html}.html'/g;' $${fn} ; \
sed = $${fn} | sed 'N;s/\n/ /;s/^\([0-9_]*\)/<a href="#\1" name="\1" class="l">\1<\/a>/' >$${fn}.tmp ; \
mv $${fn}.tmp $${fn} ; \
sed -i 's/<a href="#1" name="1" class="l">1<\/a> <html><link rel="stylesheet" type="text\/css"href="escobar.css"><\/link><body><pre><span class="comment" >/<html><link rel="stylesheet" type="text\/css" href="escobar.css"><\/link><body><pre><span class="comment" ><a href="#1" name="1" class="l">1<\/a> /g;' $${fn} ; \
done
-mv *.erl.html $(DEVDOCDIR)
find $(DDTDIR) -type f -name '*.html' -exec sed -i 's/<a href="overview-summary.html" target="overviewFrame">/<a href="index.html" target="_parent">/g;' {} \;
find $(DDTDIR) -type f -name '*.html' -exec sed -i 's/align=\"right\" border=\"0\" alt=\"erlang logo\"/alt=\"erlang logo\"><\/a><a href=\"http:\/\/www.ejabberd.im\/\"><img src=\"ejabberd-im.png\" alt=\"ejabberd Community\"><\/a><a href=\"http:\/\/www.process-one.net\/en\/ejabberd\/\"><img src=\"ejabberd-p1.png\" alt=\"ejabberd home\"><\/a><a href=\"http:\/\/www.process-one.net\/\"><img src=\"process-one.png\" alt=\"ProcessOne\"/g;' {} \;
find $(DDTDIR) -type f -name '*.html' -exec sed -i 's/^<h1>Module \([A-Za-z0-9_]*\)<\/h1>/<h1>Module \1 [<a href="\1.erl.html">erl<\/a> <a href="\1.svg">svg<\/a>]<\/h1>/g;' {} \;
find $(DDTDIR) -type f -name '*.html' -exec sed -i 's/class="function"><a name="\([A-Za-z0-9_]*\)-\([0-9_]*\)">/class="function"><a name="\1-\2" href="ESCOFILENAME#\1-\2">/g;' {} \;
-for fn in *.html; do sed -i 's/ESCOFILENAME/'$${fn%.html}.erl.html'/g;' $${fn} ; done
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="EXP\([A-Za-z0-9_]*\).html#\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="\1.html#\2-\3"/g;' {} \;
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="PRI\([A-Za-z0-9_]*\).html#\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="\1.html#\2-\3"/g;' {} \;
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="APP\([A-Za-z0-9_]*\):\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="\1.html#\2-\3"/g;' {} \;
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="EXM\([A-Za-z0-9_]*\):\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="http:\/\/www.process-one.net\/docs\/exmpp\/devdoc\/trunk\/\1.html#\2-\3"/g;' {} \;
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="OTP\([A-Za-z0-9_]*\):\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="http:\/\/www.erlang.org\/doc\/man\/\1.html#\2-\3"/g;' {} \;
find $(DDTDIR) -type f -name '*.svg' -exec sed -i 's/xlink:href="OTP\([A-Za-z0-9_]*\)\/\([0-9_]*\)"/xlink:href="http:\/\/www.erlang.org\/doc\/man\/erlang.html#\1-\2"/g;' {} \;
endef
define devdoc-move
-rm *.dot
-mv *.html $(DEVDOCDIR)
-mv *.svg $(DEVDOCDIR)
endef
compile-devdoc: $(DEVDOC_BEAMS)
[ ! -f funrelg.beam ] || mv funrelg.beam $(DEVDOCDIR)
[ ! -f escobar_hilite.beam ] || mv escobar_hilite.beam $(DEVDOCDIR)
[ ! -f escobar_run.beam ] || mv escobar_run.beam $(DEVDOCDIR)
$(EDOCINDEX):
@ERL@ -noshell -run edoc_run application "'$(APPNAME)'" '"$(SRCDIR)"' \
'[{dir,"$(DDTDIR)"},{packages,false},{todo,true},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"overview.edoc"}]' -s init stop
sed -i 's/<title>The ejabberd application<\/title>/<title>ejabberd devdoc<\/title><link rel="shortcut icon" href="favicon.ico" type="image\/x-icon"\/>/g;' $(DDTDIR)/index.html
mv edoc-info $(DEVDOCDIR)
mv *.png $(DEVDOCDIR)
cp *.html $(DEVDOCDIR)
@ERL@ -noshell -pa $(DEVDOCDIR) -run escobar_run dir $(SRCDIR) $(SRCDIR) -s init stop
@ERL@ -noshell -pa $(DEVDOCDIR) -run funrelg dir $(SRCDIR) $(SRCDIR) -s init stop
$(devdoc-customize)
$(devdoc-move)
$(DEVDOCDIR)/%.erl.html: %.erl
@ERL@ -noshell -pa $(DEVDOCDIR) -run escobar_run file $< $(SRCDIR) -s init stop
$(DEVDOCDIR)/%.html: %.erl
@ERL@ -noshell -run edoc_run file $< \
'[{dir,"$(DDTDIR)"},{packages,false},{todo,true},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"overview.edoc"}]' -s init stop
$(DEVDOCDIR)/%.svg: %.erl
@ERL@ -noshell -pa $(DEVDOCDIR) -run funrelg file $< $(SRCDIR) -s init stop
clean-devdoc:
rm -f $(DEVDOCDIR)/edoc-info
rm -f $(DEVDOCDIR)/erlang.png
rm -f $(DEVDOCDIR)/*.beam
rm -f $(DEVDOCDIR)/*.dot
rm -f $(DEVDOCDIR)/*.html
rm -f $(DEVDOCDIR)/*.svg
LASTSVNREVCHANGELOG = 2075
changelog:
svn up -r $(LASTSVNREVCHANGELOG) ../ChangeLog
mv ../ChangeLog ../ChangeLog.old
svn2cl -r BASE:$(LASTSVNREVCHANGELOG) -o ../ChangeLog --group-by-day \
--separate-daylogs --break-before-msg --reparagraph ..
cat ../ChangeLog.old >> ../ChangeLog
rm ../ChangeLog.old
+21 -5
View File
@@ -52,6 +52,9 @@ release : build release_clean
mkdir $(SRC_DIR)\eldap
copy eldap\eldap.* $(SRC_DIR)\eldap
copy eldap\ELDAPv3.asn $(SRC_DIR)\eldap
mkdir $(SRC_DIR)\mod_irc
copy mod_irc\*.erl $(SRC_DIR)\mod_irc
copy mod_irc\*.c $(SRC_DIR)\mod_irc
mkdir $(SRC_DIR)\mod_muc
copy mod_muc\*.erl $(SRC_DIR)\mod_muc
mkdir $(SRC_DIR)\mod_pubsub
@@ -69,6 +72,9 @@ release : build release_clean
mkdir $(SRC_DIR)\tls
copy tls\*.erl $(SRC_DIR)\tls
copy tls\*.c $(SRC_DIR)\tls
mkdir $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.erl $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.c $(SRC_DIR)\ejabberd_zlib
mkdir $(SRC_DIR)\web
copy web\*.erl $(SRC_DIR)\web
mkdir $(SRC_DIR)\odbc
@@ -79,15 +85,17 @@ release : build release_clean
copy ..\doc\*.html $(DOC_DIR)
copy ..\doc\*.png $(DOC_DIR)
SOURCE =
OBJECT =
DLL =
SOURCE = expat_erl.c
OBJECT = expat_erl.o
DLL = expat_erl.dll
build : $(DLL) compile-beam all-recursive
all-recursive :
cd eldap
nmake -nologo -f Makefile.win32
cd ..\mod_irc
nmake -nologo -f Makefile.win32
cd ..\mod_muc
nmake -nologo -f Makefile.win32
cd ..\mod_pubsub
@@ -100,6 +108,8 @@ all-recursive :
nmake -nologo -f Makefile.win32
cd ..\tls
nmake -nologo -f Makefile.win32
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32
cd ..\web
nmake -nologo -f Makefile.win32
cd ..\odbc
@@ -117,6 +127,8 @@ CLEAN : clean-recursive clean-local
clean-local :
-@erase $(OBJECT)
-@erase $(DLL)
-@erase expat_erl.exp
-@erase expat_erl.lib
-@erase *.beam
-@erase XmppAddr.asn1db
-@erase XmppAddr.erl
@@ -125,6 +137,8 @@ clean-local :
clean-recursive :
cd eldap
nmake -nologo -f Makefile.win32 clean
cd ..\mod_irc
nmake -nologo -f Makefile.win32 clean
cd ..\mod_muc
nmake -nologo -f Makefile.win32 clean
cd ..\mod_pubsub
@@ -137,6 +151,8 @@ clean-recursive :
nmake -nologo -f Makefile.win32 clean
cd ..\tls
nmake -nologo -f Makefile.win32 clean
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32 clean
cd ..\web
nmake -nologo -f Makefile.win32 clean
cd ..\odbc
@@ -148,10 +164,10 @@ distclean : release_clean clean
-@erase Makefile.inc
CC=cl.exe
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include"
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT $(EXPAT_FLAG) -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(EXPAT_DIR)\source\lib"
LD=link.exe
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(EXPAT_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
$(DLL) : $(OBJECT)
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
-199
View File
@@ -1,199 +0,0 @@
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
PAM_CFLAGS=
PAM_LIBS=
if test x"$with_pam" != x; then
PAM_CFLAGS="-I$with_pam/include"
PAM_LIBS="-L$with_pam/lib"
fi
AC_CHECK_LIB(pam, pam_start,
[ PAM_LIBS="$PAM_LIBS -lpam"
pam_found=yes ],
[ pam_found=no ],
"$PAM_LIBS")
if test $pam_found = no; then
AC_MSG_ERROR([Could not find development files of PAM library. Install them or disable `pam' with: --disable-pam])
fi
pam_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PAM_CFLAGS"
pam_save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $PAM_CFLAGS"
AC_CHECK_HEADERS(security/pam_appl.h, , pam_found=no)
if test $pam_found = no; then
AC_MSG_ERROR([Could not find security/pam_appl.h. Install it or disable `pam' with: --disable-pam])
fi
CFLAGS="$pam_save_CFLAGS"
CPPFLAGS="$pam_save_CPPFLAGS"
AC_SUBST(PAM_CFLAGS)
AC_SUBST(PAM_LIBS)
fi
])
AC_DEFUN([AM_WITH_ERLANG],
[ AC_ARG_WITH(erlang,
[AC_HELP_STRING([--with-erlang=PREFIX], [path to erlc and erl])])
AC_PATH_TOOL(ERLC, erlc, , $with_erlang:$with_erlang/bin:$PATH)
AC_PATH_TOOL(ERL, erl, , $with_erlang:$with_erlang/bin:$PATH)
if test "z$ERLC" = "z" || test "z$ERL" = "z"; then
AC_MSG_ERROR([erlang not found])
fi
cat >>conftest.erl <<_EOF
-module(conftest).
-author('alexey@sevcom.net').
-export([[start/0]]).
start() ->
EIDirS = code:lib_dir("erl_interface") ++ "\n",
EILibS = libpath("erl_interface") ++ "\n",
EXMPPDir = code:lib_dir("exmpp"),
case EXMPPDir of
{error, bad_name} -> exit("exmpp not found");
_ -> ok
end,
EXMPPDirS = EXMPPDir ++ "\n",
RootDirS = code:root_dir() ++ "\n",
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ EXMPPDirS ++ RootDirS)),
halt().
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() ->
ArchStr = erlang:system_info(system_architecture),
case os:type() of
{win32, _} -> "windows";
{unix,UnixName} ->
Specs = string:tokens(ArchStr,"-"),
Cpu = case lists:nth(2,Specs) of
"pc" -> "x86";
_ -> hd(Specs)
end,
atom_to_list(UnixName) ++ "-" ++ Cpu;
_ -> "generic"
end.
%% Return arch-based library path or a default value if this directory
%% does not exist
libpath(App) ->
PrivDir = code:priv_dir(App),
ArchDir = archname(),
LibArchDir = filename:join([[PrivDir,"lib",ArchDir]]),
case file:list_dir(LibArchDir) of
%% Arch lib dir exists: We use it
{ok, _List} -> LibArchDir;
%% Arch lib dir does not exist: Return the default value
%% ({error, enoent}):
_Error -> code:lib_dir("erl_interface") ++ "/lib"
end.
_EOF
if ! $ERLC conftest.erl; then
AC_MSG_ERROR([could not compile sample program])
fi
if ! $ERL -s conftest -noshell; then
AC_MSG_ERROR([could not run sample program])
fi
if ! test -f conftest.out; then
AC_MSG_ERROR([erlang program was not properly executed, (conftest.out was not produced)])
fi
# First line
ERLANG_EI_DIR=`cat conftest.out | head -n 1`
# Second line
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
# Third line
ERLANG_SSLVER=`cat conftest.out | head -n 3 | tail -n 1`
# Fourth line
ERLANG_EXMPP=`cat conftest.out | head -n 4 | tail -n 1`
# End line
ERLANG_DIR=`cat conftest.out | tail -n 1`
ERLANG_CFLAGS="-I$ERLANG_EI_DIR/include -I$ERLANG_DIR/usr/include"
ERLANG_LIBS="-L$ERLANG_EI_LIB -lerl_interface -lei"
AC_SUBST(ERLANG_CFLAGS)
AC_SUBST(ERLANG_LIBS)
AC_SUBST(ERLANG_SSLVER)
AC_SUBST(ERLANG_EXMPP)
AC_SUBST(ERLC)
AC_SUBST(ERL)
])
AC_DEFUN([AC_MOD_ENABLE],
[
$1=
make_$1=
AC_MSG_CHECKING([whether build $1])
AC_ARG_ENABLE($1,
[AC_HELP_STRING([--enable-$1], [enable $1 (default: $2)])],
[mr_enable_$1="$enableval"],
[mr_enable_$1=$2])
if test "$mr_enable_$1" = "yes"; then
$1=$1
make_$1=$1/Makefile
fi
AC_MSG_RESULT($mr_enable_$1)
AC_SUBST($1)
AC_SUBST(make_$1)
])
dnl <openssl>
AC_DEFUN([AM_WITH_OPENSSL],
[ AC_ARG_WITH(openssl,
[AC_HELP_STRING([--with-openssl=PREFIX], [prefix where OPENSSL is installed])])
unset SSL_LIBS;
unset SSL_CFLAGS;
have_openssl=no
if test x"$tls" != x; then
for ssl_prefix in $withval /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr; do
printf "looking for openssl in $ssl_prefix...\n"
SSL_CFLAGS="-I$ssl_prefix/include"
SSL_LIBS="-L$ssl_prefix/lib -lcrypto"
AC_CHECK_LIB(ssl, SSL_new, [ have_openssl=yes ], [ have_openssl=no ], [ $SSL_LIBS $SSL_CFLAGS ])
if test x"$have_openssl" = xyes; then
save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="-I$ssl_prefix/include $CPPFLAGS"
AC_CHECK_HEADERS(openssl/ssl.h, have_openssl_h=yes)
CPPFLAGS=$save_CPPFLAGS
if test x"$have_openssl_h" = xyes; then
have_openssl=yes
printf "openssl found in $ssl_prefix\n";
SSL_LIBS="-L$ssl_prefix/lib -lssl -lcrypto"
CPPFLAGS="-I$ssl_prefix/include $CPPFLAGS"
SSL_CFLAGS="-DHAVE_SSL"
break
fi
else
# Clear this from the autoconf cache, so in the next pass of
# this loop with different -L arguments, it will test again.
unset ac_cv_lib_ssl_SSL_new
fi
done
if test x${have_openssl} != xyes; then
AC_MSG_ERROR([Could not find development files of OpenSSL library. Install them or disable `tls' with: --disable-tls])
fi
AC_SUBST(SSL_LIBS)
AC_SUBST(SSL_CFLAGS)
fi
])
dnl <openssl/>
+21 -119
View File
@@ -5,7 +5,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,45 +32,13 @@
add/3,
add_list/3,
match_rule/3,
for_host/1,
% for debugging only
match_acl/3]).
-include("ejabberd.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
%% @type aclspec() = all | JID_Exact | JID_Regexp | JID_Glob | Shared_Group
%% JID_Exact = {user, U} | {user, U, S} | {server, S} | {resource, R}
%% U = string()
%% S = string()
%% R = string()
%% JID_Regexp = {user_regexp, UR} | {user_regexp, UR, S} | {server_regexp, SR} | {resource_regexp, RR} | {node_regexp, UR, SR}
%% UR = string()
%% SR = string()
%% RR = string()
%% JID_Glob = {user_glob, UG} | {user_glob, UG, S} | {server_glob, SG} | {resource_glob, RG} | {node_glob, UG, SG}
%% UG = string()
%% SG = string()
%% RG = string()
%% Shared_Group = {shared_group, G} | {shared_group, G, H}
%% G = string()
%% H = string().
%% @type acl() = {acl, ACLName, ACLSpec}
%% ACLName = atom()
%% ACLSpec = aclspec().
%% Record in its Ejabberd-configuration-file variant.
%% @type storedacl() = {acl, {ACLName, Host}, ACLSpec}
%% ACLName = atom()
%% Host = global | string()
%% ACLSpec = aclspec().
%% Record in its Mnesia-table-record variant.
-record(acl, {aclname, aclspec}).
%% @spec () -> ok
start() ->
mnesia:create_table(acl,
[{disc_copies, [node()]},
@@ -79,20 +47,9 @@ start() ->
mnesia:add_table_copy(acl, node(), ram_copies),
ok.
%% @spec (Host, ACLName, ACLSpec) -> storedacl()
%% Host = global | string()
%% ACLName = atom()
%% ACLSpec = aclspec()
to_record(Host, ACLName, ACLSpec) ->
#acl{aclname = {ACLName, Host}, aclspec = normalize_spec(ACLSpec)}.
%% @spec (Host, ACLName, ACLSpec) -> {atomic, ok} | {aborted, Reason}
%% Host = global | string()
%% ACLName = atom()
%% ACLSpec = all | none | aclspec()
%% Reason = term()
add(Host, ACLName, ACLSpec) ->
F = fun() ->
mnesia:write(#acl{aclname = {ACLName, Host},
@@ -100,11 +57,6 @@ add(Host, ACLName, ACLSpec) ->
end,
mnesia:transaction(F).
%% @spec (Host, ACLs, Clear) -> ok | false
%% Host = global | string()
%% ACLs = [acl()]
%% Clear = bool()
add_list(Host, ACLs, Clear) ->
F = fun() ->
if
@@ -134,17 +86,8 @@ add_list(Host, ACLs, Clear) ->
false
end.
%% @spec (String) -> Prepd_String
%% String = string()
%% Prepd_String = string()
normalize(String) ->
exmpp_stringprep:nodeprep(String).
%% @spec (ACLSpec) -> Normalized_ACLSpec
%% ACLSpec = all | none | aclspec()
%% Normalized_ACLSpec = aclspec()
normalize(A) ->
jlib:nodeprep(A).
normalize_spec({A, B}) ->
{A, normalize(B)};
normalize_spec({A, B, C}) ->
@@ -156,12 +99,6 @@ normalize_spec(none) ->
%% @spec (Host, Rule, JID) -> Access
%% Host = global | string()
%% Rule = all | none | atom()
%% JID = exmpp_jid:jid()
%% Access = allow | deny | atom()
match_rule(global, Rule, JID) ->
case Rule of
all -> allow;
@@ -206,37 +143,23 @@ match_rule(Host, Rule, JID) ->
end
end.
%% @spec (ACLs, JID, Host) -> Access
%% ACLs = [{Access, ACLName}]
%% Access = deny | atom()
%% ACLName = atom()
%% JID = exmpp_jid:jid()
%% Host = string()
match_acls([], _, _Host) ->
deny;
match_acls([{Access, ACLName} | ACLs], JID, Host) ->
case match_acl(ACLName, JID, Host) of
match_acls([{Access, ACL} | ACLs], JID, Host) ->
case match_acl(ACL, JID, Host) of
true ->
Access;
_ ->
match_acls(ACLs, JID, Host)
end.
%% @spec (ACLName, JID, Host) -> bool()
%% ACLName = all | none | atom()
%% JID = exmpp_jid:jid()
%% Host = string()
match_acl(ACLName, JID, Host) ->
case ACLName of
match_acl(ACL, JID, Host) ->
case ACL of
all -> true;
none -> false;
_ ->
User = exmpp_jid:prep_node_as_list(JID),
Server = exmpp_jid:prep_domain_as_list(JID),
Resource = exmpp_jid:prep_resource_as_list(JID),
lists:any(fun(#acl{aclname=Name, aclspec = Spec}) ->
{User, Server, Resource} = jlib:jid_tolower(JID),
lists:any(fun(#acl{aclspec = Spec}) ->
case Spec of
all ->
true;
@@ -245,21 +168,17 @@ match_acl(ACLName, JID, Host) ->
andalso
((Host == Server) orelse
((Host == global) andalso
?IS_MY_HOST(Server)));
lists:member(Server, ?MYHOSTS)));
{user, U, S} ->
(U == User) andalso (S == Server);
{server, S} ->
S == Server;
{resource, R} ->
R == Resource;
{user_regexp, UR} when is_tuple(Name),
element(2, Name) =:= global ->
?IS_MY_HOST(Server)
andalso is_regexp_match(User, UR);
{user_regexp, UR} ->
((Host == Server) orelse
((Host == global) andalso
?IS_MY_HOST(Server)))
lists:member(Server, ?MYHOSTS)))
andalso is_regexp_match(User, UR);
{shared_group, G} ->
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
@@ -278,7 +197,7 @@ match_acl(ACLName, JID, Host) ->
{user_glob, UR} ->
((Host == Server) orelse
((Host == global) andalso
?IS_MY_HOST(Server)))
lists:member(Server, ?MYHOSTS)))
andalso
is_glob_match(User, UR);
{user_glob, UR, S} ->
@@ -299,41 +218,24 @@ match_acl(ACLName, JID, Host) ->
false
end
end,
ets:lookup(acl, {ACLName, global}) ++
ets:lookup(acl, {ACLName, Host}))
ets:lookup(acl, {ACL, global}) ++
ets:lookup(acl, {ACL, Host}))
end.
%% @spec (String, RegExp) -> bool()
%% String = string() | undefined
%% RegExp = string()
is_regexp_match(undefined, _RegExp) ->
false;
is_regexp_match(String, RegExp) ->
try re:run(String, RegExp, [{capture, none}]) of
case regexp:first_match(String, RegExp) of
nomatch ->
false;
match ->
true
catch
_:ErrDesc ->
{match, _, _} ->
true;
{error, ErrDesc} ->
?ERROR_MSG(
"Wrong regexp ~p in ACL:~n~p",
[RegExp, ErrDesc]),
"Wrong regexp ~p in ACL: ~p",
[RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
false
end.
%% @spec (String, Glob) -> bool()
%% String = string() | undefined
%% Glob = string()
is_glob_match(String, Glob) ->
is_regexp_match(String, xmerl_regexp:sh_to_awk(Glob)).
is_regexp_match(String, regexp:sh_to_awk(Glob)).
for_host(Host) ->
mnesia:select(acl,
ets:fun2ms(fun (#acl{aclname = {_ACLName, H}})
when H =:= Host ->
object()
end)).
+349
View File
@@ -0,0 +1,349 @@
AC_DEFUN(AM_WITH_EXPAT,
[ AC_ARG_WITH(expat,
[AC_HELP_STRING([--with-expat=PREFIX], [prefix where EXPAT is installed])])
EXPAT_CFLAGS=
EXPAT_LIBS=
if test x"$with_expat" != x; then
EXPAT_CFLAGS="-I$with_expat/include"
EXPAT_LIBS="-L$with_expat/lib"
fi
AC_CHECK_LIB(expat, XML_ParserCreate,
[ EXPAT_LIBS="$EXPAT_LIBS -lexpat"
expat_found=yes ],
[ expat_found=no ],
"$EXPAT_LIBS")
if test $expat_found = no; then
AC_MSG_ERROR([Could not find development files of Expat library])
fi
expat_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $EXPAT_CFLAGS"
expat_save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $EXPAT_CFLAGS"
AC_CHECK_HEADERS(expat.h, , expat_found=no)
if test $expat_found = no; then
AC_MSG_ERROR([Could not find expat.h])
fi
CFLAGS="$expat_save_CFLAGS"
CPPFLAGS="$expat_save_CPPFLAGS"
AC_SUBST(EXPAT_CFLAGS)
AC_SUBST(EXPAT_LIBS)
])
AC_DEFUN(AM_WITH_ZLIB,
[ AC_ARG_WITH(zlib,
[AC_HELP_STRING([--with-zlib=PREFIX], [prefix where zlib is installed])])
if test x"$ejabberd_zlib" != x; then
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
AC_CHECK_LIB(z, gzgets,
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes ],
[ zlib_found=no ],
"$ZLIB_LIBS")
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find development files of zlib library. Install them or disable `ejabberd_zlib' with: --disable-ejabberd_zlib])
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
zlib_save_CPPFLAGS="$CFLAGS"
CPPFLAGS="$CPPFLAGS $ZLIB_CFLAGS"
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find zlib.h. Install it or disable `ejabberd_zlib' with: --disable-ejabberd_zlib])
fi
CFLAGS="$zlib_save_CFLAGS"
CPPFLAGS="$zlib_save_CPPFLAGS"
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
fi
])
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
PAM_CFLAGS=
PAM_LIBS=
if test x"$with_pam" != x; then
PAM_CFLAGS="-I$with_pam/include"
PAM_LIBS="-L$with_pam/lib"
fi
AC_CHECK_LIB(pam, pam_start,
[ PAM_LIBS="$PAM_LIBS -lpam"
pam_found=yes ],
[ pam_found=no ],
"$PAM_LIBS")
if test $pam_found = no; then
AC_MSG_ERROR([Could not find development files of PAM library. Install them or disable `pam' with: --disable-pam])
fi
pam_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PAM_CFLAGS"
pam_save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $PAM_CFLAGS"
AC_CHECK_HEADERS(security/pam_appl.h, , pam_found=no)
if test $pam_found = no; then
AC_MSG_ERROR([Could not find security/pam_appl.h. Install it or disable `pam' with: --disable-pam])
fi
CFLAGS="$pam_save_CFLAGS"
CPPFLAGS="$pam_save_CPPFLAGS"
AC_SUBST(PAM_CFLAGS)
AC_SUBST(PAM_LIBS)
fi
])
AC_DEFUN(AM_WITH_ERLANG,
[ AC_ARG_WITH(erlang,
[AC_HELP_STRING([--with-erlang=PREFIX], [path to erlc and erl])])
AC_PATH_TOOL(ERLC, erlc, , $with_erlang:$with_erlang/bin:$PATH)
AC_PATH_TOOL(ERL, erl, , $with_erlang:$with_erlang/bin:$PATH)
if test "z$ERLC" = "z" || test "z$ERL" = "z"; then
AC_MSG_ERROR([erlang not found])
fi
cat >>conftest.erl <<_EOF
-module(conftest).
-author('alexey@sevcom.net').
-export([[start/0]]).
-include_lib("ssl/include/ssl_pkix.hrl").
start() ->
EIDirS = code:lib_dir("erl_interface") ++ "\n",
EILibS = libpath("erl_interface") ++ "\n",
RootDirS = code:root_dir() ++ "\n",
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
halt().
-[ifdef]('id-pkix').
ssldef() -> "-DSSL39\n".
-else.
ssldef() -> "\n".
-endif.
%% return physical architecture based on OS/Processor
archname() ->
ArchStr = erlang:system_info(system_architecture),
case os:type() of
{win32, _} -> "windows";
{unix,UnixName} ->
Specs = string:tokens(ArchStr,"-"),
Cpu = case lists:nth(2,Specs) of
"pc" -> "x86";
_ -> hd(Specs)
end,
atom_to_list(UnixName) ++ "-" ++ Cpu;
_ -> "generic"
end.
%% Return arch-based library path or a default value if this directory
%% does not exist
libpath(App) ->
PrivDir = code:priv_dir(App),
ArchDir = archname(),
LibArchDir = filename:join([[PrivDir,"lib",ArchDir]]),
case file:list_dir(LibArchDir) of
%% Arch lib dir exists: We use it
{ok, _List} -> LibArchDir;
%% Arch lib dir does not exist: Return the default value
%% ({error, enoent}):
_Error -> code:lib_dir("erl_interface") ++ "/lib"
end.
_EOF
if ! $ERLC conftest.erl; then
AC_MSG_ERROR([could not compile sample program])
fi
if ! $ERL -s conftest -noshell; then
AC_MSG_ERROR([could not run sample program])
fi
if ! test -f conftest.out; then
AC_MSG_ERROR([erlang program was not properly executed, (conftest.out was not produced)])
fi
# First line
ERLANG_EI_DIR=`cat conftest.out | head -n 1`
# 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`
# End line
ERLANG_DIR=`cat conftest.out | tail -n 1`
ERLANG_CFLAGS="-I$ERLANG_EI_DIR/include -I$ERLANG_DIR/usr/include"
ERLANG_LIBS="-L$ERLANG_EI_LIB -lerl_interface -lei"
AC_SUBST(ERLANG_CFLAGS)
AC_SUBST(ERLANG_LIBS)
AC_SUBST(ERLANG_SSL39)
AC_SUBST(ERLC)
AC_SUBST(ERL)
])
AC_DEFUN(AC_MOD_ENABLE,
[
$1=
make_$1=
AC_MSG_CHECKING([whether build $1])
AC_ARG_ENABLE($1,
[AC_HELP_STRING([--enable-$1], [enable $1 (default: $2)])],
[mr_enable_$1="$enableval"],
[mr_enable_$1=$2])
if test "$mr_enable_$1" = "yes"; then
$1=$1
make_$1=$1/Makefile
fi
AC_MSG_RESULT($mr_enable_$1)
AC_SUBST($1)
AC_SUBST(make_$1)
])
dnl From Bruno Haible.
AC_DEFUN([AM_ICONV],
[
dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
dnl those with the standalone portable GNU libiconv installed).
AC_ARG_WITH([libiconv-prefix],
[AC_HELP_STRING([--with-libiconv-prefix=PREFIX], [prefix where libiconv is installed])], [
for dir in `echo "$withval" | tr : ' '`; do
if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi
if test -d $dir/include; then CFLAGS="$CFLAGS -I$dir/include"; fi
if test -d $dir/lib; then LDFLAGS="$LDFLAGS -L$dir/lib"; fi
done
])
AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
am_cv_func_iconv="no, consider installing GNU libiconv"
am_cv_lib_iconv=no
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("","");
iconv(cd,NULL,NULL,NULL,NULL);
iconv_close(cd);],
am_cv_func_iconv=yes)
if test "$am_cv_func_iconv" != yes; then
am_save_LIBS="$LIBS"
LIBS="$LIBS -liconv"
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("","");
iconv(cd,NULL,NULL,NULL,NULL);
iconv_close(cd);],
am_cv_lib_iconv=yes
am_cv_func_iconv=yes)
LIBS="$am_save_LIBS"
fi
dnl trying /usr/local
if test "$am_cv_func_iconv" != yes; then
am_save_LIBS="$LIBS"
am_save_CFLAGS="$CFLAGS"
am_save_LDFLAGS="$LDFLAGS"
LIBS="$LIBS -liconv"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
CFLAGS="$CFLAGS -I/usr/local/include"
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("","");
iconv(cd,NULL,NULL,NULL,NULL);
iconv_close(cd);],
am_cv_lib_iconv=yes
am_cv_func_iconv=yes
CPPFLAGS="$CPPFLAGS -I/usr/local/include",
LDFLAGS="$am_save_LDFLAGS"
CFLAGS="$am_save_CFLAGS")
LIBS="$am_save_LIBS"
fi
])
if test "$am_cv_func_iconv" = yes; then
AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
AC_MSG_CHECKING([for iconv declaration])
AC_CACHE_VAL(am_cv_proto_iconv, [
AC_TRY_COMPILE([
#include <stdlib.h>
#include <iconv.h>
extern
#ifdef __cplusplus
"C"
#endif
#if defined(__STDC__) || defined(__cplusplus)
size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
#else
size_t iconv();
#endif
], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
AC_MSG_RESULT([$]{ac_t:-
}[$]am_cv_proto_iconv)
AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
[Define as const if the declaration of iconv() needs const.])
fi
LIBICONV=
if test "$am_cv_lib_iconv" = yes; then
LIBICONV="-liconv"
fi
AC_SUBST(LIBICONV)
])
dnl <openssl>
AC_DEFUN(AM_WITH_OPENSSL,
[ AC_ARG_WITH(openssl,
[AC_HELP_STRING([--with-openssl=PREFIX], [prefix where OPENSSL is installed])])
unset SSL_LIBS;
unset SSL_CFLAGS;
have_openssl=no
if test x"$tls" != x; then
for ssl_prefix in $withval /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr; do
printf "looking for openssl in $ssl_prefix...\n"
SSL_CFLAGS="-I$ssl_prefix/include"
SSL_LIBS="-L$ssl_prefix/lib -lcrypto"
AC_CHECK_LIB(ssl, SSL_new, [ have_openssl=yes ], [ have_openssl=no ], [ $SSL_LIBS $SSL_CFLAGS ])
if test x"$have_openssl" = xyes; then
save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="-I$ssl_prefix/include $CPPFLAGS"
AC_CHECK_HEADERS(openssl/ssl.h, have_openssl_h=yes)
CPPFLAGS=$save_CPPFLAGS
if test x"$have_openssl_h" = xyes; then
have_openssl=yes
printf "openssl found in $ssl_prefix\n";
SSL_LIBS="-L$ssl_prefix/lib -lssl -lcrypto"
CPPFLAGS="-I$ssl_prefix/include $CPPFLAGS"
SSL_CFLAGS="-DHAVE_SSL"
break
fi
else
# Clear this from the autoconf cache, so in the next pass of
# this loop with different -L arguments, it will test again.
unset ac_cv_lib_ssl_SSL_new
fi
done
if test x${have_openssl} != xyes; then
AC_MSG_ERROR([Could not find development files of OpenSSL library. Install them or disable `tls' with: --disable-tls])
fi
AC_SUBST(SSL_LIBS)
AC_SUBST(SSL_CFLAGS)
fi
])
dnl <openssl/>
+43 -47
View File
@@ -1,11 +1,11 @@
%%%----------------------------------------------------------------------
%%% File : adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,52 +31,47 @@
produce_response/2,
produce_response/1]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
%% Parse an ad-hoc request. Return either an adhoc_request record or
%% an {error, ErrorType} tuple.
parse_request(#iq{type = Type, ns = NS, payload = SubEl, lang = Lang}) ->
try
case {Type, NS} of
{set, ?NS_ADHOC} ->
?DEBUG("entering parse_request...", []),
Node = exmpp_xml:get_attribute_as_list(SubEl, <<"node">>, ""),
SessionID = exmpp_xml:get_attribute_as_list(SubEl, <<"sessionid">>, ""),
Action = exmpp_xml:get_attribute_as_list(SubEl, <<"action">>, ""),
XData = find_xdata_el(SubEl),
AllEls = exmpp_xml:get_child_elements(SubEl),
Others = case XData of
false ->
AllEls;
_ ->
lists:delete(XData, AllEls)
end,
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
Node = xml:get_tag_attr_s("node", SubEl),
SessionID = xml:get_tag_attr_s("sessionid", SubEl),
Action = xml:get_tag_attr_s("action", SubEl),
XData = find_xdata_el(SubEl),
{xmlelement, _, _, AllEls} = SubEl,
if XData ->
Others = lists:delete(XData, AllEls);
true ->
Others = AllEls
end,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData,
others = Others};
_ ->
{error, 'bad-request'}
end
catch
_ ->
{error, 'bad-request'}
end.
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData,
others = Others};
parse_request(_) ->
{error, ?ERR_BAD_REQUEST}.
%% Borrowed from mod_vcard.erl
find_xdata_el(#xmlel{children = SubEls}) ->
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
find_xdata_el1([]) ->
false;
find_xdata_el1([#xmlel{ns = ?NS_DATA_FORMS} = El | _Els]) ->
El;
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_XDATA ->
{xmlelement, Name, Attrs, SubEls};
_ ->
find_xdata_el1(Els)
end;
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).
@@ -114,19 +109,20 @@ produce_response(#adhoc_response{lang = _Lang,
"" ->
ActionsElAttrs = [];
_ ->
ActionsElAttrs = [?XMLATTR(<<"execute">>, DefaultAction)]
ActionsElAttrs = [{"execute", DefaultAction}]
end,
ActionsEls = [#xmlel{ns = ?NS_ADHOC, name = 'actions', attrs =
ActionsElAttrs, children =
[#xmlel{ns = ?NS_ADHOC, name = Action} || Action <- Actions]}]
ActionsEls = [{xmlelement, "actions",
ActionsElAttrs,
[{xmlelement, Action, [], []} || Action <- Actions]}]
end,
NotesEls = lists:map(fun({Type, Text}) ->
#xmlel{ns = ?NS_ADHOC, name = 'note', attrs =
[?XMLATTR(<<"type">>, Type)],
children = [#xmlcdata{cdata = list_to_binary(Text)}]}
{xmlelement, "note",
[{"type", Type}],
[{xmlcdata, Text}]}
end, Notes),
#xmlel{ns = ?NS_ADHOC, name = 'command', attrs =
[?XMLATTR(<<"sessionid">>, SessionID),
?XMLATTR(<<"node">>, Node),
?XMLATTR(<<"status">>, Status)], children =
{xmlelement, "command",
[{"xmlns", ?NS_COMMANDS},
{"sessionid", SessionID},
{"node", Node},
{"status", atom_to_list(Status)}],
ActionsEls ++ NotesEls ++ Elements}.
+1 -1
View File
@@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
-604
View File
@@ -1,604 +0,0 @@
%%%-------------------------------------------------------------------
%%% 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).
Vendored Executable
+6215
View File
File diff suppressed because it is too large Load Diff
+15 -24
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.53)
AC_INIT(ejabberd, m4_esyscmd([grep -o -E "\{vsn,.\".*\"\}" ejabberd.app | cut -d \" -f 2 | tr -d '\n']), [ejabberd@process-one.net], [ejabberd])
AC_INIT(ejabberd.erl, version, [ejabberd@process-one.net], [ejabberd])
# Checks for programs.
AC_PROG_CC
@@ -14,6 +14,10 @@ fi
#locating erlang
AM_WITH_ERLANG
#locating iconv
AM_ICONV
#locating libexpat
AM_WITH_EXPAT
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -28,6 +32,7 @@ AC_PREFIX_DEFAULT(/)
AC_FUNC_MALLOC
AC_HEADER_STDC
AC_MOD_ENABLE(mod_irc, yes)
AC_MOD_ENABLE(mod_muc, yes)
AC_MOD_ENABLE(mod_proxy65, yes)
AC_MOD_ENABLE(mod_pubsub, yes)
@@ -36,6 +41,10 @@ AC_MOD_ENABLE(odbc, no)
AC_MOD_ENABLE(tls, yes)
AC_MOD_ENABLE(web, yes)
AC_MOD_ENABLE(ejabberd_zlib, yes)
#locating zlib
AM_WITH_ZLIB
AC_MOD_ENABLE(pam, no)
#locating PAM
AM_WITH_PAM
@@ -58,15 +67,6 @@ AC_ARG_ENABLE(roster_gateway_workaround,
esac],[roster_gateway_workaround=false])
AC_SUBST(roster_gateway_workaround)
AC_ARG_ENABLE(flash_hack,
[AC_HELP_STRING([--enable-flash-hack], [support Adobe Flash client XML (default: no)])],
[case "${enableval}" in
yes) flash_hack=true ;;
no) flash_hack=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-flash-hack) ;;
esac],[flash_hack=false])
AC_SUBST(flash_hack)
AC_ARG_ENABLE(mssql,
[AC_HELP_STRING([--enable-mssql], [use Microsoft SQL Server database (default: no, requires --enable-odbc)])],
[case "${enableval}" in
@@ -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-transient_supervisors) ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;;
esac],[transient_supervisors=true])
AC_SUBST(transient_supervisors)
@@ -95,15 +95,18 @@ esac],[full_xml=false])
AC_SUBST(full_xml)
AC_CONFIG_FILES([Makefile
$make_mod_irc
$make_mod_muc
$make_mod_pubsub
$make_mod_proxy65
$make_eldap
$make_pam
$make_web
stringprep/Makefile
stun/Makefile
$make_tls
$make_odbc])
$make_odbc
$make_ejabberd_zlib])
#openssl
AM_WITH_OPENSSL
# If ssl is kerberized it need krb5.h
@@ -136,9 +139,6 @@ 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)
@@ -167,12 +167,3 @@ LD_SHARED="$LD_SHARED $CBIT"
echo "CBIT is set to '$CBIT'"
AC_OUTPUT
echo
echo "********************** WARNING ! ************************"
echo "* ejabberd master is NOT ready for real usage yet, *"
echo "* because it is still in heavy development. *"
echo "* Don't use ejabberd master, it is still alpha code! *"
echo "* Please use ejabberd 2.1.x branch instead. *"
echo "*********************************************************"
echo
+21 -5
View File
@@ -1,11 +1,11 @@
%%%----------------------------------------------------------------------
%%% File : configure.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose :
%%% Purpose :
%%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -42,24 +42,33 @@ start() ->
end,
case Static of
true ->
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\StaticLibs\\libexpatMT.lib\n",
ExpatFlag = "EXPAT_FLAG = -DXML_STATIC\n",
IconvDir = "ICONV_DIR = c:\\sdk\\GnuWin32\n",
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\libiconv.lib\n",
ZlibDir = "ZLIB_DIR = c:\\sdk\\GnuWin32\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n";
false ->
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\Libs\\libexpat.lib\n",
ExpatFlag = "",
IconvDir = "ICONV_DIR = c:\\sdk\\GnuWin32\n",
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\libiconv.lib\n",
ZlibDir = "ZLIB_DIR = c:\\sdk\\GnuWin32\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n"
end,
EVersion = "ERLANG_VERSION = " ++ erlang:system_info(version) ++ "\n",
EIDirS = "EI_DIR = " ++ code:lib_dir(erl_interface) ++ "\n",
EIDirS = "EI_DIR = " ++ code:lib_dir("erl_interface") ++ "\n",
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
%% Load the ejabberd application description so that ?VERSION can read the vsn key
application:load(ejabberd),
Version = "EJABBERD_VERSION = " ++ ?VERSION ++ "\n",
ExpatDir = "EXPAT_DIR = c:\\sdk\\Expat-2.0.0\n",
OpenSSLDir = "OPENSSL_DIR = c:\\sdk\\OpenSSL\n",
DBType = "DBTYPE = generic\n", %% 'generic' or 'mssql'
SSLDir = "SSLDIR = " ++ code:lib_dir(ssl) ++ "\n",
StdLibDir = "STDLIBDIR = " ++ code:lib_dir(stdlib) ++ "\n",
SSLDir = "SSLDIR = " ++ code:lib_dir("ssl") ++ "\n",
StdLibDir = "STDLIBDIR = " ++ code:lib_dir("stdlib") ++ "\n",
file:write_file("Makefile.inc",
list_to_binary(EVersion ++
@@ -70,6 +79,13 @@ start() ->
StdLibDir ++
OpenSSLDir ++
DBType ++
ExpatDir ++
ExpatLib ++
ExpatFlag ++
IconvDir ++
IconvLib ++
ZlibDir ++
ZlibLib)),
halt().
+64 -182
View File
@@ -5,7 +5,7 @@
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,221 +30,117 @@
-export([start/0,
register_mechanism/3,
listmech/1,
server_new/8,
server_new/7,
server_start/3,
server_step/2]).
-include("cyrsasl.hrl").
-include("ejabberd.hrl").
%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain}
%% Mechanism = string()
%% Module = atom()
%% Require_Plain = bool().
%% Registry entry of a supported SASL mechanism.
-record(sasl_mechanism, {mechanism, module, password_type}).
%% @type saslstate() = {sasl_state, Service, Myname, Realm, GetPassword, CheckPassword, CheckPasswordDigest, Mech_Mod, Mech_State}
%% Service = string()
%% Myname = string()
%% Realm = string()
%% GetPassword = function()
%% CheckPassword = function()
%% CheckPasswordDigest = any()
%% Mech_Mod = atom()
%% Mech_State = term().
%% State of this process.
-record(sasl_state, {service, myname,
mech_mod, mech_state, params}).
-record(sasl_mechanism, {mechanism, module, require_plain_password}).
-record(sasl_state, {service, myname, realm,
get_password, check_password, check_password_digest,
mech_mod, mech_state}).
-export([behaviour_info/1]).
%% @hidden
behaviour_info(callbacks) ->
[{mech_new, 1}, {mech_step, 2}];
[{mech_new, 4}, {mech_step, 2}];
behaviour_info(_Other) ->
undefined.
%% @spec () -> ok
start() ->
ets:new(sasl_mechanism, [named_table,
public,
{keypos, #sasl_mechanism.mechanism}]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
cyrsasl_scram:start([]),
cyrsasl_anonymous:start([]),
maybe_try_start_gssapi(),
ok.
maybe_try_start_gssapi() ->
case os:getenv("KRB5_KTNAME") of
false ->
ok;
_String ->
try_start_gssapi()
end.
try_start_gssapi() ->
case code:load_file(esasl) of
{module, _Module} ->
cyrsasl_gssapi:start([]);
{error, What} ->
?ERROR_MSG("Support for GSSAPI not started because esasl.beam was not found: ~p", [What])
end.
%% @spec (Mechanism, Module, Require_Plain) -> true
%% Mechanism = string()
%% Module = atom()
%% Require_Plain = bool()
register_mechanism(Mechanism, Module, PasswordType) ->
register_mechanism(Mechanism, Module, RequirePlainPassword) ->
ets:insert(sasl_mechanism,
#sasl_mechanism{mechanism = Mechanism,
module = Module,
password_type = PasswordType}).
require_plain_password = RequirePlainPassword}).
% TODO use callbacks
%-include("ejabberd.hrl").
%-include("jlib.hrl").
%check_authzid(_State, Props) ->
% AuthzId = xml:get_attr_s(authzid, Props),
% case jlib:string_to_jid(AuthzId) of
% error ->
% {error, "invalid-authzid"};
% JID ->
% LUser = jlib:nodeprep(xml:get_attr_s(username, Props)),
% {U, S, R} = jlib:short_prepd_jid(JID),
% case R of
% "" ->
% {error, "invalid-authzid"};
% _ ->
% case {LUser, ?MYNAME} of
% {U, S} ->
% ok;
% _ ->
% {error, "invalid-authzid"}
% end
% end
% end.
%% @spec (State, Props) -> ok | {error, 'not-authorized'}
%% State = saslstate()
%% Props = [{Key, Value}]
%% Key = atom()
%% Value = string()
%%% TODO: use callbacks
%%-include("ejabberd.hrl").
%%-include("jlib.hrl").
%%check_authzid(_State, Props) ->
%% AuthzId = xml:get_attr_s(authzid, Props),
%% case jlib:string_to_jid(AuthzId) of
%% error ->
%% {error, "invalid-authzid"};
%% JID ->
%% LUser = jlib:nodeprep(xml:get_attr_s(username, Props)),
%% {U, S, R} = jlib:jid_tolower(JID),
%% case R of
%% "" ->
%% {error, "invalid-authzid"};
%% _ ->
%% case {LUser, ?MYNAME} of
%% {U, S} ->
%% ok;
%% _ ->
%% {error, "invalid-authzid"}
%% end
%% end
%% end.
check_credentials(_State, Props) ->
case proplists:get_value(username, Props) of
undefined ->
{error, 'not-authorized'};
User ->
case exmpp_stringprep:is_node(User) of
false -> {error, 'not-authorized'};
true -> ok
end
User = xml:get_attr_s(username, Props),
case jlib:nodeprep(User) of
error ->
{error, "not-authorized"};
"" ->
{error, "not-authorized"};
_LUser ->
ok
end.
%% @spec (Host) -> [Mechanism]
%% Host = string()
%% Mechanism = string()
listmech(Host) ->
RequirePlainPassword = ejabberd_auth:plain_password_required(Host),
Mechs = ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1',
password_type = '$2',
require_plain_password = '$2',
_ = '_'},
case catch ejabberd_auth:store_type(Host) of
external ->
[{'==', '$2', plain}];
scram ->
[{'/=', '$2', digest}];
{'EXIT',{undef,[{Module,store_type,[]} | _]}} ->
?WARNING_MSG("~p doesn't implement the function store_type/0", [Module]),
[];
_Else ->
[]
if
RequirePlainPassword ->
[{'==', '$2', false}];
true ->
[]
end,
['$1']}]),
filter_anonymous(Host, Mechs).
%% @spec (Service, ServerFQDN, UserRealm, SecFlags, GetPassword, CheckPassword, CheckPasswordDigest, Socket) -> saslstate()
%% Service = string()
%% ServerFQDN = string()
%% UserRealm = string()
%% SecFlags = [term()]
%% GetPassword = function()
%% CheckPassword = function()
server_new(Service, ServerFQDN, UserRealm, _SecFlags,
GetPassword, CheckPassword, CheckPasswordDigest, Socket) ->
Params = #sasl_params{
host = ServerFQDN,
realm = UserRealm,
get_password = GetPassword,
check_password = CheckPassword,
check_password_digest= CheckPasswordDigest,
socket = Socket
},
GetPassword, CheckPassword, CheckPasswordDigest) ->
#sasl_state{service = Service,
myname = ServerFQDN,
params = Params}.
%% @spec (State, Mech, ClientIn) -> Ok | Continue | Error
%% State = saslstate()
%% Mech = string()
%% ClientIn = string()
%% Ok = {ok, Props}
%% Props = [Prop]
%% Prop = [{Key, Value}]
%% Key = atom()
%% Value = string()
%% Continue = {continue, ServerOut, New_State}
%% ServerOut = string()
%% New_State = saslstate()
%% Error = {error, Reason} | {error, Reason, Username}
%% Reason = term()
%% Username = string()
realm = UserRealm,
get_password = GetPassword,
check_password = CheckPassword,
check_password_digest= CheckPasswordDigest}.
server_start(State, Mech, ClientIn) ->
case lists:member(Mech, listmech(State#sasl_state.myname)) of
true ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
{ok, MechState} =
Module:mech_new(State#sasl_state.params),
{ok, MechState} = Module:mech_new(
State#sasl_state.myname,
State#sasl_state.get_password,
State#sasl_state.check_password,
State#sasl_state.check_password_digest),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);
_ ->
{error, 'invalid-mechanism'}
{error, "no-mechanism"}
end;
false ->
{error, 'invalid-mechanism'}
{error, "no-mechanism"}
end.
%% @spec (State, ClientIn) -> Ok | Continue | Error
%% State = saslstate()
%% ClientIn = string()
%% Ok = {ok, Props} | {ok, Props, ServerOut}
%% Props = [Prop]
%% Prop = [{Key, Value}]
%% Key = atom()
%% Value = string()
%% Continue = {continue, ServerOut, New_State}
%% ServerOut = string()
%% New_State = saslstate()
%% Error = {error, Reason} | {error, Reason, Text, Username}
%% Reason = term()
%% Text = string()
%% Username = string()
server_step(State, ClientIn) ->
Module = State#sasl_state.mech_mod,
MechState = State#sasl_state.mech_state,
@@ -256,31 +152,17 @@ server_step(State, ClientIn) ->
{error, Error} ->
{error, Error}
end;
{ok, Props, ServerOut} ->
case check_credentials(State, Props) of
ok ->
{ok, Props, ServerOut};
{error, Error} ->
{error, Error}
end;
{continue, ServerOut, NewMechState} ->
{continue, ServerOut,
State#sasl_state{mech_state = NewMechState}};
{error, Error, Text, Username} ->
{error, Error, Text, Username};
{error, Error, Username} ->
{error, Error, Username};
{error, Error} ->
{error, Error}
end.
%% @spec (Host, Mechs) -> [Filtered_Mechs]
%% Host = string()
%% Mechs = [Mech]
%% Mech = string()
%% Filtered_Mechs = [Mech]
%%
%% @doc Remove the anonymous mechanism from the list if not enabled for
%% the given host.
%% Remove the anonymous mechanism from the list if not enabled for the given
%% host
filter_anonymous(Host, Mechs) ->
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
true -> Mechs;
-15
View File
@@ -1,15 +0,0 @@
%% @type saslparams() = {sasl_params, Host, Realm, GetPassword, CheckPassword, CheckPasswordDigest}
%% Host = string()
%% Realm = string()
%% GetPassword = function()
%% CheckPassword = function()
%% CheckPasswordDigest = any().
%% Parameters for SASL.
-record(sasl_params, {
host,
realm,
get_password,
check_password,
check_password_digest,
socket}).
+5 -25
View File
@@ -6,7 +6,7 @@
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,42 +27,22 @@
-module(cyrsasl_anonymous).
-export([start/1, stop/0, mech_new/1, mech_step/2]).
-include("cyrsasl.hrl").
-export([start/1, stop/0, mech_new/4, mech_step/2]).
-behaviour(cyrsasl).
%% @type mechstate() = {state, Server}
%% Server = string().
-record(state, {server}).
%% @spec (Opts) -> true
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, plain),
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
ok.
%% @spec () -> ok
stop() ->
ok.
mech_new(#sasl_params{host=Host}) ->
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
{ok, #state{server = Host}}.
%% @spec (State, ClientIn) -> Ok | Error
%% State = mechstate()
%% ClientIn = string()
%% Ok = {ok, Props}
%% Props = [Prop]
%% Prop = {username, Username} | {auth_module, AuthModule}
%% Username = string()
%% AuthModule = ejabberd_auth_anonymous
%% Error = {error, 'not-authorized'}
mech_step(State, _ClientIn) ->
%% We generate a random username:
User = lists:concat([randoms:get_string() | tuple_to_list(now())]),
@@ -70,7 +50,7 @@ mech_step(State, _ClientIn) ->
%% Checks that the username is available
case ejabberd_auth:is_user_exists(User, Server) of
true -> {error, 'not-authorized'};
true -> {error, "not-authorized"};
false -> {ok, [{username, User},
{auth_module, ejabberd_auth_anonymous}]}
end.
+22 -167
View File
@@ -5,7 +5,7 @@
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -29,60 +29,29 @@
-export([start/1,
stop/0,
mech_new/1,
mech_new/4,
mech_step/2]).
-include("ejabberd.hrl").
-include("cyrsasl.hrl").
-behaviour(cyrsasl).
%% @type mechstate() = {state, Step, Nonce, Username, AuthzId, GetPassword, CheckPassword, AuthModule, Host}
%% Step = 1 | 3 | 5
%% Nonce = string()
%% Username = string()
%% AuthzId = string()
%% GetPassword = function()
%% AuthModule = atom()
%% Host = string().
-record(state, {step, nonce, username, authzid, get_password, check_password, auth_module,
host}).
%% @spec (Opts) -> true
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, digest).
%% @spec () -> ok
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
stop() ->
ok.
mech_new(#sasl_params{host=Host, get_password=GetPassword,
check_password_digest=CheckPasswordDigest}) ->
mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
{ok, #state{step = 1,
nonce = randoms:get_string(),
host = Host,
get_password = GetPassword,
check_password = CheckPasswordDigest}}.
%% @spec (State, ClientIn) -> Ok | Continue | Error
%% State = mechstate()
%% ClientIn = string()
%% Ok = {ok, Props}
%% Props = [Prop]
%% Prop = {username, Username} | {authzid, AuthzId} | {auth_module, AuthModule}
%% Username = string()
%% AuthzId = string()
%% AuthModule = atom()
%% Continue = {continue, ServerOut, New_State}
%% ServerOut = string()
%% New_State = mechstate()
%% Error = {error, Reason} | {error, Reason, Text, Username}
%% Reason = term()
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
{continue,
"nonce=\"" ++ Nonce ++
@@ -91,23 +60,23 @@ mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
case parse(ClientIn) of
bad ->
{error, 'malformed-request'};
{error, "bad-protocol"};
KeyVals ->
DigestURI = proplists:get_value("digest-uri", KeyVals, ""),
UserName = proplists:get_value("username", KeyVals, ""),
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
UserName = xml:get_attr_s("username", KeyVals),
case is_digesturi_valid(DigestURI, State#state.host) of
false ->
?DEBUG("User login not authorized because digest-uri "
"seems invalid: ~p", [DigestURI]),
{error, 'not-authorized', "", UserName};
{error, "not-authorized", UserName};
true ->
AuthzId = proplists:get_value("authzid", KeyVals, ""),
AuthzId = xml:get_attr_s("authzid", KeyVals),
case (State#state.get_password)(UserName) of
{false, _} ->
{error, 'not-authorized', "", UserName};
{error, "not-authorized", UserName};
{Passwd, AuthModule} ->
case (State#state.check_password)(UserName, "",
proplists:get_value("response", KeyVals, ""),
xml:get_attr_s("response", KeyVals),
fun(PW) -> response(KeyVals, UserName, PW, Nonce, AuthzId,
"AUTHENTICATE") end) of
{true, _} ->
@@ -121,9 +90,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
username = UserName,
authzid = AuthzId}};
false ->
{error, 'not-authorized', "", UserName};
{error, "not-authorized", UserName};
{false, _} ->
{error, 'not-authorized', "", UserName}
{error, "not-authorized", UserName}
end
end
end
@@ -136,18 +105,11 @@ mech_step(#state{step = 5,
{auth_module, AuthModule}]};
mech_step(A, B) ->
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
{error, 'malformed-request'}.
%% @spec (S) -> [{Key, Value}] | bad
%% S = string()
%% Key = string()
%% Value = string()
{error, "bad-protocol"}.
parse(S) ->
parse1(S, "", []).
%% @hidden
parse1([$= | Cs], S, Ts) ->
parse2(Cs, lists:reverse(S), "", Ts);
parse1([$, | Cs], [], Ts) ->
@@ -161,8 +123,6 @@ parse1([], [], T) ->
parse1([], _S, _T) ->
bad.
%% @hidden
parse2([$\" | Cs], Key, Val, Ts) ->
parse3(Cs, Key, Val, Ts);
parse2([C | Cs], Key, Val, Ts) ->
@@ -170,8 +130,6 @@ parse2([C | Cs], Key, Val, Ts) ->
parse2([], _, _, _) ->
bad.
%% @hidden
parse3([$\" | Cs], Key, Val, Ts) ->
parse4(Cs, Key, Val, Ts);
parse3([$\\, C | Cs], Key, Val, Ts) ->
@@ -181,8 +139,6 @@ parse3([C | Cs], Key, Val, Ts) ->
parse3([], _, _, _) ->
bad.
%% @hidden
parse4([$, | Cs], Key, Val, Ts) ->
parse1(Cs, "", [{Key, lists:reverse(Val)} | Ts]);
parse4([$\s | Cs], Key, Val, Ts) ->
@@ -193,10 +149,6 @@ parse4([], Key, Val, Ts) ->
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
%% @spec (DigestURICase, JabberHost) -> bool()
%% DigestURICase = string()
%% JabberHost = string()
%%
%% @doc Check if the digest-uri is valid.
%% RFC-2831 allows to provide the IP address in Host,
%% however ejabberd doesn't allow that.
@@ -204,9 +156,8 @@ parse4([], Key, Val, Ts) ->
%% is provided by several hosts (being one of them server3.example.org),
%% then digest-uri can be like xmpp/server3.example.org/jabber.example.org
%% In that case, ejabberd only checks the service name, not the host.
is_digesturi_valid(DigestURICase, JabberHost) ->
DigestURI = exmpp_stringprep:to_lower(DigestURICase),
DigestURI = stringprep:tolower(DigestURICase),
case catch string:tokens(DigestURI, "/") of
["xmpp", Host] when Host == JabberHost ->
true;
@@ -219,20 +170,14 @@ is_digesturi_valid(DigestURICase, JabberHost) ->
%% @hidden
digit_to_xchar(D) when (D >= 0) and (D < 10) ->
D + 48;
digit_to_xchar(D) ->
D + 87.
%% @hidden
hex(S) ->
hex(S, []).
%% @hidden
hex([], Res) ->
lists:reverse(Res);
hex([N | Ns], Res) ->
@@ -240,35 +185,20 @@ hex([N | Ns], Res) ->
digit_to_xchar(N div 16) | Res]).
%% @spec (KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) -> string()
%% KeyVals = [{Key, Value}]
%% Key = string()
%% Value = string()
%% User = string()
%% Passwd = string()
%% Nonce = string()
%% AuthzId = nil() | string()
%% A2Prefix = string()
response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
Realm = proplists:get_value("realm", KeyVals, ""),
CNonce = proplists:get_value("cnonce", KeyVals, ""),
DigestURI = proplists:get_value("digest-uri", KeyVals, ""),
NC = proplists:get_value("nc", KeyVals, ""),
QOP = proplists:get_value("qop", KeyVals, ""),
%% handle non-fully latin-1 strings as specified
%% on RFC 2831 Section 2.1.2.1 (EJAB-476)
SUser = sanitize(User),
SPasswd = sanitize(Passwd),
SRealm = sanitize(Realm),
Realm = xml:get_attr_s("realm", KeyVals),
CNonce = xml:get_attr_s("cnonce", KeyVals),
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
NC = xml:get_attr_s("nc", KeyVals),
QOP = xml:get_attr_s("qop", KeyVals),
A1 = case AuthzId of
"" ->
binary_to_list(
crypto:md5(SUser ++ ":" ++ SRealm ++ ":" ++ SPasswd)) ++
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
":" ++ Nonce ++ ":" ++ CNonce;
_ ->
binary_to_list(
crypto:md5(SUser ++ ":" ++ SRealm ++ ":" ++ SPasswd)) ++
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
end,
A2 = case QOP of
@@ -284,79 +214,4 @@ response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
hex(binary_to_list(crypto:md5(T))).
sanitize(V) ->
L = from_utf8(V),
case lists:all(fun is_latin1/1, L) of
true -> L;
false -> V
end.
%%%% copied from xmerl_ucs:from_utf8/1 and xmerl_ucs:is_latin1/1 , to not
%%%% require xmerl as a dependency only for this.
from_utf8(Bin) when is_binary(Bin) -> from_utf8(binary_to_list(Bin));
from_utf8(List) ->
case expand_utf8(List) of
{Result,0} -> Result;
{_Res,_NumBadChar} ->
exit({ucs,{bad_utf8_character_code}})
end.
%% expand_utf8([Byte]) -> {[UnicodeChar],NumberOfBadBytes}
%% Expand UTF8 byte sequences to ISO 10646/Unicode
%% charactes. Any illegal bytes are removed and the number of
%% bad bytes are returned.
%%
%% Reference:
%% RFC 3629: "UTF-8, a transformation format of ISO 10646".
expand_utf8(Str) ->
expand_utf8_1(Str, [], 0).
expand_utf8_1([C|Cs], Acc, Bad) when C < 16#80 ->
%% Plain Ascii character.
expand_utf8_1(Cs, [C|Acc], Bad);
expand_utf8_1([C1,C2|Cs], Acc, Bad) when C1 band 16#E0 =:= 16#C0,
C2 band 16#C0 =:= 16#80 ->
case ((C1 band 16#1F) bsl 6) bor (C2 band 16#3F) of
C when 16#80 =< C ->
expand_utf8_1(Cs, [C|Acc], Bad);
_ ->
%% Bad range.
expand_utf8_1(Cs, Acc, Bad+1)
end;
expand_utf8_1([C1,C2,C3|Cs], Acc, Bad) when C1 band 16#F0 =:= 16#E0,
C2 band 16#C0 =:= 16#80,
C3 band 16#C0 =:= 16#80 ->
case ((((C1 band 16#0F) bsl 6) bor (C2 band 16#3F)) bsl 6) bor
(C3 band 16#3F) of
C when 16#800 =< C ->
expand_utf8_1(Cs, [C|Acc], Bad);
_ ->
%% Bad range.
expand_utf8_1(Cs, Acc, Bad+1)
end;
expand_utf8_1([C1,C2,C3,C4|Cs], Acc, Bad) when C1 band 16#F8 =:= 16#F0,
C2 band 16#C0 =:= 16#80,
C3 band 16#C0 =:= 16#80,
C4 band 16#C0 =:= 16#80 ->
case ((((((C1 band 16#0F) bsl 6) bor (C2 band 16#3F)) bsl 6) bor
(C3 band 16#3F)) bsl 6) bor (C4 band 16#3F) of
C when 16#10000 =< C ->
expand_utf8_1(Cs, [C|Acc], Bad);
_ ->
%% Bad range.
expand_utf8_1(Cs, Acc, Bad+1)
end;
expand_utf8_1([_|Cs], Acc, Bad) ->
%% Ignore bad character.
expand_utf8_1(Cs, Acc, Bad+1);
expand_utf8_1([], Acc, Bad) -> {lists:reverse(Acc),Bad}.
%%% Test for legitimate Latin-1 code
is_latin1(Ch) when is_integer(Ch), Ch >= 0, Ch =< 255 -> true;
is_latin1(_) -> false.
-164
View File
@@ -1,164 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_gssapi.erl
%%% Author : Mikael Magnusson <mikma@users.sourceforge.net>
%%% Purpose : GSSAPI SASL mechanism
%%% Created : 1 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
%%%----------------------------------------------------------------------
%%%
%%% Copyright (C) 2007-2009 Mikael Magnusson <mikma@users.sourceforge.net>
%%%
%%% Permission is hereby granted, free of charge, to any person
%%% obtaining a copy of this software and associated documentation
%%% files (the "Software"), to deal in the Software without
%%% restriction, including without limitation the rights to use, copy,
%%% modify, merge, publish, distribute, sublicense, and/or sell copies
%%% of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be
%%% included in all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
%%% SOFTWARE.
%%%
%%%
%%% configuration options:
%%% {sasl_realm, "<Kerberos realm>"}.
%%%
%%% environment variables:
%%% KRB5_KTNAME
%%%
-module(cyrsasl_gssapi).
-author('mikma@users.sourceforge.net').
-export([start/1,
stop/0,
mech_new/1,
mech_step/2]).
-include("ejabberd.hrl").
-include("cyrsasl.hrl").
-behaviour(cyrsasl).
-define(SERVER, ?MODULE).
-define(SERVICE, "xmpp").
-record(state, {sasl,
needsmore=true,
step=0,
host,
realm,
authid,
authzid,
authrealm,
error}).
start(_Opts) ->
ChildSpec =
{?SERVER,
{esasl, start_link, [{local, ?SERVER}]},
transient,
1000,
worker,
[esasl]},
case supervisor:start_child(ejabberd_sup, ChildSpec) of
{ok, _Pid} ->
cyrsasl:register_mechanism("GSSAPI", ?MODULE, false);
{error, Error} = E ->
?ERROR_MSG("esasl failed: ~p", [Error]),
E
end.
stop() ->
catch esasl:stop(?SERVER),
supervisor:terminate_child(ejabberd_sup, ?SERVER),
supervisor:delete_child(ejabberd_sup, ?SERVER).
mech_new(#sasl_params{host=Host, realm=Realm, socket=Socket}) ->
case ejabberd_socket:gethostname(Socket) of
{ok, FQDN} ->
?DEBUG("mech_new ~p ~p ~p~n", [Host, Realm, FQDN]),
case esasl:server_start(?SERVER, "GSSAPI", ?SERVICE, FQDN) of
{ok, Sasl} ->
{ok, #state{sasl=Sasl,host=Host,realm=Realm}};
{error, {gsasl_error, Error}} ->
{ok, Str} = esasl:str_error(?SERVER, Error),
?DEBUG("esasl error: ~p", [Str]),
{ok, #state{needsmore=error,error="internal-server-error"}};
{error, Error} ->
?DEBUG("esasl error: ~p", [Error]),
{ok, #state{needsmore=error,error="internal-server-error"}}
end;
{error, Error} ->
?DEBUG("gethostname error: ~p", [Error]),
{ok, #state{needsmore=error,error="internal-server-error"}}
end.
mech_step(State, ClientIn) when is_list(ClientIn) ->
catch do_step(State, ClientIn).
do_step(#state{needsmore=error,error=Error}=_State, _) ->
{error, Error};
do_step(#state{needsmore=false}=State, _) ->
check_user(State);
do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
?DEBUG("mech_step~n", []),
case esasl:step(Sasl, list_to_binary(ClientIn)) of
{ok, RspAuth} ->
?DEBUG("ok~n", []),
{ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name),
{ok, Authzid} = esasl:property_get(Sasl, authzid),
{Authid, [$@ | Auth_realm]} =
lists:splitwith(fun(E)->E =/= $@ end, Display_name),
State1 = State#state{authid=Authid,
authzid=Authzid,
authrealm=Auth_realm},
handle_step_ok(State1, binary_to_list(RspAuth));
{needsmore, RspAuth} ->
?DEBUG("needsmore~n", []),
if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
{error, 'not-authorized'};
true ->
{continue, binary_to_list(RspAuth),
State#state{step=Step+1}}
end;
{error, _} ->
{error, 'not-authorized'}
end.
handle_step_ok(State, []) ->
check_user(State);
handle_step_ok(#state{step=Step}=State, RspAuth) ->
?DEBUG("continue~n", []),
{continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
check_user(#state{authid=Authid,authzid=Authzid,
authrealm=Auth_realm,host=Host,realm=Realm}) ->
if Realm =/= Auth_realm ->
?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
throw({error, 'not-authorized'});
true ->
ok
end,
case ejabberd_auth:is_user_exists(Authid, Host) of
false ->
?DEBUG("bad user ~p~n",[Authid]),
throw({error, 'not-authorized'});
true ->
ok
end,
?DEBUG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
{ok, [{username, Authid}, {authzid, Authzid}]}.
+8 -60
View File
@@ -5,7 +5,7 @@
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,85 +27,40 @@
-module(cyrsasl_plain).
-author('alexey@process-one.net').
-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]).
-include("cyrsasl.hrl").
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
-behaviour(cyrsasl).
%% @type mechstate() = {state, CheckPassword}
%% CheckPassword = function().
-record(state, {check_password}).
%% @spec (Opts) -> true
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("PLAIN", ?MODULE, plain),
cyrsasl:register_mechanism("PLAIN", ?MODULE, false),
ok.
%% @spec () -> ok
stop() ->
ok.
mech_new(#sasl_params{check_password = CheckPassword}) ->
mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
{ok, #state{check_password = CheckPassword}}.
%% @spec (State, ClientIn) -> Ok | Error
%% State = mechstate()
%% ClientIn = string()
%% Ok = {ok, Props}
%% Props = [Prop]
%% Prop = {username, Username} | {authzid, AuthzId} | {auth_module, AuthModule}
%% Username = string()
%% AuthzId = string()
%% AuthModule = atom()
%% Error = {error, Reason} | {error, Reason, Text, Username}
mech_step(State, ClientIn) ->
case prepare(ClientIn) of
case parse(ClientIn) of
[AuthzId, User, Password] ->
case (State#state.check_password)(User, Password) of
{true, AuthModule} ->
{ok, [{username, User}, {authzid, AuthzId},
{auth_module, AuthModule}]};
{false, ReasonAuthFail} when is_list(ReasonAuthFail) ->
{error, 'not-authorized', ReasonAuthFail, User};
_ ->
{error, 'not-authorized', "", User}
{error, "not-authorized", User}
end;
_ ->
{error, 'malformed-request'}
{error, "bad-protocol"}
end.
prepare(ClientIn) ->
case parse(ClientIn) of
[[], UserMaybeDomain, Password] ->
case parse_domain(UserMaybeDomain) of
%% <NUL>login@domain<NUL>pwd
[User, _Domain] ->
[UserMaybeDomain, User, Password];
%% <NUL>login<NUL>pwd
[User] ->
["", User, Password]
end;
%% login@domain<NUL>login<NUL>pwd
[AuthzId, User, Password] ->
[AuthzId, User, Password];
_ ->
error
end.
%% @hidden
parse(S) ->
parse1(S, "", []).
%% @hidden
parse1([0 | Cs], S, T) ->
parse1(Cs, "", [lists:reverse(S) | T]);
parse1([C | Cs], S, T) ->
@@ -116,12 +71,5 @@ parse1([], S, T) ->
lists:reverse([lists:reverse(S) | T]).
parse_domain(S) ->
parse_domain1(S, "", []).
parse_domain1([$@ | Cs], S, T) ->
parse_domain1(Cs, "", [lists:reverse(S) | T]);
parse_domain1([C | Cs], S, T) ->
parse_domain1(Cs, [C | S], T);
parse_domain1([], S, T) ->
lists:reverse([lists:reverse(S) | T]).
-191
View File
@@ -1,191 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_scram.erl
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
%%% Purpose : SASL SCRAM authentication
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-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(cyrsasl_scram).
-author('stephen.roettger@googlemail.com').
-export([start/1,
stop/0,
mech_new/1,
mech_step/2]).
-include("ejabberd.hrl").
-include("cyrsasl.hrl").
-behaviour(cyrsasl).
-record(state, {step, stored_key, server_key, username, get_password, check_password,
auth_message, client_nonce, server_nonce}).
-define(SALT_LENGTH, 16).
-define(NONCE_LENGTH, 16).
start(_Opts) ->
cyrsasl:register_mechanism("SCRAM-SHA-1", ?MODULE, scram).
stop() ->
ok.
mech_new(#sasl_params{get_password=GetPassword}) ->
{ok, #state{step = 2, get_password = GetPassword}}.
mech_step(#state{step = 2} = State, ClientIn) ->
case string:tokens(ClientIn, ",") of
[CBind, UserNameAttribute, ClientNonceAttribute] when (CBind == "y") or (CBind == "n") ->
case parse_attribute(UserNameAttribute) of
{error, Reason} ->
{error, Reason};
{_, EscapedUserName} ->
case unescape_username(EscapedUserName) of
error ->
{error, 'malformed-request', "Error in username encoding", EscapedUserName};
UserName ->
case parse_attribute(ClientNonceAttribute) of
{$r, ClientNonce} ->
case (State#state.get_password)(UserName) of
{false, _} ->
{error, 'not-authorized', "", UserName};
{Ret, _AuthModule} ->
{StoredKey, ServerKey, Salt, IterationCount} = if
is_tuple(Ret) ->
Ret;
true ->
TempSalt = crypto:rand_bytes(?SALT_LENGTH),
SaltedPassword = scram:salted_password(Ret, TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT),
{scram:stored_key(scram:client_key(SaltedPassword)),
scram:server_key(SaltedPassword), TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT}
end,
ClientFirstMessageBare = string:substr(ClientIn, string:str(ClientIn, "n=")),
ServerNonce = base64:encode_to_string(crypto:rand_bytes(?NONCE_LENGTH)),
ServerFirstMessage = "r=" ++ ClientNonce ++ ServerNonce ++ "," ++
"s=" ++ base64:encode_to_string(Salt) ++ "," ++
"i=" ++ integer_to_list(IterationCount),
{continue,
ServerFirstMessage,
State#state{step = 4, stored_key = StoredKey, server_key = ServerKey,
auth_message = ClientFirstMessageBare ++ "," ++ ServerFirstMessage,
client_nonce = ClientNonce, server_nonce = ServerNonce, username = UserName}}
end;
_Else ->
{error, 'malformed-request'}
end
end
end;
_Else ->
{error, 'malformed-request'}
end;
mech_step(#state{step = 4} = State, ClientIn) ->
case string:tokens(ClientIn, ",") of
[GS2ChannelBindingAttribute, NonceAttribute, ClientProofAttribute] ->
case parse_attribute(GS2ChannelBindingAttribute) of
{$c, CVal} when (CVal == "biws") or (CVal == "eSws") ->
%% biws is base64 for n,, => channelbinding not supported
%% eSws is base64 for y,, => channelbinding supported by client only
Nonce = State#state.client_nonce ++ State#state.server_nonce,
case parse_attribute(NonceAttribute) of
{$r, CompareNonce} when CompareNonce == Nonce ->
case parse_attribute(ClientProofAttribute) of
{$p, ClientProofB64} ->
ClientProof = base64:decode(ClientProofB64),
AuthMessage = State#state.auth_message ++ "," ++ string:substr(ClientIn, 1, string:str(ClientIn, ",p=")-1),
ClientSignature = scram:client_signature(State#state.stored_key, AuthMessage),
ClientKey = scram:client_key(ClientProof, ClientSignature),
CompareStoredKey = scram:stored_key(ClientKey),
if CompareStoredKey == State#state.stored_key ->
ServerSignature = scram:server_signature(State#state.server_key, AuthMessage),
{ok, [{username, State#state.username}], "v=" ++ base64:encode_to_string(ServerSignature)};
true ->
{error, 'not-authorized', "", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
{$r, _} ->
{error, 'malformed-request', "Bad nonce", State#state.username};
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end.
parse_attribute(Attribute) ->
AttributeLen = string:len(Attribute),
if
AttributeLen >= 3 ->
SecondChar = lists:nth(2, Attribute),
case is_alpha(lists:nth(1, Attribute)) of
true ->
if
SecondChar == $= ->
String = string:substr(Attribute, 3),
{lists:nth(1, Attribute), String};
true ->
{error, 'malformed-request', "Second char not equal sign", ""}
end;
_Else ->
{error, 'malformed-request', "First char not a letter", ""}
end;
true ->
{error, 'malformed-request', "Attribute too short", ""}
end.
unescape_username("") ->
"";
unescape_username(EscapedUsername) ->
Pos = string:str(EscapedUsername, "="),
if
Pos == 0 ->
EscapedUsername;
true ->
Start = string:substr(EscapedUsername, 1, Pos-1),
End = string:substr(EscapedUsername, Pos),
EndLen = string:len(End),
if
EndLen < 3 ->
error;
true ->
case string:substr(End, 1, 3) of
"=2C" ->
Start ++ "," ++ unescape_username(string:substr(End, 4));
"=3D" ->
Start ++ "=" ++ unescape_username(string:substr(End, 4));
_Else ->
error
end
end
end.
is_alpha(Char) when Char >= $a, Char =< $z ->
true;
is_alpha(Char) when Char >= $A, Char =< $Z ->
true;
is_alpha(_) ->
false.
+17 -6
View File
@@ -1,6 +1,8 @@
%% $Id$
{application, ejabberd,
[{description, "ejabberd"},
{vsn, "3.0.0-alpha-4"},
{vsn, "2.1.1"},
{modules, [acl,
adhoc,
configure,
@@ -8,15 +10,15 @@
cyrsasl,
cyrsasl_digest,
cyrsasl_plain,
cyrsasl_scram,
ejabberd_admin,
ejabberd_app,
ejabberd_auth_anonymous,
ejabberd_auth,
ejabberd_auth_external,
ejabberd_auth_internal,
ejabberd_auth_ldap,
ejabberd_auth_odbc,
ejabberd_auth_pam,
ejabberd_auth_storage,
ejabberd,
ejabberd_c2s,
ejabberd_c2s_config,
@@ -35,7 +37,6 @@
ejabberd_rdbms,
ejabberd_receiver,
ejabberd_router,
ejabberd_router_multicast,
ejabberd_s2s,
ejabberd_s2s_in,
ejabberd_s2s_out,
@@ -48,6 +49,7 @@
ejabberd_update,
ejabberd_web_admin,
ejabberd_web,
ejabberd_zlib,
ejd2odbc,
eldap,
eldap_filter,
@@ -59,6 +61,7 @@
gen_mod,
gen_pubsub_node,
gen_pubsub_nodetree,
iconv,
idna,
jd2ejd,
jlib,
@@ -71,13 +74,19 @@
mod_echo,
mod_http_bind,
mod_http_fileserver,
mod_irc,
mod_irc_connection,
mod_last,
mod_last_odbc,
mod_muc,
mod_muc_log,
mod_muc_room,
mod_offline,
mod_offline_odbc,
mod_privacy,
mod_privacy_odbc,
mod_private,
mod_private_odbc,
mod_proxy65,
mod_proxy65_lib,
mod_proxy65_service,
@@ -86,12 +95,14 @@
mod_pubsub,
mod_register,
mod_roster,
mod_roster_odbc,
mod_service_log,
mod_shared_roster,
mod_stats,
mod_time,
mod_vcard,
mod_vcard_ldap,
mod_vcard_odbc,
mod_version,
node_buddy,
node_club,
@@ -104,7 +115,6 @@
nodetree_virtual,
p1_fsm,
p1_mnesia,
p1_prof,
randoms,
sha,
shaper,
@@ -113,13 +123,13 @@
tls,
translate,
xml,
xml_stream,
'XmppAddr'
]},
{registered, [ejabberd,
ejabberd_sup,
ejabberd_auth,
ejabberd_router,
ejabberd_router_multicast,
ejabberd_sm,
ejabberd_s2s,
ejabberd_local,
@@ -132,6 +142,7 @@
ejabberd_mod_roster,
ejabberd_mod_echo,
ejabberd_mod_pubsub,
ejabberd_mod_irc,
ejabberd_mod_muc,
ejabberd_offline,
random_generator
+83 -175
View File
@@ -5,7 +5,7 @@
%%% The parameters used in this configuration file are explained in more detail
%%% in the ejabberd Installation and Operation Guide.
%%% Please consult the Guide in case of doubts, it is included with
%%% Please consult the Guide in case of doubts, it is included in
%%% your copy of ejabberd, and is also available online at
%%% http://www.process-one.net/en/ejabberd/docs/
@@ -26,11 +26,11 @@
%%% [http_poll, web_admin, tls]
%%%
%%% - A keyword of ejabberd is a word in lowercase.
%%% Strings are enclosed in "" and can contain spaces, dots, ...
%%% The strings are enclosed in "" and can have spaces, dots...
%%% {language, "en"}.
%%% {ldap_rootdn, "dc=example,dc=com"}.
%%%
%%% - This term includes a tuple, a keyword, a list, and two strings:
%%% - This term includes a tuple, a keyword, a list and two strings:
%%% {hosts, ["jabber.example.net", "im.example.com"]}.
%%%
@@ -74,36 +74,26 @@
%%
%% watchdog_admins: Only useful for developers: if an ejabberd process
%% consumes a lot of memory, send live notifications to these XMPP
%% consumes a lot of memory, send live notifications to these XMPP
%% accounts.
%%
%%{watchdog_admins, ["bob@example.com"]}.
%%%. =====================
%%%' CLUSTER CONFIGURATION
%% clusterid: the integer id of the cluster of nodes this node will
%% belong to.
{clusterid, 1}.
%%%. ================
%%%' SERVED HOSTNAMES
%%
%% hosts: Domains served by ejabberd.
%% You can define one or several, for example:
%% {hosts, ["localhost", "example.net", "example.com", "example.org"]}.
%% {hosts, ["example.net", "example.com", "example.org"]}.
%%
%% Additional hosts will be discovered from the 'hosts' database table
%% configured for the first host.
{hosts, ["localhost"]}.
%%
%% route_subdomains: Delegate subdomains to other XMPP servers.
%% route_subdomains: Delegate subdomains to other XMPP server.
%% For example, if this ejabberd serves example.org and you want
%% to allow communication with an XMPP server called im.example.org.
%% to allow communication with a XMPP server called im.example.org.
%%
%%{route_subdomains, s2s}.
@@ -112,8 +102,8 @@
%%%' LISTENING PORTS
%%
%% listen: The ports ejabberd will listen on, which service each is handled
%% by and what options to start it with.
%% listen: Which ports will ejabberd listen, which service handles it
%% and what options to start it with.
%%
{listen,
[
@@ -121,8 +111,8 @@
{5222, ejabberd_c2s, [
%%
%% If TLS is compiled in and you installed a SSL
%% certificate, specify the full path to the
%% If TLS is compiled and you installed a SSL
%% certificate, put the correct path to the
%% file and uncomment this line:
%%
%%{certfile, "/path/to/ssl.pem"}, starttls,
@@ -133,7 +123,7 @@
]},
%%
%% To enable the old SSL connection method on port 5223:
%% To enable the old SSL connection method in port 5223:
%%
%%{5223, ejabberd_c2s, [
%% {access, c2s},
@@ -148,7 +138,7 @@
]},
%%
%% ejabberd_service: Interact with external components (transports, ...)
%% ejabberd_service: Interact with external components (transports...)
%%
%%{8888, ejabberd_service, [
%% {access, all},
@@ -172,7 +162,6 @@
captcha,
http_bind,
http_poll,
%%register,
web_admin
]}
@@ -180,10 +169,10 @@
%%
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
%% Allowed values are: false optional required required_trusted
%% Allowed values are: true or false.
%% You must specify a certificate file.
%%
%%{s2s_use_starttls, optional}.
%%{s2s_use_starttls, true}.
%%
%% s2s_certfile: Specify a certificate file.
@@ -223,19 +212,11 @@
%%
%% auth_method: Method used to authenticate the users.
%% The default method is the internal Mnesia database storage.
%% The default method is the internal.
%% If you want to use a different method,
%% comment those lines and enable the desired lines later.
%% comment this line and enable the correct ones.
%%
{auth_method, storage}.
{auth_storage, mnesia}.
%%
%% Authentication using ODBC
%% Remember to setup a database in the next section.
%%
%%{auth_method, storage}.
%%{auth_storage, odbc}.
{auth_method, internal}.
%%
%% Authentication using external script
@@ -244,6 +225,12 @@
%%{auth_method, external}.
%%{extauth_program, "/path/to/authentication/script"}.
%%
%% Authentication using ODBC
%% Remember to setup a database in the next section.
%%
%%{auth_method, odbc}.
%%
%% Authentication using PAM
%%
@@ -262,14 +249,14 @@
%%{ldap_encrypt, none}.
%%{ldap_encrypt, tls}.
%%
%% Port to connect to on LDAP servers:
%% Port connect to LDAP servers:
%%{ldap_port, 389}.
%%{ldap_port, 636}.
%%
%% LDAP manager:
%%{ldap_rootdn, "dc=example,dc=com"}.
%%
%% Password of LDAP manager:
%% Password to LDAP manager:
%%{ldap_password, "******"}.
%%
%% Search base of LDAP directory:
@@ -295,51 +282,32 @@
%%
%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
%%
%% auth_password_format: Format of storing users passwords
%% The default format is plain text.
%% If you change to hashed scram, you can never go back to plain.
%% This option is only supported by the 'storage' auth_method.
%%
{auth_password_format, plain}.
%%{auth_password_format, scram}.
%%%. ==============
%%%' DATABASE SETUP
%% ejabberd by default uses the internal Mnesia database,
%% so you do not necessarily need this section.
%% ejabberd uses by default the internal Mnesia database,
%% so you can avoid this section.
%% This section provides configuration examples in case
%% you want to use other database backends.
%% Please consult the ejabberd Guide for details on database creation.
%% Please consult the ejabberd Guide for details about database creation.
%% Template host ODBC configuration.
%% The host specified here will be used by default for all discovered vhosts.
%% The actual ODBC server configuration must be provided in a host_config directive.
{odbc_server, {host, "localhost"}}.
{host_config, "localhost",
[
%%
%% MySQL server:
%%
%%{odbc_server, {mysql, "server", "database", "username", "password"}}
%%{odbc_server, {mysql, "server", "database", "username", "password"}}.
%%
%% If you want to specify the port:
%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}
%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}.
%%
%% PostgreSQL server:
%%
%%{odbc_server, {pgsql, "server", "database", "username", "password"}}
%%{odbc_server, {pgsql, "server", "database", "username", "password"}}.
%%
%% If you want to specify the port:
%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}
%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}.
%%
]
}.
%% If you use PostgreSQL, have a large database, and need a
%% faster but inexact replacement for "select count(*) from users"
%%
@@ -356,8 +324,8 @@
%%{odbc_pool_size, 10}.
%%
%% Interval to make a dummy SQL request to keep the connections to the
%% database alive. Specify in seconds: for example 28800 means 8 hours
%% Interval to make a dummy SQL request to keep alive the connections
%% to the database. Specify in seconds: for example 28800 means 8 hours
%%
%%{odbc_keepalive_interval, undefined}.
@@ -366,28 +334,22 @@
%%%' TRAFFIC SHAPERS
%%
%% The "normal" shaper limits traffic speed to 1000 B/s
%% The "normal" shaper limits traffic speed to 1.000 B/s
%%
{shaper, normal, {maxrate, 1000}}.
%%
%% The "fast" shaper limits traffic speed to 50000 B/s
%% The "fast" shaper limits traffic speed to 50.000 B/s
%%
{shaper, fast, {maxrate, 50000}}.
%%
%% This option specifies the maximum number of elements in the queue
%% of the FSM. Refer to the documentation for details.
%%
{max_fsm_queue, 1000}.
%%%. ====================
%%%' ACCESS CONTROL LISTS
%%
%% The 'admin' ACL grants administrative privileges to XMPP accounts.
%% You can put here as many accounts as you want.
%% You can put as many accounts as you want.
%%
%%{acl, admin, {user, "aleksey", "localhost"}}.
%%{acl, admin, {user, "ermine", "example.org"}}.
@@ -428,7 +390,7 @@
{access, max_user_sessions, [{10, all}]}.
%% Maximum number of offline messages that users can have:
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
%% This rule allows access only for local users:
{access, local, [{allow, local}]}.
@@ -437,41 +399,41 @@
{access, c2s, [{deny, blocked},
{allow, all}]}.
%% For C2S connections, all users except admins use the "normal" shaper
%% For C2S connections, all users except admins use "normal" shaper
{access, c2s_shaper, [{none, admin},
{normal, all}]}.
%% All S2S connections use the "fast" shaper
%% All S2S connections use "fast" shaper
{access, s2s_shaper, [{fast, all}]}.
%% Only admins can send announcement messages:
{access, announce, [{allow, admin}]}.
%% Only admins can use the configuration interface:
%% Only admins can use configuration interface:
{access, configure, [{allow, admin}]}.
%% Admins of this server are also admins of the MUC service:
%% Admins of this server are also admins of MUC service:
{access, muc_admin, [{allow, admin}]}.
%% Only accounts of the local ejabberd server can create rooms:
{access, muc_create, [{allow, local}]}.
%% All users are allowed to use the MUC service:
%% All users are allowed to use MUC service:
{access, muc, [{allow, all}]}.
%% Only accounts on the local ejabberd server can create Pubsub nodes:
%% Only accounts in the local ejabberd server can create Pubsub nodes:
{access, pubsub_createnode, [{allow, local}]}.
%% In-band registration allows registration of any possible username.
%% To disable in-band registration, replace 'allow' with 'deny'.
{access, register, [{allow, all}]}.
%% By default the frequency of account registrations from the same IP
%% is limited to 1 account every 10 minutes. To disable, specify: infinity
%% By default frequency of account registrations from the same IP
%% is limited to 1 account every 10 minutes. To disable put: infinity
%%{registration_timeout, 600}.
%%
%% Define specific Access Rules in a virtual host.
%% Define specific Access rules in a virtual host.
%%
%%{host_config, "localhost",
%% [
@@ -481,19 +443,6 @@
%%}.
%%%. =======
%%%' CAPTCHA
%%
%% Full path to a script that generates the image.
%%
%%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
%% Host for the URL and port where ejabberd listens for CAPTCHA requests.
%%
%%{captcha_host, "example.org:5280"}.
%%%. ================
%%%' DEFAULT LANGUAGE
@@ -510,62 +459,61 @@
%%}.
%%%. ================
%%%' INSTANCE MODULES
%%%. =======
%%%' CAPTCHA
%%
%% Modules to start for every virtual host.
%% Full path to a script that generates the image.
%%
%% Note: One copy of each module will be started for each host.
%%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
%%
%% Host part of the URL sent to the user.
%%
%%{captcha_host, "example.org:5280"}.
%%%. =======
%%%' MODULES
%%
%% Modules enabled in all ejabberd virtual hosts.
%%
{modules,
[
{mod_adhoc, []},
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
{mod_blocking,[]}, % requires mod_privacy
{mod_caps, []}, % 1 proc/host
{mod_caps, []},
{mod_configure,[]}, % requires mod_adhoc
{mod_disco, []},
%%{mod_echo, [{host, "echo.localhost"}]},
{mod_irc, []},
{mod_http_bind, []},
%%{mod_http_fileserver, [
%% {docroot, "/var/www"},
%% {docroot, "/var/www"},
%% {accesslog, "/var/log/ejabberd/access.log"}
%% ]},
{mod_last, []},
{mod_muc, [ % 1 proc/host
{host, "conference.@HOST@"},
{access, muc},
{access_create, muc_create},
{access_persistent, muc_create},
{access_admin, muc_admin}
]},
{mod_muc, [
%%{host, "conference.@HOST@"},
{access, muc},
{access_create, muc_create},
{access_persistent, muc_create},
{access_admin, muc_admin}
]},
%%{mod_muc_log,[]},
%%{mod_multicast,[]},
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
{mod_ping, []},
%%{mod_pres_counter,[{count, 5}, {interval, 60}]},
{mod_privacy, []},
{mod_private, []},
%%{mod_proxy65,[]},
%%{mod_pubsub, [ % 1 proc/host
%% {access_createnode, pubsub_createnode},
%% {ignore_pep_from_offline, true}, % reduces resource comsumption, but XEP incompliant
%% %%{ignore_pep_from_offline, false}, % XEP compliant, but increases resource comsumption
%% {last_item_cache, false},
%% {plugins, ["flat", "pep"]} % pep requires mod_caps
%% ]},
{mod_pubsub, [
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]} % pep requires mod_caps
]},
{mod_register, [
%%
%% Protect In-Band account registrations with CAPTCHA.
%%
%%{captcha_protected, true},
%%
%% Set the minimum informational entropy for passwords.
%%
%%{password_strength, 32},
%%
%% After successful registration, the user receives
%% a message with this subject and body.
@@ -579,59 +527,17 @@
%%
%%{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,[]},
{mod_stats, []},
{mod_time, []},
{mod_vcard, []}, % 1 proc/host
{mod_vcard, []},
{mod_version, []}
]}.
%%%.
%%%' STATIC MODULES
%%
%% Modules to start for the global virtual host ("localhost").
%%
%% The functionality of these modules will be enabled in all ejabberd
%% virtual hosts without consuming more resources.
%%
{static_modules,
[
%% {mod_adhoc, []},
%% {mod_announce, []},
%% {mod_disco, []},
%% {mod_echo, [{prefix, "echo"}]},
%% {mod_last, []},
%% {mod_offline, []}
]}.
%%%.
%%%' HOST SPECIFIC MODULES
%%
%% Enable modules with custom options in a specific virtual host
%%
@@ -647,6 +553,8 @@
%%%.
%%%'
%%% $Id$
%%% Local Variables:
%%% mode: erlang
%%% End:
+3 -36
View File
@@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,14 +28,8 @@
-author('alexey@process-one.net').
-export([start/0, stop/0,
get_so_path/0,
get_bin_path/0,
get_pid_file/0,
is_my_host/1,
normalize_host/1
]).
-include("ejabberd.hrl").
get_pid_file/0,
get_so_path/0, get_bin_path/0]).
start() ->
%%ejabberd_cover:start(),
@@ -71,33 +65,6 @@ get_bin_path() ->
Path
end.
is_my_host([$* | _]) ->
false;
is_my_host(Host) ->
case ejabberd_config:get_local_option({Host, host}) of
undefined ->
WCHost = re:replace(Host, "^[^.]*\.", "*.", [{return, list}]),
case ejabberd_config:get_local_option({WCHost, host}) of
undefined ->
false;
_ ->
true
end;
_ ->
true
end.
normalize_host(global) ->
global;
normalize_host(Host) ->
WCHost = re:replace(Host, "^[^.]*\.", "*.", [{return, list}]),
case ejabberd_config:get_local_option({WCHost, host}) of
undefined ->
Host;
_ ->
WCHost
end.
%% @spec () -> false | string()
get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of
+3 -15
View File
@@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -23,8 +23,7 @@
%% If the ejabberd application description isn't loaded, returns atom: undefined
-define(VERSION, element(2, application:get_key(ejabberd,vsn))).
-define(IS_MY_HOST(Host), ejabberd:is_my_host(Host)).
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)). %% Deprecated
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
-define(MYLANG, ejabberd_config:get_global_option(language)).
@@ -32,24 +31,12 @@
-define(CONFIG_PATH, "ejabberd.cfg").
-define(LOG_PATH, "ejabberd.log").
-ifdef(ENABLE_FLASH_HACK).
-define(FLASH_HACK, true).
-else.
-define(FLASH_HACK, false).
-endif.
-define(EJABBERD_URI, "http://www.process-one.net/en/ejabberd/").
-define(S2STIMEOUT, 600000).
-define(PRIVACY_SUPPORT, true).
%%-define(DBGFSM, true).
-record(scram, {storedkey, serverkey, salt, iterationcount}).
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
%% ---------------------------------
%% Logging mechanism
@@ -71,3 +58,4 @@
-define(CRITICAL_MSG(Format, Args),
ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
+4 -21
View File
@@ -1,20 +1,6 @@
#! /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
@@ -46,17 +32,14 @@ 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|status}"
echo "Usage: $0 {start|stop|restart|force-reload}"
exit 1
esac
+10 -138
View File
@@ -5,7 +5,7 @@
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,21 +30,14 @@
-export([start/0, stop/0,
%% 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,
%% For debugging gen_storage
get_last_info/0,
get_last_info/2,
%% Migration jabberd1.4
import_file/1, import_dir/1,
%% Purge DB
delete_expired_messages/0, delete_old_messages/1,
%% Mnesia
set_master/1,
backup_mnesia/1, restore_mnesia/1,
dump_mnesia/1, dump_table/2, load_mnesia/1,
install_fallback_mnesia/1,
@@ -86,11 +79,6 @@ commands() ->
desc = "Reopen the log files",
module = ?MODULE, function = reopen_log,
args = [], result = {res, rescode}},
#ejabberd_commands{name = stop_kindly, tags = [server],
desc = "Inform users and rooms, wait, and stop the server",
module = ?MODULE, function = stop_kindly,
args = [{delay, integer}, {announcement, string}],
result = {res, rescode}},
#ejabberd_commands{name = get_loglevel, tags = [logs, server],
desc = "Get the current loglevel",
module = ejabberd_loglevel, function = get,
@@ -100,17 +88,6 @@ 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,
@@ -127,21 +104,6 @@ commands() ->
args = [{host, string}],
result = {users, {list, {username, string}}}},
#ejabberd_commands{name = gli, tags = [accounts],
desc = "Get information about last access of an account",
module = ?MODULE, function = get_last_info,
args = [],
result = {lastinfo, {tuple, [{timestamp, integer},
{status, string}
]}}},
#ejabberd_commands{name = get_last_info, tags = [accounts],
desc = "Get information about last access of an account",
module = ?MODULE, function = get_last_info,
args = [{user, string}, {host, string}],
result = {lastinfo, {tuple, [{timestamp, integer},
{status, string}
]}}},
#ejabberd_commands{name = import_file, tags = [mnesia],
desc = "Import user data from jabberd14 spool file",
module = ?MODULE, function = import_file,
@@ -173,13 +135,12 @@ commands() ->
desc = "Delete offline messages older than DAYS",
module = ?MODULE, function = delete_old_messages,
args = [{days, integer}], result = {res, rescode}},
#ejabberd_commands{name = rename_default_nodeplugin, tags = [mnesia],
desc = "Update PubSub table from ejabberd trunk SVN to 2.1.0",
module = mod_pubsub, function = rename_default_nodeplugin,
args = [], result = {res, rescode}},
#ejabberd_commands{name = set_master, tags = [mnesia],
desc = "Set master node of the clustered Mnesia tables",
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}},
#ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia],
desc = "Change the erlang node name in a backup file",
module = ?MODULE, function = mnesia_change_nodename,
@@ -254,75 +215,6 @@ get_sasl_error_logger_type () ->
_ -> all
end.
%%%
%%% Stop Kindly
%%%
stop_kindly(DelaySeconds, AnnouncementText) ->
Subject = io_lib:format("Server stop in ~p seconds!", [DelaySeconds]),
WaitingDesc = io_lib:format("Waiting ~p seconds", [DelaySeconds]),
Steps = [{"Stopping ejabberd port listeners",
ejabberd_listener, stop_listeners, []},
{"Sending announcement to connected users",
mod_announce, send_announcement_to_all,
[?MYNAME, Subject, AnnouncementText]},
{"Sending service message to MUC rooms",
ejabberd_admin, send_service_message_all_mucs,
[Subject, AnnouncementText]},
{WaitingDesc, timer, sleep, [DelaySeconds * 1000]},
{"Stopping ejabberd", application, stop, [ejabberd]},
{"Stopping Mnesia", mnesia, stop, []},
{"Stopping Erlang node", init, stop, []}
],
NumberLast = length(Steps),
TimestampStart = calendar:datetime_to_gregorian_seconds({date(), time()}),
lists:foldl(
fun({Desc, Mod, Func, Args}, NumberThis) ->
SecondsDiff =
calendar:datetime_to_gregorian_seconds({date(), time()})
- TimestampStart,
io:format("[~p/~p ~ps] ~s... ",
[NumberThis, NumberLast, SecondsDiff, Desc]),
Result = apply(Mod, Func, Args),
io:format("~p~n", [Result]),
NumberThis+1
end,
1,
Steps),
ok.
send_service_message_all_mucs(Subject, AnnouncementText) ->
Message = io_lib:format("~s~n~s", [Subject, AnnouncementText]),
lists:foreach(
fun(ServerHost) ->
MUCHost = gen_mod:get_module_opt_host(
ServerHost, mod_muc, "conference.@HOST@"),
MUCHostB = list_to_binary(MUCHost),
mod_muc:broadcast_service_message(MUCHostB, Message)
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
%%%
@@ -330,7 +222,7 @@ update_module(ModuleNameString) ->
register(User, Host, Password) ->
case ejabberd_auth:try_register(User, Host, Password) of
{atomic, ok} ->
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
{ok, io_lib:format("User ~s@~s succesfully registered", [User, Host])};
{atomic, exists} ->
String = io_lib:format("User ~s@~s already registered at node ~p",
[User, Host, node()]),
@@ -350,12 +242,6 @@ registered_users(Host) ->
SUsers = lists:sort(Users),
lists:map(fun({U, _S}) -> U end, SUsers).
get_last_info() -> get_last_info("badlop", "localhost").
get_last_info(User, Server) ->
case mod_last:get_last_info(User, Server) of
{ok, TimeStamp, Status} -> {TimeStamp, Status};
not_found -> {"never", ""}
end.
%%%
%%% Migration management
@@ -387,11 +273,11 @@ import_dir(Path) ->
%%%
delete_expired_messages() ->
mod_offline:remove_expired_messages(),
{atomic, ok} = mod_offline:remove_expired_messages(),
ok.
delete_old_messages(Days) ->
mod_offline:remove_old_messages(Days),
{atomic, _} = mod_offline:remove_old_messages(Days),
ok.
@@ -399,20 +285,6 @@ delete_old_messages(Days) ->
%%% Mnesia management
%%%
set_master("self") ->
set_master(node());
set_master(NodeString) when is_list(NodeString) ->
set_master(list_to_atom(NodeString));
set_master(Node) when is_atom(Node) ->
case mnesia:set_master_nodes([Node]) of
ok ->
{ok, ""};
{error, Reason} ->
String = io_lib:format("Can't set master node ~p at node ~p:~n~p",
[Node, node(), Reason]),
{error, String}
end.
backup_mnesia(Path) ->
case mnesia:backup(Path) of
ok ->
@@ -453,7 +325,7 @@ restore(Path) ->
%% Obsolete tables or tables created by module who are no longer used are not
%% restored and are ignored.
keep_tables() ->
lists:flatten([acl, passwd, config, local_config,
lists:flatten([acl, passwd, config, local_config, disco_publish,
keep_modules_tables()]).
%% Returns the list of modules tables in use, according to the list of actually
+16 -35
View File
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -42,10 +42,10 @@ start(normal, _Args) ->
ejabberd_loglevel:set(4),
write_pid_file(),
application:start(sasl),
application:start(exmpp),
randoms:start(),
db_init(),
sha:start(),
stringprep_sup:start_link(),
start(),
translate:start(),
acl:start(),
@@ -56,8 +56,6 @@ 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(),
@@ -66,10 +64,7 @@ start(normal, _Args) ->
%ejabberd_debug:eprof_start(),
%ejabberd_debug:fprof_start(),
maybe_add_nameservers(),
print_start_debug_info(),
start_modules(),
ejabberd_cluster:announce(),
ejabberd_node_groups:start(),
ejabberd_listener:start_listeners(),
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
Sup;
@@ -82,15 +77,13 @@ start(_, _) ->
prep_stop(State) ->
stop_modules(),
ejabberd_admin:stop(),
broadcast_c2s_shutdown(),
timer:sleep(5000),
State.
%% All the processes were killed when this function is called
stop(_State) ->
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
delete_pid_file(),
%%ejabberd_debug:stop(),
ejabberd_debug:stop(),
ok.
@@ -108,7 +101,19 @@ init() ->
LogPath = get_log_path(),
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
ok.
case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, expat_erl}, [binary]),
loop(Port).
loop(Port) ->
receive
_ ->
loop(Port)
end.
db_init() ->
case mnesia:system_info(extra_db_nodes) of
@@ -122,15 +127,6 @@ db_init() ->
%% Start all the modules in all the hosts
start_modules() ->
case ejabberd_config:get_local_option({static_modules, global}) of
undefined ->
ok;
StaticModules ->
lists:foreach(
fun({Module, Args}) ->
gen_mod:start_module(global, Module, Args)
end, StaticModules)
end,
lists:foreach(
fun(Host) ->
case ejabberd_config:get_local_option({modules, Host}) of
@@ -202,13 +198,6 @@ add_windows_nameservers() ->
lists:foreach(fun(IPT) -> inet_db:add_ns(IPT) end, IPTs).
broadcast_c2s_shutdown() ->
Children = supervisor:which_children(ejabberd_c2s_sup),
lists:foreach(
fun({_, C2SPid, _, _}) ->
C2SPid ! system_shutdown
end, Children).
%%%
%%% PID file
%%%
@@ -238,11 +227,3 @@ delete_pid_file() ->
PidFilename ->
file:delete(PidFilename)
end.
print_start_debug_info() ->
?DEBUG("Inet DB RC:~n~p", [inet_db:get_rc()]),
?DEBUG("Mnesia database:~n~p", [mnesia:system_info(all)]),
?DEBUG("Mnesia tables:~n~p",
[ [{Table, mnesia:table_info(Table, all)} ||
Table <- lists:sort(mnesia:system_info(tables))] ]),
ok.
+94 -291
View File
@@ -5,7 +5,7 @@
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,174 +49,86 @@
is_user_exists_in_other_modules/3,
remove_user/2,
remove_user/3,
plain_password_required/1,
store_type/1,
entropy/1
plain_password_required/1
]).
-export([start/1
,start_modules/2
,stop_modules/2
,start_methods/2
,stop_methods/2
]).
-export([auth_modules/1]).
-include("ejabberd.hrl").
%% @type authmodule() = ejabberd_auth_anonymous | ejabberd_auth_external |
%% ejabberd_auth_ldap | ejabberd_auth_pam |
%% ejabberd_auth_storage | atom().
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
%% @spec () -> term()
start() ->
?DEBUG("About to start auth modules. Hosts: ~p", [?MYHOSTS]),
lists:foreach(fun start/1, ?MYHOSTS).
lists:foreach(
fun(Host) ->
lists:foreach(
fun(M) ->
M:start(Host)
end, auth_modules(Host))
end, ?MYHOSTS).
start(Host) ->
start_modules(Host, auth_modules(Host)).
start_modules(Host, Modules) when is_list(Modules) ->
lists:foreach(fun (M) -> start_modules(Host, M) end, Modules);
start_modules(Host, Module) when is_atom(Module) ->
Module:start(Host).
stop_modules(Host, Modules) when is_list(Modules) ->
lists:foreach(fun (M) -> stop_modules(Host, M) end, Modules);
stop_modules(Host, Module) when is_atom(Module) ->
Module:stop(Host).
start_methods(Host, Methods) when is_list(Methods) ->
lists:foreach(fun (M) -> start_methods(Host, M) end, Methods);
start_methods(Host, Method) when is_atom(Method) ->
start_modules(Host, module_name(Method)).
stop_methods(Host, Methods) when is_list(Methods) ->
lists:foreach(fun (M) -> stop_methods(Host, M) end, Methods);
stop_methods(Host, Method) when is_atom(Method) ->
stop_modules(Host, module_name(Method)).
%% @spec (Server) -> bool()
%% Server = string()
%% This is only executed by ejabberd_c2s for non-SASL auth client
plain_password_required(Server) when is_list(Server) ->
plain_password_required(Server) ->
lists:any(
fun(M) ->
M:plain_password_required()
end, auth_modules(Server)).
%% @spec (Server) -> bool()
%% Server = string()
store_type(Server) ->
lists:foldl(
fun(_, external) ->
external;
(M, scram) ->
case M:store_type() of
external ->
external;
_Else ->
scram
end;
(M, plain) ->
M:store_type()
end, plain, auth_modules(Server)).
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% @doc Check if the user and password can login in server.
check_password(User, Server, Password)
when is_list(User), is_list(Server), is_list(Password) ->
%% @spec (User::string(), Server::string(), Password::string()) ->
%% true | false
check_password(User, Server, Password) ->
case check_password_with_authmodule(User, Server, Password) of
{true, _AuthModule} -> true;
{false, _Reason} -> false
false -> false
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% Digest = string()
%% DigestGen = function()
%% @doc Check if the user and password can login in server.
check_password(User, Server, Password, Digest, DigestGen)
when is_list(User), is_list(Server), is_list(Password),
is_list(Digest), is_function(DigestGen) ->
%% @spec (User::string(), Server::string(), Password::string(),
%% Digest::string(), DigestGen::function()) ->
%% true | false
check_password(User, Server, Password, Digest, DigestGen) ->
case check_password_with_authmodule(User, Server, Password,
Digest, DigestGen) of
{true, _AuthModule} -> true;
{false, _Reason} -> false
false -> false
end.
%% @spec (User::string(), Server::string(), Password::string()) ->
%% {true, AuthModule} | {false, Reason::string()}
%% where
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
%% | ejabberd_auth_internal | ejabberd_auth_ldap
%% | ejabberd_auth_odbc | ejabberd_auth_pam
%% @doc Check if the user and password can login in server.
%% The user can login if at least an authentication method accepts the user
%% and the password.
%% The first authentication method that accepts the credentials is returned.
check_password_with_authmodule(User, Server, Password)
when is_list(User), is_list(Server), is_list(Password) ->
check_password_loop(auth_modules(Server), [User, Server, Password], "").
%% @spec (User::string(), Server::string(), Password::string()) ->
%% {true, AuthModule} | false
%% where
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
%% | ejabberd_auth_internal | ejabberd_auth_ldap
%% | ejabberd_auth_odbc | ejabberd_auth_pam
check_password_with_authmodule(User, Server, Password) ->
check_password_loop(auth_modules(Server), [User, Server, Password]).
%% @spec (User, Server, Password, Digest, DigestGen) -> {true, AuthModule} | false
%% User = string()
%% Server = string()
%% Password = string() | undefined
%% Digest = string() | undefined
%% DigestGen = function()
%% AuthModule = authmodule()
%% @doc Check the password is valid and also return the authentication module that accepts it.
%% The password is 'undefined' if the client
%% authenticates using the digest method as defined in
%% XEP-0078: Non-SASL Authentication
check_password_with_authmodule(User, Server, Password, Digest, DigestGen)
when is_list(User), is_list(Server), (is_list(Password) orelse Password == 'undefined'),
is_function(DigestGen), (is_list(Digest) orelse Digest == 'undefined')->
check_password_with_authmodule(User, Server, Password, Digest, DigestGen) ->
check_password_loop(auth_modules(Server), [User, Server, Password,
Digest, DigestGen], "").
Digest, DigestGen]).
check_password_loop([], _Args, LastReason) ->
{false, LastReason};
check_password_loop([AuthModule | AuthModules], Args, PreviousReason) ->
check_password_loop([], _Args) ->
false;
check_password_loop([AuthModule | AuthModules], Args) ->
case apply(AuthModule, check_password, Args) of
true ->
{true, AuthModule};
{false, Reason} when Reason /= "" ->
check_password_loop(AuthModules, Args, Reason);
{false, ""} ->
check_password_loop(AuthModules, Args, PreviousReason);
false ->
check_password_loop(AuthModules, Args, PreviousReason)
check_password_loop(AuthModules, Args)
end.
%% @spec (User, Server, Password) -> ok | {error, ErrorType}
%% User = string()
%% Server = string()
%% Password = string()
%% ErrorType = empty_password | not_allowed | invalid_jid
%% @spec (User::string(), Server::string(), Password::string()) ->
%% ok | {error, ErrorType}
%% where ErrorType = empty_password | not_allowed | invalid_jid
set_password(_User, _Server, "") ->
%% We do not allow empty password
{error, empty_password};
set_password(User, Server, Password)
when is_list(User), is_list(Server), is_list(Password) ->
set_password(User, Server, Password) ->
lists:foldl(
fun(M, {error, _}) ->
M:set_password(User, Server, Password);
@@ -225,71 +137,49 @@ set_password(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server)).
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string() | nil()
try_register(_User, _Server, "") ->
%% We do not allow empty password
{error, not_allowed};
try_register(User, Server, Password)
when is_list(User), is_list(Server), is_list(Password) ->
case is_user_exists(User, Server) of
{error, not_allowed};
try_register(User, Server, Password) ->
case is_user_exists(User,Server) of
true ->
{atomic, exists};
false ->
case ?IS_MY_HOST(exmpp_stringprep:nameprep(Server)) of
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
true ->
Res = lists:foldl(
fun (_M, {atomic, ok} = Res) ->
Res;
(M, _) ->
M:try_register(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server)),
trigger_register_hooks(Res, User, Server);
fun(_M, {atomic, ok} = Res) ->
Res;
(M, _) ->
M:try_register(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server)),
case Res of
{atomic, ok} ->
ejabberd_hooks:run(register_user, Server,
[User, Server]),
{atomic, ok};
_ -> Res
end;
false ->
{error, not_allowed}
end
end.
trigger_register_hooks({atomic, ok} = Res, User, Server) ->
ejabberd_hooks:run(register_user, list_to_binary(Server),
[User, Server]),
Res;
trigger_register_hooks(Res, _User, _Server) ->
Res.
%% @spec () -> [{LUser, LServer}]
%% LUser = string()
%% LServer = string()
%% @doc Registered users list do not include anonymous users logged.
%% Registered users list do not include anonymous users logged
dirty_get_registered_users() ->
lists:flatmap(
fun(M) ->
M:dirty_get_registered_users()
end, auth_modules()).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
%% LUser = string()
%% LServer = string()
%% @doc Registered users list do not include anonymous users logged.
get_vh_registered_users(Server) when is_list(Server) ->
%% Registered users list do not include anonymous users logged
get_vh_registered_users(Server) ->
lists:flatmap(
fun(M) ->
M:get_vh_registered_users(Server)
end, auth_modules(Server)).
%% @spec (Server, Opts) -> [{LUser, LServer}]
%% Server = string()
%% Opts = [{Opt, Val}]
%% Opt = atom()
%% Val = term()
%% LUser = string()
%% LServer = string()
get_vh_registered_users(Server, Opts) when is_list(Server) ->
get_vh_registered_users(Server, Opts) ->
lists:flatmap(
fun(M) ->
case erlang:function_exported(
@@ -301,11 +191,7 @@ get_vh_registered_users(Server, Opts) when is_list(Server) ->
end
end, auth_modules(Server)).
%% @spec (Server) -> Users_Number
%% Server = string()
%% Users_Number = integer()
get_vh_registered_users_number(Server) when is_list(Server) ->
get_vh_registered_users_number(Server) ->
lists:sum(
lists:map(
fun(M) ->
@@ -318,14 +204,7 @@ get_vh_registered_users_number(Server) when is_list(Server) ->
end
end, auth_modules(Server))).
%% @spec (Server, Opts) -> Users_Number
%% Server = string()
%% Opts = [{Opt, Val}]
%% Opt = atom()
%% Val = term()
%% Users_Number = integer()
get_vh_registered_users_number(Server, Opts) when is_list(Server) ->
get_vh_registered_users_number(Server, Opts) ->
lists:sum(
lists:map(
fun(M) ->
@@ -338,13 +217,9 @@ get_vh_registered_users_number(Server, Opts) when is_list(Server) ->
end
end, auth_modules(Server))).
%% @spec (User, Server) -> Password | false
%% User = string()
%% Server = string()
%% Password = string()
%% @doc Get the password of the user.
get_password(User, Server) when is_list(User), is_list(Server) ->
%% @spec (User::string(), Server::string()) -> Password::string()
get_password(User, Server) ->
lists:foldl(
fun(M, false) ->
M:get_password(User, Server);
@@ -352,31 +227,18 @@ get_password(User, Server) when is_list(User), is_list(Server) ->
Password
end, false, auth_modules(Server)).
%% @spec (User, Server) -> Password | nil()
%% User = string()
%% Server = string()
%% Password = string()
%% @doc Get the password of the user.
get_password_s(User, Server) when is_list(User), is_list(Server) ->
get_password_s(User, Server) ->
case get_password(User, Server) of
false ->
"";
Password when is_list(Password) ->
Password;
_ ->
""
Password ->
Password
end.
%% @spec (User, Server) -> {Password, AuthModule} | {false, none}
%% User = string()
%% Server = string()
%% Password = string()
%% AuthModule = authmodule()
%% @doc Get the password of the user and the auth module.
get_password_with_authmodule(User, Server)
when is_list(User), is_list(Server) ->
%% @spec (User::string(), Server::string()) ->
%% {Password::string(), AuthModule::atom()} | {false, none}
get_password_with_authmodule(User, Server) ->
lists:foldl(
fun(M, {false, _}) ->
{M:get_password(User, Server), M};
@@ -384,36 +246,18 @@ get_password_with_authmodule(User, Server)
{Password, AuthModule}
end, {false, none}, auth_modules(Server)).
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
%% @doc Returns true if the user exists in the DB or if an anonymous
%% user is logged under the given name.
is_user_exists(User, Server) when is_list(User), is_list(Server) ->
%% Returns true if the user exists in the DB or if an anonymous user is logged
%% under the given name
is_user_exists(User, Server) ->
lists:any(
fun(M) ->
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
M:is_user_exists(User, Server)
end, auth_modules(Server)).
%% @spec (Module, User, Server) -> true | false | maybe
%% Module = authmodule()
%% User = string()
%% Server = string()
%% @doc Check if the user exists in all authentications module except
%% the module passed as parameter.
is_user_exists_in_other_modules(Module, User, Server)
when is_list(User), is_list(Server) ->
%% Check if the user exists in all authentications module except the module
%% passed as parameter
%% @spec (Module::atom(), User, Server) -> true | false | maybe
is_user_exists_in_other_modules(Module, User, Server) ->
is_user_exists_in_other_modules_loop(
auth_modules(Server)--[Module],
User, Server).
@@ -434,33 +278,25 @@ is_user_exists_in_other_modules_loop([AuthModule|AuthModules], User, Server) ->
%% @spec (User, Server) -> ok | error | {error, not_allowed}
%% User = string()
%% Server = string()
%% @doc Remove user.
%% TODO: Fix me: It always return ok even if there was some problem removing the user.
%% dialyzer warning
%% ejabberd_auth.erl:388: The variable _ can never match since previous clauses completely covered the type 'ok'
remove_user(User, Server) when is_list(User), is_list(Server) ->
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
R = lists:foreach(
fun(M) ->
M:remove_user(User, Server)
end, auth_modules(Server)),
ejabberd_hooks:run(remove_user, list_to_binary(exmpp_stringprep:nameprep(Server)),
[list_to_binary(User), list_to_binary(Server)]),
fun(M) ->
M:remove_user(User, Server)
end, auth_modules(Server)),
case R of
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
_ -> none
end,
R.
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error
%% User = string()
%% Server = string()
%% Password = string()
%% @doc Try to remove user if the provided password is correct.
%% The removal is attempted in each auth method provided:
%% when one returns 'ok' the loop stops;
%% if no method returns 'ok' then it returns the error message indicated by the last method attempted.
remove_user(User, Server, Password)
when is_list(User), is_list(Server), is_list(Password) ->
remove_user(User, Server, Password) ->
R = lists:foldl(
fun(_M, ok = Res) ->
Res;
@@ -468,44 +304,17 @@ remove_user(User, Server, Password)
M:remove_user(User, Server, Password)
end, error, auth_modules(Server)),
case R of
ok -> ejabberd_hooks:run(remove_user, list_to_binary(exmpp_stringprep:nameprep(Server)),
[list_to_binary(User), list_to_binary(Server)]);
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
_ -> none
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
%%%----------------------------------------------------------------------
%% @spec () -> [authmodule()]
%% @doc Return the lists of all the auth modules actually used in the
%% configuration.
%% Return the lists of all the auth modules actually used in the
%% configuration
auth_modules() ->
lists:usort(
lists:flatmap(
@@ -513,19 +322,13 @@ auth_modules() ->
auth_modules(Server)
end, ?MYHOSTS)).
%% @spec (Server) -> [authmodule()]
%% Server = string()
%% @doc Return the list of authenticated modules for a given host.
auth_modules(Server) when is_list(Server) ->
LServer = exmpp_stringprep:nameprep(Server),
Method = ejabberd_config:get_local_option({auth_method, ejabberd:normalize_host(LServer)}),
%% Return the list of authenticated modules for a given host
auth_modules(Server) ->
LServer = jlib:nameprep(Server),
Method = ejabberd_config:get_local_option({auth_method, LServer}),
Methods = if
Method == undefined -> [];
is_list(Method) -> Method;
is_atom(Method) -> [Method]
end,
[module_name(M) || M <- Methods].
module_name(Method) when is_atom(Method) ->
list_to_atom("ejabberd_auth_" ++ atom_to_list(Method)).
[list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].
+57 -179
View File
@@ -5,7 +5,7 @@
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,7 +28,6 @@
-author('mickael.remond@process-one.net').
-export([start/1,
stop/1,
allow_anonymous/1,
is_sasl_anonymous_enabled/1,
is_login_anonymous_enabled/1,
@@ -52,59 +51,35 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(anonymous, {us, sid}).
%% @spec (Host) -> ok
%% Host = string()
%% @doc Create the anonymous table if at least one virtual host has
%% anonymous features enabled.
%% Register to login / logout events.
start(Host) when is_list(Host) ->
HostB = list_to_binary(Host),
%% Create the anonymous table if at least one virtual host has anonymous features enabled
%% Register to login / logout events
start(Host) ->
%% TODO: Check cluster mode
update_tables(),
mnesia:create_table(anonymous, [{ram_copies, [node()]},
{type, bag}, {local_content, true},
{attributes, record_info(fields, anonymous)}]),
mnesia:add_table_copy(anonymous, node(), ram_copies),
{type, bag},
{attributes, record_info(fields, anonymous)}]),
%% The hooks are needed to add / remove users from the anonymous tables
ejabberd_hooks:add(sm_register_connection_hook, HostB,
ejabberd_hooks:add(sm_register_connection_hook, Host,
?MODULE, register_connection, 100),
ejabberd_hooks:add(sm_remove_connection_hook, HostB,
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, unregister_connection, 100),
ok.
stop(Host) when is_list(Host) ->
HostB = list_to_binary(Host),
ejabberd_hooks:delete(sm_register_connection_hook, HostB,
?MODULE, register_connection, 100),
ejabberd_hooks:delete(sm_remove_connection_hook, HostB,
?MODULE, unregister_connection, 100),
ok.
%% @spec (Host) -> bool()
%% Host = string()
%% @doc Return true if anonymous is allowed for host or false otherwise.
allow_anonymous(Host) when is_list(Host) ->
%% Return true if anonymous is allowed for host or false otherwise
allow_anonymous(Host) ->
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
%% @spec (Host) -> bool()
%% Host = string()
%% @doc Return true if anonymous mode is enabled and if anonymous
%% protocol is SASL anonymous.
%% protocol can be: sasl_anon|login_anon|both
is_sasl_anonymous_enabled(Host) when is_list(Host) ->
%% Return true if anonymous mode is enabled and if anonymous protocol is SASL
%% anonymous protocol can be: sasl_anon|login_anon|both
is_sasl_anonymous_enabled(Host) ->
case allow_anonymous(Host) of
false -> false;
false -> false;
true ->
case anonymous_protocol(Host) of
sasl_anon -> true;
@@ -113,29 +88,23 @@ is_sasl_anonymous_enabled(Host) when is_list(Host) ->
end
end.
%% @spec (Host) -> bool()
%% Host = string()
%% @doc Return true if anonymous login is enabled on the server.
%% Return true if anonymous login is enabled on the server
%% anonymous login can be use using standard authentication method (i.e. with
%% clients that do not support anonymous login)
is_login_anonymous_enabled(Host) when is_list(Host) ->
is_login_anonymous_enabled(Host) ->
case allow_anonymous(Host) of
false -> false;
true ->
case anonymous_protocol(Host) of
login_anon -> true;
both -> true;
both -> true;
_Other -> false
end
end.
%% @spec (Host) -> sasl_anon | login_anon | both
%% Host = string()
%% @doc Return the anonymous protocol to use: sasl_anon|login_anon|both.
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
%% defaults to login_anon
anonymous_protocol(Host) when is_list(Host) ->
anonymous_protocol(Host) ->
case ejabberd_config:get_local_option({anonymous_protocol, Host}) of
sasl_anon -> sasl_anon;
login_anon -> login_anon;
@@ -143,26 +112,18 @@ anonymous_protocol(Host) when is_list(Host) ->
_Other -> sasl_anon
end.
%% @spec (Host) -> bool()
%% Host = string()
%% @doc Return true if multiple connections have been allowed in the
%% config file.
%% Return true if multiple connections have been allowed in the config file
%% defaults to false
allow_multiple_connections(Host) ->
case ejabberd_config:get_local_option({allow_multiple_connections, Host}) of
true -> true;
_Other -> false
end.
allow_multiple_connections(Host) when is_list(Host) ->
ejabberd_config:get_local_option({allow_multiple_connections, Host})
=:= true.
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
%% @doc Check if user exist in the anonymous database.
anonymous_user_exist(User, Server) when is_list(User), is_list(Server) ->
anonymous_user_exist(list_to_binary(User), list_to_binary(Server));
anonymous_user_exist(User, Server) when is_binary(User), is_binary(Server) ->
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
%% Check if user exist in the anonymus database
anonymous_user_exist(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({anonymous, US}) of
[] ->
@@ -171,81 +132,51 @@ anonymous_user_exist(User, Server) when is_binary(User), is_binary(Server) ->
true
end.
%% @spec (SID, LUser, LServer) -> term()
%% SID = term()
%% LUser = string()
%% LServer = string()
%% @doc Remove connection from Mnesia tables.
remove_connection(SID, LUser, LServer) when is_binary(LUser), is_binary(LServer) ->
%% Remove connection from Mnesia tables
remove_connection(SID, LUser, LServer) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete_object({anonymous, US, SID})
end,
mnesia:async_dirty(F).
mnesia:transaction(F).
%% @spec (SID, JID, Info) -> term()
%% SID = term()
%% JID = exmpp_jid:jid()
%% Info = [term()]
%% @doc Register connection.
register_connection(SID, JID, Info) when ?IS_JID(JID) ->
LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(JID),
case proplists:get_value(auth_module, Info) of
undefined ->
ok;
?MODULE ->
ejabberd_hooks:run(register_user, LServer, [LUser, LServer]),
%% Register connection
register_connection(SID, #jid{luser = LUser, lserver = LServer}, Info) ->
AuthModule = xml:get_attr_s(auth_module, Info),
case AuthModule == ?MODULE of
true ->
US = {LUser, LServer},
mnesia:async_dirty(
mnesia:sync_dirty(
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
end);
_ ->
ok
false ->
ok
end.
%% @spec (SID, JID, Ignored) -> term()
%% SID = term()
%% JID = exmpp_jid:jid()
%% Ignored = term()
%% @doc Remove an anonymous user from the anonymous users table.
unregister_connection(SID, JID, _) when ?IS_JID(JID) ->
LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(JID),
%% Remove an anonymous user from the anonymous users table
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}, _) ->
purge_hook(anonymous_user_exist(LUser, LServer),
LUser, LServer),
remove_connection(SID, LUser, LServer).
%% @spec (bool(), LUser, LServer) -> term()
%% LUser = string()
%% LServer = string()
%% @doc Launch the hook to purge user data only for anonymous users.
%% Launch the hook to purge user data only for anonymous users
purge_hook(false, _LUser, _LServer) ->
ok;
purge_hook(true, LUser, LServer) when is_binary(LUser), is_binary(LServer) ->
purge_hook(true, LUser, LServer) ->
ejabberd_hooks:run(anonymous_purge_hook, LServer, [LUser, LServer]).
%% ---------------------------------
%% Specific anonymous auth functions
%% ---------------------------------
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% @doc When anonymous login is enabled, check the password for
%% permenant users before allowing access.
%% When anonymous login is enabled, check the password for permenant users
%% before allowing access
check_password(User, Server, Password) ->
check_password(User, Server, Password, undefined, undefined).
check_password(User, Server, _Password, _Digest, _DigestGen) ->
%% We refuse login for registered accounts (They cannot logged but
%% they however are "reserved")
case ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
case ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
User, Server) of
%% If user exists in other module, reject anonnymous authentication
true -> false;
@@ -254,10 +185,6 @@ check_password(User, Server, _Password, _Digest, _DigestGen) ->
false -> login(User, Server)
end.
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
login(User, Server) ->
case is_login_anonymous_enabled(Server) of
false -> false;
@@ -272,13 +199,8 @@ login(User, Server) ->
end
end.
%% @spec (User, Server, Password) -> ok | {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
%% @doc When anonymous login is enabled, check that the user is
%% permanent before changing its password.
%% When anonymous login is enabled, check that the user is permanent before
%% changing its password
set_password(User, Server, _Password) ->
case anonymous_user_exist(User, Server) of
true ->
@@ -287,41 +209,22 @@ set_password(User, Server, _Password) ->
{error, not_allowed}
end.
%% @spec (User, Server, Password) -> {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
%% @doc When anonymous login is enabled, check if permanent users are
%% allowed on the server:
%% When anonymous login is enabled, check if permanent users are allowed on
%% the server:
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
%% @spec () -> nil()
dirty_get_registered_users() ->
[].
%% @spec (Server) -> nil()
%% Server = string()
get_vh_registered_users(_Server) ->
[].
get_vh_registered_users(Server) ->
[{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(list_to_binary(Server))].
%% @spec (User, Server) -> Password | false
%% User = string()
%% Server = string()
%% Password = nil()
%% @doc Return password of permanent user or false for anonymous users.
%% Return password of permanent user or false for anonymous users
get_password(User, Server) ->
get_password(User, Server, "").
%% @spec (User, Server, DefaultValue) -> DefaultValue | false
%% User = string()
%% Server = string()
%% DefaultValue = string()
get_password(User, Server, DefaultValue) ->
case anonymous_user_exist(User, Server) or login(User, Server) of
%% We return the default value if the user is anonymous
@@ -332,42 +235,17 @@ get_password(User, Server, DefaultValue) ->
false
end.
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
%% @doc Returns true if the user exists in the DB or if an anonymous
%% user is logged under the given name.
%% Returns true if the user exists in the DB or if an anonymous user is logged
%% under the given name
is_user_exists(User, Server) ->
anonymous_user_exist(User, Server).
%% @spec (User, Server) -> {error, not_allowed}
%% User = string()
%% Server = string()
remove_user(_User, _Server) ->
{error, not_allowed}.
%% @spec (User, Server, Password) -> {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
remove_user(_User, _Server, _Password) ->
not_allowed.
%% @spec () -> bool()
plain_password_required() ->
false.
store_type() ->
plain.
update_tables() ->
case catch mnesia:table_info(anonymous, local_content) of
false ->
mnesia:delete_table(anonymous);
_ ->
ok
end.
+19 -324
View File
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -29,187 +29,61 @@
%% External exports
-export([start/1,
stop/1,
set_password/3,
check_password/3,
check_password/5,
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,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
-include("ejabberd.hrl").
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
%% @spec (Host) -> ok
%% Host = string()
start(Host) ->
extauth:start(
Host, ejabberd_config:get_local_option({extauth_program, Host})),
case check_cache_last_options(Host) of
cache ->
ok = ejabberd_auth_storage:start(Host);
no_cache ->
ok
end.
stop(Host) ->
extauth:stop(Host),
case check_cache_last_options(Host) of
cache ->
ok = ejabberd_auth_storage:stop(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.
%% @spec () -> bool()
ok.
plain_password_required() ->
true.
store_type() ->
external.
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
check_password(User, Server, Password) ->
case get_cache_option(Server) of
false -> check_password_extauth(User, Server, Password);
{true, CacheTime} -> check_password_cache(User, Server, Password, CacheTime)
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% Digest = string()
%% DigestGen = function()
extauth:check_password(User, Server, Password) andalso Password /= "".
check_password(User, Server, Password, _Digest, _DigestGen) ->
check_password(User, Server, Password).
%% @spec (User, Server, Password) -> ok | {error, unknown_problem}
%% User = string()
%% Server = string()
%% Password = string()
set_password(User, Server, Password) ->
case extauth:set_password(User, Server, Password) of
true -> set_password_storage(User, Server, Password),
ok;
true -> ok;
_ -> {error, unknown_problem}
end.
%% @spec (User, Server, Password) -> {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
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.
%% @spec () -> nil()
%% @todo Write it.
%% @doc Return the list of all users handled by external.
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
%% TODO
%% Return the list of all users handled by external
dirty_get_registered_users() ->
ejabberd_auth_storage:dirty_get_registered_users().
[].
get_vh_registered_users(Server) ->
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users(Server);
no_cache ->
[]
end.
get_vh_registered_users(_Server) ->
[].
get_vh_registered_users(Server, Data) ->
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users(Server, Data);
no_cache ->
[]
end.
get_password(_User, _Server) ->
false.
%% @spec (Server) -> nil()
%% Server = string()
get_vh_registered_users_number(Server) ->
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users_number(Server);
no_cache ->
0
end.
get_vh_registered_users_number(Server, Data) ->
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users_number(Server, Data);
no_cache ->
0
end.
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
%% @doc Get the user cached password, if possible.
%% 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.
%% @spec (User, Server) -> nil()
%% User = string()
%% Server = string()
get_password_s(User, Server) ->
case get_password(User, Server) of
false -> [];
Other -> Other
end.
get_password_s(_User, _Server) ->
"".
%% @spec (User, Server) -> true | false | {error, Error}
%% User = string()
%% Server = string()
is_user_exists(User, Server) ->
try extauth:is_user_exists(User, Server) of
Res -> Res
@@ -217,188 +91,9 @@ is_user_exists(User, Server) ->
_:Error -> {error, Error}
end.
%% @spec (User, Server) -> {error, not_allowed}
%% User = string()
%% Server = string()
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_storage:remove_user(User, Server)
end
end.
remove_user(_User, _Server, _Password) ->
not_allowed.
%% @spec (User, Server, Password) -> not_allowed
%% User = string()
%% Server = string()
%% Password = string()
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_storage: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_storage(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_storage(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_storage(User, Server) ->
ejabberd_auth_storage:get_password(User, Server).
%% @spec (User, Server, CacheTime) -> Password::string() | false
get_password_cache(User, Server, CacheTime) ->
case get_last_access(User, Server) of
online ->
get_password_storage(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_storage(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_storage(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_storage(User, Server, Password),
R;
_ -> {error, not_allowed}
end.
%% @spec (User, Server, Password) -> true | false
check_password_storage(User, Server, Password) ->
ejabberd_auth_storage:check_password(User, Server, Password).
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
set_password_storage(User, Server, Password) ->
ejabberd_auth_storage: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) -> TimeStamp::integer() | online | never | mod_last_required
get_last_access(UserS, ServerS) ->
%% Code copied from mod_configure.erl
%% Code copied from web/ejabberd_web_admin.erl
%% TODO: Update time format to XEP-0202: Entity Time
User = list_to_binary(UserS),
Server = list_to_binary(ServerS),
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);
no_mod_last -> mod_last_required
end.
%% @spec (Server) -> mod_last | no_mod_last
get_mod_last_enabled(ServerB) when is_binary(ServerB)->
Server = binary_to_list(ServerB),
get_mod_last_enabled(Server);
get_mod_last_enabled(Server) ->
case gen_mod:is_loaded(Server, mod_last) of
true -> mod_last;
false -> no_mod_last
end.
get_mod_last_configured(Server) ->
case is_configured(Server, mod_last) of
true -> mod_last;
false -> no_mod_last
end.
is_configured(Host, Module) ->
lists:keymember(Module, 1, ejabberd_config:get_local_option({modules, Host})).
+382
View File
@@ -0,0 +1,382 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_internal.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Authentification via mnesia
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 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_auth_internal).
-author('alexey@process-one.net').
%% External exports
-export([start/1,
set_password/3,
check_password/3,
check_password/5,
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,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
-include("ejabberd.hrl").
-record(passwd, {us, password}).
-record(reg_users_counter, {vhost, count}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
mnesia:create_table(passwd, [{disc_copies, [node()]},
{attributes, record_info(fields, passwd)}]),
mnesia:create_table(reg_users_counter,
[{ram_copies, [node()]},
{attributes, record_info(fields, reg_users_counter)}]),
update_table(),
update_reg_users_counter_table(Host),
ok.
update_reg_users_counter_table(Server) ->
Set = get_vh_registered_users(Server),
Size = length(Set),
LServer = jlib:nameprep(Server),
set_vh_registered_users_counter(LServer, Size).
plain_password_required() ->
false.
check_password(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Password}] ->
Password /= "";
_ ->
false
end.
check_password(User, Server, Password, Digest, DigestGen) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Passwd}] ->
DigRes = if
Digest /= "" ->
Digest == DigestGen(Passwd);
true ->
false
end,
if DigRes ->
true;
true ->
(Passwd == Password) and (Password /= "")
end;
_ ->
false
end.
%% @spec (User::string(), Server::string(), Password::string()) ->
%% ok | {error, invalid_jid}
set_password(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
if
(LUser == error) or (LServer == error) ->
{error, invalid_jid};
true ->
F = fun() ->
mnesia:write(#passwd{us = US,
password = Password})
end,
{atomic, ok} = mnesia:transaction(F),
ok
end.
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
try_register(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
if
(LUser == error) or (LServer == error) ->
{error, invalid_jid};
true ->
F = fun() ->
case mnesia:read({passwd, US}) of
[] ->
mnesia:write(#passwd{us = US,
password = Password}),
inc_vh_registered_users_counter(LServer),
ok;
[_E] ->
exists
end
end,
mnesia:transaction(F)
end.
%% Get all registered users in Mnesia
dirty_get_registered_users() ->
mnesia:dirty_all_keys(passwd).
get_vh_registered_users(Server) ->
LServer = jlib:nameprep(Server),
mnesia:dirty_select(
passwd,
[{#passwd{us = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]).
get_vh_registered_users(Server, [{from, Start}, {to, End}])
when is_integer(Start) and is_integer(End) ->
get_vh_registered_users(Server, [{limit, End-Start+1}, {offset, Start}]);
get_vh_registered_users(Server, [{limit, Limit}, {offset, Offset}])
when is_integer(Limit) and is_integer(Offset) ->
case get_vh_registered_users(Server) of
[] ->
[];
Users ->
Set = lists:keysort(1, Users),
L = length(Set),
Start = if Offset < 1 -> 1;
Offset > L -> L;
true -> Offset
end,
lists:sublist(Set, Start, Limit)
end;
get_vh_registered_users(Server, [{prefix, Prefix}])
when is_list(Prefix) ->
Set = [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
lists:keysort(1, Set);
get_vh_registered_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
when is_list(Prefix) and is_integer(Start) and is_integer(End) ->
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, End-Start+1}, {offset, Start}]);
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_list(Prefix) and is_integer(Limit) and is_integer(Offset) ->
case [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)] of
[] ->
[];
Users ->
Set = lists:keysort(1, Users),
L = length(Set),
Start = if Offset < 1 -> 1;
Offset > L -> L;
true -> Offset
end,
lists:sublist(Set, Start, Limit)
end;
get_vh_registered_users(Server, _) ->
get_vh_registered_users(Server).
get_vh_registered_users_number(Server) ->
LServer = jlib:nameprep(Server),
Query = mnesia:dirty_select(
reg_users_counter,
[{#reg_users_counter{vhost = LServer, count = '$1'},
[],
['$1']}]),
case Query of
[Count] ->
Count;
_ -> 0
end.
get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix) ->
Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
length(Set);
get_vh_registered_users_number(Server, _) ->
get_vh_registered_users_number(Server).
inc_vh_registered_users_counter(LServer) ->
F = fun() ->
case mnesia:wread({reg_users_counter, LServer}) of
[C] ->
Count = C#reg_users_counter.count + 1,
C2 = C#reg_users_counter{count = Count},
mnesia:write(C2);
_ ->
mnesia:write(#reg_users_counter{vhost = LServer,
count = 1})
end
end,
mnesia:sync_dirty(F).
dec_vh_registered_users_counter(LServer) ->
F = fun() ->
case mnesia:wread({reg_users_counter, LServer}) of
[C] ->
Count = C#reg_users_counter.count - 1,
C2 = C#reg_users_counter{count = Count},
mnesia:write(C2);
_ ->
error
end
end,
mnesia:sync_dirty(F).
set_vh_registered_users_counter(LServer, Count) ->
F = fun() ->
mnesia:write(#reg_users_counter{vhost = LServer,
count = Count})
end,
mnesia:sync_dirty(F).
get_password(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read(passwd, US) of
[#passwd{password = Password}] ->
Password;
_ ->
false
end.
get_password_s(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read(passwd, US) of
[#passwd{password = Password}] ->
Password;
_ ->
[]
end.
%% @spec (User, Server) -> true | false | {error, Error}
is_user_exists(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[] ->
false;
[_] ->
true;
Other ->
{error, Other}
end.
%% @spec (User, Server) -> ok
%% @doc Remove user.
%% Note: it returns ok even if there was some problem removing the user.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
mnesia:delete({passwd, US}),
dec_vh_registered_users_counter(LServer)
end,
mnesia:transaction(F),
ok.
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
%% @doc Remove user if the provided password is correct.
remove_user(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
case mnesia:read({passwd, US}) of
[#passwd{password = Password}] ->
mnesia:delete({passwd, US}),
dec_vh_registered_users_counter(LServer),
ok;
[_] ->
not_allowed;
_ ->
not_exists
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
ok;
{atomic, Res} ->
Res;
_ ->
bad_request
end.
update_table() ->
Fields = record_info(fields, passwd),
case mnesia:table_info(passwd, attributes) of
Fields ->
ok;
[user, password] ->
?INFO_MSG("Converting passwd table from "
"{user, password} format", []),
Host = ?MYNAME,
{atomic, ok} = mnesia:create_table(
ejabberd_auth_internal_tmp_table,
[{disc_only_copies, [node()]},
{type, bag},
{local_content, true},
{record_name, passwd},
{attributes, record_info(fields, passwd)}]),
mnesia:transform_table(passwd, ignore, Fields),
F1 = fun() ->
mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
mnesia:foldl(
fun(#passwd{us = U} = R, _) ->
mnesia:dirty_write(
ejabberd_auth_internal_tmp_table,
R#passwd{us = {U, Host}})
end, ok, passwd)
end,
mnesia:transaction(F1),
mnesia:clear_table(passwd),
F2 = fun() ->
mnesia:write_lock_table(passwd),
mnesia:foldl(
fun(R, _) ->
mnesia:dirty_write(R)
end, ok, ejabberd_auth_internal_tmp_table)
end,
mnesia:transaction(F2),
mnesia:delete_table(ejabberd_auth_internal_tmp_table);
_ ->
?INFO_MSG("Recreating passwd table", []),
mnesia:transform_table(passwd, ignore, Fields)
end.
+60 -246
View File
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -54,7 +54,6 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
@@ -67,14 +66,14 @@
servers,
backups,
port,
tls_options,
encrypt,
dn,
password,
base,
uids,
ufilter,
sfilter,
lfilter, %% Local filter (performed by ejabberd, not LDAP)
deref_aliases,
dn_filter,
dn_filter_attrs
}).
@@ -96,68 +95,27 @@ handle_info(_Info, State) ->
%%% API
%%%----------------------------------------------------------------------
%% @spec (Host) -> term()
%% Host = string()
start(Host) ->
?DEBUG("Starting ~p for ~p.", [?MODULE, Host]),
Option = case ejabberd_config:get_host_option(Host, ldap_servers) of
undefined -> check_local_config(Host);
{host, _Host} -> nothing;
_ ->
{start, Host}
end,
case Option of
nothing -> ok;
{start, Host2} ->
Proc = gen_mod:get_module_proc(Host2, ?MODULE),
ChildSpec = {
Proc, {?MODULE, start_link, [Host2]},
transient, 1000, worker, [?MODULE]
},
supervisor:start_child(ejabberd_sup, ChildSpec)
end.
check_local_config(Host) ->
case ejabberd_config:get_local_option({ldap_servers, Host}) of
undefined ->
?ERROR_MSG("Can't start ~p for host ~p: missing ldap_servers "
"configuration", [?MODULE, Host]),
{error, bad_config};
X when is_list(X)->
{start, Host}
end.
%% @spec (Host) -> term()
%% Host = string()
Proc = gen_mod:get_module_proc(Host, ?MODULE),
ChildSpec = {
Proc, {?MODULE, start_link, [Host]},
transient, 1000, worker, [?MODULE]
},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
case ejabberd_config:get_host_option(Host, ldap_servers) of
undefined -> ok;
{host, _Host} -> ok;
_ ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc)
end.
%% @spec (Host) -> term()
%% Host = string()
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
start_link(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:start_link({local, Proc}, ?MODULE, Host, []).
%% @hidden
terminate(_Reason, _State) ->
ok.
%% @spec (Host) -> {ok, State}
%% Host = string()
%% State = term()
init(Host) ->
State = parse_options(Host),
eldap_pool:start_link(State#state.eldap_id,
@@ -166,29 +124,19 @@ init(Host) ->
State#state.port,
State#state.dn,
State#state.password,
State#state.tls_options),
State#state.encrypt),
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.tls_options),
State#state.encrypt),
{ok, State}.
%% @spec () -> true
plain_password_required() ->
true.
store_type() ->
external.
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
check_password(User, Server, Password) ->
%% In LDAP spec: empty password means anonymous authentication.
%% As ejabberd is providing other anonymous authentication mechanisms
@@ -202,42 +150,15 @@ check_password(User, Server, Password) ->
end
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% Digest = string()
%% DigestGen = function()
check_password(User, Server, Password, _Digest, _DigestGen) ->
check_password(User, Server, Password).
%% @spec (User, Server, Password) -> {error, Reason} | ok
%% User = string()
%% Server = string()
%% Password = string()
%% Reason = term()
set_password(User, Server, Password) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
case find_user_dn(User, Server, 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}
%% User = string()
%% Server = string()
%% Password = string()
try_register(_User, _Server, _Password) ->
set_password(_User, _Server, _Password) ->
{error, not_allowed}.
%% @spec () -> [{LUser, LServer}]
%% LUser = string()
%% LServer = string()
%% @spec (User, Server, Password) -> {error, not_allowed}
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
dirty_get_registered_users() ->
Servers = ejabberd_config:get_vh_by_auth_method(ldap),
@@ -246,42 +167,22 @@ dirty_get_registered_users() ->
get_vh_registered_users(Server)
end, Servers).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
%% LUser = string()
%% LServer = string()
get_vh_registered_users(Server) ->
case catch get_vh_registered_users_ldap(Server) of
{'EXIT', _} -> [];
Result -> Result
end.
%% @spec (Server) -> Users_Number
%% Server = string()
%% Users_Number = integer()
get_vh_registered_users_number(Server) ->
length(get_vh_registered_users(Server)).
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
get_password(_User, _Server) ->
false.
%% @spec (User, Server) -> nil()
%% User = string()
%% Server = string()
get_password_s(_User, _Server) ->
"".
%% @spec (User, Server) -> true | false | {error, Error}
%% User = string()
%% Server = string()
is_user_exists(User, Server) ->
case catch is_user_exists_ldap(User, Server) of
{'EXIT', Error} ->
@@ -290,33 +191,18 @@ is_user_exists(User, Server) ->
Result
end.
%% @spec (User, Server) -> {error, not_allowed}
%% User = string()
%% Server = string()
remove_user(_User, _Server) ->
{error, not_allowed}.
%% @spec (User, Server, Password) -> not_allowed
%% User = string()
%% Server = string()
%% Password = string()
remove_user(_User, _Server, _Password) ->
not_allowed.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
check_password_ldap(User, Server, Password) ->
{ok, State} = get_state(Server),
case find_user_dn(User, Server, State) of
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
case find_user_dn(User, State) of
false ->
false;
DN ->
@@ -324,46 +210,25 @@ check_password_ldap(User, Server, Password) ->
ok -> true;
_ -> false
end
end.
%% We need an ?MODULE server state to use for queries. This will
%% either be Server if this is a statically configured host or the
%% Server for a different host if this is a dynamically configured
%% vhost.
%% The {ldap_vhost, Server} -> Host. ejabberd config option specifies
%% which actual ?MODULE server to use for a particular Host. The value
%% of the option if it is defined or Server by default.
get_state(Server) ->
Host = case ejabberd_config:get_local_option({ldap_servers, Server}) of
{host, H} -> H;
_ -> Server
end,
eldap_utils:get_state(Host, ?MODULE).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
%% LUser = string()
%% LServer = string()
end.
get_vh_registered_users_ldap(Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
UIDs = eldap_utils:uids_domain_subst(Server, State#state.uids),
UIDs = State#state.uids,
Eldap_ID = State#state.eldap_id,
SearchFilter = build_sfilter(State, UIDs),
ResAttrs = result_attrs(State),
case eldap_filter:parse(SearchFilter) of
Server = State#state.host,
SortedDNAttrs = eldap_utils:usort_attrs(State#state.dn_filter_attrs),
case eldap_filter:parse(State#state.sfilter) of
{ok, EldapFilter} ->
case eldap_pool:search(Eldap_ID,
[{base, State#state.base},
{filter, EldapFilter},
{timeout, ?LDAP_SEARCH_TIMEOUT},
{deref_aliases, State#state.deref_aliases},
{attributes, ResAttrs}]) of
case eldap_pool:search(Eldap_ID, [{base, State#state.base},
{filter, EldapFilter},
{timeout, ?LDAP_SEARCH_TIMEOUT},
{attributes, SortedDNAttrs}]) of
#eldap_search_result{entries = Entries} ->
lists:flatmap(
fun(#eldap_entry{attributes = Attrs,
object_name = DN}) ->
case is_valid_dn(DN, Server, Attrs, State) of
case is_valid_dn(DN, Attrs, State) of
false -> [];
_ ->
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
@@ -371,11 +236,9 @@ get_vh_registered_users_ldap(Server) ->
{User, UIDFormat} ->
case eldap_utils:get_user_part(User, UIDFormat) of
{ok, U} ->
try
[{exmpp_stringprep:nodeprep(U), exmpp_stringprep:nameprep(Server)}]
catch
_ ->
[]
case jlib:nodeprep(U) of
error -> [];
LU -> [{LU, jlib:nameprep(Server)}]
end;
_ -> []
end
@@ -389,13 +252,9 @@ get_vh_registered_users_ldap(Server) ->
[]
end.
%% @spec (User, Server) -> bool()
%% User = string()
%% Server = string()
is_user_exists_ldap(User, Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
case find_user_dn(User, Server, State) of
case find_user_dn(User, State) of
false -> false;
_DN -> true
end.
@@ -409,19 +268,16 @@ handle_call(stop, _From, State) ->
handle_call(_Request, _From, State) ->
{reply, bad_request, State}.
find_user_dn(User, Server, State) ->
ResAttrs = result_attrs(State),
UserFilter = build_ufilter(State, Server),
case eldap_filter:parse(UserFilter, [{"%u", User}]) of
find_user_dn(User, State) ->
DNAttrs = eldap_utils:usort_attrs(State#state.dn_filter_attrs),
case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
{ok, Filter} ->
case eldap_pool:search(State#state.eldap_id,
[{base, State#state.base},
{filter, Filter},
{deref_aliases, State#state.deref_aliases},
{attributes, ResAttrs}]) of
case eldap_pool:search(State#state.eldap_id, [{base, State#state.base},
{filter, Filter},
{attributes, DNAttrs}]) of
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
object_name = DN} | _]} ->
dn_filter(DN, Server, Attrs, State);
dn_filter(DN, Attrs, State);
_ ->
false
end;
@@ -430,20 +286,20 @@ find_user_dn(User, Server, State) ->
end.
%% apply the dn filter and the local filter:
dn_filter(DN, Server, Attrs, State) ->
dn_filter(DN, Attrs, State) ->
%% Check if user is denied access by attribute value (local check)
case check_local_filter(Attrs, State) of
false -> false;
true -> is_valid_dn(DN, Server, Attrs, State)
true -> is_valid_dn(DN, Attrs, State)
end.
%% Check that the DN is valid, based on the dn filter
is_valid_dn(DN, _, _, #state{dn_filter = undefined}) ->
is_valid_dn(DN, _, #state{dn_filter = undefined}) ->
DN;
is_valid_dn(DN, Server, Attrs, State) ->
is_valid_dn(DN, Attrs, State) ->
DNAttrs = State#state.dn_filter_attrs,
UIDs = eldap_utils:uids_domain_subst(Server, State#state.uids),
UIDs = State#state.uids,
Values = [{"%s", eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
"" -> Values;
@@ -455,11 +311,10 @@ is_valid_dn(DN, Server, Attrs, State) ->
end ++ [{"%d", State#state.host}, {"%D", DN}],
case eldap_filter:parse(State#state.dn_filter, SubstValues) of
{ok, EldapFilter} ->
case eldap_pool:search(State#state.eldap_id,
[{base, State#state.base},
{filter, EldapFilter},
{deref_aliases, State#state.deref_aliases},
{attributes, ["dn"]}]) of
case eldap_pool:search(State#state.eldap_id, [
{base, State#state.base},
{filter, EldapFilter},
{attributes, ["dn"]}]) of
#eldap_search_result{entries = [_|_]} ->
DN;
_ ->
@@ -491,30 +346,6 @@ local_filter(equal, Attrs, FilterMatch) ->
local_filter(notequal, Attrs, FilterMatch) ->
not local_filter(equal, Attrs, FilterMatch).
result_attrs(#state{uids = UIDs, dn_filter_attrs = DNFilterAttrs}) ->
lists:foldl(
fun({UID}, Acc) ->
[UID | Acc];
({UID, _}, Acc) ->
[UID | Acc]
end, DNFilterAttrs, UIDs).
build_ufilter(State, VHost) ->
UIDs = eldap_utils:uids_domain_subst(VHost, State#state.uids),
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
case State#state.ufilter of
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
end.
build_sfilter(State, FormattedUIDs) ->
SubFilter = lists:flatten(eldap_utils:generate_subfilter(FormattedUIDs)),
UserFilter = case State#state.ufilter of
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
end,
eldap_filter:do_sub(UserFilter, [{"%u", "*"}]).
%%%----------------------------------------------------------------------
%%% Auxiliary functions
%%%----------------------------------------------------------------------
@@ -527,9 +358,6 @@ 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}),
LDAPTLSCAFile = ejabberd_config:get_local_option({ldap_tls_cacertfile, Host}),
LDAPTLSDepth = ejabberd_config:get_local_option({ldap_tls_depth, Host}),
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
undefined -> case LDAPEncrypt of
tls -> ?LDAPS_PORT;
@@ -552,46 +380,32 @@ parse_options(Host) ->
end,
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> "";
"" -> "";
F ->
eldap_utils:check_filter(F),
"(&" ++ SubFilter ++ F ++ ")"
undefined -> SubFilter;
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
end,
SearchFilter = eldap_filter:do_sub(UserFilter, [{"%u", "*"}]),
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
{DNFilter, DNFilterAttrs} =
case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of
undefined ->
{undefined, []};
{DNF, undefined} ->
{DNF, []};
{DNF, DNFA} ->
{DNF, DNFA}
undefined -> {undefined, undefined};
{DNF, DNFA} -> {DNF, DNFA}
end,
eldap_utils:check_filter(DNFilter),
LocalFilter = ejabberd_config:get_local_option({ldap_local_filter, Host}),
DerefAliases = case ejabberd_config:get_local_option(
{ldap_deref_aliases, Host}) of
undefined -> never;
Val -> Val
end,
LocalFilter = ejabberd_config:get_local_option({ldap_local_filter, Host}),
#state{host = Host,
eldap_id = Eldap_ID,
bind_eldap_id = Bind_Eldap_ID,
servers = LDAPServers,
backups = LDAPBackups,
port = LDAPPort,
tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify},
{tls_cacertfile, LDAPTLSCAFile},
{tls_depth, LDAPTLSDepth}],
encrypt = LDAPEncrypt,
dn = RootDN,
password = Password,
base = LDAPBase,
uids = UIDs,
ufilter = UserFilter,
sfilter = SearchFilter,
lfilter = LocalFilter,
deref_aliases = DerefAliases,
dn_filter = DNFilter,
dn_filter_attrs = DNFilterAttrs
}.
+281
View File
@@ -0,0 +1,281 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_odbc.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Authentification via ODBC
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 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_auth_odbc).
-author('alexey@process-one.net').
%% External exports
-export([start/1,
set_password/3,
check_password/3,
check_password/5,
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,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
-include("ejabberd.hrl").
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(_Host) ->
ok.
plain_password_required() ->
false.
%% @spec (User, Server, Password) -> true | false | {error, Error}
check_password(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
try odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
Password /= ""; %% Password is correct, and not empty
{selected, ["password"], [{_Password2}]} ->
false; %% Password is not correct
{selected, ["password"], []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
catch
_:_ ->
false %% Typical error is database not accessible
end
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
check_password(User, Server, Password, Digest, DigestGen) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
try odbc_queries:get_password(LServer, Username) of
%% Account exists, check if password is valid
{selected, ["password"], [{Passwd}]} ->
DigRes = if
Digest /= "" ->
Digest == DigestGen(Passwd);
true ->
false
end,
if DigRes ->
true;
true ->
(Passwd == Password) and (Password /= "")
end;
{selected, ["password"], []} ->
false; %% Account does not exist
{error, _Error} ->
false %% Typical error is that table doesn't exist
catch
_:_ ->
false %% Typical error is database not accessible
end
end.
%% @spec (User::string(), Server::string(), Password::string()) ->
%% ok | {error, invalid_jid}
set_password(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
{error, invalid_jid};
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
case catch odbc_queries:set_password_t(LServer, Username, Pass) of
{atomic, ok} -> ok;
Other -> {error, Other}
end
end.
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
try_register(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
{error, invalid_jid};
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
case catch odbc_queries:add_user(LServer, Username, Pass) of
{updated, 1} ->
{atomic, ok};
_ ->
{atomic, exists}
end
end.
dirty_get_registered_users() ->
Servers = ejabberd_config:get_vh_by_auth_method(odbc),
lists:flatmap(
fun(Server) ->
get_vh_registered_users(Server)
end, Servers).
get_vh_registered_users(Server) ->
LServer = jlib:nameprep(Server),
case catch odbc_queries:list_users(LServer) of
{selected, ["username"], Res} ->
[{U, LServer} || {U} <- Res];
_ ->
[]
end.
get_vh_registered_users(Server, Opts) ->
LServer = jlib:nameprep(Server),
case catch odbc_queries:list_users(LServer, Opts) of
{selected, ["username"], Res} ->
[{U, LServer} || {U} <- Res];
_ ->
[]
end.
get_vh_registered_users_number(Server) ->
LServer = jlib:nameprep(Server),
case catch odbc_queries:users_number(LServer) of
{selected, [_], [{Res}]} ->
list_to_integer(Res);
_ ->
0
end.
get_vh_registered_users_number(Server, Opts) ->
LServer = jlib:nameprep(Server),
case catch odbc_queries:users_number(LServer, Opts) of
{selected, [_], [{Res}]} ->
list_to_integer(Res);
_Other ->
0
end.
get_password(User, Server) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
false
end
end.
get_password_s(User, Server) ->
case jlib:nodeprep(User) of
error ->
"";
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
""
end
end.
%% @spec (User, Server) -> true | false | {error, Error}
is_user_exists(User, Server) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
try odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{_Password}]} ->
true; %% Account exists
{selected, ["password"], []} ->
false; %% Account does not exist
{error, Error} ->
{error, Error} %% Typical error is that table doesn't exist
catch
_:B ->
{error, B} %% Typical error is database not accessible
end
end.
%% @spec (User, Server) -> ok | error
%% @doc Remove user.
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
case jlib:nodeprep(User) of
error ->
error;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
catch odbc_queries:del_user(LServer, Username),
ok
end.
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
%% @doc Remove user if the provided password is correct.
remove_user(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
error;
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
F = fun() ->
Result = odbc_queries:del_user_return_password(
LServer, Username, Pass),
case Result of
{selected, ["password"], [{Password}]} ->
ok;
{selected, ["password"], []} ->
not_exists;
_ ->
not_allowed
end
end,
{atomic, Result} = odbc_queries:sql_transaction(LServer, F),
Result
end.
+12 -98
View File
@@ -5,7 +5,7 @@
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,7 +28,6 @@
%% External exports
-export([start/1,
stop/1,
set_password/3,
check_password/3,
check_password/5,
@@ -40,17 +39,12 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
%%====================================================================
%% API
%%====================================================================
%% @spec (Host) -> ok | term()
%% Host = string()
start(_Host) ->
case epam:start() of
{ok, _} -> ok;
@@ -58,137 +52,57 @@ start(_Host) ->
Err -> Err
end.
%% TODO: Stop epam if no other auth_pam are running.
stop(_Host) ->
ok.
%% @spec (User, Server, Password) -> {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
set_password(_User, _Server, _Password) ->
{error, not_allowed}.
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% Digest = string()
%% DigestGen = function()
check_password(User, Server, Password, _StreamID, _Digest) ->
check_password(User, Server, Password, _Digest, _DigestGen) ->
check_password(User, Server, Password).
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
check_password(User, Server, Password) ->
Service = get_pam_service(Server),
UserInfo = case get_pam_userinfotype(Server) of
username -> User;
jid -> User++"@"++Server
end,
case catch epam:authenticate(Service, UserInfo, Password) of
check_password(User, Host, Password) ->
Service = get_pam_service(Host),
case catch epam:authenticate(Service, User, Password) of
true -> true;
_ -> false
end.
%% @spec (User, Server, Password) -> {error, not_allowed}
%% User = string()
%% Server = string()
%% Password = string()
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
%% @spec () -> [{LUser, LServer}]
%% LUser = string()
%% LServer = string()
dirty_get_registered_users() ->
[].
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
%% LUser = string()
%% LServer = string()
get_vh_registered_users(_Server) ->
get_vh_registered_users(_Host) ->
[].
%% @spec (User, Server) -> Password | false
%% User = string()
%% Server = string()
%% Password = string()
get_password(_User, _Server) ->
false.
%% @spec (User, Server) -> Password | nil()
%% User = string()
%% Server = string()
%% Password = string()
get_password_s(_User, _Server) ->
"".
%% @spec (User, Server) -> true | false | {error, Error}
%% User = string()
%% Server = string()
%% TODO: Improve this function to return an error instead of 'false' when
%% connection to PAM failed
is_user_exists(User, Server) ->
Service = get_pam_service(Server),
UserInfo = case get_pam_userinfotype(Server) of
username -> User;
jid -> User++"@"++Server
end,
case catch epam:acct_mgmt(Service, UserInfo) of
%% 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
true -> true;
_ -> false
end.
%% @spec (User, Server) -> {error, not_allowed}
%% User = string()
%% Server = string()
remove_user(_User, _Server) ->
{error, not_allowed}.
%% @spec (User, Server, Password) -> not_allowed
%% User = string()
%% Server = string()
%% Password = string()
remove_user(_User, _Server, _Password) ->
not_allowed.
%% @spec () -> bool()
plain_password_required() ->
true.
store_type() ->
external.
%%====================================================================
%% Internal functions
%%====================================================================
%% @spec (Server) -> string()
%% Server = string()
get_pam_service(Server) ->
case ejabberd_config:get_local_option({pam_service, Server}) of
get_pam_service(Host) ->
case ejabberd_config:get_local_option({pam_service, Host}) of
undefined -> "ejabberd";
Service -> Service
end.
get_pam_userinfotype(Host) ->
case ejabberd_config:get_local_option({pam_userinfotype, Host}) of
undefined -> username;
Type -> Type
end.
-655
View File
@@ -1,655 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_storage.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>, Stephan Maka
%%% Purpose : Authentification via gen_storage
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@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
%%%
%%%----------------------------------------------------------------------
%%% Database schema (version / storage / table)
%%%
%%% 2.1.x / mnesia / passwd
%%% us = {Username::string(), Host::string()}
%%% password = string()
%%%
%%% 2.1.x / odbc / users
%%% username = varchar250
%%% password = text
%%%
%%% 3.0.0-prealpha / mnesia / passwd
%%% Same as 2.1.x
%%%
%%% 3.0.0-prealpha / odbc / users
%%% Same as 2.1.x
%%%
%%% 3.0.0-alpha / mnesia / passwd
%%% user_host = {Username::string(), Host::string()}
%%% password = string()
%%%
%%% 3.0.0-beta / mnesia / passwd
%%% user_host = {Username::string(), Host::string()}
%%% password = string()
%%% storedkey = base64 binary()
%%% serverkey = base64 binary()
%%% iterationcount = integer()
%%% salt = base64 binary()
%%%
%%% 3.0.0-alpha / odbc / passwd
%%% user = varchar150
%%% host = varchar150
%%% password = text
%%%
%%% 3.0.0-beta / odbc / passwd
%%% user = varchar150
%%% host = varchar150
%%% password = base64 text
%%% storedkey = base64 text
%%% serverkey = base64 text
%%% iterationcount = integer
%%% salt = base64 text
-module(ejabberd_auth_storage).
-author('alexey@process-one.net').
%% External exports
-export([start/1,
stop/1,
set_password/3,
check_password/3,
check_password/5,
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,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
-include("ejabberd.hrl").
-record(passwd, {user_host, password, storedkey, serverkey, salt, iterationcount}).
-record(reg_users_counter, {vhost, count}).
-define(SALT_LENGTH, 16).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
%% @spec (Host) -> ok
%% Host = string()
start(Host) ->
Backend =
case ejabberd_config:get_local_option({auth_storage, Host}) of
undefined -> mnesia;
B -> B
end,
HostB = list_to_binary(Host),
gen_storage:create_table(Backend, HostB, passwd,
[{odbc_host, Host},
{disc_copies, [node()]},
{attributes, record_info(fields, passwd)},
{types, [{user_host, {text, text}},
{storedkey, binary},
{serverkey, binary},
{salt, binary},
{iterationcount, int}]}
]),
update_table(Host, Backend),
maybe_scram_passwords(HostB),
mnesia:create_table(reg_users_counter,
[{ram_copies, [node()]},
{attributes, record_info(fields, reg_users_counter)}]),
update_reg_users_counter_table(Host),
maybe_alert_password_scrammed_without_option(HostB),
ok.
stop(_Host) ->
ok.
update_reg_users_counter_table(Server) ->
Set = get_vh_registered_users(Server),
Size = length(Set),
LServer = exmpp_jid:prep_domain(exmpp_jid:parse(Server)),
F = fun() ->
mnesia:write(#reg_users_counter{vhost = LServer,
count = Size})
end,
mnesia:sync_dirty(F).
%% @spec () -> bool()
plain_password_required() ->
case is_scrammed(?MYNAME) of
false -> false;
true -> true
end.
store_type() ->
case is_scrammed(?MYNAME) of
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
true -> scram %% allows: PLAIN SCRAM
end.
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
check_password(User, Server, Password) ->
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
is_password_scram_valid(Password, Passwd);
[#passwd{password = Password}] ->
Password /= "";
_ ->
false
end.
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
%% User = string()
%% Server = string()
%% Password = string()
%% Digest = string()
%% DigestGen = function()
check_password(User, Server, Password, Digest, DigestGen) ->
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
Storedkey = base64:decode(Passwd#passwd.storedkey),
DigRes = if
Digest /= "" ->
Digest == DigestGen(Storedkey);
true ->
false
end,
if DigRes ->
true;
true ->
(Storedkey == Password) and (Password /= "")
end;
[#passwd{password = Passwd}] ->
DigRes = if
Digest /= "" ->
Digest == DigestGen(Passwd);
true ->
false
end,
if DigRes ->
true;
true ->
(Passwd == Password) and (Password /= "")
end;
_ ->
false
end.
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
%% User = string()
%% Server = string()
%% Password = string()
set_password(User, Server, Password) ->
LUser = (catch exmpp_stringprep:nodeprep(User)),
LServer = (catch exmpp_stringprep:nameprep(Server)),
case {LUser, LServer} of
{{stringprep, _, invalid_string, _}, _} ->
{error, invalid_jid};
{_, {stringprep, _, invalid_string, _}} ->
{error, invalid_jid};
US ->
%% TODO: why is this a transaction?
F = fun() ->
Passwd = case is_scrammed(LServer) and (Password /= "") of
true -> password_to_scram(Password, #passwd{user_host=US});
false -> #passwd{user_host = US, password = Password}
end,
gen_storage:write(LServer, Passwd)
end,
{atomic, ok} = gen_storage:transaction(LServer, passwd, F),
ok
end.
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
%% User = string()
%% Server = string()
%% Password = string()
try_register(User, Server, Password) ->
LUser = (catch exmpp_stringprep:nodeprep(User)),
LServer = (catch exmpp_stringprep:nameprep(Server)),
case {LUser, LServer} of
{{stringprep, _, invalid_string, _}, _} ->
{error, invalid_jid};
{_, {stringprep, _, invalid_string, _}} ->
{error, invalid_jid};
US ->
F = fun() ->
case gen_storage:read(LServer, {passwd, US}) of
[] ->
Passwd = case is_scrammed(LServer) and (Password /= "") of
true -> password_to_scram(Password, #passwd{user_host=US});
false -> #passwd{user_host = US, password = Password}
end,
gen_storage:write(LServer, Passwd),
mnesia:dirty_update_counter(
reg_users_counter,
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), 1),
ok;
[_E] ->
exists
end
end,
%% TODO: transaction return value?
gen_storage:transaction(LServer, passwd, F)
end.
%% @spec () -> [{LUser, LServer}]
%% LUser = string()
%% LServer = string()
%% @doc Get all registered users in Mnesia.
dirty_get_registered_users() ->
lists:foldl(
fun(HostB, Res) ->
get_vh_registered_users(binary_to_list(HostB)) ++ Res
end,
[],
gen_storage:all_table_hosts(passwd)).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
%% LUser = string()
%% LServer = string()
get_vh_registered_users(Server) ->
LServer = exmpp_stringprep:nameprep(Server),
lists:map(fun(#passwd{user_host = US}) ->
US
end,
gen_storage:dirty_select(LServer, passwd,
[{'=', user_host, {'_', LServer}}])).
%% @spec (Server, Opts) -> [{LUser, LServer}]
%% Server = string()
%% Opts = [{Opt, Val}]
%% Opt = atom()
%% Val = term()
%% LUser = string()
%% LServer = string()
%% @doc Return the registered users for the specified host.
%%
%% `Opts' can be one of the following:
%% <ul>
%% <li>`[{from, integer()}, {to, integer()}]'</li>
%% <li>`[{limit, integer()}, {offset, integer()}]'</li>
%% <li>`[{prefix, string()}]'</li>
%% <li>`[{prefix, string()}, {from, integer()}, {to, integer()}]'</li>
%% <li>`[{prefix, string()}, {limit, integer()}, {offset, integer()}]'</li>
%% </ul>
get_vh_registered_users(Server, [{from, Start}, {to, End}])
when is_integer(Start) and is_integer(End) ->
get_vh_registered_users(Server, [{limit, End-Start+1}, {offset, Start}]);
get_vh_registered_users(Server, [{limit, Limit}, {offset, Offset}])
when is_integer(Limit) and is_integer(Offset) ->
case get_vh_registered_users(Server) of
[] ->
[];
Users ->
Set = lists:keysort(1, Users),
L = length(Set),
Start = if Offset < 1 -> 1;
Offset > L -> L;
true -> Offset
end,
lists:sublist(Set, Start, Limit)
end;
get_vh_registered_users(Server, [{prefix, Prefix}])
when is_list(Prefix) ->
Set = [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
lists:keysort(1, Set);
get_vh_registered_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
when is_list(Prefix) and is_integer(Start) and is_integer(End) ->
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, End-Start+1}, {offset, Start}]);
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_list(Prefix) and is_integer(Limit) and is_integer(Offset) ->
case [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)] of
[] ->
[];
Users ->
Set = lists:keysort(1, Users),
L = length(Set),
Start = if Offset < 1 -> 1;
Offset > L -> L;
true -> Offset
end,
lists:sublist(Set, Start, Limit)
end;
get_vh_registered_users(Server, _) ->
get_vh_registered_users(Server).
%% @spec (Server) -> Users_Number
%% Server = string()
%% Users_Number = integer()
get_vh_registered_users_number(Server) ->
LServer = exmpp_jid:prep_domain(exmpp_jid:parse(Server)),
Query = mnesia:dirty_select(
reg_users_counter,
[{#reg_users_counter{vhost = LServer, count = '$1'},
[],
['$1']}]),
case Query of
[Count] ->
Count;
_ -> 0
end.
%% @spec (Server, [{prefix, Prefix}]) -> Users_Number
%% Server = string()
%% Prefix = string()
%% Users_Number = integer()
get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix) ->
Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
length(Set);
get_vh_registered_users_number(Server, _) ->
get_vh_registered_users_number(Server).
%% @spec (User, Server) -> Password | false
%% User = string()
%% Server = string()
%% Password = string()
get_password(User, Server) ->
try
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, passwd, US) of
[#passwd{password = ""} = Passwd] ->
{base64:decode(Passwd#passwd.storedkey),
base64:decode(Passwd#passwd.serverkey),
base64:decode(Passwd#passwd.salt),
Passwd#passwd.iterationcount};
[#passwd{password = Password}] ->
Password;
_ ->
false
end
catch
_ ->
false
end.
%% @spec (User, Server) -> Password | nil()
%% User = string()
%% Server = string()
%% Password = string()
get_password_s(User, Server) ->
try
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, passwd, US) of
[#passwd{password = Password}] ->
Password;
_ ->
[]
end
catch
_ ->
[]
end.
%% @spec (User, Server) -> true | false | {error, Error}
%% User = string()
%% Server = string()
is_user_exists(User, Server) ->
try
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
[] ->
false;
[_] ->
true;
Other ->
{error, Other}
end
catch
_ ->
false
end.
%% @spec (User, Server) -> ok
%% User = string()
%% Server = string()
%% @doc Remove user.
%% Note: it returns ok even if there was some problem removing the user.
remove_user(User, Server) ->
try
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
gen_storage:delete(LServer, {passwd, US}),
mnesia:dirty_update_counter(reg_users_counter,
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1)
end,
gen_storage:transaction(LServer, passwd, F),
ok
catch
_ ->
ok
end.
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
%% User = string()
%% Server = string()
%% Password = string()
%% @doc Remove user if the provided password is correct.
remove_user(User, Server, Password) ->
try
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
case gen_storage:read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
case is_password_scram_valid(Password, Passwd) of
true ->
gen_storage:delete(LServer, {passwd, US}),
mnesia:dirty_update_counter(reg_users_counter,
LServer, -1),
ok;
false ->
not_allowed
end;
[#passwd{password = Password}] ->
gen_storage:delete(LServer, {passwd, US}),
mnesia:dirty_update_counter(reg_users_counter,
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1),
ok;
_ ->
not_exists
end
end,
case gen_storage:transaction(LServer, passwd, F) of
{atomic, ok} ->
ok;
{atomic, Res} ->
Res
end
catch
_ ->
bad_request
end.
%%%
%%% SCRAM
%%%
%% The passwords are stored scrammed in the table either if the option says so,
%% or if at least the first password is empty.
is_scrammed(Host) ->
case action_password_format(Host) of
scram -> true;
must_scram -> true;
plain -> false;
forced_scram -> true
end.
action_password_format(HostB) ->
OptionScram = is_option_scram(),
case {OptionScram, get_format_first_element(HostB)} of
{true, scram} -> scram;
{true, any} -> scram;
{true, plain} -> must_scram;
{false, plain} -> plain;
{false, any} -> plain;
{false, scram} -> forced_scram
end.
get_format_first_element(HostB) ->
case gen_storage:dirty_select(HostB, passwd, []) of
[] -> any;
[#passwd{password = ""} | _] -> scram;
[#passwd{} | _] -> plain
end.
is_option_scram() ->
scram == ejabberd_config:get_local_option({auth_password_format, ?MYNAME}).
maybe_alert_password_scrammed_without_option(Host) ->
case is_scrammed(Host) andalso not is_option_scram() of
true ->
?ERROR_MSG("Some passwords were stored in the database as SCRAM, "
"but 'auth_password_format' is not configured 'scram'. "
"The option will now be considered to be 'scram'.", []);
false ->
ok
end.
maybe_scram_passwords(Host) ->
case action_password_format(Host) of
must_scram -> scram_passwords(Host);
_ -> ok
end.
scram_passwords(Host) ->
Backend =
case ejabberd_config:get_local_option({auth_storage, Host}) of
undefined -> mnesia;
B -> B
end,
scram_passwords(Host, Backend).
scram_passwords(Host, mnesia) ->
?INFO_MSG("Converting the passwords stored in mnesia for host ~p into SCRAM bits", [Host]),
gen_storage_migration:migrate_mnesia(
Host, passwd,
[{passwd, [user_host, password, storedkey, serverkey, salt, iterationcount],
fun(#passwd{password = Password} = Passwd) ->
password_to_scram(Password, Passwd)
end}]);
scram_passwords(Host, odbc) ->
?INFO_MSG("Converting the passwords stored in odbc for host ~p into SCRAM bits", [Host]),
gen_storage_migration:migrate_odbc(
Host, [passwd],
[{"passwd", ["user", "host", "password", "storedkey", "serverkey", "salt", "iterationcount"],
fun(_, User, Host2, Password, _Storedkey, _Serverkey, _Iterationcount, _Salt) ->
password_to_scram(Password, #passwd{user_host = {User, Host2}})
end}]).
password_to_scram(Password, Passwd) ->
password_to_scram(Password, Passwd, ?SCRAM_DEFAULT_ITERATION_COUNT).
password_to_scram(Password, Passwd, IterationCount) ->
Salt = crypto:rand_bytes(?SALT_LENGTH),
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
ServerKey = scram:server_key(SaltedPassword),
Passwd#passwd{password = "",
storedkey = base64:encode(StoredKey),
salt = base64:encode(Salt),
iterationcount = IterationCount,
serverkey = base64:encode(ServerKey)}.
is_password_scram_valid(Password, Passwd) ->
IterationCount = Passwd#passwd.iterationcount,
Salt = base64:decode(Passwd#passwd.salt),
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
(base64:decode(Passwd#passwd.storedkey) == StoredKey).
update_table(Host, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, passwd,
[{passwd, [us, password],
fun({passwd, {User, _Host}, Password}) ->
case is_list(Password) of
true ->
#passwd{user_host = {User, Host},
password = Password};
false ->
#passwd{user_host = {User, Host},
password = "",
storedkey = Password#scram.storedkey,
serverkey = Password#scram.serverkey,
salt = Password#scram.salt,
iterationcount = Password#scram.iterationcount}
end
end}]);
update_table(Host, odbc) ->
gen_storage_migration:migrate_odbc(
Host, [passwd],
[{"users", ["username", "password"],
fun(_, User, Password) ->
#passwd{user_host = {User, Host},
password = Password}
end}]).
+986 -1279
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -6,7 +6,7 @@
%%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
+137 -522
View File
@@ -5,7 +5,7 @@
%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,22 +24,6 @@
%%%
%%%-------------------------------------------------------------------
%%% Database schema (version / storage / table)
%%%
%%% 2.1.x / mnesia / captcha
%%% id = string()
%%% pid = pid()
%%% key = string()
%%% tref = any()
%%% args = any()
%%%
%%% 3.0.0-alpha / ets / captcha
%%% id = string()
%%% pid = pid()
%%% key = string()
%%% tref = any()
%%% args = any()
-module(ejabberd_captcha).
-behaviour(gen_server).
@@ -51,39 +35,32 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([create_captcha/5, build_captcha_html/2, check_captcha/2,
process_reply/1, process/2, is_feature_available/0,
create_captcha_x/4, create_captcha_x/5]).
-include_lib("exmpp/include/exmpp.hrl").
-export([create_captcha/6, build_captcha_html/2, check_captcha/2,
process_reply/1, process/2, is_feature_available/0]).
-include("jlib.hrl").
-include("ejabberd.hrl").
-include("web/ejabberd_http.hrl").
-define(VFIELD(Type, Var, Value),
#xmlel{name = 'field',
attrs = [
#xmlattr{name = <<"type">>,
value = Type
},
#xmlattr{name = <<"var">>,
value = Var
}
],
children = [
#xmlel{name = 'value',
children = [Value]
}
]}).
{xmlelement, "field", [{"type", Type}, {"var", Var}],
[{xmlelement, "value", [], [Value]}]}).
-define(CAPTCHA_TEXT(Lang), list_to_binary(translate:translate(Lang, "Enter the text you see"))).
-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")).
-define(CAPTCHA_LIFETIME, 120000). % two minutes
-define(RPC_TIMEOUT, 5000).
-record(state, {}).
-record(captcha, {id, pid, key, tref, args}).
-define(T(S),
case catch mnesia:transaction(fun() -> S end) of
{atomic, Res} ->
Res;
{_, Reason} ->
?ERROR_MSG("mnesia transaction failed: ~p", [Reason]),
{error, Reason}
end).
%%====================================================================
%% API
%%====================================================================
@@ -94,228 +71,47 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
create_captcha(SID, From, To, Lang, Args)
when is_binary(Lang), is_binary(SID) ->
create_captcha(Id, SID, From, To, Lang, Args)
when is_list(Id), is_list(Lang), is_list(SID),
is_record(From, jid), is_record(To, jid) ->
case create_image() of
{ok, Type, Key, Image} ->
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
B64Image = list_to_binary(jlib:encode_base64(binary_to_list(Image))),
JID = exmpp_jid:to_list(From),
CID = list_to_binary(["sha1+", sha:sha(Image), "@bob.xmpp.org"]),
% Data = {xmlelement, "data",
% [{"xmlns", ?NS_BOB}, {"cid", CID},
% {"max-age", "0"}, {"type", Type}],
% [{xmlcdata, B64Image}]},
Data =
#xmlel{
name = 'data',
ns = ?NS_BOB,
attrs = [
#xmlattr{name = <<"cid">>,
value = CID
},
#xmlattr{name = <<"max-age">>,
value = <<"0">>
},
#xmlattr{name = <<"type">>,
value = Type
}
],
children = [#xmlcdata{cdata = B64Image}]},
% {xmlelement, "captcha", [{"xmlns", ?NS_CAPTCHA}],
% %% ?NS_DATA_FORMS is 'jabber:x:data'
% [{xmlelement, "x", [{"xmlns", "jabber:x:data"}, {"type", "form"}],
% [?VFIELD("hidden", "FORM_TYPE", {xmlcdata, ?NS_CAPTCHA}),
% ?VFIELD("hidden", "from", {xmlcdata, exmpp_jid:to_list(To)}),
% ?VFIELD("hidden", "challenge", {xmlcdata, Id}),
% ?VFIELD("hidden", "sid", {xmlcdata, SID}),
% {xmlelement, "field", [{"var", "ocr"}, {"label", ?CAPTCHA_TEXT(Lang)}],
% [{xmlelement, "media", [{"xmlns", ?NS_DATA_FORMS_MEDIA_s}],
% [{xmlelement, "uri", [{"type", Type}],
% [{xmlcdata, "cid:" ++ CID}]}]}]}]}]},
%% TODO : kael : write exmpp_captcha.erl
B64Image = jlib:encode_base64(binary_to_list(Image)),
JID = jlib:jid_to_string(From),
CID = "sha1+" ++ sha:sha(Image) ++ "@bob.xmpp.org",
Data = {xmlelement, "data",
[{"xmlns", ?NS_BOB}, {"cid", CID},
{"max-age", "0"}, {"type", Type}],
[{xmlcdata, B64Image}]},
Captcha =
#xmlel{
name = 'captcha',
ns = ?NS_CAPTCHA,
children = [
#xmlel{name = 'x',
ns = ?NS_DATA_FORMS_s,
attrs = [
#xmlattr{name = <<"type">>,
value = <<"form">>
}
],
children = [
?VFIELD(<<"hidden">>, <<"FORM_TYPE">>, #xmlcdata{cdata = ?NS_CAPTCHA_b}),
?VFIELD(<<"hidden">>, <<"from">>, #xmlcdata{cdata = exmpp_jid:to_binary(To)}),
?VFIELD(<<"hidden">>, <<"challenge">>, #xmlcdata{cdata = list_to_binary(Id)}),
?VFIELD(<<"hidden">>, <<"sid">>, #xmlcdata{cdata = SID}),
#xmlel{name = 'field',
attrs = [
#xmlattr{name = <<"var">>,
value = <<"ocr">>
},
#xmlattr{name = <<"label">>,
value = ?CAPTCHA_TEXT(Lang)
}
],
children = [
#xmlel{ns = ?NS_DATA_FORMS, name = 'required'},
#xmlel{name = 'media',
ns = ?NS_DATA_FORMS_MEDIA_s,
children = [
#xmlel{name = 'uri',
attrs = [
#xmlattr{name = <<"type">>,
value = Type
}
],
children = [
#xmlcdata{cdata = list_to_binary(["cid:", CID])}
]
}
]
}
]
}
]
}
]
},
{xmlelement, "captcha", [{"xmlns", ?NS_CAPTCHA}],
[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
[?VFIELD("hidden", "FORM_TYPE", {xmlcdata, ?NS_CAPTCHA}),
?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, "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"),
BodyString = io_lib:format(BodyString1, [JID, get_url(Id)]),
Body =
#xmlel{name = 'body',
children = [
#xmlcdata{cdata = list_to_binary(BodyString)
}
]
},
OOB =
#xmlel{name = 'x',
ns = ?NS_OOBD_X_s,
children = [
#xmlel{name = 'url',
children = [
#xmlcdata{cdata = list_to_binary(get_url(Id))}
]
}
]
},
%Body = {xmlelement, "body", [],
% [{xmlcdata, BodyString}]},
%OOB = {xmlelement, "x", [{"xmlns", ?NS_OOBD_X_s}],
% [{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]},
Body = {xmlelement, "body", [],
[{xmlcdata, BodyString}]},
OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}],
[{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key,
tref=Tref, args=Args}),
{ok, Id, [Body, OOB, Captcha, Data]};
case ?T(mnesia:write(#captcha{id=Id, pid=self(), key=Key,
tref=Tref, args=Args})) of
ok ->
{ok, [Body, OOB, Captcha, Data]};
_Err ->
error
end;
_Err ->
error
end.
create_captcha_x(SID, To, Lang, HeadEls) ->
create_captcha_x(SID, To, Lang, HeadEls, []).
create_captcha_x(SID, To, Lang, HeadEls, TailEls) ->
case create_image() of
{ok, Type, Key, Image} ->
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
B64Image = list_to_binary(jlib:encode_base64(binary_to_list(Image))),
CID = list_to_binary(["sha1+", sha:sha(Image), "@bob.xmpp.org"]),
Data =
#xmlel{
name = 'data',
ns = ?NS_BOB,
attrs = [
#xmlattr{name = <<"cid">>,
value = CID
},
#xmlattr{name = <<"max-age">>,
value = <<"0">>
},
#xmlattr{name = <<"type">>,
value = Type
}
],
children = [#xmlcdata{cdata = B64Image}]},
HelpTxt = translate:translate(
Lang,
"If you don't see the CAPTCHA image here, "
"visit the web page."),
Imageurl = list_to_binary(get_url(Id ++ "/image")),
Captcha =
#xmlel{name = 'x',
ns = ?NS_DATA_FORMS_s,
attrs = [
#xmlattr{name = <<"type">>,
value = <<"form">>
}
],
children = [
?VFIELD(<<"hidden">>, <<"FORM_TYPE">>, #xmlcdata{cdata = ?NS_CAPTCHA_b}) | HeadEls] ++ [
#xmlel{ns = ?NS_DATA_FORMS, name = 'field', attrs =
[?XMLATTR(<<"type">>, <<"fixed">>), ?XMLATTR(<<"label">>, HelpTxt)]},
?VFIELD(<<"hidden">>, <<"captchahidden">>, #xmlcdata{cdata = <<"workaround-for-psi">>}),
#xmlel{name = 'field',
attrs = [
#xmlattr{name = <<"type">>,
value = <<"text-single">>
},
#xmlattr{name = <<"label">>,
value = list_to_binary(translate:translate(Lang, "CAPTCHA web page"))
},
#xmlattr{name = <<"var">>,
value = <<"url">>
}
],
children = [
#xmlel{name = 'value',
children = [#xmlcdata{cdata = Imageurl}]
}
]},
?VFIELD(<<"hidden">>, <<"from">>, #xmlcdata{cdata = exmpp_jid:to_binary(To)}),
?VFIELD(<<"hidden">>, <<"challenge">>, #xmlcdata{cdata = list_to_binary(Id)}),
?VFIELD(<<"hidden">>, <<"sid">>, #xmlcdata{cdata = SID}),
#xmlel{name = 'field',
attrs = [
#xmlattr{name = <<"var">>,
value = <<"ocr">>
},
#xmlattr{name = <<"label">>,
value = ?CAPTCHA_TEXT(Lang)
}
],
children = [
#xmlel{ns = ?NS_DATA_FORMS, name = 'required'},
#xmlel{name = 'media',
ns = ?NS_DATA_FORMS_MEDIA_s,
children = [
#xmlel{name = 'uri',
attrs = [
#xmlattr{name = <<"type">>,
value = Type
}
],
children = [
#xmlcdata{cdata = list_to_binary(["cid:", CID])}
]
}
]
}
]
}
] ++ TailEls
},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
ets:insert(captcha, #captcha{id=Id, key=Key, tref=Tref}),
{ok, [Captcha, Data]};
_ ->
error
end.
%% @spec (Id::string(), Lang::string()) -> {FormEl, {ImgEl, TextEl, IdEl, KeyEl}} | captcha_not_found
%% where FormEl = xmlelement()
%% ImgEl = xmlelement()
@@ -323,106 +119,30 @@ create_captcha_x(SID, To, Lang, HeadEls, TailEls) ->
%% IdEl = xmlelement()
%% KeyEl = xmlelement()
build_captcha_html(Id, Lang) ->
case lookup_captcha(Id) of
{ok, _} ->
%ImgEl = {xmlelement, "img", [{"src", get_url(Id ++ "/image")}], []},
ImgEl =
#xmlel{name = 'img',
attrs = [
#xmlattr{name = <<"src">>,
value = list_to_binary(get_url(Id ++ "/image"))
}
]
},
%TextEl = {xmlcdata, ?CAPTCHA_TEXT(Lang)},
TextEl = #xmlcdata{cdata = ?CAPTCHA_TEXT(Lang)},
%IdEl = {xmlelement, "input", [{"type", "hidden"},
% {"name", "id"},
% {"value", Id}], []},
IdEl =
#xmlel{name = 'input',
attrs = [
#xmlattr{name = <<"type">>,
value = <<"hidden">>
},
#xmlattr{name = <<"name">>,
value = <<"id">>
},
#xmlattr{name = <<"value">>,
value = list_to_binary(Id)
}
]
},
%KeyEl = {xmlelement, "input", [{"type", "text"},
% {"name", "key"},
% {"size", "10"}], []},
KeyEl =
#xmlel{name = 'input',
attrs = [
#xmlattr{name = <<"type">>,
value = <<"text">>
},
#xmlattr{name = <<"name">>,
value = <<"key">>
},
#xmlattr{name = <<"size">>,
value = <<"10">>
}
]
},
%FormEl = {xmlelement, "form", [{"action", get_url(Id)},
% {"name", "captcha"},
% {"method", "POST"}],
% [ImgEl,
% {xmlelement, "br", [], []},
% TextEl,
% {xmlelement, "br", [], []},
% IdEl,
% KeyEl,
% {xmlelement, "br", [], []},
% {xmlelement, "input", [{"type", "submit"},
% {"name", "enter"},
% {"value", "OK"}], []}
% ]},
FormEl =
#xmlel{name = 'form',
attrs = [
#xmlattr{name = <<"action">>,
value = list_to_binary(get_url(Id))
},
#xmlattr{name = <<"name">>,
value = <<"captcha">>
},
#xmlattr{name = <<"method">>,
value = <<"POST">>
}
],
children = [
ImgEl,
#xmlel{name = 'br'
},
TextEl,
#xmlel{name = 'br'
},
IdEl,
KeyEl,
#xmlel{name = 'br'
},
#xmlel{name = 'input',
attrs = [
#xmlattr{name = <<"type">>,
value = <<"submit">>
},
#xmlattr{name = <<"name">>,
value = <<"enter">>
},
#xmlattr{name = <<"value">>,
value = <<"OK">>
}
]
}
]
},
case mnesia:dirty_read(captcha, Id) of
[#captcha{}] ->
ImgEl = {xmlelement, "img", [{"src", get_url(Id ++ "/image")}], []},
TextEl = {xmlcdata, ?CAPTCHA_TEXT(Lang)},
IdEl = {xmlelement, "input", [{"type", "hidden"},
{"name", "id"},
{"value", Id}], []},
KeyEl = {xmlelement, "input", [{"type", "text"},
{"name", "key"},
{"size", "10"}], []},
FormEl = {xmlelement, "form", [{"action", get_url(Id)},
{"name", "captcha"},
{"method", "POST"}],
[ImgEl,
{xmlelement, "br", [], []},
TextEl,
{xmlelement, "br", [], []},
IdEl,
KeyEl,
{xmlelement, "br", [], []},
{xmlelement, "input", [{"type", "submit"},
{"name", "enter"},
{"value", "OK"}], []}
]},
{FormEl, {ImgEl, TextEl, IdEl, KeyEl}};
_ ->
captcha_not_found
@@ -430,71 +150,67 @@ build_captcha_html(Id, Lang) ->
%% @spec (Id::string(), ProvidedKey::string()) -> captcha_valid | captcha_non_valid | captcha_not_found
check_captcha(Id, ProvidedKey) ->
case string:tokens(Id, "-") of
[_, NodeID] ->
case ejabberd_cluster:get_node_by_id(NodeID) of
Node when Node == node() ->
do_check_captcha(Id, ProvidedKey);
Node ->
case catch rpc:call(Node, ?MODULE, check_captcha,
[Id, ProvidedKey], ?RPC_TIMEOUT) of
{'EXIT', _} ->
captcha_not_found;
{badrpc, _} ->
captcha_not_found;
Res ->
Res
end
end;
_ ->
captcha_not_found
end.
?T(case mnesia:read(captcha, Id, write) of
[#captcha{pid=Pid, args=Args, key=StoredKey, tref=Tref}] ->
mnesia:delete({captcha, Id}),
erlang:cancel_timer(Tref),
if StoredKey == ProvidedKey ->
Pid ! {captcha_succeed, Args},
captcha_valid;
true ->
Pid ! {captcha_failed, Args},
captcha_non_valid
end;
_ ->
captcha_not_found
end).
process_reply(El) ->
case exmpp_xml:get_element(El, x) of
undefined ->
process_reply({xmlelement, "captcha", _, _} = El) ->
case xml:get_subtag(El, "x") of
false ->
{error, malformed};
Xdata ->
Fields = jlib:parse_xdata_submit(Xdata),
case catch {proplists:get_value("challenge", Fields),
proplists:get_value("ocr", Fields)} of
case {proplists:get_value("challenge", Fields),
proplists:get_value("ocr", Fields)} of
{[Id|_], [OCR|_]} ->
case check_captcha(Id, OCR) of
captcha_valid ->
ok;
captcha_non_valid ->
{error, bad_match};
captcha_not_found ->
{error, not_found}
end;
?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},
ok;
true ->
Pid ! {captcha_failed, Args},
{error, bad_match}
end;
_ ->
{error, not_found}
end);
_ ->
{error, malformed}
end
end.
end;
process_reply(_) ->
{error, malformed}.
process(_Handlers, #request{method='GET', lang=Lang, path=[_, Id]}) ->
case build_captcha_html(Id, Lang) of
{FormEl, CaptchaTuple} when is_tuple(CaptchaTuple) ->
{FormEl, _} when is_tuple(FormEl) ->
Form =
%{xmlelement, "div", [{"align", "center"}],
%[FormEl]},
#xmlel{name = 'div',
attrs = [
#xmlattr{name = <<"align">>,
value = <<"center">>
}
],
children = [FormEl]
},
{xmlelement, "div", [{"align", "center"}],
[FormEl]},
ejabberd_web:make_xhtml([Form]);
captcha_not_found ->
ejabberd_web:error(not_found)
end;
process(_Handlers, #request{method='GET', path=[_, Id, "image"]}) ->
case lookup_captcha(Id) of
{ok, #captcha{key=Key}} ->
case mnesia:dirty_read(captcha, Id) of
[#captcha{key=Key}] ->
case create_image(Key) of
{ok, Type, _, Img} ->
{200,
@@ -514,15 +230,10 @@ process(_Handlers, #request{method='POST', q=Q, lang=Lang, path=[_, Id]}) ->
case check_captcha(Id, ProvidedKey) of
captcha_valid ->
Form =
%{xmlelement, "p", [],
% [{xmlcdata,
% translate:translate(Lang, "The captcha is valid.")
% }]},
#xmlel{name = 'p',
children = [
#xmlcdata{cdata = list_to_binary(translate:translate(Lang, "The captcha is valid."))}
]
},
{xmlelement, "p", [],
[{xmlcdata,
translate:translate(Lang, "The captcha is valid.")
}]},
ejabberd_web:make_xhtml([Form]);
captcha_non_valid ->
ejabberd_web:error(not_allowed);
@@ -533,12 +244,15 @@ process(_Handlers, #request{method='POST', q=Q, lang=Lang, path=[_, Id]}) ->
process(_Handlers, _Request) ->
ejabberd_web:error(not_found).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([]) ->
mnesia:delete_table(captcha),
ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
mnesia:create_table(captcha,
[{ram_copies, [node()]},
{attributes, record_info(fields, captcha)}]),
mnesia:add_table_copy(captcha, node(), ram_copies),
check_captcha_setup(),
{ok, #state{}}.
@@ -550,17 +264,13 @@ handle_cast(_Msg, State) ->
handle_info({remove_id, Id}, State) ->
?DEBUG("captcha ~p timed out", [Id]),
case ets:lookup(captcha, Id) of
[#captcha{args=Args, pid=Pid}] ->
if is_pid(Pid) ->
Pid ! {captcha_failed, Args};
true ->
ok
end,
ets:delete(captcha, Id);
_ ->
ok
end,
_ = ?T(case mnesia:read(captcha, Id, write) of
[#captcha{args=Args, pid=Pid}] ->
Pid ! {captcha_failed, Args},
mnesia:delete({captcha, Id});
_ ->
ok
end),
{noreply, State};
handle_info(_Info, State) ->
@@ -578,7 +288,7 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
%% Function: create_image() -> {ok, Type, Key, Image} | {error, Reason}
%% Type = "image/png" | "image/jpeg" | "image/gif"
%% Key = binary()
%% Key = string()
%% Image = binary()
%% Reason = atom()
%%--------------------------------------------------------------------
@@ -592,11 +302,11 @@ create_image(Key) ->
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
case cmd(Cmd) of
{ok, <<16#89, $P, $N, $G, $\r, $\n, 16#1a, $\n, _/binary>> = Img} ->
{ok, <<"image/png">>, Key, Img};
{ok, "image/png", Key, Img};
{ok, <<16#ff, 16#d8, _/binary>> = Img} ->
{ok, <<"image/jpeg">>, Key, Img};
{ok, "image/jpeg", Key, Img};
{ok, <<$G, $I, $F, $8, X, $a, _/binary>> = Img} when X==$7; X==$9 ->
{ok, <<"image/gif">>, Key, Img};
{ok, "image/gif", Key, Img};
{error, enodata = Reason} ->
?ERROR_MSG("Failed to process output from \"~s\". "
"Maybe ImageMagick's Convert program is not installed.",
@@ -618,63 +328,17 @@ get_prog_name() ->
FileName when is_list(FileName) ->
FileName;
_ ->
?DEBUG("The option captcha_cmd is not configured, but some "
"module wants to use the CAPTCHA feature.", []),
throw({error, option_not_configured_captcha_cmd})
""
end.
%% @doc (Str::string()) -> string()
get_url(Str) ->
CaptchaHost = ejabberd_config:get_local_option(captcha_host),
case string:tokens(CaptchaHost, ":") of
[Host] ->
case ejabberd_config:get_local_option(captcha_host) of
Host when is_list(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).
%%--------------------------------------------------------------------
%% Function: cmd(Cmd) -> Data | {error, Reason}
%% Cmd = string()
@@ -724,10 +388,9 @@ return(Port, TRef, Result) ->
Result.
is_feature_enabled() ->
try get_prog_name() of
case get_prog_name() of
"" -> false;
Prog when is_list(Prog) -> true
catch
_:_ -> false
end.
is_feature_available() ->
@@ -748,51 +411,3 @@ check_captcha_setup() ->
false ->
ok
end.
lookup_captcha(Id) ->
case string:tokens(Id, "-") of
[_, NodeID] ->
case ejabberd_cluster:get_node_by_id(NodeID) of
Node when Node == node() ->
case ets:lookup(captcha, Id) of
[C] ->
{ok, C};
_ ->
{error, enoent}
end;
Node ->
case catch rpc:call(Node, ets, lookup,
[captcha, Id], ?RPC_TIMEOUT) of
[C] ->
{ok, C};
_ ->
{error, enoent}
end
end;
_ ->
{error, enoent}
end.
do_check_captcha(Id, ProvidedKey) ->
case ets:lookup(captcha, Id) of
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
ets:delete(captcha, Id),
erlang:cancel_timer(Tref),
if ValidKey == ProvidedKey ->
if is_pid(Pid) ->
Pid ! {captcha_succeed, Args};
true ->
ok
end,
captcha_valid;
true ->
if is_pid(Pid) ->
Pid ! {captcha_failed, Args};
true ->
ok
end,
captcha_non_valid
end;
_ ->
captcha_not_found
end.
+3 -5
View File
@@ -5,7 +5,7 @@
%%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,8 +46,6 @@ config() ->
check_database_modules() ->
[check_database_module(M)||M<-get_db_used()].
check_database_module(host) ->
ok;
check_database_module(odbc) ->
check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]);
check_database_module(mysql) ->
@@ -88,8 +86,8 @@ get_db_used() ->
DBs = lists:foldr(
fun([Domain, DB], Acc) ->
case check_odbc_option(
ejabberd_config:get_local_option
({auth_method, Domain})) of
ejabberd_config:get_local_option(
{auth_method, Domain})) of
true -> [get_db_type(DB)|Acc];
_ -> Acc
end
-177
View File
@@ -1,177 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_cluster.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Description :
%%%
%%% Created : 2 Apr 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(ejabberd_cluster).
-behaviour(gen_server).
%% API
-export([start_link/0, get_node/1, get_node_new/1, announce/0,
node_id/0, get_node_by_id/1, get_nodes/0, rehash_timeout/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-define(HASHTBL, nodes_hash).
-define(HASHTBL_NEW, nodes_hash_new).
-define(POINTS, 16).
-define(REHASH_TIMEOUT, 5000).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
get_node(Key) ->
Hash = erlang:phash2(Key),
get_node_by_hash(?HASHTBL, Hash).
get_node_new(Key) ->
Hash = erlang:phash2(Key),
get_node_by_hash(?HASHTBL_NEW, Hash).
get_nodes() ->
%% TODO
mnesia:system_info(running_db_nodes).
announce() ->
gen_server:call(?MODULE, announce, infinity).
node_id() ->
integer_to_list(erlang:phash2(node())).
rehash_timeout() ->
?REHASH_TIMEOUT.
get_node_by_id(NodeID) when is_list(NodeID) ->
case catch list_to_existing_atom(NodeID) of
{'EXIT', _} ->
node();
Res ->
get_node_by_id(Res)
end;
get_node_by_id(NodeID) ->
case global:whereis_name(NodeID) of
Pid when is_pid(Pid) ->
node(Pid);
_ ->
node()
end.
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([]) ->
net_kernel:monitor_nodes(true, [{node_type, visible}]),
ets:new(?HASHTBL, [named_table, public, ordered_set]),
ets:new(?HASHTBL_NEW, [named_table, public, ordered_set]),
register_node(),
AllNodes = mnesia:system_info(running_db_nodes),
OtherNodes = case AllNodes of
[_] ->
AllNodes;
_ ->
AllNodes -- [node()]
end,
append_nodes(?HASHTBL, OtherNodes),
append_nodes(?HASHTBL_NEW, AllNodes),
{ok, #state{}}.
handle_call(announce, _From, State) ->
case mnesia:system_info(running_db_nodes) of
[_MyNode] ->
ok;
Nodes ->
OtherNodes = Nodes -- [node()],
lists:foreach(
fun(Node) ->
{?MODULE, Node} ! {node_ready, node()}
end, OtherNodes),
?INFO_MSG("waiting for migration from nodes: ~w",
[OtherNodes]),
timer:sleep(?REHASH_TIMEOUT),
append_node(?HASHTBL, node())
end,
{reply, ok, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({node_ready, Node}, State) ->
?INFO_MSG("node ~p is ready, starting migration", [Node]),
append_node(?HASHTBL_NEW, Node),
ejabberd_hooks:run(node_hash_update, [?REHASH_TIMEOUT]),
timer:sleep(?REHASH_TIMEOUT),
?INFO_MSG("adding node ~p to hash", [Node]),
append_node(?HASHTBL, Node),
{noreply, State};
handle_info({nodedown, Node, _}, State) ->
?INFO_MSG("node ~p goes down", [Node]),
delete_node(?HASHTBL, Node),
delete_node(?HASHTBL_NEW, Node),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
append_nodes(Tab, Nodes) ->
lists:foreach(
fun(Node) ->
append_node(Tab, Node)
end, Nodes).
append_node(Tab, Node) ->
lists:foreach(
fun(I) ->
Hash = erlang:phash2({I, Node}),
ets:insert(Tab, {Hash, Node})
end, lists:seq(1, ?POINTS)).
delete_node(Tab, Node) ->
lists:foreach(
fun(I) ->
Hash = erlang:phash2({I, Node}),
ets:delete(Tab, Hash)
end, lists:seq(1, ?POINTS)).
get_node_by_hash(Tab, Hash) ->
NodeHash = case ets:next(Tab, Hash) of
'$end_of_table' ->
ets:first(Tab);
NH ->
NH
end,
if NodeHash == '$end_of_table' ->
erlang:error(no_running_nodes);
true ->
case ets:lookup(Tab, NodeHash) of
[] ->
get_node_by_hash(Tab, Hash);
[{_, Node}] ->
Node
end
end.
register_node() ->
global:register_name(list_to_atom(node_id()), self()).
+7 -9
View File
@@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -356,14 +356,15 @@ get_tags_commands() ->
%% @doc Check access is allowed to that command.
%% At least one AccessCommand must be satisfied.
%% It may throw {error, Error} where:
%% Error = account_unprivileged | invalid_account_data
%% Error = account_unprivileged | invalid_account_data | no_auth_provided
check_access_commands([], _Auth, _Method, _Command, _Arguments) ->
ok;
check_access_commands(AccessCommands, Auth, Method, Command, Arguments) ->
{ok, User, Server} = check_auth(Auth),
AccessCommandsAllowed =
lists:filter(
fun({Access, Commands, ArgumentRestrictions}) ->
case check_access(Access, Auth) of
case check_access(Access, User, Server) of
true ->
check_access_command(Commands, Command, ArgumentRestrictions,
Method, Arguments);
@@ -378,7 +379,7 @@ check_access_commands(AccessCommands, Auth, Method, Command, Arguments) ->
end.
check_auth(noauth) ->
no_auth_provided;
throw({error, no_auth_provided});
check_auth({User, Server, Password}) ->
%% Check the account exists and password is valid
AccountPass = ejabberd_auth:get_password_s(User, Server),
@@ -393,12 +394,9 @@ get_md5(AccountPass) ->
lists:flatten([io_lib:format("~.16B", [X])
|| X <- binary_to_list(crypto:md5(AccountPass))]).
check_access(all, _) ->
true;
check_access(Access, Auth) ->
{ok, User, Server} = check_auth(Auth),
check_access(Access, User, Server) ->
%% Check this user has access permission
case acl:match_rule(Server, Access, exmpp_jid:make(User, Server, "")) of
case acl:match_rule(Server, Access, jlib:make_jid(User, Server, "")) of
allow -> true;
deny -> false
end.
+1 -1
View File
@@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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 -253
View File
@@ -5,7 +5,7 @@
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,32 +27,16 @@
-module(ejabberd_config).
-author('alexey@process-one.net').
-export([start/0, load_file/1, get_host_option/2,
-export([start/0, load_file/1,
add_global_option/2, add_local_option/2,
mne_add_local_option/2, mne_del_local_option/1,
del_global_option/1, del_local_option/1,
get_global_option/1, get_local_option/1]).
-export([for_host/1
,configure_host/2
,delete_host/1
]).
-export([search/1]).
-export([get_vh_by_auth_method/1]).
-export([is_file_readable/1]).
-include("ejabberd.hrl").
-include("ejabberd_config.hrl").
-include_lib("kernel/include/file.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-record(state, {opts = [],
hosts = [],
override_local = false,
override_global = false,
override_acls = false}).
%% @type macro() = {macro_key(), macro_value()}
@@ -117,14 +101,15 @@ get_plain_terms_file(File1) ->
case file:consult(File) of
{ok, Terms} ->
include_config_files(Terms);
{error, {LineNumber, erl_parse, _ParseMessage} = Reason} ->
ExitText = describe_config_problem(File, Reason, LineNumber),
?ERROR_MSG(ExitText, []),
exit_or_halt(ExitText);
{error, {_LineNumber, erl_parse, _ParseMessage} = Reason} ->
ExitText = lists:flatten(File ++ " approximately in the line "
++ file:format_error(Reason)),
?ERROR_MSG("Problem loading ejabberd config file ~n~s", [ExitText]),
exit(ExitText);
{error, Reason} ->
ExitText = describe_config_problem(File, Reason),
?ERROR_MSG(ExitText, []),
exit_or_halt(ExitText)
ExitText = lists:flatten(File ++ ": " ++ file:format_error(Reason)),
?ERROR_MSG("Problem loading ejabberd config file ~n~s", [ExitText]),
exit(ExitText)
end.
%% @doc Convert configuration filename to absolute path.
@@ -167,16 +152,7 @@ search_hosts(Term, State) ->
end.
add_hosts_to_option(Hosts, State) ->
PrepHosts1 = normalize_hosts(Hosts),
PrepHosts = ensure_localhost_is_first(PrepHosts1),
mnesia:transaction(
fun() ->
lists:foreach(
fun(H) ->
mnesia:write(#local_config{key = {H, host},
value = []})
end, PrepHosts)
end),
PrepHosts = normalize_hosts(Hosts),
add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}).
normalize_hosts(Hosts) ->
@@ -184,89 +160,15 @@ normalize_hosts(Hosts) ->
normalize_hosts([], PrepHosts) ->
lists:reverse(PrepHosts);
normalize_hosts([Host|Hosts], PrepHosts) ->
try
PrepHost = exmpp_stringprep:nodeprep(Host),
normalize_hosts(Hosts, [PrepHost|PrepHosts])
catch
_ ->
case jlib:nodeprep(Host) of
error ->
?ERROR_MSG("Can't load config file: "
"invalid host name [~p]", [Host]),
exit("invalid hostname")
exit("invalid hostname");
PrepHost ->
normalize_hosts(Hosts, [PrepHost|PrepHosts])
end.
%% @spec (Hosts::[string()]) -> [Localhost::string() | [string()]]
%% @doc Return the list where the first is surely "localhost".
ensure_localhost_is_first(Hosts) ->
case lists:all(fun is_list/1, Hosts) of
true ->
ensure_localhost_is_first1(Hosts);
false ->
?ERROR_MSG("This list of hosts is bad formed:~n~p", [Hosts]),
ensure_localhost_is_first1([])
end.
ensure_localhost_is_first1(["localhost" | _] = Hosts) ->
Hosts;
ensure_localhost_is_first1(Hosts) ->
case lists:member("localhost", Hosts) of
true ->
["localhost" | lists:delete("localhost", Hosts)];
false ->
?INFO_MSG("ejabberd added the default virtual host \"localhost\""
" to the list of hosts.", []),
["localhost" | Hosts]
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Errors reading the config file
describe_config_problem(Filename, Reason) ->
Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename),
Text2 = lists:flatten(" : " ++ file:format_error(Reason)),
ExitText = Text1 ++ Text2,
ExitText.
describe_config_problem(Filename, Reason, LineNumber) ->
Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename),
Text2 = lists:flatten(" approximately in the line "
++ file:format_error(Reason)),
ExitText = Text1 ++ Text2,
Lines = get_config_lines(Filename, LineNumber, 10, 3),
?ERROR_MSG("The following lines from your configuration file might be"
" relevant to the error: ~n~s", [Lines]),
ExitText.
get_config_lines(Filename, TargetNumber, PreContext, PostContext) ->
{ok, Fd} = file:open(Filename, [read]),
LNumbers = lists:seq(TargetNumber-PreContext, TargetNumber+PostContext),
NextL = io:get_line(Fd, no_prompt),
R = get_config_lines2(Fd, NextL, 1, LNumbers, []),
file:close(Fd),
R.
get_config_lines2(_Fd, eof, _CurrLine, _LNumbers, R) ->
lists:reverse(R);
get_config_lines2(_Fd, _NewLine, _CurrLine, [], R) ->
lists:reverse(R);
get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(Data) ->
NextL = io:get_line(Fd, no_prompt),
if
CurrLine >= NextWanted ->
Line2 = [integer_to_list(CurrLine), ": " | Data],
get_config_lines2(Fd, NextL, CurrLine+1, LNumbers, [Line2 | R]);
true ->
get_config_lines2(Fd, NextL, CurrLine+1, [NextWanted | LNumbers], R)
end.
%% If ejabberd isn't yet running in this node, then halt the node
exit_or_halt(ExitText) ->
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
[] ->
timer:sleep(1000),
halt(string:substr(ExitText, 1, 199));
[_] ->
exit(ExitText)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Support for 'include_config_file'
@@ -425,8 +327,6 @@ process_term(Term, State) ->
{host_config, Host, Terms} ->
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
State, Terms);
{clusterid, ClusterID} ->
add_option(clusterid, ClusterID, State);
{listen, Listeners} ->
Listeners2 =
lists:map(
@@ -443,8 +343,6 @@ process_term(Term, State) ->
add_option(outgoing_s2s_port, Port, State);
{outgoing_s2s_options, Methods, Timeout} ->
add_option(outgoing_s2s_options, {Methods, Timeout}, State);
{outgoing_s2s_local_address, Addr} ->
add_option(outgoing_s2s_local_address, Addr, State);
{s2s_dns_options, PropList} ->
add_option(s2s_dns_options, PropList, State);
{s2s_use_starttls, Port} ->
@@ -479,19 +377,20 @@ process_term(Term, State) ->
add_option(watchdog_large_heap, LH, State);
{registration_timeout, Timeout} ->
add_option(registration_timeout, Timeout, State);
{ejabberdctl_access_commands, ACs} ->
add_option(ejabberdctl_access_commands, ACs, State);
{captcha_cmd, Cmd} ->
add_option(captcha_cmd, Cmd, State);
{captcha_host, Host} ->
add_option(captcha_host, Host, State);
{ejabberdctl_access_commands, ACs} ->
add_option(ejabberdctl_access_commands, ACs, State);
{loglevel, Loglevel} ->
ejabberd_loglevel:set(Loglevel),
State;
{max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State);
{_Opt, _Val} ->
process_host_term(Term, global, State)
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts)
end.
process_host_term(Term, Host, State) ->
@@ -513,15 +412,6 @@ process_host_term(Term, Host, State) ->
State;
{odbc_server, ODBC_server} ->
add_option({odbc_server, Host}, ODBC_server, State);
{auth_method, Methods} ->
{Methods2, StorageOption} = replace_storage_auth(Host, Methods),
State2 = case StorageOption of
{auth_storage, Storage} ->
add_option({auth_storage, Host}, Storage, State);
undefined ->
State
end,
add_option({auth_method, Host}, Methods2, State2);
{Opt, Val} ->
add_option({Opt, Host}, Val, State)
end.
@@ -625,47 +515,20 @@ add_global_option(Opt, Val) ->
end).
add_local_option(Opt, Val) ->
mnesia:transaction(fun mne_add_local_option/2, [Opt, Val]).
mne_add_local_option(Opt, Val) ->
mnesia:write(#local_config{key = Opt,
value = Val}).
del_global_option(Opt) ->
mnesia:transaction(fun() ->
mnesia:delete({config, Opt})
end).
del_local_option(Opt) ->
mnesia:transaction(fun() ->
mnesia:delete({local_config, Opt})
mnesia:write(#local_config{key = Opt,
value = Val})
end).
get_global_option({Opt1, Host} = Opt) when is_list(Host) ->
case ets:lookup(config, Opt) of
[#config{value = Val}] ->
Val;
_ ->
get_global_option({Opt1, global})
end;
get_global_option(Opt) ->
case ets:lookup(config, Opt) of
[#config{value = Val}] when Opt == hosts ->
ensure_localhost_is_first(Val);
[#config{value = Val}] ->
Val;
_ ->
undefined
end.
get_local_option({Opt1, Host} = Opt) when is_list(Host) ->
case ets:lookup(local_config, Opt) of
[#local_config{value = Val}] ->
Val;
_ ->
get_local_option({Opt1, global})
end;
get_local_option(Opt) ->
case ets:lookup(local_config, Opt) of
[#local_config{value = Val}] ->
@@ -674,17 +537,6 @@ get_local_option(Opt) ->
undefined
end.
get_host_option(Host, Option) ->
case ets:lookup(local_config, {Option, Host}) of
[#local_config{value=V}] -> V;
_ -> undefined
end.
mne_del_local_option({_OptName, Host} = Opt) when is_list(Host) ->
mnesia:delete({local_config, Opt});
mne_del_local_option({Host, host} = Opt) when is_list(Host) ->
mnesia:delete({local_config, Opt}).
%% Return the list of hosts handled by a given module
get_vh_by_auth_method(AuthMethod) ->
mnesia:dirty_select(local_config,
@@ -703,86 +555,3 @@ is_file_readable(Path) ->
{error, _Reason} ->
false
end.
search(Pattern) ->
{atomic, Res} = mnesia:transaction(fun mnesia:select/2, [local_config, Pattern]),
Res.
for_host(Host) ->
mnesia:read({local_config, {Host, host}})
++ mnesia:select(local_config,
ets:fun2ms(fun (#local_config{key={_, H}})
when H =:= Host ->
object()
end))
++ acl:for_host(Host).
delete_host(Host) ->
mnesia_delete_objects(for_host(Host)),
ok.
configure_host(Host, Config) ->
HostExistenceTerm = {{Host, host}, []},
Records = host_terms_to_records(Host, [HostExistenceTerm | Config]),
mnesia_write_objects(Records),
ok.
host_terms_to_records(Host, Terms) ->
lists:foldl(fun (Term, Acc) ->
host_term_to_record(Term, Host, Acc)
end, [], Terms).
host_term_to_record({acl, ACLName, ACLData}, Host, Acc) ->
[acl:to_record(Host, ACLName, ACLData) | Acc];
host_term_to_record({access, RuleName, Rules}, Host, Acc) ->
[#config{key={access, RuleName, Host}, value=Rules} | Acc];
host_term_to_record({shaper, Name, Data}, Host, Acc) ->
[#config{key={shaper, Name, Host}, value=Data} | Acc];
host_term_to_record({host, _}, _Host, Acc) -> Acc;
host_term_to_record({hosts, _}, _Host, Acc) -> Acc;
host_term_to_record({{Host, host}, []}, Host, Acc) ->
[#local_config{key={Host, host}, value=[]} | Acc];
host_term_to_record({Opt, Val}, Host, Acc) when is_atom(Opt) ->
[#local_config{key={Opt, Host}, value=Val} | Acc].
mnesia_delete_objects(List) when is_list(List) ->
true = lists:all(fun (I) ->
ok =:= mnesia:delete_object(I)
end, List).
mnesia_write_objects(List) when is_list(List) ->
true = lists:all(fun (I) ->
ok =:= mnesia:write(I)
end, List).
%% Replace internal and odbc auth_methods with storage.
%% Only one storage type can be used, either internal or odbc.
replace_storage_auth(Host, Val) when not is_list(Val) ->
replace_storage_auth(Host, [Val]);
replace_storage_auth(Host, Val) ->
replace_storage_auth(Host, Val, [], undefined).
replace_storage_auth(_Host, [], Val2, Storage) ->
{lists:reverse(Val2), Storage};
replace_storage_auth(Host, [internal = Val | ValT], Val2, undefined) ->
Storage = {auth_storage, mnesia},
?WARNING_MSG("The auth method '~p' is deprecated.~nReplace it with 'storage'"
" and also add this option: ~n~p.", [Val, Storage]),
replace_storage_auth(Host, ValT, [storage | Val2], Storage);
replace_storage_auth(Host, [odbc = Val | ValT], Val2, undefined) ->
Storage = {auth_storage, odbc},
?WARNING_MSG("The auth method '~p' is deprecated.~nReplace it with 'storage'"
" and also add this option: ~n~p.", [Val, Storage]),
replace_storage_auth(Host, ValT, [storage | Val2], Storage);
replace_storage_auth(Host, [Val | ValT], Val2, Storage)
when (Val /= internal) and (Val /= odbc) ->
replace_storage_auth(Host, ValT, [Val | Val2], Storage);
replace_storage_auth(Host, [Val | _ValT], _Val2, Storage) ->
?CRITICAL_MSG("The auth method '~p' conflicts with~n~p in the host \"~p\"."
"~nOnly one of them can be used in each host.",
[Val, Storage, Host]),
throw({unacceptable_auth_conflict, Host, Val, Storage}).
+6 -1
View File
@@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -21,3 +21,8 @@
-record(config, {key, value}).
-record(local_config, {key, value}).
-record(state, {opts = [],
hosts = [],
override_local = false,
override_global = false,
override_acls = false}).
+47 -49
View File
@@ -5,7 +5,7 @@
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -189,9 +189,8 @@ process(["help" | Mode]) ->
["help"] ->
print_usage_help(MaxC, ShCode),
?STATUS_SUCCESS;
[CmdString | _] ->
CmdStringU = re:replace(CmdString, "-", "_", [global, {return, list}]),
print_usage_commands(CmdStringU, MaxC, ShCode),
[CommandString | _] ->
print_usage_commands(CommandString, MaxC, ShCode),
?STATUS_SUCCESS
end;
@@ -201,7 +200,8 @@ process(Args) ->
case String of
[] -> ok;
_ ->
io:format("~s~n", [String])
io:format(String),
io:format("\n")
end,
Code.
@@ -267,8 +267,10 @@ try_run_ctp(Args, Auth, AccessCommands) ->
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
try_call_command(Args, Auth, AccessCommands) ->
try call_command(Args, Auth, AccessCommands) of
{error, String} ->
{String, ?STATUS_ERROR};
{error, command_unknown} ->
{io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR};
{error, wrong_number_parameters} ->
{"Error: wrong number of parameters", ?STATUS_ERROR};
Res ->
Res
catch
@@ -277,13 +279,13 @@ try_call_command(Args, Auth, AccessCommands) ->
{io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, string()}
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType}
call_command([CmdString | Args], Auth, AccessCommands) ->
CmdStringU = re:replace(CmdString, "-", "_", [global,{return,list}]),
{ok, CmdStringU, _} = regexp:gsub(CmdString, "-", "_"),
Command = list_to_atom(CmdStringU),
case ejabberd_commands:get_command_format(Command) of
{error, command_unknown} ->
{error, io_lib:format("Error: command ~p not known.", [CmdString])};
{error, command_unknown};
{ArgsFormat, ResultFormat} ->
case (catch format_args(Args, ArgsFormat)) of
ArgsFormatted when is_list(ArgsFormatted) ->
@@ -296,8 +298,9 @@ call_command([CmdString | Args], Auth, AccessCommands) ->
{L1, L2} when L1 < L2 -> {L2-L1, "less argument"};
{L1, L2} when L1 > L2 -> {L1-L2, "more argument"}
end,
{error, io_lib:format("Error: the command ~p requires ~p ~s.",
[CmdString, NumCompa, TextCompa])}
{io_lib:format("Error: the command ~p requires ~p ~s.",
[CmdString, NumCompa, TextCompa]),
wrong_command_arguments}
end
end.
@@ -315,19 +318,18 @@ format_args(Args, ArgsFormat) ->
[],
lists:zip(ArgsFormat, Args)).
format_arg(Arg, integer) ->
format_arg2(Arg, "~d");
format_arg("", string) ->
"";
format_arg(Arg, string) ->
NumChars = integer_to_list(string:len(Arg)),
Parse = "~" ++ NumChars ++ "c",
format_arg2(Arg, Parse).
format_arg2(Arg, Parse)->
format_arg(Arg, Format) ->
Parse = case Format of
integer ->
"~d";
string ->
NumChars = integer_to_list(string:len(Arg)),
"~" ++ NumChars ++ "c"
end,
{ok, [Arg2], _RemainingArguments} = io_lib:fread(Parse, Arg),
Arg2.
%%-----------------------------
%% Format result
%%-----------------------------
@@ -438,7 +440,8 @@ print_usage() ->
print_usage(dual, MaxC, ShCode).
print_usage(HelpMode, MaxC, ShCode) ->
AllCommands =
[{"status", [], "Get ejabberd status"},
[
{"status", [], "Get ejabberd status"},
{"stop", [], "Stop ejabberd"},
{"restart", [], "Restart ejabberd"},
{"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"},
@@ -610,26 +613,26 @@ print_usage_tags(Tag, MaxC, ShCode) ->
print_usage_help(MaxC, ShCode) ->
LongDesc =
"The special 'help' ejabberdctl command provides help of ejabberd commands.\n\n"
"The format is:\n "++?B("ejabberdctl")++" "++?B("help")++" ["++?B("--tags")++" "++?U("[tag]")++" | "++?U("com?*")++"]\n\n"
["The special 'help' ejabberdctl command provides help of ejabberd commands.\n\n"
"The format is:\n ", ?B("ejabberdctl"), " ", ?B("help"), " [", ?B("--tags"), " ", ?U("[tag]"), " | ", ?U("com?*"), "]\n\n"
"The optional arguments:\n"
" "++?B("--tags")++" Show all tags and the names of commands in each tag\n"
" "++?B("--tags")++" "++?U("tag")++" Show description of commands in this tag\n"
" "++?U("command")++" Show detailed description of the command\n"
" "++?U("com?*")++" Show detailed description of commands that match this glob.\n"
" ",?B("--tags")," Show all tags and the names of commands in each tag\n"
" ",?B("--tags"), " ", ?U("tag")," Show description of commands in this tag\n"
" ",?U("command")," Show detailed description of the command\n"
" ",?U("com?*")," Show detailed description of commands that match this glob.\n"
" You can use ? to match a simple character,\n"
" and * to match several characters.\n"
"\n"
"Some example usages:\n"
" ejabberdctl help\n"
" ejabberdctl help --tags\n"
" ejabberdctl help --tags accounts\n"
" ejabberdctl help register\n"
" ejabberdctl help regist*\n"
"\n"
"Please note that 'ejabberdctl help' shows all ejabberd commands,\n"
"even those that cannot be used in the shell with ejabberdctl.\n"
"Those commands can be identified because the description starts with: *",
"\n",
"Some example usages:\n",
" ejabberdctl help\n",
" ejabberdctl help --tags\n",
" ejabberdctl help --tags accounts\n",
" ejabberdctl help register\n",
" ejabberdctl help regist*\n",
"\n",
"Please note that 'ejabberdctl help' shows all ejabberd commands,\n",
"even those that cannot be used in the shell with ejabberdctl.\n",
"Those commands can be identified because the description starts with: *"],
ArgsDef = [],
C = #ejabberd_commands{
desc = "Show help of ejabberd commands",
@@ -674,13 +677,13 @@ filter_commands(All, SubString) ->
end.
filter_commands_regexp(All, Glob) ->
RegExp = xmerl_regexp:sh_to_awk(Glob),
RegExp = regexp:sh_to_awk(Glob),
lists:filter(
fun(Command) ->
case re:run(Command, RegExp, [{capture, none}]) of
match ->
case regexp:first_match(Command, RegExp) of
{match, _, _} ->
true;
nomatch ->
_ ->
false
end
end,
@@ -696,7 +699,6 @@ print_usage_command(Cmd, MaxC, ShCode) ->
print_usage_command(Cmd, C, MaxC, ShCode)
end.
%% @spec (Cmd::string(), C::ejabberd_commands(), MaxC::integer(), ShCode::boolean()) -> ok
print_usage_command(Cmd, C, MaxC, ShCode) ->
#ejabberd_commands{
tags = TagsAtoms,
@@ -738,10 +740,6 @@ 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 -1
View File
@@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
+97
View File
@@ -0,0 +1,97 @@
%%%----------------------------------------------------------------------
%%% 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-2009 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.
+48 -43
View File
@@ -5,7 +5,7 @@
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -45,8 +45,6 @@
get_peer_certificate/1,
get_verify_result/1,
close/1,
setopts/2,
change_controller/2,
sockname/1, peername/1]).
%% gen_server callbacks
@@ -96,15 +94,18 @@ start(Module, SockMod, Socket, Opts) ->
todo
end.
starttls(FsmRef, TLSOpts) ->
starttls(FsmRef, TLSOpts, undefined).
starttls(FsmRef, _TLSOpts) ->
%% TODO: Frontend improvements planned by Aleksey
%%gen_server:call(FsmRef, {starttls, TLSOpts}),
FsmRef.
starttls(FsmRef, TLSOpts, Data) ->
gen_server:call(FsmRef, {starttls, TLSOpts, Data}),
FsmRef.
compress(FsmRef) ->
compress(FsmRef, undefined).
gen_server:call(FsmRef, compress),
FsmRef.
compress(FsmRef, Data) ->
gen_server:call(FsmRef, {compress, Data}),
@@ -137,14 +138,11 @@ close(FsmRef) ->
sockname(FsmRef) ->
gen_server:call(FsmRef, sockname).
setopts(FsmRef, Opts) ->
gen_server:call(FsmRef, {setopts, Opts}).
peername(_FsmRef) ->
%% TODO: Frontend improvements planned by Aleksey
%%gen_server:call(FsmRef, peername).
{ok, {{0, 0, 0, 0}, 0}}.
change_controller(FsmRef, C2SPid) ->
gen_server:call(FsmRef, {change_controller, C2SPid}).
peername(FsmRef) ->
gen_server:call(FsmRef, peername).
%%====================================================================
%% gen_server callbacks
@@ -160,16 +158,9 @@ peername(FsmRef) ->
init([Module, SockMod, Socket, Opts, Receiver]) ->
%% TODO: monitor the receiver
Node = ejabberd_node_groups:get_closest_node(backend),
IP = case peername(SockMod, Socket) of
{ok, IP1} ->
IP1;
_ ->
undefined
end,
{SockMod2, Socket2} = check_starttls(SockMod, Socket, Receiver, Opts),
{ok, Pid} =
rpc:call(Node, Module, start,
[{?MODULE, self()}, [{frontend_ip, IP} | Opts]]),
rpc:call(Node, Module, start, [{?MODULE, self()}, Opts]),
ejabberd_receiver:become_controller(Receiver, Pid),
{ok, #state{sockmod = SockMod2,
socket = Socket2,
@@ -184,16 +175,38 @@ init([Module, SockMod, Socket, Opts, Receiver]) ->
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({starttls, TLSOpts, Data}, _From, State) ->
{ok, TLSSocket} = ejabberd_receiver:starttls(
State#state.receiver, TLSOpts, Data),
handle_call({starttls, TLSOpts}, _From, State) ->
{ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts),
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
Reply = ok,
{reply, Reply, State#state{socket = TLSSocket, sockmod = tls},
?HIBERNATE_TIMEOUT};
handle_call({starttls, TLSOpts, Data}, _From, State) ->
{ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts),
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
catch (State#state.sockmod):send(
State#state.socket, Data),
Reply = ok,
{reply, Reply, State#state{socket = TLSSocket, sockmod = tls},
?HIBERNATE_TIMEOUT};
handle_call(compress, _From, State) ->
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
State#state.sockmod,
State#state.socket),
ejabberd_receiver:compress(State#state.receiver, ZlibSocket),
Reply = ok,
{reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib},
?HIBERNATE_TIMEOUT};
handle_call({compress, Data}, _From, State) ->
{ok, ZlibSocket} = ejabberd_receiver:compress(
State#state.receiver, Data),
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
State#state.sockmod,
State#state.socket),
ejabberd_receiver:compress(State#state.receiver, ZlibSocket),
catch (State#state.sockmod):send(
State#state.socket, Data),
Reply = ok,
{reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib},
?HIBERNATE_TIMEOUT};
@@ -233,7 +246,13 @@ handle_call(close, _From, State) ->
handle_call(sockname, _From, State) ->
#state{sockmod = SockMod, socket = Socket} = State,
Reply = peername(SockMod, Socket),
Reply =
case SockMod of
gen_tcp ->
inet:sockname(Socket);
_ ->
SockMod:sockname(Socket)
end,
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(peername, _From, State) ->
@@ -247,14 +266,6 @@ handle_call(peername, _From, State) ->
end,
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call({setopts, Opts}, _From, State) ->
ejabberd_receiver:setopts(State#state.receiver, Opts),
{reply, ok, State, ?HIBERNATE_TIMEOUT};
handle_call({change_controller, Pid}, _From, State) ->
ejabberd_receiver:change_controller(State#state.receiver, Pid),
{reply, ok, State, ?HIBERNATE_TIMEOUT};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State, ?HIBERNATE_TIMEOUT}.
@@ -307,16 +318,10 @@ check_starttls(SockMod, Socket, Receiver, Opts) ->
end, Opts),
if
TLSEnabled ->
{ok, TLSSocket} = ejabberd_receiver:starttls(Receiver, TLSOpts),
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(Receiver, TLSSocket),
{tls, TLSSocket};
true ->
{SockMod, Socket}
end.
peername(SockMod, Socket) ->
case SockMod of
gen_tcp ->
inet:peername(Socket);
_ ->
SockMod:peername(Socket)
end.
-280
View File
@@ -1,280 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_global_router.erl
%%% Author : Geoff Cant <gcant@process-one.net>
%%% Purpose : Router for virtual global routes
%%% Created : 07 Aug 2002 by Geoff Cant <gcant@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 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_global_router).
-behaviour(gen_server).
-include("ejabberd.hrl").
-include("ejabberd_global_router.hrl").
%% API
-export([start_link/0
,route/3
,route/4
,find_route/1
,register_route/1
,register_route/2
,unregister_route/1
,unregister_route/2
,expand_routes/1
,all_prefixes/0
,server_host/2
]).
%% Eunit test exports.
-export([remove_prefix/2
,find_prefix/2
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
%% @doc Starts the server
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
route(From, To, Packet) ->
case find_route(To) of
Route = #global_route{} ->
route(Route, From, To, Packet);
no_route ->
ok
end.
route(#global_route{pids=Pids}, From, To, Packet) ->
Pid = pick_route(Pids),
Pid ! {route, From, To, Packet},
ok.
find_route(Domain) ->
case prefix_match(Domain, all_prefixes()) of
nomatch ->
no_route;
Prefix when is_list(Prefix) ->
dirty_get_route(Prefix)
end.
register_route(Prefix) ->
register_route(Prefix, self()).
register_route(Prefix, Pid) ->
Route = #global_route{prefix=Prefix,
pids=[Pid]},
{atomic, ok} = mnesia:transaction(fun write_route/1,
[Route]),
ok.
unregister_route(Prefix) ->
unregister_route(Prefix, self()).
unregister_route(Prefix, Pid) ->
mnesia:transaction(fun delete_route/2, [Prefix, Pid]).
expand_routes(Domain) ->
[expand_route(Prefix, Domain) || Prefix <- all_prefixes()].
expand_route(Prefix, Domain) ->
lists:append([Prefix, ".", Domain]).
all_prefixes() ->
mnesia:dirty_all_keys(global_route).
server_host(Host, Pid) ->
#global_route{prefix=P,pids=Pids} = find_route(Host),
true = lists:member(Pid, Pids),
remove_prefix(P, Host).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% @private
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @doc Initialises the server's state
%% @end
%%--------------------------------------------------------------------
init([]) ->
update_tables(),
mnesia:create_table(global_route,
[{ram_copies, [node()]},
{type, set},
{attributes,
record_info(fields, global_route)}]),
mnesia:add_table_copy(global_route, node(), ram_copies),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% @private
%% @spec
%% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% @doc Call message handler callbacks
%% @end
%%--------------------------------------------------------------------
handle_call(Call, _From, State) ->
?WARNING_MSG("Unexpected call ~p.", [Call]),
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @spec
%% handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Cast message handler callbacks
%% @end
%%--------------------------------------------------------------------
handle_cast(Msg, State) ->
?WARNING_MSG("Unexpected cast ~p", [Msg]),
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @spec
%% handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Non gen-server message handler callbacks
%% @end
%%--------------------------------------------------------------------
handle_info(Info, State) ->
?WARNING_MSG("Unexpected info ~p", [Info]),
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @spec terminate(Reason, State) -> void()
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @doc Convert process state when code is changed
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
pick_route(Pids) when is_list(Pids) ->
pick_local_route(Pids).
pick_local_route(Pids) when is_list(Pids) ->
case [Pid || Pid <- Pids,
node(Pid) =:= node()] of
[] -> pick_remote_route(Pids);
LocalPids -> pick_one_route(LocalPids)
end.
pick_remote_route(Pids) when is_list(Pids) -> pick_one_route(Pids).
pick_one_route(Pids) when is_list(Pids) ->
hd(Pids). % should pick random pid?
update_tables() ->
CorrectAttributes = record_info(fields, global_route),
case catch mnesia:table_info(global_route, attributes) of
CorrectAttributes ->
ok;
IncorrectAttributes when is_list(IncorrectAttributes) ->
mnesia:delete_table(global_route);
{'EXIT', _} ->
ok
end.
write_route(R = #global_route{prefix=Prefix}) ->
NewRoute = case mnesia:read({global_route, Prefix}) of
[] -> R;
[OldRoute] ->
combine_routes(OldRoute, R)
end,
ok = mnesia:write(NewRoute).
delete_route(Prefix, Pid) ->
case mnesia:read({global_route, Prefix}, write) of
[] -> ok;
[#global_route{pids=[Pid]}] ->
mnesia:delete({global_route, Prefix});
[Route = #global_route{pids=Pids}] ->
mnesia:write(Route#global_route{pids=Pids -- [Pid]})
end.
combine_routes(R = #global_route{prefix=P, pids=APids},
#global_route{prefix=P, pids=BPids}) ->
R#global_route{pids=lists:usort(APids ++ BPids)}.
prefix_match(Domain, Prefixes) ->
case find_prefix(Domain, Prefixes) of
[] -> nomatch;
[Prefix] -> Prefix;
_Prefixes ->
erlang:error(multiple_prefix_matches)
end.
dirty_get_route(Prefix) ->
case mnesia:dirty_read({global_route, Prefix}) of
[Route = #global_route{}] ->
Route;
_ -> no_route
end.
find_prefix(Domain, PossiblePrefixes) ->
[Prefix
|| Prefix <- PossiblePrefixes,
lists:prefix(Prefix, Domain)].
remove_prefix(Prefix, Domain) ->
[$. | BareDomain] = Domain -- Prefix,
BareDomain.
%%--------------------------------------------------------------------
%%% Eunit Tests.
%%--------------------------------------------------------------------
-27
View File
@@ -1,27 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_global_router.hrl
%%% Author : Geoff Cant <gcant@process-one.net>
%%% Purpose : Virtual global routes record definitions
%%% Created : 08 Aug 2002 by Geoff Cant <gcant@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2009 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
%%%
%%%----------------------------------------------------------------------
-record(global_route, {prefix, pids}).
+20 -50
View File
@@ -5,7 +5,7 @@
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% ejabberd, Copyright (C) 2002-2009 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,8 +81,7 @@ add(Hook, Host, Function, Seq) when is_function(Function) ->
add(Hook, Module, Function, Seq) ->
add(Hook, global, Module, Function, Seq).
add(Hook, Host, Module, Function, Seq)
when is_binary(Host) orelse is_atom(Host) ->
add(Hook, Host, Module, Function, Seq) ->
gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}).
add_dist(Hook, Node, Module, Function, Seq) ->
@@ -105,8 +104,7 @@ delete(Hook, Host, Function, Seq) when is_function(Function) ->
delete(Hook, Module, Function, Seq) ->
delete(Hook, global, Module, Function, Seq).
delete(Hook, Host, Module, Function, Seq)
when is_binary(Host) orelse is_atom(Host) ->
delete(Hook, Host, Module, Function, Seq) ->
gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}).
delete_dist(Hook, Node, Module, Function, Seq) ->
@@ -122,11 +120,11 @@ run(Hook, Args) ->
run(Hook, global, Args).
run(Hook, Host, Args) ->
case get_registered_hooks_for(Hook, Host) of
[] ->
ok;
Ls ->
run1(Ls, Hook, Args)
case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
ok
end.
%% @spec (Hook::atom(), Val, Args) -> Val | stopped | NewVal
@@ -138,14 +136,12 @@ run(Hook, Host, Args) ->
run_fold(Hook, Val, Args) ->
run_fold(Hook, global, Val, Args).
%% @spec (Hook::atom(), Host, Val, Args) -> Val | stopped | NewVal
%% Host = global | binary()
run_fold(Hook, Host, Val, Args) ->
case get_registered_hooks_for(Hook, Host) of
[] ->
Val;
Ls ->
run_fold1(Ls, Hook, Val, Args)
case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
Val
end.
%%%----------------------------------------------------------------------
@@ -173,8 +169,7 @@ init([]) ->
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) ->
NHost = ejabberd:normalize_host(Host),
Reply = case ets:lookup(hooks, {Hook, NHost}) of
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
El = {Seq, Module, Function},
case lists:member(El, Ls) of
@@ -182,12 +177,12 @@ handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) ->
ok;
false ->
NewLs = lists:merge(Ls, [El]),
ets:insert(hooks, {{Hook, NHost}, NewLs}),
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok
end;
[] ->
NewLs = [{Seq, Module, Function}],
ets:insert(hooks, {{Hook, NHost}, NewLs}),
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok
end,
{reply, Reply, State};
@@ -210,14 +205,10 @@ handle_call({add, Hook, Host, Node, Module, Function, Seq}, _From, State) ->
end,
{reply, Reply, State};
handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) ->
NHost = ejabberd:normalize_host(Host),
Reply = case ets:lookup(hooks, {Hook, NHost}) of
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Module, Function}, Ls),
case NewLs of
[] -> ets:delete(hooks, {Hook, NHost});
_ -> ets:insert(hooks, {{Hook, NHost}, NewLs})
end,
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok;
[] ->
ok
@@ -227,10 +218,7 @@ handle_call({delete, Hook, Host, Node, Module, Function, Seq}, _From, State) ->
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Node, Module, Function}, Ls),
case NewLs of
[] -> ets:delete(hooks, {Hook, Host});
_ -> ets:insert(hooks, {{Hook, Host}, NewLs})
end,
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok;
[] ->
ok
@@ -307,7 +295,7 @@ run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
[Reason, {Hook, Args}]),
run1(Ls, Hook, Args);
stop ->
stop;
ok;
_ ->
run1(Ls, Hook, Args)
end.
@@ -354,21 +342,3 @@ run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
NewVal ->
run_fold1(Ls, Hook, NewVal, Args)
end.
get_registered_hooks_for(Hook, global) ->
case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
Ls;
[] ->
[]
end;
get_registered_hooks_for(Hook, Host) ->
GlobalHooks = get_registered_hooks_for(Hook, global),
HostHooks = case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
Ls;
[] ->
[]
end,
lists:umerge(GlobalHooks, HostHooks).

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