Compare commits

..

1 Commits

Author SHA1 Message Date
Alexey Shchepin cec11aca59 Added release tags
SVN Revision: 306
2005-04-17 00:21:31 +00:00
193 changed files with 11694 additions and 45760 deletions
-3
View File
@@ -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
+6 -2076
View File
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -1,13 +1,20 @@
Win32 build: Make it possible to compile with +debug_info flag.
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
-21
View File
@@ -1,21 +0,0 @@
extract_translations - auxiliary tool that extracts lines to be translated
from ejabberd source tree.
Building:
erlc extract_translations.erl
Invoking 1:
erl -noinput -s extract_translations -extra dirname message_file
where dirname is the directory "src" in ejabberd's source tree root,
message_file is a file with translated messages (src/msgs/*.msg).
Result is a list of messages from source files which aren't contained in
message file.
Invoking 2:
erl -noinput -s extract_translations -extra -unused dirname message_file
Result is a list of messages from message file which aren't in source
files anymore.
@@ -1,190 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : extract_translations.erl
%%% Author : Sergei Golovan <sgolovan@nes.ru>
%%% Purpose : Auxiliary tool for interface/messages translators
%%% Created : 23 Apr 2005 by Sergei Golovan <sgolovan@nes.ru>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(extract_translations).
-author('sgolovan@nes.ru').
-export([start/0]).
-define(STATUS_SUCCESS, 0).
-define(STATUS_ERROR, 1).
-define(STATUS_USAGE, 2).
-include_lib("kernel/include/file.hrl").
start() ->
ets:new(translations, [named_table, public]),
ets:new(files, [named_table, public]),
ets:new(vars, [named_table, public]),
case init:get_plain_arguments() of
["-unused", Dir, File] ->
Status = process(Dir, File, unused),
halt(Status);
[Dir, File] ->
Status = process(Dir, File, used),
halt(Status);
_ ->
print_usage(),
halt(?STATUS_USAGE)
end.
process(Dir, File, Used) ->
case load_file(File) of
{error, Reason} ->
io:format("~s: ~s~n", [File, file:format_error(Reason)]),
?STATUS_ERROR;
_ ->
FileList = find_src_files(Dir),
lists:foreach(
fun(F) ->
parse_file(Dir, F, Used)
end, FileList),
case Used of
unused ->
ets:foldl(fun({Key, _}, _) ->
io:format("~p~n", [Key])
end, ok, translations);
_ ->
ok
end,
?STATUS_SUCCESS
end.
parse_file(Dir, File, Used) ->
ets:delete_all_objects(vars),
case epp:parse_file(File, [Dir, filename:dirname(File)], []) of
{ok, Forms} ->
lists:foreach(
fun(F) ->
parse_form(Dir, File, F, Used)
end, Forms);
_ ->
ok
end.
parse_form(Dir, File, Form, Used) ->
case Form of
{call,
_,
{remote, _, {atom, _, translate}, {atom, _, translate}},
[_, {string, _, Str}]
} ->
process_string(Dir, File, Str, Used);
{call,
_,
{remote, _, {atom, _, translate}, {atom, _, translate}},
[_, {var, _, Name}]
} ->
case ets:lookup(vars, Name) of
[{_Name, Value}] ->
process_string(Dir, File, Value, Used);
_ ->
ok
end;
{match,
_,
{var, _, Name},
{string, _, Value}
} ->
ets:insert(vars, {Name, Value});
L when is_list(L) ->
lists:foreach(
fun(F) ->
parse_form(Dir, File, F, Used)
end, L);
T when is_tuple(T) ->
lists:foreach(
fun(F) ->
parse_form(Dir, File, F, Used)
end, tuple_to_list(T));
_ ->
ok
end.
process_string(_Dir, File, Str, Used) ->
case {ets:lookup(translations, Str), Used} of
{[{_Key, _Trans}], unused} ->
ets:delete(translations, Str);
{[{_Key, _Trans}], used} ->
ok;
{_, used} ->
case ets:lookup(files, File) of
[{_}] ->
ok;
_ ->
io:format("~n% ~s~n", [File]),
ets:insert(files, {File})
end,
io:format("{~p, \"\"}.~n", [Str]),
ets:insert(translations, {Str, ""});
_ ->
ok
end.
load_file(File) ->
case file:consult(File) of
{ok, Terms} ->
lists:foreach(
fun({Orig, Trans}) ->
case Trans of
"" ->
ok;
_ ->
ets:insert(translations, {Orig, Trans})
end
end, Terms);
Err ->
Err
end.
find_src_files(Dir) ->
case file:list_dir(Dir) of
{ok, FileList} ->
recurse_filelist(
lists:map(
fun(F) ->
filename:join(Dir, F)
end, FileList));
_ ->
[]
end.
recurse_filelist(FileList) ->
recurse_filelist(FileList, []).
recurse_filelist([], Acc) ->
lists:reverse(Acc);
recurse_filelist([H | T], Acc) ->
case file:read_file_info(H) of
{ok, #file_info{type = directory}} ->
recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc);
{ok, #file_info{type = regular}} ->
case string:substr(H, string:len(H) - 3) of
".erl" ->
recurse_filelist(T, [H | Acc]);
".hrl" ->
recurse_filelist(T, [H | Acc]);
_ ->
recurse_filelist(T, Acc)
end;
_ ->
recurse_filelist(T, Acc)
end.
print_usage() ->
io:format(
"Usage: extract_translations [-unused] dir file~n"
"~n"
"Example:~n"
" extract_translations . ./msgs/ru.msg~n"
).
@@ -1,101 +0,0 @@
#!/bin/bash
# Frontend for ejabberd's extract_translations.erl
# by Badlop
# last updated: 8 December 2005
while [ "$1" != "" ]
do
case "$1" in
-help)
echo "Options:"
echo " -lang LANGUAGE"
echo " -src FULL_PATH_EJABBERD"
echo ""
echo "Example:"
echo " ./prepare-translation.sh -lang es -src /home/admin/ejabberd"
exit 0
;;
-lang)
# This is the language to be extracted
LANGU=$2
shift
shift
;;
-src)
# This is the path to the ejabberd source dir
EJA_DIR=$2
shift
shift
;;
*)
echo "unknown option: '$1 $2'"
shift
shift
;;
esac
done
# Where is Erlang binary
ERL=`which erl`
EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/
EXTRACT_ERL=extract_translations.erl
EXTRACT_BEAM=extract_translations.beam
SRC_DIR=$EJA_DIR/src
MSGS_DIR=$SRC_DIR/msgs
MSGS_FILE=$LANGU.msg
MSGS_FILE2=$LANGU.msg.translate
MSGS_PATH=$MSGS_DIR/$MSGS_FILE
MSGS_PATH2=$MSGS_DIR/$MSGS_FILE2
if !([[ -n $EJA_DIR ]])
then
echo "ejabberd dir does not exist: $EJA_DIR"
fi
if !([[ -x $EXTRACT_BEAM ]])
then
echo -n "Compiling extract_translations.erl: "
sh -c "cd $EXTRACT_DIR; $ERL -compile $EXTRACT_ERL"
fi
echo ""
echo -n "Extracting language strings for '$LANGU':"
echo -n " new..."
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra $SRC_DIR $MSGS_PATH >$MSGS_PATH.new
echo -n " old..."
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra -unused $SRC_DIR $MSGS_PATH >$MSGS_PATH.unused
cat $MSGS_PATH >$MSGS_PATH2
echo "" >>$MSGS_PATH2
cat $MSGS_PATH.new >>$MSGS_PATH2
rm $MSGS_PATH.new
echo "" >>$MSGS_PATH2
cat $MSGS_PATH.unused >>$MSGS_PATH2
rm $MSGS_PATH.unused
echo " ok"
echo ""
echo "Process completed."
echo ""
echo " A new file has been created for you, with the current, the new and the deprecated strings:"
echo " $MSGS_PATH2"
echo ""
echo " At the end of that file you will find the strings you must update:"
echo " - Untranslated strings are like this: {"March", ""}."
echo " To translate the string, add the text inside the commas. Example:"
echo " {"March", "Marzo"}."
echo " - Old strings that are not used: "Woowoa""
echo " Search the entire file for those strings and remove them"
echo ""
echo " Once you have translated all the strings and removed all the old ones,"
echo " rename the file to overwrite the previous one:"
echo " $MSGS_PATH"
-46
View File
@@ -1,46 +0,0 @@
# $Id$
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 change the version number in version.tex"
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
@read -p "press any key to continue"
html: guide.html dev.html features.html
pdf: guide.pdf features.pdf
clean:
rm -f *.aux
rm -f *.haux
rm -f *.html
rm -f *.htoc
rm -f *.idx
rm -f *.ilg
rm -f *.ind
rm -f *.log
rm -f *.out
rm -f *.pdf
rm -f *.toc
guide.html: guide.tex
hevea -fix -noiso -pedantic guide.tex
dev.html: dev.tex
hevea -fix -noiso -pedantic dev.tex
features.html: features.tex
hevea -fix -noiso -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
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

+152 -237
View File
@@ -1,184 +1,97 @@
<!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>
<HEAD>
<TITLE>Ejabberd 1.1.2 Developers Guide</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="hevea 1.08">
<STYLE type="text/css">
.toc{list-style:none;}
.title{margin:auto;text-align:center}
.center{text-align:center;margin-left:auto;margin-right:auto;}
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
DIV TABLE{margin-left:inherit;margin-right:inherit;}
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
.part{margin:auto;text-align:center}
</STYLE>
<META http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
<META name="GENERATOR" content="hevea 1.06">
</HEAD>
<BODY >
<!--HEVEA command line is: hevea -fix -noiso -pedantic dev.tex -->
<!--HEVEA command line is: /usr/bin/hevea -charset ISO8859-1 dev.tex -->
<!--HTMLHEAD-->
<!--ENDHTML-->
<!--PREFIX <ARG ></ARG>-->
<!--CUT DEF section 1 -->
<BR>
<BR>
<A NAME="sec:titlepage"></A>
<TABLE CLASS="title">
<TR><TD>
<H1 CLASS="titlemain">Ejabberd 1.1.2 Developers Guide</H1>
<H3 CLASS="titlerest">Alexey Shchepin<BR>
<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></TD>
</TR></TABLE><BR>
<BR>
<DIV CLASS="center">
<IMG SRC="logo.png" ALT="logo.png">
<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">
<BR>
</DIV><BR>
<BR>
</DIV>
<BLOCKQUOTE CLASS="quotation"><I>I can thoroughly recommend ejabberd for ease of setup &ndash;
Kevin Smith, Current maintainer of the Psi project</I></BLOCKQUOTE>
<!--TOC section Contents-->
<H2 CLASS="section">Contents</H2><!--SEC END -->
<!--TOC section Table of Contents-->
<UL CLASS="toc"><LI CLASS="li-toc">
<H2>Table of Contents</H2><!--SEC END -->
<UL><LI>
<A HREF="#htoc1">1&nbsp;&nbsp;Introduction</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc2">1.1&nbsp;&nbsp;Key Features</A>
<LI CLASS="li-toc"><A HREF="#htoc3">1.2&nbsp;&nbsp;Additional Features</A>
<UL><LI>
<A HREF="#htoc2">1.1&nbsp;&nbsp;How it works</A>
<UL><LI>
<A HREF="#htoc3">1.1.1&nbsp;&nbsp;Router</A>
<LI><A HREF="#htoc4">1.1.2&nbsp;&nbsp;Local Router</A>
<LI><A HREF="#htoc5">1.1.3&nbsp;&nbsp;Session Manager</A>
<LI><A HREF="#htoc6">1.1.4&nbsp;&nbsp;S2S Manager</A>
</UL>
<LI CLASS="li-toc"><A HREF="#htoc4">2&nbsp;&nbsp;How it Works</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc5">2.1&nbsp;&nbsp;Router</A>
<LI CLASS="li-toc"><A HREF="#htoc6">2.2&nbsp;&nbsp;Local Router</A>
<LI CLASS="li-toc"><A HREF="#htoc7">2.3&nbsp;&nbsp;Session Manager</A>
<LI CLASS="li-toc"><A HREF="#htoc8">2.4&nbsp;&nbsp;S2S Manager</A>
</UL>
<LI CLASS="li-toc"><A HREF="#htoc9">3&nbsp;&nbsp;XML Representation</A>
<LI CLASS="li-toc"><A HREF="#htoc10">4&nbsp;&nbsp;Module <TT>xml</TT></A>
<LI CLASS="li-toc"><A HREF="#htoc11">5&nbsp;&nbsp;Module <TT>xml_stream</TT></A>
<LI CLASS="li-toc"><A HREF="#htoc12">6&nbsp;&nbsp;Modules</A>
<UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc13">6.1&nbsp;&nbsp;Module gen_iq_handler</A>
<LI CLASS="li-toc"><A HREF="#htoc14">6.2&nbsp;&nbsp;Services</A>
<LI><A HREF="#htoc7">2&nbsp;&nbsp;XML representation</A>
<LI><A HREF="#htoc8">3&nbsp;&nbsp;Module <TT>xml</TT></A>
<LI><A HREF="#htoc9">4&nbsp;&nbsp;<TT>ejabberd</TT> modules</A>
<UL><LI>
<A HREF="#htoc10">4.1&nbsp;&nbsp;<CODE>gen_mod</CODE> behaviour</A>
<LI><A HREF="#htoc11">4.2&nbsp;&nbsp;Module <CODE>gen_iq_handler</CODE></A>
<LI><A HREF="#htoc12">4.3&nbsp;&nbsp;Services</A>
</UL>
</UL>
<!--TOC section Introduction-->
<H2 CLASS="section"><A NAME="htoc1">1</A>&nbsp;&nbsp;<A NAME="intro">Introduction</A></H2><!--SEC END -->
<H2><A NAME="htoc1">1</A>&nbsp;&nbsp;Introduction</H2><!--SEC END -->
<A NAME="sec:intro"></A>
<TT>ejabberd</TT> is a free and open source instant messaging server written in <A HREF="http://www.erlang.org/">Erlang</A>.<BR>
<TT>ejabberd</TT> is a Free and Open Source fault-tolerant distributed Jabber
server. It is writen mostly in Erlang.<BR>
<BR>
<TT>ejabberd</TT> is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication.<BR>
<BR>
<TT>ejabberd</TT> is designed to be a rock-solid and feature rich XMPP server.<BR>
<BR>
<TT>ejabberd</TT> is suitable for small deployments, whether they need to be scalable or not, as well as extremely big deployments.<BR>
<BR>
<!--TOC subsection Key Features-->
<H3 CLASS="subsection"><A NAME="htoc2">1.1</A>&nbsp;&nbsp;<A NAME="keyfeatures">Key Features</A></H3><!--SEC END -->
<A NAME="sec:keyfeatures"></A>
<TT>ejabberd</TT> is:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Cross-platform: <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.<BR>
<BR>
<LI CLASS="li-itemize">Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.<BR>
<BR>
<LI CLASS="li-itemize">Fault-tolerant: You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.<BR>
<BR>
<LI CLASS="li-itemize">Administrator Friendly: <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Comprehensive documentation.
<LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows.
<LI CLASS="li-itemize">Web interface for administration tasks.
<LI CLASS="li-itemize">Shared Roster Groups.
<LI CLASS="li-itemize">Command line administration tool.
<LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
<LI CLASS="li-itemize">Capability to send announce messages.
</UL><BR>
<BR>
<LI CLASS="li-itemize">Internationalized: <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Translated in 12 languages.
<LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
</UL><BR>
<BR>
<LI CLASS="li-itemize">Open Standards: <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
<UL CLASS="itemize"><LI CLASS="li-itemize">
Fully XMPP compliant.
<LI CLASS="li-itemize">XML-based protocol.
<LI CLASS="li-itemize"><A HREF="http://ejabberd.jabber.ru/protocols">Many JEPs supported</A>.
</UL></UL>
<!--TOC subsection Additional Features-->
<H3 CLASS="subsection"><A NAME="htoc3">1.2</A>&nbsp;&nbsp;<A NAME="addfeatures">Additional Features</A></H3><!--SEC END -->
<A NAME="sec:addfeatures"></A>
Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Modular
<UL CLASS="itemize"><LI CLASS="li-itemize">
Load only the modules you want.
<LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
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>
<LI CLASS="li-itemize">Security
<UL CLASS="itemize"><LI CLASS="li-itemize">
SASL and STARTTLS for c2s and s2s connections.
<LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
<LI CLASS="li-itemize">Web interface accessible via HTTPS secure access.
</UL>
<LI CLASS="li-itemize">Databases
<UL CLASS="itemize"><LI CLASS="li-itemize">
Native MySQL support.
<LI CLASS="li-itemize">Native PostgreSQL support.
<LI CLASS="li-itemize">Mnesia.
<LI CLASS="li-itemize">ODBC data storage support.
<LI CLASS="li-itemize">Microsoft SQL Server support.
</UL>
<LI CLASS="li-itemize">Authentication
<UL CLASS="itemize"><LI CLASS="li-itemize">
LDAP and ODBC.
<LI CLASS="li-itemize">External Authentication script.
<LI CLASS="li-itemize">Internal Authentication.
</UL>
<LI CLASS="li-itemize">Others
<UL CLASS="itemize"><LI CLASS="li-itemize">
Compressing XML streams with Stream Compression (<A HREF="http://www.jabber.org/jeps/jep-0138.html">JEP-0138</A>).
<LI CLASS="li-itemize">Interface with networks such as AIM, ICQ and MSN.
<LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>).
<LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> module with logging.
<LI CLASS="li-itemize">Users Directory based on users vCards.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> component.
<LI CLASS="li-itemize">Support for virtual hosting.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service.
<LI CLASS="li-itemize">IRC transport.
</UL>
</UL>
<!--TOC section How it Works-->
<!--TOC subsection How it works-->
<H2 CLASS="section"><A NAME="htoc4">2</A>&nbsp;&nbsp;How it Works</H2><!--SEC END -->
<H3><A NAME="htoc2">1.1</A>&nbsp;&nbsp;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
@@ -190,15 +103,15 @@ 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 CLASS="itemize"><LI CLASS="li-itemize">
<UL><LI>
router;
<LI CLASS="li-itemize">local router.
<LI CLASS="li-itemize">session manager;
<LI CLASS="li-itemize">S2S manager;
<LI>local router.
<LI>session manager;
<LI>S2S manager;
</UL>
<!--TOC subsection Router-->
<!--TOC subsubsection Router-->
<H3 CLASS="subsection"><A NAME="htoc5">2.1</A>&nbsp;&nbsp;Router</H3><!--SEC END -->
<H4><A NAME="htoc3">1.1.1</A>&nbsp;&nbsp;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
@@ -208,26 +121,26 @@ searches in global table, and is routed to the appropriate <TT>ejabberd</TT> nod
process. If it does not exists in either tables, then it sent to the S2S
manager.<BR>
<BR>
<!--TOC subsection Local Router-->
<!--TOC subsubsection Local Router-->
<H3 CLASS="subsection"><A NAME="htoc6">2.2</A>&nbsp;&nbsp;Local Router</H3><!--SEC END -->
<H4><A NAME="htoc4">1.1.2</A>&nbsp;&nbsp;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 subsection Session Manager-->
<!--TOC subsubsection Session Manager-->
<H3 CLASS="subsection"><A NAME="htoc7">2.3</A>&nbsp;&nbsp;Session Manager</H3><!--SEC END -->
<H4><A NAME="htoc5">1.1.3</A>&nbsp;&nbsp;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 subsection S2S Manager-->
<!--TOC subsubsection S2S Manager-->
<H3 CLASS="subsection"><A NAME="htoc8">2.4</A>&nbsp;&nbsp;S2S Manager</H3><!--SEC END -->
<H4><A NAME="htoc6">1.1.4</A>&nbsp;&nbsp;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
@@ -236,13 +149,13 @@ 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-->
<!--TOC section XML representation-->
<H2 CLASS="section"><A NAME="htoc9">3</A>&nbsp;&nbsp;XML Representation</H2><!--SEC END -->
<H2><A NAME="htoc7">2</A>&nbsp;&nbsp;XML representation</H2><!--SEC END -->
<A NAME="sec:xmlrepr"></A>
Each XML stanza is represented as the following tuple:
<PRE CLASS="verbatim">
Each XML stanza represented as following tuple:
<PRE>
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
Name = string()
Attrs = [Attr]
@@ -252,14 +165,14 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
ElementOrCDATA = XMLElement | CDATA
CDATA = {xmlcdata, string()}
</PRE>E.&nbsp;g. this stanza:
<PRE CLASS="verbatim">
&lt;message to='test@conference.example.org' type='groupchat'&gt;
<PRE>
&lt;message to='test@conference.e.localhost' type='groupchat'&gt;
&lt;body&gt;test&lt;/body&gt;
&lt;/message&gt;
</PRE>is represented as the following structure:
<PRE CLASS="verbatim">
</PRE>represented as following structure:
<PRE>
{xmlelement, "message",
[{"to", "test@conference.example.org"},
[{"to", "test@conference.e.localhost"},
{"type", "groupchat"}],
[{xmlelement, "body",
[],
@@ -267,28 +180,29 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
</PRE>
<!--TOC section Module <TT>xml</TT>-->
<H2 CLASS="section"><A NAME="htoc10">4</A>&nbsp;&nbsp;Module <TT>xml</TT></H2><!--SEC END -->
<H2><A NAME="htoc8">3</A>&nbsp;&nbsp;Module <TT>xml</TT></H2><!--SEC END -->
<A NAME="sec:xmlmod"></A>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>element_to_string(El) -&gt; string()</CODE>
<PRE CLASS="verbatim">
<DL COMPACT=compact><DT>
<CODE><B>element_to_string(El) -&gt; string()</B></CODE><DD>
<PRE>
El = XMLElement
</PRE>Returns string representation of XML stanza <TT>El</TT>.<BR>
<BR>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>crypt(S) -&gt; string()</CODE>
<PRE CLASS="verbatim">
<DT><CODE><B>crypt(S) -&gt; string()</B></CODE><DD>
<PRE>
S = string()
</PRE>Returns string which correspond to <TT>S</TT> with encoded XML special
characters.<BR>
<BR>
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_cdata(ECList) -&gt; EList</CODE>
<PRE CLASS="verbatim">
<DT><CODE><B>remove_cdata(ECList) -&gt; 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 CLASS="dt-description"><DD CLASS="dd-description"><CODE>get_path_s(El, Path) -&gt; Res</CODE>
<PRE CLASS="verbatim">
<DT><CODE><B>get_path_s(El, Path) -&gt; Res</B></CODE><DD>
<PRE>
El = XMLElement
Path = [PathItem]
PathItem = PathElem | PathAttr | PathCDATA
@@ -299,78 +213,75 @@ Name = string()
Res = string() | XMLElement
</PRE>If <TT>Path</TT> is empty, then returns <TT>El</TT>. Else sequentially
consider elements of <TT>Path</TT>. Each element is one of:
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>{elem, Name}</CODE> <TT>Name</TT> is name of subelement 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 CLASS="dt-description"><DD CLASS="dd-description"><CODE>{attr, Name}</CODE> If <TT>El</TT> have attribute <TT>Name</TT>, then
<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 CLASS="dt-description"><DD CLASS="dd-description"><CODE>cdata</CODE> Returns CDATA of <TT>El</TT>.
<DT><CODE><B>cdata</B></CODE><DD> Returns CDATA of <TT>El</TT>.
</DL><BR>
<BR>
<DT CLASS="dt-description"><DD CLASS="dd-description">TODO:
<PRE CLASS="verbatim">
<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 Module <TT>xml_stream</TT>-->
<!--TOC section <TT>ejabberd</TT> modules-->
<H2 CLASS="section"><A NAME="htoc11">5</A>&nbsp;&nbsp;Module <TT>xml_stream</TT></H2><!--SEC END -->
<A NAME="sec:xmlstreammod"></A>
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>parse_element(Str) -&gt; XMLElement | {error, Err}</CODE>
<PRE CLASS="verbatim">
Str = string()
Err = term()
</PRE>Parses <TT>Str</TT> using XML parser, returns either parsed element or error
tuple.
</DL>
<!--TOC section Modules-->
<H2 CLASS="section"><A NAME="htoc12">6</A>&nbsp;&nbsp;Modules</H2><!--SEC END -->
<H2><A NAME="htoc9">4</A>&nbsp;&nbsp;<TT>ejabberd</TT> modules</H2><!--SEC END -->
<A NAME="sec:emods"></A>
<!--TOC subsection Module gen_iq_handler-->
<!--TOC subsection <CODE>gen_mod</CODE> behaviour-->
<H3 CLASS="subsection"><A NAME="htoc13">6.1</A>&nbsp;&nbsp;Module gen_iq_handler</H3><!--SEC END -->
<H3><A NAME="htoc10">4.1</A>&nbsp;&nbsp;<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>&nbsp;&nbsp;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 CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>add_iq_handler(Component, Host, NS, Module, Function, Type)</CODE>
<PRE CLASS="verbatim">
<DL COMPACT=compact><DT>
<CODE><B>add_iq_handler(Component, NS, Module, Function, Type)</B></CODE><DD>
<PRE>
Component = Module = Function = atom()
Host = NS = string()
NS = string()
Type = no_queue | one_queue | parallel
</PRE>Registers function <CODE>Module:Function</CODE> as handler for IQ packets on
virtual host <CODE>Host</CODE> that contain child of namespace <CODE>NS</CODE> in
<CODE>Component</CODE>. Queueing discipline is <CODE>Type</CODE>. There are at least
two components defined:
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_local</CODE> Handles packets that addressed to server JID;
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_sm</CODE> Handles packets that addressed to users bare JIDs.
</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 CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_iq_handler(Component, Host, NS)</CODE>
<PRE CLASS="verbatim">
<DT><CODE><B>remove_iq_handler(Component, NS)</B></CODE><DD>
<PRE>
Component = atom()
Host = NS = string()
</PRE>Removes IQ handler on virtual host <CODE>Host</CODE> for namespace <CODE>NS</CODE> from
<CODE>Component</CODE>.
NS = string()
</PRE>Removes IQ handler for namespace <CODE>NS</CODE> from <CODE>Component</CODE>.
</DL>
Handler function must have the following type:
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>Module:Function(From, To, IQ)</CODE>
<PRE CLASS="verbatim">
<DL COMPACT=compact><DT>
<CODE><B>Module:Function(From, To, IQ)</B></CODE><DD>
<PRE>
From = To = jid()
</PRE></DL>
<PRE CLASS="verbatim">
<PRE>
-module(mod_cputime).
-behaviour(gen_mod).
-export([start/2,
stop/1,
-export([start/1,
stop/0,
process_local_iq/3]).
-include("ejabberd.hrl").
@@ -378,13 +289,13 @@ From = To = jid()
-define(NS_CPUTIME, "ejabberd:cputime").
start(Host, Opts) -&gt;
start(Opts) -&gt;
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) -&gt;
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
stop() -&gt;
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_CPUTIME).
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -&gt;
case Type of
@@ -402,23 +313,25 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -&gt;
</PRE>
<!--TOC subsection Services-->
<H3 CLASS="subsection"><A NAME="htoc14">6.2</A>&nbsp;&nbsp;Services</H3><!--SEC END -->
<H3><A NAME="htoc12">4.3</A>&nbsp;&nbsp;Services</H3><!--SEC END -->
<A NAME="sec:services"></A>
<PRE CLASS="verbatim">
TBD<BR>
<BR>
TODO: use <CODE>proc_lib</CODE>
<PRE>
-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) -&gt;
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost])).
start(Opts) -&gt;
Host = gen_mod:get_opt(host, Opts, "echo." ++ ?MYNAME),
register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])).
init(Host) -&gt;
ejabberd_router:register_local_route(Host),
@@ -430,20 +343,22 @@ loop(Host) -&gt;
ejabberd_router:route(To, From, Packet),
loop(Host);
stop -&gt;
ejabberd_router:unregister_route(Host),
ejabberd_router:unregister_local_route(Host),
ok;
_ -&gt;
loop(Host)
end.
stop(Host) -&gt;
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
stop() -&gt;
ejabberd_mod_echo ! stop,
ok.
</PRE>
<!--HTMLFOOT-->
<!--ENDHTML-->
<!--FOOTER-->
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://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>
<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>
+110 -130
View File
@@ -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,84 +16,86 @@
\end{latexonly}
}
%% Various
\newcommand{\ns}[1]{\texttt{#1}}
\newcommand{\ejabberd}{\texttt{ejabberd}}
\newcommand{\Jabber}{Jabber}
%% 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{\modlastodbc}{\module{mod\_last\_odbc}}
\newcommand{\modmuc}{\module{mod\_muc}}
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
\newcommand{\modoffline}{\module{mod\_offline}}
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
\newcommand{\modprivacy}{\module{mod\_privacy}}
\newcommand{\modprivate}{\module{mod\_private}}
\newcommand{\modpubsub}{\module{mod\_pubsub}}
\newcommand{\modregister}{\module{mod\_register}}
\newcommand{\modroster}{\module{mod\_roster}}
\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}}
\newcommand{\modservicelog}{\module{mod\_service\_log}}
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
\newcommand{\modstats}{\module{mod\_stats}}
\newcommand{\modtime}{\module{mod\_time}}
\newcommand{\modvcard}{\module{mod\_vcard}}
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
\newcommand{\modversion}{\module{mod\_version}}
\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{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
\date{September 10, 2003}
\begin{document}
\label{sec: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}
\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}
\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
@@ -119,7 +115,7 @@ Each \ejabberd{} node have following modules:
\end{itemize}
\subsection{Router}
\subsubsection{Router}
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
@@ -130,14 +126,14 @@ 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 sended via presence table. If this resource is connected to
@@ -145,7 +141,7 @@ 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 \Jabber{} servers. First, it checks if an
open S2S connection from the domain of the packet source to the domain of
@@ -157,10 +153,10 @@ does not exist, then it is opened and registered.
\section{XML Representation}
\section{XML representation}
\label{sec: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()
@@ -173,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",
[],
@@ -193,20 +189,20 @@ is represented as the following structure:
\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]
@@ -215,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]
@@ -229,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
@@ -247,30 +243,18 @@ Res = string() | XMLElement
\end{description}
\section{Module \texttt{xml\_stream}}
\label{sec: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}
\section{\ejabberd{} modules}
\label{sec:emods}
%\subsection{gen\_mod behaviour}
%\label{sec:genmod}
\subsection{\verb|gen_mod| behaviour}
\label{sec:genmod}
%TBD
TBD
\subsection{Module gen\_iq\_handler}
\subsection{Module \verb|gen_iq_handler|}
\label{sec:geniqhandl}
The module \verb|gen_iq_handler| allows to easily write handlers for IQ packets
@@ -278,32 +262,30 @@ 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}
@@ -316,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").
@@ -325,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
@@ -352,24 +334,23 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
\subsection{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),
@@ -381,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}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

-163
View File
@@ -1,163 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD>
<TITLE>Ejabberd 1.1.2 Feature Sheet</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="hevea 1.08">
<STYLE type="text/css">
.toc{list-style:none;}
.title{margin:auto;text-align:center}
.center{text-align:center;margin-left:auto;margin-right:auto;}
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
DIV TABLE{margin-left:inherit;margin-right:inherit;}
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
.part{margin:auto;text-align:center}
SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
</STYLE>
</HEAD>
<BODY >
<!--HEVEA command line is: hevea -fix -noiso -pedantic features.tex -->
<!--HTMLHEAD-->
<!--ENDHTML-->
<!--PREFIX <ARG ></ARG>-->
<!--CUT DEF section 1 -->
<BR>
<BR>
<A NAME="titlepage"></A>
<TABLE CLASS="title">
<TR><TD>
<H1 CLASS="titlemain">Ejabberd 1.1.2 Feature Sheet</H1>
<H3 CLASS="titlerest">Sander Devrieze<BR>
<A HREF="mailto:s.devrieze@pandora.be"><TT>mailto:s.devrieze@pandora.be</TT></A><BR>
<A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD>
</TR></TABLE><BR>
<BR>
<DIV CLASS="center">
<IMG SRC="logo.png" ALT="logo.png">
<BR>
<BR>
</DIV>
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I can thoroughly recommend ejabberd for ease of setup &ndash;
Kevin Smith, Current maintainer of the Psi project</I></FONT></BLOCKQUOTE>
<!--TOC section Introduction-->
<H2 CLASS="section"><A NAME="intro">Introduction</A></H2><!--SEC END -->
<A NAME="sec:intro"></A>
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. &mdash;
Joeri</I></FONT></BLOCKQUOTE>
<TT>ejabberd</TT> is a <B><FONT SIZE=4><FONT COLOR="#001376">free and open source</FONT></FONT></B> instant messaging server written in <A HREF="http://www.erlang.org/">Erlang</A>.<BR>
<BR>
<TT>ejabberd</TT> is <B><FONT SIZE=4><FONT COLOR="#001376">cross-platform</FONT></FONT></B>, distributed, fault-tolerant, and based on open standards to achieve real-time communication.<BR>
<BR>
<TT>ejabberd</TT> is designed to be a <B><FONT SIZE=4><FONT COLOR="#001376">rock-solid and feature rich</FONT></FONT></B> XMPP server.<BR>
<BR>
<TT>ejabberd</TT> is suitable for small deployments, whether they need to be <B><FONT SIZE=4><FONT COLOR="#001376">scalable</FONT></FONT></B> or not, as well as extremely big deployments.<BR>
<BR>
<!--TOC subsection Key Features-->
<H3 CLASS="subsection"><A NAME="keyfeatures">Key Features</A></H3><!--SEC END -->
<A NAME="sec:keyfeatures"></A>
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>Erlang seems to be tailor-made for writing stable, robust servers. &mdash;
Peter Saint-Andr&eacute;, Executive Director of the Jabber Software Foundation</I></FONT></BLOCKQUOTE>
<TT>ejabberd</TT> is:
<UL CLASS="itemize"><LI CLASS="li-itemize">
<B><FONT SIZE=4><FONT COLOR="#001376">Cross-platform:</FONT></FONT></B> <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.<BR>
<BR>
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Distributed:</FONT></FONT></B> You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.<BR>
<BR>
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Fault-tolerant:</FONT></FONT></B> You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.<BR>
<BR>
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Administrator Friendly:</FONT></FONT></B> <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Comprehensive documentation.
<LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
<LI CLASS="li-itemize">Web interface for administration tasks.
<LI CLASS="li-itemize">Shared Roster Groups.
<LI CLASS="li-itemize">Command line administration tool.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
<LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
<LI CLASS="li-itemize">Capability to send announce messages.
</UL><BR>
<BR>
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Internationalized:</FONT></FONT></B> <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Translated in 12 languages.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
<LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
</UL><BR>
<BR>
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Open Standards:</FONT></FONT></B> <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
<UL CLASS="itemize"><LI CLASS="li-itemize">
Fully XMPP compliant.
<LI CLASS="li-itemize">XML-based protocol.
<LI CLASS="li-itemize"><A HREF="http://ejabberd.jabber.ru/protocols">Many JEPs supported</A>.
</UL></UL>
<!--TOC subsection Additional Features-->
<H3 CLASS="subsection"><A NAME="addfeatures">Additional Features</A></H3><!--SEC END -->
<A NAME="sec:addfeatures"></A>
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>ejabberd is making inroads to solving the "buggy incomplete server" problem &mdash;
Justin Karneges, Founder of the Psi and the Delta projects</I></FONT></BLOCKQUOTE>
Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
<UL CLASS="itemize"><LI CLASS="li-itemize">
Modular
<UL CLASS="itemize"><LI CLASS="li-itemize">
Load only the modules you want.
<LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
</UL>
<LI CLASS="li-itemize">Security
<UL CLASS="itemize"><LI CLASS="li-itemize">
SASL and STARTTLS for c2s and s2s connections.
<LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
<LI CLASS="li-itemize">Web interface accessible via HTTPS secure access.
</UL>
<LI CLASS="li-itemize">Databases
<UL CLASS="itemize"><LI CLASS="li-itemize">
Native MySQL support.
<LI CLASS="li-itemize">Native PostgreSQL support.
<LI CLASS="li-itemize">Mnesia.
<LI CLASS="li-itemize">ODBC data storage support.
<LI CLASS="li-itemize">Microsoft SQL Server support.<FONT COLOR="red"><SPAN STYLE="font-variant:small-caps">new</SPAN></FONT>
</UL>
<LI CLASS="li-itemize">Authentication
<UL CLASS="itemize"><LI CLASS="li-itemize">
LDAP and ODBC.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
<LI CLASS="li-itemize">External Authentication script.
<LI CLASS="li-itemize">Internal Authentication.
</UL>
<LI CLASS="li-itemize">Others
<UL CLASS="itemize"><LI CLASS="li-itemize">
Compressing XML streams with Stream Compression (<A HREF="http://www.jabber.org/jeps/jep-0138.html">JEP-0138</A>).
<LI CLASS="li-itemize">Interface with networks such as AIM, ICQ and MSN.
<LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>).
<LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> module with logging.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
<LI CLASS="li-itemize">Users Directory based on users vCards.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> component.
<LI CLASS="li-itemize">Support for virtual hosting.
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service.
<LI CLASS="li-itemize">IRC transport.
</UL>
</UL>
<!--HTMLFOOT-->
<!--ENDHTML-->
<!--FOOTER-->
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
</EM><A HREF="http://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>
-136
View File
@@ -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{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
%% Fancy header
\fancyhf{}
\pagestyle{fancy}
\rhead{\textcolor{ejblue}{The Expandable Jabber 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
+759 -3773
View File
File diff suppressed because it is too large Load Diff
+652 -2698
View File
File diff suppressed because it is too large Load Diff
-131
View File
@@ -1,131 +0,0 @@
\section{\aname{intro}{Introduction}}
\label{sec:intro}
\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}.
\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{\aname{layout}{Layout with example deployment (title needs a better name)}}
%\label{sec: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{\aname{trytoday}{Try It Today}}
%\label{sec: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
\subsection{\aname{keyfeatures}{Key Features}}
\label{sec: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 interface for administration tasks.
\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 in 12 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://ejabberd.jabber.ru/protocols}{Many JEPs supported}.
\end{itemize}
\end{itemize}
\newpage
\subsection{\aname{addfeatures}{Additional Features}}
\label{sec: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 interface accessible via HTTPS secure access.
\end{itemize}
\item Databases
\begin{itemize}
\item Native MySQL support.
\item Native PostgreSQL support.
\item Mnesia.
\item ODBC data storage support.
\item Microsoft SQL Server support.\new{}
\end{itemize}
\item Authentication
\begin{itemize}
\item LDAP and ODBC.\improved{}
\item External Authentication script.
\item Internal Authentication.
\end{itemize}
\item Others
\begin{itemize}
\item Compressing XML streams with Stream Compression (\jepref{0138}).
\item Interface with networks such as AIM, ICQ and MSN.
\item Statistics via Statistics Gathering (\jepref{0039}).
\item IPv6 support both for c2s and s2s connections.
\item \tjepref{0045}{Multi-User Chat} module with logging.\improved{}
\item Users Directory based on users vCards.
\item \tjepref{0060}{Publish-Subscribe} component.
\item Support for virtual hosting.
\item \tjepref{0025}{HTTP Polling} service.
\item IRC transport.
\end{itemize}
\end{itemize}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 20 KiB

-62
View File
@@ -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.
-99
View File
@@ -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
-88
View File
@@ -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.
-120
View File
@@ -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
-115
View File
@@ -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
-119
View File
@@ -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
-119
View File
@@ -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
-50
View File
@@ -1,50 +0,0 @@
Release Notes
ejabberd 1.1.3
2 February 2007
This document describes the change in ejabberd 1.1.3.
This version is a security improvement over ejabberd 1.1.2, solving
the mod_roster_odbc SQL injection issue.
Users of ejabberd 1.1.2 that are using mod_roster_odbc MUST upgrade to
this new release.
ejabberd can be downloaded from the Process-one website:
http://www.process-one.net/en/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/ejabberd/docs.html
Installation Notes
Supported Erlang Version
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.3.
- The recommanded version is Erlang/OTP R11B-3.
Installation
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
They can be retrieved from:
http://www.process-one.net/en/ejabberd/download.html
Migration Notes
- Before any migration, ejabberd system and database must be properly
backed up.
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
-2
View File
@@ -1,2 +0,0 @@
% Define ejabberd version here.
\newcommand{\version}{1.1.2}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

-50
View File
@@ -1,50 +0,0 @@
#!/usr/local/bin/perl
use Unix::Syslog qw(:macros :subs);
my $domain = $ARGV[0] || "example.com";
while(1)
{
# my $rin = '',$rout;
# vec($rin,fileno(STDIN),1) = 1;
# $ein = $rin;
# my $nfound = select($rout=$rin,undef,undef,undef);
my $buf = "";
syslog LOG_INFO,"waiting for packet";
my $nread = sysread STDIN,$buf,2;
do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2;
my $len = unpack "n",$buf;
my $nread = sysread STDIN,$buf,$len;
my ($op,$user,$password) = split /:/,$buf;
#$user =~ s/\./\//og;
my $jid = "$user\@$domain";
my $result;
syslog(LOG_INFO,"request (%s)", $op);
SWITCH:
{
$op eq 'auth' and do
{
$result = 1;
},last SWITCH;
$op eq 'setpass' and do
{
$result = 1;
},last SWITCH;
$op eq 'isuser' and do
{
# password is null. Return 1 if the user $user\@$domain exitst.
$result = 1;
},last SWITCH;
};
my $out = pack "nn",2,$result ? 1 : 0;
syswrite STDOUT,$out;
}
closelog;
@@ -1,77 +0,0 @@
<!-- aim-transport.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/aim-transport-error.log</file>
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<format>%d %h %s</format>
<file>/var/log/jabber/aim-transport-record.log</file>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file> <!-- This file is part of jabberd-1.4.x. -->
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id='aim.SERVER.COM'>
<!-- aim-transport configuration. -->
<aimtrans xmlns='jabber:config:aimtrans'>
<vCard>
<FN>AIM/ICQ Transport</FN>
<DESC>This is the AIM/ICQ Transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://aim-transport.jabberstudio.org/</URL>
</vCard>
<charset>cp1252</charset>
</aimtrans>
<!-- aim-transport module. -->
<load>
<aim_transport>/usr/local/lib/jabber/aim-transport.so</aim_transport>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="icq-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5233</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/aim-transport.pid</pidfile>
</jabber>
-136
View File
@@ -1,136 +0,0 @@
<!-- ile.xml -->
<config>
<jabber>
<server>127.0.0.1</server>
<port>5238</port>
<secret>SECRET</secret>
<service>ile.SERVER.COM</service>
<connectsleep>7</connectsleep> <!-- seconds to wait if we get disconnected -->
<language>en</language>
<vCard>
<FN>I Love Email</FN>
<DESC>With this service you can receive email notifications.
Security warning: Be careful when using this. Your password will travel in clear from your client to your jabber server if you don't use SSL and it will probably travel in clear from the jabber server to your email server. Use with care. This shouldn't be an issue in your Intranet, but it is if you use an ILE installed in a foreign jabber server.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://ile.jabberstudio.org/</URL>
</vCard>
</jabber>
<debug>
<file>/var/log/jabber/ile.log</file>
<level>1</level> <!-- man Net::Jabber::Debug -->
</debug>
<mail>
<checkinterval>10</checkinterval> <!-- in minutes -->
<timeout>20</timeout> <!-- timeout for IMAP/POP connection, in seconds -->
</mail>
<files>
<users>/var/spool/jabber/ile.SERVER.COM/users.db</users>
<passwords>/var/spool/jabber/ile.SERVER.COM/passwords.db</passwords>
<hosts>/var/spool/jabber/ile.SERVER.COM/hosts.db</hosts>
<types>/var/spool/jabber/ile.SERVER.COM/types.db</types>
<notifyxa>/var/spool/jabber/ile.SERVER.COM/notifyxa.db</notifyxa>
<notifydnd>/var/spool/jabber/ile.SERVER.COM/notifydnd.db</notifydnd>
<urls>/var/spool/jabber/ile.SERVER.COM/urls.db</urls>
</files>
<form>
<en>
<instructions>Please fill in the fields,according to your email account settings and notification preferences</instructions>
<title>ILE: Email notification service</title>
<email_options>Email account settings</email_options>
<user>Username</user>
<pass>Password</pass>
<host>Hostname</host>
<type>Type</type>
<newmail>You have received NUM email messages since last time I checked, which was CHECKINTERVAL minutes ago.</newmail>
<errorcheck>There was an error while trying to check mail for ACCOUNT.</errorcheck>
<notify_options>Notification Options</notify_options>
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Login to ACCOUNT</webmail_login>
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
</en>
<es>
<instructions>Por favor, rellene los campos del formulario.</instructions>
<title>ILE: Servicio de notificación de correo</title>
<email_options>Configuración de la cuenta de correo</email_options>
<user>Usuario</user>
<pass>Clave</pass>
<host>Host</host>
<type>Tipo</type>
<newmail>Ha recibido NUM email(s) desde la última comprobación que fue hace CHECKINTERVAL minutos</newmail>
<errorcheck>Ha habido un error en la comprobación del correo para la cuenta ACCOUNT.</errorcheck>
<notify_options>Opciones de notificación</notify_options>
<notifyxa>Notificar incluso si muy ausente (XA)</notifyxa>
<notifydnd>Notificar incluso si no molestar (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Leer correo de ACCOUNT</webmail_login>
<iledesc>ILE: un notificador de nuevo email - http://ile.jabberstudio.org</iledesc>
</es>
<ca>
<instructions>Ompli els camps del formulari.</instructions>
<title>ILE: Servei de notificació de nou email</title>
<email_options>Dades del compte de mail</email_options>
<user>Usuari</user>
<pass>Clau</pass>
<host>Host</host>
<type>Tipus</type>
<newmail>Ha rebut NUM email(s) des de la última comprobació que va ser fa CHECKINTERVAL minuts.</newmail>
<errorcheck>S'ha produit un error en la comprobació del correu per al compte ACCOUNT.</errorcheck>
<notify_options>Opcions de notificació</notify_options>
<notifyxa>Notificar si molt absent (XA)</notifyxa>
<notifydnd>Notificar si no molestar (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Llegir correu de ACCOUNT</webmail_login>
<iledesc>ILE: un notificador de nou email - http://ile.jabberstudio.org</iledesc>
</ca>
<ro>
<!-- Contributed by Adrian Rappa -->
<instructions>Va rog completati urmatoarele campuri</instructions>
<title>I Love Email: new email notification service</title>
<email_options>Email account settings</email_options>
<user>Nume utilizator</user>
<pass>Parola</pass>
<host>Nume gazda</host>
<type>Tip</type>
<newmail>Ati primit NUM mesaj(e) de la ultima verificare, care a fost acum CHECKINTERVAL minute.</newmail>
<errorcheck>A fost eroare in timp ce incercam sa verific posta pentru ACCOUNT.</errorcheck>
<notify_options>Notification Options</notify_options>
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
<webmail_url>Webmail URL</webmail_url>
<webmail_login>Login to ACCOUNT</webmail_login>
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
</ro>
<nl>
<!-- Contributed by Sander Devrieze -->
<instructions>Vul volgende velden in.</instructions>
<title>ILE: Dienst voor e-mailnotificaties</title>
<email_options>Instellingen van e-mailaccount</email_options>
<user>Gebruikersnaam</user>
<pass>Wachtwoord</pass>
<host>Inkomende mailserver</host>
<type>Type verbinding</type>
<newmail>U hebt NUM berichten ontvangen sinds CHECKINTERVAL minuten geleden.</newmail>
<errorcheck>Fout tijdens controle op nieuwe e-mails bij ACCOUNT. ILE zal deze account niet meer opnieuw controleren tot u uw registratiegegevens wijzigt of opnieuw aanmeldt.</errorcheck>
<notify_options>Notificatie-instellingen</notify_options>
<notifyxa>Notificeer ook in de status Niet Beschikbaar (XA)</notifyxa>
<notifydnd>Notificeer ook in de status Niet Storen (DND)</notifydnd>
<webmail_url>URL van webmail</webmail_url>
<webmail_login>Aanmelden op ACCOUNT</webmail_login>
<iledesc>ILE: een dienst om e-mailnotificaties te ontvangen: http://ile.jabberstudio.org</iledesc>
</nl>
</form>
</config>
@@ -1,149 +0,0 @@
<jggtrans>
<service jid="gg.SERVER.COM"/>
<!-- This connects the jabber-gg-transport process to ejabberd. -->
<connect id="gglinker">
<ip>127.0.0.1</ip>
<port>5237</port>
<secret>SECRET</secret>
</connect>
<register>
<!-- This tag contains the message displayed to users at registration time.
You can use <p/> and/or <br/> to break lines. Multiple spaces and newlines
are converted to just one, so formatting of config file doesn't really matter. -->
<instructions>
Fill in your GG number (after "username")
and password to register on the transport.
<p/>To change your information in the GaduGadu directory you need to fill in the other fields.
<p/>To remove registration you need to leave the form blank.
</instructions>
</register>
<search>
<!-- This tag contains the message displayed to users at search time. -->
<instructions>
To search people:<br/>
First fill in surname or family name, nickname, city, birthyear or range of birthyears (eg. 1950-1960)
and gender (you may fill in more fields at once).<br/>
or<br/>
Fill in phone number<br/>
or<br/>
Fill in the GG number of the person you are searching.
</instructions>
</search>
<gateway>
<!-- This is message, that may be displayed to user when adding gg contact. -->
<desc>Please fill in the GaduGadu number of the person you want to add.</desc>
<!-- And this is the prompt for GG number. -->
<prompt>GG Nummer</prompt>
</gateway>
<vCard>
<FN>Gadu-Gadu Transport</FN>
<DESC>This is the Gadu-Gadu Transport.</DESC>
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
<URL>http://www.jabberstudio.org/projects/jabber-gg-transport/</URL>
</vCard>
<!-- Default user locale (language).
Empty means system locale setting,
no (or commented-out) <default_locale> tag means no translations. -->
<!-- <default_locale>pl_PL</default_locale> -->
<!-- Logger configuration.
You may configure one logger of type "syslog" and/or one of type "file".
You may also not configure logging at all. -->
<log type="syslog" facility="local0"/>
<log type="file">/var/log/jabber/jabber-gg-transport.log</log>
<!-- Uncomment this, if you want proxy to be used for Gadu-Gadu connection. -->
<!--
<proxy>
<ip>127.0.0.1</ip>
<port>8080</port>
</proxy>
-->
<!-- You can change these values according to your needs. -->
<conn_timeout>60</conn_timeout>
<ping_interval>10</ping_interval>
<!-- Gadu-Gadu server doesn't seem to answer pings anymore :-(
So let's give it 10 year :-) -->
<pong_timeout>315360000</pong_timeout>
<!-- This time after disconnection from Gadu-Gadu server the transport
will try to connect again. -->
<reconnect>300</reconnect>
<!-- How long to wait before restart, after jabber server connection is broken
negative value means, that jggtrans should terminate. -->
<restart_timeout>60</restart_timeout>
<!-- Delay between the unavailable presence is received from user and loggin out
from Gadu-Gadu - for nice <presence type="invisible"/> support. -->
<disconnect_delay>5</disconnect_delay>
<!-- list of Gadu-Gadu servers to use.
<hub/> means "use GG hub to find server"
<server/> tag should contain server address and may contain "port"
attribute with port number. When TLS is available (supported by libgadu)
it will be used unless "tls" attribute is set to "no". Please notice,
that not all servers will accept TLS connections.
Servers (including hub) are tried in order as they appear in <servers/>
element.
A reasonable default server list is hardcoded in jggtrans.
-->
<!--
<servers>
<hub/>
<server port="443">217.17.41.90</server>
<server tls="no">217.17.41.85</server>
<server tls="no">217.17.41.88</server>
</servers>
-->
<!-- Spool directory. This is the place, where user info will be stored. -->
<!-- Be careful about permissions - users' Gadu-Gadu passwords are stored there. -->
<spool>/var/spool/jabber/gg.SERVER.COM/</spool>
<!-- Where to store pid file. This tag is optional. -->
<pidfile>/var/run/jabber/jabber-gg-transport.pid</pidfile>
<!-- jid allowed to do some administrative task (eg. discovering online users).
May be used multiple times. -->
<admin>GG_TRANSPORT_ADMIN@SERVER.COM</admin>
<!-- ACL gives detailed access control to the transport. -->
<acl>
<!-- Example entries: -->
<allow who="admin@SERVER.COM" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
<!-- will allow statistics gathering to admin@SERVER.COM -->
<deny who="*" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
<!-- will deny statistics gathering for anybody else -->
<!-- <allow who="*@SERVER.COM"/> -->
<!-- will allow anything else to users from "SERVER.COM" -->
<!-- <deny what="iq/query?xmlns=jabber:x:register"/> -->
<!-- will deny registration for all other users -->
<!-- <allow what="presence"/> -->
<!-- allow presence from anybody -->
<!-- <allow what="iq"/> -->
<!-- allow iq from anybody -->
<!-- <allow what="message"/> -->
<!-- allow message from anybody -->
<!-- <deny/> -->
<!-- will deny anything else -->
</acl>
</jggtrans>
-128
View File
@@ -1,128 +0,0 @@
<!-- jit.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<file>/var/log/jabber/jit-error</file> <!-- WPJabber logs with date. -->
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<file>/var/log/jabber/jit-record</file> <!-- WPJabber logs with date. -->
</log>
<!--
ejabberd do not provide XDB services.
xdb_file-jit.so (the renamed xdb_file.so from WPJabber) is
loaded in to handle all XDB requests.
Read also the documentation in xdb_file/README from the JIT package.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/xdb_file.so</xdb_file> <!-- The xdb_file.so from WPJabber/JIT. -->
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="icq.SERVER.COM">
<!--
Replace SERVER.COM with the same as above to enable sms.
-->
<host>sms.icq.SERVER.COM</host>
<!-- JIT configuration. -->
<icqtrans xmlns="jabber:config:icqtrans">
<sms>
<host>sms.icq.SERVER.COM</host>
<!-- Status of virtual "sms-contacts". -->
<show>away</show>
<status/>
</sms>
<instructions>Fill in your UIN and password.</instructions>
<search>Search ICQ users.</search>
<vCard>
<FN>ICQ Transport (JIT)</FN>
<DESC>This is the Jabber ICQ Transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://jit.jabberstudio.org/</URL>
</vCard>
<!-- Hashtable for users. -->
<prime>3907</prime>
<!-- Send messages from ICQ as chat to Jabber clients. -->
<chat/>
<!-- Enable this for ICQ web presence. -->
<web/>
<!--
If you don't want jabber:x:data forms
in reg and search uncomment this tag
(Not recomended).
-->
<no_xdata/>
<!--
This tag is necessary when using ejabberd.
In this way JIT will have its own contact list.
-->
<own_roster/>
<!--
When present, this tag will tell JIT not to try to
get the user's roster (which will take a bit of time
to fail in scenarios described above).
-->
<no_jabber_roster/>
<!-- File with stats. -->
<user_count_file>/var/spool/jabber/jit-count</user_count_file>
<!--
Interval beetween checking sessions: ping, messages, acks.
-->
<session_check>5</session_check>
<!-- Reconnect retries. -->
<reconnects>5</reconnects>
<!--
Time in sec when session can be inactive, 0=disabled.
-->
<session_timeout>18000</session_timeout>
<charset>windows-1252</charset>
<server>
<host port="5190">login.icq.com</host>
</server>
</icqtrans>
<!-- JIT module. -->
<load>
<icqtrans>/usr/local/lib/jabber/jit.so</icqtrans>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="icq-linker">
<host>SERVER.COM</host>
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5234</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/jit.pid</pidfile>
</jabber>
@@ -1,118 +0,0 @@
<!-- msn-transport.xml -->
<jabber>
<!--
You need to add elogger and rlogger entries here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/msn-transport-error.log</file>
</log>
<log id='rlogger'>
<host/>
<logtype>record</logtype>
<format>%d %h %s</format>
<file>/var/log/jabber/msn-transport-record.log</file>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="msn.SERVER.COM">
<!-- msn-transport configuration. -->
<msntrans xmlns="jabber:config:msntrans">
<instructions>Fill in your MSN account and password (eg: user1@hotmail.com). A nickname is optional.</instructions>
<vCard>
<FN>MSN Transport</FN>
<DESC>This is the MSN Transport.</DESC>
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
<URL>http://msn-transport.jabberstudio.org/</URL>
</vCard>
<!--
Conference support allows you to create groupchat rooms on the
msn-transport and invite MSN users to join.
-->
<conference id="conference.msn.SERVER.COM">
<!--
This will make MSN transport invite you to a special groupchat
room when more then one user joins a normal one-on-one session.
Joining this room will make MSN transport "switch" the session
into groupchat mode. If you ignore it, MSN transport will
continue to send the messages as one-on-one chats.
-->
<invite>More than one user entered this chat session. Enter this room to switch to groupchat modus.</invite>
<notice>
<join> is available</join>
<leave> has leaved the room</leave>
</notice>
</conference>
<!-- Enable Hotmail inbox notification. -->
<headlines/>
<!--
Enable fancy friendly names
If the user enters a nickname upon registration, and the user has
a status message, their MSN friendly name will be "nickname - status message".
If the user does not enter a nickname on registration, but they do have
a status message, their friendly name will just be their status message.
If the user did enter a nickname on registration, but they have a blank status message,
then their friendly name will just be the registered nickname.
If the user did not enter a nickname on registration, and they have a blank status message,
their nickname will just be the username portion of their JID.
If the above chosen friendly name is too long, then it will be truncated and "..." placed
at the end. MSN only supports friendly names of 128 characters, so this is unavoidable.
If this is disabled, then the registered nick is always sent as the MSN friendly name,
or if that is blank, the username portion of their JID is sent instead.
-->
<fancy_friendly/>
</msntrans>
<!-- msn-transport module. -->
<load>
<msntrans>/usr/local/lib/jabber/msn-transport.so</msntrans>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="msn-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5235</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/msn-transport.pid</pidfile>
</jabber>
@@ -1,86 +0,0 @@
<!-- yahoo-transport-2.xml -->
<jabber>
<!--
You need to add the elogger entry here when using ejabberd.
In this case the transport will do the logging.
-->
<log id='elogger'>
<host/>
<logtype/>
<format>%d: [%t] (%h): %s</format>
<file>/var/log/jabber/yahoo-transport-2-error.log</file>
<stderr/>
</log>
<!--
ejabberd do not provide XDB services.
xdb_file.so is loaded in to handle all XDB requests.
-->
<xdb id="xdb">
<host/>
<load>
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
</load>
<xdb_file xmlns="jabber:config:xdb_file">
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
</xdb_file>
</xdb>
<!--
Make sure that all host names here are resolveable via DNS if you
want the transport to be available to the public.
-->
<service id="yahoo.SERVER.COM">
<!-- yahoo-transport-2 configuration. -->
<config xmlns="jabber:config:yahoo">
<vCard>
<NAME>Yahoo! Transport</NAME>
<FN>vCard not implemented in current version</FN>
<DESC>This is the Yahoo! transport.</DESC>
<MAIL>EMAIL@ADDRESS.COM</MAIL>
<URL>http://yahoo-transport-2.jabberstudio.org/</URL>
</vCard>
<instructions>Fill in your YAHOO! Messenger username and password to register on this transport.</instructions>
<server>scs.msg.yahoo.com</server>
<port>5050</port>
<!--
The character map. This provides character set translation from UTF-8
to the indicated character map. See the man page for 'iconv' for available
character maps on your platform. CP1252 is the standard Windows character
set.
-->
<charmap>CP1252</charmap>
<!--
When this element exists, the transport will send new mail notifications as
well as a count of unread messages when the user initially logs in.
-->
<newmail/>
</config>
<!-- yahoo-transport-2 module. -->
<load>
<yahoo_transport>/usr/local/lib/jabber/yahoo-transport-2.so</yahoo_transport>
</load>
</service>
<!--
The settings below have to match the settings you made
in your ejabberd.cfg configuration file.
-->
<service id="yahoo-linker">
<uplink/>
<connect>
<ip>127.0.0.1</ip>
<port>5236</port>
<secret>SECRET</secret>
</connect>
</service>
<pidfile>/var/run/jabber/yahoo-transport-2.pid</pidfile>
</jabber>
@@ -1,45 +0,0 @@
#!/bin/sh
#########################################################
#
# aim-transport -- script to start aim-transport.
#
#########################################################
DAEMON=/usr/local/sbin/jabberd-aim-transport
CONF=/etc/jabber/aim-transport.xml
NAME=jabberd-aim-transport
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
@@ -1,43 +0,0 @@
#!/bin/sh
#########################################################
#
# ile -- script to start ILE.
#
#########################################################
DAEMON=/usr/local/sbin/ile.pl
NAME=ile.pl
CONF=/etc/jabber/ile.xml
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
echo "Not implemented yet. Starting in normal mode"
$0 start
;;
start)
test -f $DAEMON || exit 0
echo "Starting $NAME."
$DAEMON $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|status|restart}"
exit 1
esac
@@ -1,47 +0,0 @@
#!/bin/sh
#########################################################
#
# jabber-gg-transport -- script to start jabber-gg-transport.
#
#########################################################
DAEMON=/usr/local/sbin/jggtrans
CONF=/etc/jabber/jabber-gg-transport.xml
NAME=jggtrans
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
rm /var/run/jabber/jabber-gg-transport.pid
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
@@ -1,45 +0,0 @@
#!/bin/sh
#########################################################
#
# jit -- script to start JIT.
#
#########################################################
DAEMON=/usr/local/sbin/wpjabber-jit
CONF=/etc/jabber/jit.xml
NAME=wpjabber-jit
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
@@ -1,50 +0,0 @@
#!/bin/sh
#########################################################
#
# msn-transport -- script to start MSN Transport.
#
#########################################################
DAEMON=/usr/local/sbin/jabberd-msn-transport
CONF=/etc/jabber/msn-transport.xml
NAME=jabberd-msn-transport
HOME=/etc/jabber/
USER=ejabberd
#########################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
strace)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in strace mode."
strace -o /opt/ejabberd/var/log/jabber/strace.log $DAEMON -H $HOME -c $CONF &
;;
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
@@ -1,45 +0,0 @@
#!/bin/sh
##############################################################
#
# yahoo-transport-2 -- script to start Yahoo-transport-2.
#
#############################################################
DAEMON=/usr/local/sbin/jabberd-yahoo-transport-2
CONF=/etc/jabber/yahoo-transport-2.xml
NAME=jabberd-yahoo-transport-2
HOME=/etc/jabber/
USER=ejabberd
#############################################################
if [ "`/usr/bin/whoami`" != "$USER" ]; then
echo "You need to be" $USER "user to run this script."
exit 1
fi
case "$1" in
debug)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME in debugging mode."
$DAEMON -D -H $HOME -c $CONF &
;;
start)
test -f $DAEMON -a -f $CONF || exit 0
echo "Starting $NAME."
$DAEMON -H $HOME -c $CONF &
;;
stop)
echo "Stopping $NAME."
killall $NAME &
;;
restart|reload)
$0 stop
sleep 3
$0 start
;;
*)
echo "Usage: $0 {debug|start|stop|restart}"
exit 1
esac
+18 -48
View File
@@ -6,57 +6,30 @@ CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
EXPAT_CFLAGS = @EXPAT_CFLAGS@
ERLANG_CFLAGS= @ERLANG_CFLAGS@
EXPAT_LIBS = @EXPAT_LIBS@
ERLANG_LIBS = @ERLANG_LIBS@
# make debug=true to compile Erlang module with debug informations.
ifdef debug
ERLC_FLAGS+=+debug_info
endif
ifdef ejabberd_debug
ERLC_FLAGS+=-Dejabberd_debug
endif
ifeq (@roster_gateway_workaround@, true)
ERLC_FLAGS+=-DROSTER_GATEWAY_WORKAROUND
endif
prefix = @prefix@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
INCLUDES = @ERLANG_CFLAGS@ @EXPAT_CFLAGS@
LIBDIRS = @ERLANG_LIBS@ @EXPAT_LIBS@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep
ERLSHLIBS = expat_erl.so
SOURCES = $(wildcard *.erl)
BEAMS = $(SOURCES:.erl=.beam)
DESTDIR =
DESTDIR =
EJABBERDDIR = $(DESTDIR)@prefix@/var/lib/ejabberd
EJABBERDDIR = $(DESTDIR)/var/lib/ejabberd
BEAMDIR = $(EJABBERDDIR)/ebin
PRIVDIR = $(EJABBERDDIR)/priv
SODIR = $(PRIVDIR)/lib
MSGSDIR = $(PRIVDIR)/msgs
LOGDIR = $(DESTDIR)@prefix@/var/log/ejabberd
ETCDIR = $(DESTDIR)@prefix@/etc/ejabberd
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
ifeq ($(shell uname),Darwin)
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
else
# Assume Linux-style dynamic library flags
DYNAMIC_LIB_CFLAGS = -fpic -shared
endif
LOGDIR = $(DESTDIR)/var/log/ejabberd
ETCDIR = $(DESTDIR)/etc/ejabberd
all: $(ERLSHLIBS) compile-beam all-recursive
compile-beam: XmppAddr.hrl $(BEAMS)
%.beam: %.erl
@ERLC@ -W $(ERLC_FLAGS) $<
compile-beam:
@erl -s make all report -noinput -s erlang halt
all-recursive install-recursive uninstall-recursive \
@@ -69,15 +42,13 @@ mostlyclean-recursive maintainer-clean-recursive:
done
%.hrl: %.asn1
@ERLC@ $(ASN_FLAGS) $<
$(ERLSHLIBS): %.so: %.c
gcc -Wall $(CFLAGS) $(LDFLAGS) $(LIBS) \
gcc -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) \
$(subst ../,,$(subst .so,.c,$@)) \
$(EXPAT_LIBS) $(EXPAT_CFLAGS) \
$(ERLANG_LIBS) $(ERLANG_CFLAGS) \
-o $@ $(DYNAMIC_LIB_CFLAGS)
-lexpat \
-lerl_interface \
-lei \
-o $@ -fpic -shared \
install: all
install -d $(BEAMDIR)
@@ -89,14 +60,13 @@ install: all
install -d $(MSGSDIR)
install -m 644 msgs/*.msg $(MSGSDIR)
install -d $(ETCDIR)
[ -f $(ETCDIR)/ejabberd.cfg ] && install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg-new || install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
install -d $(LOGDIR)
clean: clean-recursive clean-local
clean-local:
rm -f *.beam $(ERLSHLIBS)
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
distclean: distclean-recursive clean-local
rm -f config.status
+14 -32
View File
@@ -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)
@@ -63,19 +66,12 @@ release : build release_clean
copy stringprep\*.erl $(SRC_DIR)\stringprep
copy stringprep\*.c $(SRC_DIR)\stringprep
copy stringprep\*.tcl $(SRC_DIR)\stringprep
mkdir $(SRC_DIR)\tls
copy tls\*.erl $(SRC_DIR)\tls
copy tls\*.c $(SRC_DIR)\tls
mkdir $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.erl $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.c $(SRC_DIR)\ejabberd_zlib
mkdir $(SRC_DIR)\web
copy web\*.erl $(SRC_DIR)\web
mkdir $(SRC_DIR)\odbc
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)
@@ -96,22 +92,15 @@ all-recursive :
nmake -nologo -f Makefile.win32
cd ..\stringprep
nmake -nologo -f Makefile.win32
cd ..\tls
nmake -nologo -f Makefile.win32
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32
cd ..\web
nmake -nologo -f Makefile.win32
cd ..\odbc
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 :
@@ -120,9 +109,6 @@ clean-local :
-@erase expat_erl.exp
-@erase expat_erl.lib
-@erase *.beam
-@erase XmppAddr.asn1db
-@erase XmppAddr.erl
-@erase XmppAddr.hrl
clean-recursive :
cd eldap
@@ -135,15 +121,11 @@ clean-recursive :
nmake -nologo -f Makefile.win32 clean
cd ..\stringprep
nmake -nologo -f Makefile.win32 clean
cd ..\tls
nmake -nologo -f Makefile.win32 clean
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32 clean
cd ..\web
nmake -nologo -f Makefile.win32 clean
cd ..\odbc
cd ..\win32\5.3
nmake -nologo -f Makefile.win32 clean
cd ..
cd ..\..
distclean : release_clean clean
-@erase $(NSIS_HEADER)
-14
View File
@@ -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
+29 -82
View File
@@ -11,12 +11,12 @@
-vsn('$Revision$ ').
-export([start/0,
to_record/3,
add/3,
add_list/3,
match_rule/3,
to_record/2,
add/2,
add_list/2,
match_rule/2,
% for debugging only
match_acl/3]).
match_acl/2]).
-include("ejabberd.hrl").
@@ -30,35 +30,30 @@ start() ->
mnesia:add_table_copy(acl, node(), ram_copies),
ok.
to_record(Host, ACLName, ACLSpec) ->
#acl{aclname = {ACLName, Host}, aclspec = ACLSpec}.
to_record(ACLName, ACLSpec) ->
#acl{aclname = ACLName, aclspec = ACLSpec}.
add(Host, ACLName, ACLSpec) ->
add(ACLName, ACLSpec) ->
F = fun() ->
mnesia:write(#acl{aclname = {ACLName, Host},
aclspec = ACLSpec})
mnesia:write(#acl{aclname = ACLName, aclspec = ACLSpec})
end,
mnesia:transaction(F).
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 = ACLSpec})
#acl{} ->
mnesia:write(ACL)
end
end, ACLs)
end,
@@ -71,61 +66,30 @@ add_list(Host, ACLs, Clear) ->
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.
match_acls([], _, Host) ->
match_acls([], _) ->
deny;
match_acls([{Access, ACL} | ACLs], JID, Host) ->
case match_acl(ACL, 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.
match_acl(ACL, JID, Host) ->
match_acl(ACL, JID) ->
case ACL of
all -> true;
none -> false;
@@ -136,20 +100,14 @@ match_acl(ACL, JID, Host) ->
all ->
true;
{user, U} ->
(U == User)
andalso
((Host == Server) orelse
((Host == global) andalso
lists:member(Server, ?MYHOSTS)));
(U == User) andalso (?MYNAME == Server);
{user, U, S} ->
(U == User) andalso (S == Server);
{server, S} ->
S == Server;
{user_regexp, UR} ->
((Host == Server) orelse
((Host == global) andalso
lists:member(Server, ?MYHOSTS)))
andalso is_regexp_match(User, UR);
(?MYNAME == Server) andalso
is_regexp_match(User, UR);
{user_regexp, UR, S} ->
(S == Server) andalso
is_regexp_match(User, UR);
@@ -159,10 +117,7 @@ match_acl(ACL, JID, Host) ->
is_regexp_match(Server, SR) andalso
is_regexp_match(User, UR);
{user_glob, UR} ->
((Host == Server) orelse
((Host == global) andalso
lists:member(Server, ?MYHOSTS)))
andalso
(?MYNAME == Server) andalso
is_glob_match(User, UR);
{user_glob, UR, S} ->
(S == Server) andalso
@@ -171,17 +126,9 @@ match_acl(ACL, JID, Host) ->
is_glob_match(Server, SR);
{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, {ACL, global}) ++
ets:lookup(acl, {ACL, Host}))
end, ets:lookup(acl, ACL))
end.
is_regexp_match(String, RegExp) ->
+16 -115
View File
@@ -1,14 +1,15 @@
AC_DEFUN(AM_WITH_EXPAT,
[ AC_ARG_WITH(expat,
[ --with-expat=PREFIX prefix where EXPAT is installed])
[ --with-expat=PREFIX path to expat library],
, with_expat=no)
EXPAT_CFLAGS=
EXPAT_LIBS=
if test x"$with_expat" != x; then
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 ],
@@ -24,42 +25,12 @@ AC_DEFUN(AM_WITH_EXPAT,
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_ZLIB,
[ AC_ARG_WITH(zlib,
[ --with-zlib=PREFIX prefix where zlib is installed])
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
AC_CHECK_LIB(z, gzgets,
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes ],
[ zlib_found=no ],
"$ZLIB_LIBS")
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find the zlib library])
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find zlib.h])
fi
CFLAGS="$zlib_save_CFLAGS"
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
])
AC_DEFUN(AM_WITH_ERLANG,
[ AC_ARG_WITH(erlang,
[ --with-erlang=PREFIX path to erlc and erl ])
@@ -68,7 +39,7 @@ AC_DEFUN(AM_WITH_ERLANG,
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
if test "z$ERLC" == "z" || test "z$ERL" == "z"; then
AC_MSG_ERROR([erlang not found])
fi
@@ -77,44 +48,14 @@ AC_DEFUN(AM_WITH_ERLANG,
-module(conftest).
-author('alexey@sevcom.net').
-export([[start/0]]).
start() ->
EIDirS = code:lib_dir("erl_interface") ++ "\n",
EILibS = libpath("erl_interface") ++ "\n",
RootDirS = code:root_dir() ++ "\n",
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ RootDirS)),
halt().
%% 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.
EIDirS = code:lib_dir("erl_interface") ++ "\n",
RootDirS = code:root_dir() ++ "\n",
file:write_file("conftest.out", list_to_binary(EIDirS ++ RootDirS)),
halt().
_EOF
@@ -130,15 +71,11 @@ _EOF
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_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"
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib"
AC_SUBST(ERLANG_CFLAGS)
AC_SUBST(ERLANG_LIBS)
@@ -156,7 +93,7 @@ AC_ARG_ENABLE($1,
[ --enable-$1 enable $1 (default: $2)],
[mr_enable_$1="$enableval"],
[mr_enable_$1=$2])
if test "$mr_enable_$1" = "yes"; then
if test "$mr_enable_$1" = $2; then
$1=$1
make_$1=$1/Makefile
fi
@@ -173,8 +110,9 @@ 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=PREFIX prefix where libiconv is installed], [
[ --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
@@ -255,40 +193,3 @@ size_t iconv();
fi
AC_SUBST(LIBICONV)
])
dnl <openssl>
AC_DEFUN(AM_WITH_OPENSSL,
[ AC_ARG_WITH(openssl,
[ --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/openssl"
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/lib $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/lib $CPPFLAGS"
SSL_CFLAGS="-DHAVE_SSL"
break
fi
fi
done
if test x${have_openssl} != xyes; then
AC_MSG_ERROR([openssl library cannot be found. Install openssl or disable `tls' module (--disable-tls).])
fi
AC_SUBST(SSL_LIBS)
AC_SUBST(SSL_CFLAGS)
fi
])
dnl <openssl/>
-111
View File
@@ -1,111 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision$ ').
-export([parse_request/1,
produce_response/2,
produce_response/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
%% Parse an ad-hoc request. Return either an adhoc_request record or
%% an {error, ErrorType} tuple.
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
Node = xml:get_tag_attr_s("node", SubEl),
SessionID = xml:get_tag_attr_s("sessionid", SubEl),
Action = xml:get_tag_attr_s("action", SubEl),
XData = find_xdata_el(SubEl),
{xmlelement, _, _, AllEls} = SubEl,
if XData ->
Others = lists:delete(XData, AllEls);
true ->
Others = AllEls
end,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData,
others = Others};
parse_request(_) ->
{error, ?ERR_BAD_REQUEST}.
%% Borrowed from mod_vcard.erl
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
find_xdata_el1([]) ->
false;
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_XDATA ->
{xmlelement, Name, Attrs, SubEls};
_ ->
find_xdata_el1(Els)
end;
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).
%% 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 = [{"execute", DefaultAction}]
end,
ActionsEls = [{xmlelement, "actions",
ActionsElAttrs,
[{xmlelement, Action, [], []} || Action <- Actions]}]
end,
NotesEls = lists:map(fun({Type, Text}) ->
{xmlelement, "note",
[{"type", Type}],
[{xmlcdata, Text}]}
end, Notes),
{xmlelement, "command",
[{"xmlns", ?NS_COMMANDS},
{"sessionid", SessionID},
{"node", Node},
{"status", atom_to_list(Status)}],
ActionsEls ++ NotesEls ++ Elements}.
-15
View File
@@ -1,15 +0,0 @@
-record(adhoc_request, {lang,
node,
sessionid,
action,
xdata,
others}).
-record(adhoc_response, {lang,
node,
sessionid,
status,
defaultaction = "",
actions = [],
notes = [],
elements = []}).
+2586 -4585
View File
File diff suppressed because it is too large Load Diff
+6 -48
View File
@@ -14,72 +14,30 @@ AM_WITH_ERLANG
AM_ICONV
#locating libexpat
AM_WITH_EXPAT
#locating zlib
AM_WITH_ZLIB
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
# Change default prefix
AC_PREFIX_DEFAULT /
# Checks for library functions.
AC_FUNC_MALLOC
AC_HEADER_STDC
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(web, yes)
AC_MOD_ENABLE(tls, yes)
AC_MOD_ENABLE(odbc, no)
AC_MOD_ENABLE(ejabberd_zlib, yes)
AC_ARG_ENABLE(roster_gateway_workaround,
[ --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(mssql,
[ --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_CONFIG_FILES([Makefile
$make_mod_irc
$make_mod_muc
$make_mod_pubsub
$make_eldap
$make_web
stringprep/Makefile
$make_tls
$make_odbc
$make_ejabberd_zlib])
#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,,)
stringprep/Makefile])
AC_OUTPUT
+4 -16
View File
@@ -28,45 +28,33 @@ start() ->
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",
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n";
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n";
false ->
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",
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib1.lib\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",
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
Version = "EJABBERD_VERSION = " ++ ?VERSION ++ "\n",
ExpatDir = "EXPAT_DIR = c:\\progra~1\\expat-1.95.7\n",
OpenSSLDir = "OPENSSL_DIR = c:\\progra~1\\OpenSSL\n",
DBType = "DBTYPE = generic\n", %% 'generic' or 'mssql'
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 ++
ExpatDir ++
ExpatLib ++
ExpatFlag ++
IconvDir ++
IconvLib ++
ZlibDir ++
ZlibLib)),
IconvLib)),
halt().
+23 -56
View File
@@ -11,21 +11,20 @@
-vsn('$Revision$ ').
-export([start/0,
register_mechanism/3,
listmech/1,
server_new/6,
register_mechanism/2,
listmech/0,
server_new/4,
server_start/3,
server_step/2]).
-record(sasl_mechanism, {mechanism, module, require_plain_password}).
-record(sasl_state, {service, myname, realm,
get_password, check_password,
mech_mod, mech_state}).
-record(sasl_mechanism, {mechanism, module}).
-record(sasl_state, {service, myname, realm, mech_mod, mech_state}).
-export([behaviour_info/1]).
behaviour_info(callbacks) ->
[{mech_new, 3}, {mech_step, 2}];
[{mech_new, 0},
{mech_step, 2}];
behaviour_info(Other) ->
undefined.
@@ -35,14 +34,11 @@ start() ->
{keypos, #sasl_mechanism.mechanism}]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
cyrsasl_anonymous:start([]),
ok.
register_mechanism(Mechanism, Module, RequirePlainPassword) ->
ets:insert(sasl_mechanism,
#sasl_mechanism{mechanism = Mechanism,
module = Module,
require_plain_password = RequirePlainPassword}).
register_mechanism(Mechanism, Module) ->
ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism,
module = Module}).
% TODO: use callbacks
-include("ejabberd.hrl").
@@ -79,46 +75,24 @@ check_credentials(State, Props) ->
ok
end.
listmech(Host) ->
RequirePlainPassword = ejabberd_auth:plain_password_required(Host),
Mechs = ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1',
require_plain_password = '$2',
_ = '_'},
if
RequirePlainPassword ->
[{'==', '$2', false}];
true ->
[]
end,
['$1']}]),
filter_anonymous(Host, Mechs).
listmech() ->
ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1', _ = '_'}, [], ['$1']}]).
server_new(Service, ServerFQDN, UserRealm, SecFlags,
GetPassword, CheckPassword) ->
server_new(Service, ServerFQDN, UserRealm, SecFlags) ->
#sasl_state{service = Service,
myname = ServerFQDN,
realm = UserRealm,
get_password = GetPassword,
check_password = CheckPassword}.
realm = UserRealm}.
server_start(State, Mech, ClientIn) ->
case lists:member(Mech, listmech(State#sasl_state.myname)) of
true ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
{ok, MechState} = Module:mech_new(
State#sasl_state.myname,
State#sasl_state.get_password,
State#sasl_state.check_password),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);
_ ->
{error, "no-mechanism"}
end;
false ->
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.
@@ -140,10 +114,3 @@ server_step(State, ClientIn) ->
{error, Error}
end.
%% 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.
-39
View File
@@ -1,39 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_anonymous.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : ANONYMOUS SASL mechanism
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
-module(cyrsasl_anonymous).
-vsn('$Revision$').
-export([start/1, stop/0, mech_new/3, mech_step/2]).
-behaviour(cyrsasl).
-record(state, {server}).
start(_Opts) ->
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
ok.
stop() ->
ok.
mech_new(Host, _GetPassword, _CheckPassword) ->
{ok, #state{server = Host}}.
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}]}
end.
+26 -25
View File
@@ -12,28 +12,35 @@
-export([start/1,
stop/0,
mech_new/3,
mech_new/0,
mech_step/2]).
-behaviour(cyrsasl).
%-behaviour(gen_mod).
-record(state, {step, nonce, username, authzid, get_password}).
-record(state, {step, nonce, username, authzid}).
start(_Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
start(Opts) ->
case ejabberd_auth:plain_password_required() of
true ->
ok;
false ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
ok
end.
stop() ->
ok.
mech_new(_Host, GetPassword, _CheckPassword) ->
mech_new() ->
{ok, #state{step = 1,
nonce = randoms:get_string(),
get_password = GetPassword}}.
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
@@ -42,9 +49,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
KeyVals ->
UserName = xml:get_attr_s("username", KeyVals),
AuthzId = xml:get_attr_s("authzid", KeyVals),
case (State#state.get_password)(UserName) of
case ejabberd_auth:get_password(UserName) of
false ->
{error, "not-authorized"};
{error, "no-user"};
Passwd ->
Response = response(KeyVals, UserName, Passwd,
Nonce, AuthzId, "AUTHENTICATE"),
@@ -59,13 +66,13 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
username = UserName,
authzid = AuthzId}};
_ ->
{error, "not-authorized"}
{error, "bad-auth"}
end
end
end;
mech_step(#state{step = 5,
username = UserName,
authzid = AuthzId}, "") ->
authzid = AuthzId} = State, "") ->
{ok, [{username, UserName}, {authzid, AuthzId}]};
mech_step(A, B) ->
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
@@ -77,15 +84,11 @@ parse(S) ->
parse1([$= | Cs], S, Ts) ->
parse2(Cs, lists:reverse(S), "", Ts);
parse1([$, | Cs], [], Ts) ->
parse1(Cs, [], Ts);
parse1([$\s | Cs], [], Ts) ->
parse1(Cs, [], Ts);
parse1([C | Cs], S, Ts) ->
parse1(Cs, [C | S], Ts);
parse1([], [], T) ->
lists:reverse(T);
parse1([], _S, _T) ->
parse1([], S, T) ->
bad.
parse2([$" | Cs], Key, Val, Ts) ->
@@ -104,8 +107,6 @@ parse3([], _, _, _) ->
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) ->
@@ -147,13 +148,13 @@ response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
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))),
+7 -8
View File
@@ -10,26 +10,25 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/1, stop/0, mech_new/3, mech_step/2, parse/1]).
-export([start/1, stop/0, mech_new/0, mech_step/2, parse/1]).
-behaviour(cyrsasl).
%-behaviour(gen_mod).
-record(state, {check_password}).
start(_Opts) ->
cyrsasl:register_mechanism("PLAIN", ?MODULE, false),
start(Opts) ->
cyrsasl:register_mechanism("PLAIN", ?MODULE),
ok.
stop() ->
ok.
mech_new(_Host, _GetPassword, CheckPassword) ->
{ok, #state{check_password = CheckPassword}}.
mech_new() ->
{ok, []}.
mech_step(State, ClientIn) ->
case parse(ClientIn) of
[AuthzId, User, Password] ->
case (State#state.check_password)(User, Password) of
case ejabberd_auth:check_password(User, Password) of
true ->
{ok, [{username, User}, {authzid, AuthzId}]};
_ ->
+1 -1
View File
@@ -2,7 +2,7 @@
{application, ejabberd,
[{description, "ejabberd"},
{vsn, "1.1.2"},
{vsn, "0.1-alpha"},
{modules, [acl,
configure,
cyrsasl,
+9 -55
View File
@@ -20,15 +20,11 @@
%{acl, test, {user_regexp, "^test"}}.
%{acl, test, {user_glob, "test*"}}.
% Everybody can create pubsub nodes
{access, pubsub_createnode, [{allow, all}]}.
% Only admins can use configuration interface:
{access, configure, [{allow, admin}]}.
% Every username can be registered via in-band registration:
% You could replace {allow, all} with {deny, all} to prevent user from using
% in-band registration
{access, register, [{allow, all}]}.
% After successful registration user will get message with following subject
@@ -75,73 +71,35 @@
{access, local, [{allow, local}]}.
% Authentication method. If you want to use internal user base, then use
% Authentification method. If you want to use internal user base, then use
% this line:
{auth_method, internal}.
% For LDAP authentication use these lines instead of above one:
% 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"}. % Search base of LDAP directory
%{ldap_rootdn, "dc=example,dc=com"}. % LDAP manager
%{ldap_password, "******"}. % Password to LDAP manager
% For authentication via external script use the following:
%{auth_method, external}.
%{extauth_program, "/path/to/authentication/script"}.
% For authentication via ODBC use the following:
%{auth_method, odbc}.
%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
%{ldap_base, "dc=example,dc=com"}. % Base of LDAP directory
% Host name:
{hosts, ["localhost"]}.
{host, "localhost"}.
%% Define the maximum number of time a single user is allowed to connect:
{max_user_sessions, 10}.
%% 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, [anonymous, internal]}]}.
% Default language for server messages
{language, "en"}.
% Listened ports:
{listen,
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper},
{max_stanza_size, 65536},
starttls, {certfile, "./ssl.pem"}]},
{5223, ejabberd_c2s, [{access, c2s},
{max_stanza_size, 65536},
tls, {certfile, "./ssl.pem"}]},
% Use these two lines instead if TLS support is not compiled
%{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]},
%{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper},
{max_stanza_size, 131072}
]},
[{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"}]}]}
]}.
% Use STARTTLS+Dialback for S2S connections
{s2s_use_starttls, true}.
{s2s_certfile, "./ssl.pem"}.
%{domain_certfile, "example.org", "./example_org.pem"}.
%{domain_certfile, "example.com", "./example_com.pem"}.
% If SRV lookup fails, then port 5269 is used to communicate with remote server
{outgoing_s2s_port, 5269}.
@@ -152,14 +110,12 @@
{mod_register, [{access, register}]},
{mod_roster, []},
{mod_privacy, []},
{mod_adhoc, []},
{mod_configure, []}, % Depends on mod_adhoc
{mod_configure, []},
{mod_configure2, []},
{mod_disco, []},
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
{mod_announce, [{access, announce}]}, % Depends on mod_adhoc
{mod_echo, [{host, "echo.localhost"}]},
{mod_private, []},
{mod_irc, []},
@@ -171,9 +127,7 @@
{mod_muc, [{access, muc},
{access_create, muc},
{access_admin, muc_admin}]},
% {mod_muc_log, []},
% {mod_shared_roster, []},
{mod_pubsub, [{access_createnode, pubsub_createnode}]},
{mod_pubsub, []},
{mod_time, []},
{mod_last, []},
{mod_version, []}
+4 -3
View File
@@ -6,7 +6,7 @@
%%% Id : $Id$
%%%----------------------------------------------------------------------
-define(VERSION, "1.1.3").
-define(VERSION, "0.6-alpha").
%-define(ejabberd_debug, true).
%-define(DBGFSM, true).
@@ -27,9 +27,10 @@
[self(),?MODULE,?LINE]++Args)).
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
%-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").
-59
View File
@@ -1,59 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_admin.erl
%%% Author : Mickael Remond <mremond@process-one.net>
%%% Description : This module gathers admin functions used by different
%%% access method:
%%% - ejabberdctl command-line tool
%%% - web admin interface
%%% - adhoc mode
%%%
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
%%% Id : $Id: $
%%%----------------------------------------------------------------------
%%% Copyright (c) 2006 Process One
%%%-------------------------------------------------------------------
-module(ejabberd_admin).
-author('mickael.remond@process-one.net').
-vsn('$Revision: $ ').
-export([restore/1]).
-include("ejabberd.hrl").
%% 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, disco_publish,
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) -> [].
+18 -19
View File
@@ -14,6 +14,8 @@
-export([start/2, stop/1, init/0]).
-export([dump_ports/0]).
-include("ejabberd.hrl").
start(normal, _Args) ->
@@ -24,16 +26,15 @@ start(normal, _Args) ->
catch ssl:start(),
translate:start(),
acl:start(),
ejabberd_ctl:init(),
gen_mod:start(),
ejabberd_config:start(),
Sup = ejabberd_sup:start_link(),
ejabberd_auth:start(),
cyrsasl:start(),
% Profiling
%eprof:start(),
%eprof:profile([self()]),
%fprof:trace(start, "/tmp/fprof"),
Sup = ejabberd_sup:start_link(),
start(),
load_modules(),
Sup;
@@ -63,11 +64,8 @@ init() ->
end
end,
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of
ok -> ok;
{error, already_loaded} -> ok
end,
%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).
@@ -89,16 +87,17 @@ db_init() ->
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
load_modules() ->
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).
case ejabberd_config:get_local_option(modules) of
undefined ->
ok;
Modules ->
lists:foreach(fun({Module, Args}) ->
gen_mod:start_module(Module, Args)
end, Modules)
end.
dump_ports() ->
?INFO_MSG("ports:~n ~p",
[lists:map(fun(P) -> erlang:port_info(P) end, erlang:ports())]).
+327 -144
View File
@@ -1,176 +1,359 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification
%%% Purpose :
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Updated : 23 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%% for anonymous login support
%%% Id : $Id$
%%%----------------------------------------------------------------------
%% TODO: Use the functions in ejabberd auth to add and remove users.
-module(ejabberd_auth).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% External exports
-export([start/0,
set_password/3,
check_password/3,
check_password/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_password/2,
get_password_s/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,
ctl_process_get_registered/3
plain_password_required/0,
check_password_ldap/2, % TODO: remove
is_user_exists_ldap/1 % TODO: remove
]).
-export([auth_modules/1]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
code_change/3,
handle_info/2,
terminate/2]).
-include("ejabberd.hrl").
-include("ejabberd_ctl.hrl").
-include("eldap/eldap.hrl").
-record(state, {}).
-record(passwd, {user, password}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start() ->
lists:foreach(
fun(Host) ->
lists:foreach(
fun(M) ->
M:start(Host)
end, auth_modules(Host))
end, ?MYHOSTS).
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
start_link() ->
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
plain_password_required(Server) ->
lists:any(
fun(M) ->
M:plain_password_required()
end, auth_modules(Server)).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------------
check_password(User, Server, Password) ->
lists:any(
fun(M) ->
M:check_password(User, Server, Password)
end, auth_modules(Server)).
%%----------------------------------------------------------------------
%% 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,
{ok, #state{}}.
check_password(User, Server, Password, StreamID, Digest) ->
lists:any(
fun(M) ->
M:check_password(User, Server, Password, StreamID, Digest)
end, auth_modules(Server)).
%%----------------------------------------------------------------------
%% 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}.
set_password(User, Server, Password) ->
lists:foldl(
fun(M, {error, _}) ->
M:set_password(User, Server, Password);
(_M, Res) ->
Res
end, {error, not_allowed}, auth_modules(Server)).
try_register(User, Server, Password) ->
case is_user_exists(User,Server) of
true ->
{atomic, exists};
false ->
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
true ->
lists:foldl(
fun(_M, {atomic, ok} = Res) ->
Res;
(M, _) ->
M:try_register(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server));
false ->
{error, not_allowed}
end
end.
%% 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(?MYNAME)).
%% Registered users list do not include anonymous users logged
get_vh_registered_users(Server) ->
lists:flatmap(
fun(M) ->
M:get_vh_registered_users(Server)
end, auth_modules(Server)).
get_password(User, Server) ->
lists:foldl(
fun(M, false) ->
M:get_password(User, Server);
(_M, Password) ->
Password
end, false, auth_modules(Server)).
get_password_s(User, Server) ->
case get_password(User, Server) of
false ->
"";
Password ->
Password
end.
%% Returns true if the user exists in the DB or if an anonymous user is logged
%% under the given name
is_user_exists(User, Server) ->
lists:any(
fun(M) ->
M:is_user_exists(User, Server)
end, auth_modules(Server)).
%% Check if the user exists in all authentications module except the module
%% passed as parameter
is_user_exists_in_other_modules(Module, User, Server) ->
lists:any(
fun(M) ->
M:is_user_exists(User, Server)
end, auth_modules(Server)--[Module]).
remove_user(User, Server) ->
lists:foreach(
fun(M) ->
M:remove_user(User, Server)
end, auth_modules(Server)).
remove_user(User, Server, Password) ->
lists:foreach(
fun(M) ->
M:remove_user(User, Server, Password)
end, auth_modules(Server)).
%%----------------------------------------------------------------------
%% Func: handle_cast/2
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
ctl_process_get_registered(_Val, Host, ["registered-users"]) ->
Users = ejabberd_auth:get_vh_registered_users(Host),
NewLine = io_lib:format("~n", []),
SUsers = lists:sort(Users),
FUsers = lists:map(fun({U, _S}) -> [U, NewLine] end, SUsers),
io:format("~s", [FUsers]),
{stop, ?STATUS_SUCCESS};
ctl_process_get_registered(Val, _Host, _Args) ->
Val.
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
%%%----------------------------------------------------------------------
auth_modules(Server) ->
LServer = jlib:nameprep(Server),
Method = ejabberd_config:get_local_option({auth_method, LServer}),
Methods = if
Method == undefined -> [];
is_list(Method) -> Method;
is_atom(Method) -> [Method]
end,
[list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].
auth_method() ->
case ejabberd_config:get_local_option(auth_method) of
ldap ->
ldap;
_ ->
internal
end.
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.
-221
View File
@@ -1,221 +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>
%%%
%%% Anonymous support is based on the work of Magnus Henoch
%%% <henoch@dtek.chalmers.se> and heavily extended by Process-one.
%%%
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_anonymous).
-author('mickael.remond@process-one.net').
-vsn('$Revision$ ').
-export([start/1,
allow_anonymous/1,
is_sasl_anonymous_enabled/1,
is_login_anonymous_enabled/1,
anonymous_user_exist/2,
allow_multiple_connections/1,
register_connection/2,
unregister_connection/2
]).
%% 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,
plain_password_required/0]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(anonymous, {us, sid}).
%% Create the anonymous table if at least one virtual host has anonymous features enabled
%% Register to login / logout events
start(Host) ->
%% TODO: Check cluster mode
mnesia:create_table(anonymous, [{ram_copies, [node()]},
{type, bag},
{attributes, record_info(fields, anonymous)}]),
%% The hooks are needed to add / remove users from the anonymous tables
ejabberd_hooks:add(sm_register_connection_hook, Host,
?MODULE, register_connection, 100),
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, unregister_connection, 100),
ok.
%% Return true if anonymous is allowed for host or false otherwise
allow_anonymous(Host) ->
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
%% Return true if anonymous mode is enabled and if anonymous protocol is SASL
%% anonymous protocol can be: sasl_anon|login_anon|both
is_sasl_anonymous_enabled(Host) ->
case allow_anonymous(Host) of
false -> false;
true ->
case anonymous_protocol(Host) of
sasl_anon -> true;
both -> true;
_Other -> false
end
end.
%% 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) ->
case allow_anonymous(Host) of
false -> false;
true ->
case anonymous_protocol(Host) of
login_anon -> true;
both -> true;
_Other -> false
end
end.
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
%% defaults to login_anon
anonymous_protocol(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.
%% Return true if multiple connections have been allowed in the config file
%% defaults to false
allow_multiple_connections(Host) ->
case ejabberd_config:get_local_option({allow_multiple_connections, Host}) of
true -> true;
_Other -> false
end.
%% Check if user exist in the anonymus database
anonymous_user_exist(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({anonymous, US}) of
[] ->
false;
[_H|_T] ->
true
end.
%% Remove connection from Mnesia tables
remove_connection(SID, LUser, LServer) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete_object({anonymous, US, SID})
end,
mnesia:transaction(F).
%% Register connection
register_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
US = {LUser, LServer},
mnesia:sync_dirty(
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
end).
%% Remove an anonymous user from the anonymous users table
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
remove_connection(SID, LUser, LServer).
%% ---------------------------------
%% Specific anonymous auth functions
%% ---------------------------------
%% 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, _StreamID, _Digest) ->
%% 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
true -> false;
false -> login(User, Server)
end.
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.
%% 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.
%% When anonymous login is enabled, check if permanent users are allowed on
%% the server:
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
dirty_get_registered_users() ->
[].
get_vh_registered_users(_Server) ->
[].
%% Return password of permanent user or false for anonymous users
get_password(User, Server) ->
get_password(User, Server, "").
get_password(User, Server, DefaultValue) ->
case anonymous_user_exist(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.
%% 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).
remove_user(_User, _Server) ->
{error, not_allowed}.
remove_user(_User, _Server, _Password) ->
not_allowed.
plain_password_required() ->
false.
-72
View File
@@ -1,72 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_external.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification via LDAP external script
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_external).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
%% External exports
-export([start/1,
set_password/3,
check_password/3,
check_password/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
get_password/2,
get_password_s/2,
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
extauth:start(
Host, ejabberd_config:get_local_option({extauth_program, Host})),
ok.
plain_password_required() ->
true.
check_password(User, Server, Password) ->
extauth:check_password(User, Server, Password).
check_password(User, Server, Password, _StreamID, _Digest) ->
check_password(User, Server, Password).
set_password(User, Server, Password) ->
extauth:set_password(User, Server, Password).
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
dirty_get_registered_users() ->
[].
get_vh_registered_users(_Server) ->
[].
get_password(_User, _Server) ->
false.
get_password_s(_User, _Server) ->
"".
is_user_exists(User, Server) ->
extauth:is_user_exists(User, Server).
remove_user(_User, _Server) ->
{error, not_allowed}.
remove_user(_User, _Server, _Password) ->
not_allowed.
-243
View File
@@ -1,243 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_internal.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification via mnesia
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_internal).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
%% External exports
-export([start/1,
set_password/3,
check_password/3,
check_password/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
get_password/2,
get_password_s/2,
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
-include("ejabberd.hrl").
-record(passwd, {us, password}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
mnesia:create_table(passwd, [{disc_copies, [node()]},
{attributes, record_info(fields, passwd)}]),
update_table(),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
ok.
plain_password_required() ->
false.
check_password(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Password}] ->
true;
_ ->
false
end.
check_password(User, Server, Password, StreamID, Digest) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[#passwd{password = Passwd}] ->
DigRes = if
Digest /= "" ->
Digest == sha:sha(StreamID ++ Passwd);
true ->
false
end,
if DigRes ->
true;
true ->
(Passwd == Password) and (Password /= "")
end;
_ ->
false
end.
set_password(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
if
(LUser == error) or (LServer == error) ->
{error, invalid_jid};
true ->
F = fun() ->
mnesia:write(#passwd{us = US,
password = Password})
end,
mnesia:transaction(F)
end.
try_register(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
if
(LUser == error) or (LServer == error) ->
{error, invalid_jid};
true ->
F = fun() ->
case mnesia:read({passwd, US}) of
[] ->
mnesia:write(#passwd{us = US,
password = Password}),
ok;
[_E] ->
exists
end
end,
mnesia:transaction(F)
end.
dirty_get_registered_users() ->
mnesia:dirty_all_keys(passwd).
get_vh_registered_users(Server) ->
LServer = jlib:nameprep(Server),
mnesia:dirty_select(
passwd,
[{#passwd{us = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]).
get_password(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read(passwd, US) of
[#passwd{password = Password}] ->
Password;
_ ->
false
end.
get_password_s(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read(passwd, US) of
[#passwd{password = Password}] ->
Password;
_ ->
[]
end.
is_user_exists(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_read({passwd, US}) of
[] ->
false;
[_] ->
true;
_ ->
false
end.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
mnesia:delete({passwd, US})
end,
mnesia:transaction(F),
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
remove_user(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
case mnesia:read({passwd, US}) of
[#passwd{password = Password}] ->
mnesia:delete({passwd, US}),
ok;
[_] ->
not_allowed;
_ ->
not_exists
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
ok;
{atomic, Res} ->
Res;
_ ->
bad_request
end.
update_table() ->
Fields = record_info(fields, passwd),
case mnesia:table_info(passwd, attributes) of
Fields ->
ok;
[user, password] ->
?INFO_MSG("Converting passwd table from "
"{user, password} format", []),
Host = ?MYNAME,
{atomic, ok} = mnesia:create_table(
ejabberd_auth_internal_tmp_table,
[{disc_only_copies, [node()]},
{type, bag},
{local_content, true},
{record_name, passwd},
{attributes, record_info(fields, passwd)}]),
mnesia:transform_table(passwd, ignore, Fields),
F1 = fun() ->
mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
mnesia:foldl(
fun(#passwd{us = U} = R, _) ->
mnesia:dirty_write(
ejabberd_auth_internal_tmp_table,
R#passwd{us = {U, Host}})
end, ok, passwd)
end,
mnesia:transaction(F1),
mnesia:clear_table(passwd),
F2 = fun() ->
mnesia:write_lock_table(passwd),
mnesia:foldl(
fun(R, _) ->
mnesia:dirty_write(R)
end, ok, ejabberd_auth_internal_tmp_table)
end,
mnesia:transaction(F2),
mnesia:delete_table(ejabberd_auth_internal_tmp_table);
_ ->
?INFO_MSG("Recreating passwd table", []),
mnesia:transform_table(passwd, ignore, Fields)
end.
-385
View File
@@ -1,385 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_ldap.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification via LDAP
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_ldap).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-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_password/2,
get_password_s/2,
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
-include("ejabberd.hrl").
-include("eldap/eldap.hrl").
-record(state, {host,
eldap_id,
bind_eldap_id,
servers,
port,
dn,
password,
base,
uidattr,
uidattr_format,
ufilter,
sfilter,
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}.
%% -----
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
ChildSpec = {
Proc, {?MODULE, start_link, [Host]},
permanent, 1000, worker, [?MODULE]
},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
start_link(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
gen_server:start_link({local, Proc}, ?MODULE, Host, []).
terminate(_Reason, State) ->
ejabberd_ctl:unregister_commands(
State#state.host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered).
init(Host) ->
State = parse_options(Host),
eldap:start_link(State#state.eldap_id,
State#state.servers,
State#state.port,
State#state.dn,
State#state.password),
eldap:start_link(State#state.bind_eldap_id,
State#state.servers,
State#state.port,
State#state.dn,
State#state.password),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
{ok, State}.
-define(REPLY_TIMEOUT, 10000).
plain_password_required() ->
true.
check_password(User, Server, Password) ->
Proc = gen_mod:get_module_proc(Server, ?MODULE),
case catch gen_server:call(Proc,
{check_pass, User, Password}, ?REPLY_TIMEOUT) of
{'EXIT', _} ->
false;
Result ->
Result
end.
check_password(User, Server, Password, _StreamID, _Digest) ->
check_password(User, Server, Password).
set_password(_User, _Server, _Password) ->
{error, not_allowed}.
try_register(_User, _Server, _Password) ->
{error, not_allowed}.
dirty_get_registered_users() ->
get_vh_registered_users(?MYNAME).
get_vh_registered_users(Server) ->
Proc = gen_mod:get_module_proc(Server, ?MODULE),
case catch gen_server:call(Proc,
get_vh_registered_users, ?REPLY_TIMEOUT) of
{'EXIT', _} ->
[];
Result ->
Result
end.
get_password(_User, _Server) ->
false.
get_password_s(_User, _Server) ->
"".
is_user_exists(User, Server) ->
Proc = gen_mod:get_module_proc(Server, ?MODULE),
case catch gen_server:call(Proc,
{is_user_exists, User}, ?REPLY_TIMEOUT) of
{'EXIT', _} ->
false;
Result ->
Result
end.
remove_user(_User, _Server) ->
{error, not_allowed}.
remove_user(_User, _Server, _Password) ->
not_allowed.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
handle_call({check_pass, User, Password}, _From, State) ->
Reply = case find_user_dn(User, State) of
false ->
false;
DN ->
case eldap:bind(State#state.bind_eldap_id, DN, Password) of
ok -> true;
_ -> false
end
end,
{reply, Reply, State};
handle_call(get_vh_registered_users, _From, State) ->
UA = State#state.uidattr,
UAF = State#state.uidattr_format,
Eldap_ID = State#state.eldap_id,
Server = State#state.host,
SortedDNAttrs = usort_attrs(State#state.dn_filter_attrs),
Reply = case eldap_filter:parse(State#state.sfilter) of
{ok, EldapFilter} ->
case eldap:search(Eldap_ID, [{base, State#state.base},
{filter, EldapFilter},
{attributes, SortedDNAttrs}]) of
#eldap_search_result{entries = Entries} ->
lists:flatmap(
fun(#eldap_entry{attributes = Attrs,
object_name = DN}) ->
case is_valid_dn(DN, Attrs, State) of
false -> [];
_ ->
case get_ldap_attr(UA, Attrs) of
"" -> [];
User ->
case get_user_part(User, UAF) of
{ok, U} ->
case jlib:nodeprep(U) of
error -> [];
LU -> [{LU, jlib:nameprep(Server)}]
end;
_ -> []
end
end
end
end, Entries);
_ ->
[]
end;
_ ->
[]
end,
{reply, Reply, State};
handle_call({is_user_exists, User}, _From, State) ->
Reply = case find_user_dn(User, State) of
false -> false;
_DN -> true
end,
{reply, Reply, State};
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
handle_call(_Request, _From, State) ->
{reply, bad_request, State}.
find_user_dn(User, State) ->
DNAttrs = usort_attrs(State#state.dn_filter_attrs),
case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
{ok, Filter} ->
case eldap:search(State#state.eldap_id, [{base, State#state.base},
{filter, Filter},
{attributes, DNAttrs}]) of
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
object_name = DN} | _]} ->
is_valid_dn(DN, Attrs, State);
_ ->
false
end;
_ ->
false
end.
is_valid_dn(DN, _, #state{dn_filter = undefined}) ->
DN;
is_valid_dn(DN, Attrs, State) ->
DNAttrs = State#state.dn_filter_attrs,
UA = State#state.uidattr,
UAF = State#state.uidattr_format,
Values = [{"%s", get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
SubstValues = case get_ldap_attr(UA, Attrs) of
"" -> Values;
S ->
case 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:search(State#state.eldap_id, [
{base, State#state.base},
{filter, EldapFilter},
{attributes, ["dn"]}]) of
#eldap_search_result{entries = [_|_]} ->
DN;
_ ->
false
end;
_ ->
false
end.
%%%----------------------------------------------------------------------
%%% Auxiliary functions
%%%----------------------------------------------------------------------
get_user_part(String, Pattern) ->
F = fun(S, P) ->
First = string:str(P, "%u"),
TailLength = length(P) - (First+1),
string:sub_string(S, First, length(S) - TailLength)
end,
case catch F(String, Pattern) of
{'EXIT', _} ->
{error, badmatch};
Result ->
case regexp:sub(Pattern, "%u", Result) of
{ok, String, _} -> {ok, Result};
_ -> {error, badmatch}
end
end.
case_insensitive_match(X, Y) ->
X1 = stringprep:tolower(X),
Y1 = stringprep:tolower(Y),
if
X1 == Y1 -> true;
true -> false
end.
get_ldap_attr(LDAPAttr, Attributes) ->
Res = lists:filter(
fun({Name, _}) ->
case_insensitive_match(Name, LDAPAttr)
end, Attributes),
case Res of
[{_, [Value|_]}] -> Value;
_ -> ""
end.
usort_attrs(Attrs) when is_list(Attrs) ->
lists:usort(Attrs);
usort_attrs(_) ->
[].
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}),
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
undefined -> 389;
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,
UIDAttr = case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
undefined -> "uid";
UA -> UA
end,
UIDAttrFormat = case ejabberd_config:get_local_option({ldap_uidattr_format, Host}) of
undefined -> "%u";
UAF -> UAF
end,
SubFilter = "(" ++ UIDAttr ++ "=" ++ UIDAttrFormat ++ ")",
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> SubFilter;
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
end,
SearchFilter = eldap_filter:do_sub(UserFilter, [{"%u", "*"}]),
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
{DNFilter, DNFilterAttrs} =
case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of
undefined -> {undefined, undefined};
{DNF, DNFA} -> {DNF, DNFA}
end,
#state{host = Host,
eldap_id = Eldap_ID,
bind_eldap_id = Bind_Eldap_ID,
servers = LDAPServers,
port = LDAPPort,
dn = RootDN,
password = Password,
base = LDAPBase,
uidattr = UIDAttr,
uidattr_format = UIDAttrFormat,
ufilter = UserFilter,
sfilter = SearchFilter,
dn_filter = DNFilter,
dn_filter_attrs = DNFilterAttrs
}.
-215
View File
@@ -1,215 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_auth_odbc.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Authentification via ODBC
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_auth_odbc).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
%% External exports
-export([start/1,
set_password/3,
check_password/3,
check_password/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
get_password/2,
get_password_s/2,
is_user_exists/2,
remove_user/2,
remove_user/3,
plain_password_required/0
]).
-include("ejabberd.hrl").
-record(passwd, {user, password}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
ChildSpec =
{gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
{ejabberd_odbc_sup, start_link, [Host]},
temporary,
infinity,
supervisor,
[ejabberd_odbc_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec),
ejabberd_ctl:register_commands(
Host,
[{"registered-users", "list all registered users"}],
ejabberd_auth, ctl_process_get_registered),
ok.
plain_password_required() ->
false.
check_password(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
true;
_ ->
false
end
end.
check_password(User, Server, Password, StreamID, Digest) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Passwd}]} ->
DigRes = if
Digest /= "" ->
Digest == sha:sha(StreamID ++ Passwd);
true ->
false
end,
if DigRes ->
true;
true ->
(Passwd == Password) and (Password /= "")
end;
_ ->
false
end
end.
set_password(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
{error, invalid_jid};
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
catch odbc_queries:set_password_t(LServer, Username, Pass)
end.
try_register(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
{error, invalid_jid};
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
case catch odbc_queries:add_user(LServer, Username, Pass) of
{updated, 1} ->
{atomic, ok};
_ ->
{atomic, exists}
end
end.
dirty_get_registered_users() ->
get_vh_registered_users(?MYNAME).
get_vh_registered_users(Server) ->
LServer = jlib:nameprep(Server),
case catch odbc_queries:list_users(LServer) of
{selected, ["username"], Res} ->
[{U, LServer} || {U} <- Res];
_ ->
[]
end.
get_password(User, Server) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
false
end
end.
get_password_s(User, Server) ->
case jlib:nodeprep(User) of
error ->
"";
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{Password}]} ->
Password;
_ ->
""
end
end.
is_user_exists(User, Server) ->
case jlib:nodeprep(User) of
error ->
false;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
case catch odbc_queries:get_password(LServer, Username) of
{selected, ["password"], [{_Password}]} ->
true;
_ ->
false
end
end.
remove_user(User, Server) ->
case jlib:nodeprep(User) of
error ->
error;
LUser ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
catch odbc_queries:del_user(LServer, Username),
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server])
end.
remove_user(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
error;
LUser ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
LServer = jlib:nameprep(Server),
F = fun() ->
Result = odbc_queries:del_user_return_password(
LServer, Username, Pass),
case Result of
{selected, ["password"], [{Password}]} ->
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server]),
ok;
{selected, ["password"], []} ->
not_exists;
_ ->
not_allowed
end
end,
{atomic, Result} = odbc_queries:transaction(LServer, F),
Result
end.
+207 -629
View File
File diff suppressed because it is too large Load Diff
+9 -75
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_config.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Load config file
%%% Purpose :
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -19,12 +19,12 @@
-record(config, {key, value}).
-record(local_config, {key, value}).
-record(state, {opts = [],
hosts = [],
override_local = false,
override_global = false,
override_acls = false}).
start() ->
%ets:new(ejabberd_config, [named_table, public]),
mnesia:create_table(config,
[{disc_copies, [node()]},
{attributes, record_info(fields, config)}]),
@@ -36,13 +36,7 @@ start() ->
mnesia:add_table_copy(local_config, node(), ram_copies),
Config = case application:get_env(config) of
{ok, Path} -> Path;
undefined ->
case os:getenv("EJABBERD_CONFIG_PATH") of
false ->
?CONFIG_PATH;
Path ->
Path
end
undefined -> ?CONFIG_PATH
end,
load_file(Config).
@@ -50,38 +44,13 @@ start() ->
load_file(File) ->
case file:consult(File) of
{ok, Terms} ->
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
Res = lists:foldl(fun process_term/2, State, Terms),
Res = lists:foldl(fun process_term/2, #state{}, Terms),
set_opts(Res);
{error, Reason} ->
?ERROR_MSG("Can't load config file ~p: ~p", [File, Reason]),
exit(file:format_error(Reason))
end.
search_hosts(Term, State) ->
case Term of
{host, Host} ->
if
State#state.hosts == [] ->
add_option(hosts, [Host], State#state{hosts = [Host]});
true ->
?ERROR_MSG("Can't load config file: "
"too many hosts definitions", []),
exit("too many hosts definitions")
end;
{hosts, Hosts} ->
if
State#state.hosts == [] ->
add_option(hosts, Hosts, State#state{hosts = Hosts});
true ->
?ERROR_MSG("Can't load config file: "
"too many hosts definitions", []),
exit("too many hosts definitions")
end;
_ ->
State
end.
process_term(Term, State) ->
case Term of
override_global ->
@@ -90,59 +59,24 @@ process_term(Term, State) ->
State#state{override_local = true};
override_acls ->
State#state{override_acls = true};
{acl, ACLName, ACLData} ->
process_host_term(Term, global, State);
{access, RuleName, Rules} ->
process_host_term(Term, global, State);
{shaper, Name, Data} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts);
{host, Host} ->
State;
{hosts, Hosts} ->
State;
{host_config, Host, Terms} ->
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
State, Terms);
{listen, Val} ->
add_option(listen, Val, State);
{outgoing_s2s_port, Port} ->
add_option(outgoing_s2s_port, Port, State);
{s2s_use_starttls, Port} ->
add_option(s2s_use_starttls, Port, State);
{s2s_certfile, CertFile} ->
add_option(s2s_certfile, CertFile, State);
{domain_certfile, Domain, CertFile} ->
add_option({domain_certfile, Domain}, CertFile, State);
{Opt, Val} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts)
end.
process_host_term(Term, Host, State) ->
case Term of
{acl, ACLName, ACLData} ->
State#state{opts =
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
[acl:to_record(ACLName, ACLData) | State#state.opts]};
{access, RuleName, Rules} ->
State#state{opts = [#config{key = {access, RuleName, Host},
State#state{opts = [#config{key = {access, RuleName},
value = Rules} |
State#state.opts]};
{shaper, Name, Data} ->
State#state{opts = [#config{key = {shaper, Name, Host},
State#state{opts = [#config{key = {shaper, Name},
value = Data} |
State#state.opts]};
{host, Host} ->
State;
{hosts, Hosts} ->
State;
{Opt, Val} ->
add_option({Opt, Host}, Val, State)
add_option(Opt, Val, State)
end.
add_option(Opt, Val, State) ->
Table = case Opt of
hosts ->
host ->
config;
language ->
config;
+111 -291
View File
@@ -8,344 +8,164 @@
-module(ejabberd_ctl).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/0,
init/0,
process/1,
register_commands/3,
register_commands/4,
unregister_commands/3,
unregister_commands/4]).
-include("ejabberd_ctl.hrl").
-include("ejabberd.hrl").
-export([start/0]).
start() ->
case init:get_plain_arguments() of
[SNode | Args] ->
Node = list_to_atom(SNode),
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
{badrpc, Reason} ->
io:format("RPC failed on the node ~p: ~p~n",
[Node, Reason]),
?STATUS_BADRPC;
S ->
S
end,
halt(Status);
process(Node, Args);
_ ->
print_usage(),
halt(?STATUS_USAGE)
end.
init() ->
ets:new(ejabberd_ctl_cmds, [named_table, set, public]),
ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]).
print_usage()
end,
halt().
process(["status"]) ->
{InternalStatus, ProvidedStatus} = init:get_status(),
io:format("Node ~p is ~p. Status: ~p~n",
[node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of
false ->
io:format("ejabberd is not running~n", []),
?STATUS_ERROR;
{value,_Version} ->
io:format("ejabberd is running~n", []),
?STATUS_SUCCESS
process(Node, ["stop"]) ->
case rpc:call(Node, init, stop, []) of
{badrpc, Reason} ->
io:format("Can't stop node ~p: ~p~n",
[Node, Reason]);
_ ->
ok
end;
process(["stop"]) ->
init:stop(),
?STATUS_SUCCESS;
process(Node, ["restart"]) ->
case rpc:call(Node, init, restart, []) of
{badrpc, Reason} ->
io:format("Can't restart node ~p: ~p~n",
[Node, Reason]);
_ ->
ok
end;
process(["restart"]) ->
init:restart(),
?STATUS_SUCCESS;
process(Node, ["reopen-log"]) ->
{error_logger, Node} ! {emulator, noproc, reopen};
process(["reopen-log"]) ->
ejabberd_logger_h:reopen_log(),
?STATUS_SUCCESS;
process(["register", User, Server, Password]) ->
case ejabberd_auth:try_register(User, Server, Password) of
process(Node, ["register", User, Password]) ->
case rpc:call(Node, ejabberd_auth, try_register, [User, Password]) of
{atomic, ok} ->
?STATUS_SUCCESS;
ok;
{atomic, exists} ->
io:format("User ~p already registered at node ~p~n",
[User ++ "@" ++ Server, node()]),
?STATUS_ERROR;
io:format("User ~p already registered on node ~p~n",
[User, Node]);
{error, Reason} ->
io:format("Can't register user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR
io:format("Can't register user ~p on node ~p: ~p~n",
[User, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't register user ~p on node ~p: ~p~n",
[User, Node, Reason])
end;
process(["unregister", User, Server]) ->
case ejabberd_auth:remove_user(User, Server) of
process(Node, ["unregister", User]) ->
case rpc:call(Node, ejabberd_auth, remove_user, [User]) of
{atomic, ok} ->
ok;
{error, Reason} ->
io:format("Can't unregister user ~p at node ~p: ~p~n",
[User ++ "@" ++ Server, node(), Reason]),
?STATUS_ERROR;
_ ->
?STATUS_SUCCESS
io:format("Can't unregister user ~p on node ~p: ~p~n",
[User, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't unregister user ~p on node ~p: ~p~n",
[User, Node, Reason])
end;
process(["backup", Path]) ->
case mnesia:backup(Path) of
process(Node, ["backup", Path]) ->
case rpc:call(Node, mnesia, backup, [Path]) of
ok ->
?STATUS_SUCCESS;
ok;
{error, Reason} ->
io:format("Can't store backup in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
io:format("Can't store backup in ~p on node ~p: ~p~n",
[Path, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't store backup in ~p on node ~p: ~p~n",
[Path, Node, Reason])
end;
process(["dump", Path]) ->
case dump_to_textfile(Path) of
process(Node, ["dump", Path]) ->
case rpc:call(Node, mnesia, dump_to_textfile, [Path]) of
ok ->
?STATUS_SUCCESS;
ok;
{error, Reason} ->
io:format("Can't store dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
io:format("Can't store dump in ~p on node ~p: ~p~n",
[Path, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't store dump in ~p on node ~p: ~p~n",
[Path, Node, Reason])
end;
process(["load", Path]) ->
case mnesia:load_textfile(Path) of
{atomic, ok} ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't load dump in ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["restore", Path]) ->
case ejabberd_admin:restore(Path) of
{atomic, _} ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't restore backup from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR;
{aborted,{no_exists,Table}} ->
io:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.~n",
[filename:absname(Path), node(), Table]),
?STATUS_ERROR;
{aborted,enoent} ->
io:format("Can't restore backup from ~p at node ~p: File not found.~n",
[filename:absname(Path), node()]),
?STATUS_ERROR
end;
process(["install-fallback", Path]) ->
case mnesia:install_fallback(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't install fallback from ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
end;
process(["import-file", Path]) ->
case jd2ejd:import_file(Path) of
process(Node, ["load", Path]) ->
case rpc:call(Node, mnesia, load_textfile, [Path]) of
ok ->
?STATUS_SUCCESS;
ok;
{error, Reason} ->
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
io:format("Can't load dump in ~p on node ~p: ~p~n",
[Path, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't load dump in ~p on node ~p: ~p~n",
[Path, Node, Reason])
end;
process(["import-dir", Path]) ->
case jd2ejd:import_dir(Path) of
ok ->
?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
[filename:absname(Path), node(), Reason]),
?STATUS_ERROR
process(Node, ["restore", Path]) ->
case rpc:call(Node,
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
{atomic, ok} ->
ok;
{error, Reason} ->
io:format("Can't restore backup from ~p on node ~p: ~p~n",
[Path, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't restore backup from ~p on node ~p: ~p~n",
[Path, Node, Reason])
end;
process(["delete-expired-messages"]) ->
mod_offline:remove_expired_messages(),
?STATUS_SUCCESS;
process(["delete-old-messages", Days]) ->
case catch list_to_integer(Days) of
{'EXIT',{Reason, _Stack}} ->
io:format("Can't delete old messages (~p). Please pass an integer as parameter.~n",
[Reason]),
?STATUS_ERROR;
Integer when Integer >= 0 ->
{atomic, _} = mod_offline:remove_old_messages(Integer),
io:format("Removed messages older than ~s days~n", [Days]),
?STATUS_SUCCESS;
Integer ->
io:format("Can't delete old messages. Please pass a positive integer as parameter.~n", []),
?STATUS_ERROR
process(Node, ["install-fallback", Path]) ->
case rpc:call(Node, mnesia, install_fallback, [Path]) of
{atomic, ok} ->
ok;
{error, Reason} ->
io:format("Can't install fallback from ~p on node ~p: ~p~n",
[Path, Node, Reason]);
{badrpc, Reason} ->
io:format("Can't install fallback from ~p on node ~p: ~p~n",
[Path, Node, Reason])
end;
process(["vhost", H | Args]) ->
case jlib:nameprep(H) of
false ->
io:format("Bad hostname: ~p~n", [H]),
?STATUS_ERROR;
Host ->
case ejabberd_hooks:run_fold(
ejabberd_ctl_process, Host, false, [Host, Args]) of
false ->
print_vhost_usage(Host),
?STATUS_USAGE;
Status ->
Status
end
process(Node, ["registered-users"]) ->
case rpc:call(Node, ejabberd_auth, dirty_get_registered_users, []) of
Users when is_list(Users) ->
NewLine = io_lib:format("~n", []),
SUsers = lists:sort(Users),
FUsers = lists:map(fun(U) -> [U, NewLine] end, SUsers),
io:format("~s", [FUsers]),
ok;
{ErrorTag, Reason} when (ErrorTag == error) or (ErrorTag == badrpc) ->
io:format("Can't get list of registered users on node ~p: ~p~n",
[Node, Reason])
end;
process(Args) ->
case ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
false ->
print_usage(),
?STATUS_USAGE;
Status ->
Status
end.
process(_Node, _Args) ->
print_usage().
print_usage() ->
CmdDescs =
[{"status", "get ejabberd status"},
{"stop", "stop ejabberd"},
{"restart", "restart ejabberd"},
{"reopen-log", "reopen log file"},
{"register user server password", "register a user"},
{"unregister user server", "unregister a user"},
{"backup file", "store a database backup to file"},
{"restore file", "restore a database backup from file"},
{"install-fallback file", "install a database fallback from file"},
{"dump file", "dump a database to a text file"},
{"load file", "restore a database from a text file"},
{"import-file file", "import user data from jabberd 1.4 spool file"},
{"import-dir dir", "import user data from jabberd 1.4 spool directory"},
{"delete-expired-messages", "delete expired offline messages from database"},
{"delete-old-messages n", "delete offline messages older than n days from database"},
{"vhost host ...", "execute host-specific commands"}] ++
ets:tab2list(ejabberd_ctl_cmds),
MaxCmdLen =
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs)),
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
io:format(
"Usage: ejabberdctl node command~n"
"~n"
"Available commands:~n"
++ FmtCmdDescs ++
" stop\t\t\t\tstop ejabberd~n"
" restart\t\t\trestart ejabberd~n"
" reopen-log\t\t\treopen log file~n"
" register user password\tregister a user~n"
" unregister user\t\tunregister a user~n"
" backup file\t\t\tstore a database backup in file~n"
" restore file\t\t\trestore a database backup from file~n"
" install-fallback file\t\tinstall a database fallback from file~n"
" dump file\t\t\tdump a database in a text file~n"
" load file\t\t\trestore a database from a text file~n"
" registered-users\t\tlist all registered users~n"
"~n"
"Example:~n"
" ejabberdctl ejabberd@host restart~n"
).
print_vhost_usage(Host) ->
CmdDescs =
ets:select(ejabberd_ctl_host_cmds,
[{{Host, '$1', '$2'}, [], [{{'$1', '$2'}}]}]),
MaxCmdLen =
if
CmdDescs == [] ->
0;
true ->
lists:max(lists:map(
fun({Cmd, _Desc}) ->
length(Cmd)
end, CmdDescs))
end,
NewLine = io_lib:format("~n", []),
FmtCmdDescs =
lists:map(
fun({Cmd, Desc}) ->
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
Desc, NewLine]
end, CmdDescs),
io:format(
"Usage: ejabberdctl node vhost host command~n"
"~n"
"Available commands:~n"
++ FmtCmdDescs ++
"~n"
).
register_commands(CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_cmds, CmdDescs),
ejabberd_hooks:add(ejabberd_ctl_process,
Module, Function, 50),
ok.
register_commands(Host, CmdDescs, Module, Function) ->
ets:insert(ejabberd_ctl_host_cmds,
[{Host, Cmd, Desc} || {Cmd, Desc} <- CmdDescs]),
ejabberd_hooks:add(ejabberd_ctl_process, Host,
Module, Function, 50),
ok.
unregister_commands(CmdDescs, Module, Function) ->
lists:foreach(fun(CmdDesc) ->
ets:delete_object(ejabberd_ctl_cmds, CmdDesc)
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
unregister_commands(Host, CmdDescs, Module, Function) ->
lists:foreach(fun({Cmd, Desc}) ->
ets:delete_object(ejabberd_ctl_host_cmds,
{Host, Cmd, Desc})
end, CmdDescs),
ejabberd_hooks:delete(ejabberd_ctl_process,
Module, Function, 50),
ok.
dump_to_textfile(File) ->
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
dump_to_textfile(yes, {ok, F}) ->
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),
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).
-12
View File
@@ -1,12 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_ctl.hrl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose :
%%% Created : 13 Feb 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-define(STATUS_SUCCESS, 0).
-define(STATUS_ERROR, 1).
-define(STATUS_USAGE, 2).
-define(STATUS_BADRPC, 3).
-199
View File
@@ -1,199 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_hooks.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Manage hooks
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_hooks).
-author('alexey@sevcom.net').
-behaviour(gen_server).
%% External exports
-export([start_link/0,
add/4,
delete/4,
run/2,
run_fold/3,
add/5,
delete/5,
run/3,
run_fold/4]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
code_change/3,
handle_info/2,
terminate/2]).
-include("ejabberd.hrl").
-record(state, {}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []).
add(Hook, Module, Function, Seq) ->
add(Hook, global, Module, Function, Seq).
add(Hook, Host, Module, Function, Seq) ->
gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}).
delete(Hook, Module, Function, Seq) ->
delete(Hook, global, Module, Function, Seq).
delete(Hook, Host, Module, Function, Seq) ->
gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}).
run(Hook, Args) ->
run(Hook, global, Args).
run(Hook, Host, Args) ->
case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
ok
end.
run_fold(Hook, Val, Args) ->
run_fold(Hook, global, Val, Args).
run_fold(Hook, Host, Val, Args) ->
case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
Val
end.
%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------------
%%----------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%%----------------------------------------------------------------------
init([]) ->
ets:new(hooks, [named_table]),
{ok, #state{}}.
%%----------------------------------------------------------------------
%% 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({add, Hook, Host, Module, Function, Seq}, From, State) ->
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
El = {Seq, Module, Function},
case lists:member(El, Ls) of
true ->
ok;
false ->
NewLs = lists:merge(Ls, [El]),
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok
end;
[] ->
NewLs = [{Seq, Module, Function}],
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok
end,
{reply, Reply, State};
handle_call({delete, Hook, Host, Module, Function, Seq}, From, State) ->
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Module, Function}, Ls),
ets:insert(hooks, {{Hook, Host}, NewLs}),
ok;
[] ->
ok
end,
{reply, Reply, State};
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}.
%%----------------------------------------------------------------------
%% 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.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
run1([], Hook, Args) ->
ok;
run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
case catch apply(Module, Function, Args) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nrunning hook: ~p",
[Reason, {Hook, Args}]),
run1(Ls, Hook, Args);
stop ->
ok;
_ ->
run1(Ls, Hook, Args)
end.
run_fold1([], Hook, Val, Args) ->
Val;
run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
case catch apply(Module, Function, [Val | Args]) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nrunning hook: ~p",
[Reason, {Hook, Args}]),
run_fold1(Ls, Hook, Val, Args);
stop ->
stopped;
{stop, NewVal} ->
NewVal;
NewVal ->
run_fold1(Ls, Hook, NewVal, Args)
end.
+3 -11
View File
@@ -70,8 +70,7 @@ init(Port, Module, Opts) ->
{packet, 0},
{active, false},
{reuseaddr, true},
{nodelay, true},
{keepalive, true} |
{nodelay, true} |
SockOpts]),
case Res of
{ok, ListenSocket} ->
@@ -93,13 +92,7 @@ accept(ListenSocket, Module, Opts) ->
ok
end,
{ok, Pid} = Module:start({gen_tcp, Socket}, Opts),
case gen_tcp:controlling_process(Socket, Pid) of
ok ->
ok;
{error, _Reason} ->
gen_tcp:close(Socket)
end,
Module:become_controller(Pid),
gen_tcp:controlling_process(Socket, Pid),
accept(ListenSocket, Module, Opts);
{error, Reason} ->
?INFO_MSG("(~w) Failed TCP accept: ~w",
@@ -146,8 +139,7 @@ accept_ssl(ListenSocket, Module, Opts) ->
ok
end,
{ok, Pid} = Module:start({ssl, Socket}, Opts),
catch ssl:controlling_process(Socket, Pid),
Module:become_controller(Pid),
ssl:controlling_process(Socket, Pid),
accept_ssl(ListenSocket, Module, Opts);
{error, timeout} ->
accept_ssl(ListenSocket, Module, Opts);
+120 -190
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_local.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Route local packets
%%% Purpose :
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -10,46 +10,118 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-export([start_link/0, init/0]).
%% API
-export([start_link/0]).
-export([route/3,
-export([register_iq_handler/3,
register_iq_handler/4,
register_iq_handler/5,
unregister_iq_handler/2,
refresh_iq_handlers/0,
bounce_resource_packet/3
unregister_iq_handler/1,
refresh_iq_handlers/0
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {}).
-record(state, {mydomain, iqtable}).
-define(IQTABLE, local_iqtable).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
register(ejabberd_local,
Pid = proc_lib:spawn_link(ejabberd_local, init, [])),
{ok, Pid}.
process_iq(From, To, Packet) ->
init() ->
MyDomain = ?MYNAME,
ejabberd_router:register_route(MyDomain),
catch ets:new(local_iqtable, [named_table, public]),
loop(#state{mydomain = MyDomain,
iqtable = local_iqtable}).
loop(State) ->
receive
{route, From, To, Packet} ->
case catch do_route(State, From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop(State);
{register_iq_handler, XMLNS, Module, Function} ->
ets:insert(State#state.iqtable, {XMLNS, Module, Function}),
catch mod_disco:register_feature(XMLNS),
loop(State);
{register_iq_handler, XMLNS, Module, Function, Opts} ->
ets:insert(State#state.iqtable, {XMLNS, Module, Function, Opts}),
catch mod_disco:register_feature(XMLNS),
loop(State);
{unregister_iq_handler, XMLNS} ->
case ets:lookup(State#state.iqtable, XMLNS) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(State#state.iqtable, XMLNS),
catch mod_disco:unregister_feature(XMLNS),
loop(State);
refresh_iq_handlers ->
lists:foreach(
fun(T) ->
case T of
{XMLNS, _Module, _Function, _Opts} ->
catch mod_disco:register_feature(XMLNS);
{XMLNS, _Module, _Function} ->
catch mod_disco:register_feature(XMLNS);
_ ->
ok
end
end, ets:tab2list(State#state.iqtable)),
loop(State);
_ ->
loop(State)
end.
do_route(State, From, To, Packet) ->
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
[From, To, Packet, 8]),
case To of
#jid{luser = "", lresource = ""} ->
{xmlelement, Name, Attrs, _Els} = Packet,
case Name of
"iq" ->
process_iq(State, From, To, Packet);
"message" ->
ok;
"presence" ->
ok;
_ ->
ok
end;
#jid{luser = "", lresource = Res} ->
{xmlelement, Name, Attrs, _Els} = Packet,
case xml:get_attr_s("type", Attrs) of
"error" -> ok;
"result" -> ok;
_ ->
case {Res, Name} of
{"announce/online", "message"} ->
announce_online(From, To, Packet);
_ ->
Err = jlib:make_error_reply(
Packet, ?ERR_ITEM_NOT_FOUND),
ejabberd_router:route(To, From, Err)
end
end;
_ ->
ejabberd_sm ! {route, From, To, Packet}
end.
process_iq(State, From, To, Packet) ->
IQ = jlib:iq_query_info(Packet),
case IQ of
#iq{xmlns = XMLNS} ->
Host = To#jid.lserver,
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
case ets:lookup(State#state.iqtable, XMLNS) of
[{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ),
if
@@ -60,7 +132,7 @@ process_iq(From, To, Packet) ->
ok
end;
[{_, Module, Function, Opts}] ->
gen_iq_handler:handle(Host, Module, Function, Opts,
gen_iq_handler:handle(Module, Function, Opts,
From, To, IQ);
[] ->
Err = jlib:make_error_reply(
@@ -75,171 +147,29 @@ process_iq(From, To, Packet) ->
ok
end.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end.
register_iq_handler(XMLNS, Module, Fun) ->
ejabberd_local ! {register_iq_handler, XMLNS, Module, Fun}.
register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.
register_iq_handler(XMLNS, Module, Fun, Opts) ->
ejabberd_local ! {register_iq_handler, XMLNS, Module, Fun, Opts}.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
unregister_iq_handler(Host, XMLNS) ->
ejabberd_local ! {unregister_iq_handler, Host, XMLNS}.
unregister_iq_handler(XMLNS) ->
ejabberd_local ! {unregister_iq_handler, XMLNS}.
refresh_iq_handlers() ->
ejabberd_local ! refresh_iq_handlers.
bounce_resource_packet(From, To, Packet) ->
Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
ejabberd_router:route(To, From, Err),
stop.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, bounce_resource_packet, 100)
end, ?MYHOSTS),
catch ets:new(?IQTABLE, [named_table, public]),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(?IQTABLE, {XMLNS, Host}),
catch mod_disco:unregister_feature(Host, XMLNS),
{noreply, State};
handle_info(refresh_iq_handlers, State) ->
lists:foreach(
fun(T) ->
case T of
{{XMLNS, Host}, _Module, _Function, _Opts} ->
catch mod_disco:register_feature(Host, XMLNS);
{{XMLNS, Host}, _Module, _Function} ->
catch mod_disco:register_feature(Host, XMLNS);
_ ->
ok
end
end, ets:tab2list(?IQTABLE)),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_route(From, To, Packet) ->
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
[From, To, Packet, 8]),
if
To#jid.luser /= "" ->
ejabberd_sm:route(From, To, Packet);
To#jid.lresource == "" ->
{xmlelement, Name, _Attrs, _Els} = Packet,
case Name of
"iq" ->
process_iq(From, To, Packet);
"message" ->
ok;
"presence" ->
ok;
_ ->
ok
end;
true ->
{xmlelement, _Name, Attrs, _Els} = Packet,
case xml:get_attr_s("type", Attrs) of
"error" -> ok;
"result" -> ok;
_ ->
ejabberd_hooks:run(local_send_to_resource_hook,
To#jid.lserver,
[From, To, Packet])
end
end.
announce_online(From, To, Packet) ->
case acl:match_rule(announce, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
ejabberd_router:route(To, From, Err);
allow ->
Local = jlib:make_jid("", ?MYNAME, ""),
lists:foreach(
fun({U, R}) ->
Dest = jlib:make_jid(U, ?MYNAME, R),
ejabberd_router:route(Local, Dest, Packet)
end, ejabberd_sm:dirty_get_sessions_list())
end.
+12 -30
View File
@@ -15,7 +15,7 @@
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
code_change/3, reopen_log/0]).
code_change/3]).
-record(state, {fd, file}).
@@ -29,7 +29,7 @@
%% Other
%%----------------------------------------------------------------------
init(File) ->
case file:open(File, [append, raw]) of
case file:open(File, [append]) of
{ok, Fd} ->
{ok, #state{fd = Fd, file = File}};
Error ->
@@ -66,8 +66,7 @@ handle_info({'EXIT', _Fd, _Reason}, _State) ->
remove_handler;
handle_info({emulator, _GL, reopen}, State) ->
file:close(State#state.fd),
rotate_log(State#state.file),
case file:open(State#state.file, [append, raw]) of
case file:open(State#state.file, [append]) of
{ok, Fd} ->
{ok, State#state{fd = Fd}};
Error ->
@@ -90,9 +89,6 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
reopen_log() ->
error_logger ! {emulator, noproc, reopen}.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
@@ -102,38 +98,38 @@ write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
T = write_time(Time),
case catch io_lib:format(add_node(Format,Pid), Args) of
S when list(S) ->
file:write(Fd, io_lib:format(T ++ S, []));
io:format(Fd, T ++ S, []);
_ ->
F = add_node("ERROR: ~p - ~p~n", Pid),
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
io:format(Fd, T ++ F, [Format,Args])
end;
write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
T = write_time(Time),
case catch io_lib:format(Chars, []) of
S when list(S) ->
file:write(Fd, io_lib:format(T ++ S, []));
io:format(Fd, T ++ S, []);
_ ->
file:write(Fd, io_lib:format(T ++ "ERROR: ~p ~n", [Chars]))
io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
end;
write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
T = write_time(Time),
file:write(Fd, io_lib:format(T ++ add_node("~p~n",Pid), [Info]));
io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
T = write_time(Time),
S = format_report(Rep),
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
io:format(Fd, T ++ S ++ add_node("", Pid), []);
write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
T = write_time(Time, "INFO REPORT"),
S = format_report(Rep),
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
io:format(Fd, T ++ S ++ add_node("", Pid), []);
write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
T = write_time(Time, "INFO REPORT"),
case catch io_lib:format(add_node(Format,Pid), Args) of
S when list(S) ->
file:write(Fd, io_lib:format(T ++ S, []));
io:format(Fd, T ++ S, []);
_ ->
F = add_node("ERROR: ~p - ~p~n", Pid),
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
io:format(Fd, T ++ F, [Format,Args])
end;
write_event(_, _) ->
ok.
@@ -190,17 +186,3 @@ write_time({{Y,Mo,D},{H,Mi,S}}, Type) ->
io_lib:format("~n=~s==== ~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ===~n",
[Type, Y, Mo, D, H, Mi, S]).
%% Rename the log file if it the filename exists
%% This is needed in systems when the file must be closed before rotation (Windows).
%% On most Unix-like system, the file can be renamed from the command line and
%%the log can directly be reopened.
rotate_log(Filename) ->
case file:read_file_info(Filename) of
{ok, _FileInfo} ->
RotationName = filename:rootname(Filename),
file:rename(Filename, [RotationName, "-old.log"]),
ok;
{error, _Reason} ->
ok
end.
+47 -241
View File
@@ -10,88 +10,20 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([start_link/5,
start/3,
start/4,
-export([start/3,
receiver/4,
change_shaper/2,
reset_stream/1,
starttls/2,
compress/2,
become_controller/1,
close/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
reset_stream/1]).
-include("ejabberd.hrl").
-record(state, {socket,
sock_mod,
shaper_state,
c2s_pid,
max_stanza_size,
xml_stream_state,
timeout}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Socket, SockMod, Shaper, MaxStanzaSize, C2SPid) ->
gen_server:start_link(
?MODULE, [Socket, SockMod, Shaper, MaxStanzaSize, C2SPid], []).
%%--------------------------------------------------------------------
%% Function: start() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start(Socket, SockMod, Shaper) ->
start(Socket, SockMod, Shaper, infinity).
proc_lib:spawn(?MODULE, receiver, [Socket, SockMod, Shaper, self()]).
start(Socket, SockMod, Shaper, MaxStanzaSize) ->
{ok, Pid} = supervisor:start_child(
ejabberd_receiver_sup,
[Socket, SockMod, Shaper, MaxStanzaSize, self()]),
Pid.
change_shaper(Pid, Shaper) ->
gen_server:cast(Pid, {change_shaper, Shaper}).
reset_stream(Pid) ->
gen_server:call(Pid, reset_stream).
starttls(Pid, TLSSocket) ->
gen_server:call(Pid, {starttls, TLSSocket}).
compress(Pid, ZlibSocket) ->
gen_server:call(Pid, {compress, ZlibSocket}).
become_controller(Pid) ->
gen_server:call(Pid, become_controller).
close(Pid) ->
gen_server:cast(Pid, close).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Socket, SockMod, Shaper, MaxStanzaSize, C2SPid]) ->
XMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
receiver(Socket, SockMod, Shaper, C2SPid) ->
XMLStreamPid = xml_stream:start(C2SPid),
ShaperState = shaper:new(Shaper),
Timeout = case SockMod of
ssl ->
@@ -99,174 +31,48 @@ init([Socket, SockMod, Shaper, MaxStanzaSize, C2SPid]) ->
_ ->
infinity
end,
{ok, #state{socket = Socket,
sock_mod = SockMod,
shaper_state = ShaperState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize,
xml_stream_state = XMLStreamState,
timeout = Timeout}}.
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout).
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({starttls, TLSSocket}, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
NewState = State#state{socket = TLSSocket,
sock_mod = tls,
xml_stream_state = NewXMLStreamState},
case tls:recv_data(TLSSocket, "") of
{ok, TLSData} ->
{reply, ok, process_data(TLSData, NewState)};
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call({compress, ZlibSocket}, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
NewState = State#state{socket = ZlibSocket,
sock_mod = ejabberd_zlib,
xml_stream_state = NewXMLStreamState},
case ejabberd_zlib:recv_data(ZlibSocket, "") of
{ok, ZlibData} ->
{reply, ok, process_data(ZlibData, NewState)};
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call(reset_stream, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid,
max_stanza_size = MaxStanzaSize} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
Reply = ok,
{reply, Reply, State#state{xml_stream_state = NewXMLStreamState}};
handle_call(become_controller, _From, State) ->
activate_socket(State),
Reply = ok,
{reply, Reply, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({change_shaper, Shaper}, State) ->
NewShaperState = shaper:new(Shaper),
{noreply, State#state{shaper_state = NewShaperState}};
handle_cast(close, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({Tag, _TCPSocket, Data},
#state{socket = Socket,
sock_mod = SockMod} = State)
when (Tag == tcp) or (Tag == ssl) ->
case SockMod of
tls ->
case tls:recv_data(Socket, Data) of
{ok, TLSData} ->
{noreply, process_data(TLSData, State)};
{error, _Reason} ->
{stop, normal, State}
end;
ejabberd_zlib ->
case ejabberd_zlib:recv_data(Socket, Data) of
{ok, ZlibData} ->
{noreply, process_data(ZlibData, State)};
{error, _Reason} ->
{stop, normal, State}
end;
_ ->
{noreply, process_data(Data, State)}
end;
handle_info({Tag, _TCPSocket}, State)
when (Tag == tcp_closed) or (Tag == ssl_closed) ->
{stop, normal, State};
handle_info({Tag, _TCPSocket, Reason}, State)
when (Tag == tcp_error) or (Tag == ssl_error) ->
case Reason of
timeout ->
{noreply, State};
_ ->
{stop, normal, State}
end;
handle_info({timeout, _Ref, activate}, State) ->
activate_socket(State),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, #state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid} = State) ->
xml_stream:close(XMLStreamState),
gen_fsm:send_event(C2SPid, closed),
catch (State#state.sock_mod):close(State#state.socket),
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
activate_socket(#state{socket = Socket,
sock_mod = SockMod}) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, [{active, once}]);
_ ->
SockMod:setopts(Socket, [{active, once}])
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout) ->
case catch SockMod:recv(Socket, 0, Timeout) of
{ok, Text} ->
ShaperSt1 = receive
{change_shaper, Shaper} ->
shaper:new(Shaper)
after 0 ->
ShaperState
end,
NewShaperState = shaper:update(ShaperSt1, size(Text)),
XMLStreamPid1 = receive
reset_stream ->
exit(XMLStreamPid, closed),
xml_stream:start(C2SPid)
after 0 ->
XMLStreamPid
end,
xml_stream:send_text(XMLStreamPid1, Text),
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamPid1,
Timeout);
{error, timeout} ->
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid,
Timeout);
{error, Reason} ->
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok;
{'EXIT', Reason} ->
?ERROR_MSG("(~w) abnormal ~w:recv termination:~n\t~p~n",
[Socket, SockMod, Reason]),
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok
end.
process_data(Data,
#state{xml_stream_state = XMLStreamState,
shaper_state = ShaperState} = State) ->
?DEBUG("Received XML on stream = ~p", [binary_to_list(Data)]),
XMLStreamState1 = xml_stream:parse(XMLStreamState, Data),
{NewShaperState, Pause} = shaper:update(ShaperState, size(Data)),
if
Pause > 0 ->
erlang:start_timer(Pause, self(), activate);
true ->
activate_socket(State)
end,
State#state{xml_stream_state = XMLStreamState1,
shaper_state = NewShaperState}.
change_shaper(Pid, Shaper) ->
Pid ! {change_shaper, Shaper}.
reset_stream(Pid) ->
Pid ! reset_stream.
+88 -229
View File
@@ -10,121 +10,27 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([route/3,
register_route/1,
register_route/2,
register_routes/1,
unregister_route/1,
unregister_routes/1,
dirty_get_all_routes/0,
dirty_get_all_domains/0
]).
-export([start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([start_link/0, init/0]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(route, {domain, pid, local_hint}).
-record(state, {}).
-record(route, {domain, pid}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
Pid = proc_lib:spawn_link(ejabberd_router, init, []),
register(ejabberd_router, Pid),
{ok, Pid}.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end.
register_route(Domain) ->
case jlib:nameprep(Domain) of
error ->
[] = {invalid_domain, Domain};
LDomain ->
Pid = self(),
F = fun() ->
mnesia:write(#route{domain = LDomain,
pid = Pid})
end,
mnesia:transaction(F)
end.
register_route(Domain, LocalHint) ->
case jlib:nameprep(Domain) of
error ->
[] = {invalid_domain, Domain};
LDomain ->
Pid = self(),
F = fun() ->
mnesia:write(#route{domain = LDomain,
pid = Pid,
local_hint = LocalHint})
end,
mnesia:transaction(F)
end.
register_routes(Domains) ->
lists:foreach(fun(Domain) ->
register_route(Domain)
end, Domains).
unregister_route(Domain) ->
case jlib:nameprep(Domain) of
error ->
[] = {invalid_domain, Domain};
LDomain ->
Pid = self(),
F = fun() ->
mnesia:delete_object(#route{domain = LDomain,
pid = Pid})
end,
mnesia:transaction(F)
end.
unregister_routes(Domains) ->
lists:foreach(fun(Domain) ->
unregister_route(Domain)
end, Domains).
dirty_get_all_routes() ->
lists:usort(mnesia:dirty_all_keys(route)) -- ?MYHOSTS.
dirty_get_all_domains() ->
lists:usort(mnesia:dirty_all_keys(route)).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
init() ->
update_tables(),
mnesia:create_table(route,
[{ram_copies, [node()]},
@@ -133,136 +39,91 @@ init([]) ->
record_info(fields, route)}]),
mnesia:add_table_copy(route, node(), ram_copies),
mnesia:subscribe({table, route, simple}),
lists:foreach(
fun(Pid) ->
erlang:monitor(process, Pid)
end,
mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])),
{ok, #state{}}.
loop().
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
loop() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
{register_route, Domain, Pid} ->
F = fun() ->
mnesia:write(#route{domain = Domain,
pid = Pid})
end,
mnesia:transaction(F),
loop();
{unregister_route, Domain, Pid} ->
F = fun() ->
mnesia:delete_object(#route{domain = Domain,
pid = Pid})
end,
mnesia:transaction(F),
loop();
{mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}} ->
erlang:monitor(process, Pid),
loop();
{'DOWN', _Ref, _Type, Pid, _Info} ->
F = fun() ->
Es = mnesia:select(
route,
[{#route{pid = Pid, _ = '_'},
[],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F),
loop();
_ ->
ok
end,
{noreply, State};
handle_info({mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}},
State) ->
erlang:monitor(process, Pid),
{noreply, State};
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
F = fun() ->
Es = mnesia:select(
route,
[{#route{pid = Pid, _ = '_'},
[],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_route(OrigFrom, OrigTo, OrigPacket) ->
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n",
[OrigFrom, OrigTo, OrigPacket]),
LOrigDstDomain = OrigTo#jid.lserver,
case ejabberd_hooks:run_fold(filter_packet,
{OrigFrom, OrigTo, OrigPacket}, []) of
{From, To, Packet} ->
LDstDomain = To#jid.lserver,
case mnesia:dirty_read(route, LDstDomain) of
[] ->
ejabberd_s2s:route(From, To, Packet);
[R] ->
Pid = R#route.pid,
if
node(Pid) == node() ->
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end;
true ->
Pid ! {route, From, To, Packet}
end;
Rs ->
case [R || R <- Rs, node(R#route.pid) == node()] of
[] ->
R = lists:nth(erlang:phash(now(), length(Rs)), Rs),
Pid = R#route.pid,
Pid ! {route, From, To, Packet};
LRs ->
LRs,
R = lists:nth(erlang:phash(now(), length(LRs)), LRs),
Pid = R#route.pid,
case R#route.local_hint of
{apply, Module, Function} ->
Module:Function(From, To, Packet);
_ ->
Pid ! {route, From, To, Packet}
end
end
end;
drop ->
ok
loop()
end.
do_route(From, To, Packet) ->
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", [From, To, Packet]),
LDstDomain = To#jid.lserver,
case mnesia:dirty_read(route, LDstDomain) of
[] ->
ejabberd_s2s ! {route, From, To, Packet};
[R] ->
Pid = R#route.pid,
?DEBUG("routed to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet};
Rs ->
Rs1 = case [R || R <- Rs, node(R#route.pid) == node()] of
[] -> Rs;
LRs -> LRs
end,
R = lists:nth(erlang:phash(now(), length(Rs1)), Rs1),
Pid = R#route.pid,
?DEBUG("routed to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end.
route(From, To, Packet) ->
ejabberd_router ! {route, From, To, Packet}.
register_route(Domain) ->
ejabberd_router ! {register_route, Domain, self()}.
unregister_route(Domain) ->
ejabberd_router ! {unregister_route, Domain, self()}.
dirty_get_all_routes() ->
lists:delete(?MYNAME, lists:usort(mnesia:dirty_all_keys(route))).
dirty_get_all_domains() ->
lists:usort(mnesia:dirty_all_keys(route)).
update_tables() ->
@@ -270,8 +131,6 @@ update_tables() ->
[domain, node, pid] ->
mnesia:delete_table(route);
[domain, pid] ->
mnesia:delete_table(route);
[domain, pid, local_hint] ->
ok;
{'EXIT', _} ->
ok
+56 -141
View File
@@ -10,55 +10,75 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([start_link/0,
route/3,
-export([start_link/0, init/0,
have_connection/1,
get_key/1,
try_register/1,
remove_connection/1,
dirty_get_connections/0,
ctl_process/2
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
dirty_get_connections/0]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("ejabberd_ctl.hrl").
-record(s2s, {fromto, pid, key}).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
Pid = proc_lib:spawn_link(ejabberd_s2s, init, []),
register(ejabberd_s2s, Pid),
{ok, Pid}.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
init() ->
update_tables(),
mnesia:create_table(s2s,[{ram_copies, [node()]},
{attributes, record_info(fields, s2s)}]),
mnesia:add_table_copy(s2s, node(), ram_copies),
mnesia:subscribe(system),
loop().
loop() ->
receive
{closed_conection, FromTo} ->
remove_connection(FromTo),
loop();
{mnesia_system_event, {mnesia_down, Node}} ->
clean_table_from_bad_node(Node),
loop();
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
_ ->
ok
loop()
end.
remove_connection(FromTo) ->
F = fun() ->
mnesia:delete({s2s, FromTo})
end,
mnesia:transaction(F).
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
s2s,
[{#s2s{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F).
have_connection(FromTo) ->
case catch mnesia:dirty_read(s2s, FromTo) of
[_] ->
@@ -95,107 +115,9 @@ try_register(FromTo) ->
false
end.
dirty_get_connections() ->
mnesia:dirty_all_keys(s2s).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
update_tables(),
mnesia:create_table(s2s, [{ram_copies, [node()]},
{attributes, record_info(fields, s2s)}]),
mnesia:add_table_copy(s2s, node(), ram_copies),
mnesia:subscribe(system),
ejabberd_ctl:register_commands(
[{"incoming-s2s-number", "print number of incoming s2s connections on the node"},
{"outgoing-s2s-number", "print number of outgoing s2s connections on the node"}],
?MODULE, ctl_process),
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
clean_table_from_bad_node(Node),
{noreply, State};
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
s2s,
[{#s2s{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete_object(E)
end, Es)
end,
mnesia:transaction(F).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
do_route(From, To, Packet) ->
?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
@@ -237,26 +159,19 @@ find_connection(From, To) ->
Pid
end
end,
TRes = mnesia:transaction(F),
ejabberd_s2s_out:start_connection(Pid),
TRes;
mnesia:transaction(F);
[El] ->
{atomic, El#s2s.pid}
end.
send_element(Pid, El) ->
Pid ! {send_element, El}.
ctl_process(_Val, ["incoming-s2s-number"]) ->
N = length(supervisor:which_children(ejabberd_s2s_in_sup)),
io:format("~p~n", [N]),
{stop, ?STATUS_SUCCESS};
ctl_process(_Val, ["outgoing-s2s-number"]) ->
N = length(supervisor:which_children(ejabberd_s2s_out_sup)),
io:format("~p~n", [N]),
{stop, ?STATUS_SUCCESS};
ctl_process(Val, _Args) ->
Val.
dirty_get_connections() ->
mnesia:dirty_all_keys(s2s).
update_tables() ->
case catch mnesia:table_info(s2s, attributes) of
+50 -365
View File
@@ -1,26 +1,26 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_s2s_in.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Serve incoming s2s connection
%%% Purpose :
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_s2s_in).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_fsm).
%% External exports
-export([start/2,
start_link/2,
become_controller/1,
match_domain/2]).
send_text/2,
send_element/2]).
%% gen_fsm callbacks
-export([init/1,
wait_for_stream/2,
wait_for_feature_request/2,
stream_established/2,
handle_event/3,
handle_sync_event/4,
@@ -30,22 +30,13 @@
-include("ejabberd.hrl").
-include("jlib.hrl").
-include_lib("ssl/include/PKIX1Explicit88.hrl").
-include_lib("ssl/include/PKIX1Implicit88.hrl").
-include("XmppAddr.hrl").
-define(DICT, dict).
-record(state, {socket,
sockmod,
receiver,
streamid,
shaper,
tls = false,
tls_enabled = false,
tls_options = [],
authenticated = false,
auth_domain,
connections = ?DICT:new(),
timer}).
@@ -58,13 +49,13 @@
-define(FSMOPTS, []).
-endif.
-define(STREAM_HEADER(Version),
-define(STREAM_HEADER,
("<?xml version='1.0'?>"
"<stream:stream "
"xmlns:stream='http://etherx.jabber.org/streams' "
"xmlns='jabber:server' "
"xmlns:db='jabber:server:dialback' "
"id='" ++ StateData#state.streamid ++ "'" ++ Version ++ ">")
"id='" ++ StateData#state.streamid ++ "'>")
).
-define(STREAM_TRAILER, "</stream:stream>").
@@ -87,9 +78,6 @@ start(SockData, Opts) ->
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
become_controller(Pid) ->
gen_fsm:send_all_state_event(Pid, become_controller).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -103,39 +91,17 @@ become_controller(Pid) ->
%%----------------------------------------------------------------------
init([{SockMod, Socket}, Opts]) ->
?INFO_MSG("started: ~p", [{SockMod, Socket}]),
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
Shaper = case lists:keysearch(shaper, 1, Opts) of
{value, {_, S}} -> S;
_ -> none
end,
MaxStanzaSize =
case lists:keysearch(max_stanza_size, 1, Opts) of
{value, {_, Size}} -> Size;
_ -> infinity
end,
ReceiverPid = ejabberd_receiver:start(
Socket, SockMod, none, MaxStanzaSize),
StartTLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
undefined ->
false;
UseStartTLS ->
UseStartTLS
end,
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
undefined ->
[];
CertFile ->
[{certfile, CertFile}]
end,
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
{ok, wait_for_stream,
#state{socket = Socket,
sockmod = SockMod,
receiver = ReceiverPid,
streamid = new_id(),
shaper = Shaper,
tls = StartTLS,
tls_enabled = false,
tls_options = TLSOpts,
timer = Timer}}.
%%----------------------------------------------------------------------
@@ -146,61 +112,19 @@ init([{SockMod, Socket}, Opts]) ->
%%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
case {xml:get_attr_s("xmlns", Attrs),
xml:get_attr_s("xmlns:db", Attrs),
xml:get_attr_s("version", Attrs) == "1.0"} of
{"jabber:server", _, true} when
StateData#state.tls and (not StateData#state.authenticated) ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
SASL =
if
StateData#state.tls_enabled ->
case tls:get_peer_certificate(StateData#state.socket) of
{ok, _Cert} ->
case tls:get_verify_result(
StateData#state.socket) of
0 ->
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL}],
[{xmlelement, "mechanism", [],
[{xmlcdata, "EXTERNAL"}]}]}];
_ ->
[]
end;
error ->
[]
end;
true ->
[]
end,
StartTLS = if
StateData#state.tls_enabled ->
[];
true ->
[{xmlelement, "starttls",
[{"xmlns", ?NS_TLS}], []}]
end,
send_element(StateData,
{xmlelement, "stream:features", [],
SASL ++ StartTLS}),
{next_state, wait_for_feature_request, StateData};
{"jabber:server", _, true} when
StateData#state.authenticated ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
send_element(StateData,
{xmlelement, "stream:features", [], []}),
{next_state, stream_established, StateData};
{"jabber:server", "jabber:server:dialback", _} ->
send_text(StateData, ?STREAM_HEADER("")),
{next_state, stream_established, StateData};
% TODO
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
{"jabber:server", "jabber:server:dialback"} ->
send_text(StateData#state.socket, ?STREAM_HEADER),
{next_state, stream_established, StateData#state{}};
_ ->
send_text(StateData, ?INVALID_NAMESPACE_ERR),
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
{stop, normal, StateData}
end;
wait_for_stream({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?STREAM_HEADER("") ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
send_text(StateData#state.socket,
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_stream(timeout, StateData) ->
@@ -209,106 +133,6 @@ wait_for_stream(timeout, StateData) ->
wait_for_stream(closed, StateData) ->
{stop, normal, StateData}.
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
SockMod = StateData#state.sockmod,
case {xml:get_attr_s("xmlns", Attrs), Name} of
{?NS_TLS, "starttls"} when TLS == true,
TLSEnabled == false,
SockMod == gen_tcp ->
?INFO_MSG("starttls", []),
Socket = StateData#state.socket,
TLSOpts = StateData#state.tls_options,
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(StateData#state.receiver, TLSSocket),
send_element(StateData,
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []}),
{next_state, wait_for_stream,
StateData#state{sockmod = tls,
socket = TLSSocket,
streamid = new_id(),
tls_enabled = true
}};
{?NS_SASL, "auth"} when TLSEnabled ->
Mech = xml:get_attr_s("mechanism", Attrs),
case Mech of
"EXTERNAL" ->
Auth = jlib:decode_base64(xml:get_cdata(Els)),
AuthDomain = jlib:nameprep(Auth),
AuthRes =
case tls:get_peer_certificate(StateData#state.socket) of
{ok, Cert} ->
case tls:get_verify_result(
StateData#state.socket) of
0 ->
case AuthDomain of
error ->
false;
_ ->
case idna:domain_utf8_to_ascii(AuthDomain) of
false ->
false;
PCAuthDomain ->
lists:any(
fun(D) ->
match_domain(
PCAuthDomain, D)
end, get_cert_domains(Cert))
end
end;
_ ->
false
end;
error ->
false
end,
if
AuthRes ->
ejabberd_receiver:reset_stream(
StateData#state.receiver),
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
?INFO_MSG("(~w) Accepted s2s authentication for ~s",
[StateData#state.socket, AuthDomain]),
{next_state, wait_for_stream,
StateData#state{streamid = new_id(),
authenticated = true,
auth_domain = AuthDomain
}};
true ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL}], []}),
send_text(StateData, ?STREAM_TRAILER),
{stop, normal, StateData}
end;
_ ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL}],
[{xmlelement, "invalid-mechanism", [], []}]}),
{stop, normal, StateData}
end;
_ ->
stream_established({xmlstreamelement, El}, StateData)
end;
wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_feature_request({xmlstreamerror, _}, StateData) ->
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_feature_request(closed, StateData) ->
{stop, normal, StateData}.
stream_established({xmlstreamelement, El}, StateData) ->
cancel_timer(StateData#state.timer),
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
@@ -324,13 +148,13 @@ stream_established({xmlstreamelement, El}, StateData) ->
Key, StateData#state.streamid}),
Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
StateData#state.connections),
change_shaper(StateData, LTo, jlib:make_jid("", LFrom, "")),
change_shaper(StateData, jlib:make_jid("", LFrom, "")),
{next_state,
stream_established,
StateData#state{connections = Conns,
timer = Timer}};
_ ->
send_text(StateData, ?HOST_UNKNOWN_ERR),
send_text(StateData#state.socket, ?HOST_UNKNOWN_ERR),
{stop, normal, StateData}
end;
{verify, To, From, Id, Key} ->
@@ -341,7 +165,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
Type = if Key == Key1 -> "valid";
true -> "invalid"
end,
send_element(StateData,
send_element(StateData#state.socket,
{xmlelement,
"db:verify",
[{"from", To},
@@ -351,8 +175,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
[]}),
{next_state, stream_established, StateData#state{timer = Timer}};
_ ->
NewEl = jlib:remove_attr("xmlns", El),
{xmlelement, Name, Attrs, _Els} = NewEl,
{xmlelement, Name, Attrs, _Els} = El,
From_s = xml:get_attr_s("from", Attrs),
From = jlib:string_to_jid(From_s),
To_s = xml:get_attr_s("to", Attrs),
@@ -361,40 +184,18 @@ stream_established({xmlstreamelement, El}, StateData) ->
(To /= error) and (From /= error) ->
LFrom = From#jid.lserver,
LTo = To#jid.lserver,
if
StateData#state.authenticated ->
case (LFrom == StateData#state.auth_domain)
andalso
lists:member(
LTo,
ejabberd_router:dirty_get_all_domains()) of
true ->
if ((Name == "iq") or
(Name == "message") or
(Name == "presence")) ->
ejabberd_router:route(
From, To, NewEl);
true ->
error
end;
false ->
case ?DICT:find({LFrom, LTo},
StateData#state.connections) of
{ok, established} ->
if ((Name == "iq") or
(Name == "message") or
(Name == "presence")) ->
ejabberd_router:route(From, To, El);
true ->
error
end;
true ->
case ?DICT:find({LFrom, LTo},
StateData#state.connections) of
{ok, established} ->
if ((Name == "iq") or
(Name == "message") or
(Name == "presence")) ->
ejabberd_router:route(
From, To, NewEl);
true ->
error
end;
_ ->
error
end
_ ->
error
end;
true ->
error
@@ -403,7 +204,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
end;
stream_established({valid, From, To}, StateData) ->
send_element(StateData,
send_element(StateData#state.socket,
{xmlelement,
"db:result",
[{"from", To},
@@ -418,7 +219,7 @@ stream_established({valid, From, To}, StateData) ->
{next_state, stream_established, NSD};
stream_established({invalid, From, To}, StateData) ->
send_element(StateData,
send_element(StateData#state.socket,
{xmlelement,
"db:result",
[{"from", To},
@@ -436,8 +237,8 @@ stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData};
stream_established({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
send_text(StateData#state.socket,
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
stream_established(timeout, StateData) ->
@@ -467,12 +268,6 @@ stream_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(become_controller, StateName, StateData) ->
ok = (StateData#state.sockmod):controlling_process(
StateData#state.socket,
StateData#state.receiver),
ejabberd_receiver:become_controller(StateData#state.receiver),
{next_state, StateName, StateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
@@ -499,10 +294,10 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
send_text(StateData#state.socket, Text),
{next_state, StateName, StateData};
handle_info({timeout, Timer, _}, _StateName,
handle_info({timeout, Timer, _}, StateName,
#state{timer = Timer} = StateData) ->
{stop, normal, StateData};
@@ -516,23 +311,31 @@ handle_info(_, StateName, StateData) ->
%% Returns: any
%%----------------------------------------------------------------------
terminate(Reason, _StateName, StateData) ->
% case StateData#state.user of
% "" ->
% ok;
% _ ->
% %ejabberd_sm:close_session(StateData#state.user,
% % StateData#state.resource)
% end,
%ejabberd_s2s ! {closed_conection, StateData#state.server},
?INFO_MSG("terminated: ~p", [Reason]),
ejabberd_receiver:close(StateData#state.receiver),
gen_tcp:close(StateData#state.socket),
ok.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text).
send_text(Socket, Text) ->
gen_tcp:send(Socket,Text).
send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)).
send_element(Socket, El) ->
send_text(Socket, xml:element_to_string(El)).
change_shaper(StateData, Host, JID) ->
Shaper = acl:match_rule(Host, StateData#state.shaper, JID),
change_shaper(StateData, JID) ->
Shaper = acl:match_rule(StateData#state.shaper, JID),
ejabberd_receiver:change_shaper(StateData#state.receiver, Shaper).
@@ -565,122 +368,4 @@ is_key_packet(_) ->
false.
get_cert_domains(Cert) ->
{rdnSequence, Subject} =
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject,
Extensions =
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.extensions,
lists:flatmap(
fun(#'AttributeTypeAndValue'{type = ?'id-at-commonName',
value = Val}) ->
case 'PKIX1Explicit88':decode('X520CommonName', Val) of
{ok, {_, D1}} ->
D = if
is_list(D1) -> D1;
is_binary(D1) -> binary_to_list(D1);
true -> error
end,
if
D /= error ->
case jlib:string_to_jid(D) of
#jid{luser = "",
lserver = LD,
lresource = ""} ->
[LD];
_ ->
[]
end;
true ->
[]
end;
_ ->
[]
end;
(_) ->
[]
end, lists:flatten(Subject)) ++
lists:flatmap(
fun(#'Extension'{extnID = ?'id-ce-subjectAltName',
extnValue = Val}) ->
BVal = if
is_list(Val) -> list_to_binary(Val);
is_binary(Val) -> Val;
true -> Val
end,
case 'PKIX1Implicit88':decode('SubjectAltName', BVal) of
{ok, SANs} ->
lists:flatmap(
fun({otherName,
#'AnotherName'{'type-id' = ?'id-on-xmppAddr',
value = XmppAddr
}}) ->
case 'XmppAddr':decode(
'XmppAddr', XmppAddr) of
{ok, D} when is_binary(D) ->
case jlib:string_to_jid(
binary_to_list(D)) of
#jid{luser = "",
lserver = LD,
lresource = ""} ->
case idna:domain_utf8_to_ascii(LD) of
false ->
[];
PCLD ->
[PCLD]
end;
_ ->
[]
end;
_ ->
[]
end;
({dNSName, D}) when is_list(D) ->
case jlib:string_to_jid(D) of
#jid{luser = "",
lserver = LD,
lresource = ""} ->
[LD];
_ ->
[]
end;
(_) ->
[]
end, SANs);
_ ->
[]
end;
(_) ->
[]
end, Extensions).
match_domain(Domain, Domain) ->
true;
match_domain(Domain, Pattern) ->
DLabels = string:tokens(Domain, "."),
PLabels = string:tokens(Pattern, "."),
match_labels(DLabels, PLabels).
match_labels([], []) ->
true;
match_labels([], [_ | _]) ->
false;
match_labels([_ | _], []) ->
false;
match_labels([DL | DLabels], [PL | PLabels]) ->
case lists:all(fun(C) -> (($a =< C) andalso (C =< $z))
orelse (($0 =< C) andalso (C =< $9))
orelse (C == $-) orelse (C == $*)
end, PL) of
true ->
Regexp = regexp:sh_to_awk(PL),
case regexp:match(DL, Regexp) of
{match, _, _} ->
match_labels(DLabels, PLabels);
_ ->
false
end;
false ->
false
end.
+122 -451
View File
@@ -8,46 +8,30 @@
-module(ejabberd_s2s_out).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_fsm).
%% External exports
-export([start/3,
start_link/3,
start_connection/1]).
-export([start/3, start_link/3, send_text/2, send_element/2]).
%% gen_fsm callbacks
-export([init/1,
open_socket/2,
wait_for_stream/2,
wait_for_validation/2,
wait_for_features/2,
wait_for_auth_result/2,
wait_for_starttls_proceed/2,
reopen_socket/2,
stream_established/2,
handle_event/3,
handle_sync_event/4,
handle_info/3,
terminate/3,
code_change/4,
test_get_addr_port/1]).
code_change/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {socket, receiver,
sockmod,
streamid,
use_v10,
tls = false,
tls_required = false,
tls_enabled = false,
tls_options = [],
authenticated = false,
db_enabled = true,
try_auth = true,
myname, server, queue,
-record(state, {socket, receiver, streamid,
myname, server, xmlpid, queue,
new = false, verify = false,
timer}).
@@ -65,7 +49,7 @@
"xmlns:stream='http://etherx.jabber.org/streams' "
"xmlns='jabber:server' "
"xmlns:db='jabber:server:dialback' "
"to='~s'~s>"
"to='~s'>"
).
-define(STREAM_TRAILER, "</stream:stream>").
@@ -88,9 +72,6 @@ start(From, Host, Type) ->
start_link(From, Host, Type) ->
gen_fsm:start_link(ejabberd_s2s_out, [From, Host, Type], ?FSMOPTS).
start_connection(Pid) ->
gen_fsm:send_event(Pid, init).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -104,31 +85,15 @@ start_connection(Pid) ->
%%----------------------------------------------------------------------
init([From, Server, Type]) ->
?INFO_MSG("started: ~p", [{From, Server, Type}]),
TLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
undefined ->
false;
UseStartTLS ->
UseStartTLS
end,
UseV10 = TLS,
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
undefined ->
[];
CertFile ->
[{certfile, CertFile}, connect]
end,
gen_fsm:send_event(self(), init),
{New, Verify} = case Type of
{new, Key} ->
{Key, false};
{verify, Pid, Key, SID} ->
start_connection(self()),
{false, {Pid, Key, SID}}
end,
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
{ok, open_socket, #state{use_v10 = UseV10,
tls = TLS,
tls_options = TLSOpts,
queue = queue:new(),
{ok, open_socket, #state{queue = queue:new(),
myname = From,
server = Server,
new = New,
@@ -142,97 +107,92 @@ init([From, Server, Type]) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
open_socket(init, StateData) ->
AddrList = get_addr_port(StateData#state.server),
case lists:foldl(fun({Addr, Port}, Acc) ->
case Acc of
{ok, Socket} ->
{ok, Socket};
_ ->
open_socket1(Addr, Port)
end
end, {error, badarg}, AddrList) of
{ok, Socket} ->
ReceiverPid = ejabberd_receiver:start(Socket, gen_tcp, none),
ok = gen_tcp:controlling_process(Socket, ReceiverPid),
ejabberd_receiver:become_controller(ReceiverPid),
Version = if
StateData#state.use_v10 ->
" version='1.0'";
true ->
""
end,
NewStateData = StateData#state{socket = Socket,
sockmod = gen_tcp,
tls_enabled = false,
receiver = ReceiverPid,
streamid = new_id()},
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
[StateData#state.server,
Version])),
{next_state, wait_for_stream, NewStateData};
{error, _Reason} ->
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData}
end;
open_socket(_, StateData) ->
{next_state, open_socket, StateData}.
%%----------------------------------------------------------------------
open_socket1(Addr, Port) ->
{Addr, Port} = get_addr_port(StateData#state.server),
Res = case idna:domain_utf8_to_ascii(Addr) of
false -> {error, badarg};
ASCIIAddr ->
?DEBUG("s2s_out: connecting to ~s:~p~n", [ASCIIAddr, Port]),
case catch gen_tcp:connect(ASCIIAddr, Port,
[binary, {packet, 0},
{active, false}]) of
case gen_tcp:connect(ASCIIAddr, Port,
[binary, {packet, 0}]) of
{ok, _Socket} = R -> R;
{error, Reason1} ->
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
catch gen_tcp:connect(Addr, Port,
[binary, {packet, 0},
{active, false}, inet6]);
{'EXIT', Reason1} ->
?DEBUG("s2s_out: connect crashed ~p~n", [Reason1]),
catch gen_tcp:connect(Addr, Port,
[binary, {packet, 0},
{active, false}, inet6])
[binary, {packet, 0}, inet6])
end
end,
case Res of
{ok, Socket} ->
{ok, Socket};
XMLStreamPid = xml_stream:start(self()),
send_text(Socket, io_lib:format(?STREAM_HEADER,
[StateData#state.server])),
{next_state, wait_for_stream,
StateData#state{socket = Socket,
xmlpid = XMLStreamPid,
streamid = new_id()}};
{error, Reason} ->
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
{error, Reason};
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData};
{'EXIT', Reason} ->
?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
{error, Reason}
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_messages(Error),
{stop, normal, StateData}
end.
%%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
case {xml:get_attr_s("xmlns", Attrs),
xml:get_attr_s("xmlns:db", Attrs),
xml:get_attr_s("version", Attrs) == "1.0"} of
{"jabber:server", "jabber:server:dialback", false} ->
send_db_request(StateData);
{"jabber:server", "jabber:server:dialback", true} when
StateData#state.use_v10 ->
{next_state, wait_for_features, StateData};
{"jabber:server", "", true} when StateData#state.use_v10 ->
{next_state, wait_for_features, StateData#state{db_enabled = false}};
% TODO
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
{"jabber:server", "jabber:server:dialback"} ->
Server = StateData#state.server,
New = case StateData#state.new of
false ->
case
ejabberd_s2s:try_register(
{StateData#state.myname, Server}) of
{key, Key} ->
Key;
false ->
false
end;
Key ->
Key
end,
case New of
false ->
ok;
Key1 ->
send_element(StateData#state.socket,
{xmlelement,
"db:result",
[{"from", StateData#state.myname},
{"to", Server}],
[{xmlcdata, Key1}]})
end,
case StateData#state.verify of
false ->
ok;
{Pid, Key2, SID} ->
send_element(StateData#state.socket,
{xmlelement,
"db:verify",
[{"from", StateData#state.myname},
{"to", StateData#state.server},
{"id", SID}],
[{xmlcdata, Key2}]})
end,
{next_state, wait_for_validation, StateData#state{new = New}};
_ ->
send_text(StateData, ?INVALID_NAMESPACE_ERR),
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
{stop, normal, StateData}
end;
wait_for_stream({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
send_text(StateData#state.socket,
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_stream(timeout, StateData) ->
@@ -249,7 +209,7 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
?INFO_MSG("recv result: ~p", [{From, To, Id, Type}]),
case Type of
"valid" ->
send_queue(StateData, StateData#state.queue),
send_queue(StateData#state.socket, StateData#state.queue),
{next_state, stream_established,
StateData#state{queue = queue:new()}};
_ ->
@@ -274,12 +234,7 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
StateData#state.server,
StateData#state.myname})
end,
if
StateData#state.verify == false ->
{stop, normal, StateData};
true ->
{next_state, wait_for_validation, StateData}
end
{stop, normal, StateData}
end;
_ ->
{next_state, wait_for_validation, StateData}
@@ -289,8 +244,8 @@ wait_for_validation({xmlstreamend, Name}, StateData) ->
{stop, normal, StateData};
wait_for_validation({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
send_text(StateData#state.socket,
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_validation(timeout, StateData) ->
@@ -300,234 +255,6 @@ wait_for_validation(closed, StateData) ->
{stop, normal, StateData}.
wait_for_features({xmlstreamelement, El}, StateData) ->
case El of
{xmlelement, "stream:features", _Attrs, Els} ->
{SASLEXT, StartTLS, StartTLSRequired} =
lists:foldl(
fun({xmlelement, "mechanisms", Attrs1, Els1} = El1,
{SEXT, STLS, STLSReq} = Acc) ->
case xml:get_attr_s("xmlns", Attrs1) of
?NS_SASL ->
NewSEXT =
lists:any(
fun({xmlelement, "mechanism", _, Els2}) ->
case xml:get_cdata(Els2) of
"EXTERNAL" -> true;
_ -> false
end;
(_) -> false
end, Els1),
{NewSEXT, STLS, STLSReq};
_ ->
Acc
end;
({xmlelement, "starttls", Attrs1, Els1} = El1,
{SEXT, STLS, STLSReq} = Acc) ->
case xml:get_attr_s("xmlns", Attrs1) of
?NS_TLS ->
Req = case xml:get_subtag(El1, "required") of
{xmlelement, _, _, _} -> true;
false -> false
end,
{SEXT, true, Req};
_ ->
Acc
end;
(_, Acc) ->
Acc
end, {false, false, false}, Els),
if
(not SASLEXT) and (not StartTLS) and
StateData#state.authenticated ->
send_queue(StateData, StateData#state.queue),
{next_state, stream_established,
StateData#state{queue = queue:new()}};
SASLEXT and StateData#state.try_auth and
(StateData#state.new /= false) ->
send_element(StateData,
{xmlelement, "auth",
[{"xmlns", ?NS_SASL},
{"mechanism", "EXTERNAL"}],
[{xmlcdata,
jlib:encode_base64(
StateData#state.myname)}]}),
{next_state, wait_for_auth_result,
StateData#state{try_auth = false}};
StartTLS and StateData#state.tls and
(not StateData#state.tls_enabled) ->
send_element(StateData,
{xmlelement, "starttls",
[{"xmlns", ?NS_TLS}], []}),
{next_state, wait_for_starttls_proceed, StateData};
StartTLSRequired and (not StateData#state.tls) ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket,
StateData#state{socket = undefined,
use_v10 = false}};
StateData#state.db_enabled ->
send_db_request(StateData);
true ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
% TODO: clear message queue
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket, StateData#state{socket = undefined,
use_v10 = false}}
end;
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
{stop, normal, StateData}
end;
wait_for_features({xmlstreamend, Name}, StateData) ->
{stop, normal, StateData};
wait_for_features({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_features(timeout, StateData) ->
{stop, normal, StateData};
wait_for_features(closed, StateData) ->
{stop, normal, StateData}.
wait_for_auth_result({xmlstreamelement, El}, StateData) ->
case El of
{xmlelement, "success", Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_SASL ->
?INFO_MSG("auth: ~p", [{StateData#state.myname,
StateData#state.server}]),
ejabberd_receiver:reset_stream(
StateData#state.receiver),
send_text(StateData,
io_lib:format(?STREAM_HEADER,
[StateData#state.server,
" version='1.0'"])),
{next_state, wait_for_stream,
StateData#state{streamid = new_id(),
authenticated = true
}};
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
{stop, normal, StateData}
end;
{xmlelement, "failure", Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_SASL ->
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
ejabberd_receiver:close(StateData#state.receiver),
{next_state, reopen_socket,
StateData#state{socket = undefined}};
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
{stop, normal, StateData}
end;
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
{stop, normal, StateData}
end;
wait_for_auth_result({xmlstreamend, Name}, StateData) ->
{stop, normal, StateData};
wait_for_auth_result({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_auth_result(timeout, StateData) ->
{stop, normal, StateData};
wait_for_auth_result(closed, StateData) ->
{stop, normal, StateData}.
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
case El of
{xmlelement, "proceed", Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_TLS ->
?INFO_MSG("starttls: ~p", [{StateData#state.myname,
StateData#state.server}]),
Socket = StateData#state.socket,
TLSOpts = case ejabberd_config:get_local_option(
{domain_certfile,
StateData#state.server}) of
undefined ->
StateData#state.tls_options;
CertFile ->
[{certfile, CertFile} |
lists:keydelete(
certfile, 1,
StateData#state.tls_options)]
end,
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(
StateData#state.receiver, TLSSocket),
NewStateData = StateData#state{sockmod = tls,
socket = TLSSocket,
streamid = new_id(),
tls_enabled = true
},
send_text(NewStateData,
io_lib:format(?STREAM_HEADER,
[StateData#state.server,
" version='1.0'"])),
{next_state, wait_for_stream, NewStateData};
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
{stop, normal, StateData}
end;
_ ->
{stop, normal, StateData}
end;
wait_for_starttls_proceed({xmlstreamend, Name}, StateData) ->
{stop, normal, StateData};
wait_for_starttls_proceed({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_starttls_proceed(timeout, StateData) ->
{stop, normal, StateData};
wait_for_starttls_proceed(closed, StateData) ->
{stop, normal, StateData}.
reopen_socket({xmlstreamelement, El}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket({xmlstreamend, Name}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket({xmlstreamerror, _}, StateData) ->
{next_state, reopen_socket, StateData};
reopen_socket(timeout, StateData) ->
{stop, normal, StateData};
reopen_socket(closed, StateData) ->
gen_fsm:send_event(self(), init),
{next_state, open_socket, StateData}.
stream_established({xmlstreamelement, El}, StateData) ->
?INFO_MSG("stream established", []),
case is_verify_res(El) of
@@ -559,8 +286,8 @@ stream_established({xmlstreamend, Name}, StateData) ->
{stop, normal, StateData};
stream_established({xmlstreamerror, _}, StateData) ->
send_text(StateData,
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
send_text(StateData#state.socket,
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
stream_established(timeout, StateData) ->
@@ -616,7 +343,7 @@ code_change(OldVsn, StateName, StateData, Extra) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
send_text(StateData#state.socket, Text),
cancel_timer(StateData#state.timer),
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
{next_state, StateName, StateData#state{timer = Timer}};
@@ -626,7 +353,7 @@ handle_info({send_element, El}, StateName, StateData) ->
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
case StateName of
stream_established ->
send_element(StateData, El),
send_element(StateData#state.socket, El),
{next_state, StateName, StateData#state{timer = Timer}};
_ ->
Q = queue:in(El, StateData#state.queue),
@@ -634,17 +361,17 @@ handle_info({send_element, El}, StateName, StateData) ->
timer = Timer}}
end;
%handle_info({tcp, Socket, Data}, StateName, StateData) ->
% xml_stream:send_text(StateData#state.xmlpid, Data),
% {next_state, StateName, StateData};
%
%handle_info({tcp_closed, Socket}, StateName, StateData) ->
% gen_fsm:send_event(self(), closed),
% {next_state, StateName, StateData};
%
%handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
% gen_fsm:send_event(self(), closed),
% {next_state, StateName, StateData};
handle_info({tcp, Socket, Data}, StateName, StateData) ->
xml_stream:send_text(StateData#state.xmlpid, Data),
{next_state, StateName, StateData};
handle_info({tcp_closed, Socket}, StateName, StateData) ->
gen_fsm:send_event(self(), closed),
{next_state, StateName, StateData};
handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
gen_fsm:send_event(self(), closed),
{next_state, StateName, StateData};
handle_info({timeout, Timer, _}, StateName,
#state{timer = Timer} = StateData) ->
@@ -660,19 +387,21 @@ handle_info(_, StateName, StateData) ->
%%----------------------------------------------------------------------
terminate(Reason, StateName, StateData) ->
?INFO_MSG("terminated: ~p", [Reason]),
bounce_queue(StateData#state.queue, ?ERR_REMOTE_SERVER_NOT_FOUND),
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
bounce_queue(StateData#state.queue, Error),
case StateData#state.new of
false ->
ok;
Key ->
ejabberd_s2s:remove_connection({StateData#state.myname,
StateData#state.server})
ejabberd_s2s ! {closed_conection, {StateData#state.myname,
StateData#state.server}}
end,
case StateData#state.socket of
undefined ->
ok;
_Socket ->
ejabberd_receiver:close(StateData#state.receiver)
Socket ->
gen_tcp:close(Socket),
exit(StateData#state.xmlpid, closed)
end,
ok.
@@ -680,17 +409,17 @@ terminate(Reason, StateName, StateData) ->
%%% Internal functions
%%%----------------------------------------------------------------------
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text).
send_text(Socket, Text) ->
gen_tcp:send(Socket,Text).
send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)).
send_element(Socket, El) ->
send_text(Socket, xml:element_to_string(El)).
send_queue(StateData, Q) ->
send_queue(Socket, Q) ->
case queue:out(Q) of
{{value, El}, Q1} ->
send_element(StateData, El),
send_queue(StateData, Q1);
send_element(Socket, El),
send_queue(Socket, Q1);
{empty, Q1} ->
ok
end.
@@ -737,46 +466,20 @@ bounce_messages(Error) ->
ok
end.
send_db_request(StateData) ->
Server = StateData#state.server,
New = case StateData#state.new of
false ->
case ejabberd_s2s:try_register(
{StateData#state.myname, Server}) of
{key, Key} ->
Key;
false ->
false
end;
Key ->
Key
end,
case New of
false ->
ok;
Key1 ->
send_element(StateData,
{xmlelement,
"db:result",
[{"from", StateData#state.myname},
{"to", Server}],
[{xmlcdata, Key1}]})
end,
case StateData#state.verify of
false ->
ok;
{Pid, Key2, SID} ->
send_element(StateData,
{xmlelement,
"db:verify",
[{"from", StateData#state.myname},
{"to", StateData#state.server},
{"id", SID}],
[{xmlcdata, Key2}]})
end,
{next_state, wait_for_validation, StateData#state{new = New}}.
%is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
% {key,
% xml:get_attr_s("to", Attrs),
% xml:get_attr_s("from", Attrs),
% xml:get_attr_s("id", Attrs),
% xml:get_cdata(Els)};
%is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
% {verify,
% xml:get_attr_s("to", Attrs),
% xml:get_attr_s("from", Attrs),
% xml:get_attr_s("id", Attrs),
% xml:get_cdata(Els)};
%is_key_packet(_) ->
% false.
is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
{result,
@@ -808,50 +511,18 @@ get_addr_port(Server) ->
case Res of
{error, Reason} ->
?DEBUG("srv lookup of '~s' failed: ~p~n", [Server, Reason]),
[{Server, ejabberd_config:get_local_option(outgoing_s2s_port)}];
{Server, ejabberd_config:get_local_option(outgoing_s2s_port)};
{ok, HEnt} ->
?DEBUG("srv lookup of '~s': ~p~n",
[Server, HEnt#hostent.h_addr_list]),
case HEnt#hostent.h_addr_list of
[] ->
[{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)}];
AddrList ->
% Probabilities are not exactly proportional to weights
% for simplicity (higher weigths are overvalued)
{A1, A2, A3} = now(),
random:seed(A1, A2, A3),
case (catch lists:map(
fun({Priority, Weight, Port, Host}) ->
N = case Weight of
0 -> 0;
_ -> (Weight + 1) * random:uniform()
end,
{Priority * 65536 - N, Host, Port}
end, AddrList)) of
{'EXIT', _Reasn} ->
[{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)}];
SortedList ->
List = lists:map(
fun({_, Host, Port}) ->
{Host, Port}
end, lists:keysort(1, SortedList)),
?DEBUG("srv lookup of '~s': ~p~n", [Server, List]),
List
end
{Server,
ejabberd_config:get_local_option(outgoing_s2s_port)};
[{_, _, Port, Host} | _] ->
{Host, Port}
end
end.
test_get_addr_port(Server) ->
lists:foldl(
fun(_, Acc) ->
[HostPort | _] = get_addr_port(Server),
case lists:keysearch(HostPort, 1, Acc) of
false ->
[{HostPort, 1} | Acc];
{value, {_, Num}} ->
lists:keyreplace(HostPort, 1, Acc, {HostPort, Num + 1})
end
end, [], lists:seq(1, 100000)).
+25 -25
View File
@@ -13,11 +13,7 @@
-behaviour(gen_fsm).
%% External exports
-export([start/2,
start_link/2,
send_text/2,
send_element/2,
become_controller/1]).
-export([start/2, start_link/2, receiver/3, send_text/2, send_element/2]).
%% gen_fsm callbacks
-export([init/1,
@@ -79,9 +75,6 @@ start(SockData, Opts) ->
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_service, [SockData, Opts], ?FSMOPTS).
become_controller(Pid) ->
gen_fsm:send_all_state_event(Pid, become_controller).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -123,7 +116,7 @@ init([{SockMod, Socket}, Opts]) ->
false
end
end,
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
ReceiverPid = spawn(?MODULE, receiver, [Socket, SockMod, self()]),
{ok, wait_for_stream, #state{socket = Socket,
receiver = ReceiverPid,
streamid = new_id(),
@@ -178,11 +171,11 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
end, StateData#state.hosts),
{next_state, stream_established, StateData};
_ ->
send_text(StateData, ?INVALID_HANDSHAKE_ERR),
send_text(StateData, ?INVALID_HEADER_ERR),
{stop, normal, StateData}
end;
_ ->
{next_state, wait_for_handshake, StateData}
{next_state, wait_for_key, StateData}
end;
wait_for_handshake({xmlstreamend, _Name}, StateData) ->
@@ -197,8 +190,7 @@ wait_for_handshake(closed, StateData) ->
stream_established({xmlstreamelement, El}, StateData) ->
NewEl = jlib:remove_attr("xmlns", El),
{xmlelement, Name, Attrs, _Els} = NewEl,
{xmlelement, Name, Attrs, _Els} = El,
From = xml:get_attr_s("from", Attrs),
FromJID1 = jlib:string_to_jid(From),
FromJID = case FromJID1 of
@@ -218,9 +210,9 @@ stream_established({xmlstreamelement, El}, StateData) ->
(Name == "message") or
(Name == "presence")) and
(ToJID /= error) and (FromJID /= error) ->
ejabberd_router:route(FromJID, ToJID, NewEl);
ejabberd_router:route(FromJID, ToJID, El);
true ->
Err = jlib:make_error_reply(NewEl, ?ERR_BAD_REQUEST),
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
send_element(StateData, Err),
error
end,
@@ -259,12 +251,6 @@ stream_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(become_controller, StateName, StateData) ->
ok = (StateData#state.sockmod):controlling_process(
StateData#state.socket,
StateData#state.receiver),
ejabberd_receiver:become_controller(StateData#state.receiver),
{next_state, StateName, StateData};
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
@@ -297,7 +283,7 @@ handle_info({send_element, El}, StateName, StateData) ->
send_element(StateData, El),
{next_state, StateName, StateData};
handle_info({route, From, To, Packet}, StateName, StateData) ->
case acl:match_rule(global, StateData#state.access, From) of
case acl:match_rule(StateData#state.access, From) of
allow ->
{xmlelement, Name, Attrs, Els} = Packet,
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
@@ -317,8 +303,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
%% Purpose: Shutdown the fsm
%% Returns: any
%%----------------------------------------------------------------------
terminate(Reason, StateName, StateData) ->
?INFO_MSG("terminated: ~p", [Reason]),
terminate(_Reason, StateName, StateData) ->
case StateName of
stream_established ->
lists:foreach(
@@ -328,13 +313,28 @@ terminate(Reason, StateName, StateData) ->
_ ->
ok
end,
ejabberd_receiver:close(StateData#state.receiver),
(StateData#state.sockmod):close(StateData#state.socket),
ok.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
receiver(Socket, SockMod, C2SPid) ->
XMLStreamPid = xml_stream:start(C2SPid),
receiver(Socket, SockMod, C2SPid, XMLStreamPid).
receiver(Socket, SockMod, C2SPid, XMLStreamPid) ->
case SockMod:recv(Socket, 0) of
{ok, Text} ->
xml_stream:send_text(XMLStreamPid, Text),
receiver(Socket, SockMod, C2SPid, XMLStreamPid);
{error, _Reason} ->
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok
end.
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket,Text).
+225 -434
View File
@@ -8,288 +8,133 @@
-module(ejabberd_sm).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([start_link/0,
route/3,
open_session/4, close_session/4,
bounce_offline_message/3,
disconnect_removed_user/2,
get_user_resources/2,
set_presence/6,
unset_presence/5,
close_session_unset_presence/5,
-export([start_link/0, init/0, open_session/2, close_session/2,
get_user_resources/1,
set_presence/3,
unset_presence/3,
dirty_get_sessions_list/0,
dirty_get_my_sessions_list/0,
get_vh_session_list/1,
register_iq_handler/3,
register_iq_handler/4,
register_iq_handler/5,
unregister_iq_handler/2,
ctl_process/2
unregister_iq_handler/1
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("ejabberd_ctl.hrl").
-record(session, {sid, usr, us, priority}).
-record(state, {}).
-record(session, {ur, user, pid}).
-record(presence, {ur, user, priority}).
%% default value for the maximum number of user connections
-define(MAX_USER_SESSIONS, infinity).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
register(ejabberd_sm, Pid),
{ok, Pid}.
route(From, To, Packet) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end.
open_session(SID, User, Server, Resource) ->
set_session(SID, User, Server, Resource, undefined),
check_for_sessions_to_replace(User, Server, Resource),
JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver,
[SID, JID]).
close_session(SID, User, Server, Resource) ->
F = fun() ->
mnesia:delete({session, SID})
end,
mnesia:sync_dirty(F),
JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
[SID, JID]).
bounce_offline_message(From, To, Packet) ->
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err),
stop.
disconnect_removed_user(User, Server) ->
ejabberd_sm:route(jlib:make_jid("", "", ""),
jlib:make_jid(User, Server, ""),
{xmlelement, "broadcast", [],
[{exit, "User removed"}]}).
get_user_resources(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case catch mnesia:dirty_index_read(session, US, #session.us) of
{'EXIT', _Reason} ->
[];
Ss ->
[element(3, S#session.usr) || S <- clean_session_list(Ss)]
end.
set_presence(SID, User, Server, Resource, Priority, Presence) ->
set_session(SID, User, Server, Resource, Priority),
ejabberd_hooks:run(set_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Presence]).
unset_presence(SID, User, Server, Resource, Status) ->
set_session(SID, User, Server, Resource, undefined),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
close_session_unset_presence(SID, User, Server, Resource, Status) ->
close_session(SID, User, Server, Resource),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
dirty_get_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{usr = '$1', _ = '_'},
[],
['$1']}]).
dirty_get_my_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{sid = {'_', '$1'}, _ = '_'},
[{'==', {node, '$1'}, node()}],
['$_']}]).
get_vh_session_list(Server) ->
LServer = jlib:nameprep(Server),
mnesia:dirty_select(
session,
[{#session{usr = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, LServer}],
['$1']}]).
register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
unregister_iq_handler(Host, XMLNS) ->
ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
init() ->
update_tables(),
mnesia:create_table(session,
[{ram_copies, [node()]},
{attributes, record_info(fields, session)}]),
mnesia:add_table_index(session, usr),
mnesia:add_table_index(session, us),
mnesia:create_table(session, [{ram_copies, [node()]},
{attributes, record_info(fields, session)}]),
mnesia:add_table_index(session, user),
mnesia:add_table_copy(session, node(), ram_copies),
mnesia:create_table(presence,
[{ram_copies, [node()]},
{attributes, record_info(fields, presence)}]),
mnesia:add_table_index(presence, user),
mnesia:subscribe(system),
ets:new(sm_iqtable, [named_table]),
lists:foreach(
fun(Host) ->
ejabberd_hooks:add(offline_message_hook, Host,
ejabberd_sm, bounce_offline_message, 100),
ejabberd_hooks:add(remove_user, Host,
ejabberd_sm, disconnect_removed_user, 100)
end, ?MYHOSTS),
ejabberd_ctl:register_commands(
[{"connected-users", "list all established sessions"},
{"connected-users-number", "print a number of established sessions"},
{"user-resources user server", "print user's connected resources"}],
?MODULE, ctl_process),
loop().
{ok, #state{}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
loop() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
loop();
{open_session, User, Resource, From} ->
register_connection(User, Resource, From),
loop();
{close_session, User, Resource} ->
remove_connection(User, Resource),
loop();
{mnesia_system_event, {mnesia_down, Node}} ->
clean_table_from_bad_node(Node),
loop();
{register_iq_handler, XMLNS, Module, Function} ->
ets:insert(sm_iqtable, {XMLNS, Module, Function}),
loop();
{register_iq_handler, XMLNS, Module, Function, Opts} ->
ets:insert(sm_iqtable, {XMLNS, Module, Function, Opts}),
loop();
{unregister_iq_handler, XMLNS} ->
case ets:lookup(sm_iqtable, XMLNS) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(sm_iqtable, XMLNS),
loop();
_ ->
ok
end,
{noreply, State};
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
clean_table_from_bad_node(Node),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
ets:delete(sm_iqtable, {XMLNS, Host}),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
loop()
end.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
open_session(User, Resource) ->
ejabberd_sm ! {open_session, User, Resource, self()}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
close_session(User, Resource) ->
ejabberd_sm ! {close_session, User, Resource}.
set_session(SID, User, Server, Resource, Priority) ->
register_connection(User, Resource, Pid) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LResource = jlib:resourceprep(Resource),
US = {LUser, LServer},
USR = {LUser, LServer, LResource},
UR = {LUser, LResource},
F = fun() ->
mnesia:write(#session{sid = SID,
usr = USR,
us = US,
priority = Priority})
end,
mnesia:sync_dirty(F).
Ss = mnesia:wread({session, UR}),
mnesia:write(#session{ur = UR, user = LUser, pid = Pid}),
Ss
end,
case mnesia:transaction(F) of
{atomic, Ss} ->
lists:foreach(
fun(R) ->
R#session.pid ! replaced
end, Ss);
_ ->
false
end.
remove_connection(User, Resource) ->
LUser = jlib:nodeprep(User),
LResource = jlib:resourceprep(Resource),
UR = {LUser, LResource},
F = fun() ->
mnesia:delete({session, UR})
end,
mnesia:transaction(F).
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
session,
[{#session{sid = {'_', '$1'}, _ = '_'},
[{#session{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
mnesia:delete({session, E#session.sid})
mnesia:delete_object(E)
end, Es)
end,
mnesia:sync_dirty(F).
mnesia:transaction(F).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -297,7 +142,7 @@ clean_table_from_bad_node(Node) ->
do_route(From, To, Packet) ->
?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
[From, To, Packet, 8]),
#jid{user = User, server = Server,
#jid{user = User,
luser = LUser, lserver = LServer, lresource = LResource} = To,
{xmlelement, Name, Attrs, _Els} = Packet,
case LResource of
@@ -307,55 +152,62 @@ do_route(From, To, Packet) ->
{Pass, Subsc} =
case xml:get_attr_s("type", Attrs) of
"subscribe" ->
Reason = xml:get_path_s(
Packet,
[{elem, "status"}, cdata]),
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
[User, Server, From, subscribe, Reason]),
{mod_roster:in_subscription(User,
From,
subscribe),
true};
"subscribed" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
[User, Server, From, subscribed, ""]),
{mod_roster:in_subscription(User,
From,
subscribed),
true};
"unsubscribe" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
[User, Server, From, unsubscribe, ""]),
{mod_roster:in_subscription(User,
From,
unsubscribe),
true};
"unsubscribed" ->
{ejabberd_hooks:run_fold(
roster_in_subscription,
LServer,
false,
[User, Server, From, unsubscribed, ""]),
{mod_roster:in_subscription(User,
From,
unsubscribed),
true};
_ ->
{true, false}
end,
if Pass ->
LFrom = jlib:jid_tolower(From),
PResources = get_user_present_resources(
LUser, LServer),
lists:foreach(
fun({_, R}) ->
if LFrom /=
{LUser, LServer, R} ->
do_route(
From,
jlib:jid_replace_resource(To, R),
Packet);
true ->
ok
end
end, PResources);
PResources = get_user_present_resources(User),
if
PResources /= [] ->
lists:foreach(
fun({_, R}) ->
if LFrom /=
{LUser, LServer, R} ->
ejabberd_sm !
{route,
From,
jlib:jid_replace_resource(To, R),
Packet};
true ->
ok
end
end, PResources);
true ->
if
Subsc ->
case ejabberd_auth:is_user_exists(LUser) of
true ->
catch mod_offline:store_packet(
From, To, Packet);
_ ->
Err = jlib:make_error_reply(
Packet, ?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err)
end;
true ->
ok
end
end;
true ->
ok
end;
@@ -366,16 +218,17 @@ do_route(From, To, Packet) ->
"broadcast" ->
lists:foreach(
fun(R) ->
do_route(From,
jlib:jid_replace_resource(To, R),
Packet)
end, get_user_resources(User, Server));
ejabberd_sm ! {route,
From,
jlib:jid_replace_resource(To, R),
Packet}
end, get_user_resources(User));
_ ->
ok
end;
_ ->
USR = {LUser, LServer, LResource},
case mnesia:dirty_index_read(session, USR, #session.usr) of
LUR = {LUser, LResource},
case mnesia:dirty_read({session, LUR}) of
[] ->
case Name of
"message" ->
@@ -393,9 +246,8 @@ do_route(From, To, Packet) ->
_ ->
?DEBUG("packet droped~n", [])
end;
Ss ->
Session = lists:max(Ss),
Pid = element(2, Session#session.sid),
[Sess] ->
Pid = Sess#session.pid,
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end
@@ -403,136 +255,96 @@ do_route(From, To, Packet) ->
route_message(From, To, Packet) ->
LUser = To#jid.luser,
LServer = To#jid.lserver,
PrioRes = get_user_present_resources(LUser, LServer),
case catch lists:max(PrioRes) of
{Priority, _R} when is_integer(Priority), Priority >= 0 ->
lists:foreach(
%% Route messages to all priority that equals the max, if
%% positive
fun({P, R}) when P == Priority ->
LResource = jlib:resourceprep(R),
USR = {LUser, LServer, LResource},
case mnesia:dirty_index_read(session, USR, #session.usr) of
[] ->
ok; % Race condition
Ss ->
Session = lists:max(Ss),
Pid = element(2, Session#session.sid),
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end;
%% Ignore other priority:
({_Prio, _Res}) ->
ok
end,
PrioRes);
_ ->
case catch lists:max(get_user_present_resources(LUser)) of
{'EXIT', _} ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
ok;
_ ->
case ejabberd_auth:is_user_exists(LUser, LServer) of
case ejabberd_auth:is_user_exists(LUser) of
true ->
ejabberd_hooks:run(offline_message_hook,
LServer,
[From, To, Packet]);
case catch mod_offline:store_packet(
From, To, Packet) of
{'EXIT', _} ->
Err = jlib:make_error_reply(
Packet, ?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err);
_ ->
ok
end;
_ ->
Err = jlib:make_error_reply(
Packet, ?ERR_SERVICE_UNAVAILABLE),
Packet, ?ERR_ITEM_NOT_FOUND),
ejabberd_router:route(To, From, Err)
end
end;
{_, R} ->
LResource = jlib:resourceprep(R),
LUR = {LUser, LResource},
case mnesia:dirty_read({session, LUR}) of
[] ->
ok; % Race condition
[Sess] ->
Pid = Sess#session.pid,
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clean_session_list(Ss) ->
clean_session_list(lists:keysort(#session.usr, Ss), []).
clean_session_list([], Res) ->
Res;
clean_session_list([S], Res) ->
[S | Res];
clean_session_list([S1, S2 | Rest], Res) ->
if
S1#session.usr == S2#session.usr ->
if
S1#session.sid > S2#session.sid ->
clean_session_list([S1 | Rest], Res);
true ->
clean_session_list([S2 | Rest], Res)
end;
true ->
clean_session_list([S2 | Rest], [S1 | Res])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_user_present_resources(LUser, LServer) ->
US = {LUser, LServer},
case catch mnesia:dirty_index_read(session, US, #session.us) of
get_user_resources(User) ->
LUser = jlib:nodeprep(User),
case catch mnesia:dirty_index_read(session, LUser, #session.user) of
{'EXIT', _Reason} ->
[];
Ss ->
[{S#session.priority, element(3, S#session.usr)} ||
S <- clean_session_list(Ss), is_integer(S#session.priority)]
Rs ->
lists:map(fun(R) ->
element(2, R#session.ur)
end, Rs)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% On new session, check if some existing connections need to be replace
check_for_sessions_to_replace(User, Server, Resource) ->
set_presence(User, Resource, Priority) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LResource = jlib:resourceprep(Resource),
F = fun() ->
UR = {User, Resource},
mnesia:write(#presence{ur = UR, user = LUser,
priority = Priority})
end,
mnesia:transaction(F).
%% TODO: Depending on how this is executed, there could be an unneeded
%% replacement for max_sessions. We need to check this at some point.
check_existing_resources(LUser, LServer, LResource),
check_max_sessions(LUser, LServer).
unset_presence(User, Resource, Status) ->
LUser = jlib:nodeprep(User),
F = fun() ->
UR = {User, Resource},
mnesia:delete({presence, UR})
end,
mnesia:transaction(F),
catch mod_last:on_presence_update(LUser, Status).
check_existing_resources(LUser, LServer, LResource) ->
USR = {LUser, LServer, LResource},
%% A connection exist with the same resource. We replace it:
SIDs = mnesia:dirty_select(
session,
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
if
SIDs == [] -> ok;
true ->
MaxSID = lists:max(SIDs),
lists:foreach(
fun({_, Pid} = S) when S /= MaxSID ->
Pid ! replaced;
(_) -> ok
end, SIDs)
get_user_present_resources(LUser) ->
case catch mnesia:dirty_index_read(presence, LUser, #presence.user) of
{'EXIT', _Reason} ->
[];
Rs ->
lists:map(fun(R) ->
{R#presence.priority, element(2, R#presence.ur)}
end, Rs)
end.
check_max_sessions(LUser, LServer) ->
%% If the max number of sessions for a given is reached, we replace the
%% first one
SIDs = mnesia:dirty_select(
session,
[{#session{sid = '$1', usr = {LUser, LServer, '_'}, _ = '_'}, [], ['$1']}]),
MaxSessions = get_max_user_sessions(LServer),
if length(SIDs) =< MaxSessions -> ok;
true -> {_, Pid} = lists:min(SIDs),
Pid ! replaced
end.
dirty_get_sessions_list() ->
mnesia:dirty_all_keys(session).
%% Get the user_max_session setting
%% This option defines the max number of time a given users are allowed to
%% log in
%% Defaults to infinity
get_max_user_sessions(Host) ->
case ejabberd_config:get_local_option({max_user_sessions, Host}) of
undefined -> ?MAX_USER_SESSIONS;
Max -> Max
end.
dirty_get_my_sessions_list() ->
mnesia:dirty_select(
session,
[{#session{pid = '$1', _ = '_'},
[{'==', {node, '$1'}, node()}],
['$_']}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -541,24 +353,25 @@ process_iq(From, To, Packet) ->
IQ = jlib:iq_query_info(Packet),
case IQ of
#iq{xmlns = XMLNS} ->
Host = To#jid.lserver,
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
case ets:lookup(sm_iqtable, XMLNS) of
[{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ),
if
ResIQ /= ignore ->
ejabberd_router:route(To, From,
jlib:iq_to_xml(ResIQ));
ejabberd_router ! {route,
To,
From,
jlib:iq_to_xml(ResIQ)};
true ->
ok
end;
[{_, Module, Function, Opts}] ->
gen_iq_handler:handle(Host, Module, Function, Opts,
gen_iq_handler:handle(Module, Function, Opts,
From, To, IQ);
[] ->
Err = jlib:make_error_reply(
Packet, ?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From, Err)
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
ejabberd_router ! {route, To, From, Err}
end;
reply ->
ok;
@@ -568,27 +381,15 @@ process_iq(From, To, Packet) ->
ok
end.
register_iq_handler(XMLNS, Module, Fun) ->
ejabberd_sm ! {register_iq_handler, XMLNS, Module, Fun}.
register_iq_handler(XMLNS, Module, Fun, Opts) ->
ejabberd_sm ! {register_iq_handler, XMLNS, Module, Fun, Opts}.
unregister_iq_handler(XMLNS) ->
ejabberd_sm ! {unregister_iq_handler, XMLNS}.
ctl_process(_Val, ["connected-users"]) ->
USRs = dirty_get_sessions_list(),
NewLine = io_lib:format("~n", []),
SUSRs = lists:sort(USRs),
FUSRs = lists:map(fun({U, S, R}) -> [U, $@, S, $/, R, NewLine] end, SUSRs),
io:format("~s", [FUSRs]),
{stop, ?STATUS_SUCCESS};
ctl_process(_Val, ["connected-users-number"]) ->
N = length(dirty_get_sessions_list()),
io:format("~p~n", [N]),
{stop, ?STATUS_SUCCESS};
ctl_process(_Val, ["user-resources", User, Server]) ->
Resources = get_user_resources(User, Server),
NewLine = io_lib:format("~n", []),
SResources = lists:sort(Resources),
FResources = lists:map(fun(R) -> [R, NewLine] end, SResources),
io:format("~s", [FResources]),
{stop, ?STATUS_SUCCESS};
ctl_process(Val, _Args) ->
Val.
update_tables() ->
@@ -596,20 +397,10 @@ update_tables() ->
[ur, user, node] ->
mnesia:delete_table(session);
[ur, user, pid] ->
mnesia:delete_table(session);
[usr, us, pid] ->
mnesia:delete_table(session);
[sid, usr, us, priority] ->
ok;
{'EXIT', _} ->
ok
end,
case lists:member(presence, mnesia:system_info(tables)) of
true ->
mnesia:delete_table(presence);
false ->
ok
end,
case lists:member(local_session, mnesia:system_info(tables)) of
true ->
mnesia:delete_table(local_session);
+31 -56
View File
@@ -19,13 +19,36 @@ start_link() ->
init([]) ->
Hooks =
{ejabberd_hooks,
{ejabberd_hooks, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_hooks]},
Router = {ejabberd_router,
{ejabberd_router, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_router]},
SM = {ejabberd_sm,
{ejabberd_sm, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_sm]},
S2S = {ejabberd_s2s,
{ejabberd_s2s, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_s2s]},
Local = {ejabberd_local,
{ejabberd_local, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_local]},
Listener = {ejabberd_listener,
{ejabberd_listener, start_link, []},
permanent,
infinity,
supervisor,
[ejabberd_listener]},
StringPrep =
{stringprep,
{stringprep, start_link, []},
@@ -33,49 +56,6 @@ init([]) ->
brutal_kill,
worker,
[stringprep]},
Router =
{ejabberd_router,
{ejabberd_router, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_router]},
SM =
{ejabberd_sm,
{ejabberd_sm, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_sm]},
S2S =
{ejabberd_s2s,
{ejabberd_s2s, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_s2s]},
Local =
{ejabberd_local,
{ejabberd_local, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_local]},
Listener =
{ejabberd_listener,
{ejabberd_listener, start_link, []},
permanent,
infinity,
supervisor,
[ejabberd_listener]},
ReceiverSupervisor =
{ejabberd_receiver_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_receiver_sup, ejabberd_receiver]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
C2SSupervisor =
{ejabberd_c2s_sup,
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
@@ -132,13 +112,8 @@ init([]) ->
supervisor,
[ejabberd_tmp_sup]},
{ok, {{one_for_one, 10, 1},
[Hooks,
[Router, SM, S2S, Local,
StringPrep,
Router,
SM,
S2S,
Local,
ReceiverSupervisor,
C2SSupervisor,
S2SInSupervisor,
S2SOutSupervisor,
-128
View File
@@ -1,128 +0,0 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_update.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Update ejabberd
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id: ejabberd_c2s.erl 492 2006-01-25 00:35:12Z alexey $
%%%-------------------------------------------------------------------
-module(ejabberd_update).
-author('alexey@sevcom.net').
%% API
-export([update/0, update_info/0]).
%%====================================================================
%% API
%%====================================================================
update() ->
case update_info() of
{ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} ->
Eval =
release_handler_1:eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]),
io:format("eval: ~p~n", [Eval]),
Eval;
{error, Reason} ->
{error, Reason}
end.
update_info() ->
Dir = filename:dirname(code:which(ejabberd)),
case file:list_dir(Dir) of
{ok, Files} ->
Beams = [list_to_atom(filename:rootname(FN)) ||
FN <- Files, lists:suffix(".beam", FN)],
UpdatedBeams =
lists:filter(
fun(Module) ->
{ok, {Module, [NewVsn]}} =
beam_lib:version(code:which(Module)),
case code:is_loaded(Module) of
{file, _} ->
Attrs = Module:module_info(attributes),
{value, {vsn, [CurVsn]}} =
lists:keysearch(vsn, 1, Attrs),
NewVsn /= CurVsn;
false ->
false
end
end, Beams),
io:format("beam files: ~p~n", [UpdatedBeams]),
Script = make_script(UpdatedBeams),
io:format("script: ~p~n", [Script]),
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
io:format("low level script: ~p~n", [LowLevelScript]),
Check =
release_handler_1:check_script(
LowLevelScript,
[{ejabberd, "", filename:join(Dir, "..")}]),
io:format("check: ~p~n", [Check]),
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check};
{error, Reason} ->
{error, Reason}
end.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%% From systools.hrl
-record(application,
{name, %% Name of the application, atom().
type = permanent, %% Application start type, atom().
vsn = "", %% Version of the application, string().
id = "", %% Id of the application, string().
description = "", %% Description of application, string().
modules = [], %% [Module | {Module,Vsn}] of modules
%% incorporated in the application,
%% Module = atom(), Vsn = string().
uses = [], %% [Application] list of applications required
%% by the application, Application = atom().
includes = [], %% [Application] list of applications included
%% by the application, Application = atom().
regs = [], %% [RegNames] a list of registered process
%% names used by the application, RegNames =
%% atom().
env = [], %% [{Key,Value}] environment variable of
%% application, Key = Value = term().
maxT = infinity, %% Max time an application may exist,
%% integer() | infinity.
maxP = infinity, %% Max number of processes in an application,
%% integer() | infinity.
mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
%% StartArgs = list().
start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(),
%% PhaseArgs = list().
dir = "" %% The directory where the .app file was
%% found (internal use).
}).
make_script(UpdatedBeams) ->
lists:map(
fun(Module) ->
{ok, {Module, [{attributes, NewAttrs}]}} =
beam_lib:chunks(code:which(Module), [attributes]),
CurAttrs = Module:module_info(attributes),
case lists:keysearch(update_info, 1, NewAttrs) of
{value, {_, [{update, _}]}} ->
case lists:keysearch(update_info, 1, CurAttrs) of
{value, {_, [{update, Extra}]}} ->
{update, Module, {advanced, Extra}};
false ->
{update, Module, {advanced, 0}}
end;
false ->
{load_module, Module}
end
end, UpdatedBeams).
make_low_level_script(UpdatedBeams, Script) ->
EJDApp = #application{name = ejabberd,
modules = UpdatedBeams},
{ok, LowLevelScript} =
systools_rc:translate_scripts([Script], [EJDApp], [EJDApp]),
LowLevelScript.
-45
View File
@@ -1,45 +0,0 @@
# $Id$
CC = @CC@
CFLAGS = @CFLAGS@ @ZLIB_CFLAGS@ @ERLANG_CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ @ZLIB_LIBS@ @ERLANG_LIBS@
ifeq ($(shell uname),Darwin)
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
else
# Assume Linux-style dynamic library flags
DYNAMIC_LIB_CFLAGS = -fpic -shared
endif
SUBDIRS =
ERLSHLIBS = ../ejabberd_zlib_drv.so
OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)/ejabberd_zlib.beam
all: $(OBJS) $(ERLSHLIBS)
$(OUTDIR)/%.beam: %.erl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
#all: $(ERLSHLIBS)
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
$(ERLSHLIBS): ../%.so: %.c
$(CC) -Wall $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
-o $@ $(DYNAMIC_LIB_CFLAGS)
clean:
rm -f $(OBJS) $(ERLSHLIBS)
distclean: clean
rm -f Makefile
TAGS:
etags *.erl
-37
View File
@@ -1,37 +0,0 @@
include ..\Makefile.inc
OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)\ejabberd_zlib.beam
SOURCE = ejabberd_zlib_drv.c
OBJECT = ejabberd_zlib_drv.o
DLL = $(OUTDIR)\ejabberd_zlib_drv.dll
ALL : $(DLL) $(OBJS)
CLEAN :
-@erase $(DLL)
-@erase $(OUTDIR)\ejabberd_zlib_drv.exp
-@erase $(OUTDIR)\ejabberd_zlib_drv.lib
-@erase $(OBJECT)
-@erase $(OBJS)
$(OUTDIR)\ejabberd_zlib.beam : ejabberd_zlib.erl
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_zlib.erl
CC=cl.exe
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(ZLIB_DIR)\include"
LD=link.exe
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(ZLIB_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)
-134
View File
@@ -1,134 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_zlib.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Interface to zlib
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_zlib).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-export([start/0, start_link/0,
enable_zlib/2, disable_zlib/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
controlling_process/2,
close/1]).
%% Internal exports, call-back functions.
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2]).
-define(DEFLATE, 1).
-define(INFLATE, 2).
-record(zlibsock, {sockmod, socket, zlibport}).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, Port}.
%%% --------------------------------------------------------
%%% The call-back functions.
%%% --------------------------------------------------------
handle_call(_, _, State) ->
{noreply, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Port, Reason}, Port) ->
{stop, {port_died, Reason}, Port};
handle_info({'EXIT', _Pid, _Reason}, Port) ->
{noreply, Port};
handle_info(_, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, Port) ->
Port ! {self, close},
ok.
enable_zlib(SockMod, Socket) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
port_close(Port),
{SockMod, Socket}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).
recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock,
Length, Timeout) ->
case SockMod:recv(Socket, Length, Timeout) of
{ok, Packet} ->
recv_data(ZlibSock, Packet);
{error, _Reason} = Error ->
Error
end.
recv_data(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
case port_control(Port, ?INFLATE, Packet) of
<<0, In/binary>> ->
{ok, In};
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
Packet) ->
case port_control(Port, ?DEFLATE, Packet) of
<<0, Out/binary>> ->
SockMod:send(Socket, Out);
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, Opts);
_ ->
SockMod:setopts(Socket, Opts)
end.
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) ->
SockMod:controlling_process(Socket, Pid).
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
SockMod:close(Socket),
port_close(Port).
-171
View File
@@ -1,171 +0,0 @@
/* $Id$ */
#include <stdio.h>
#include <string.h>
#include <erl_driver.h>
#include <zlib.h>
#define BUF_SIZE 1024
typedef struct {
ErlDrvPort port;
z_stream *d_stream;
z_stream *i_stream;
} ejabberd_zlib_data;
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
{
ejabberd_zlib_data *d =
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
d->port = port;
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
d->d_stream->zalloc = (alloc_func)0;
d->d_stream->zfree = (free_func)0;
d->d_stream->opaque = (voidpf)0;
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
d->i_stream->zalloc = (alloc_func)0;
d->i_stream->zfree = (free_func)0;
d->i_stream->opaque = (voidpf)0;
inflateInit(d->i_stream);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)d;
}
static void ejabberd_zlib_drv_stop(ErlDrvData handle)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
deflateEnd(d->d_stream);
free(d->d_stream);
inflateEnd(d->i_stream);
free(d->i_stream);
driver_free((char *)handle);
}
#define DEFLATE 1
#define INFLATE 2
#define die_unless(cond, errstr) \
if (!(cond)) \
{ \
rlen = strlen(errstr) + 1; \
b = driver_realloc_binary(b, rlen); \
b->orig_bytes[0] = 1; \
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
*rbuf = (char *)b; \
return rlen; \
}
static int ejabberd_zlib_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
int err;
int size;
ErlDrvBinary *b;
switch (command)
{
case DEFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
d->d_stream->next_in = buf;
d->d_stream->avail_in = len;
d->d_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->d_stream->avail_out == 0)
{
d->d_stream->next_out = b->orig_bytes + rlen;
d->d_stream->avail_out = BUF_SIZE;
err = deflate(d->d_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Deflate error");
rlen += (BUF_SIZE - d->d_stream->avail_out);
size += (BUF_SIZE - d->d_stream->avail_out);
b = driver_realloc_binary(b, size);
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
case INFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
if (len > 0) {
d->i_stream->next_in = buf;
d->i_stream->avail_in = len;
d->i_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->i_stream->avail_out == 0)
{
d->i_stream->next_out = b->orig_bytes + rlen;
d->i_stream->avail_out = BUF_SIZE;
err = inflate(d->i_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Inflate error");
rlen += (BUF_SIZE - d->i_stream->avail_out);
size += (BUF_SIZE - d->i_stream->avail_out);
b = driver_realloc_binary(b, size);
}
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
}
b = driver_alloc_binary(1);
b->orig_bytes[0] = 0;
*rbuf = (char *)b;
return 1;
}
ErlDrvEntry ejabberd_zlib_driver_entry = {
NULL, /* F_PTR init, N/A */
ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */
ejabberd_zlib_drv_stop, /* F_PTR stop, called when port is closed */
NULL, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* handle */
ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */
{
return &ejabberd_zlib_driver_entry;
}
-310
View File
@@ -1,310 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejd2odbc.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Export some mnesia tables to SQL DB
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejd2odbc).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
%% External exports
-export([export_passwd/2,
export_roster/2,
export_offline/2,
export_last/2,
export_vcard/2,
export_vcard_search/2]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_roster.hrl").
-record(offline_msg, {us, timestamp, expire, from, to, packet}).
-record(last_activity, {us, timestamp, status}).
-record(vcard, {us, vcard}).
-record(vcard_search, {us,
user, luser,
fn, lfn,
family, lfamily,
given, lgiven,
middle, lmiddle,
nickname, lnickname,
bday, lbday,
ctry, lctry,
locality, llocality,
email, lemail,
orgname, lorgname,
orgunit, lorgunit
}).
-define(MAX_RECORDS_PER_TRANSACTION, 1000).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
export_passwd(Server, Output) ->
export_common(
Server, passwd, Output,
fun(Host, {passwd, {LUser, LServer}, Password} = R)
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
Pass = ejabberd_odbc:escape(Password),
["delete from users where username='", Username ,"';"
"insert into users(username, password) "
"values ('", Username, "', '", Pass, "');"];
(_Host, _R) ->
[]
end).
export_roster(Server, Output) ->
export_common(
Server, roster, Output,
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
ItemVals = record_to_string(R),
ItemGroups = groups_to_string(R),
["delete from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"
"insert into rosterusers("
" username, jid, nick, "
" subscription, ask, askmessage, "
" server, subscribe, type) "
" values ", ItemVals, ";"
"delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';",
[["insert into rostergroups("
" username, jid, grp) "
" values ", ItemGroup, ";"] ||
ItemGroup <- ItemGroups]];
(_Host, _R) ->
[]
end).
export_offline(Server, Output) ->
export_common(
Server, offline_msg, Output,
fun(Host, #offline_msg{us = {LUser, LServer},
timestamp = TimeStamp,
from = From,
to = To,
packet = Packet})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
{xmlelement, Name, Attrs, Els} = Packet,
Attrs2 = jlib:replace_from_to_attrs(
jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
NewPacket = {xmlelement, Name, Attrs2,
Els ++
[jlib:timestamp_to_xml(
calendar:now_to_universal_time(TimeStamp))]},
XML =
ejabberd_odbc:escape(
lists:flatten(
xml:element_to_string(NewPacket))),
["insert into spool(username, xml) "
"values ('", Username, "', '",
XML,
"');"];
(_Host, _R) ->
[]
end).
export_last(Server, Output) ->
export_common(
Server, last_activity, Output,
fun(Host, #last_activity{us = {LUser, LServer},
timestamp = TimeStamp,
status = Status})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
State = ejabberd_odbc:escape(Status),
["delete from last where username='", Username, "';"
"insert into last(username, seconds, state) "
"values ('", Username, "', '", Seconds, "', '", State, "');"];
(_Host, _R) ->
[]
end).
export_vcard(Server, Output) ->
export_common(
Server, vcard, Output,
fun(Host, #vcard{us = {LUser, LServer},
vcard = VCARD})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
SVCARD = ejabberd_odbc:escape(
lists:flatten(xml:element_to_string(VCARD))),
["delete from vcard where username='", Username, "';"
"insert into vcard(username, vcard) "
"values ('", Username, "', '", SVCARD, "');"];
(_Host, _R) ->
[]
end).
export_vcard_search(Server, Output) ->
export_common(
Server, vcard_search, Output,
fun(Host, #vcard_search{user = {User, LServer},
luser = LUser,
fn = FN, lfn = LFN,
family = Family, lfamily = LFamily,
given = Given, lgiven = LGiven,
middle = Middle, lmiddle = LMiddle,
nickname = Nickname, lnickname = LNickname,
bday = BDay, lbday = LBDay,
ctry = CTRY, lctry = LCTRY,
locality = Locality, llocality = LLocality,
email = EMail, lemail = LEMail,
orgname = OrgName, lorgname = LOrgName,
orgunit = OrgUnit, lorgunit = LOrgUnit
})
when LServer == Host ->
Username = ejabberd_odbc:escape(User),
LUsername = ejabberd_odbc:escape(LUser),
SFN = ejabberd_odbc:escape(FN),
SLFN = ejabberd_odbc:escape(LFN),
SFamily = ejabberd_odbc:escape(Family),
SLFamily = ejabberd_odbc:escape(LFamily),
SGiven = ejabberd_odbc:escape(Given),
SLGiven = ejabberd_odbc:escape(LGiven),
SMiddle = ejabberd_odbc:escape(Middle),
SLMiddle = ejabberd_odbc:escape(LMiddle),
SNickname = ejabberd_odbc:escape(Nickname),
SLNickname = ejabberd_odbc:escape(LNickname),
SBDay = ejabberd_odbc:escape(BDay),
SLBDay = ejabberd_odbc:escape(LBDay),
SCTRY = ejabberd_odbc:escape(CTRY),
SLCTRY = ejabberd_odbc:escape(LCTRY),
SLocality = ejabberd_odbc:escape(Locality),
SLLocality = ejabberd_odbc:escape(LLocality),
SEMail = ejabberd_odbc:escape(EMail),
SLEMail = ejabberd_odbc:escape(LEMail),
SOrgName = ejabberd_odbc:escape(OrgName),
SLOrgName = ejabberd_odbc:escape(LOrgName),
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
["delete from vcard_search where lusername='", LUsername, "';"
"insert into vcard_search("
" username, lusername, fn, lfn, family, lfamily,"
" given, lgiven, middle, lmiddle, nickname, lnickname,"
" bday, lbday, ctry, lctry, locality, llocality,"
" email, lemail, orgname, lorgname, orgunit, lorgunit)"
"values (",
" '", Username, "', '", LUsername, "',"
" '", SFN, "', '", SLFN, "',"
" '", SFamily, "', '", SLFamily, "',"
" '", SGiven, "', '", SLGiven, "',"
" '", SMiddle, "', '", SLMiddle, "',"
" '", SNickname, "', '", SLNickname, "',"
" '", SBDay, "', '", SLBDay, "',"
" '", SCTRY, "', '", SLCTRY, "',"
" '", SLocality, "', '", SLLocality, "',"
" '", SEMail, "', '", SLEMail, "',"
" '", SOrgName, "', '", SLOrgName, "',"
" '", SOrgUnit, "', '", SLOrgUnit, "');"];
(_Host, _R) ->
[]
end).
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
export_common(Server, Table, Output, ConvertFun) ->
IO = case Output of
odbc ->
odbc;
_ ->
{ok, IODevice} = file:open(Output, [write, raw]),
IODevice
end,
mnesia:transaction(
fun() ->
mnesia:read_lock_table(Table),
LServer = jlib:nameprep(Server),
{_N, SQLs} =
mnesia:foldl(
fun(R, {N, SQLs} = Acc) ->
case ConvertFun(LServer, R) of
[] ->
Acc;
SQL ->
if
N < ?MAX_RECORDS_PER_TRANSACTION - 1 ->
{N + 1, [SQL | SQLs]};
true ->
output(LServer, IO,
["begin;",
lists:reverse([SQL | SQLs]),
"commit"]),
{0, []}
end
end
end, {0, []}, Table),
output(LServer, IO,
["begin;",
lists:reverse(SQLs),
"commit"])
end).
output(LServer, IO, SQL) ->
case IO of
odbc ->
catch ejabberd_odbc:sql_query(LServer, SQL);
_ ->
file:write(IO, [SQL, $;, $\n])
end.
record_to_string(#roster{usj = {User, Server, JID},
name = Name,
subscription = Subscription,
ask = Ask,
askmessage = AskMessage}) ->
Username = ejabberd_odbc:escape(User),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)),
Nick = ejabberd_odbc:escape(Name),
SSubscription = case Subscription of
both -> "B";
to -> "T";
from -> "F";
none -> "N"
end,
SAsk = case Ask of
subscribe -> "S";
unsubscribe -> "U";
both -> "B";
out -> "O";
in -> "I";
none -> "N"
end,
["("
"'", Username, "',"
"'", SJID, "',"
"'", Nick, "',"
"'", SSubscription, "',"
"'", SAsk, "',"
"'", AskMessage, "',"
"'N', '', 'item')"].
groups_to_string(#roster{usj = {User, Server, JID},
groups = Groups}) ->
Username = ejabberd_odbc:escape(User),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)),
[["("
"'", Username, "',"
"'", SJID, "',"
"'", ejabberd_odbc:escape(Group), "')"] || Group <- Groups].
+10 -6
View File
@@ -1,27 +1,31 @@
# $Id$
CC = @CC@
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ @ERLANG_LIBS@
LIBS = @LIBS@
INCLUDES = @ERLANG_CFLAGS@
LIBDIRS = @ERLANG_LIBS@
SUBDIRS =
OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)/eldap.beam \
$(OUTDIR)/ELDAPv3.beam \
$(OUTDIR)/eldap_filter.beam
$(OUTDIR)/ELDAPv3.beam
all: $(OBJS)
ELDAPv3.erl: ELDAPv3.asn
@ERLC@ -bber_bin -W $(EFLAGS) $<
erlc -bber_bin -W $(EFLAGS) $<
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
erlc -W $(EFLAGS) -o $(OUTDIR) $<
clean:
+1 -2
View File
@@ -6,8 +6,7 @@ EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)\eldap.beam \
$(OUTDIR)\ELDAPv3.beam \
$(OUTDIR)\eldap_filter.beam
$(OUTDIR)\ELDAPv3.beam
ALL : $(OBJS)
+2 -2
View File
@@ -765,7 +765,7 @@ retry_connect() ->
cmd_timeout(Timer, Id, S) ->
Dict = S#eldap.dict,
case dict:find(Id, Dict) of
{ok, [{Timer, From, Name}|Res]} ->
{ok, [{Id, Timer, From, Name}|Res]} ->
case Name of
searchRequest ->
{Res1, Ref1} = polish(Res),
@@ -808,7 +808,7 @@ polish([], Res, Ref) ->
%%-----------------------------------------------------------------------
connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts),
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true}, binary],
TcpOpts = [{packet, asn1}, {active, true}, binary],
case gen_tcp:connect(Host, S#eldap.port, TcpOpts) of
{ok, Socket} ->
case bind_request(Socket, S) of
-285
View File
@@ -1,285 +0,0 @@
%%%====================================================
%%% File: eldap_filter.erl
%%% Purpose: Converts String Representation of
%%% LDAP Search Filter (RFC 2254)
%%% to eldap's representation of filter
%%% Author: Evgeniy Khramtsov <xramtsov@gmail.com>
%%% License: GPL
%%%====================================================
-module(eldap_filter).
-author('xram@jabber.ru').
%%%======================
%%% Export functions
%%%======================
-export([parse/1,
parse/2,
do_sub/2
]).
%%%-------------------------------------------------------------------------
%%% Arity: parse/1
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} |
%%% {error, bad_filter}
%%%
%%% RFC2254_Filter = string().
%%%
%%% Description: Converts String Representation of LDAP Search Filter (RFC 2254)
%%% to eldap's representation of filter.
%%%
%%% Example:
%%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))").
%%%
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
%%% {present,"mail"}]}}
%%%-------------------------------------------------------------------------
parse(RFC2254_Filter) ->
parse(RFC2254_Filter, []).
%%%-------------------------------------------------------------------------
%%% Arity: parse/2
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) ->
%%% {ok, EldapFilter} |
%%% {error, bad_filter} |
%%% {error, bad_regexp} |
%%% {error, max_substitute_recursion}
%%%
%%% SubstValue = {RegExp, Value} | {RegExp, Value, N},
%%% RFC2254_Filter = RegExp = Value = string(),
%%% N = integer().
%%%
%%% Description: The same as parse/1, but substitutes N or all occurences
%%% of RegExp with Value *after* parsing.
%%%
%%% Example:
%%% > eldap_filter:parse(
%%% "(|(mail=%u@%d)(jid=%u@%d))",
%%% [{"%u", "xramtsov"},{"%d","gmail.com"}]).
%%%
%%% {ok,{'or',[{equalityMatch,{'AttributeValueAssertion',
%%% "mail",
%%% "xramtsov@gmail.com"}},
%%% {equalityMatch,{'AttributeValueAssertion',
%%% "jid",
%%% "xramtsov@gmail.com"}}]}}
%%%--------------------------------------------------------------------------
parse(RFC2254_Filter, ListOfSubValues) ->
case catch convert_filter(parse_filter(RFC2254_Filter), ListOfSubValues) of
[EldapFilter] when is_tuple(EldapFilter) ->
{ok, EldapFilter};
{regexp, Error} ->
{error, Error};
_ ->
{error, bad_filter}
end.
%%%==========================
%%% Internal functions
%%%==========================
%%%----------------------
%%% split/1,4
%%%----------------------
split(Filter) ->
split(Filter, 0, [], []).
split([], _, _, Result) ->
Result;
split([H|T], Num, Rest, Result) ->
NewNum = case H of
$( -> Num + 1;
$) -> Num - 1;
_ -> Num
end,
if
NewNum == 0 ->
X = Rest++[H],
LenX = length(X),
if
LenX > 2 ->
split(T, 0, [], Result ++ [lists:sublist(X, 2, LenX-2)]);
true ->
split(T, 0, Rest, Result)
end;
true ->
split(T, NewNum, Rest++[H], Result)
end.
%%%-----------------------
%%% parse_filter/1
%%%-----------------------
parse_filter(Filter) ->
case Filter of
[$! | T] ->
{'not', parse_filter(T)};
[$| | T] ->
{'or', parse_filter(T)};
[$& | T] ->
{'and', parse_filter(T)};
[$( | _] ->
parse_filter(split(Filter));
[List | _] when is_list(List) ->
[parse_filter(X) || X <- Filter];
_ ->
Filter
end.
%%%--------------------
%%% convert_filter/2
%%%--------------------
convert_filter({'not', [Val | _]}, Replace) ->
eldap:'not'(convert_filter(Val, Replace));
convert_filter({'or', Vals}, Replace) ->
eldap:'or'([convert_filter(X, Replace) || X <- Vals]);
convert_filter({'and', Vals}, Replace) ->
eldap:'and'([convert_filter(X, Replace) || X <- Vals]);
convert_filter([H|_] = Filter, Replace) when is_integer(H) ->
parse_attr(Filter, Replace);
convert_filter(Filter, Replace) when is_list(Filter) ->
[convert_filter(X, Replace) || X <- Filter].
%%%-----------------
%%% parse_attr/2,3
%%%-----------------
parse_attr(Attr, ListOfSubValues) ->
{Action, [_|_] = Name, [_|_] = Value} = split_attribute(Attr),
parse_attr(Action, {Name, Value}, ListOfSubValues).
parse_attr(approx, {Name, Value}, ListOfSubValues) ->
NewValue = do_sub(Value, ListOfSubValues),
eldap:approxMatch(Name, NewValue);
parse_attr(greater, {Name, Value}, ListOfSubValues) ->
NewValue = do_sub(Value, ListOfSubValues),
eldap:greaterOrEqual(Name, NewValue);
parse_attr(less, {Name, Value}, ListOfSubValues) ->
NewValue = do_sub(Value, ListOfSubValues),
eldap:lessOrEqual(Name, NewValue);
parse_attr(equal, {Name, Value}, ListOfSubValues) ->
{ok, RegSList} = regexp:split(remove_extra_asterisks(Value), "[*]"),
Pattern = case [do_sub(X, ListOfSubValues) || X <- RegSList] of
[Head | Tail] when Tail /= [] ->
{Head, lists:sublist(Tail, length(Tail)-1), lists:last(Tail)};
R ->
R
end,
case Pattern of
[V] ->
eldap:equalityMatch(Name, V);
{[], [], []} ->
eldap:present(Name);
{"", Any, ""} ->
eldap:substrings(Name, [{any, X} || X<-Any]);
{H, Any, ""} ->
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]);
{"", Any, T} ->
eldap:substrings(Name, [{any, X} || X<-Any]++[{final, T}]);
{H, Any, T} ->
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]++[{final, T}])
end;
parse_attr(_, _, _) ->
false.
%%%--------------------
%%% do_sub/2,3
%%%--------------------
-define(MAX_RECURSION, 100).
do_sub(S, []) ->
S;
do_sub([], _) ->
[];
do_sub(S, [{RegExp, New} | T]) ->
Result = do_sub(S, {RegExp, replace_amps(New)}, 1),
do_sub(Result, T);
do_sub(S, [{RegExp, New, Times} | T]) ->
Result = do_sub(S, {RegExp, replace_amps(New), Times}, 1),
do_sub(Result, T).
do_sub(S, {RegExp, New}, Iter) ->
case regexp:sub(S, RegExp, New) of
{ok, NewS, 0} ->
NewS;
{ok, NewS, _} when Iter =< ?MAX_RECURSION ->
do_sub(NewS, {RegExp, New}, Iter+1);
{ok, _, _} when Iter > ?MAX_RECURSION ->
throw({regexp, max_substitute_recursion});
_ ->
throw({regexp, bad_regexp})
end;
do_sub(S, {_, _, N}, _) when N<1 ->
S;
do_sub(S, {RegExp, New, Times}, Iter) ->
case regexp:sub(S, RegExp, New) of
{ok, NewS, 0} ->
NewS;
{ok, NewS, _} when Iter < Times ->
do_sub(NewS, {RegExp, New, Times}, Iter+1);
{ok, NewS, _} ->
NewS;
_ ->
throw({regexp, bad_regexp})
end.
remove_extra_asterisks(String) ->
{Res, _} = lists:foldl(
fun(X, {Acc, Last}) ->
case X of
$* when Last==$* ->
{Acc, X};
_ ->
{Acc ++ [X], X}
end
end,
{"", ""}, String),
Res.
replace_amps(String) ->
lists:foldl(
fun(X, Acc) ->
if
X == $& ->
Acc ++ "\\&";
true ->
Acc ++ [X]
end
end,
"", String).
split_attribute(String) ->
split_attribute(String, "", $0).
split_attribute([], _, _) ->
{error, "", ""};
split_attribute([H|Tail], Acc, Last) ->
case H of
$= when Last==$> ->
{greater, lists:sublist(Acc, 1, length(Acc)-1), Tail};
$= when Last==$< ->
{less, lists:sublist(Acc, 1, length(Acc)-1), Tail};
$= when Last==$~ ->
{approx, lists:sublist(Acc, 1, length(Acc)-1), Tail};
$= when Last==$: ->
{equal, lists:sublist(Acc, 1, length(Acc)-1), Tail};
$= ->
{equal, Acc, Tail};
_ ->
split_attribute(Tail, Acc++[H], H)
end.
+68 -82
View File
@@ -106,10 +106,6 @@ int ei_x_encode_string_fixed(ei_x_buff* x, const char* s)
#define XML_CDATA 2
#define XML_ERROR 3
#define PARSE_COMMAND 0
#define PARSE_FINAL_COMMAND 1
ei_x_buff event_buf;
typedef struct {
ErlDrvPort port;
@@ -121,39 +117,44 @@ void *erlXML_StartElementHandler(expat_data *d,
const XML_Char **atts)
{
int i;
ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_START);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_string_fixed(&event_buf, name);
ei_x_buff buf;
ei_x_new_with_version(&buf);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_long(&buf, XML_START);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_string_fixed(&buf, name);
for (i = 0; atts[i]; i += 2) {}
if (i > 0)
ei_x_encode_list_header(&buf, i/2);
for (i = 0; atts[i]; i += 2)
{
ei_x_encode_list_header(&event_buf, i/2);
for (i = 0; atts[i]; i += 2)
{
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_string_fixed(&event_buf, atts[i]);
ei_x_encode_string_fixed(&event_buf, atts[i+1]);
}
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_string_fixed(&buf, atts[i]);
ei_x_encode_string_fixed(&buf, atts[i+1]);
}
ei_x_encode_empty_list(&event_buf);
ei_x_encode_empty_list(&buf);
driver_output(d->port, buf.buff, buf.index);
ei_x_free(&buf);
return NULL;
}
void *erlXML_EndElementHandler(expat_data *d,
const XML_Char *name)
{
ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_END);
ei_x_encode_string_fixed(&event_buf, name);
ei_x_buff buf;
ei_x_new_with_version(&buf);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_long(&buf, XML_END);
ei_x_encode_string_fixed(&buf, name);
driver_output(d->port, buf.buff, buf.index);
ei_x_free(&buf);
return NULL;
}
@@ -161,10 +162,15 @@ void *erlXML_CharacterDataHandler(expat_data *d,
const XML_Char *s,
int len)
{
ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_CDATA);
ei_x_encode_binary(&event_buf, s, len);
ei_x_buff buf;
ei_x_new_with_version(&buf);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_long(&buf, XML_CDATA);
ei_x_encode_string_len_fixed(&buf, s, len);
driver_output(d->port, buf.buff, buf.index);
ei_x_free(&buf);
return NULL;
}
@@ -176,8 +182,6 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
d->parser = XML_ParserCreate("UTF-8");
XML_SetUserData(d->parser, d);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
XML_SetStartElementHandler(
d->parser, (XML_StartElementHandler)erlXML_StartElementHandler);
XML_SetEndElementHandler(
@@ -195,66 +199,48 @@ static void expat_erl_stop(ErlDrvData handle)
driver_free((char*)handle);
}
static int expat_erl_control(ErlDrvData drv_data,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
static void expat_erl_output(ErlDrvData handle, char *buff, int bufflen)
{
expat_data* d = (expat_data*)drv_data;
expat_data* d = (expat_data*)handle;
int res, errcode;
char *errstring;
ErlDrvBinary *b;
size_t size;
ei_x_buff buf;
switch (command)
res = XML_Parse(d->parser, buff, bufflen, 0);
if(!res)
{
case PARSE_COMMAND:
case PARSE_FINAL_COMMAND:
ei_x_new_with_version(&event_buf);
res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND);
errcode = XML_GetErrorCode(d->parser);
errstring = (char *)XML_ErrorString(errcode);
if(!res)
{
errcode = XML_GetErrorCode(d->parser);
errstring = (char *)XML_ErrorString(errcode);
ei_x_new_with_version(&buf);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_long(&buf, XML_ERROR);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_long(&buf, errcode);
ei_x_encode_string_fixed(&buf, errstring);
ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_ERROR);
ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, errcode);
ei_x_encode_string_fixed(&event_buf, errstring);
}
ei_x_encode_empty_list(&event_buf);
size = event_buf.index;
b = driver_alloc_binary(size);
memcpy(b->orig_bytes, event_buf.buff, size);
ei_x_free(&event_buf);
*rbuf = (char *)b;
return size;
default:
return 0;
driver_output(d->port, buf.buff, buf.index);
ei_x_free(&buf);
}
//driver_output(d->port, &res, 1);
}
ErlDrvEntry expat_driver_entry = {
NULL, /* F_PTR init, N/A */
expat_erl_start, /* L_PTR start, called when port is opened */
expat_erl_stop, /* F_PTR stop, called when port is closed */
NULL, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"expat_erl", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* handle */
expat_erl_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
NULL, /* F_PTR init, N/A */
expat_erl_start, /* L_PTR start, called when port is opened */
expat_erl_stop, /* F_PTR stop, called when port is closed */
expat_erl_output, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"expat_erl", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(expat_erl) /* must match name in driver_entry */
-77
View File
@@ -1,77 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : extauth.erl
%%% Author : Leif Johansson <leifj@it.su.se>
%%% Purpose : External authentication using a simple port-driver
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(extauth).
-author('leifj@it.su.se').
-export([start/2, stop/1, init/2,
check_password/3, set_password/3, is_user_exists/2]).
start(Host, ExtPrg) ->
spawn(?MODULE, init, [Host, ExtPrg]).
init(Host, ExtPrg) ->
register(gen_mod:get_module_proc(Host, eauth), self()),
process_flag(trap_exit,true),
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
loop(Port).
stop(Host) ->
gen_mod:get_module_proc(Host, eauth) ! stop.
check_password(User, Server, Password) ->
call_port(Server, ["auth", User, Server, Password]).
is_user_exists(User, Server) ->
call_port(Server, ["isuser", User, Server]).
set_password(User, Server, Password) ->
call_port(Server, ["setpass", User, Server, Password]).
call_port(Server, Msg) ->
LServer = jlib:nameprep(Server),
gen_mod:get_module_proc(LServer, eauth) ! {call, self(), Msg},
receive
{eauth,Result} ->
Result
end.
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
{Port, {data, Data}} ->
Caller ! {eauth, decode(Data)}
end,
loop(Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, Reason} ->
io:format("~p ~n", [Reason]),
exit(port_terminated)
end.
join(List, Sep) ->
lists:foldl(fun(A, "") -> A;
(A, Acc) -> Acc ++ Sep ++ A
end, "", List).
encode(L) ->
join(L,":").
decode([0,0]) ->
false;
decode([0,1]) ->
true.
+36 -128
View File
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : gen_iq_handler.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : IQ handler support
%%% Purpose :
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -10,92 +10,58 @@
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
%% API
-export([start_link/3,
add_iq_handler/6,
remove_iq_handler/3,
-export([start/0,
start_link/2,
add_iq_handler/5,
remove_iq_handler/2,
stop_iq_handler/3,
handle/7,
process_iq/6]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
handle/6,
process_iq/5,
queue_init/2]).
-include("ejabberd.hrl").
-record(state, {host,
module,
function}).
start() ->
ok.
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Module, Function) ->
gen_server:start_link(?MODULE, [Host, Module, Function], []).
add_iq_handler(Component, Host, NS, Module, Function, Type) ->
add_iq_handler(Component, NS, Module, Function, Type) ->
case Type of
no_queue ->
Component:register_iq_handler(Host, NS, Module, Function, no_queue);
Component:register_iq_handler(NS, Module, Function, no_queue);
one_queue ->
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
[Host, Module, Function]),
Component:register_iq_handler(Host, NS, Module, Function,
[Module, Function]),
Component:register_iq_handler(NS, Module, Function,
{one_queue, Pid});
{queues, N} ->
Pids =
lists:map(
fun(_) ->
{ok, Pid} = supervisor:start_child(
ejabberd_iq_sup,
[Host, Module, Function]),
Pid
end, lists:seq(1, N)),
Component:register_iq_handler(Host, NS, Module, Function,
{queues, Pids});
parallel ->
Component:register_iq_handler(Host, NS, Module, Function, parallel)
Component:register_iq_handler(NS, Module, Function, parallel)
end.
remove_iq_handler(Component, Host, NS) ->
Component:unregister_iq_handler(Host, NS).
remove_iq_handler(Component, NS) ->
Component:unregister_iq_handler(NS).
stop_iq_handler(_Module, _Function, Opts) ->
stop_iq_handler(Module, Function, Opts) ->
case Opts of
{one_queue, Pid} ->
gen_server:call(Pid, stop);
{queues, Pids} ->
lists:foreach(fun(Pid) ->
catch gen_server:call(Pid, stop)
end, Pids);
exit(Pid, kill);
_ ->
ok
end.
handle(Host, Module, Function, Opts, From, To, IQ) ->
handle(Module, Function, Opts, From, To, IQ) ->
case Opts of
no_queue ->
process_iq(Host, Module, Function, From, To, IQ);
process_iq(Module, Function, From, To, IQ);
{one_queue, Pid} ->
Pid ! {process_iq, From, To, IQ};
{queues, Pids} ->
Pid = lists:nth(erlang:phash(now(), length(Pids)), Pids),
Pid ! {process_iq, From, To, IQ};
parallel ->
spawn(?MODULE, process_iq, [Host, Module, Function, From, To, IQ]);
spawn(?MODULE, process_iq, [Module, Function, From, To, IQ]);
_ ->
todo
end.
process_iq(_Host, Module, Function, From, To, IQ) ->
process_iq(Module, Function, From, To, IQ) ->
case catch Module:Function(From, To, IQ) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
@@ -109,76 +75,18 @@ process_iq(_Host, Module, Function, From, To, IQ) ->
end
end.
%%====================================================================
%% gen_server callbacks
%%====================================================================
start_link(Module, Function) ->
{ok, proc_lib:spawn_link(?MODULE, queue_init, [Module, Function])}.
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Module, Function]) ->
{ok, #state{host = Host,
module = Module,
function = Function}}.
queue_init(Module, Function) ->
queue_loop(Module, Function).
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
Reply = ok,
{stop, normal, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({process_iq, From, To, IQ},
#state{host = Host,
module = Module,
function = Function} = State) ->
process_iq(Host, Module, Function, From, To, IQ),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
% TODO: use gen_event
queue_loop(Module, Function) ->
receive
{process_iq, From, To, IQ} ->
process_iq(Module, Function, From, To, IQ),
queue_loop(Module, Function);
_ ->
queue_loop(Module, Function)
end.

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