Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cec11aca59 |
@@ -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
|
||||
@@ -1,6 +1,3 @@
|
||||
As a special exception, the authors give permission to link this program
|
||||
with the OpenSSL library and distribute the resulting binary.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
ejabberd - High-Performance Enterprise Instant Messaging Server
|
||||
|
||||
Quickstart guide
|
||||
|
||||
|
||||
0. Requirements
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
|
||||
1. Compile and install on *nix systems
|
||||
|
||||
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):
|
||||
|
||||
sudo make install
|
||||
|
||||
These commands will:
|
||||
- Install the configuration files in /etc/ejabberd/
|
||||
- Install ejabberd binary, header and runtime files in /lib/ejabberd/
|
||||
- Install the administration script: /sbin/ejabberdctl
|
||||
- Install ejabberd documentation in /share/doc/ejabberd/
|
||||
- Create a spool directory: /var/lib/ejabberd/
|
||||
- Create a directory for log files: /var/log/ejabberd/
|
||||
|
||||
|
||||
2. Start ejabberd
|
||||
|
||||
You can use the ejabberdctl command line administration script to
|
||||
start and stop ejabberd. For example:
|
||||
ejabberdctl start
|
||||
|
||||
|
||||
For detailed information please refer to the
|
||||
ejabberd Installation and Operation Guide
|
||||
@@ -0,0 +1,21 @@
|
||||
admin tool
|
||||
mod_muc logging
|
||||
|
||||
admin interface
|
||||
users management
|
||||
statistics about each user
|
||||
statistics about each connection
|
||||
node management
|
||||
node restart/shutdown
|
||||
statistics about memory usage
|
||||
|
||||
S2S:
|
||||
rewrite S2S key validation
|
||||
check "id" attributes in db:verify packets
|
||||
|
||||
more correctly work with SRV DNS records (priority, weight, etc...)
|
||||
TLS
|
||||
make roster set to work in one transaction
|
||||
add traffic shapers to c2s connection before authentification
|
||||
more traffic shapers
|
||||
SNMP
|
||||
@@ -1,5 +0,0 @@
|
||||
% List of ejabberd-modules to add for ejabberd packaging (source archive and installer)
|
||||
%
|
||||
% HTTP-binding:
|
||||
%https://svn.process-one.net/ejabberd-modules/http_bind/trunk
|
||||
%https://svn.process-one.net/ejabberd-modules/mod_http_fileserver/trunk
|
||||
@@ -1,61 +0,0 @@
|
||||
SHELL = /bin/bash
|
||||
|
||||
CONTRIBUTED_MODULES = ""
|
||||
#ifeq ($(shell ls mod_http_bind.tex),mod_http_bind.tex)
|
||||
# CONTRIBUTED_MODULES += "\\n\\setboolean{modhttpbind}{true}"
|
||||
#endif
|
||||
|
||||
|
||||
all: release pdf html
|
||||
|
||||
release:
|
||||
@echo "Notes for the releaser:"
|
||||
@echo "* Do not forget to add a link to the release notes in guide.tex"
|
||||
@echo "* Do not forget to update the version number in src/ejabberd.app!"
|
||||
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
|
||||
@echo "Press any key to continue"
|
||||
##@read foo
|
||||
@echo "% ejabberd version (automatically generated)." > version.tex
|
||||
@echo "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../src/ejabberd.app`"}" >> version.tex
|
||||
@echo -n "% Contributed modules (automatically generated)." > contributed_modules.tex
|
||||
@echo -e "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
|
||||
|
||||
html: guide.html dev.html features.html
|
||||
|
||||
pdf: guide.pdf features.pdf
|
||||
|
||||
clean:
|
||||
rm -f *.aux
|
||||
rm -f *.haux
|
||||
rm -f *.htoc
|
||||
rm -f *.idx
|
||||
rm -f *.ilg
|
||||
rm -f *.ind
|
||||
rm -f *.log
|
||||
rm -f *.out
|
||||
rm -f *.pdf
|
||||
rm -f *.toc
|
||||
rm -f version.tex
|
||||
[ ! -f contributed_modules.tex ] || rm contributed_modules.tex
|
||||
|
||||
distclean: clean
|
||||
rm -f *.html
|
||||
|
||||
guide.html: guide.tex
|
||||
hevea -fix -pedantic guide.tex
|
||||
|
||||
dev.html: dev.tex
|
||||
hevea -fix -pedantic dev.tex
|
||||
|
||||
features.html: features.tex
|
||||
hevea -fix -pedantic features.tex
|
||||
|
||||
guide.pdf: guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
makeindex guide.idx
|
||||
pdflatex guide.tex
|
||||
|
||||
features.pdf: features.tex
|
||||
pdflatex features.tex
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,364 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD><TITLE>Ejabberd Developers Guide</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
|
||||
<META name="GENERATOR" content="hevea 1.06">
|
||||
</HEAD>
|
||||
<BODY >
|
||||
<!--HEVEA command line is: /usr/bin/hevea -charset ISO8859-1 dev.tex -->
|
||||
<!--HTMLHEAD-->
|
||||
<!--ENDHTML-->
|
||||
<!--PREFIX <ARG ></ARG>-->
|
||||
<!--CUT DEF section 1 -->
|
||||
|
||||
|
||||
|
||||
<H1 ALIGN=center>Ejabberd Developers Guide</H1>
|
||||
|
||||
<H3 ALIGN=center>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>
|
||||
|
||||
<H3 ALIGN=center>September 10, 2003</H3><DIV ALIGN=center>
|
||||
|
||||
<IMG SRC="logo.png">
|
||||
|
||||
|
||||
</DIV><BR>
|
||||
<BR>
|
||||
|
||||
|
||||
<!--TOC section Table of Contents-->
|
||||
|
||||
<H2>Table of Contents</H2><!--SEC END -->
|
||||
|
||||
<UL><LI>
|
||||
<A HREF="#htoc1">1 Introduction</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc2">1.1 How it works</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc3">1.1.1 Router</A>
|
||||
<LI><A HREF="#htoc4">1.1.2 Local Router</A>
|
||||
<LI><A HREF="#htoc5">1.1.3 Session Manager</A>
|
||||
<LI><A HREF="#htoc6">1.1.4 S2S Manager</A>
|
||||
</UL>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc7">2 XML representation</A>
|
||||
<LI><A HREF="#htoc8">3 Module <TT>xml</TT></A>
|
||||
<LI><A HREF="#htoc9">4 <TT>ejabberd</TT> modules</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc10">4.1 <CODE>gen_mod</CODE> behaviour</A>
|
||||
<LI><A HREF="#htoc11">4.2 Module <CODE>gen_iq_handler</CODE></A>
|
||||
<LI><A HREF="#htoc12">4.3 Services</A>
|
||||
</UL>
|
||||
</UL>
|
||||
|
||||
<!--TOC section Introduction-->
|
||||
|
||||
<H2><A NAME="htoc1">1</A> Introduction</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:intro"></A>
|
||||
<TT>ejabberd</TT> is a Free and Open Source fault-tolerant distributed Jabber
|
||||
server. It is writen mostly in Erlang.<BR>
|
||||
<BR>
|
||||
The main features of <TT>ejabberd</TT> is:
|
||||
<UL><LI>
|
||||
Works on most of popular platforms: *nix (tested on Linux and FreeBSD)
|
||||
and Win32
|
||||
<LI>Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of
|
||||
them will serve one Jabber domain.
|
||||
<LI>Fault-tolerance: You can setup an <TT>ejabberd</TT> cluster so that all the
|
||||
information required for a properly working service will be stored
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace more nodes ``on the fly''.
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User
|
||||
Chat</A> service
|
||||
<LI>Built-in IRC transport
|
||||
<LI>Built-in
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A>
|
||||
service
|
||||
<LI>Built-in Jabber Users Directory service based on users vCards
|
||||
<LI>Support for
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A>
|
||||
(Service Discovery).
|
||||
<LI>Support for
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>
|
||||
(Statistics Gathering).
|
||||
<LI>Support for <TT>xml:lang</TT> attribute in many XML elements
|
||||
</UL>
|
||||
<!--TOC subsection How it works-->
|
||||
|
||||
<H3><A NAME="htoc2">1.1</A> How it works</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:howitworks"></A>
|
||||
A Jabber 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...<BR>
|
||||
<BR>
|
||||
Each <TT>ejabberd</TT> node have following modules:
|
||||
<UL><LI>
|
||||
router;
|
||||
<LI>local router.
|
||||
<LI>session manager;
|
||||
<LI>S2S manager;
|
||||
</UL>
|
||||
<!--TOC subsubsection Router-->
|
||||
|
||||
<H4><A NAME="htoc3">1.1.1</A> Router</H4><!--SEC END -->
|
||||
|
||||
This module is the main router of Jabber 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.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection Local Router-->
|
||||
|
||||
<H4><A NAME="htoc4">1.1.2</A> Local Router</H4><!--SEC END -->
|
||||
|
||||
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's content.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection Session Manager-->
|
||||
|
||||
<H4><A NAME="htoc5">1.1.3</A> Session Manager</H4><!--SEC END -->
|
||||
|
||||
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.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection S2S Manager-->
|
||||
|
||||
<H4><A NAME="htoc6">1.1.4</A> S2S Manager</H4><!--SEC END -->
|
||||
|
||||
This module routes packets to other Jabber 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.<BR>
|
||||
<BR>
|
||||
<!--TOC section XML representation-->
|
||||
|
||||
<H2><A NAME="htoc7">2</A> XML representation</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:xmlrepr"></A>
|
||||
Each XML stanza represented as following tuple:
|
||||
<PRE>
|
||||
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
Name = string()
|
||||
Attrs = [Attr]
|
||||
Attr = {Key, Val}
|
||||
Key = string()
|
||||
Val = string()
|
||||
ElementOrCDATA = XMLElement | CDATA
|
||||
CDATA = {xmlcdata, string()}
|
||||
</PRE>E. g. this stanza:
|
||||
<PRE>
|
||||
<message to='test@conference.e.localhost' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
</PRE>represented as following structure:
|
||||
<PRE>
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.e.localhost"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
[{xmlcdata, "test"}]}]}}
|
||||
</PRE>
|
||||
<!--TOC section Module <TT>xml</TT>-->
|
||||
|
||||
<H2><A NAME="htoc8">3</A> Module <TT>xml</TT></H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:xmlmod"></A>
|
||||
<DL COMPACT=compact><DT>
|
||||
<CODE><B>element_to_string(El) -> string()</B></CODE><DD>
|
||||
<PRE>
|
||||
El = XMLElement
|
||||
</PRE>Returns string representation of XML stanza <TT>El</TT>.<BR>
|
||||
<BR>
|
||||
<DT><CODE><B>crypt(S) -> string()</B></CODE><DD>
|
||||
<PRE>
|
||||
S = string()
|
||||
</PRE>Returns string which correspond to <TT>S</TT> with encoded XML special
|
||||
characters.<BR>
|
||||
<BR>
|
||||
<DT><CODE><B>remove_cdata(ECList) -> EList</B></CODE><DD>
|
||||
<PRE>
|
||||
ECList = [ElementOrCDATA]
|
||||
EList = [XMLElement]
|
||||
</PRE><TT>EList</TT> is a list of all non-CDATA elements of ECList.<BR>
|
||||
<BR>
|
||||
<DT><CODE><B>get_path_s(El, Path) -> Res</B></CODE><DD>
|
||||
<PRE>
|
||||
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 COMPACT=compact><DT>
|
||||
<CODE><B>{elem, Name}</B></CODE><DD> <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.
|
||||
<DT><CODE><B>{attr, Name}</B></CODE><DD> If <TT>El</TT> have attribute <TT>Name</TT>, then
|
||||
returns value of this attribute, else returns empty string.
|
||||
<DT><CODE><B>cdata</B></CODE><DD> Returns CDATA of <TT>El</TT>.
|
||||
</DL><BR>
|
||||
<BR>
|
||||
<DT><B>TODO:</B><DD>
|
||||
<PRE>
|
||||
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></DL>
|
||||
<!--TOC section <TT>ejabberd</TT> modules-->
|
||||
|
||||
<H2><A NAME="htoc9">4</A> <TT>ejabberd</TT> modules</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:emods"></A>
|
||||
<!--TOC subsection <CODE>gen_mod</CODE> behaviour-->
|
||||
|
||||
<H3><A NAME="htoc10">4.1</A> <CODE>gen_mod</CODE> behaviour</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:genmod"></A>
|
||||
TBD<BR>
|
||||
<BR>
|
||||
<!--TOC subsection Module <CODE>gen_iq_handler</CODE>-->
|
||||
|
||||
<H3><A NAME="htoc11">4.2</A> Module <CODE>gen_iq_handler</CODE></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:geniqhandl"></A>
|
||||
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.<BR>
|
||||
<BR>
|
||||
In this module the following functions are defined:
|
||||
<DL COMPACT=compact><DT>
|
||||
<CODE><B>add_iq_handler(Component, NS, Module, Function, Type)</B></CODE><DD>
|
||||
<PRE>
|
||||
Component = Module = Function = atom()
|
||||
NS = string()
|
||||
Type = no_queue | one_queue | parallel
|
||||
</PRE>Registers function <CODE>Module:Function</CODE> as handler for IQ packets 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 COMPACT=compact><DT>
|
||||
<CODE><B>ejabberd_local</B></CODE><DD> Handles packets that addressed to server JID;
|
||||
<DT><CODE><B>ejabberd_sm</B></CODE><DD> Handles packets that addressed to users bare JIDs.
|
||||
</DL>
|
||||
<DT><CODE><B>remove_iq_handler(Component, NS)</B></CODE><DD>
|
||||
<PRE>
|
||||
Component = atom()
|
||||
NS = string()
|
||||
</PRE>Removes IQ handler for namespace <CODE>NS</CODE> from <CODE>Component</CODE>.
|
||||
</DL>
|
||||
Handler function must have the following type:
|
||||
<DL COMPACT=compact><DT>
|
||||
<CODE><B>Module:Function(From, To, IQ)</B></CODE><DD>
|
||||
<PRE>
|
||||
From = To = jid()
|
||||
</PRE></DL>
|
||||
<PRE>
|
||||
-module(mod_cputime).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_local_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(NS_CPUTIME, "ejabberd:cputime").
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_CPUTIME,
|
||||
?MODULE, process_local_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_CPUTIME).
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
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><A NAME="htoc12">4.3</A> Services</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:services"></A>
|
||||
TBD<BR>
|
||||
<BR>
|
||||
TODO: use <CODE>proc_lib</CODE>
|
||||
<PRE>
|
||||
-module(mod_echo).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/1, stop/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(Opts) ->
|
||||
Host = gen_mod:get_opt(host, Opts, "echo." ++ ?MYNAME),
|
||||
register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
loop(Host).
|
||||
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
ejabberd_router:route(To, From, Packet),
|
||||
loop(Host);
|
||||
stop ->
|
||||
ejabberd_router:unregister_local_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ejabberd_mod_echo ! stop,
|
||||
ok.
|
||||
</PRE>
|
||||
<!--HTMLFOOT-->
|
||||
<!--ENDHTML-->
|
||||
<!--FOOTER-->
|
||||
<HR SIZE=2>
|
||||
<BLOCKQUOTE><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
|
||||
</EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.
|
||||
</EM></BLOCKQUOTE>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -1,20 +1,14 @@
|
||||
\documentclass[a4paper,10pt]{article}
|
||||
\documentclass[10pt]{article}
|
||||
|
||||
%% Packages
|
||||
\usepackage{graphics}
|
||||
\usepackage{hevea}
|
||||
\usepackage{makeidx}
|
||||
\usepackage{verbatim}
|
||||
|
||||
%% Index
|
||||
\makeindex
|
||||
% Remove the index anchors from the HTML version to save size and bandwith.
|
||||
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
|
||||
|
||||
%% Images
|
||||
\newcommand{\logoscale}{0.7}
|
||||
\newcommand{\imgscale}{0.58}
|
||||
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
|
||||
|
||||
\newcommand{\insscaleimg}[2]{
|
||||
\imgsrc{#2}{}
|
||||
\begin{latexonly}
|
||||
@@ -22,82 +16,87 @@
|
||||
\end{latexonly}
|
||||
}
|
||||
|
||||
%% Various
|
||||
\newcommand{\ns}[1]{\texttt{#1}}
|
||||
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
\newcommand{\XMPP}{XMPP}
|
||||
|
||||
%% Modules
|
||||
\newcommand{\module}[1]{\texttt{#1}}
|
||||
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||
\newcommand{\modconfigure}{\module{mod\_configure}}
|
||||
\newcommand{\moddisco}{\module{mod\_disco}}
|
||||
\newcommand{\modecho}{\module{mod\_echo}}
|
||||
\newcommand{\modirc}{\module{mod\_irc}}
|
||||
\newcommand{\modlast}{\module{mod\_last}}
|
||||
\newcommand{\modmuc}{\module{mod\_muc}}
|
||||
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\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{\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{\modversion}{\module{mod\_version}}
|
||||
\newcommand{\modregister}{\texttt{mod\_register}}
|
||||
\newcommand{\modroster}{\texttt{mod\_roster}}
|
||||
\newcommand{\modconfigure}{\texttt{mod\_configure}}
|
||||
\newcommand{\moddisco}{\texttt{mod\_disco}}
|
||||
\newcommand{\modstats}{\texttt{mod\_stats}}
|
||||
\newcommand{\modvcard}{\texttt{mod\_vcard}}
|
||||
\newcommand{\modoffline}{\texttt{mod\_offline}}
|
||||
\newcommand{\modecho}{\texttt{mod\_echo}}
|
||||
\newcommand{\modprivate}{\texttt{mod\_private}}
|
||||
\newcommand{\modtime}{\texttt{mod\_time}}
|
||||
\newcommand{\modversion}{\texttt{mod\_version}}
|
||||
c
|
||||
%\setcounter{tocdepth}{3}
|
||||
|
||||
%% Title page
|
||||
\include{version}
|
||||
\title{Ejabberd \version\ Developers Guide}
|
||||
|
||||
\title{Ejabberd Developers Guide}
|
||||
\author{Alexey Shchepin \\
|
||||
\ahrefurl{mailto:alexey@sevcom.net} \\
|
||||
\ahrefurl{xmpp:aleksey@jabber.ru}}
|
||||
|
||||
%% Options
|
||||
\newcommand{\marking}[1]{#1} % Marking disabled
|
||||
\newcommand{\quoting}[2][yozhik]{} % Quotes disabled
|
||||
\newcommand{\new}{\begin{latexonly}\marginpar{\textsc{new}}\end{latexonly}} % Highlight new features
|
||||
\newcommand{\improved}{\begin{latexonly}\marginpar{\textsc{improved}}\end{latexonly}} % Highlight improved features
|
||||
\newcommand{\moreinfo}[1]{} % Hide details
|
||||
|
||||
%% Footnotes
|
||||
\newcommand{\txepref}[2]{\footahref{http://www.xmpp.org/extensions/xep-#1.html}{#2}}
|
||||
\newcommand{\xepref}[1]{\txepref{#1}{XEP-#1}}
|
||||
\date{September 10, 2003}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\label{titlepage}
|
||||
\begin{titlepage}
|
||||
\maketitle{}
|
||||
|
||||
\begin{center}
|
||||
{\insscaleimg{\logoscale}{logo.png}
|
||||
|
||||
{\centering
|
||||
\insscaleimg{\logoscale}{logo.png}
|
||||
\par
|
||||
}
|
||||
\end{center}
|
||||
|
||||
\begin{quotation}\textit{I can thoroughly recommend ejabberd for ease of setup --
|
||||
Kevin Smith, Current maintainer of the Psi project}\end{quotation}
|
||||
|
||||
\end{titlepage}
|
||||
|
||||
%\newpage
|
||||
\tableofcontents{}
|
||||
|
||||
% Input introduction.tex
|
||||
\input{introduction}
|
||||
\newpage
|
||||
\section{Introduction}
|
||||
\label{sec:intro}
|
||||
|
||||
\section{How it Works}
|
||||
\label{howitworks}
|
||||
\ejabberd{} is a Free and Open Source fault-tolerant distributed \Jabber{}
|
||||
server. It is writen mostly in Erlang.
|
||||
|
||||
The main features of \ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item Works on most of popular platforms: *nix (tested on Linux and FreeBSD)
|
||||
and Win32
|
||||
\item Distributed: You can run \ejabberd{} on a cluster of machines and all of
|
||||
them will serve one Jabber domain.
|
||||
\item Fault-tolerance: You can setup an \ejabberd{} cluster so that all the
|
||||
information required for a properly working service will be stored
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace more nodes ``on the fly''.
|
||||
\item Built-in \footahref{http://www.jabber.org/jeps/jep-0045.html}{Multi-User
|
||||
Chat} service
|
||||
\item Built-in IRC transport
|
||||
\item Built-in
|
||||
\footahref{http://www.jabber.org/jeps/jep-0060.html}{Publish-Subscribe}
|
||||
service
|
||||
\item Built-in Jabber Users Directory service based on users vCards
|
||||
\item Support for
|
||||
\footahref{http://www.jabber.org/jeps/jep-0030.html}{JEP-0030}
|
||||
(Service Discovery).
|
||||
\item Support for
|
||||
\footahref{http://www.jabber.org/jeps/jep-0039.html}{JEP-0039}
|
||||
(Statistics Gathering).
|
||||
\item Support for \ns{xml:lang} attribute in many XML elements
|
||||
\end{itemize}
|
||||
|
||||
|
||||
A \XMPP{} domain is served by one or more \ejabberd{} nodes. These nodes can
|
||||
|
||||
|
||||
|
||||
\subsection{How it works}
|
||||
\label{sec:howitworks}
|
||||
|
||||
|
||||
|
||||
A \Jabber{} domain is served by one or more \ejabberd{} 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
|
||||
@@ -116,9 +115,9 @@ Each \ejabberd{} node have following modules:
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\subsection{Router}
|
||||
\subsubsection{Router}
|
||||
|
||||
This module is the main router of \XMPP{} packets on each node. It routes
|
||||
This module is the main router of \Jabber{} 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
|
||||
@@ -127,24 +126,24 @@ process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.
|
||||
|
||||
|
||||
\subsection{Local Router}
|
||||
\subsubsection{Local Router}
|
||||
|
||||
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's content.
|
||||
|
||||
|
||||
\subsection{Session Manager}
|
||||
\subsubsection{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.
|
||||
|
||||
|
||||
\subsection{S2S Manager}
|
||||
\subsubsection{S2S Manager}
|
||||
|
||||
This module routes packets to other \XMPP{} servers. First, it checks if an
|
||||
This module routes packets to other \Jabber{} 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
|
||||
@@ -152,83 +151,12 @@ it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.
|
||||
|
||||
|
||||
\section{Authentication}
|
||||
|
||||
\subsubsection{External}
|
||||
\label{externalauth}
|
||||
\ind{external authentication}
|
||||
|
||||
The external authentication script follows
|
||||
\footahref{http://www.erlang.org/doc/tutorial/c_portdriver.html}{the erlang port driver API}.
|
||||
\section{XML representation}
|
||||
\label{sec:xmlrepr}
|
||||
|
||||
That script is supposed to do theses actions, in an infinite loop:
|
||||
\begin{itemize}
|
||||
\item read from stdin: AABBBBBBBBB.....
|
||||
\begin{itemize}
|
||||
\item A: 2 bytes of length data (a short in network byte order)
|
||||
\item B: a string of length found in A that contains operation in plain text
|
||||
operation are as follows:
|
||||
\begin{itemize}
|
||||
\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
|
||||
\begin{itemize}
|
||||
\item A: the number 2 (coded as a short, which is bytes length of following result)
|
||||
\item B: the result code (coded as a short), should be 1 for success/valid, or 0 for failure/invalid
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
||||
Example python script
|
||||
\begin{verbatim}
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
from struct import *
|
||||
|
||||
def from_ejabberd():
|
||||
input_length = sys.stdin.read(2)
|
||||
(size,) = unpack('>h', input_length)
|
||||
return sys.stdin.read(size).split(':')
|
||||
|
||||
def to_ejabberd(bool):
|
||||
answer = 0
|
||||
if bool:
|
||||
answer = 1
|
||||
token = pack('>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)
|
||||
\end{verbatim}
|
||||
|
||||
\section{XML Representation}
|
||||
\label{xmlrepr}
|
||||
|
||||
Each XML stanza is represented as the following tuple:
|
||||
Each XML stanza represented as following tuple:
|
||||
\begin{verbatim}
|
||||
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
Name = string()
|
||||
@@ -241,14 +169,14 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
\end{verbatim}
|
||||
E.\,g. this stanza:
|
||||
\begin{verbatim}
|
||||
<message to='test@conference.example.org' type='groupchat'>
|
||||
<message to='test@conference.e.localhost' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
\end{verbatim}
|
||||
is represented as the following structure:
|
||||
represented as following structure:
|
||||
\begin{verbatim}
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
[{"to", "test@conference.e.localhost"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
@@ -258,23 +186,23 @@ is represented as the following structure:
|
||||
|
||||
|
||||
\section{Module \texttt{xml}}
|
||||
\label{xmlmod}
|
||||
\label{sec:xmlmod}
|
||||
|
||||
\begin{description}
|
||||
\item{\verb|element_to_string(El) -> string()|}
|
||||
\item[\verb|element_to_string(El) -> string()|]
|
||||
\begin{verbatim}
|
||||
El = XMLElement
|
||||
\end{verbatim}
|
||||
Returns string representation of XML stanza \texttt{El}.
|
||||
|
||||
\item{\verb|crypt(S) -> string()|}
|
||||
\item[\verb|crypt(S) -> string()|]
|
||||
\begin{verbatim}
|
||||
S = string()
|
||||
\end{verbatim}
|
||||
Returns string which correspond to \texttt{S} with encoded XML special
|
||||
characters.
|
||||
|
||||
\item{\verb|remove_cdata(ECList) -> EList|}
|
||||
\item[\verb|remove_cdata(ECList) -> EList|]
|
||||
\begin{verbatim}
|
||||
ECList = [ElementOrCDATA]
|
||||
EList = [XMLElement]
|
||||
@@ -283,7 +211,7 @@ EList = [XMLElement]
|
||||
|
||||
|
||||
|
||||
\item{\verb|get_path_s(El, Path) -> Res|}
|
||||
\item[\verb|get_path_s(El, Path) -> Res|]
|
||||
\begin{verbatim}
|
||||
El = XMLElement
|
||||
Path = [PathItem]
|
||||
@@ -297,15 +225,15 @@ Res = string() | XMLElement
|
||||
If \texttt{Path} is empty, then returns \texttt{El}. Else sequentially
|
||||
consider elements of \texttt{Path}. Each element is one of:
|
||||
\begin{description}
|
||||
\item{\verb|{elem, Name}|} \texttt{Name} is name of subelement of
|
||||
\item[\verb|{elem, Name}|] \texttt{Name} is name of subelement of
|
||||
\texttt{El}, if such element exists, then this element considered in
|
||||
following steps, else returns empty string.
|
||||
\item{\verb|{attr, Name}|} If \texttt{El} have attribute \texttt{Name}, then
|
||||
\item[\verb|{attr, Name}|] If \texttt{El} have attribute \texttt{Name}, then
|
||||
returns value of this attribute, else returns empty string.
|
||||
\item{\verb|cdata|} Returns CDATA of \texttt{El}.
|
||||
\item[\verb|cdata|] Returns CDATA of \texttt{El}.
|
||||
\end{description}
|
||||
|
||||
\item{TODO:}
|
||||
\item[TODO:]
|
||||
\begin{verbatim}
|
||||
get_cdata/1, get_tag_cdata/1
|
||||
get_attr/2, get_attr_s/2
|
||||
@@ -315,63 +243,49 @@ Res = string() | XMLElement
|
||||
\end{description}
|
||||
|
||||
|
||||
\section{Module \texttt{xml\_stream}}
|
||||
\label{xmlstreammod}
|
||||
|
||||
\begin{description}
|
||||
\item{\verb!parse_element(Str) -> XMLElement | {error, Err}!}
|
||||
\begin{verbatim}
|
||||
Str = string()
|
||||
Err = term()
|
||||
\end{verbatim}
|
||||
Parses \texttt{Str} using XML parser, returns either parsed element or error
|
||||
tuple.
|
||||
\end{description}
|
||||
|
||||
|
||||
\section{Modules}
|
||||
\label{emods}
|
||||
\section{\ejabberd{} modules}
|
||||
\label{sec:emods}
|
||||
|
||||
|
||||
%\subsection{gen\_mod behaviour}
|
||||
%\label{genmod}
|
||||
\subsection{\verb|gen_mod| behaviour}
|
||||
\label{sec:genmod}
|
||||
|
||||
%TBD
|
||||
TBD
|
||||
|
||||
\subsection{Module gen\_iq\_handler}
|
||||
\label{geniqhandl}
|
||||
\subsection{Module \verb|gen_iq_handler|}
|
||||
\label{sec:geniqhandl}
|
||||
|
||||
The module \verb|gen_iq_handler| allows to easily write handlers for IQ packets
|
||||
of particular XML namespaces that addressed to server or to users bare JIDs.
|
||||
|
||||
In this module the following functions are defined:
|
||||
\begin{description}
|
||||
\item{\verb|add_iq_handler(Component, Host, NS, Module, Function, Type)|}
|
||||
\item[\verb|add_iq_handler(Component, NS, Module, Function, Type)|]
|
||||
\begin{verbatim}
|
||||
Component = Module = Function = atom()
|
||||
Host = NS = string()
|
||||
NS = string()
|
||||
Type = no_queue | one_queue | parallel
|
||||
\end{verbatim}
|
||||
Registers function \verb|Module:Function| as handler for IQ packets on
|
||||
virtual host \verb|Host| that contain child of namespace \verb|NS| in
|
||||
\verb|Component|. Queueing discipline is \verb|Type|. There are at least
|
||||
two components defined:
|
||||
Registers function \verb|Module:Function| as handler for IQ packets that
|
||||
contain child of namespace \verb|NS| in \verb|Component|. Queueing
|
||||
discipline is \verb|Type|. There are at least two components defined:
|
||||
\begin{description}
|
||||
\item{\verb|ejabberd_local|} Handles packets that addressed to server JID;
|
||||
\item{\verb|ejabberd_sm|} Handles packets that addressed to users bare JIDs.
|
||||
\item[\verb|ejabberd_local|] Handles packets that addressed to server JID;
|
||||
\item[\verb|ejabberd_sm|] Handles packets that addressed to users bare JIDs.
|
||||
\end{description}
|
||||
\item{\verb|remove_iq_handler(Component, Host, NS)|}
|
||||
\item[\verb|remove_iq_handler(Component, NS)|]
|
||||
\begin{verbatim}
|
||||
Component = atom()
|
||||
Host = NS = string()
|
||||
NS = string()
|
||||
\end{verbatim}
|
||||
Removes IQ handler on virtual host \verb|Host| for namespace \verb|NS| from
|
||||
\verb|Component|.
|
||||
Removes IQ handler for namespace \verb|NS| from \verb|Component|.
|
||||
\end{description}
|
||||
|
||||
Handler function must have the following type:
|
||||
\begin{description}
|
||||
\item{\verb|Module:Function(From, To, IQ)|}
|
||||
\item[\verb|Module:Function(From, To, IQ)|]
|
||||
\begin{verbatim}
|
||||
From = To = jid()
|
||||
\end{verbatim}
|
||||
@@ -384,8 +298,8 @@ From = To = jid()
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_local_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -393,13 +307,13 @@ From = To = jid()
|
||||
|
||||
-define(NS_CPUTIME, "ejabberd:cputime").
|
||||
|
||||
start(Host, Opts) ->
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_CPUTIME,
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_CPUTIME,
|
||||
?MODULE, process_local_iq, IQDisc).
|
||||
|
||||
stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_CPUTIME).
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case Type of
|
||||
@@ -418,26 +332,25 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
|
||||
|
||||
\subsection{Services}
|
||||
\label{services}
|
||||
\label{sec:services}
|
||||
|
||||
%TBD
|
||||
TBD
|
||||
|
||||
|
||||
%TODO: use \verb|proc_lib|
|
||||
TODO: use \verb|proc_lib|
|
||||
\begin{verbatim}
|
||||
-module(mod_echo).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, init/1, stop/1]).
|
||||
-export([start/1, init/1, stop/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
|
||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
spawn(?MODULE, init, [MyHost])).
|
||||
start(Opts) ->
|
||||
Host = gen_mod:get_opt(host, Opts, "echo." ++ ?MYNAME),
|
||||
register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
@@ -449,16 +362,15 @@ loop(Host) ->
|
||||
ejabberd_router:route(To, From, Packet),
|
||||
loop(Host);
|
||||
stop ->
|
||||
ejabberd_router:unregister_route(Host),
|
||||
ejabberd_router:unregister_local_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
Proc ! stop,
|
||||
{wait, Proc}.
|
||||
stop() ->
|
||||
ejabberd_mod_echo ! stop,
|
||||
ok.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
APPNAME = ejabberd
|
||||
VSN = $(shell sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../../src/ejabberd.app)
|
||||
|
||||
DOCDIR=.
|
||||
SRCDIR=../../src
|
||||
|
||||
.PHONY = all
|
||||
|
||||
all: docs
|
||||
|
||||
clean:
|
||||
rm -f *.html
|
||||
rm edoc-info
|
||||
rm erlang.png
|
||||
|
||||
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
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 976 B |
|
Before Width: | Height: | Size: 679 B |
@@ -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;
|
||||
}
|
||||
@@ -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("<<")),beside(Doc,floating(text(">>"))));
|
||||
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]) -> ">"++debracket(Str);
|
||||
debracket([$<|Str]) -> "<"++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.
|
||||
@@ -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.
|
||||
|
Before Width: | Height: | Size: 318 B |
@@ -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.
|
||||
@@ -1,115 +0,0 @@
|
||||
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;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
color: #4a5389;
|
||||
border-bottom: solid 1px #000;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
text-align: right;
|
||||
color: #4a5389;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
color: #900;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a[href] {
|
||||
color: #4a5389;
|
||||
}
|
||||
|
||||
a[href]:hover {
|
||||
background-color: #ecefff;
|
||||
}
|
||||
|
||||
p, li, dd {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-style: italic;
|
||||
color: #009;
|
||||
text-align: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
table[border="1"] {
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
table[border="1"] td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
color: #461b7e;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin:1ex 2ex;
|
||||
border:1px dashed lightgrey;
|
||||
background-color:#f9f9f9;
|
||||
padding:0.5ex;
|
||||
}
|
||||
|
||||
pre em {
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt {
|
||||
margin:0ex 2ex;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin:0ex 0ex 1ex 4ex;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 717 B |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.8 KiB |
@@ -1,136 +0,0 @@
|
||||
\documentclass[a4paper,10pt]{article}
|
||||
|
||||
%% Packages
|
||||
\usepackage{epsfig}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphics}
|
||||
\usepackage{hevea}
|
||||
\usepackage[pdftex,colorlinks,unicode,urlcolor=blue,linkcolor=blue,pdftitle=Ejabberd\
|
||||
Feature\ Sheet,pdfauthor=Sander\
|
||||
Devrieze,pdfsubject=ejabberd,pdfkeywords=ejabberd]{hyperref}
|
||||
\usepackage{verbatim}
|
||||
\usepackage{color}
|
||||
|
||||
%% Index
|
||||
% Remove the index anchors from the HTML version to save size and bandwith.
|
||||
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
|
||||
|
||||
%% Images
|
||||
\newcommand{\logoscale}{0.7}
|
||||
\newcommand{\imgscale}{0.58}
|
||||
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
|
||||
\newcommand{\insscaleimg}[2]{
|
||||
\imgsrc{#2}{}
|
||||
\begin{latexonly}
|
||||
\scalebox{#1}{\includegraphics{#2}}
|
||||
\end{latexonly}
|
||||
}
|
||||
|
||||
%% Various
|
||||
\newcommand{\bracehack}{\def\{{\char"7B}\def\}{\char"7D}}
|
||||
\newcommand{\titem}[1]{\item[\bracehack\texttt{#1}]}
|
||||
\newcommand{\ns}[1]{\texttt{#1}}
|
||||
\newcommand{\jid}[1]{\texttt{#1}}
|
||||
\newcommand{\option}[1]{\texttt{#1}}
|
||||
\newcommand{\poption}[1]{{\bracehack\texttt{#1}}}
|
||||
\newcommand{\node}[1]{\texttt{#1}}
|
||||
\newcommand{\term}[1]{\texttt{#1}}
|
||||
\newcommand{\shell}[1]{\texttt{#1}}
|
||||
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
|
||||
%% Title page
|
||||
\include{version}
|
||||
\title{Ejabberd \version\ Feature Sheet}
|
||||
\author{Sander Devrieze \\
|
||||
\ahrefurl{mailto:s.devrieze@pandora.be} \\
|
||||
\ahrefurl{xmpp:sander@devrieze.dyndns.org}}
|
||||
|
||||
% Options
|
||||
\newcommand{\marking}[1]{\textbf{\begin{large}\textcolor{ejblue}{#1}\end{large}}} % Marking enabled
|
||||
\newcommand{\quoting}[2][yozhik]{\begin{quotation}\textcolor{#1}{\textit{#2}}\end{quotation}} % Quotes enabled
|
||||
\newcommand{\new}{\marginpar{\textcolor{red}{\textsc{new}}}} % Highlight new features
|
||||
\newcommand{\improved}{\marginpar{\textcolor{orange}{\textsc{improved}}}} % Highlight improved features
|
||||
\setcounter{secnumdepth}{-1} % Disable section numbering
|
||||
|
||||
%% To by-pass errors in the HTML version.
|
||||
\newstyle{SPAN}{width:20\%; float:right; text-align:left; margin-left:auto;}
|
||||
\definecolor{orange} {cmyk}{0.000,0.333,1.000,0.000}
|
||||
|
||||
%% Footnotes
|
||||
\begin{latexonly}
|
||||
\global\parskip=9pt plus 3pt minus 1pt
|
||||
\global\parindent=0pt
|
||||
\gdef\ahrefurl#1{\href{#1}{\texttt{#1}}}
|
||||
\gdef\footahref#1#2{#2\footnote{\href{#1}{\texttt{#1}}}}
|
||||
\end{latexonly}
|
||||
\newcommand{\txepref}[2]{\footahref{http://www.xmpp.org/extensions/xep-#1.html}{#2}}
|
||||
\newcommand{\xepref}[1]{\txepref{#1}{XEP-#1}}
|
||||
|
||||
%% Fancy header
|
||||
\fancyhf{}
|
||||
\pagestyle{fancy}
|
||||
\rhead{\textcolor{ejblue}{The Expandable Jabber/XMPP Daemon.}}
|
||||
\renewcommand{\headrule}{{\color{ejblue}%
|
||||
\hrule width\headwidth height\headrulewidth \vskip-\headrulewidth}}
|
||||
\lhead{\setlength{\unitlength}{-6mm}
|
||||
\begin{picture}(0,0)
|
||||
\put(5.8,3.25){\includegraphics[width=1.3\textwidth]{yozhikheader.png}}
|
||||
\end{picture}}
|
||||
|
||||
% Official ejabberd colours
|
||||
\definecolor{ejblue} {cmyk}{1.000,0.831,0.000,0.537} %RGB: 0,0,118 HTML: 000076
|
||||
\definecolor{ejgreenyellow}{cmyk}{0.079,0.000,0.275,0.102} %RGB: 209,229,159 HTML: d1e59f
|
||||
\definecolor{ejgreendark} {cmyk}{0.131,0.000,0.146,0.220} %RGB: 166,199,162 HTML: a6c7a2
|
||||
\definecolor{ejgreen} {cmyk}{0.077,0.000,0.081,0.078} %RGB: 216,236,215 HTML: d8ecd7
|
||||
\definecolor{ejgreenwhite} {cmyk}{0.044,0.000,0.048,0.020} %RGB: 239,250,238 HTML: effaee
|
||||
\definecolor{yozhik} {cmyk}{0.000,0.837,1.000,0.424} %RGB: 147,0,0 HTML: 930000
|
||||
|
||||
\begin{document}
|
||||
|
||||
\label{titlepage}
|
||||
\begin{titlepage}
|
||||
\maketitle{}
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\begin{center}
|
||||
{\insscaleimg{\logoscale}{logo.png}
|
||||
\par
|
||||
}
|
||||
\end{center}
|
||||
\quoting{I can thoroughly recommend ejabberd for ease of setup --
|
||||
Kevin Smith, Current maintainer of the Psi project}
|
||||
|
||||
\end{titlepage}
|
||||
|
||||
\newpage
|
||||
% Set the page counter to 2 so that the titlepage and the second page do not
|
||||
% have the same page number. This fixes the PDFLaTeX warning "destination with
|
||||
% the same identifier".
|
||||
\begin{latexonly}
|
||||
\setcounter{page}{2}
|
||||
\pagecolor{ejgreenwhite}
|
||||
\end{latexonly}
|
||||
|
||||
% Input introduction.tex
|
||||
\input{introduction}
|
||||
|
||||
\end{document}
|
||||
|
||||
%% TODO
|
||||
% * illustrations (e.g. screenshot from web interface)
|
||||
% * commented parts
|
||||
% * slides, guide and html version
|
||||
% * cleaning and improving LaTeX code
|
||||
% * key features: something like this (shorter)? (more focussed on Erlang now): "To reach the goal of high
|
||||
% availability, performance and clustering, ejabberd is written in Erlang, a programming language perfectly
|
||||
% suited for this. Besides that, some parts are written in C to also incude the advantages of this language. In
|
||||
% short, ejabberd is a perfect mix of mainly Erlang code, peppered with some C code to get the final touch!"
|
||||
% <picture of a cocktail>
|
||||
% * key features: saying that ejabberd the only XMPP server is that can do real clustering:
|
||||
% http://www.jivesoftware.org/forums/thread.jspa?threadID=14602
|
||||
% "What I find interesting is that *no* XMPP servers truly provide clustering. This includes all the commercial
|
||||
% servers. The one partial exception appears to be ejabberd, which can cluster certain data such as sessions,
|
||||
% but not all services such as MUC."
|
||||
% * try it today: links to migration tutorials
|
||||
@@ -1,132 +0,0 @@
|
||||
\chapter{Introduction}
|
||||
\label{intro}
|
||||
|
||||
%% TODO: improve the feature sheet with a nice table to highlight new features.
|
||||
|
||||
\quoting{I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. ---
|
||||
Joeri}
|
||||
|
||||
%ejabberd is a free and open source instant messaging server written in Erlang. ejabberd is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication (Jabber/XMPP).
|
||||
|
||||
\ejabberd{} is a \marking{free and open source} instant messaging server written in \footahref{http://www.erlang.org/}{Erlang/OTP}.
|
||||
|
||||
\ejabberd{} is \marking{cross-platform}, distributed, fault-tolerant, and based on open standards to achieve real-time communication.
|
||||
|
||||
\ejabberd{} is designed to be a \marking{rock-solid and feature rich} XMPP server.
|
||||
|
||||
\ejabberd{} is suitable for small deployments, whether they need to be \marking{scalable} or not, as well as extremely big deployments.
|
||||
|
||||
%\subsection{Layout with example deployment (title needs a better name)}
|
||||
%\label{layout}
|
||||
|
||||
%In this section there will be a graphical overview like these:\\
|
||||
%\verb|http://www.tipic.com/var/timp/timp_dep.gif| \\
|
||||
%\verb|http://www.jabber.com/images/jabber_Com_Platform.jpg| \\
|
||||
%\verb|http://www.antepo.com/files/OPN45systemdatasheet.pdf| \\
|
||||
|
||||
%A page full with names of Jabber client that are known to work with ejabberd. \begin{tiny}tiny font\end{tiny}
|
||||
|
||||
%\subsection{Try It Today}
|
||||
%\label{trytoday}
|
||||
|
||||
%(Not sure if I will include/finish this section for the next version.)
|
||||
|
||||
%\begin{itemize}
|
||||
%\item Erlang REPOS
|
||||
%\item Packages in distributions
|
||||
%\item Windows binary
|
||||
%\item source tar.gz
|
||||
%\item Migration from Jabberd14 (and so also Jabberd2 because you can migrate from version 2 back to 14) and Jabber Inc. XCP possible.
|
||||
%\end{itemize}
|
||||
|
||||
\newpage
|
||||
\section{Key Features}
|
||||
\label{keyfeatures}
|
||||
\ind{features!key features}
|
||||
|
||||
\quoting{Erlang seems to be tailor-made for writing stable, robust servers. ---
|
||||
Peter Saint-Andr\'e, Executive Director of the Jabber Software Foundation}
|
||||
|
||||
\ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item \marking{Cross-platform:} \ejabberd{} runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.
|
||||
|
||||
\item \marking{Distributed:} You can run \ejabberd{} 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.
|
||||
|
||||
\item \marking{Fault-tolerant:} You can deploy an \ejabberd{} cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.
|
||||
|
||||
\item \marking{Administrator Friendly:} \ejabberd{} is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
\begin{itemize}
|
||||
\item Comprehensive documentation.
|
||||
\item Straightforward installers for Linux, Mac OS X, and Windows. %%\improved{}
|
||||
\item Web Administration.
|
||||
\item Shared Roster Groups.
|
||||
\item Command line administration tool. %%\improved{}
|
||||
\item Can integrate with existing authentication mechanisms.
|
||||
\item Capability to send announce messages.
|
||||
\end{itemize}
|
||||
|
||||
\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 Support for \footahref{http://www.ietf.org/rfc/rfc3490.txt}{IDNA}.
|
||||
\end{itemize}
|
||||
|
||||
\item \marking{Open Standards:} \ejabberd{} is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
\begin{itemize}
|
||||
\item Fully XMPP compliant.
|
||||
\item XML-based protocol.
|
||||
\item \footahref{http://www.ejabberd.im/protocols}{Many protocols supported}.
|
||||
\end{itemize}
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\newpage
|
||||
|
||||
\section{Additional Features}
|
||||
\label{addfeatures}
|
||||
\ind{features!additional features}
|
||||
|
||||
\quoting{ejabberd is making inroads to solving the "buggy incomplete server" problem ---
|
||||
Justin Karneges, Founder of the Psi and the Delta projects}
|
||||
|
||||
Moreover, \ejabberd{} comes with a wide range of other state-of-the-art features:
|
||||
\begin{itemize}
|
||||
\item Modular
|
||||
\begin{itemize}
|
||||
\item Load only the modules you want.
|
||||
\item Extend \ejabberd{} with your own custom modules.
|
||||
\end{itemize}
|
||||
\item Security
|
||||
\begin{itemize}
|
||||
\item SASL and STARTTLS for c2s and s2s connections.
|
||||
\item STARTTLS and Dialback s2s connections.
|
||||
\item Web Admin accessible via HTTPS secure access.
|
||||
\end{itemize}
|
||||
\item Databases
|
||||
\begin{itemize}
|
||||
\item Internal database for fast deployment (Mnesia).
|
||||
\item Native MySQL support.
|
||||
\item Native PostgreSQL support.
|
||||
\item ODBC data storage support.
|
||||
\item Microsoft SQL Server support. %%\new{}
|
||||
\end{itemize}
|
||||
\item Authentication
|
||||
\begin{itemize}
|
||||
\item Internal Authentication.
|
||||
\item PAM, LDAP and ODBC. %%\improved{}
|
||||
\item External Authentication script.
|
||||
\end{itemize}
|
||||
\item Others
|
||||
\begin{itemize}
|
||||
\item Support for virtual hosting.
|
||||
\item Compressing XML streams with Stream Compression (\xepref{0138}).
|
||||
\item Statistics via Statistics Gathering (\xepref{0039}).
|
||||
\item IPv6 support both for c2s and s2s connections.
|
||||
\item \txepref{0045}{Multi-User Chat} module with support for clustering and HTML logging. %%\improved{}
|
||||
\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 Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB |
@@ -1,62 +0,0 @@
|
||||
Release notes
|
||||
ejabberd 0.9.1
|
||||
|
||||
This document describes the main changes from [25]ejabberd 0.9.
|
||||
|
||||
The code can be downloaded from the [26]download page.
|
||||
|
||||
For more detailed information, please refer to ejabberd [27]User Guide.
|
||||
|
||||
|
||||
Groupchat (Multi-user chat and IRC) improvements
|
||||
|
||||
The multi-user chat code has been improved to comply with the latest version
|
||||
of Jabber Enhancement Proposal 0045.
|
||||
|
||||
The IRC (Internet Relay Chat) features now support WHOIS and USERINFO
|
||||
requests.
|
||||
|
||||
|
||||
Web interface
|
||||
|
||||
ejabberd modules management features have been added to the web interface.
|
||||
They now allow to start or stop extension module without restarting the
|
||||
ejabberd server.
|
||||
|
||||
|
||||
Publish and subscribe
|
||||
|
||||
It is now possible to a subscribe node with a JabberID that includes a
|
||||
resource.
|
||||
|
||||
|
||||
Translations
|
||||
|
||||
A new script has been included to help translate ejabberd into new languages
|
||||
and maintain existing translations.
|
||||
|
||||
As a result, ejabberd is now translating into 10 languages:
|
||||
* Dutch
|
||||
* English
|
||||
* French
|
||||
* German
|
||||
* Polish
|
||||
* Portuguese
|
||||
* Russian
|
||||
* Spanish
|
||||
* Swedish
|
||||
* Ukrainian
|
||||
|
||||
|
||||
Migration
|
||||
|
||||
No changes have been made to the database. No particular conversion steps
|
||||
are needed. However, you should backup your database before upgrading to a
|
||||
new ejabberd version.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
This release contains several bugfixes and architectural changes. Please
|
||||
refer to the Changelog file supplied with this release for details of all
|
||||
improvements in the ejabberd code.
|
||||
@@ -1,99 +0,0 @@
|
||||
Release notes
|
||||
ejabberd 0.9.8
|
||||
2005-08-01
|
||||
|
||||
This document describes the main changes in ejabberd 0.9.8. This
|
||||
version prepares the way for the release of ejabberd 1.0, which
|
||||
is due later this year.
|
||||
|
||||
The code can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
For more detailed information, please refer to ejabberd User Guide
|
||||
on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
|
||||
Recent changes include....
|
||||
|
||||
|
||||
Enhanced virtual hosting
|
||||
|
||||
Virtual hosting applies to many more setting options and
|
||||
features and is transparent. Virtual hosting accepts different
|
||||
parameters for different virtual hosts regarding the following
|
||||
features: authentication method, access control lists and access
|
||||
rules, users management, statistics, and shared roster. The web
|
||||
interface gives access to each virtual host's parameters.
|
||||
|
||||
|
||||
Enhanced Publish-Subscribe module
|
||||
|
||||
ejabberd's Publish-Subscribe module integrates enhancements
|
||||
coming from J-EAI, an XMPP-based integration server built on
|
||||
ejabberd. ejabberd thus supports Publish-Subscribe node
|
||||
configuration. It is possible to define nodes that should be
|
||||
persistent, and the number of items to persist. Besides that, it
|
||||
is also possible to define various notification parameters, such
|
||||
as the delivery of the payload with the notifications, and the
|
||||
notification of subscribers when some changes occur on items.
|
||||
Other examples are: the maximum size of the items payload, the
|
||||
subscription approvers, the limitation of the notification to
|
||||
online users only, etc.
|
||||
|
||||
|
||||
Code reorganisation and update
|
||||
|
||||
- The mod_register module has been cleaned up.
|
||||
- ODBC support has been updated and several bugs have been fixed.
|
||||
|
||||
|
||||
Development API
|
||||
|
||||
To ease the work of Jabber/XMPP developers, a filter_packet hook
|
||||
has been added. As a result it is possible to develop plugins to
|
||||
filter or modify packets flowing through ejabberd.
|
||||
|
||||
|
||||
Translations
|
||||
|
||||
- Translations have been updated to support the new Publish-Subscribe features.
|
||||
- A new Brazilian Portuguese translation has been contributed.
|
||||
|
||||
|
||||
Web interface
|
||||
|
||||
- The CSS stylesheet from the web interface is W3C compliant.
|
||||
|
||||
|
||||
Installers
|
||||
|
||||
Installers are provided for Microsoft Windows and Linux/x86. The
|
||||
Linux installer includes Erlang ASN.1 modules for LDAP
|
||||
authentication support.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains several bugfixes and architectural
|
||||
changes. Among other bugfixes include improvements in LDAP
|
||||
authentication. Please refer to the ChangeLog file supplied
|
||||
with this release regarding all improvements in ejabberd.
|
||||
|
||||
|
||||
References
|
||||
|
||||
The ejabberd feature sheet helps comparing with other Jabber/XMPP
|
||||
servers:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs/features.pdf
|
||||
|
||||
Contributed tutorials of interest are:
|
||||
- Migration from Jabberd1.4 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd1-to-ejabberd
|
||||
- Migration from Jabberd2 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd2-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
|
||||
END
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
Release notes
|
||||
ejabberd 0.9
|
||||
|
||||
This document describes the major new features of and changes to
|
||||
ejabberd 0.9, compared to latest public release ejabber 0.7.5.
|
||||
|
||||
For more detailed information, please refer to ejabberd User
|
||||
Guide.
|
||||
|
||||
|
||||
Virtual Hosting
|
||||
|
||||
ejabberd now can host several domain on the same instance.
|
||||
This option is enabled by using:
|
||||
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
|
||||
instead of the previous host directive.
|
||||
|
||||
Note that you are now using a list of hosts. The main one should
|
||||
be the first listed. See migration section further in this release
|
||||
note for details.
|
||||
|
||||
|
||||
Shared Roster
|
||||
|
||||
Shared roster is a new feature that allow the ejabberd
|
||||
administrator to add jabber user that will be present in the
|
||||
roster of every users on the server.
|
||||
Shared roster are enabled by adding:
|
||||
|
||||
{mod_shared_roster, []}
|
||||
|
||||
at the end of your module list in your ejabberd.cfg file.
|
||||
|
||||
|
||||
PostgreSQL (ODBC) support
|
||||
|
||||
This feature is experimental and not yet properly documented. This
|
||||
feature is released for testing purpose.
|
||||
|
||||
You need to have Erlang/OTP R10 to compile with ODBC on various
|
||||
flavour of *nix. You should use Erlang/OTP R10B-4, as this task
|
||||
has became easier with this release. It comes already build in
|
||||
Erlang/OTP Microsoft Windows binary.
|
||||
|
||||
PostgreSQL support is enabled by using the following module in
|
||||
ejabberd.cfg instead of their standard counterpart:
|
||||
|
||||
mod_last_odbc.erl
|
||||
mod_offline_odbc.erl
|
||||
mod_roster_odbc.erl
|
||||
|
||||
The database schema is located in the src/odbc/pq.sql file.
|
||||
|
||||
Look at the src/ejabberd.cfg.example file for more information on
|
||||
how to configure ejabberd with odbc support. You can get support
|
||||
on how to configure ejabberd with a relational database.
|
||||
|
||||
|
||||
Migration from ejabberd 0.7.5
|
||||
|
||||
Migration is pretty straightforward as Mnesia database schema
|
||||
conversions is handled automatically. Remember however that you
|
||||
must backup your ejabberd database before migration.
|
||||
|
||||
Here are the following steps to proceed:
|
||||
|
||||
1. Stop your instance of ejabberd.
|
||||
|
||||
2. In ejabberd.cfg, define the host lists. Change the host
|
||||
directive to the hosts one:
|
||||
Before:
|
||||
{host, "erlang-projects.org"}.
|
||||
After:
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
Note that when you restart the server the existing users will be
|
||||
affected to the first virtual host, so the order is important. You
|
||||
should keep the previous hostname as the first virtual host.
|
||||
|
||||
3. Restart ejabberd.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
This release contains several bugfixes and architectural changes.
|
||||
Please refer to the Changelog file supplied with this release for
|
||||
details of all improvements in the ejabberd code.
|
||||
@@ -1,120 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.0.0
|
||||
14 December 2005
|
||||
|
||||
This document describes the main changes in ejabberd 1.0.0. Unique in this
|
||||
version is the compliancy with the XMPP (eXtensible Messaging and Presence
|
||||
Protocol) standard. ejabberd is the first Open Source Jabber server claiming
|
||||
to fully comply to the XMPP standard.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
Server-to-server Encryption for Enhanced Security
|
||||
|
||||
- Support for STARTTLS and SASL EXTERNAL to secure server-to-server traffic
|
||||
has been added.
|
||||
- Also, STARTTLS and Dialback has been implemented for server-to-server (s2s)
|
||||
connections. Detailed information about these new features can be found on
|
||||
http://ejabberd.jabber.ru/s2s-encryption
|
||||
- commonName and dNSName fields matching were introduced to ease the process
|
||||
of retrieving certificates.
|
||||
- Different certificates can be defined for each virtual host.
|
||||
|
||||
ODBC Support
|
||||
|
||||
- ODBC support has been improved to allow production use of ejabberd with
|
||||
relational databases.
|
||||
- Support for vCard storage in ODBC has been added.
|
||||
- ejd2odbc.erl is a tool to convert an installation from Erlang's database
|
||||
Mnesia to an ODBC compatible relational database.
|
||||
|
||||
Native PostgreSQL Support
|
||||
|
||||
- Native PostgreSQL support gives you a better performance when you use
|
||||
PostgreSQL.
|
||||
|
||||
Shared Roster groups
|
||||
|
||||
- Shared Roster groups support has been enhanced. New is the ability to add
|
||||
all registered users to everyone's roster. Detailed information about this
|
||||
new feature can be found on http://ejabberd.jabber.ru/shared-roster-all
|
||||
|
||||
Web Interface
|
||||
|
||||
- The web interface internal code has been modified for better integration
|
||||
and compliancy with J-EAI, an ejabberd-based Enterprise Application
|
||||
Integration platform.
|
||||
- More XHTML 1.0 Transitional compliancy work was done.
|
||||
|
||||
Transports
|
||||
|
||||
- A transport workaround can be enabled during compilation. To do this, you
|
||||
can pass the "--enable-roster-gateway-workaround" option to the configure
|
||||
script. (./configure --enable-roster-gateway-workaround)
|
||||
This option allows transports to add items with subscription "to" in the
|
||||
roster by sending <presence type='subscribed'/> stanza to user. This option
|
||||
is only needed for JIT ICQ transport.
|
||||
Warning: by enabling this option, ejabberd will not be fully XMPP compliant
|
||||
anymore.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains several bugfixes.
|
||||
- Among other bugfixes include improvements to the client-to-server (c2s)
|
||||
connection management module.
|
||||
- Please refer to the ChangeLog file supplied
|
||||
with this release regarding all improvements in ejabberd.
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C to run ejabberd 1.0.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows and Linux/x86.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- When upgrading an ODBC-based installation, you will need to change the
|
||||
relational database schema. The following SQL commands must be run on the
|
||||
database:
|
||||
CREATE SEQUENCE spool_seq_seq;
|
||||
ALTER TABLE spool ADD COLUMN seq integer;
|
||||
ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
|
||||
UPDATE spool SET seq = DEFAULT;
|
||||
ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials of interest are:
|
||||
- Migration from Jabberd1.4 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd1-to-ejabberd
|
||||
- Migration from Jabberd2 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd2-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
|
||||
END
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.1.0
|
||||
24 April 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.0. This version
|
||||
introduce new features including support for new Jabber Enhancement
|
||||
Proposals and several performance improvements enabling deployments on an
|
||||
even larger scale than already possible.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
A complete list of changes is available from:
|
||||
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
New Jabber Enhancement Proposal support:
|
||||
|
||||
- JEP-0050: Ad-Hoc Commands.
|
||||
- JEP-0138: Stream Compression.
|
||||
- JEP-0175: SASL anonymous.
|
||||
|
||||
Anonymous login
|
||||
|
||||
- SASL anonymous.
|
||||
- Anonymous login for clients that do not yet support SASL Anonymous.
|
||||
|
||||
Relational database Support
|
||||
|
||||
- MySQL is now fully supported through ODBC and in native mode.
|
||||
- Various improvements to the native database interfaces.
|
||||
- The migration tool can use relational databases.
|
||||
|
||||
Multi-User Chat improvements
|
||||
|
||||
- Logging of room discussion to text file is now supported.
|
||||
- Better reconfiguration support.
|
||||
- Security oriented fixes.
|
||||
- Several improvements and updates to latest JEP-0045.
|
||||
|
||||
Performance scalability improvements for large clusters
|
||||
|
||||
- Improved session synchronisation management between cluster nodes.
|
||||
- Internal architecture has been reworked to use generalize Erlang/OTP
|
||||
framework usage.
|
||||
- Speed improvement on logger.
|
||||
- TCP/IP packet reception change for better network throttling and
|
||||
regulation.
|
||||
As a result, these improvements will reduce load on large scale deployments.
|
||||
|
||||
XMPP Protocol related improvements
|
||||
|
||||
- XML stanza size can be limited.
|
||||
- Messages are send to all resources with the same highest priority.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Web interface
|
||||
|
||||
- XHTML 1.0 compliance.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
|
||||
chain, installers, IRC gateway, ejabberdctl, amongst others.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The database schema has not been changed comparing to version 1.0.0 and
|
||||
consequently it does not require any migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Using ejabberd with MySQL native driver:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
|
||||
- Anonymous User Support:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
||||
@@ -1,119 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.1.1
|
||||
28 April 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.x. This version
|
||||
introduce new features including support for new Jabber Enhancement
|
||||
Proposals and several performance improvements enabling deployments on an
|
||||
even larger scale than already possible.
|
||||
|
||||
This release fix a security issue introduced in ejabberd 1.1.0. In SASL
|
||||
mode, anonymous login was enabled as a default. Upgrading ejabberd 1.1.0 to
|
||||
ejabberd 1.1.1 is highly recommended.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
A complete list of changes is available from:
|
||||
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
New Jabber Enhancement Proposal support:
|
||||
|
||||
- JEP-0050: Ad-Hoc Commands.
|
||||
- JEP-0138: Stream Compression.
|
||||
- JEP-0175: SASL anonymous.
|
||||
|
||||
Anonymous login
|
||||
|
||||
- SASL anonymous.
|
||||
- Anonymous login for clients that do not yet support SASL Anonymous.
|
||||
|
||||
Relational database Support
|
||||
|
||||
- MySQL is now fully supported through ODBC and in native mode.
|
||||
- Various improvements to the native database interfaces.
|
||||
- The migration tool can use relational databases.
|
||||
|
||||
Multi-User Chat improvements
|
||||
|
||||
- Logging of room discussion to text file is now supported.
|
||||
- Better reconfiguration support.
|
||||
- Security oriented fixes.
|
||||
- Several improvements and updates to latest JEP-0045.
|
||||
|
||||
Performance scalability improvements for large clusters
|
||||
|
||||
- Improved session synchronisation management between cluster nodes.
|
||||
- Internal architecture has been reworked to use generalize Erlang/OTP
|
||||
framework usage.
|
||||
- Speed improvement on logger.
|
||||
- TCP/IP packet reception change for better network throttling and
|
||||
regulation.
|
||||
As a result, these improvements will reduce load on large scale deployments.
|
||||
|
||||
XMPP Protocol related improvements
|
||||
|
||||
- XML stanza size can be limited.
|
||||
- Messages are send to all resources with the same highest priority.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Web interface
|
||||
|
||||
- XHTML 1.0 compliance.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
|
||||
chain, installers, IRC gateway, ejabberdctl, amongst others.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The database schema has not been changed comparing to version 1.0.0 and
|
||||
consequently it does not require any migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Using ejabberd with MySQL native driver:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
|
||||
- Anonymous User Support:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
||||
@@ -1,119 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.1.2
|
||||
27 September 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.2.
|
||||
|
||||
This version is a major improvement over ejabberd 1.1.1, improving the
|
||||
overall behaviour of the server in many areas. Users of ejabberd 1.1.1
|
||||
should upgrade to this new release for improved robustness and compliance.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the Feature Sheet and in the
|
||||
Installation and Operation Guide which are both available on the
|
||||
Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
ejabberd includes 44 improvements. A complete list of changes can be
|
||||
retrieved from:
|
||||
http://redir.process-one.net/ejabberd-1.1.2
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
LDAP Improvements
|
||||
|
||||
- Major improvements have been made on the LDAP module. It is now more
|
||||
flexible and more robust.
|
||||
|
||||
HTTP Polling Fixes
|
||||
|
||||
- The HTTP polling modules have been fixed and improved: the connections are
|
||||
closed properly and polled messages cannot be lost anymore.
|
||||
|
||||
Roster Management Improvement
|
||||
|
||||
- Roster management improvements increase reliability, especially in cases
|
||||
where users are on different servers.
|
||||
- Shared rosters are more reliable.
|
||||
|
||||
Improved Robustness
|
||||
|
||||
- It is now possible to limit the number of opened connections for a single
|
||||
user.
|
||||
|
||||
Relational databases
|
||||
|
||||
- Database support: Microsoft SQL Server is now officially supported in ODBC
|
||||
mode.
|
||||
|
||||
Publish-Subscribe Improvement
|
||||
|
||||
- Restricting node creation with a dedicated ACL rule is now possible.
|
||||
|
||||
Localization
|
||||
|
||||
- A Czech translation has been added.
|
||||
- Translations have been updated.
|
||||
|
||||
Binary Installer
|
||||
|
||||
- New binary installer for Windows including all requirements.
|
||||
- Improved installers for Linux and MacOSX (PowerPC)
|
||||
|
||||
XMPP Compliancy
|
||||
|
||||
- Some protocol compliance fix have been added, after the Portland XMPP
|
||||
Interop Meeting in July.
|
||||
|
||||
Miscelanous
|
||||
|
||||
- MUC have been improved (logging rendering).
|
||||
- The command line tool ejabberdctl has been improved.
|
||||
- The build chain has been improved, including MacOSX support.
|
||||
- The documentation has been improved and updated to describe the new
|
||||
features.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.2.
|
||||
- The recommanded version is Erlang/OTP R10B-10.
|
||||
- Erlang/OTP R11B has not yet been fully certified for ejabberd.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
They can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The relational database schema has changed between version 1.1.1 and
|
||||
1.1.2. An "askmessage" column needs to be added in the "rosterusers" table
|
||||
to perform the migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from other XMPP servers to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
||||
@@ -1,14 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.1.3
|
||||
2 February 2007
|
||||
|
||||
ejabberd 1.1.3 is a security fix release for ejabberd roster ODBC
|
||||
module.
|
||||
|
||||
The upgrade is only necessary if you are using ejabberd with the
|
||||
mod_roster_odbc.
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
END
|
||||
@@ -1,31 +0,0 @@
|
||||
Release Notes
|
||||
ejabberd 1.1.4
|
||||
3 september 2007
|
||||
|
||||
ejabberd 1.1.4 is a bugfix release for ejabberd 1.1.x branch.
|
||||
|
||||
ejabberd 1.1.4 includes 10 improvements. A complete list of changes
|
||||
can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-1.1.4
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
- Better LDAP support.
|
||||
- Support for UTF-8 with MySQL 5.
|
||||
- Roster migration script bugfixes.
|
||||
- Performance improvements on user removal.
|
||||
- Traffic shapers bugfix.
|
||||
- Configuration: host value is now case insensitive.
|
||||
- Build: ejabberd.cfg file is not overwritten with 'make install' command.
|
||||
|
||||
|
||||
Bugs report
|
||||
|
||||
You can officially report bugs on Process-one support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,208 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.0
|
||||
21 February 2008
|
||||
|
||||
ejabberd 2.0.0 is a major new version for ejabberd adding plenty of
|
||||
new features, performance and scalability improvements and
|
||||
architectural changes.
|
||||
|
||||
ejabberd 2.0.0 includes more than 200 improvements over ejabberd
|
||||
1.1.x. A complete list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.0
|
||||
|
||||
The new code can be downloaded from ejabberd downloads page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
* Clustering and Architecture
|
||||
|
||||
- New front-end and back-end cluster architecture for better
|
||||
scalability and robustness. Back-end nodes are able to run a fully
|
||||
fault-tolerant XMPP router and services, but you can now deploy
|
||||
many front-end nodes to share the load without needing to synchronize
|
||||
any state with the back-ends.
|
||||
- All components now run in cluster mode (For example, Multi-User chat
|
||||
service and file transfer proxy).
|
||||
- New load balancing algorithm to support Multi-User chat and gateways
|
||||
clustering. More generally it supports any external component load
|
||||
balancing.
|
||||
- ejabberd watchdog to receive warning on suspicious resources consumption.
|
||||
- Traffic shapers are now supported on components. This protect
|
||||
ejabberd from components and gateways abuses.
|
||||
|
||||
|
||||
* Publish and Subscribe
|
||||
|
||||
- Complete rewrite of the PubSub module. The new PubSub module is
|
||||
plugin-based, allowing developers to create new nodes type. Any
|
||||
application can be plugged to ejabberd and can provide rich presence
|
||||
as a pubsub plugin.
|
||||
- Personal Eventing via Pubsub support (XEP-0163). This module is
|
||||
implemented as a PubSub service. It supports user mood (XEP-107),
|
||||
User Tune (XEP-118), user location (XEP-0080) or user avatar
|
||||
(XEP-0084) for example.
|
||||
|
||||
|
||||
* Server to Server (s2s)
|
||||
|
||||
- More robust code with connection timeout implementation.
|
||||
- Support for multiple s2s connections per domain.
|
||||
- s2s whitelist and blacklist support.
|
||||
- s2s retrial interval.
|
||||
|
||||
|
||||
* LDAP
|
||||
|
||||
- Many enterprise-class enhancements such as better behaviour under
|
||||
heavy load.
|
||||
- Support for LDAP servers pool.
|
||||
- Simplified use of virtual hosting with LDAP with domain substitution
|
||||
in config.
|
||||
- Ability to match on several userid attributes.
|
||||
|
||||
|
||||
* Multi-User Chat
|
||||
|
||||
- Clustering and load balancing support.
|
||||
- Ability to define default room configuration in ejabberd config file.
|
||||
- Many anti abuse features have been added:
|
||||
. New ACL to limit the creation of persistent room to authorized users.
|
||||
. Ability to define the maximum number of users per room.
|
||||
. Limitation of the rate of message and presence packets.
|
||||
. Limitation of the maximum number of room a user can join at the same time.
|
||||
|
||||
|
||||
* File Transfer
|
||||
|
||||
- XEP-0065 - Proxy65 file transfer proxy. The proxy can run in
|
||||
cluster mode.
|
||||
|
||||
|
||||
* Authentication
|
||||
|
||||
- PAM (Pluggable Authentication Modules) support on *nix systems.
|
||||
- External Authentication protocol is now fully documented.
|
||||
|
||||
|
||||
* Web Client Support
|
||||
|
||||
- XEP-0124 - BOSH support: BOSH (Bidirectional-streams Over
|
||||
Synchronous HTTP) was formerly known as "HTTP binding". It provides
|
||||
an efficient alternative to HTTP polling for scalable Web based chat
|
||||
solutions.
|
||||
- HTTP module can now serve static documents (with
|
||||
mod_http_fileserver). It is needed for high-performance Web 2.0 chat
|
||||
/ IM application. System administrators can now avoid using a proxy
|
||||
(like Apache) that handles much less simultaneous than ejabberd HTTP
|
||||
module.
|
||||
- Added limitations enforcement on HTTP poll and HTTP bind modules
|
||||
(bandwidth, packet size).
|
||||
|
||||
|
||||
* System Administration
|
||||
|
||||
- XEP-0133 - Service administration support. System administrators can
|
||||
now perform lot of ejabberd related admin tasks from their XMPP
|
||||
client, through adhoc commands.
|
||||
- Dynamic log levels: Improved logging with more log levels. You can
|
||||
now change the loglevel at run time. No performance penalty is
|
||||
involved when less verbose levels are used.
|
||||
- The ejabberdctl command-line administration script now can start
|
||||
and stop ejabberd. It also includes other useful options.
|
||||
|
||||
|
||||
* Localization
|
||||
|
||||
- ejabberd is now translated to 24 languages: Catalan, Chinese, Czech,
|
||||
Dutch, English, Esperanto, French, Galician, German, Italian, Japanese,
|
||||
Norwegian, Polish, Portuguese, Portuguese (Brazil), Russian, Slovak,
|
||||
Spanish, Swedish, Thai, Turkish, Ukrainian, Vietnamese, Walloon.
|
||||
|
||||
|
||||
* Build and Installer
|
||||
|
||||
- Many launch script improvements.
|
||||
- New translations. The binary installer is now available in Chinese,
|
||||
Dutch, English, French, German, Spanish, Russian.
|
||||
- Makefile now implements uninstall command.
|
||||
- Full MacOSX compliance in Makefile.
|
||||
- Configure script is clever at finding libraries in unusual places.
|
||||
|
||||
|
||||
* Development API
|
||||
|
||||
- Several hooks have been added for module developers (most notably
|
||||
presence related hooks).
|
||||
- HTTP request handler to write HTTP based plugins.
|
||||
- Manage connections IP address.
|
||||
|
||||
|
||||
* Bugfixes
|
||||
|
||||
- ejabberd 2.0.0 also fixes numerous small bugs :) Read the full
|
||||
changelog for details.
|
||||
|
||||
|
||||
|
||||
Important Note:
|
||||
|
||||
- Since this release, ejabberd requires Erlang R10B-5 or higher.
|
||||
R11B-5 is the recommended version. R12 is not yet officially
|
||||
supported, and is not recommended for production servers.
|
||||
|
||||
|
||||
|
||||
Upgrading From ejabberd 1.x:
|
||||
|
||||
- If you upgrade from a version older than 1.1.4, please check the
|
||||
Release Notes of the intermediate versions for additional
|
||||
information about database or configuration changes.
|
||||
|
||||
- The database schemas didn't change since ejabberd 1.1.4. Of course,
|
||||
you are encouraged to make a database backup of your SQL database,
|
||||
or your Mnesia spool directory before upgrading ejabberd.
|
||||
|
||||
- The ejabberdctl command line administration script is improved in
|
||||
ejabberd 2.0.0, and now it can start and stop ejabberd. If you
|
||||
already wrote your own start script for ejabberd 1.x, you can
|
||||
continue using it, or try ejabberdctl. For your convenience, the
|
||||
ejabberd Guide describes all the ejabberd and Erlang options used by
|
||||
ejabberdctl.
|
||||
|
||||
- The example ejabberd.cfg file has been reorganized, but its format
|
||||
and syntax rules are the same. So, you can continue using your
|
||||
ejabberd.cfg file from 1.x if you want. The most important changes
|
||||
are described now.
|
||||
|
||||
- The 'ssl' option is no longer available in the listening ports. For
|
||||
legacy SSL encryption use the option 'tls'. For STARTTLS encryption
|
||||
as defined in RFC 3920 XMPP-CORE use the option 'starttls'. Check
|
||||
the ejabberd Guide for more information about configuring listening
|
||||
ports.
|
||||
|
||||
- The options 'welcome_message' and 'registration_watchers' are now
|
||||
options of the module mod_register. Check in the ejabberd Guide how
|
||||
to configure that module.
|
||||
|
||||
- To enable PEP support in mod_pubsub, you need to enable it in the
|
||||
mod_pubsub configuration, and also enable the new module
|
||||
mod_caps. Check the section about mod_pubsub in the ejabberd Guide.
|
||||
|
||||
- Other new features and improvements also require changes in the
|
||||
ejabberd.cfg, like mod_http_bind, mod_http_fileserver, mod_proxy65,
|
||||
loglevel, pam_service, and watchdog_admins. Search for those words
|
||||
in the ejabberd Guide and the example ejabberd.cfg.
|
||||
|
||||
|
||||
|
||||
Bug Reports
|
||||
|
||||
You can officially report bugs on Process-one support site:
|
||||
https://support.process-one.net/
|
||||
|
||||
|
||||
END
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.1
|
||||
20 May 2008
|
||||
|
||||
ejabberd 2.0.1 is a bugfix release for ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.1 includes 10 improvements and 32 bugfixes.
|
||||
A complete list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.1
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
- Erlang R12 support.
|
||||
- Better LDAP handling.
|
||||
- PubSub bugfixes.
|
||||
- Documentation improvements.
|
||||
- inband registration limitation per IP.
|
||||
- s2s improvements.
|
||||
|
||||
Bugs report
|
||||
|
||||
You can officially report bugs on Process-one support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.2
|
||||
28 August 2008
|
||||
|
||||
ejabberd 2.0.2 is the second bug fix release for ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.2 includes many bugfixes and a few improvements.
|
||||
A complete list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.2
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
- Anti-abuse feature: client blacklist support by IP.
|
||||
- Guide: new section Securing ejabberd; improved usability.
|
||||
- LDAP filter optimisation: ability to filter user in ejabberd and not LDAP.
|
||||
- MUC improvements: room options to restrict visitors; broadcast reasons.
|
||||
- Privacy rules: fix MySQL storage.
|
||||
- Pub/Sub and PEP: many improvements in implementation and protocol compliance.
|
||||
- Proxy65: send valid SOCKS5 reply (removed support for Psi < 0.10).
|
||||
- Web server embedded: better support for HTTPS.
|
||||
- Binary installers: SMP on Windows; don't remove config when uninstalling.
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.3
|
||||
14 January 2009
|
||||
|
||||
ejabberd 2.0.3 is the third bugfix release for ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.3 includes several bugfixes and a few improvements.
|
||||
A complete list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.3
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
- Do not ask certificate for client (c2s)
|
||||
- Check digest-uri in SASL digest authentication
|
||||
- Use send timeout to avoid locking on gen_tcp:send
|
||||
- Fix ejabberd reconnection to database
|
||||
- HTTP-Bind: handle wrong order of packets
|
||||
- MUC: Improve traffic regulation management
|
||||
- PubSub: Several bugfixes and improvements for best coverage of XEP-0060 v1.12
|
||||
- Shared Roster Groups: push immediately membership changes
|
||||
- Rotate also sasl.log on "reopen-log" command
|
||||
- Binary Windows installer: better detect "Error running Post Install Script"
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.4
|
||||
|
||||
ejabberd 2.0.4 is the fourth bugfix release for ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.4 includes several bugfixes.
|
||||
A detailed list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.4
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
- Ensure ID attribute in roster push is unique
|
||||
- Authentication: Fix Anonymous auth when enabled with broken ODBC
|
||||
- Authentication: Unquote correctly backslash in DIGEST-MD5 SASL responses
|
||||
- Authentication: Cancel presence subscriptions on account deletion
|
||||
- LDAP: Close a connection on tcp_error
|
||||
- LDAP: Implemented queue for pending queries
|
||||
- LDAP: On failure of LDAP connection, waiting is done on pending queue
|
||||
- MUC: Owner of a password protected room must also provide the password
|
||||
- MUC: Prevent XSS in MUC logs by linkifying only a few known protocols
|
||||
- Privacy rules: Items are now processed in the specified order
|
||||
- Privacy rules: Fix to correctly block subscription requests
|
||||
- Proxy65: If ip option is not defined, take an IP address of a local hostname
|
||||
- PubSub: Add roster subscription handling; send PEP events to all resources
|
||||
- PubSub: Allow node creation without configure item
|
||||
- PubSub: Requesting items on a node which exists, but empty returns an error
|
||||
- PEP: Fix sending notifications to other domains and s2s
|
||||
- S2S: Fix problem with encrypted connection to Gtalk and recent Openfire
|
||||
- S2S: Workaround to get DNS SRV lookup to work on Windows machine
|
||||
- Shared Roster Groups: Fix to not resend authorization request
|
||||
- WebAdmin: Fix encryption problem for ejabberd_http after timeout
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.5
|
||||
|
||||
ejabberd 2.0.5 is the fifth bugfix release in ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.5 includes three bugfixes.
|
||||
More details of those fixes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.5
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
- Fix two problems introduced in ejabberd 2.0.4: subscription request
|
||||
produced many authorization requests with some clients and
|
||||
transports; and subscription requests were not stored for later
|
||||
delivery when receiver was offline.
|
||||
|
||||
- Fix warning in expat_erl.c about implicit declaration of x_fix_buff
|
||||
|
||||
- HTTP-Bind (BOSH): Fix a missing stream:error in the returned
|
||||
remote-stream-error stanza
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -1,281 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.0
|
||||
|
||||
ejabberd 2.1.0 is a major new version for ejabberd adding many
|
||||
new features, performance and scalability improvements.
|
||||
|
||||
ejabberd 2.1.0 includes many new features, improvements and bug fixes.
|
||||
A complete list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.1.0
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
New features and improvements:
|
||||
|
||||
* Anti-abuse
|
||||
- Captcha support (XEP-0158). The example script uses ImageMagick.
|
||||
- New option: registration_timeout to limit registrations by time
|
||||
- Use send timeout to avoid locking on gen_tcp:send
|
||||
- mod_ip_blacklist: client blacklist support by IP
|
||||
|
||||
* API
|
||||
- ejabberd_http provides Host, Port, Headers and Protocol in HTTP requests
|
||||
- Export function to create MUC room
|
||||
- New events: s2s_send_packet and s2s_receive_packet
|
||||
- New event: webadmin_user_parse_query when POST in web admin user page
|
||||
- Support distributed hooks over the cluster
|
||||
|
||||
* Authentification
|
||||
- Extauth responses: log strange responses and add timeout
|
||||
|
||||
* Binary Installer
|
||||
- Includes exmpp library to support import/export XML files
|
||||
|
||||
* Caps
|
||||
- Remove useless caps tables entries
|
||||
- mod_caps must handle correctly external contacts with several resources
|
||||
- Complain if mod_caps disabled and mod_pubsub has PEP plugin enabled
|
||||
|
||||
* Clustering and Architecture
|
||||
|
||||
* Configuration
|
||||
- Added option access_max_user_messages for mod_offline
|
||||
- Added option backlog for ejabberd_listener to increase TCP backlog
|
||||
- Added option define_macro and use_macro
|
||||
- Added option include_config_file to include additional configuration files
|
||||
- Added option max_fsm_queue
|
||||
- Added option outgoing_s2s_options to define IP address families and timeout
|
||||
- Added option registration_timeout to ejabberd.cfg.example
|
||||
- Added option s2s_dns_options to define DNS timeout and retries
|
||||
- Added option ERL_OPTIONS to ejabberdctl.cfg
|
||||
- Added option FIREWALL_WINDOW to ejabberdctl.cfg
|
||||
- Added option EJABBERD_PID_PATH to ejabberdctl.cfg
|
||||
- Deleted option user_max_messages of mod_offline
|
||||
- Check certfiles are readable on server start and listener start
|
||||
- Config file management mix file reading and sanity check
|
||||
- Include example PAM configuration file: ejabberd.pam
|
||||
- New ejabberd listener: ejabberd_stun
|
||||
- Support to bind the same port to multiple interfaces
|
||||
- New syntax to specify the IP address and IPv6 in listeners
|
||||
configuration. The old options {ip,{1,2,3,4}} and inet6 are
|
||||
supported even if they aren't documented.
|
||||
- New syntax to specify the network protocol: tcp or udp
|
||||
- Report error at startup if a listener module isn't available
|
||||
- Only listen in a port when actually ready to serve requests
|
||||
- In default config, only local accounts can create rooms and PubSub nodes
|
||||
|
||||
* Core architecture
|
||||
- More verbose error reporting for xml:element_to_string
|
||||
- Deliver messages when first presence is Invisible
|
||||
- Better log message when config file is not found
|
||||
- Include original timestamp on delayed presences
|
||||
|
||||
* Crypto
|
||||
- Do not ask certificate for client (c2s)
|
||||
- SSL code remove from ejabberd in favor of TLS
|
||||
- Support Zlib compression after STARTTLS encryption
|
||||
- tls v1 client hello
|
||||
|
||||
* Documentation
|
||||
- Document possible default MUC room options
|
||||
- Document service_check_from in the Guide
|
||||
- Document s2s_default_policy and s2s_host in the Guide
|
||||
- new command and guide instructions to change node name in a Mnesia database
|
||||
|
||||
* ejabberd commands
|
||||
- ejabberd commands: separate command definition and calling interface
|
||||
- access_commands restricts who can execute what commands and arguments
|
||||
- ejabberdctl script now displays help and categorization of commands
|
||||
|
||||
* HTTP Binding and HTTP Polling
|
||||
- HTTP-Bind: module optimization and clean-up
|
||||
- HTTP-Bind: allow configuration of max_inactivity timeout
|
||||
- HTTP-Poll: turn session timeout into a config file parameter
|
||||
|
||||
* Jingle
|
||||
- STUN server that facilitates the client-to-client negotiation process
|
||||
|
||||
* LDAP
|
||||
- Faster reconnection to LDAP servers
|
||||
- LDAP filter optimisation: Add ability to filter user in ejabberd and not LDAP
|
||||
- LDAP differentiates failed auth and unavailable auth service
|
||||
- Improve LDAP logging
|
||||
- LDAPS support using TLS.
|
||||
|
||||
* Localization
|
||||
- Use Gettext PO for translators, export to ejabberd MSG
|
||||
- Support translation files for additional projects
|
||||
- Most translations are updated to latest code
|
||||
- New translation to Greek language
|
||||
|
||||
* Multi-User Chat (MUC)
|
||||
- Allow admins to send messages to rooms
|
||||
- Allow to store room description
|
||||
- Captcha support in MUC: the admin of a room can configure it to
|
||||
require participants to fill a captcha to join the room.
|
||||
- Limit number of characters in Room ID, Name and Description
|
||||
- Prevent unvoiced occupants from changing nick
|
||||
- Support Result Set Management (XEP-0059) for listing rooms
|
||||
- Support for decline of invitation to MUC room
|
||||
- mod_muc_log options: plaintext format; filename with only room name
|
||||
|
||||
* Performance
|
||||
- Run roster_get_jid_info only if privacy list has subscription or group item
|
||||
- Significant PubSub performance improvements
|
||||
|
||||
* Publish-Subscribe
|
||||
- Add nodetree filtering/authorization
|
||||
- Add subscription option support for collection nodes
|
||||
- Allow Multiple Subscriptions
|
||||
- Check option of the nodetree instead of checking configuration
|
||||
- Implement whitelist authorize and roster access model
|
||||
- Implicit item deletion is not notified when deleting node
|
||||
- Make PubSub x-data configuration form handles list value
|
||||
- Make default node name convention XEP-compatible, document usage of hierarchy
|
||||
- Node names are used verbatim, without separating by slash, unless a
|
||||
node plugin uses its own separator
|
||||
- Send authorization update event (XEP-0060, 8.6)
|
||||
- Support of collection node subscription options
|
||||
- Support ODBC storage. Experimental, needs more testing.
|
||||
|
||||
* Relational databases:
|
||||
- Added MSSQL 2000 and 2005
|
||||
- Privacy rules storage in MySQL
|
||||
- Implement reliable ODBC transaction nesting
|
||||
|
||||
* Source Package
|
||||
- Default installation directories changed. Please see the upgrade notes below.
|
||||
- Allow more environment variable overrides in ejabberdctl
|
||||
- ChangeLog is not edited manually anymore; it's generated automatically.
|
||||
- Install the ejabberd Guide
|
||||
- Install the ejabberd include files
|
||||
- New option for the 'configure' script: --enable-user which installs
|
||||
ejabberd granting permission to manage it to a regular system user;
|
||||
no need to use root account to.
|
||||
- Only try to install epam if pam was enabled in configure script
|
||||
- Spool, config and log dirs: owner writes, group reads, others do nothing.
|
||||
- Provides an example ejabberd.init file
|
||||
|
||||
* S2S
|
||||
- Option to define s2s outgoing behaviour: IPv4, IPv6 and timeout
|
||||
- DNS timeout and retries, configurable with s2s_dns_options.
|
||||
|
||||
* Shared rosters
|
||||
- When a member is added/removed to group, send roster upgrade to group members
|
||||
|
||||
* Users management
|
||||
- When account is deleted, cancel presence subscription for all roster items
|
||||
|
||||
* XEP Support
|
||||
- Added XEP-0059 Result Set Management (for listing rooms)
|
||||
- Added XEP-0082 Date Time
|
||||
- Added XEP-0085 Chat State Notifications
|
||||
- Added XEP-0157 Contact Addresses for XMPP Services
|
||||
- Added XEP-0158 CAPTCHA Forms (in MUC rooms)
|
||||
- Added STUN server, for XEP-0176: Jingle ICE-UDP Transport Method
|
||||
- Added XEP-0199 XMPP Ping
|
||||
- Added XEP-0202 Entity Time
|
||||
- Added XEP-0203 Delayed Delivery
|
||||
- Added XEP-0227 Portable Import/Export Format for XMPP-IM Servers
|
||||
- Added XEP-0237 Roster Versioning
|
||||
|
||||
* Web Admin
|
||||
- Display the connection method of user sessions
|
||||
- Cross link of ejabberd users in the list of users and rosters
|
||||
- Improved the browsing menu: don't disappear when browsing a host or node
|
||||
- Include Last-Modified HTTP header in responses to allow caching
|
||||
- Make some Input areas multiline: options of listening ports and modules
|
||||
- Support PUT and DELETE methods in ejabberd_http
|
||||
- WebAdmin serves Guide and links to related sections
|
||||
|
||||
* Web plugins
|
||||
- mod_http_fileserver: new option directory_indices, and improve logging
|
||||
|
||||
|
||||
Important Notes:
|
||||
|
||||
- ejabberd 2.1.0 requires Erlang R10B-9 or higher.
|
||||
R12B-5 is the recommended version. Support for R13B is experimental.
|
||||
|
||||
|
||||
Upgrading From ejabberd 1.x.x:
|
||||
|
||||
- Check the Release Notes of the intermediate versions for additional
|
||||
information about database or configuration changes.
|
||||
|
||||
|
||||
Upgrading From ejabberd 2.0.x:
|
||||
|
||||
- The database schemas have three changes since ejabberd 2.0.x.
|
||||
Check the database creation SQL files and update your database.
|
||||
1) New table roster_version to support roster versioning.
|
||||
2) Six new tables for optional pubsub ODBC storage.
|
||||
3) Some tables in the MySQL database have a new created_at column.
|
||||
|
||||
- As usual, it is recommended to backup the Mnesia spool directory and
|
||||
your SQL database (if used) before upgrading ejabberd.
|
||||
|
||||
- Between ejabberd 2.0.0 and 2.0.5, mod_pubsub used "default" as the
|
||||
default node plugin. But in 2.1.0 this is renamed to "hometree".
|
||||
You have to edit your ejabberd config file and replace those names.
|
||||
If you used ejabberd 2.0.5 or older, the database will be updated
|
||||
automatically. But if you were using ejabberd from SVN, you must
|
||||
manually run ejabberdctl with the command: rename_default_nodeplugin.
|
||||
Running this command on already updated database does nothing.
|
||||
|
||||
- The listener options 'ip' and 'inet6' are not documented anymore
|
||||
but they are supported and you can continue using them.
|
||||
There is a new syntax to define IP address and IP version.
|
||||
As usual, check the ejabberd Guide for more information.
|
||||
|
||||
- The log file sasl.log is now called erlang.log
|
||||
|
||||
- ejabberdctl commands now have _ characters instead of -.
|
||||
For backwards compatibility, it is still supported -.
|
||||
|
||||
- mod_offline has a new option: access_max_user_messages.
|
||||
The old option user_max_messages is no longer supported.
|
||||
|
||||
- If you upgrade from ejabberd trunk SVN, you must execute this:
|
||||
$ ejabberdctl rename_default_nodeplugin
|
||||
|
||||
- Default installation directories changed a bit:
|
||||
* The Mnesia spool files that were previously stored in
|
||||
/var/lib/ejabberd/db/NODENAME/*
|
||||
are now stored in
|
||||
/var/lib/ejabberd/*
|
||||
* The directories
|
||||
/var/lib/ejabberd/ebin
|
||||
/var/lib/ejabberd/priv
|
||||
and their content is now installed as
|
||||
/lib/ejabberd/ebin
|
||||
/lib/ejabberd/priv
|
||||
* There is a new directory with Erlang header files:
|
||||
/lib/ejabberd/include
|
||||
* There is a new directory for ejabberd documentation,
|
||||
which includes the Admin Guide and the release notes::
|
||||
/share/doc/ejabberd
|
||||
|
||||
- How to upgrade from previous version to ejabberd 2.1.0:
|
||||
1. Stop the old instance of ejabberd.
|
||||
2. Run 'make install' of new ejabberd 2.1.0 to create the new directories.
|
||||
3. Copy the content of your old directory:
|
||||
/var/lib/ejabberd/db/NODENAME/
|
||||
to the new location:
|
||||
/var/lib/ejabberd/
|
||||
so you will have the files like this:
|
||||
/var/lib/ejabberd/acl.DCD ...
|
||||
4. You can backup the content of those directories and delete them:
|
||||
/var/lib/ejabberd/ebin
|
||||
/var/lib/ejabberd/priv
|
||||
/var/lib/ejabberd/db
|
||||
5. Now try to start your new ejabberd 2.1.0.
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.1
|
||||
|
||||
ejabberd 2.1.1 is the first bugfix release in ejabberd 2.1.x branch.
|
||||
|
||||
ejabberd 2.1.1 includes several important bugfixes.
|
||||
More details of those fixes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.1.1
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
* Core
|
||||
- Call ejabberd_router:route/3 instead of sending a message
|
||||
- Can't connect if starttls_required and zlib are set
|
||||
- Routes vCard request to the occupant full JID, but should to bare JID
|
||||
- S2S: fix allow_host/2 on subdomains. added hook s2s_allow_host
|
||||
|
||||
* MUC
|
||||
- Support converting one-to-one chat to MUC
|
||||
- Add support for serving a Unique Room Name
|
||||
|
||||
* Publish Subscribe
|
||||
- Receive same last published PEP items at reconnect if several resources online
|
||||
- Typo in mod_pubsub_odbc breaks Service Discovery and more
|
||||
|
||||
* Web
|
||||
- Fix memory and port leak when TLS is enabled in HTTP
|
||||
- WebAdmin doesn't report correct last activity with postgresql backend
|
||||
- Option to define custom HTTP headers in mod_http_fileserver
|
||||
- Show informative webpage when browsing the HTTP-Poll page
|
||||
|
||||
* Other
|
||||
- Change captcha.sh to not depend on bash
|
||||
- Generate main XML file also when exporting only a vhost
|
||||
- Fix last newline in ejabberdctl result
|
||||
- Guide: fix -setcookie, mod_pubsub_odbc host, content_types
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
@@ -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/
|
||||
@@ -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/
|
||||
@@ -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/
|
||||
|
||||
@@ -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/
|
||||
@@ -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/
|
||||
@@ -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/
|
||||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 157 KiB |
@@ -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
|
||||
@@ -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'
|
||||
@@ -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 @@
|
||||
*.beam
|
||||
@@ -1,155 +1,39 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
ERLANG_CFLAGS= @ERLANG_CFLAGS@
|
||||
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
|
||||
|
||||
INSTALLUSER=@INSTALLUSER@
|
||||
# if no user was enabled, don't set privileges or ownership
|
||||
ifeq ($(INSTALLUSER),)
|
||||
O_USER=
|
||||
G_USER=
|
||||
CHOWN_COMMAND=echo
|
||||
CHOWN_OUTPUT=/dev/null
|
||||
INIT_USER=root
|
||||
else
|
||||
O_USER=-o $(INSTALLUSER)
|
||||
G_USER=-g $(INSTALLUSER)
|
||||
CHOWN_COMMAND=chown
|
||||
CHOWN_OUTPUT=&1
|
||||
INIT_USER=$(INSTALLUSER)
|
||||
endif
|
||||
|
||||
EFLAGS += @ERLANG_SSLVER@ -pa .
|
||||
|
||||
# make debug=true to compile Erlang module with debug informations.
|
||||
ifdef debug
|
||||
EFLAGS+=+debug_info +export_all
|
||||
endif
|
||||
|
||||
DEBUGTOOLS = p1_prof.erl etop_tr.erl
|
||||
ifdef debugtools
|
||||
SOURCES+=$(DEBUGTOOLS)
|
||||
endif
|
||||
|
||||
ifeq (@hipe@, true)
|
||||
EFLAGS+=+native
|
||||
endif
|
||||
|
||||
ifeq (@roster_gateway_workaround@, true)
|
||||
EFLAGS+=-DROSTER_GATEWAY_WORKAROUND
|
||||
endif
|
||||
|
||||
ifeq (@full_xml@, true)
|
||||
EFLAGS+=-DFULL_XML_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq (@transient_supervisors@, false)
|
||||
EFLAGS+=-DNO_TRANSIENT_SUPERVISORS
|
||||
endif
|
||||
|
||||
ifeq (@md2@, true)
|
||||
EFLAGS+=-DHAVE_MD2
|
||||
ERLANG_CFLAGS += -DHAVE_MD2
|
||||
endif
|
||||
|
||||
INSTALL_EPAM=
|
||||
ifeq (@pam@, pam)
|
||||
INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR)
|
||||
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 =
|
||||
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
|
||||
SOURCES_ALL = $(wildcard *.erl)
|
||||
SOURCES_MISC = $(ERLBEHAVS) $(DEBUGTOOLS)
|
||||
SOURCES += $(filter-out $(SOURCES_MISC),$(SOURCES_ALL))
|
||||
ERLBEHAVBEAMS = $(ERLBEHAVS:.erl=.beam)
|
||||
BEAMS = $(SOURCES:.erl=.beam)
|
||||
INCLUDES = @ERLANG_CFLAGS@ @EXPAT_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@ @EXPAT_LIBS@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep
|
||||
|
||||
ERLSHLIBS = expat_erl.so
|
||||
|
||||
DESTDIR =
|
||||
|
||||
# /etc/ejabberd/
|
||||
ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd
|
||||
|
||||
# /sbin/
|
||||
SBINDIR = $(DESTDIR)@sbindir@
|
||||
|
||||
# /lib/ejabberd/
|
||||
EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
|
||||
|
||||
# /share/doc/ejabberd
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
datarootdir = @datarootdir@
|
||||
DOCDIR = $(DESTDIR)@docdir@
|
||||
|
||||
# /usr/lib/ejabberd/ebin/
|
||||
EJABBERDDIR = $(DESTDIR)/var/lib/ejabberd
|
||||
BEAMDIR = $(EJABBERDDIR)/ebin
|
||||
|
||||
# /usr/lib/ejabberd/include/
|
||||
INCLUDEDIR = $(EJABBERDDIR)/include
|
||||
|
||||
# /usr/lib/ejabberd/priv/
|
||||
PRIVDIR = $(EJABBERDDIR)/priv
|
||||
|
||||
# /usr/lib/ejabberd/priv/bin
|
||||
PBINDIR = $(PRIVDIR)/bin
|
||||
|
||||
# /usr/lib/ejabberd/priv/lib
|
||||
SODIR = $(PRIVDIR)/lib
|
||||
|
||||
# /usr/lib/ejabberd/priv/msgs
|
||||
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
|
||||
|
||||
# /var/log/ejabberd/
|
||||
LOGDIR = $(DESTDIR)@localstatedir@/log/ejabberd
|
||||
|
||||
# Assume Linux-style dynamic library flags
|
||||
DYNAMIC_LIB_CFLAGS = -fpic -shared
|
||||
ifeq ($(shell uname),Darwin)
|
||||
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
|
||||
endif
|
||||
ifeq ($(shell uname),SunOs)
|
||||
DYNAMIC_LIB_CFLAGS = -KPIC -G -z text
|
||||
endif
|
||||
LOGDIR = $(DESTDIR)/var/log/ejabberd
|
||||
ETCDIR = $(DESTDIR)/etc/ejabberd
|
||||
|
||||
all: $(ERLSHLIBS) compile-beam all-recursive
|
||||
|
||||
compile-beam: XmppAddr.hrl $(ERLBEHAVBEAMS) $(BEAMS)
|
||||
|
||||
$(BEAMS): $(ERLBEHAVBEAMS)
|
||||
|
||||
all-recursive: $(ERLBEHAVBEAMS)
|
||||
|
||||
%.beam: %.erl
|
||||
@ERLC@ -W $(EFLAGS) $<
|
||||
compile-beam:
|
||||
@erl -s make all report -noinput -s erlang halt
|
||||
|
||||
|
||||
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,,'`; \
|
||||
@@ -158,143 +42,31 @@ mostlyclean-recursive maintainer-clean-recursive:
|
||||
done
|
||||
|
||||
|
||||
%.hrl: %.asn1
|
||||
@ERLC@ $(ASN_FLAGS) $<
|
||||
@ERLC@ -W $(EFLAGS) $*.erl
|
||||
|
||||
$(ERLSHLIBS): %.so: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LIBS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
$(ERLANG_LIBS) \
|
||||
$(ERLANG_CFLAGS) \
|
||||
-o $@ \
|
||||
$(DYNAMIC_LIB_CFLAGS)
|
||||
|
||||
translations:
|
||||
../tools/extract_translations/prepare-translation.sh -updateall
|
||||
gcc -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
-lexpat \
|
||||
-lerl_interface \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
|
||||
install: all
|
||||
#
|
||||
# Configuration files
|
||||
install -d -m 750 $(G_USER) $(ETCDIR)
|
||||
[ -f $(ETCDIR)/ejabberd.cfg ] \
|
||||
&& install -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg-new \
|
||||
|| install -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
||||
sed -e "s*@rootdir@*@prefix@*" \
|
||||
-e "s*@installuser@*@INSTALLUSER@*" \
|
||||
-e "s*@LIBDIR@*@libdir@*" \
|
||||
-e "s*@SYSCONFDIR@*@sysconfdir@*" \
|
||||
-e "s*@LOCALSTATEDIR@*@localstatedir@*" \
|
||||
-e "s*@DOCDIR@*@docdir@*" \
|
||||
-e "s*@erl@*@ERL@*" ejabberdctl.template \
|
||||
> ejabberdctl.example
|
||||
[ -f $(ETCDIR)/ejabberdctl.cfg ] \
|
||||
&& install -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg-new \
|
||||
|| install -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg
|
||||
install -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc
|
||||
#
|
||||
# Administration script
|
||||
[ -d $(SBINDIR) ] || install -d -m 755 $(SBINDIR)
|
||||
install -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl
|
||||
#
|
||||
# Init script
|
||||
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)
|
||||
install -m 644 *.app $(BEAMDIR)
|
||||
install -m 644 *.beam $(BEAMDIR)
|
||||
rm -f $(BEAMDIR)/configure.beam
|
||||
#
|
||||
# ejabberd header files
|
||||
install -d $(INCLUDEDIR)
|
||||
install -m 644 *.hrl $(INCLUDEDIR)
|
||||
install -d $(INCLUDEDIR)/eldap/
|
||||
install -m 644 eldap/*.hrl $(INCLUDEDIR)/eldap/
|
||||
install -d $(INCLUDEDIR)/mod_muc/
|
||||
install -m 644 mod_muc/*.hrl $(INCLUDEDIR)/mod_muc/
|
||||
install -d $(INCLUDEDIR)/mod_proxy65/
|
||||
install -m 644 mod_proxy65/*.hrl $(INCLUDEDIR)/mod_proxy65/
|
||||
install -d $(INCLUDEDIR)/mod_pubsub/
|
||||
install -m 644 mod_pubsub/*.hrl $(INCLUDEDIR)/mod_pubsub/
|
||||
install -d $(INCLUDEDIR)/web/
|
||||
install -m 644 web/*.hrl $(INCLUDEDIR)/web/
|
||||
#
|
||||
# Binary C programs
|
||||
install -d $(PBINDIR)
|
||||
install -m 750 $(O_USER) ../tools/captcha.sh $(PBINDIR)
|
||||
$(INSTALL_EPAM)
|
||||
#
|
||||
# Binary system libraries
|
||||
install -m 644 *.app $(BEAMDIR)
|
||||
install -d $(SODIR)
|
||||
install -m 644 *.so $(SODIR)
|
||||
#
|
||||
# Translated strings
|
||||
install -d $(MSGSDIR)
|
||||
install -m 644 msgs/*.msg $(MSGSDIR)
|
||||
#
|
||||
# Spool directory
|
||||
install -d -m 750 $(O_USER) $(SPOOLDIR)
|
||||
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
|
||||
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)
|
||||
chmod -R 750 $(LOGDIR)
|
||||
#
|
||||
# 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 -d $(ETCDIR)
|
||||
install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
||||
install -d $(LOGDIR)
|
||||
|
||||
uninstall: uninstall-binary
|
||||
|
||||
uninstall-binary:
|
||||
rm -f $(SBINDIR)/ejabberdctl
|
||||
rm -fr $(DOCDIR)
|
||||
rm -f $(BEAMDIR)/*.beam
|
||||
rm -f $(BEAMDIR)/*.app
|
||||
rm -fr $(BEAMDIR)
|
||||
rm -f $(INCLUDEDIR)/*.hrl
|
||||
rm -fr $(INCLUDEDIR)
|
||||
rm -fr $(PBINDIR)
|
||||
rm -f $(SODIR)/*.so
|
||||
rm -fr $(SODIR)
|
||||
rm -f $(MSGSDIR)/*.msgs
|
||||
rm -fr $(MSGSDIR)
|
||||
rm -fr $(PRIVDIR)
|
||||
rm -fr $(EJABBERDDIR)
|
||||
|
||||
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
|
||||
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
|
||||
rm -f *.beam $(ERLSHLIBS)
|
||||
|
||||
distclean: distclean-recursive clean-local
|
||||
rm -f config.status
|
||||
@@ -305,93 +77,3 @@ TAGS:
|
||||
etags *.erl
|
||||
|
||||
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
|
||||
|
||||
@@ -8,8 +8,8 @@ EREL=$(REL)\ejabberd-$(EJABBERD_VERSION)
|
||||
EBIN_DIR=$(EREL)\ebin
|
||||
SRC_DIR=$(EREL)\src
|
||||
PRIV_DIR=$(EREL)\priv
|
||||
SO_DIR=$(EREL)
|
||||
MSGS_DIR=$(EREL)\msgs
|
||||
SO_DIR=$(PRIV_DIR)\lib
|
||||
MSGS_DIR=$(PRIV_DIR)\msgs
|
||||
WIN32_DIR=$(EREL)\win32
|
||||
DOC_DIR=$(EREL)\doc
|
||||
|
||||
@@ -35,15 +35,18 @@ release : build release_clean
|
||||
copy *.beam $(EBIN_DIR)
|
||||
@erase $(EBIN_DIR)\configure.beam
|
||||
copy *.app $(EBIN_DIR)
|
||||
mkdir $(PRIV_DIR)
|
||||
mkdir $(SO_DIR)
|
||||
copy *.dll $(SO_DIR)
|
||||
mkdir $(MSGS_DIR)
|
||||
copy msgs\*.msg $(MSGS_DIR)
|
||||
mkdir $(WIN32_DIR)
|
||||
copy win32\ejabberd.cfg $(EREL)
|
||||
copy win32\inetrc $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\libeay32.dll $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\ssleay32.dll $(EREL)
|
||||
copy win32\ejabberd.ico $(WIN32_DIR)
|
||||
mkdir $(WIN32_DIR)\5.3
|
||||
copy win32\5.3\*.beam $(WIN32_DIR)\5.3
|
||||
mkdir $(SRC_DIR)
|
||||
copy *.app $(SRC_DIR)
|
||||
copy *.erl $(SRC_DIR)
|
||||
@@ -52,109 +55,91 @@ 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
|
||||
copy mod_pubsub\*.erl $(SRC_DIR)\mod_pubsub
|
||||
mkdir $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.erl $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.hrl $(SRC_DIR)\mod_proxy65
|
||||
mkdir $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.erl $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.c $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.tcl $(SRC_DIR)\stringprep
|
||||
mkdir $(SRC_DIR)\stun
|
||||
copy stun\*.erl $(SRC_DIR)\stun
|
||||
copy stun\*.hrl $(SRC_DIR)\stun
|
||||
mkdir $(SRC_DIR)\tls
|
||||
copy tls\*.erl $(SRC_DIR)\tls
|
||||
copy tls\*.c $(SRC_DIR)\tls
|
||||
mkdir $(SRC_DIR)\web
|
||||
copy web\*.erl $(SRC_DIR)\web
|
||||
mkdir $(SRC_DIR)\odbc
|
||||
copy odbc\*.erl $(SRC_DIR)\odbc
|
||||
copy odbc\*.sql $(EREL)
|
||||
mkdir $(SRC_DIR)\win32
|
||||
mkdir $(SRC_DIR)\win32\5.3
|
||||
copy win32\5.3\*.erl $(SRC_DIR)\win32\5.3
|
||||
mkdir $(DOC_DIR)
|
||||
copy ..\doc\*.txt $(DOC_DIR)
|
||||
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
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stun
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\odbc
|
||||
cd ..\win32\5.3
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..
|
||||
cd ..\..
|
||||
|
||||
compile-beam : XmppAddr.hrl
|
||||
compile-beam :
|
||||
erl -s make all report -noinput -s erlang halt
|
||||
|
||||
XmppAddr.hrl : XmppAddr.asn1
|
||||
erlc -bber_bin +der +compact_bit_string +optimize +noobj XmppAddr.asn1
|
||||
|
||||
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
|
||||
-@erase XmppAddr.hrl
|
||||
|
||||
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
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stun
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\odbc
|
||||
cd ..\win32\5.3
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..
|
||||
cd ..\..
|
||||
|
||||
distclean : release_clean clean
|
||||
-@erase $(NSIS_HEADER)
|
||||
-@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)
|
||||
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
XmppAddr { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) id-on-xmppAddr(5) }
|
||||
|
||||
DEFINITIONS EXPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) 5 }
|
||||
|
||||
XmppAddr ::= UTF8String
|
||||
|
||||
END
|
||||
@@ -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/>
|
||||
@@ -1,76 +1,27 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : acl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : ACL support
|
||||
%%% Created : 18 Jan 2003 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
|
||||
%%%
|
||||
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(acl).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
to_record/3,
|
||||
add/3,
|
||||
add_list/3,
|
||||
match_rule/3,
|
||||
for_host/1,
|
||||
to_record/2,
|
||||
add/2,
|
||||
add_list/2,
|
||||
match_rule/2,
|
||||
% for debugging only
|
||||
match_acl/3]).
|
||||
match_acl/2]).
|
||||
|
||||
-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,51 +30,30 @@ start() ->
|
||||
mnesia:add_table_copy(acl, node(), ram_copies),
|
||||
ok.
|
||||
|
||||
%% @spec (Host, ACLName, ACLSpec) -> storedacl()
|
||||
%% Host = global | string()
|
||||
%% ACLName = atom()
|
||||
%% ACLSpec = aclspec()
|
||||
to_record(ACLName, ACLSpec) ->
|
||||
#acl{aclname = ACLName, 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) ->
|
||||
add(ACLName, ACLSpec) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#acl{aclname = {ACLName, Host},
|
||||
aclspec = normalize_spec(ACLSpec)})
|
||||
mnesia:write(#acl{aclname = ACLName, aclspec = ACLSpec})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%% @spec (Host, ACLs, Clear) -> ok | false
|
||||
%% Host = global | string()
|
||||
%% ACLs = [acl()]
|
||||
%% Clear = bool()
|
||||
|
||||
add_list(Host, ACLs, Clear) ->
|
||||
add_list(ACLs, Clear) ->
|
||||
F = fun() ->
|
||||
if
|
||||
Clear ->
|
||||
Ks = mnesia:select(
|
||||
acl, [{{acl, {'$1', Host}, '$2'}, [], ['$1']}]),
|
||||
Ks = mnesia:all_keys(acl),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({acl, {K, Host}})
|
||||
mnesia:delete({acl, K})
|
||||
end, Ks);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(fun(ACL) ->
|
||||
case ACL of
|
||||
#acl{aclname = ACLName,
|
||||
aclspec = ACLSpec} ->
|
||||
mnesia:write(
|
||||
#acl{aclname = {ACLName, Host},
|
||||
aclspec = normalize_spec(ACLSpec)})
|
||||
#acl{} ->
|
||||
mnesia:write(ACL)
|
||||
end
|
||||
end, ACLs)
|
||||
end,
|
||||
@@ -134,206 +64,87 @@ 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_spec({A, B}) ->
|
||||
{A, normalize(B)};
|
||||
normalize_spec({A, B, C}) ->
|
||||
{A, normalize(B), normalize(C)};
|
||||
normalize_spec(all) ->
|
||||
all;
|
||||
normalize_spec(none) ->
|
||||
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) ->
|
||||
match_rule(Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
case ejabberd_config:get_global_option({access, Rule}) of
|
||||
undefined ->
|
||||
deny;
|
||||
GACLs ->
|
||||
match_acls(GACLs, JID, global)
|
||||
end
|
||||
end;
|
||||
|
||||
match_rule(Host, Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
undefined ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
deny;
|
||||
ACLs ->
|
||||
match_acls(ACLs, JID, Host)
|
||||
end;
|
||||
GACLs ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
match_acls(GACLs, JID, Host);
|
||||
ACLs ->
|
||||
case lists:reverse(GACLs) of
|
||||
[{allow, all} | Rest] ->
|
||||
match_acls(
|
||||
lists:reverse(Rest) ++ ACLs ++
|
||||
[{allow, all}],
|
||||
JID, Host);
|
||||
_ ->
|
||||
match_acls(GACLs ++ ACLs, JID, Host)
|
||||
end
|
||||
end
|
||||
ACLs ->
|
||||
match_acls(ACLs, 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) ->
|
||||
match_acls([], _) ->
|
||||
deny;
|
||||
match_acls([{Access, ACLName} | ACLs], JID, Host) ->
|
||||
case match_acl(ACLName, JID, Host) of
|
||||
match_acls([{Access, ACL} | ACLs], JID) ->
|
||||
case match_acl(ACL, JID) of
|
||||
true ->
|
||||
Access;
|
||||
_ ->
|
||||
match_acls(ACLs, JID, Host)
|
||||
match_acls(ACLs, JID)
|
||||
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) ->
|
||||
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;
|
||||
{user, U} ->
|
||||
(U == User)
|
||||
andalso
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
?IS_MY_HOST(Server)));
|
||||
(U == User) andalso (?MYNAME == Server);
|
||||
{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)))
|
||||
andalso is_regexp_match(User, UR);
|
||||
{shared_group, G} ->
|
||||
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
|
||||
{shared_group, G, H} ->
|
||||
mod_shared_roster:is_user_in_group({User, Server}, G, H);
|
||||
(?MYNAME == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{user_regexp, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{server_regexp, SR} ->
|
||||
is_regexp_match(Server, SR);
|
||||
{resource_regexp, RR} ->
|
||||
is_regexp_match(Resource, RR);
|
||||
{node_regexp, UR, SR} ->
|
||||
is_regexp_match(Server, SR) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{user_glob, UR} ->
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
?IS_MY_HOST(Server)))
|
||||
andalso
|
||||
(?MYNAME == Server) andalso
|
||||
is_glob_match(User, UR);
|
||||
{user_glob, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_glob_match(User, UR);
|
||||
{server_glob, SR} ->
|
||||
is_glob_match(Server, SR);
|
||||
{resource_glob, RR} ->
|
||||
is_glob_match(Resource, RR);
|
||||
{node_glob, UR, SR} ->
|
||||
is_glob_match(Server, SR) andalso
|
||||
is_glob_match(User, UR);
|
||||
WrongSpec ->
|
||||
?ERROR_MSG(
|
||||
"Wrong ACL expression: ~p~n"
|
||||
"Check your config file and reload it with the override_acls option enabled",
|
||||
[WrongSpec]),
|
||||
false
|
||||
is_glob_match(User, UR)
|
||||
end
|
||||
end,
|
||||
ets:lookup(acl, {ACLName, global}) ++
|
||||
ets:lookup(acl, {ACLName, Host}))
|
||||
end, ets:lookup(acl, ACL))
|
||||
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)).
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
AC_DEFUN(AM_WITH_EXPAT,
|
||||
[ AC_ARG_WITH(expat,
|
||||
[ --with-expat=PREFIX path to expat library],
|
||||
, with_expat=no)
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test $with_expat != no; then
|
||||
if test $with_expat != yes; 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 the Expat library])
|
||||
fi
|
||||
expat_save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $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"
|
||||
fi
|
||||
|
||||
AC_SUBST(EXPAT_CFLAGS)
|
||||
AC_SUBST(EXPAT_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_ERLANG,
|
||||
[ AC_ARG_WITH(erlang,
|
||||
[ --with-erlang=PREFIX path to erlc and erl ])
|
||||
|
||||
|
||||
AC_PATH_TOOL(ERLC, erlc, , $PATH:$with_erlang:$with_erlang/bin)
|
||||
AC_PATH_TOOL(ERL, erl, , $PATH:$with_erlang:$with_erlang/bin)
|
||||
|
||||
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",
|
||||
RootDirS = code:root_dir() ++ "\n",
|
||||
file:write_file("conftest.out", list_to_binary(EIDirS ++ RootDirS)),
|
||||
halt().
|
||||
|
||||
_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
|
||||
|
||||
ERLANG_EI_DIR=`cat conftest.out | head -n 1`
|
||||
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_DIR/lib"
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
AC_SUBST(ERLC)
|
||||
AC_SUBST(ERL)
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN(AC_MOD_ENABLE,
|
||||
[
|
||||
$1=
|
||||
make_$1=
|
||||
AC_MSG_CHECKING([whether build $1])
|
||||
AC_ARG_ENABLE($1,
|
||||
[ --enable-$1 enable $1 (default: $2)],
|
||||
[mr_enable_$1="$enableval"],
|
||||
[mr_enable_$1=$2])
|
||||
if test "$mr_enable_$1" = $2; 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],
|
||||
[ --with-libiconv-prefix=DIR Search for libiconv in DIR/include and DIR/lib], [
|
||||
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)
|
||||
])
|
||||
@@ -1,132 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
|
||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-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(adhoc).
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
|
||||
-export([parse_request/1,
|
||||
produce_response/2,
|
||||
produce_response/1]).
|
||||
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
|
||||
-include("ejabberd.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,
|
||||
|
||||
#adhoc_request{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID,
|
||||
action = Action,
|
||||
xdata = XData,
|
||||
others = Others};
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end
|
||||
catch
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end.
|
||||
|
||||
%% Borrowed from mod_vcard.erl
|
||||
find_xdata_el(#xmlel{children = SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
find_xdata_el1([]) ->
|
||||
false;
|
||||
find_xdata_el1([#xmlel{ns = ?NS_DATA_FORMS} = El | _Els]) ->
|
||||
El;
|
||||
find_xdata_el1([_ | Els]) ->
|
||||
find_xdata_el1(Els).
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record, filling in values for language, node and session id from
|
||||
%% the request.
|
||||
produce_response(#adhoc_request{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID},
|
||||
Response) ->
|
||||
produce_response(Response#adhoc_response{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID}).
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record.
|
||||
produce_response(#adhoc_response{lang = _Lang,
|
||||
node = Node,
|
||||
sessionid = ProvidedSessionID,
|
||||
status = Status,
|
||||
defaultaction = DefaultAction,
|
||||
actions = Actions,
|
||||
notes = Notes,
|
||||
elements = Elements}) ->
|
||||
SessionID = if is_list(ProvidedSessionID), ProvidedSessionID /= "" ->
|
||||
ProvidedSessionID;
|
||||
true ->
|
||||
jlib:now_to_utc_string(now())
|
||||
end,
|
||||
case Actions of
|
||||
[] ->
|
||||
ActionsEls = [];
|
||||
_ ->
|
||||
case DefaultAction of
|
||||
"" ->
|
||||
ActionsElAttrs = [];
|
||||
_ ->
|
||||
ActionsElAttrs = [?XMLATTR(<<"execute">>, DefaultAction)]
|
||||
end,
|
||||
ActionsEls = [#xmlel{ns = ?NS_ADHOC, name = 'actions', attrs =
|
||||
ActionsElAttrs, children =
|
||||
[#xmlel{ns = ?NS_ADHOC, name = 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)}]}
|
||||
end, Notes),
|
||||
#xmlel{ns = ?NS_ADHOC, name = 'command', attrs =
|
||||
[?XMLATTR(<<"sessionid">>, SessionID),
|
||||
?XMLATTR(<<"node">>, Node),
|
||||
?XMLATTR(<<"status">>, Status)], children =
|
||||
ActionsEls ++ NotesEls ++ Elements}.
|
||||
@@ -1,36 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(adhoc_request, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
action,
|
||||
xdata,
|
||||
others}).
|
||||
|
||||
-record(adhoc_response, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
status,
|
||||
defaultaction = "",
|
||||
actions = [],
|
||||
notes = [],
|
||||
elements = []}).
|
||||
@@ -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).
|
||||
@@ -1,53 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : cache_tab_sup.erl
|
||||
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% Description : Cache tables supervisor
|
||||
%%%
|
||||
%%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(cache_tab_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
%%====================================================================
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
|
||||
|
||||
%%====================================================================
|
||||
%% Supervisor callbacks
|
||||
%%====================================================================
|
||||
init([]) ->
|
||||
{ok, {{one_for_one,10,1}, []}}.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
@@ -2,177 +2,42 @@
|
||||
# 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(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_MAKE_SET
|
||||
|
||||
if test "x$GCC" = "xyes"; then
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
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
|
||||
|
||||
# Check Erlang headers are installed
|
||||
#AC_CHECK_HEADER(erl_driver.h,,[AC_MSG_ERROR([cannot find Erlang header files])])
|
||||
|
||||
# Change default prefix
|
||||
AC_PREFIX_DEFAULT(/)
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MALLOC
|
||||
AC_HEADER_STDC
|
||||
|
||||
AC_MOD_ENABLE(mod_muc, yes)
|
||||
AC_MOD_ENABLE(mod_proxy65, yes)
|
||||
|
||||
AC_MOD_ENABLE(mod_pubsub, yes)
|
||||
AC_MOD_ENABLE(mod_irc, yes)
|
||||
AC_MOD_ENABLE(mod_muc, yes)
|
||||
AC_MOD_ENABLE(eldap, yes)
|
||||
AC_MOD_ENABLE(odbc, no)
|
||||
AC_MOD_ENABLE(tls, yes)
|
||||
AC_MOD_ENABLE(web, yes)
|
||||
|
||||
AC_MOD_ENABLE(pam, no)
|
||||
#locating PAM
|
||||
AM_WITH_PAM
|
||||
|
||||
AC_ARG_ENABLE(hipe,
|
||||
[AC_HELP_STRING([--enable-hipe], [compile natively with HiPE, not recommended (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) hipe=true ;;
|
||||
no) hipe=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-hipe) ;;
|
||||
esac],[hipe=false])
|
||||
AC_SUBST(hipe)
|
||||
|
||||
AC_ARG_ENABLE(roster_gateway_workaround,
|
||||
[AC_HELP_STRING([--enable-roster-gateway-workaround], [turn on workaround for processing gateway subscriptions (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) roster_gateway_workaround=true ;;
|
||||
no) roster_gateway_workaround=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --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
|
||||
yes) db_type=mssql ;;
|
||||
no) db_type=generic ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;;
|
||||
esac],[db_type=generic])
|
||||
AC_SUBST(db_type)
|
||||
|
||||
AC_ARG_ENABLE(transient_supervisors,
|
||||
[AC_HELP_STRING([--enable-transient_supervisors], [use Erlang supervision for transient process (default: yes)])],
|
||||
[case "${enableval}" in
|
||||
yes) transient_supervisors=true ;;
|
||||
no) transient_supervisors=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-transient_supervisors) ;;
|
||||
esac],[transient_supervisors=true])
|
||||
AC_SUBST(transient_supervisors)
|
||||
|
||||
AC_ARG_ENABLE(full_xml,
|
||||
[AC_HELP_STRING([--enable-full-xml], [use XML features in XMPP stream (ex: CDATA) (default: no, requires XML compliant clients)])],
|
||||
[case "${enableval}" in
|
||||
yes) full_xml=true ;;
|
||||
no) full_xml=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;;
|
||||
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
|
||||
stun/Makefile
|
||||
$make_tls
|
||||
$make_odbc])
|
||||
#openssl
|
||||
AM_WITH_OPENSSL
|
||||
# If ssl is kerberized it need krb5.h
|
||||
# On RedHat and OpenBSD, krb5.h is in an unsual place:
|
||||
KRB5_INCLUDE="`krb5-config --cflags 2>/dev/null`"
|
||||
if test -n "$KRB5_INCLUDE" ; then
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_INCLUDE"
|
||||
else
|
||||
# For RedHat For BSD
|
||||
for D in /usr/kerberos/include /usr/include/kerberos /usr/include/kerberosV
|
||||
do
|
||||
if test -d $D ; then
|
||||
CPPFLAGS="$CPPFLAGS -I$D"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
AC_CHECK_HEADER(krb5.h,,)
|
||||
|
||||
ENABLEUSER=""
|
||||
AC_ARG_ENABLE(user,
|
||||
[AS_HELP_STRING([--enable-user[[[[=USER]]]]], [allow this system user to start ejabberd (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) ENABLEUSER=`whoami` ;;
|
||||
no) ENABLEUSER="" ;;
|
||||
*) ENABLEUSER=$enableval
|
||||
esac],
|
||||
[])
|
||||
if test "$ENABLEUSER" != ""; then
|
||||
echo "allow this system user to start ejabberd: $ENABLEUSER"
|
||||
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)
|
||||
|
||||
|
||||
case "$target_os" in
|
||||
*darwin10*)
|
||||
echo "Target OS is 'Darwin10'"
|
||||
AC_LANG(Erlang)
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([],[dnl
|
||||
halt(case erlang:system_info(wordsize) of
|
||||
8 -> 0; 4 -> 1 end)])],
|
||||
[AC_MSG_NOTICE(found 64-bit Erlang)
|
||||
CBIT=-m64],
|
||||
[AC_MSG_NOTICE(found 32-bit Erlang)
|
||||
CBIT=-m32])
|
||||
;;
|
||||
*)
|
||||
echo "Target OS is '$target_os'"
|
||||
CBIT=""
|
||||
;;
|
||||
esac
|
||||
CFLAGS="$CFLAGS $CBIT"
|
||||
LD_SHARED="$LD_SHARED $CBIT"
|
||||
echo "CBIT is set to '$CBIT'"
|
||||
|
||||
stringprep/Makefile])
|
||||
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
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : configure.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 27 Jan 2003 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
|
||||
%%%
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(configure).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
@@ -42,34 +25,36 @@ start() ->
|
||||
end,
|
||||
case Static of
|
||||
true ->
|
||||
ZlibDir = "ZLIB_DIR = c:\\sdk\\GnuWin32\n",
|
||||
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n";
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\StaticLibs\\libexpatMT.lib\n",
|
||||
ExpatFlag = "EXPAT_FLAG = -DXML_STATIC\n",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1-static\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n";
|
||||
false ->
|
||||
ZlibDir = "ZLIB_DIR = c:\\sdk\\GnuWin32\n",
|
||||
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n"
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\Libs\\libexpat.lib\n",
|
||||
ExpatFlag = "",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.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",
|
||||
OpenSSLDir = "OPENSSL_DIR = c:\\sdk\\OpenSSL\n",
|
||||
DBType = "DBTYPE = generic\n", %% 'generic' or 'mssql'
|
||||
ExpatDir = "EXPAT_DIR = c:\\progra~1\\expat-1.95.7\n",
|
||||
|
||||
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 ++
|
||||
EIDirS ++
|
||||
list_to_binary(EIDirS ++
|
||||
RootDirS ++
|
||||
Version ++
|
||||
SSLDir ++
|
||||
StdLibDir ++
|
||||
OpenSSLDir ++
|
||||
DBType ++
|
||||
ZlibDir ++
|
||||
ZlibLib)),
|
||||
ExpatDir ++
|
||||
ExpatLib ++
|
||||
ExpatFlag ++
|
||||
IconvDir ++
|
||||
IconvLib)),
|
||||
halt().
|
||||
|
||||
|
||||
|
||||
@@ -1,249 +1,100 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Cyrus SASL-like library
|
||||
%%% Created : 8 Mar 2003 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
|
||||
%%%
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
register_mechanism/3,
|
||||
listmech/1,
|
||||
server_new/8,
|
||||
register_mechanism/2,
|
||||
listmech/0,
|
||||
server_new/4,
|
||||
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}).
|
||||
-record(sasl_state, {service, myname, realm, mech_mod, mech_state}).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
%% @hidden
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{mech_new, 1}, {mech_step, 2}];
|
||||
behaviour_info(_Other) ->
|
||||
[{mech_new, 0},
|
||||
{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.
|
||||
register_mechanism(Mechanism, Module) ->
|
||||
ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism,
|
||||
module = Module}).
|
||||
|
||||
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) ->
|
||||
ets:insert(sasl_mechanism,
|
||||
#sasl_mechanism{mechanism = Mechanism,
|
||||
module = Module,
|
||||
password_type = PasswordType}).
|
||||
|
||||
% 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()
|
||||
|
||||
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
|
||||
% 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.
|
||||
|
||||
%% @spec (Host) -> [Mechanism]
|
||||
%% Host = string()
|
||||
%% Mechanism = string()
|
||||
|
||||
listmech(Host) ->
|
||||
Mechs = ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1',
|
||||
password_type = '$2',
|
||||
_ = '_'},
|
||||
case catch ejabberd_auth:store_type(Host) of
|
||||
external ->
|
||||
[{'==', '$2', plain}];
|
||||
scram ->
|
||||
[{'/=', '$2', digest}];
|
||||
{'EXIT',{undef,[{Module,store_type,[]} | _]}} ->
|
||||
?WARNING_MSG("~p doesn't implement the function store_type/0", [Module]),
|
||||
[];
|
||||
_Else ->
|
||||
[]
|
||||
end,
|
||||
['$1']}]),
|
||||
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
|
||||
},
|
||||
|
||||
#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()
|
||||
|
||||
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),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ ->
|
||||
{error, 'invalid-mechanism'}
|
||||
end;
|
||||
false ->
|
||||
{error, 'invalid-mechanism'}
|
||||
check_credentials(State, Props) ->
|
||||
User = xml:get_attr_s(username, Props),
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, "not-authorized"};
|
||||
"" ->
|
||||
{error, "not-authorized"};
|
||||
LUser ->
|
||||
ok
|
||||
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()
|
||||
listmech() ->
|
||||
ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1', _ = '_'}, [], ['$1']}]).
|
||||
|
||||
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags) ->
|
||||
#sasl_state{service = Service,
|
||||
myname = ServerFQDN,
|
||||
realm = UserRealm}.
|
||||
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} = Module:mech_new(),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ ->
|
||||
{error, "no-mechanism"}
|
||||
end.
|
||||
|
||||
server_step(State, ClientIn) ->
|
||||
Module = State#sasl_state.mech_mod,
|
||||
@@ -256,33 +107,10 @@ 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} ->
|
||||
{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.
|
||||
|
||||
filter_anonymous(Host, Mechs) ->
|
||||
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
|
||||
true -> Mechs;
|
||||
false -> Mechs -- ["ANONYMOUS"]
|
||||
end.
|
||||
|
||||
@@ -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}).
|
||||
@@ -1,76 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_anonymous.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : ANONYMOUS SASL mechanism
|
||||
%%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-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_anonymous).
|
||||
|
||||
-export([start/1, stop/0, mech_new/1, mech_step/2]).
|
||||
|
||||
-include("cyrsasl.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
%% @type mechstate() = {state, Server}
|
||||
%% Server = string().
|
||||
|
||||
-record(state, {server}).
|
||||
|
||||
%% @spec (Opts) -> true
|
||||
%% Opts = term()
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, plain),
|
||||
ok.
|
||||
|
||||
%% @spec () -> ok
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(#sasl_params{host=Host}) ->
|
||||
{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())]),
|
||||
Server = State#state.server,
|
||||
|
||||
%% Checks that the username is available
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
true -> {error, 'not-authorized'};
|
||||
false -> {ok, [{username, User},
|
||||
{auth_module, ejabberd_auth_anonymous}]}
|
||||
end.
|
||||
@@ -3,236 +3,128 @@
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : DIGEST-MD5 SASL mechanism
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-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
|
||||
%%%
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_digest).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
mech_new/1,
|
||||
mech_new/0,
|
||||
mech_step/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("cyrsasl.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
%% @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}).
|
||||
|
||||
-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
|
||||
start(Opts) ->
|
||||
case ejabberd_auth:plain_password_required() of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
|
||||
ok
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(#sasl_params{host=Host, get_password=GetPassword,
|
||||
check_password_digest=CheckPasswordDigest}) ->
|
||||
mech_new() ->
|
||||
{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()
|
||||
nonce = randoms:get_string()}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
"nonce=\"" ++ Nonce ++
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||
%"\",qop=\"auth,auth-int\",charset=utf-8,algorithm=md5-sess",
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
bad ->
|
||||
{error, 'malformed-request'};
|
||||
{error, "bad-protocol"};
|
||||
KeyVals ->
|
||||
DigestURI = proplists:get_value("digest-uri", KeyVals, ""),
|
||||
UserName = proplists:get_value("username", KeyVals, ""),
|
||||
case is_digesturi_valid(DigestURI, State#state.host) of
|
||||
UserName = xml:get_attr_s("username", KeyVals),
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case ejabberd_auth:get_password(UserName) of
|
||||
false ->
|
||||
?DEBUG("User login not authorized because digest-uri "
|
||||
"seems invalid: ~p", [DigestURI]),
|
||||
{error, 'not-authorized', "", UserName};
|
||||
true ->
|
||||
AuthzId = proplists:get_value("authzid", KeyVals, ""),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} ->
|
||||
{error, 'not-authorized', "", UserName};
|
||||
{Passwd, AuthModule} ->
|
||||
case (State#state.check_password)(UserName, "",
|
||||
proplists:get_value("response", KeyVals, ""),
|
||||
fun(PW) -> response(KeyVals, UserName, PW, Nonce, AuthzId,
|
||||
"AUTHENTICATE") end) of
|
||||
{true, _} ->
|
||||
RspAuth = response(KeyVals,
|
||||
UserName, Passwd,
|
||||
Nonce, AuthzId, ""),
|
||||
{continue,
|
||||
"rspauth=" ++ RspAuth,
|
||||
State#state{step = 5,
|
||||
auth_module = AuthModule,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
false ->
|
||||
{error, 'not-authorized', "", UserName};
|
||||
{false, _} ->
|
||||
{error, 'not-authorized', "", UserName}
|
||||
end
|
||||
{error, "no-user"};
|
||||
Passwd ->
|
||||
Response = response(KeyVals, UserName, Passwd,
|
||||
Nonce, AuthzId, "AUTHENTICATE"),
|
||||
case xml:get_attr_s("response", KeyVals) of
|
||||
Response ->
|
||||
RspAuth = response(KeyVals,
|
||||
UserName, Passwd,
|
||||
Nonce, AuthzId, ""),
|
||||
{continue,
|
||||
"rspauth=" ++ RspAuth,
|
||||
State#state{step = 5,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
_ ->
|
||||
{error, "bad-auth"}
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5,
|
||||
auth_module = AuthModule,
|
||||
username = UserName,
|
||||
authzid = AuthzId}, "") ->
|
||||
{ok, [{username, UserName}, {authzid, AuthzId},
|
||||
{auth_module, AuthModule}]};
|
||||
authzid = AuthzId} = State, "") ->
|
||||
{ok, [{username, UserName}, {authzid, AuthzId}]};
|
||||
mech_step(A, B) ->
|
||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
{error, 'malformed-request'}.
|
||||
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
{error, "bad-protocol"}.
|
||||
|
||||
%% @spec (S) -> [{Key, Value}] | bad
|
||||
%% S = string()
|
||||
%% Key = string()
|
||||
%% Value = string()
|
||||
|
||||
parse(S) ->
|
||||
parse1(S, "", []).
|
||||
|
||||
%% @hidden
|
||||
|
||||
parse1([$= | Cs], S, Ts) ->
|
||||
parse2(Cs, lists:reverse(S), "", Ts);
|
||||
parse1([$, | Cs], [], Ts) ->
|
||||
parse1(Cs, [], Ts);
|
||||
parse1([$\s | Cs], [], Ts) ->
|
||||
parse1(Cs, [], Ts);
|
||||
parse1([C | Cs], S, Ts) ->
|
||||
parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) ->
|
||||
lists:reverse(T);
|
||||
parse1([], _S, _T) ->
|
||||
parse1([], S, T) ->
|
||||
bad.
|
||||
|
||||
%% @hidden
|
||||
|
||||
parse2([$\" | Cs], Key, Val, Ts) ->
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, Val, Ts);
|
||||
parse2([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse2([], _, _, _) ->
|
||||
bad.
|
||||
|
||||
%% @hidden
|
||||
|
||||
parse3([$\" | Cs], Key, Val, Ts) ->
|
||||
parse3([$" | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse3([$\\, C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([], _, _, _) ->
|
||||
bad.
|
||||
|
||||
%% @hidden
|
||||
|
||||
parse4([$, | Cs], Key, Val, Ts) ->
|
||||
parse1(Cs, "", [{Key, lists:reverse(Val)} | Ts]);
|
||||
parse4([$\s | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse4([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse4([], Key, Val, Ts) ->
|
||||
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.
|
||||
%% If the service (for example jabber.example.org)
|
||||
%% 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),
|
||||
case catch string:tokens(DigestURI, "/") of
|
||||
["xmpp", Host] when Host == JabberHost ->
|
||||
true;
|
||||
["xmpp", _Host, ServName] when ServName == JabberHost ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
%% @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,123 +132,33 @@ 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
|
||||
"auth" ->
|
||||
A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
case QOP of
|
||||
"auth" ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
T = hex(binary_to_list(crypto:md5(A1))) ++ ":" ++ Nonce ++ ":" ++
|
||||
NC ++ ":" ++ CNonce ++ ":" ++ QOP ++ ":" ++
|
||||
hex(binary_to_list(crypto:md5(A2))),
|
||||
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.
|
||||
|
||||
|
||||
@@ -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}]}.
|
||||
@@ -1,111 +1,47 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_plain.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : PLAIN SASL mechanism
|
||||
%%% Created : 8 Mar 2003 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
|
||||
%%%
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_plain).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]).
|
||||
|
||||
-include("cyrsasl.hrl").
|
||||
-export([start/1, stop/0, mech_new/0, mech_step/2, parse/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
%% @type mechstate() = {state, CheckPassword}
|
||||
%% CheckPassword = function().
|
||||
|
||||
-record(state, {check_password}).
|
||||
|
||||
%% @spec (Opts) -> true
|
||||
%% Opts = term()
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("PLAIN", ?MODULE, plain),
|
||||
start(Opts) ->
|
||||
cyrsasl:register_mechanism("PLAIN", ?MODULE),
|
||||
ok.
|
||||
|
||||
%% @spec () -> ok
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(#sasl_params{check_password = CheckPassword}) ->
|
||||
{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_new() ->
|
||||
{ok, []}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(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}
|
||||
end;
|
||||
_ ->
|
||||
{error, 'malformed-request'}
|
||||
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];
|
||||
case ejabberd_auth:check_password(User, Password) of
|
||||
true ->
|
||||
{ok, [{username, User}, {authzid, AuthzId}]};
|
||||
_ ->
|
||||
{error, "bad-auth"}
|
||||
end;
|
||||
_ ->
|
||||
error
|
||||
{error, "bad-protocol"}
|
||||
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 +52,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]).
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
%% Copyright (c) 2007
|
||||
%% Mats Cronqvist <mats.cronqvist@ericsson.com>
|
||||
%% Chris Newcombe <chris.newcombe@gmail.com>
|
||||
%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
|
||||
%%
|
||||
%% 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.
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : dynamic_compile.erl
|
||||
%%% Description :
|
||||
%%% Authors : Mats Cronqvist <mats.cronqvist@ericsson.com>
|
||||
%%% Chris Newcombe <chris.newcombe@gmail.com>
|
||||
%%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
|
||||
%%% TODO :
|
||||
%%% - add support for limit include-file depth (and prevent circular references)
|
||||
%%% prevent circular macro expansion set FILE correctly when -module() is found
|
||||
%%% -include_lib support $ENVVAR in include filenames
|
||||
%%% substitute-stringize (??MACRO)
|
||||
%%% -undef/-ifdef/-ifndef/-else/-endif
|
||||
%%% -file(File, Line)
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(dynamic_compile).
|
||||
|
||||
%% API
|
||||
-export([from_string/1, from_string/2]).
|
||||
|
||||
-import(lists, [reverse/1, keyreplace/4]).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function:
|
||||
%% Description:
|
||||
%% Returns a binary that can be used with
|
||||
%% code:load_binary(Module, ModuleFilenameForInternalRecords, Binary).
|
||||
%%--------------------------------------------------------------------
|
||||
from_string(CodeStr) ->
|
||||
from_string(CodeStr, []).
|
||||
|
||||
% takes Options as for compile:forms/2
|
||||
from_string(CodeStr, CompileFormsOptions) ->
|
||||
%% Initialise the macro dictionary with the default predefined macros,
|
||||
%% (adapted from epp.erl:predef_macros/1
|
||||
Filename = "compiled_from_string",
|
||||
%%Machine = list_to_atom(erlang:system_info(machine)),
|
||||
Ms0 = dict:new(),
|
||||
% Ms1 = dict:store('FILE', {[], "compiled_from_string"}, Ms0),
|
||||
% Ms2 = dict:store('LINE', {[], 1}, Ms1), % actually we might add special code for this
|
||||
% Ms3 = dict:store('MODULE', {[], undefined}, Ms2),
|
||||
% Ms4 = dict:store('MODULE_STRING', {[], undefined}, Ms3),
|
||||
% Ms5 = dict:store('MACHINE', {[], Machine}, Ms4),
|
||||
% InitMD = dict:store(Machine, {[], true}, Ms5),
|
||||
InitMD = Ms0,
|
||||
|
||||
%% From the docs for compile:forms:
|
||||
%% When encountering an -include or -include_dir directive, the compiler searches for header files in the following directories:
|
||||
%% 1. ".", the current working directory of the file server;
|
||||
%% 2. the base name of the compiled file;
|
||||
%% 3. the directories specified using the i option. The directory specified last is searched first.
|
||||
%% In this case, #2 is meaningless.
|
||||
IncludeSearchPath = ["." | reverse([Dir || {i, Dir} <- CompileFormsOptions])],
|
||||
{RevForms, _OutMacroDict} = scan_and_parse(CodeStr, Filename, 1, [], InitMD, IncludeSearchPath),
|
||||
Forms = reverse(RevForms),
|
||||
|
||||
%% note: 'binary' is forced as an implicit option, whether it is provided or not.
|
||||
case compile:forms(Forms, CompileFormsOptions) of
|
||||
{ok, ModuleName, CompiledCodeBinary} when is_binary(CompiledCodeBinary) ->
|
||||
{ModuleName, CompiledCodeBinary};
|
||||
{ok, ModuleName, CompiledCodeBinary, []} when is_binary(CompiledCodeBinary) -> % empty warnings list
|
||||
{ModuleName, CompiledCodeBinary};
|
||||
{ok, _ModuleName, _CompiledCodeBinary, Warnings} ->
|
||||
throw({?MODULE, warnings, Warnings});
|
||||
Other ->
|
||||
throw({?MODULE, compile_forms, Other})
|
||||
end.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
%%% Code from Mats Cronqvist
|
||||
%%% See http://www.erlang.org/pipermail/erlang-questions/2007-March/025507.html
|
||||
%%%## 'scan_and_parse'
|
||||
%%%
|
||||
%%% basically we call the OTP scanner and parser (erl_scan and
|
||||
%%% erl_parse) line-by-line, but check each scanned line for (or
|
||||
%%% definitions of) macros before parsing.
|
||||
%% returns {ReverseForms, FinalMacroDict}
|
||||
scan_and_parse([], _CurrFilename, _CurrLine, RevForms, MacroDict, _IncludeSearchPath) ->
|
||||
{RevForms, MacroDict};
|
||||
|
||||
scan_and_parse(RemainingText, CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath) ->
|
||||
case scanner(RemainingText, CurrLine, MacroDict) of
|
||||
{tokens, NLine, NRemainingText, Toks} ->
|
||||
{ok, Form} = erl_parse:parse_form(Toks),
|
||||
scan_and_parse(NRemainingText, CurrFilename, NLine, [Form | RevForms], MacroDict, IncludeSearchPath);
|
||||
{macro, NLine, NRemainingText, NMacroDict} ->
|
||||
scan_and_parse(NRemainingText, CurrFilename, NLine, RevForms,NMacroDict, IncludeSearchPath);
|
||||
{include, NLine, NRemainingText, IncludeFilename} ->
|
||||
IncludeFileRemainingTextents = read_include_file(IncludeFilename, IncludeSearchPath),
|
||||
%%io:format("include file ~p contents: ~n~p~nRemainingText = ~p~n", [IncludeFilename,IncludeFileRemainingTextents, RemainingText]),
|
||||
%% Modify the FILE macro to reflect the filename
|
||||
%%IncludeMacroDict = dict:store('FILE', {[],IncludeFilename}, MacroDict),
|
||||
IncludeMacroDict = MacroDict,
|
||||
|
||||
%% Process the header file (inc. any nested header files)
|
||||
{RevIncludeForms, IncludedMacroDict} = scan_and_parse(IncludeFileRemainingTextents, IncludeFilename, 1, [], IncludeMacroDict, IncludeSearchPath),
|
||||
%io:format("include file results = ~p~n", [R]),
|
||||
%% Restore the FILE macro in the NEW MacroDict (so we keep any macros defined in the header file)
|
||||
%%NMacroDict = dict:store('FILE', {[],CurrFilename}, IncludedMacroDict),
|
||||
NMacroDict = IncludedMacroDict,
|
||||
|
||||
%% Continue with the original file
|
||||
scan_and_parse(NRemainingText, CurrFilename, NLine, RevIncludeForms ++ RevForms, NMacroDict, IncludeSearchPath);
|
||||
done ->
|
||||
scan_and_parse([], CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath)
|
||||
end.
|
||||
|
||||
scanner(Text, Line, MacroDict) ->
|
||||
case erl_scan:tokens([],Text,Line) of
|
||||
{done, {ok,Toks,NLine}, LeftOverChars} ->
|
||||
case pre_proc(Toks, MacroDict) of
|
||||
{tokens, NToks} -> {tokens, NLine, LeftOverChars, NToks};
|
||||
{macro, NMacroDict} -> {macro, NLine, LeftOverChars, NMacroDict};
|
||||
{include, Filename} -> {include, NLine, LeftOverChars, Filename}
|
||||
end;
|
||||
{more, _Continuation} ->
|
||||
%% This is supposed to mean "term is not yet complete" (i.e. a '.' has
|
||||
%% not been reached yet).
|
||||
%% However, for some bizarre reason we also get this if there is a comment after the final '.' in a file.
|
||||
%% So we check to see if Text only consists of comments.
|
||||
case is_only_comments(Text) of
|
||||
true ->
|
||||
done;
|
||||
false ->
|
||||
throw({incomplete_term, Text, Line})
|
||||
end
|
||||
end.
|
||||
|
||||
is_only_comments(Text) -> is_only_comments(Text, not_in_comment).
|
||||
|
||||
is_only_comments([], _) -> true;
|
||||
is_only_comments([$ |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
|
||||
is_only_comments([$\t |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
|
||||
is_only_comments([$\n |T], not_in_comment) -> is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
|
||||
is_only_comments([$% |T], not_in_comment) -> is_only_comments(T, in_comment); % found start of a comment
|
||||
is_only_comments(_, not_in_comment) -> false;
|
||||
% found any significant char NOT in a comment
|
||||
is_only_comments([$\n |T], in_comment) -> is_only_comments(T, not_in_comment); % found end of a comment
|
||||
is_only_comments([_ |T], in_comment) -> is_only_comments(T, in_comment). % skipping over in-comment chars
|
||||
|
||||
%%%## 'pre-proc'
|
||||
%%%
|
||||
%%% have to implement a subset of the pre-processor, since epp insists
|
||||
%%% on running on a file.
|
||||
%%% only handles 2 cases;
|
||||
%% -define(MACRO, something).
|
||||
%% -define(MACRO(VAR1,VARN),{stuff,VAR1,more,stuff,VARN,extra,stuff}).
|
||||
pre_proc([{'-',_},{atom,_,define},{'(',_},{_,_,Name}|DefToks],MacroDict) ->
|
||||
false = dict:is_key(Name, MacroDict),
|
||||
case DefToks of
|
||||
[{',',_} | Macro] ->
|
||||
{macro, dict:store(Name, {[], macro_body_def(Macro, [])}, MacroDict)};
|
||||
[{'(',_} | Macro] ->
|
||||
{macro, dict:store(Name, macro_params_body_def(Macro, []), MacroDict)}
|
||||
end;
|
||||
|
||||
pre_proc([{'-',_}, {atom,_,include}, {'(',_}, {string,_,Filename}, {')',_}, {dot,_}], _MacroDict) ->
|
||||
{include, Filename};
|
||||
|
||||
pre_proc(Toks,MacroDict) ->
|
||||
{tokens, subst_macros(Toks, MacroDict)}.
|
||||
|
||||
macro_params_body_def([{')',_},{',',_} | Toks], RevParams) ->
|
||||
{reverse(RevParams), macro_body_def(Toks, [])};
|
||||
macro_params_body_def([{var,_,Param} | Toks], RevParams) ->
|
||||
macro_params_body_def(Toks, [Param | RevParams]);
|
||||
macro_params_body_def([{',',_}, {var,_,Param} | Toks], RevParams) ->
|
||||
macro_params_body_def(Toks, [Param | RevParams]).
|
||||
|
||||
macro_body_def([{')',_}, {dot,_}], RevMacroBodyToks) ->
|
||||
reverse(RevMacroBodyToks);
|
||||
macro_body_def([Tok|Toks], RevMacroBodyToks) ->
|
||||
macro_body_def(Toks, [Tok | RevMacroBodyToks]).
|
||||
|
||||
subst_macros(Toks, MacroDict) ->
|
||||
reverse(subst_macros_rev(Toks, MacroDict, [])).
|
||||
|
||||
%% returns a reversed list of tokes
|
||||
subst_macros_rev([{'?',_}, {_,LineNum,'LINE'} | Toks], MacroDict, RevOutToks) ->
|
||||
%% special-case for ?LINE, to avoid creating a new MacroDict for every line in the source file
|
||||
subst_macros_rev(Toks, MacroDict, [{integer,LineNum,LineNum}] ++ RevOutToks);
|
||||
|
||||
subst_macros_rev([{'?',_}, {_,_,Name}, {'(',_} = Paren | Toks], MacroDict, RevOutToks) ->
|
||||
case dict:fetch(Name, MacroDict) of
|
||||
{[], MacroValue} ->
|
||||
%% This macro does not have any vars, so ignore the fact that the invocation is followed by "(...stuff"
|
||||
%% Recursively expand any macro calls inside this macro's value
|
||||
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
|
||||
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
|
||||
subst_macros_rev([Paren|Toks], MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
|
||||
ParamsAndBody ->
|
||||
%% This macro does have vars.
|
||||
%% Collect all of the passe arguments, in an ordered list
|
||||
{NToks, Arguments} = subst_macros_get_args(Toks, []),
|
||||
%% Expand the varibles
|
||||
ExpandedParamsToks = subst_macros_subst_args_for_vars(ParamsAndBody, Arguments),
|
||||
%% Recursively expand any macro calls inside this macro's value
|
||||
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
|
||||
RevExpandedOtherMacrosToks = subst_macros_rev(ExpandedParamsToks, MacroDict, []),
|
||||
subst_macros_rev(NToks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks)
|
||||
end;
|
||||
|
||||
subst_macros_rev([{'?',_}, {_,_,Name} | Toks], MacroDict, RevOutToks) ->
|
||||
%% This macro invocation does not have arguments.
|
||||
%% Therefore the definition should not have parameters
|
||||
{[], MacroValue} = dict:fetch(Name, MacroDict),
|
||||
|
||||
%% Recursively expand any macro calls inside this macro's value
|
||||
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
|
||||
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
|
||||
subst_macros_rev(Toks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
|
||||
|
||||
subst_macros_rev([Tok|Toks], MacroDict, RevOutToks) ->
|
||||
subst_macros_rev(Toks, MacroDict, [Tok|RevOutToks]);
|
||||
subst_macros_rev([], _MacroDict, RevOutToks) -> RevOutToks.
|
||||
|
||||
subst_macros_get_args([{')',_} | Toks], RevArgs) ->
|
||||
{Toks, reverse(RevArgs)};
|
||||
subst_macros_get_args([{',',_}, {var,_,ArgName} | Toks], RevArgs) ->
|
||||
subst_macros_get_args(Toks, [ArgName| RevArgs]);
|
||||
subst_macros_get_args([{var,_,ArgName} | Toks], RevArgs) ->
|
||||
subst_macros_get_args(Toks, [ArgName | RevArgs]).
|
||||
|
||||
subst_macros_subst_args_for_vars({[], BodyToks}, []) ->
|
||||
BodyToks;
|
||||
subst_macros_subst_args_for_vars({[Param | Params], BodyToks}, [Arg|Args]) ->
|
||||
NBodyToks = keyreplace(Param, 3, BodyToks, {var,1,Arg}),
|
||||
subst_macros_subst_args_for_vars({Params, NBodyToks}, Args).
|
||||
|
||||
read_include_file(Filename, IncludeSearchPath) ->
|
||||
case file:path_open(IncludeSearchPath, Filename, [read, raw, binary]) of
|
||||
{ok, IoDevice, FullName} ->
|
||||
{ok, Data} = file:read(IoDevice, filelib:file_size(FullName)),
|
||||
file:close(IoDevice),
|
||||
binary_to_list(Data);
|
||||
{error, Reason} ->
|
||||
throw({failed_to_read_include_file, Reason, Filename, IncludeSearchPath})
|
||||
end.
|
||||
@@ -1,125 +1,56 @@
|
||||
% $Id$
|
||||
|
||||
{application, ejabberd,
|
||||
[{description, "ejabberd"},
|
||||
{vsn, "3.0.0-alpha-4"},
|
||||
{vsn, "0.1-alpha"},
|
||||
{modules, [acl,
|
||||
adhoc,
|
||||
configure,
|
||||
cyrsasl_anonymous,
|
||||
cyrsasl,
|
||||
cyrsasl_digest,
|
||||
cyrsasl_plain,
|
||||
cyrsasl_scram,
|
||||
ejabberd_admin,
|
||||
ejabberd_app,
|
||||
ejabberd_auth_anonymous,
|
||||
ejabberd_auth,
|
||||
ejabberd_auth_external,
|
||||
ejabberd_auth_ldap,
|
||||
ejabberd_auth_pam,
|
||||
ejabberd_auth_storage,
|
||||
ejabberd,
|
||||
ejabberd_app,
|
||||
ejabberd_auth,
|
||||
ejabberd_c2s,
|
||||
ejabberd_c2s_config,
|
||||
ejabberd_config,
|
||||
ejabberd_ctl,
|
||||
ejabberd_frontend_socket,
|
||||
ejabberd_hooks,
|
||||
ejabberd_http,
|
||||
ejabberd_http_bind,
|
||||
ejabberd_http_poll,
|
||||
ejabberd_listener,
|
||||
ejabberd_local,
|
||||
ejabberd_logger_h,
|
||||
ejabberd_loglevel,
|
||||
ejabberd_node_groups,
|
||||
ejabberd_rdbms,
|
||||
ejabberd_receiver,
|
||||
ejabberd_local,
|
||||
ejabberd_router,
|
||||
ejabberd_router_multicast,
|
||||
ejabberd_s2s,
|
||||
ejabberd_s2s_in,
|
||||
ejabberd_s2s_out,
|
||||
ejabberd_service,
|
||||
ejabberd_sm,
|
||||
ejabberd_socket,
|
||||
ejabberd_sup,
|
||||
ejabberd_system_monitor,
|
||||
ejabberd_tmp_sup,
|
||||
ejabberd_update,
|
||||
ejabberd_web_admin,
|
||||
ejabberd_web,
|
||||
ejd2odbc,
|
||||
eldap,
|
||||
eldap_filter,
|
||||
eldap_pool,
|
||||
eldap_utils,
|
||||
'ELDAPv3',
|
||||
extauth,
|
||||
gen_iq_handler,
|
||||
gen_mod,
|
||||
gen_pubsub_node,
|
||||
gen_pubsub_nodetree,
|
||||
idna,
|
||||
jd2ejd,
|
||||
jlib,
|
||||
mod_adhoc,
|
||||
mod_announce,
|
||||
mod_caps,
|
||||
mod_configure2,
|
||||
mod_configure,
|
||||
mod_disco,
|
||||
mod_echo,
|
||||
mod_http_bind,
|
||||
mod_http_fileserver,
|
||||
mod_last,
|
||||
mod_muc,
|
||||
mod_muc_log,
|
||||
mod_muc_room,
|
||||
mod_offline,
|
||||
mod_privacy,
|
||||
mod_private,
|
||||
mod_proxy65,
|
||||
mod_proxy65_lib,
|
||||
mod_proxy65_service,
|
||||
mod_proxy65_sm,
|
||||
mod_proxy65_stream,
|
||||
mod_pubsub,
|
||||
mod_register,
|
||||
mod_roster,
|
||||
mod_service_log,
|
||||
mod_shared_roster,
|
||||
mod_stats,
|
||||
mod_time,
|
||||
mod_vcard,
|
||||
mod_vcard_ldap,
|
||||
mod_version,
|
||||
node_buddy,
|
||||
node_club,
|
||||
node_default,
|
||||
node_dispatch,
|
||||
node_pep,
|
||||
node_private,
|
||||
node_public,
|
||||
nodetree_default,
|
||||
nodetree_virtual,
|
||||
p1_fsm,
|
||||
p1_mnesia,
|
||||
p1_prof,
|
||||
randoms,
|
||||
sha,
|
||||
shaper,
|
||||
stringprep,
|
||||
stringprep_sup,
|
||||
tls,
|
||||
translate,
|
||||
xml,
|
||||
'XmppAddr'
|
||||
xml_stream
|
||||
]},
|
||||
{registered, [ejabberd,
|
||||
ejabberd_sup,
|
||||
ejabberd_auth,
|
||||
ejabberd_router,
|
||||
ejabberd_router_multicast,
|
||||
ejabberd_sm,
|
||||
ejabberd_s2s,
|
||||
ejabberd_local,
|
||||
@@ -132,6 +63,7 @@
|
||||
ejabberd_mod_roster,
|
||||
ejabberd_mod_echo,
|
||||
ejabberd_mod_pubsub,
|
||||
ejabberd_mod_irc,
|
||||
ejabberd_mod_muc,
|
||||
ejabberd_offline,
|
||||
random_generator
|
||||
@@ -141,7 +73,7 @@
|
||||
{mod, {ejabberd_app, []}}]}.
|
||||
|
||||
|
||||
%% Local Variables:
|
||||
%% mode: erlang
|
||||
%% End:
|
||||
%% vim: set filetype=erlang tabstop=8:
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
|
||||
@@ -1,653 +1,141 @@
|
||||
%%%
|
||||
%%% ejabberd configuration file
|
||||
%%%
|
||||
%%%'
|
||||
% $Id$
|
||||
|
||||
%%% The parameters used in this configuration file are explained in more detail
|
||||
%%% in the ejabberd Installation and Operation Guide.
|
||||
%%% Please consult the Guide in case of doubts, it is included with
|
||||
%%% your copy of ejabberd, and is also available online at
|
||||
%%% http://www.process-one.net/en/ejabberd/docs/
|
||||
%override_acls.
|
||||
|
||||
%%% This configuration file contains Erlang terms.
|
||||
%%% In case you want to understand the syntax, here are the concepts:
|
||||
%%%
|
||||
%%% - The character to comment a line is %
|
||||
%%%
|
||||
%%% - Each term ends in a dot, for example:
|
||||
%%% override_global.
|
||||
%%%
|
||||
%%% - A tuple has a fixed definition, its elements are
|
||||
%%% enclosed in {}, and separated with commas:
|
||||
%%% {loglevel, 4}.
|
||||
%%%
|
||||
%%% - A list can have as many elements as you want,
|
||||
%%% and is enclosed in [], for example:
|
||||
%%% [http_poll, web_admin, tls]
|
||||
%%%
|
||||
%%% - A keyword of ejabberd is a word in lowercase.
|
||||
%%% Strings are enclosed in "" and can contain spaces, dots, ...
|
||||
%%% {language, "en"}.
|
||||
%%% {ldap_rootdn, "dc=example,dc=com"}.
|
||||
%%%
|
||||
%%% - This term includes a tuple, a keyword, a list, and two strings:
|
||||
%%% {hosts, ["jabber.example.net", "im.example.com"]}.
|
||||
%%%
|
||||
|
||||
% Users that have admin access. Add line like one of the following after you
|
||||
% will be successfully registered on server to get admin access:
|
||||
%{acl, admin, {user, "aleksey"}}.
|
||||
%{acl, admin, {user, "ermine"}}.
|
||||
|
||||
%%%. =======================
|
||||
%%%' OVERRIDE STORED OPTIONS
|
||||
% Blocked users:
|
||||
%{acl, blocked, {user, "test"}}.
|
||||
|
||||
%%
|
||||
%% Override the old values stored in the database.
|
||||
%%
|
||||
|
||||
%%
|
||||
%% Override global options (shared by all ejabberd nodes in a cluster).
|
||||
%%
|
||||
%%override_global.
|
||||
|
||||
%%
|
||||
%% Override local options (specific for this particular ejabberd node).
|
||||
%%
|
||||
%%override_local.
|
||||
|
||||
%%
|
||||
%% Remove the Access Control Lists before new ones are added.
|
||||
%%
|
||||
%%override_acls.
|
||||
|
||||
|
||||
%%%. =========
|
||||
%%%' DEBUGGING
|
||||
|
||||
%%
|
||||
%% loglevel: Verbosity of log files generated by ejabberd.
|
||||
%% 0: No ejabberd log at all (not recommended)
|
||||
%% 1: Critical
|
||||
%% 2: Error
|
||||
%% 3: Warning
|
||||
%% 4: Info
|
||||
%% 5: Debug
|
||||
%%
|
||||
{loglevel, 4}.
|
||||
|
||||
%%
|
||||
%% watchdog_admins: Only useful for developers: if an ejabberd process
|
||||
%% consumes a lot of memory, send live notifications to these XMPP
|
||||
%% accounts.
|
||||
%%
|
||||
%%{watchdog_admins, ["bob@example.com"]}.
|
||||
|
||||
|
||||
%%%. =====================
|
||||
%%%' 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"]}.
|
||||
%%
|
||||
%% 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.
|
||||
%% For example, if this ejabberd serves example.org and you want
|
||||
%% to allow communication with an XMPP server called im.example.org.
|
||||
%%
|
||||
%%{route_subdomains, s2s}.
|
||||
|
||||
|
||||
%%%. ===============
|
||||
%%%' LISTENING PORTS
|
||||
|
||||
%%
|
||||
%% listen: The ports ejabberd will listen on, which service each is handled
|
||||
%% by and what options to start it with.
|
||||
%%
|
||||
{listen,
|
||||
[
|
||||
|
||||
{5222, ejabberd_c2s, [
|
||||
|
||||
%%
|
||||
%% If TLS is compiled in and you installed a SSL
|
||||
%% certificate, specify the full path to the
|
||||
%% file and uncomment this line:
|
||||
%%
|
||||
%%{certfile, "/path/to/ssl.pem"}, starttls,
|
||||
|
||||
{access, c2s},
|
||||
{shaper, c2s_shaper},
|
||||
{max_stanza_size, 65536}
|
||||
]},
|
||||
|
||||
%%
|
||||
%% To enable the old SSL connection method on port 5223:
|
||||
%%
|
||||
%%{5223, ejabberd_c2s, [
|
||||
%% {access, c2s},
|
||||
%% {shaper, c2s_shaper},
|
||||
%% {certfile, "/path/to/ssl.pem"}, tls,
|
||||
%% {max_stanza_size, 65536}
|
||||
%% ]},
|
||||
|
||||
{5269, ejabberd_s2s_in, [
|
||||
{shaper, s2s_shaper},
|
||||
{max_stanza_size, 131072}
|
||||
]},
|
||||
|
||||
%%
|
||||
%% ejabberd_service: Interact with external components (transports, ...)
|
||||
%%
|
||||
%%{8888, ejabberd_service, [
|
||||
%% {access, all},
|
||||
%% {shaper_rule, fast},
|
||||
%% {ip, {127, 0, 0, 1}},
|
||||
%% {hosts, ["icq.example.org", "sms.example.org"],
|
||||
%% [{password, "secret"}]
|
||||
%% }
|
||||
%% ]},
|
||||
|
||||
%%
|
||||
%% ejabberd_stun: Handles STUN Binding requests
|
||||
%%
|
||||
%%{{3478, udp}, ejabberd_stun, []},
|
||||
|
||||
{5280, ejabberd_http, [
|
||||
%%{request_handlers,
|
||||
%% [
|
||||
%% {["pub", "archive"], mod_http_fileserver}
|
||||
%% ]},
|
||||
captcha,
|
||||
http_bind,
|
||||
http_poll,
|
||||
%%register,
|
||||
web_admin
|
||||
]}
|
||||
|
||||
]}.
|
||||
|
||||
%%
|
||||
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
|
||||
%% Allowed values are: false optional required required_trusted
|
||||
%% You must specify a certificate file.
|
||||
%%
|
||||
%%{s2s_use_starttls, optional}.
|
||||
|
||||
%%
|
||||
%% s2s_certfile: Specify a certificate file.
|
||||
%%
|
||||
%%{s2s_certfile, "/path/to/ssl.pem"}.
|
||||
|
||||
%%
|
||||
%% domain_certfile: Specify a different certificate for each served hostname.
|
||||
%%
|
||||
%%{domain_certfile, "example.org", "/path/to/example_org.pem"}.
|
||||
%%{domain_certfile, "example.com", "/path/to/example_com.pem"}.
|
||||
|
||||
%%
|
||||
%% S2S whitelist or blacklist
|
||||
%%
|
||||
%% Default s2s policy for undefined hosts.
|
||||
%%
|
||||
%%{s2s_default_policy, allow}.
|
||||
|
||||
%%
|
||||
%% Allow or deny communication with specific servers.
|
||||
%%
|
||||
%%{{s2s_host, "goodhost.org"}, allow}.
|
||||
%%{{s2s_host, "badhost.org"}, deny}.
|
||||
|
||||
%%
|
||||
%% Outgoing S2S options
|
||||
%%
|
||||
%% Preferred address families (which to try first) and connect timeout
|
||||
%% in milliseconds.
|
||||
%%
|
||||
%%{outgoing_s2s_options, [ipv4, ipv6], 10000}.
|
||||
|
||||
|
||||
%%%. ==============
|
||||
%%%' AUTHENTICATION
|
||||
|
||||
%%
|
||||
%% auth_method: Method used to authenticate the users.
|
||||
%% The default method is the internal Mnesia database storage.
|
||||
%% If you want to use a different method,
|
||||
%% comment those lines and enable the desired lines later.
|
||||
%%
|
||||
{auth_method, storage}.
|
||||
{auth_storage, mnesia}.
|
||||
|
||||
%%
|
||||
%% Authentication using ODBC
|
||||
%% Remember to setup a database in the next section.
|
||||
%%
|
||||
%%{auth_method, storage}.
|
||||
%%{auth_storage, odbc}.
|
||||
|
||||
%%
|
||||
%% Authentication using external script
|
||||
%% Make sure the script is executable by ejabberd.
|
||||
%%
|
||||
%%{auth_method, external}.
|
||||
%%{extauth_program, "/path/to/authentication/script"}.
|
||||
|
||||
%%
|
||||
%% Authentication using PAM
|
||||
%%
|
||||
%%{auth_method, pam}.
|
||||
%%{pam_service, "pamservicename"}.
|
||||
|
||||
%%
|
||||
%% Authentication using LDAP
|
||||
%%
|
||||
%%{auth_method, ldap}.
|
||||
%%
|
||||
%% List of LDAP servers:
|
||||
%%{ldap_servers, ["localhost"]}.
|
||||
%%
|
||||
%% Encryption of connection to LDAP servers:
|
||||
%%{ldap_encrypt, none}.
|
||||
%%{ldap_encrypt, tls}.
|
||||
%%
|
||||
%% Port to connect to on LDAP servers:
|
||||
%%{ldap_port, 389}.
|
||||
%%{ldap_port, 636}.
|
||||
%%
|
||||
%% LDAP manager:
|
||||
%%{ldap_rootdn, "dc=example,dc=com"}.
|
||||
%%
|
||||
%% Password of LDAP manager:
|
||||
%%{ldap_password, "******"}.
|
||||
%%
|
||||
%% Search base of LDAP directory:
|
||||
%%{ldap_base, "dc=example,dc=com"}.
|
||||
%%
|
||||
%% LDAP attribute that holds user ID:
|
||||
%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}.
|
||||
%%
|
||||
%% LDAP filter:
|
||||
%%{ldap_filter, "(objectClass=shadowAccount)"}.
|
||||
|
||||
%%
|
||||
%% Anonymous login support:
|
||||
%% auth_method: anonymous
|
||||
%% anonymous_protocol: sasl_anon | login_anon | both
|
||||
%% allow_multiple_connections: true | false
|
||||
%%
|
||||
%%{host_config, "public.example.org", [{auth_method, anonymous},
|
||||
%% {allow_multiple_connections, false},
|
||||
%% {anonymous_protocol, sasl_anon}]}.
|
||||
%%
|
||||
%% To use both anonymous and internal authentication:
|
||||
%%
|
||||
%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
|
||||
|
||||
%%
|
||||
%% 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.
|
||||
%% This section provides configuration examples in case
|
||||
%% you want to use other database backends.
|
||||
%% Please consult the ejabberd Guide for details on 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"}}
|
||||
%%
|
||||
%% If you want to specify the port:
|
||||
%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}
|
||||
|
||||
%%
|
||||
%% PostgreSQL server:
|
||||
%%
|
||||
%%{odbc_server, {pgsql, "server", "database", "username", "password"}}
|
||||
%%
|
||||
%% If you want to specify the port:
|
||||
%%{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"
|
||||
%%
|
||||
%%{pgsql_users_number_estimate, true}.
|
||||
|
||||
%%
|
||||
%% ODBC compatible or MSSQL server:
|
||||
%%
|
||||
%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
|
||||
|
||||
%%
|
||||
%% Number of connections to open to the database for each virtual host
|
||||
%%
|
||||
%%{odbc_pool_size, 10}.
|
||||
|
||||
%%
|
||||
%% Interval to make a dummy SQL request to keep the connections to the
|
||||
%% database alive. Specify in seconds: for example 28800 means 8 hours
|
||||
%%
|
||||
%%{odbc_keepalive_interval, undefined}.
|
||||
|
||||
|
||||
%%%. ===============
|
||||
%%%' TRAFFIC SHAPERS
|
||||
|
||||
%%
|
||||
%% The "normal" shaper limits traffic speed to 1000 B/s
|
||||
%%
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
|
||||
%%
|
||||
%% The "fast" shaper limits traffic speed to 50000 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.
|
||||
%%
|
||||
%%{acl, admin, {user, "aleksey", "localhost"}}.
|
||||
%%{acl, admin, {user, "ermine", "example.org"}}.
|
||||
|
||||
%%
|
||||
%% Blocked users
|
||||
%%
|
||||
%%{acl, blocked, {user, "baduser", "example.org"}}.
|
||||
%%{acl, blocked, {user, "test"}}.
|
||||
|
||||
%%
|
||||
%% Local users: don't modify this line.
|
||||
%%
|
||||
% Local users:
|
||||
{acl, local, {user_regexp, ""}}.
|
||||
|
||||
%%
|
||||
%% More examples of ACLs
|
||||
%%
|
||||
%%{acl, jabberorg, {server, "jabber.org"}}.
|
||||
%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
|
||||
%%{acl, test, {user_regexp, "^test"}}.
|
||||
%%{acl, test, {user_glob, "test*"}}.
|
||||
|
||||
%%
|
||||
%% Define specific ACLs in a virtual host.
|
||||
%%
|
||||
%%{host_config, "localhost",
|
||||
%% [
|
||||
%% {acl, admin, {user, "bob-local", "localhost"}}
|
||||
%% ]
|
||||
%%}.
|
||||
% Another examples of ACLs:
|
||||
%{acl, jabberorg, {server, "jabber.org"}}.
|
||||
%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
|
||||
%{acl, test, {user_regexp, "^test"}}.
|
||||
%{acl, test, {user_glob, "test*"}}.
|
||||
|
||||
|
||||
%%%. ============
|
||||
%%%' ACCESS RULES
|
||||
% Only admins can use configuration interface:
|
||||
{access, configure, [{allow, admin}]}.
|
||||
|
||||
%% Maximum number of simultaneous sessions allowed for a single user:
|
||||
{access, max_user_sessions, [{10, all}]}.
|
||||
% Every username can be registered via in-band registration:
|
||||
{access, register, [{allow, all}]}.
|
||||
|
||||
%% Maximum number of offline messages that users can have:
|
||||
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
|
||||
% After successful registration user will get message with following subject
|
||||
% and body:
|
||||
{welcome_message,
|
||||
{"Welcome!",
|
||||
"Welcome to Jabber Service. "
|
||||
"For information about Jabber visit http://jabber.org"}}.
|
||||
% Replace them with 'none' if you don't want to send such message:
|
||||
%{welcome_message, none}.
|
||||
|
||||
%% This rule allows access only for local users:
|
||||
{access, local, [{allow, local}]}.
|
||||
% List of people who will get notifications about registered users
|
||||
%{registration_watchers, ["admin1@localhost",
|
||||
% "admin2@localhost"]}.
|
||||
|
||||
%% Only non-blocked users can use c2s connections:
|
||||
% Only admins can send announcement messages:
|
||||
{access, announce, [{allow, admin}]}.
|
||||
|
||||
|
||||
% Only non-blocked users can use c2s connections:
|
||||
{access, c2s, [{deny, blocked},
|
||||
{allow, all}]}.
|
||||
|
||||
%% For C2S connections, all users except admins use the "normal" shaper
|
||||
% Set shaper with name "normal" to limit traffic speed to 1000B/s
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
|
||||
% Set shaper with name "fast" to limit traffic speed to 50000B/s
|
||||
{shaper, fast, {maxrate, 50000}}.
|
||||
|
||||
% For all users except admins used "normal" shaper
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
|
||||
%% All S2S connections use the "fast" shaper
|
||||
% For all S2S connections used "fast" shaper
|
||||
{access, s2s_shaper, [{fast, all}]}.
|
||||
|
||||
%% Only admins can send announcement messages:
|
||||
{access, announce, [{allow, admin}]}.
|
||||
|
||||
%% Only admins can use the 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:
|
||||
{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
|
||||
%%{registration_timeout, 600}.
|
||||
|
||||
%%
|
||||
%% Define specific Access Rules in a virtual host.
|
||||
%%
|
||||
%%{host_config, "localhost",
|
||||
%% [
|
||||
%% {access, c2s, [{allow, admin}, {deny, all}]},
|
||||
%% {access, register, [{deny, all}]}
|
||||
%% ]
|
||||
%%}.
|
||||
% This rule allows access only for local users:
|
||||
{access, local, [{allow, local}]}.
|
||||
|
||||
|
||||
%%%. =======
|
||||
%%%' CAPTCHA
|
||||
% Authentification method. If you want to use internal user base, then use
|
||||
% this line:
|
||||
{auth_method, internal}.
|
||||
|
||||
%%
|
||||
%% 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"}.
|
||||
% For LDAP uthentification use these lines instead of above one:
|
||||
%{auth_method, ldap}.
|
||||
%{ldap_servers, ["localhost"]}. % List of LDAP servers
|
||||
%{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID
|
||||
%{ldap_base, "dc=example,dc=com"}. % Base of LDAP directory
|
||||
|
||||
|
||||
%%%. ================
|
||||
%%%' DEFAULT LANGUAGE
|
||||
% Host name:
|
||||
{host, "localhost"}.
|
||||
|
||||
%%
|
||||
%% language: Default language used for server messages.
|
||||
%%
|
||||
|
||||
% Default language for server messages
|
||||
{language, "en"}.
|
||||
|
||||
%%
|
||||
%% Set a different default language in a virtual host.
|
||||
%%
|
||||
%%{host_config, "localhost",
|
||||
%% [{language, "ru"}]
|
||||
%%}.
|
||||
% Listened ports:
|
||||
{listen,
|
||||
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
|
||||
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]},
|
||||
{5280, ejabberd_http, [http_poll, web_admin]},
|
||||
{8888, ejabberd_service, [{access, all},
|
||||
{hosts, ["icq.localhost", "sms.localhost"],
|
||||
[{password, "secret"}]}]}
|
||||
]}.
|
||||
|
||||
% If SRV lookup fails, then port 5269 is used to communicate with remote server
|
||||
{outgoing_s2s_port, 5269}.
|
||||
|
||||
|
||||
%%%. ================
|
||||
%%%' INSTANCE MODULES
|
||||
|
||||
%%
|
||||
%% Modules to start for every virtual host.
|
||||
%%
|
||||
%% Note: One copy of each module will be started for each host.
|
||||
%%
|
||||
% Used modules:
|
||||
{modules,
|
||||
[
|
||||
{mod_adhoc, []},
|
||||
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
|
||||
{mod_blocking,[]}, % requires mod_privacy
|
||||
{mod_caps, []}, % 1 proc/host
|
||||
{mod_configure,[]}, % requires mod_adhoc
|
||||
{mod_disco, []},
|
||||
%%{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_http_bind, []},
|
||||
%%{mod_http_fileserver, [
|
||||
%% {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_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_register, [
|
||||
%%
|
||||
%% Protect In-Band account registrations with CAPTCHA.
|
||||
%%
|
||||
%%{captcha_protected, true},
|
||||
|
||||
%%
|
||||
%% Set the minimum informational entropy for passwords.
|
||||
%%
|
||||
%%{password_strength, 32},
|
||||
|
||||
%%
|
||||
%% After successful registration, the user receives
|
||||
%% a message with this subject and body.
|
||||
%%
|
||||
{welcome_message, {"Welcome!",
|
||||
"Hi.\nWelcome to this XMPP server."}},
|
||||
|
||||
%%
|
||||
%% When a user registers, send a notification to
|
||||
%% these XMPP accounts.
|
||||
%%
|
||||
%%{registration_watchers, ["admin1@example.org"]},
|
||||
|
||||
%%
|
||||
%% Only clients in the server machine can register accounts
|
||||
%%
|
||||
{ip_access, [{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_version, []}
|
||||
{mod_register, [{access, register}]},
|
||||
{mod_roster, []},
|
||||
{mod_privacy, []},
|
||||
{mod_configure, []},
|
||||
{mod_configure2, []},
|
||||
{mod_disco, []},
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
% Default options for mod_muc:
|
||||
% host: "conference." ++ ?MYNAME
|
||||
% access: all
|
||||
% access_create: all
|
||||
% access_admin: none (only room creator has owner privileges)
|
||||
{mod_muc, [{access, muc},
|
||||
{access_create, muc},
|
||||
{access_admin, muc_admin}]},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{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
|
||||
%%
|
||||
%%{host_config, "localhost",
|
||||
%% [{{add, modules},
|
||||
%% [
|
||||
%% {mod_echo, [{host, "mirror.localhost"}]}
|
||||
%% ]
|
||||
%% }
|
||||
%% ]}.
|
||||
|
||||
|
||||
%%%.
|
||||
%%%'
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: erlang
|
||||
%%% End:
|
||||
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker:
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
|
||||
@@ -1,49 +1,24 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : ejabberd wrapper: start / stop
|
||||
%%% Created : 16 Nov 2002 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
|
||||
%%%
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-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_so_path/0]).
|
||||
|
||||
start() ->
|
||||
%%ejabberd_cover:start(),
|
||||
application:start(ejabberd).
|
||||
|
||||
stop() ->
|
||||
application:stop(ejabberd).
|
||||
%%ejabberd_cover:stop().
|
||||
|
||||
|
||||
get_so_path() ->
|
||||
case os:getenv("EJABBERD_SO_PATH") of
|
||||
@@ -57,54 +32,3 @@ get_so_path() ->
|
||||
Path ->
|
||||
Path
|
||||
end.
|
||||
|
||||
get_bin_path() ->
|
||||
case os:getenv("EJABBERD_BIN_PATH") of
|
||||
false ->
|
||||
case code:priv_dir(ejabberd) of
|
||||
{error, _} ->
|
||||
".";
|
||||
Path ->
|
||||
filename:join([Path, "bin"])
|
||||
end;
|
||||
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
|
||||
false ->
|
||||
false;
|
||||
"" ->
|
||||
false;
|
||||
Path ->
|
||||
Path
|
||||
end.
|
||||
|
||||
@@ -1,73 +1,42 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%% File : ejabberd.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 17 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% This macro returns a string of the ejabberd version running, e.g. "2.3.4"
|
||||
%% If the ejabberd application description isn't loaded, returns atom: undefined
|
||||
-define(VERSION, element(2, application:get_key(ejabberd,vsn))).
|
||||
-define(VERSION, "0.6-alpha").
|
||||
|
||||
-define(IS_MY_HOST(Host), ejabberd:is_my_host(Host)).
|
||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)). %% Deprecated
|
||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
||||
%-define(ejabberd_debug, true).
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(ejabberd_debug).
|
||||
-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
-else.
|
||||
-define(DEBUG(F,A),[]).
|
||||
-endif.
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
error_logger:error_msg("E(~p:~p:~p): "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
-define(INFO_MSG(Format, Args),
|
||||
error_logger:info_msg("I(~p:~p:~p): "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
|
||||
%-define(MYNAME,"e.localhost").
|
||||
-define(MYNAME, ejabberd_config:get_global_option(host)).
|
||||
-define(S2STIMEOUT, 600000).
|
||||
%-define(S2STIMEOUT, 6000).
|
||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
||||
|
||||
-define(MSGS_DIR, "msgs").
|
||||
-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
|
||||
|
||||
%% Print in standard output
|
||||
-define(PRINT(Format, Args),
|
||||
io:format(Format, Args)).
|
||||
|
||||
-define(DEBUG(Format, Args),
|
||||
ejabberd_logger:debug_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(INFO_MSG(Format, Args),
|
||||
ejabberd_logger:info_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(WARNING_MSG(Format, Args),
|
||||
ejabberd_logger:warning_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
ejabberd_logger:error_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(CRITICAL_MSG(Format, Args),
|
||||
ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#! /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
|
||||
|
||||
DIR=@ctlscriptpath@
|
||||
CTL="$DIR"/ejabberdctl
|
||||
USER=@installuser@
|
||||
|
||||
test -x "$CTL" || {
|
||||
echo "ERROR: ejabberd not found: $DIR"
|
||||
exit 1
|
||||
}
|
||||
grep ^"$USER": /etc/passwd >/dev/null || {
|
||||
echo "ERROR: System user not found: $USER"
|
||||
exit 2
|
||||
}
|
||||
|
||||
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
test -x "$CTL" || exit 0
|
||||
echo "Starting ejabberd..."
|
||||
su - $USER -c "$CTL start"
|
||||
su - $USER -c "$CTL started"
|
||||
echo "done."
|
||||
;;
|
||||
stop)
|
||||
test -x "$CTL" || exit 0
|
||||
echo "Stopping ejabberd..."
|
||||
su - $USER -c "$CTL stop"
|
||||
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}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,603 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : ejabberd_admin.erl
|
||||
%%% Author : Mickael Remond <mremond@process-one.net>
|
||||
%%% Purpose : Administrative functions and commands
|
||||
%%% Created : 7 May 2006 by Mickael Remond <mremond@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(ejabberd_admin).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-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,
|
||||
dump_to_textfile/1, dump_to_textfile/2,
|
||||
mnesia_change_nodename/4,
|
||||
restore/1 % Still used by some modules
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
start() ->
|
||||
ejabberd_commands:register_commands(commands()).
|
||||
|
||||
stop() ->
|
||||
ejabberd_commands:unregister_commands(commands()).
|
||||
|
||||
%%%
|
||||
%%% ejabberd commands
|
||||
%%%
|
||||
|
||||
commands() ->
|
||||
[
|
||||
%% The commands status, stop and restart are implemented also in ejabberd_ctl
|
||||
%% They are defined here so that other interfaces can use them too
|
||||
#ejabberd_commands{name = status, tags = [server],
|
||||
desc = "Get status of the ejabberd server",
|
||||
module = ?MODULE, function = status,
|
||||
args = [], result = {res, restuple}},
|
||||
#ejabberd_commands{name = stop, tags = [server],
|
||||
desc = "Stop ejabberd gracefully",
|
||||
module = init, function = stop,
|
||||
args = [], result = {res, rescode}},
|
||||
#ejabberd_commands{name = restart, tags = [server],
|
||||
desc = "Restart ejabberd gracefully",
|
||||
module = init, function = restart,
|
||||
args = [], result = {res, rescode}},
|
||||
#ejabberd_commands{name = reopen_log, tags = [logs, server],
|
||||
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,
|
||||
args = [],
|
||||
result = {leveltuple, {tuple, [{levelnumber, integer},
|
||||
{levelatom, atom},
|
||||
{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,
|
||||
args = [{user, string}, {host, string}, {password, string}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = unregister, tags = [accounts],
|
||||
desc = "Unregister a user",
|
||||
module = ?MODULE, function = unregister,
|
||||
args = [{user, string}, {host, string}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = registered_users, tags = [accounts],
|
||||
desc = "List all registered users in HOST",
|
||||
module = ?MODULE, function = registered_users,
|
||||
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,
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = import_dir, tags = [mnesia],
|
||||
desc = "Import users data from jabberd14 spool dir",
|
||||
module = ?MODULE, function = import_dir,
|
||||
args = [{file, string}],
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = import_piefxis, tags = [mnesia],
|
||||
desc = "Import users data from a PIEFXIS file (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = import_file,
|
||||
args = [{file, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = export_piefxis, tags = [mnesia],
|
||||
desc = "Export data of all users in the server to PIEFXIS files (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = export_server,
|
||||
args = [{dir, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = export_piefxis_host, tags = [mnesia],
|
||||
desc = "Export data of users in a host to PIEFXIS files (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = export_host,
|
||||
args = [{dir, string}, {host, string}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
|
||||
desc = "Delete expired offline messages from database",
|
||||
module = ?MODULE, function = delete_expired_messages,
|
||||
args = [], result = {res, rescode}},
|
||||
#ejabberd_commands{name = delete_old_messages, tags = [purge],
|
||||
desc = "Delete offline messages older than DAYS",
|
||||
module = ?MODULE, function = delete_old_messages,
|
||||
args = [{days, integer}], 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,
|
||||
args = [{oldnodename, string}, {newnodename, string},
|
||||
{oldbackup, string}, {newbackup, string}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = backup, tags = [mnesia],
|
||||
desc = "Store the database to backup file",
|
||||
module = ?MODULE, function = backup_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = restore, tags = [mnesia],
|
||||
desc = "Restore the database from backup file",
|
||||
module = ?MODULE, function = restore_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = dump, tags = [mnesia],
|
||||
desc = "Dump the database to text file",
|
||||
module = ?MODULE, function = dump_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = dump_table, tags = [mnesia],
|
||||
desc = "Dump a table to text file",
|
||||
module = ?MODULE, function = dump_table,
|
||||
args = [{file, string}, {table, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = load, tags = [mnesia],
|
||||
desc = "Restore the database from text file",
|
||||
module = ?MODULE, function = load_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = install_fallback, tags = [mnesia],
|
||||
desc = "Install the database from a fallback file",
|
||||
module = ?MODULE, function = install_fallback_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}}
|
||||
].
|
||||
|
||||
|
||||
%%%
|
||||
%%% Server management
|
||||
%%%
|
||||
|
||||
status() ->
|
||||
{InternalStatus, ProvidedStatus} = init:get_status(),
|
||||
String1 = io_lib:format("The node ~p is ~p. Status: ~p",
|
||||
[node(), InternalStatus, ProvidedStatus]),
|
||||
{Is_running, String2} =
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
false ->
|
||||
{ejabberd_not_running, "ejabberd is not running in that node."};
|
||||
{value, {_, _, Version}} ->
|
||||
{ok, io_lib:format("ejabberd ~s is running in that node", [Version])}
|
||||
end,
|
||||
{Is_running, String1 ++ String2}.
|
||||
|
||||
reopen_log() ->
|
||||
ejabberd_hooks:run(reopen_log_hook, []),
|
||||
%% TODO: Use the Reopen log API for logger_h ?
|
||||
ejabberd_logger_h:reopen_log(),
|
||||
case application:get_env(sasl,sasl_error_logger) of
|
||||
{ok, {file, SASLfile}} ->
|
||||
error_logger:delete_report_handler(sasl_report_file_h),
|
||||
ejabberd_logger_h:rotate_log(SASLfile),
|
||||
error_logger:add_report_handler(sasl_report_file_h,
|
||||
{SASLfile, get_sasl_error_logger_type()});
|
||||
_ -> false
|
||||
end,
|
||||
ok.
|
||||
|
||||
%% Function copied from Erlang/OTP lib/sasl/src/sasl.erl which doesn't export it
|
||||
get_sasl_error_logger_type () ->
|
||||
case application:get_env (sasl, errlog_type) of
|
||||
{ok, error} -> error;
|
||||
{ok, progress} -> progress;
|
||||
{ok, all} -> all;
|
||||
{ok, Bad} -> exit ({bad_config, {sasl, {errlog_type, Bad}}});
|
||||
_ -> 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
|
||||
%%%
|
||||
|
||||
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])};
|
||||
{atomic, exists} ->
|
||||
String = io_lib:format("User ~s@~s already registered at node ~p",
|
||||
[User, Host, node()]),
|
||||
{exists, String};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't register user ~s@~s at node ~p: ~p",
|
||||
[User, Host, node(), Reason]),
|
||||
{cannot_register, String}
|
||||
end.
|
||||
|
||||
unregister(User, Host) ->
|
||||
ejabberd_auth:remove_user(User, Host),
|
||||
{ok, ""}.
|
||||
|
||||
registered_users(Host) ->
|
||||
Users = ejabberd_auth:get_vh_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
|
||||
%%%
|
||||
|
||||
import_file(Path) ->
|
||||
case jd2ejd:import_file(Path) of
|
||||
ok ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't import jabberd14 spool file ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_import_file, String}
|
||||
end.
|
||||
|
||||
import_dir(Path) ->
|
||||
case jd2ejd:import_dir(Path) of
|
||||
ok ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't import jabberd14 spool dir ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_import_dir, String}
|
||||
end.
|
||||
|
||||
|
||||
%%%
|
||||
%%% Purge DB
|
||||
%%%
|
||||
|
||||
delete_expired_messages() ->
|
||||
mod_offline:remove_expired_messages(),
|
||||
ok.
|
||||
|
||||
delete_old_messages(Days) ->
|
||||
mod_offline:remove_old_messages(Days),
|
||||
ok.
|
||||
|
||||
|
||||
%%%
|
||||
%%% 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 ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't store backup in ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_backup, String}
|
||||
end.
|
||||
|
||||
restore_mnesia(Path) ->
|
||||
case ejabberd_admin:restore(Path) of
|
||||
{atomic, _} ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't restore backup from ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_restore, String};
|
||||
{aborted,{no_exists,Table}} ->
|
||||
String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.",
|
||||
[filename:absname(Path), node(), Table]),
|
||||
{table_not_exists, String};
|
||||
{aborted,enoent} ->
|
||||
String = io_lib:format("Can't restore backup from ~p at node ~p: File not found.",
|
||||
[filename:absname(Path), node()]),
|
||||
{file_not_found, String}
|
||||
end.
|
||||
|
||||
%% Mnesia database restore
|
||||
%% This function is called from ejabberd_ctl, ejabberd_web_admin and
|
||||
%% mod_configure/adhoc
|
||||
restore(Path) ->
|
||||
mnesia:restore(Path, [{keep_tables,keep_tables()},
|
||||
{default_op, skip_tables}]).
|
||||
|
||||
%% This function return a list of tables that should be kept from a previous
|
||||
%% version backup.
|
||||
%% 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,
|
||||
keep_modules_tables()]).
|
||||
|
||||
%% Returns the list of modules tables in use, according to the list of actually
|
||||
%% loaded modules
|
||||
keep_modules_tables() ->
|
||||
lists:map(fun(Module) -> module_tables(Module) end,
|
||||
gen_mod:loaded_modules(?MYNAME)).
|
||||
|
||||
%% TODO: This mapping should probably be moved to a callback function in each
|
||||
%% module.
|
||||
%% Mapping between modules and their tables
|
||||
module_tables(mod_announce) -> [motd, motd_users];
|
||||
module_tables(mod_irc) -> [irc_custom];
|
||||
module_tables(mod_last) -> [last_activity];
|
||||
module_tables(mod_muc) -> [muc_room, muc_registered];
|
||||
module_tables(mod_offline) -> [offline_msg];
|
||||
module_tables(mod_privacy) -> [privacy];
|
||||
module_tables(mod_private) -> [private_storage];
|
||||
module_tables(mod_pubsub) -> [pubsub_node];
|
||||
module_tables(mod_roster) -> [roster];
|
||||
module_tables(mod_shared_roster) -> [sr_group, sr_user];
|
||||
module_tables(mod_vcard) -> [vcard, vcard_search];
|
||||
module_tables(_Other) -> [].
|
||||
|
||||
get_local_tables() ->
|
||||
Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
|
||||
Tabs = lists:filter(
|
||||
fun(T) ->
|
||||
case mnesia:table_info(T, storage_type) of
|
||||
disc_copies -> true;
|
||||
disc_only_copies -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, Tabs1),
|
||||
Tabs.
|
||||
|
||||
dump_mnesia(Path) ->
|
||||
Tabs = get_local_tables(),
|
||||
dump_tables(Path, Tabs).
|
||||
|
||||
dump_table(Path, STable) ->
|
||||
Table = list_to_atom(STable),
|
||||
dump_tables(Path, [Table]).
|
||||
|
||||
dump_tables(Path, Tables) ->
|
||||
case dump_to_textfile(Path, Tables) of
|
||||
ok ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't store dump in ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_dump, String}
|
||||
end.
|
||||
|
||||
dump_to_textfile(File) ->
|
||||
Tabs = get_local_tables(),
|
||||
dump_to_textfile(File, Tabs).
|
||||
|
||||
dump_to_textfile(File, Tabs) ->
|
||||
dump_to_textfile(mnesia:system_info(is_running), Tabs, file:open(File, [write])).
|
||||
dump_to_textfile(yes, Tabs, {ok, F}) ->
|
||||
Defs = lists:map(
|
||||
fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
|
||||
{attributes, mnesia:table_info(T, attributes)}]}
|
||||
end,
|
||||
Tabs),
|
||||
io:format(F, "~p.~n", [{tables, Defs}]),
|
||||
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
|
||||
file:close(F);
|
||||
dump_to_textfile(_, _, {ok, F}) ->
|
||||
file:close(F),
|
||||
{error, mnesia_not_running};
|
||||
dump_to_textfile(_, _, {error, Reason}) ->
|
||||
{error, Reason}.
|
||||
|
||||
dump_tab(F, T) ->
|
||||
W = mnesia:table_info(T, wild_pattern),
|
||||
{atomic,All} = mnesia:transaction(
|
||||
fun() -> mnesia:match_object(T, W, read) end),
|
||||
lists:foreach(
|
||||
fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
|
||||
|
||||
load_mnesia(Path) ->
|
||||
case mnesia:load_textfile(Path) of
|
||||
{atomic, ok} ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't load dump in ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_load, String}
|
||||
end.
|
||||
|
||||
install_fallback_mnesia(Path) ->
|
||||
case mnesia:install_fallback(Path) of
|
||||
ok ->
|
||||
{ok, ""};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't install fallback from ~p at node ~p: ~p",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
{cannot_fallback, String}
|
||||
end.
|
||||
|
||||
mnesia_change_nodename(FromString, ToString, Source, Target) ->
|
||||
From = list_to_atom(FromString),
|
||||
To = list_to_atom(ToString),
|
||||
Switch =
|
||||
fun
|
||||
(Node) when Node == From ->
|
||||
io:format(" - Replacing nodename: '~p' with: '~p'~n", [From, To]),
|
||||
To;
|
||||
(Node) when Node == To ->
|
||||
%% throw({error, already_exists});
|
||||
io:format(" - Node: '~p' will not be modified (it is already '~p')~n", [Node, To]),
|
||||
Node;
|
||||
(Node) ->
|
||||
io:format(" - Node: '~p' will not be modified (it is not '~p')~n", [Node, From]),
|
||||
Node
|
||||
end,
|
||||
Convert =
|
||||
fun
|
||||
({schema, db_nodes, Nodes}, Acc) ->
|
||||
io:format(" +++ db_nodes ~p~n", [Nodes]),
|
||||
{[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc};
|
||||
({schema, version, Version}, Acc) ->
|
||||
io:format(" +++ version: ~p~n", [Version]),
|
||||
{[{schema, version, Version}], Acc};
|
||||
({schema, cookie, Cookie}, Acc) ->
|
||||
io:format(" +++ cookie: ~p~n", [Cookie]),
|
||||
{[{schema, cookie, Cookie}], Acc};
|
||||
({schema, Tab, CreateList}, Acc) ->
|
||||
io:format("~n * Checking table: '~p'~n", [Tab]),
|
||||
Keys = [ram_copies, disc_copies, disc_only_copies],
|
||||
OptSwitch =
|
||||
fun({Key, Val}) ->
|
||||
case lists:member(Key, Keys) of
|
||||
true ->
|
||||
io:format(" + Checking key: '~p'~n", [Key]),
|
||||
{Key, lists:map(Switch, Val)};
|
||||
false-> {Key, Val}
|
||||
end
|
||||
end,
|
||||
Res = {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc},
|
||||
Res;
|
||||
(Other, Acc) ->
|
||||
{[Other], Acc}
|
||||
end,
|
||||
mnesia:traverse_backup(Source, Target, Convert, switched).
|
||||
@@ -1,103 +1,49 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_app.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : ejabberd's application callback module
|
||||
%%% Created : 31 Jan 2003 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
|
||||
%%%
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_app).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-export([start_modules/0,start/2, get_log_path/0, prep_stop/1, stop/1, init/0]).
|
||||
-export([start/2, stop/1, init/0]).
|
||||
|
||||
-export([dump_ports/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
|
||||
%%%
|
||||
%%% Application API
|
||||
%%%
|
||||
|
||||
start(normal, _Args) ->
|
||||
ejabberd_loglevel:set(4),
|
||||
write_pid_file(),
|
||||
application:start(sasl),
|
||||
application:start(exmpp),
|
||||
randoms:start(),
|
||||
db_init(),
|
||||
sha:start(),
|
||||
start(),
|
||||
catch ssl:start(),
|
||||
translate:start(),
|
||||
acl:start(),
|
||||
ejabberd_ctl:init(),
|
||||
ejabberd_commands:init(),
|
||||
ejabberd_admin:start(),
|
||||
gen_mod:start(),
|
||||
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(),
|
||||
cyrsasl:start(),
|
||||
% Profiling
|
||||
%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()]),
|
||||
%eprof:start(),
|
||||
%eprof:profile([self()]),
|
||||
%fprof:trace(start, "/tmp/fprof"),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
start(),
|
||||
load_modules(),
|
||||
Sup;
|
||||
start(_, _) ->
|
||||
{error, badarg}.
|
||||
|
||||
%% Prepare the application for termination.
|
||||
%% This function is called when an application is about to be stopped,
|
||||
%% before shutting down the processes of the application.
|
||||
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(),
|
||||
stop(_StartArgs) ->
|
||||
ok.
|
||||
|
||||
|
||||
%%%
|
||||
%%% Internal functions
|
||||
%%%
|
||||
|
||||
start() ->
|
||||
spawn_link(?MODULE, init, []).
|
||||
|
||||
@@ -105,10 +51,30 @@ init() ->
|
||||
register(ejabberd, self()),
|
||||
%erlang:system_flag(fullsweep_after, 0),
|
||||
%error_logger:logfile({open, ?LOG_PATH}),
|
||||
LogPath = get_log_path(),
|
||||
LogPath =
|
||||
case application:get_env(log_path) of
|
||||
{ok, Path} ->
|
||||
Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
?LOG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
end,
|
||||
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
|
||||
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
ok.
|
||||
%timer:apply_interval(3600000, ?MODULE, dump_ports, []),
|
||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl),
|
||||
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
|
||||
@@ -117,132 +83,21 @@ db_init() ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
application:start(mnesia, permanent),
|
||||
mnesia:start(),
|
||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||
|
||||
%% Start all the modules in all the hosts
|
||||
start_modules() ->
|
||||
case ejabberd_config:get_local_option({static_modules, global}) of
|
||||
load_modules() ->
|
||||
case ejabberd_config:get_local_option(modules) 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
|
||||
undefined ->
|
||||
ok;
|
||||
Modules ->
|
||||
lists:foreach(
|
||||
fun({Module, Args}) ->
|
||||
gen_mod:start_module(Host, Module, Args)
|
||||
end, Modules)
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
|
||||
%% Stop all the modules in all the hosts
|
||||
stop_modules() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
case ejabberd_config:get_local_option({modules, Host}) of
|
||||
undefined ->
|
||||
ok;
|
||||
Modules ->
|
||||
lists:foreach(
|
||||
fun({Module, _Args}) ->
|
||||
gen_mod:stop_module_keep_config(Host, Module)
|
||||
end, Modules)
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
|
||||
connect_nodes() ->
|
||||
case ejabberd_config:get_local_option(cluster_nodes) of
|
||||
undefined ->
|
||||
ok;
|
||||
Nodes when is_list(Nodes) ->
|
||||
lists:foreach(fun(Node) ->
|
||||
net_kernel:connect_node(Node)
|
||||
end, Nodes)
|
||||
end.
|
||||
|
||||
%% @spec () -> string()
|
||||
%% @doc Returns the full path to the ejabberd log file.
|
||||
%% It first checks for application configuration parameter 'log_path'.
|
||||
%% If not defined it checks the environment variable EJABBERD_LOG_PATH.
|
||||
%% And if that one is neither defined, returns the default value:
|
||||
%% "ejabberd.log" in current directory.
|
||||
get_log_path() ->
|
||||
case application:get_env(log_path) of
|
||||
{ok, Path} ->
|
||||
Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
?LOG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
Modules ->
|
||||
lists:foreach(fun({Module, Args}) ->
|
||||
gen_mod:start_module(Module, Args)
|
||||
end, Modules)
|
||||
end.
|
||||
|
||||
|
||||
%% If ejabberd is running on some Windows machine, get nameservers and add to Erlang
|
||||
maybe_add_nameservers() ->
|
||||
case os:type() of
|
||||
{win32, _} -> add_windows_nameservers();
|
||||
_ -> ok
|
||||
end.
|
||||
dump_ports() ->
|
||||
?INFO_MSG("ports:~n ~p",
|
||||
[lists:map(fun(P) -> erlang:port_info(P) end, erlang:ports())]).
|
||||
|
||||
add_windows_nameservers() ->
|
||||
IPTs = win32_dns:get_nameservers(),
|
||||
?INFO_MSG("Adding machine's DNS IPs to Erlang system:~n~p", [IPTs]),
|
||||
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
|
||||
%%%
|
||||
|
||||
write_pid_file() ->
|
||||
case ejabberd:get_pid_file() of
|
||||
false ->
|
||||
ok;
|
||||
PidFilename ->
|
||||
write_pid_file(os:getpid(), PidFilename)
|
||||
end.
|
||||
|
||||
write_pid_file(Pid, PidFilename) ->
|
||||
case file:open(PidFilename, [write]) of
|
||||
{ok, Fd} ->
|
||||
io:format(Fd, "~s~n", [Pid]),
|
||||
file:close(Fd);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Cannot write PID file ~s~nReason: ~p", [PidFilename, Reason]),
|
||||
throw({cannot_write_pid_file, PidFilename, Reason})
|
||||
end.
|
||||
|
||||
delete_pid_file() ->
|
||||
case ejabberd:get_pid_file() of
|
||||
false ->
|
||||
ok;
|
||||
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.
|
||||
|
||||
@@ -1,531 +1,359 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Authentification
|
||||
%%% Created : 23 Nov 2002 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
|
||||
%%%
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% TODO: Use the functions in ejabberd auth to add and remove users.
|
||||
|
||||
-module(ejabberd_auth).
|
||||
-author('alexey@process-one.net').
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
check_password_with_authmodule/3,
|
||||
check_password_with_authmodule/5,
|
||||
try_register/3,
|
||||
-export([start/0, start_link/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
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,
|
||||
get_password_with_authmodule/2,
|
||||
is_user_exists/2,
|
||||
is_user_exists_in_other_modules/3,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/1,
|
||||
store_type/1,
|
||||
entropy/1
|
||||
plain_password_required/0,
|
||||
check_password_ldap/2, % TODO: remove
|
||||
is_user_exists_ldap/1 % TODO: remove
|
||||
]).
|
||||
|
||||
-export([start/1
|
||||
,start_modules/2
|
||||
,stop_modules/2
|
||||
,start_methods/2
|
||||
,stop_methods/2
|
||||
]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
code_change/3,
|
||||
handle_info/2,
|
||||
terminate/2]).
|
||||
|
||||
-export([auth_modules/1]).
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-record(state, {}).
|
||||
|
||||
%% @type authmodule() = ejabberd_auth_anonymous | ejabberd_auth_external |
|
||||
%% ejabberd_auth_ldap | ejabberd_auth_pam |
|
||||
%% ejabberd_auth_storage | atom().
|
||||
-record(passwd, {user, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec () -> term()
|
||||
|
||||
start() ->
|
||||
?DEBUG("About to start auth modules. Hosts: ~p", [?MYHOSTS]),
|
||||
lists:foreach(fun start/1, ?MYHOSTS).
|
||||
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
|
||||
start(Host) ->
|
||||
start_modules(Host, auth_modules(Host)).
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_server
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
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) ->
|
||||
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) ->
|
||||
case check_password_with_authmodule(User, Server, Password) of
|
||||
{true, _AuthModule} -> true;
|
||||
{false, _Reason} -> 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) ->
|
||||
case check_password_with_authmodule(User, Server, Password,
|
||||
Digest, DigestGen) of
|
||||
{true, _AuthModule} -> true;
|
||||
{false, _Reason} -> 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, 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_loop(auth_modules(Server), [User, Server, Password,
|
||||
Digest, DigestGen], "").
|
||||
|
||||
check_password_loop([], _Args, LastReason) ->
|
||||
{false, LastReason};
|
||||
check_password_loop([AuthModule | AuthModules], Args, PreviousReason) ->
|
||||
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)
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | {error, ErrorType}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Password = string()
|
||||
%% 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) ->
|
||||
lists:foldl(
|
||||
fun(M, {error, _}) ->
|
||||
M:set_password(User, Server, Password);
|
||||
(_M, Res) ->
|
||||
Res
|
||||
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
|
||||
true ->
|
||||
{atomic, exists};
|
||||
false ->
|
||||
case ?IS_MY_HOST(exmpp_stringprep:nameprep(Server)) 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);
|
||||
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.
|
||||
|
||||
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) ->
|
||||
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) ->
|
||||
lists:flatmap(
|
||||
fun(M) ->
|
||||
case erlang:function_exported(
|
||||
M, get_vh_registered_users, 2) of
|
||||
true ->
|
||||
M:get_vh_registered_users(Server, Opts);
|
||||
false ->
|
||||
M:get_vh_registered_users(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) ->
|
||||
lists:sum(
|
||||
lists:map(
|
||||
fun(M) ->
|
||||
case erlang:function_exported(
|
||||
M, get_vh_registered_users_number, 1) of
|
||||
true ->
|
||||
M:get_vh_registered_users_number(Server);
|
||||
false ->
|
||||
length(M:get_vh_registered_users(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) ->
|
||||
lists:sum(
|
||||
lists:map(
|
||||
fun(M) ->
|
||||
case erlang:function_exported(
|
||||
M, get_vh_registered_users_number, 2) of
|
||||
true ->
|
||||
M:get_vh_registered_users_number(Server, Opts);
|
||||
false ->
|
||||
length(M:get_vh_registered_users(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) ->
|
||||
lists:foldl(
|
||||
fun(M, false) ->
|
||||
M:get_password(User, Server);
|
||||
(_M, Password) ->
|
||||
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) ->
|
||||
case get_password(User, Server) of
|
||||
false ->
|
||||
"";
|
||||
Password when is_list(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) ->
|
||||
lists:foldl(
|
||||
fun(M, {false, _}) ->
|
||||
{M:get_password(User, Server), M};
|
||||
(_M, {Password, AuthModule}) ->
|
||||
{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) ->
|
||||
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
|
||||
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) ->
|
||||
is_user_exists_in_other_modules_loop(
|
||||
auth_modules(Server)--[Module],
|
||||
User, Server).
|
||||
is_user_exists_in_other_modules_loop([], _User, _Server) ->
|
||||
false;
|
||||
is_user_exists_in_other_modules_loop([AuthModule|AuthModules], User, Server) ->
|
||||
case AuthModule:is_user_exists(User, Server) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
is_user_exists_in_other_modules_loop(AuthModules, User, Server);
|
||||
{error, Error} ->
|
||||
?DEBUG("The authentication module ~p returned an error~nwhen "
|
||||
"checking user ~p in server ~p~nError message: ~p",
|
||||
[AuthModule, User, Server, Error]),
|
||||
maybe
|
||||
end.
|
||||
|
||||
|
||||
%% @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) ->
|
||||
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)]),
|
||||
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) ->
|
||||
R = lists:foldl(
|
||||
fun(_M, ok = Res) ->
|
||||
Res;
|
||||
(M, _) ->
|
||||
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)]);
|
||||
_ -> none
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
mnesia:create_table(passwd,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
case auth_method() of
|
||||
internal ->
|
||||
ok;
|
||||
ldap ->
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, "", "")
|
||||
end,
|
||||
R.
|
||||
{ok, #state{}}.
|
||||
|
||||
%% @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.
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_call/3
|
||||
%% Returns: {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} | (terminate/2 is called)
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_cast/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/2
|
||||
%% Purpose: Shutdown the server
|
||||
%% Returns: any (ignored by gen_server)
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec () -> [authmodule()]
|
||||
%% @doc Return the lists of all the auth modules actually used in the
|
||||
%% configuration.
|
||||
auth_method() ->
|
||||
case ejabberd_config:get_local_option(auth_method) of
|
||||
ldap ->
|
||||
ldap;
|
||||
_ ->
|
||||
internal
|
||||
end.
|
||||
|
||||
auth_modules() ->
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Server) ->
|
||||
auth_modules(Server)
|
||||
end, ?MYHOSTS)).
|
||||
plain_password_required() ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
false;
|
||||
ldap ->
|
||||
true
|
||||
end.
|
||||
|
||||
|
||||
check_password(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Password}] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password, StreamID, Digest);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password, StreamID, Digest)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
set_password(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
F = fun() ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
password = Password})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
try_register_internal(User, Password);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
try_register_internal(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
[] ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
password = Password}),
|
||||
ok;
|
||||
[_E] ->
|
||||
exists
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_password(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_password_s(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_user_exists(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
is_user_exists_internal(User);
|
||||
ldap ->
|
||||
is_user_exists_ldap(User)
|
||||
end.
|
||||
|
||||
is_user_exists_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
remove_user_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, LUser})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
catch mod_roster:remove_user(User),
|
||||
catch mod_offline:remove_user(User),
|
||||
catch mod_last:remove_user(User),
|
||||
catch mod_vcard:remove_user(User),
|
||||
catch mod_private:remove_user(User).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User, Password);
|
||||
ldap ->
|
||||
not_allowed
|
||||
end.
|
||||
|
||||
remove_user_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
[#passwd{password = Password}] ->
|
||||
mnesia:delete({passwd, LUser}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
catch mod_roster:remove_user(User),
|
||||
catch mod_offline:remove_user(User),
|
||||
catch mod_last:remove_user(User),
|
||||
catch mod_vcard:remove_user(User),
|
||||
catch mod_private:remove_user(User),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
check_password_ldap(User, Password, StreamID, Digest) ->
|
||||
check_password_ldap(User, Password).
|
||||
|
||||
check_password_ldap(User, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind("ejabberd", DN, Password) of
|
||||
ok ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists_ldap(User) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
_DN ->
|
||||
true
|
||||
end.
|
||||
|
||||
find_user_dn(User) ->
|
||||
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
Filter = eldap:equalityMatch(Attr, User),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("ejabberd", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = [E | _]} ->
|
||||
E#eldap_entry.object_name;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @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)}),
|
||||
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)).
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_anonymous.erl
|
||||
%%% Author : Mickael Remond <mickael.remond@process-one.net>
|
||||
%%% Purpose : Anonymous feature support in ejabberd
|
||||
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@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(ejabberd_auth_anonymous).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
allow_anonymous/1,
|
||||
is_sasl_anonymous_enabled/1,
|
||||
is_login_anonymous_enabled/1,
|
||||
anonymous_user_exist/2,
|
||||
allow_multiple_connections/1,
|
||||
register_connection/3,
|
||||
unregister_connection/3
|
||||
]).
|
||||
|
||||
|
||||
%% Function used by ejabberd_auth:
|
||||
-export([login/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password/3,
|
||||
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").
|
||||
-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),
|
||||
%% 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),
|
||||
%% The hooks are needed to add / remove users from the anonymous tables
|
||||
ejabberd_hooks:add(sm_register_connection_hook, HostB,
|
||||
?MODULE, register_connection, 100),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, HostB,
|
||||
?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) ->
|
||||
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) ->
|
||||
case allow_anonymous(Host) of
|
||||
false -> false;
|
||||
true ->
|
||||
case anonymous_protocol(Host) of
|
||||
sasl_anon -> true;
|
||||
both -> true;
|
||||
_Other -> false
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (Host) -> bool()
|
||||
%% Host = string()
|
||||
%% @doc 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) ->
|
||||
case allow_anonymous(Host) of
|
||||
false -> false;
|
||||
true ->
|
||||
case anonymous_protocol(Host) of
|
||||
login_anon -> 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.
|
||||
%% defaults to login_anon
|
||||
|
||||
anonymous_protocol(Host) when is_list(Host) ->
|
||||
case ejabberd_config:get_local_option({anonymous_protocol, Host}) of
|
||||
sasl_anon -> sasl_anon;
|
||||
login_anon -> login_anon;
|
||||
both -> both;
|
||||
_Other -> sasl_anon
|
||||
end.
|
||||
|
||||
%% @spec (Host) -> bool()
|
||||
%% Host = string()
|
||||
%% @doc Return true if multiple connections have been allowed in the
|
||||
%% config file.
|
||||
%% defaults to false
|
||||
|
||||
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),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({anonymous, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_H|_T] ->
|
||||
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) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete_object({anonymous, US, SID})
|
||||
end,
|
||||
mnesia:async_dirty(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]),
|
||||
US = {LUser, LServer},
|
||||
mnesia:async_dirty(
|
||||
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
||||
end);
|
||||
_ ->
|
||||
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),
|
||||
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.
|
||||
|
||||
purge_hook(false, _LUser, _LServer) ->
|
||||
ok;
|
||||
purge_hook(true, LUser, LServer) when is_binary(LUser), is_binary(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.
|
||||
|
||||
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,
|
||||
User, Server) of
|
||||
%% If user exists in other module, reject anonnymous authentication
|
||||
true -> false;
|
||||
%% If we are not sure whether the user exists in other module, reject anon auth
|
||||
maybe -> false;
|
||||
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;
|
||||
true ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
%% Reject the login if an anonymous user with the same login
|
||||
%% is already logged and if multiple login has not been enable
|
||||
%% in the config file.
|
||||
true -> allow_multiple_connections(Server);
|
||||
%% Accept login and add user to the anonymous table
|
||||
false -> true
|
||||
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.
|
||||
|
||||
set_password(User, Server, _Password) ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
{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:
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
%% @spec () -> nil()
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
%% @spec (Server) -> nil()
|
||||
%% Server = string()
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
true ->
|
||||
DefaultValue;
|
||||
%% We return the permanent user password otherwise
|
||||
false ->
|
||||
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.
|
||||
|
||||
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.
|
||||
@@ -1,404 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_external.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Authentification via LDAP external script
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_external).
|
||||
-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").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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()
|
||||
|
||||
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()
|
||||
|
||||
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;
|
||||
_ -> {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.
|
||||
|
||||
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, Data) ->
|
||||
case check_cache_last_options(Server) of
|
||||
cache ->
|
||||
ejabberd_auth_storage:get_vh_registered_users(Server, Data);
|
||||
no_cache ->
|
||||
[]
|
||||
end.
|
||||
|
||||
%% @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.
|
||||
|
||||
%% @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
|
||||
catch
|
||||
_:Error -> {error, Error}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> {error, not_allowed}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
|
||||
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.
|
||||
|
||||
%% @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})).
|
||||
@@ -1,597 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_ldap.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Authentification via LDAP
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_ldap).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_info/2,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
terminate/2,
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
start_link/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_number/1,
|
||||
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").
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
-record(state, {host,
|
||||
eldap_id,
|
||||
bind_eldap_id,
|
||||
servers,
|
||||
backups,
|
||||
port,
|
||||
tls_options,
|
||||
dn,
|
||||
password,
|
||||
base,
|
||||
uids,
|
||||
ufilter,
|
||||
lfilter, %% Local filter (performed by ejabberd, not LDAP)
|
||||
deref_aliases,
|
||||
dn_filter,
|
||||
dn_filter_attrs
|
||||
}).
|
||||
|
||||
%% Unused callbacks.
|
||||
handle_cast(_Request, State) ->
|
||||
{noreply, State}.
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
%% -----
|
||||
|
||||
|
||||
-define(LDAP_SEARCH_TIMEOUT, 5). % Timeout for LDAP search queries in seconds
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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()
|
||||
|
||||
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()
|
||||
|
||||
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,
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.tls_options),
|
||||
eldap_pool:start_link(State#state.bind_eldap_id,
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.tls_options),
|
||||
{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
|
||||
%% we simply prevent the use of LDAP anonymous authentication.
|
||||
if Password == "" ->
|
||||
false;
|
||||
true ->
|
||||
case catch check_password_ldap(User, Server, Password) of
|
||||
{'EXIT', _} -> false;
|
||||
Result -> Result
|
||||
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) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
%% @spec () -> [{LUser, LServer}]
|
||||
%% LUser = string()
|
||||
%% LServer = string()
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
Servers = ejabberd_config:get_vh_by_auth_method(ldap),
|
||||
lists:flatmap(
|
||||
fun(Server) ->
|
||||
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} ->
|
||||
{error, Error};
|
||||
Result ->
|
||||
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
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap_pool:bind(State#state.bind_eldap_id, DN, Password) of
|
||||
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()
|
||||
|
||||
get_vh_registered_users_ldap(Server) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
UIDs = eldap_utils:uids_domain_subst(Server, State#state.uids),
|
||||
Eldap_ID = State#state.eldap_id,
|
||||
SearchFilter = build_sfilter(State, UIDs),
|
||||
ResAttrs = result_attrs(State),
|
||||
case eldap_filter:parse(SearchFilter) 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
|
||||
#eldap_search_result{entries = Entries} ->
|
||||
lists:flatmap(
|
||||
fun(#eldap_entry{attributes = Attrs,
|
||||
object_name = DN}) ->
|
||||
case is_valid_dn(DN, Server, Attrs, State) of
|
||||
false -> [];
|
||||
_ ->
|
||||
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
|
||||
"" -> [];
|
||||
{User, UIDFormat} ->
|
||||
case eldap_utils:get_user_part(User, UIDFormat) of
|
||||
{ok, U} ->
|
||||
try
|
||||
[{exmpp_stringprep:nodeprep(U), exmpp_stringprep:nameprep(Server)}]
|
||||
catch
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
end
|
||||
end, Entries);
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
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
|
||||
false -> false;
|
||||
_DN -> true
|
||||
end.
|
||||
|
||||
handle_call(get_state, _From, State) ->
|
||||
{reply, {ok, State}, State};
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, 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
|
||||
{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
|
||||
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
|
||||
object_name = DN} | _]} ->
|
||||
dn_filter(DN, Server, Attrs, State);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% apply the dn filter and the local filter:
|
||||
dn_filter(DN, Server, 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)
|
||||
end.
|
||||
|
||||
%% Check that the DN is valid, based on the dn filter
|
||||
is_valid_dn(DN, _, _, #state{dn_filter = undefined}) ->
|
||||
DN;
|
||||
|
||||
is_valid_dn(DN, Server, Attrs, State) ->
|
||||
DNAttrs = State#state.dn_filter_attrs,
|
||||
UIDs = eldap_utils:uids_domain_subst(Server, 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;
|
||||
{S, UAF} ->
|
||||
case eldap_utils:get_user_part(S, UAF) of
|
||||
{ok, U} -> [{"%u", U} | Values];
|
||||
_ -> Values
|
||||
end
|
||||
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
|
||||
#eldap_search_result{entries = [_|_]} ->
|
||||
DN;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% The local filter is used to check an attribute in ejabberd
|
||||
%% and not in LDAP to limit the load on the LDAP directory.
|
||||
%% A local rule can be either:
|
||||
%% {equal, {"accountStatus",["active"]}}
|
||||
%% {notequal, {"accountStatus",["disabled"]}}
|
||||
%% {ldap_local_filter, {notequal, {"accountStatus",["disabled"]}}}
|
||||
check_local_filter(_Attrs, #state{lfilter = undefined}) ->
|
||||
true;
|
||||
check_local_filter(Attrs, #state{lfilter = LocalFilter}) ->
|
||||
{Operation, FilterMatch} = LocalFilter,
|
||||
local_filter(Operation, Attrs, FilterMatch).
|
||||
|
||||
local_filter(equal, Attrs, FilterMatch) ->
|
||||
{Attr, Value} = FilterMatch,
|
||||
case lists:keysearch(Attr, 1, Attrs) of
|
||||
false -> false;
|
||||
{value,{Attr,Value}} -> true;
|
||||
_ -> false
|
||||
end;
|
||||
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
|
||||
%%%----------------------------------------------------------------------
|
||||
parse_options(Host) ->
|
||||
Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||
Bind_Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||
LDAPServers = ejabberd_config:get_local_option({ldap_servers, Host}),
|
||||
LDAPBackups = case ejabberd_config:get_local_option({ldap_backups, Host}) of
|
||||
undefined -> [];
|
||||
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;
|
||||
starttls -> ?LDAP_PORT;
|
||||
_ -> ?LDAP_PORT
|
||||
end;
|
||||
P -> P
|
||||
end,
|
||||
RootDN = case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
|
||||
undefined -> "";
|
||||
RDN -> RDN
|
||||
end,
|
||||
Password = case ejabberd_config:get_local_option({ldap_password, Host}) of
|
||||
undefined -> "";
|
||||
Pass -> Pass
|
||||
end,
|
||||
UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
|
||||
undefined -> [{"uid", "%u"}];
|
||||
UI -> eldap_utils:uids_domain_subst(Host, UI)
|
||||
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 ++ ")"
|
||||
end,
|
||||
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}
|
||||
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,
|
||||
#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}],
|
||||
dn = RootDN,
|
||||
password = Password,
|
||||
base = LDAPBase,
|
||||
uids = UIDs,
|
||||
ufilter = UserFilter,
|
||||
lfilter = LocalFilter,
|
||||
deref_aliases = DerefAliases,
|
||||
dn_filter = DNFilter,
|
||||
dn_filter_attrs = DNFilterAttrs
|
||||
}.
|
||||
@@ -1,194 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_pam.erl
|
||||
%%% Author : Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%% Purpose : PAM authentication
|
||||
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%%
|
||||
%%%
|
||||
%%% 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(ejabberd_auth_pam).
|
||||
-author('xram@jabber.ru').
|
||||
|
||||
%% 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_password/2,
|
||||
get_password_s/2,
|
||||
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;
|
||||
{error,{already_started, _}} -> ok;
|
||||
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).
|
||||
|
||||
%% @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
|
||||
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) ->
|
||||
[].
|
||||
|
||||
%% @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
|
||||
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
|
||||
undefined -> "ejabberd";
|
||||
Service -> Service
|
||||
end.
|
||||
get_pam_userinfotype(Host) ->
|
||||
case ejabberd_config:get_local_option({pam_userinfotype, Host}) of
|
||||
undefined -> username;
|
||||
Type -> Type
|
||||
end.
|
||||