mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-31 15:21:38 +01:00
467 lines
13 KiB
TeX
467 lines
13 KiB
TeX
\documentclass[a4paper,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}
|
|
\scalebox{#1}{\includegraphics{#2}}
|
|
\end{latexonly}
|
|
}
|
|
|
|
%% Various
|
|
\newcommand{\ns}[1]{\texttt{#1}}
|
|
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
|
\newcommand{\Jabber}{Jabber}
|
|
\newcommand{\XMPP}{XMPP}
|
|
|
|
%% Modules
|
|
\newcommand{\module}[1]{\texttt{#1}}
|
|
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
|
\newcommand{\modannounce}{\module{mod\_announce}}
|
|
\newcommand{\modconfigure}{\module{mod\_configure}}
|
|
\newcommand{\moddisco}{\module{mod\_disco}}
|
|
\newcommand{\modecho}{\module{mod\_echo}}
|
|
\newcommand{\modirc}{\module{mod\_irc}}
|
|
\newcommand{\modlast}{\module{mod\_last}}
|
|
\newcommand{\modmuc}{\module{mod\_muc}}
|
|
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
|
|
\newcommand{\modoffline}{\module{mod\_offline}}
|
|
\newcommand{\modprivacy}{\module{mod\_privacy}}
|
|
\newcommand{\modprivate}{\module{mod\_private}}
|
|
\newcommand{\modpubsub}{\module{mod\_pubsub}}
|
|
\newcommand{\modregister}{\module{mod\_register}}
|
|
\newcommand{\modroster}{\module{mod\_roster}}
|
|
\newcommand{\modservicelog}{\module{mod\_service\_log}}
|
|
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
|
|
\newcommand{\modstats}{\module{mod\_stats}}
|
|
\newcommand{\modtime}{\module{mod\_time}}
|
|
\newcommand{\modvcard}{\module{mod\_vcard}}
|
|
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
|
|
\newcommand{\modversion}{\module{mod\_version}}
|
|
|
|
%% Title page
|
|
\include{version}
|
|
\title{Ejabberd \version\ Developers Guide}
|
|
\author{Alexey Shchepin \\
|
|
\ahrefurl{mailto:alexey@sevcom.net} \\
|
|
\ahrefurl{xmpp:aleksey@jabber.ru}}
|
|
|
|
%% Options
|
|
\newcommand{\marking}[1]{#1} % Marking disabled
|
|
\newcommand{\quoting}[2][yozhik]{} % Quotes disabled
|
|
\newcommand{\new}{\begin{latexonly}\marginpar{\textsc{new}}\end{latexonly}} % Highlight new features
|
|
\newcommand{\improved}{\begin{latexonly}\marginpar{\textsc{improved}}\end{latexonly}} % Highlight improved features
|
|
\newcommand{\moreinfo}[1]{} % Hide details
|
|
|
|
%% Footnotes
|
|
\newcommand{\txepref}[2]{\footahref{http://www.xmpp.org/extensions/xep-#1.html}{#2}}
|
|
\newcommand{\xepref}[1]{\txepref{#1}{XEP-#1}}
|
|
|
|
\begin{document}
|
|
|
|
\label{titlepage}
|
|
\begin{titlepage}
|
|
\maketitle{}
|
|
|
|
\begin{center}
|
|
{\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}
|
|
|
|
\tableofcontents{}
|
|
|
|
% Input introduction.tex
|
|
\input{introduction}
|
|
|
|
\section{How it Works}
|
|
\label{howitworks}
|
|
|
|
|
|
A \XMPP{} domain is served by one or more \ejabberd{} nodes. These nodes can
|
|
be run on different machines that are connected via a network. They all must
|
|
have the ability to connect to port 4369 of all another nodes, and must have
|
|
the same magic cookie (see Erlang/OTP documentation, in other words the file
|
|
\texttt{\~{}ejabberd/.erlang.cookie} must be the same on all nodes). This is
|
|
needed because all nodes exchange information about connected users, S2S
|
|
connections, registered services, etc\ldots
|
|
|
|
|
|
|
|
Each \ejabberd{} node have following modules:
|
|
\begin{itemize}
|
|
\item router;
|
|
\item local router.
|
|
\item session manager;
|
|
\item S2S manager;
|
|
\end{itemize}
|
|
|
|
|
|
\subsection{Router}
|
|
|
|
This module is the main router of \XMPP{} packets on each node. It routes
|
|
them based on their destinations domains. It has two tables: local and global
|
|
routes. First, domain of packet destination searched in local table, and if it
|
|
found, then the packet is routed to appropriate process. If no, then it
|
|
searches in global table, and is routed to the appropriate \ejabberd{} node or
|
|
process. If it does not exists in either tables, then it sent to the S2S
|
|
manager.
|
|
|
|
|
|
\subsection{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}
|
|
|
|
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.
|
|
|
|
|
|
\subsection{S2S Manager}
|
|
|
|
This module routes packets to other \XMPP{} servers. First, it checks if an
|
|
open S2S connection from the domain of the packet source to the domain of
|
|
packet destination already exists. If it is open on another node, then it
|
|
routes the packet to S2S manager on that node, if it is open on this node, then
|
|
it is routed to the process that serves this connection, and if a connection
|
|
does not exist, then it is opened and registered.
|
|
|
|
|
|
\section{Authentication}
|
|
|
|
\subsubsection{External}
|
|
\label{externalauth}
|
|
\ind{external authentication}
|
|
|
|
The external authentication script follows
|
|
\footahref{http://www.erlang.org/doc/tutorial/c_portdriver.html}{the erlang port driver API}.
|
|
|
|
That script is supposed to do theses actions, in an infinite loop:
|
|
\begin{itemize}
|
|
\item read from stdin: AABBBBBBBBB.....
|
|
\begin{itemize}
|
|
\item A: 2 bytes of length data (a short in network byte order)
|
|
\item B: a string of length found in A that contains operation in plain text
|
|
operation are as follows:
|
|
\begin{itemize}
|
|
\item auth:User:Server:Password (check if a username/password pair is correct)
|
|
\item isuser:User:Server (check if it's a valid user)
|
|
\item setpass:User:Server:Password (set user's password)
|
|
\item tryregister:User:Server:Password (try to register an account)
|
|
\item removeuser:User:Server (remove this account)
|
|
\item removeuser3:User:Server:Password (remove this account if the password is correct)
|
|
\end{itemize}
|
|
\end{itemize}
|
|
\item write to stdout: AABB
|
|
\begin{itemize}
|
|
\item A: the number 2 (coded as a short, which is bytes length of following result)
|
|
\item B: the result code (coded as a short), should be 1 for success/valid, or 0 for failure/invalid
|
|
\end{itemize}
|
|
\end{itemize}
|
|
|
|
Example python script
|
|
\begin{verbatim}
|
|
#!/usr/bin/python
|
|
|
|
import sys
|
|
from struct import *
|
|
|
|
def from_ejabberd():
|
|
input_length = sys.stdin.read(2)
|
|
(size,) = unpack('>h', input_length)
|
|
return sys.stdin.read(size).split(':')
|
|
|
|
def to_ejabberd(bool):
|
|
answer = 0
|
|
if bool:
|
|
answer = 1
|
|
token = pack('>hh', 2, answer)
|
|
sys.stdout.write(token)
|
|
sys.stdout.flush()
|
|
|
|
def auth(username, server, password):
|
|
return True
|
|
|
|
def isuser(username, server):
|
|
return True
|
|
|
|
def setpass(username, server, password):
|
|
return True
|
|
|
|
while True:
|
|
data = from_ejabberd()
|
|
success = False
|
|
if data[0] == "auth":
|
|
success = auth(data[1], data[2], data[3])
|
|
elif data[0] == "isuser":
|
|
success = isuser(data[1], data[2])
|
|
elif data[0] == "setpass":
|
|
success = setpass(data[1], data[2], data[3])
|
|
to_ejabberd(success)
|
|
\end{verbatim}
|
|
|
|
\section{XML Representation}
|
|
\label{xmlrepr}
|
|
|
|
Each XML stanza is represented as the following tuple:
|
|
\begin{verbatim}
|
|
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
|
Name = string()
|
|
Attrs = [Attr]
|
|
Attr = {Key, Val}
|
|
Key = string()
|
|
Val = string()
|
|
ElementOrCDATA = XMLElement | CDATA
|
|
CDATA = {xmlcdata, string()}
|
|
\end{verbatim}
|
|
E.\,g. this stanza:
|
|
\begin{verbatim}
|
|
<message to='test@conference.example.org' type='groupchat'>
|
|
<body>test</body>
|
|
</message>
|
|
\end{verbatim}
|
|
is represented as the following structure:
|
|
\begin{verbatim}
|
|
{xmlelement, "message",
|
|
[{"to", "test@conference.example.org"},
|
|
{"type", "groupchat"}],
|
|
[{xmlelement, "body",
|
|
[],
|
|
[{xmlcdata, "test"}]}]}}
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
\section{Module \texttt{xml}}
|
|
\label{xmlmod}
|
|
|
|
\begin{description}
|
|
\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()|}
|
|
\begin{verbatim}
|
|
S = string()
|
|
\end{verbatim}
|
|
Returns string which correspond to \texttt{S} with encoded XML special
|
|
characters.
|
|
|
|
\item{\verb|remove_cdata(ECList) -> EList|}
|
|
\begin{verbatim}
|
|
ECList = [ElementOrCDATA]
|
|
EList = [XMLElement]
|
|
\end{verbatim}
|
|
\texttt{EList} is a list of all non-CDATA elements of ECList.
|
|
|
|
|
|
|
|
\item{\verb|get_path_s(El, Path) -> Res|}
|
|
\begin{verbatim}
|
|
El = XMLElement
|
|
Path = [PathItem]
|
|
PathItem = PathElem | PathAttr | PathCDATA
|
|
PathElem = {elem, Name}
|
|
PathAttr = {attr, Name}
|
|
PathCDATA = cdata
|
|
Name = string()
|
|
Res = string() | XMLElement
|
|
\end{verbatim}
|
|
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
|
|
\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
|
|
returns value of this attribute, else returns empty string.
|
|
\item{\verb|cdata|} Returns CDATA of \texttt{El}.
|
|
\end{description}
|
|
|
|
\item{TODO:}
|
|
\begin{verbatim}
|
|
get_cdata/1, get_tag_cdata/1
|
|
get_attr/2, get_attr_s/2
|
|
get_tag_attr/2, get_tag_attr_s/2
|
|
get_subtag/2
|
|
\end{verbatim}
|
|
\end{description}
|
|
|
|
|
|
\section{Module \texttt{xml\_stream}}
|
|
\label{xmlstreammod}
|
|
|
|
\begin{description}
|
|
\item{\verb!parse_element(Str) -> XMLElement | {error, Err}!}
|
|
\begin{verbatim}
|
|
Str = string()
|
|
Err = term()
|
|
\end{verbatim}
|
|
Parses \texttt{Str} using XML parser, returns either parsed element or error
|
|
tuple.
|
|
\end{description}
|
|
|
|
|
|
\section{Modules}
|
|
\label{emods}
|
|
|
|
|
|
%\subsection{gen\_mod behaviour}
|
|
%\label{genmod}
|
|
|
|
%TBD
|
|
|
|
\subsection{Module gen\_iq\_handler}
|
|
\label{geniqhandl}
|
|
|
|
The module \verb|gen_iq_handler| allows to easily write handlers for IQ packets
|
|
of particular XML namespaces that addressed to server or to users bare JIDs.
|
|
|
|
In this module the following functions are defined:
|
|
\begin{description}
|
|
\item{\verb|add_iq_handler(Component, Host, NS, Module, Function, Type)|}
|
|
\begin{verbatim}
|
|
Component = Module = Function = atom()
|
|
Host = 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:
|
|
\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.
|
|
\end{description}
|
|
\item{\verb|remove_iq_handler(Component, Host, NS)|}
|
|
\begin{verbatim}
|
|
Component = atom()
|
|
Host = NS = string()
|
|
\end{verbatim}
|
|
Removes IQ handler on virtual host \verb|Host| 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)|}
|
|
\begin{verbatim}
|
|
From = To = jid()
|
|
\end{verbatim}
|
|
\end{description}
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
-module(mod_cputime).
|
|
|
|
-behaviour(gen_mod).
|
|
|
|
-export([start/2,
|
|
stop/1,
|
|
process_local_iq/3]).
|
|
|
|
-include("ejabberd.hrl").
|
|
-include("jlib.hrl").
|
|
|
|
-define(NS_CPUTIME, "ejabberd:cputime").
|
|
|
|
start(Host, Opts) ->
|
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_CPUTIME,
|
|
?MODULE, process_local_iq, IQDisc).
|
|
|
|
stop(Host) ->
|
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
|
|
|
|
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
|
case Type of
|
|
set ->
|
|
{iq, ID, error, XMLNS,
|
|
[SubEl, ?ERR_NOT_ALLOWED]};
|
|
get ->
|
|
CPUTime = element(1, erlang:statistics(runtime))/1000,
|
|
SCPUTime = lists:flatten(io_lib:format("~.3f", CPUTime)),
|
|
{iq, ID, result, XMLNS,
|
|
[{xmlelement, "query",
|
|
[{"xmlns", ?NS_CPUTIME}],
|
|
[{xmlelement, "cputime", [], [{xmlcdata, SCPUTime}]}]}]}
|
|
end.
|
|
\end{verbatim}
|
|
|
|
|
|
\subsection{Services}
|
|
\label{services}
|
|
|
|
%TBD
|
|
|
|
|
|
%TODO: use \verb|proc_lib|
|
|
\begin{verbatim}
|
|
-module(mod_echo).
|
|
|
|
-behaviour(gen_mod).
|
|
|
|
-export([start/2, init/1, stop/1]).
|
|
|
|
-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])).
|
|
|
|
init(Host) ->
|
|
ejabberd_router:register_local_route(Host),
|
|
loop(Host).
|
|
|
|
loop(Host) ->
|
|
receive
|
|
{route, From, To, Packet} ->
|
|
ejabberd_router:route(To, From, Packet),
|
|
loop(Host);
|
|
stop ->
|
|
ejabberd_router:unregister_route(Host),
|
|
ok;
|
|
_ ->
|
|
loop(Host)
|
|
end.
|
|
|
|
stop(Host) ->
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
Proc ! stop,
|
|
{wait, Proc}.
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
\end{document}
|