Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0dbedb69a6 | |||
| 5788609e5c | |||
| 5305b4a82c | |||
| d03d08539b | |||
| 76104cd117 | |||
| e211bf7131 | |||
| 32fc586c08 | |||
| 8fedc945bf | |||
| d481017746 | |||
| 19aad464da | |||
| 8df134e025 | |||
| 9091fcb1a1 | |||
| 9aa2d92d90 | |||
| 311fedaa12 | |||
| 9c36a9df78 | |||
| b47a27f3ac | |||
| 531638e946 | |||
| a724893887 | |||
| 7f4c74dec9 | |||
| 44cc99d616 | |||
| 7395cc910e | |||
| c53d764119 | |||
| 3eb0b161b2 | |||
| 65551afcba | |||
| f4376671ac | |||
| c012d7555b | |||
| 9ede414f01 | |||
| 062d2d696f | |||
| a53191fed5 | |||
| 8e27decdfd | |||
| 0dfc8ade68 | |||
| 1db65e3614 | |||
| a168340838 | |||
| 6604b9efbb | |||
| 2598375051 | |||
| 0f96414279 | |||
| edb18deb8f | |||
| 0770252e9b | |||
| b0453ea2ce | |||
| b7d7dc5201 | |||
| 5856f6d06a | |||
| a25051fead | |||
| 95138864f4 | |||
| 4045c848c4 | |||
| 3267d4c923 | |||
| 0e7d2b73be | |||
| 0474804d4b | |||
| 56034e6ed5 | |||
| df57a07dd5 | |||
| 3c12d1a960 | |||
| 87d6ad395f | |||
| 7c7e51b6e8 | |||
| 4ae1b4db03 | |||
| 72f35a6dc6 | |||
| 83cce468a5 | |||
| 6631078884 | |||
| fc0754c609 | |||
| 73a0fb418c | |||
| 04038c01f7 | |||
| 4dc23a5d81 | |||
| 36a96ae3f5 | |||
| d528457ba5 | |||
| e5a98b100d | |||
| 1cf2dfe63a | |||
| 3c47a5bb75 | |||
| e608274243 | |||
| 2110b929bc | |||
| dcf5aefea0 | |||
| 357ff6c8f7 | |||
| c04053cfae | |||
| fe10c6f6f2 | |||
| 7206bff4a4 | |||
| 6c712b6caa | |||
| faa4851dbf | |||
| 6f48f22c71 | |||
| 9a9633dbc5 | |||
| c1119b1f39 | |||
| ab196b4b02 | |||
| 730fd55ec9 | |||
| 0ae9ef2b41 | |||
| 83faff37e5 | |||
| 4ed6a3d78f | |||
| 0de7864880 | |||
| 157ff4d093 | |||
| fb6267f38e | |||
| 9a802185ad | |||
| a9f7d9481f | |||
| ea7de2097f | |||
| 48213b09ce | |||
| 22d9011c0c | |||
| 100827ec6e | |||
| a291e7d4e6 | |||
| 6cba7bf834 | |||
| e8ffbbb0b4 | |||
| 6a9c9616d0 | |||
| d0443bb714 | |||
| 5585fb1ecf | |||
| b83c07251c | |||
| b2f98099bb | |||
| 9574e71e8d | |||
| 3af507cc43 | |||
| e54e556e01 | |||
| 90225a4744 | |||
| 0d322d8dab | |||
| be6ac85f31 | |||
| b07cb1ed84 | |||
| 0c725b5fac | |||
| 3b24923458 | |||
| b04f3a4c93 | |||
| 3188941af2 | |||
| fcdb519770 | |||
| 8985a47468 | |||
| 05011e6918 | |||
| da68b9a572 | |||
| 889459b45c | |||
| 1854b5e411 | |||
| 3b267eaa61 | |||
| fba13a0b93 | |||
| 8bb994dbca | |||
| dfbef8be8f | |||
| 2af659b737 | |||
| 16532dd643 | |||
| 555c1736b7 | |||
| e66b45d886 | |||
| 0800a9733c | |||
| f5c9dfbc93 | |||
| 084d0c4efe | |||
| 61afd668c2 | |||
| de7ff536b2 | |||
| 5dbe7a3c6c | |||
| 6efce7f706 | |||
| 42c7205739 | |||
| dbcfd38c18 | |||
| 09f5565ca0 | |||
| d885435552 | |||
| eca3b46ee1 | |||
| f817762cc4 | |||
| 25beb46391 | |||
| 7ab6c4b4fd | |||
| b0b004b26e | |||
| f7d9bb7ce4 | |||
| b0b3870b2e | |||
| 7297b23508 | |||
| 6eab111d05 | |||
| fa99519f8b | |||
| 96c62acb1b | |||
| 77e57afae1 | |||
| 60fe008268 | |||
| 6987e85602 | |||
| 7492ecae52 | |||
| 4440b03b89 | |||
| 01718a4e42 | |||
| 6f99775115 | |||
| bd43385415 | |||
| 987e337c66 | |||
| dc449687a0 | |||
| 0edba763fb | |||
| 50f35f1b07 | |||
| 9c85cb5f25 | |||
| 2a15453d98 |
@@ -8,7 +8,18 @@ services:
|
||||
- riak
|
||||
|
||||
before_install:
|
||||
#
|
||||
# We need MySQL 5.6 or newer in order to get support for FULLTEXT indexes
|
||||
# with InnoDB. As soon as Travis ships that version, the following lines
|
||||
# (except for the "apt-get update" call) can go away.
|
||||
#
|
||||
# See: https://github.com/travis-ci/travis-ci/issues/1986
|
||||
#
|
||||
- sudo sed -i -e s/table_cache/table_open_cache/ -e /log_slow_queries/d /etc/mysql/my.cnf
|
||||
- sudo apt-key adv --keyserver pgp.mit.edu --recv-keys 5072E1F5
|
||||
- sudo add-apt-repository 'deb http://repo.mysql.com/apt/ubuntu/ precise mysql-5.6'
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -qq -o Dpkg::Options::=--force-confold install mysql-server-5.6
|
||||
|
||||
install:
|
||||
- sudo apt-get -qq install libexpat1-dev libyaml-dev libpam0g-dev libsqlite3-dev
|
||||
|
||||
@@ -101,10 +101,6 @@ xref: all
|
||||
translations:
|
||||
contrib/extract_translations/prepare-translation.sh -updateall
|
||||
|
||||
doc:
|
||||
echo making $$target in doc; \
|
||||
(cd doc && $(MAKE) $$target) || exit 1
|
||||
|
||||
edoc:
|
||||
$(ERL) -noinput +B -eval \
|
||||
'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.'
|
||||
@@ -207,16 +203,9 @@ install: all
|
||||
#
|
||||
# Documentation
|
||||
$(INSTALL) -d $(DOCDIR)
|
||||
[ -f doc/dev.html ] \
|
||||
&& $(INSTALL) -m 644 doc/dev.html $(DOCDIR) \
|
||||
|| echo "No doc/dev.html was built"
|
||||
[ -f doc/guide.html ] \
|
||||
&& $(INSTALL) -m 644 doc/guide.html $(DOCDIR) \
|
||||
|| echo "No doc/guide.html was built"
|
||||
[ -f doc/guide.pdf ] \
|
||||
&& $(INSTALL) -m 644 doc/guide.pdf $(DOCDIR) \
|
||||
|| echo "No doc/guide.pdf was built"
|
||||
$(INSTALL) -m 644 doc/*.png $(DOCDIR)
|
||||
|| echo "Documentation not included in sources"
|
||||
$(INSTALL) -m 644 COPYING $(DOCDIR)
|
||||
|
||||
uninstall: uninstall-binary
|
||||
@@ -326,6 +315,6 @@ test:
|
||||
quicktest:
|
||||
$(REBAR) skip_deps=true ct suites=elixir
|
||||
|
||||
.PHONY: src doc edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
||||
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
||||
install uninstall uninstall-binary uninstall-all translations deps test spec \
|
||||
quicktest erlang_plt deps_plt ejabberd_plt
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
|
||||
include Makefile.inc
|
||||
|
||||
ALL : build
|
||||
|
||||
REL=..\release
|
||||
EREL=$(REL)\ejabberd-$(EJABBERD_VERSION)
|
||||
EBIN_DIR=$(EREL)\ebin
|
||||
SRC_DIR=$(EREL)\src
|
||||
PRIV_DIR=$(EREL)\priv
|
||||
SO_DIR=$(EREL)
|
||||
MSGS_DIR=$(EREL)\msgs
|
||||
WIN32_DIR=$(EREL)\win32
|
||||
DOC_DIR=$(EREL)\doc
|
||||
|
||||
NSIS_SCRIPT=win32\ejabberd.nsi
|
||||
NSIS_HEADER=win32\ejabberd.nsh
|
||||
|
||||
installer : $(NSIS_SCRIPT) $(NSIS_HEADER)
|
||||
makensis $(NSIS_SCRIPT)
|
||||
|
||||
$(NSIS_HEADER) : Makefile.inc
|
||||
echo !define OUTFILEDIR "..\$(REL)" >$(NSIS_HEADER)
|
||||
echo !define TESTDIR "..\$(REL)\ejabberd-$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
echo !define VERSION "$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
|
||||
release_clean :
|
||||
if exist $(REL) rd /s /q $(REL)
|
||||
|
||||
|
||||
release : build release_clean
|
||||
mkdir $(REL)
|
||||
mkdir $(EREL)
|
||||
mkdir $(EBIN_DIR)
|
||||
copy *.beam $(EBIN_DIR)
|
||||
@erase $(EBIN_DIR)\configure.beam
|
||||
copy *.app $(EBIN_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 $(SRC_DIR)
|
||||
copy *.app $(SRC_DIR)
|
||||
copy *.erl $(SRC_DIR)
|
||||
copy *.hrl $(SRC_DIR)
|
||||
copy *.c $(SRC_DIR)
|
||||
mkdir $(SRC_DIR)\eldap
|
||||
copy eldap\eldap.* $(SRC_DIR)\eldap
|
||||
copy eldap\ELDAPv3.asn $(SRC_DIR)\eldap
|
||||
mkdir $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.erl $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.c $(SRC_DIR)\mod_irc
|
||||
mkdir $(SRC_DIR)\mod_muc
|
||||
copy mod_muc\*.erl $(SRC_DIR)\mod_muc
|
||||
mkdir $(SRC_DIR)\mod_pubsub
|
||||
copy mod_pubsub\*.erl $(SRC_DIR)\mod_pubsub
|
||||
mkdir $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.erl $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.hrl $(SRC_DIR)\mod_proxy65
|
||||
mkdir $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.erl $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.c $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.tcl $(SRC_DIR)\stringprep
|
||||
mkdir $(SRC_DIR)\stun
|
||||
copy stun\*.erl $(SRC_DIR)\stun
|
||||
copy stun\*.hrl $(SRC_DIR)\stun
|
||||
mkdir $(SRC_DIR)\tls
|
||||
copy tls\*.erl $(SRC_DIR)\tls
|
||||
copy tls\*.c $(SRC_DIR)\tls
|
||||
mkdir $(SRC_DIR)\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 $(DOC_DIR)
|
||||
copy ..\doc\*.txt $(DOC_DIR)
|
||||
copy ..\doc\*.html $(DOC_DIR)
|
||||
copy ..\doc\*.png $(DOC_DIR)
|
||||
|
||||
SOURCE = expat_erl.c
|
||||
OBJECT = expat_erl.o
|
||||
DLL = expat_erl.dll
|
||||
|
||||
build : $(DLL) compile-beam all-recursive
|
||||
|
||||
all-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stun
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\ejabberd_zlib
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\odbc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..
|
||||
|
||||
compile-beam : XmppAddr.hrl
|
||||
erl -s make all report -noinput -s erlang halt
|
||||
|
||||
XmppAddr.hrl : XmppAddr.asn1
|
||||
erlc -bber_bin +der +compact_bit_string +optimize +noobj XmppAddr.asn1
|
||||
|
||||
CLEAN : clean-recursive clean-local
|
||||
|
||||
clean-local :
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(DLL)
|
||||
-@erase expat_erl.exp
|
||||
-@erase expat_erl.lib
|
||||
-@erase *.beam
|
||||
-@erase XmppAddr.asn1db
|
||||
-@erase XmppAddr.erl
|
||||
-@erase XmppAddr.hrl
|
||||
|
||||
clean-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stun
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\ejabberd_zlib
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\odbc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..
|
||||
|
||||
distclean : release_clean clean
|
||||
-@erase $(NSIS_HEADER)
|
||||
-@erase Makefile.inc
|
||||
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT $(EXPAT_FLAG) -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(EXPAT_DIR)\source\lib"
|
||||
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(EXPAT_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$@ $<
|
||||
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$@ $<
|
||||
@@ -152,6 +152,18 @@ Operation Guide available online and in the `doc` directory of the source
|
||||
tarball.
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
In order to assist in the development of ejabberd, and particularly the
|
||||
execution of the test suite, a Vagrant environment is available at
|
||||
https://github.com/processone/ejabberd-vagrant-dev.
|
||||
|
||||
To start ejabberd in development mode from the repository directory, you can
|
||||
type a command like:
|
||||
|
||||
EJABBERD_CONFIG_PATH=ejabberd.yml erl -pa ebin -pa deps/*/ebin -pa test -s ejabberd
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0`
|
||||
REQUIRE_ERLANG_MIN="5.9.1 (Erlang/OTP R15B01)"
|
||||
REQUIRE_ERLANG_MAX="9.0.0 (No Max)"
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_INSTALL
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# $Id$
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
CONTRIBUTED_MODULES = ""
|
||||
#ifeq ($(shell ls mod_http_bind.tex),mod_http_bind.tex)
|
||||
# CONTRIBUTED_MODULES += "\\n\\setboolean{modhttpbind}{true}"
|
||||
#endif
|
||||
|
||||
|
||||
all: release pdf html
|
||||
|
||||
release:
|
||||
@printf '%s\n' "Notes for the releaser:"
|
||||
@printf '%s\n' "* Do not forget to add a link to the release notes in guide.tex"
|
||||
@printf '%s\n' "* Do not forget to update the version number in ebin/ejabberd.app!"
|
||||
@printf '%s\n' "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
|
||||
@printf '%s\n' "Press any key to continue"
|
||||
##@read foo
|
||||
@printf '%s\n' "% ejabberd version (automatically generated)." > version.tex
|
||||
@printf '%s\n' "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../ebin/ejabberd.app`"}" >> version.tex
|
||||
@printf '%s' "% Contributed modules (automatically generated)." > contributed_modules.tex
|
||||
@printf '%b\n' "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
|
||||
|
||||
html: guide.html dev.html features.html
|
||||
|
||||
pdf: guide.pdf features.pdf
|
||||
|
||||
clean:
|
||||
rm -f *.aux
|
||||
rm -f *.haux
|
||||
rm -f *.htoc
|
||||
rm -f *.idx
|
||||
rm -f *.ilg
|
||||
rm -f *.ind
|
||||
rm -f *.log
|
||||
rm -f *.out
|
||||
rm -f *.pdf
|
||||
rm -f *.toc
|
||||
[ ! -f contributed_modules.tex ] || rm contributed_modules.tex
|
||||
|
||||
distclean: clean
|
||||
rm -f *.html
|
||||
|
||||
guide.html: guide.tex
|
||||
hevea -fix -pedantic guide.tex
|
||||
|
||||
dev.html: dev.tex
|
||||
hevea -fix -pedantic dev.tex
|
||||
|
||||
features.html: features.tex
|
||||
hevea -fix -pedantic features.tex
|
||||
|
||||
guide.pdf: guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
makeindex guide.idx
|
||||
pdflatex guide.tex
|
||||
|
||||
features.pdf: features.tex
|
||||
pdflatex features.tex
|
||||
@@ -1,18 +0,0 @@
|
||||
APPNAME = ejabberd
|
||||
VSN = $(shell sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../../src/ejabberd.app)
|
||||
|
||||
DOCDIR=.
|
||||
SRCDIR=../../src
|
||||
|
||||
.PHONY = all
|
||||
|
||||
all: docs
|
||||
|
||||
clean:
|
||||
rm -f *.html
|
||||
rm edoc-info
|
||||
rm erlang.png
|
||||
|
||||
docs:
|
||||
erl -noshell -run edoc_run application \
|
||||
"'$(APPNAME)'" '"$(SRCDIR)"' '[{dir,"$(DOCDIR)"},{packages, false},{todo,true},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"$(DOCDIR)/overview.edoc"}]' -s init stop
|
||||
@@ -1,10 +0,0 @@
|
||||
@author Mickael Remond <mickael.remond@process-one.net>
|
||||
[http://www.process-one.net/]
|
||||
@copyright 2013 ProcessOne
|
||||
@version {@vsn}, {@date} {@time}
|
||||
@title ejabberd Development API Documentation
|
||||
|
||||
@doc
|
||||
== Introduction ==
|
||||
|
||||
TODO: Insert content from Jerome documentation.
|
||||
@@ -1,92 +0,0 @@
|
||||
html, body {
|
||||
font-family: Verdana, sans-serif;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #4a5389;
|
||||
border-bottom: solid 1px #000;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
text-align: right;
|
||||
color: #4a5389;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
color: #900;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a[href] {
|
||||
color: #4a5389;
|
||||
}
|
||||
|
||||
a[href]:hover {
|
||||
background-color: #ecefff;
|
||||
}
|
||||
|
||||
p, li, dd {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-style: italic;
|
||||
color: #009;
|
||||
text-align: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
table[border="1"] {
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
table[border="1"] td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
color: #461b7e;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin:1ex 2ex;
|
||||
border:1px dashed lightgrey;
|
||||
background-color:#f9f9f9;
|
||||
padding:0.5ex;
|
||||
}
|
||||
|
||||
pre em {
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt {
|
||||
margin:0ex 2ex;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin:0ex 0ex 1ex 4ex;
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
<meta name="generator" content="hevea 2.09">
|
||||
<style type="text/css">
|
||||
.li-itemize{margin:1ex 0ex;}
|
||||
.li-enumerate{margin:1ex 0ex;}
|
||||
.dd-description{margin:0ex 0ex 1ex 4ex;}
|
||||
.dt-description{margin:0ex;}
|
||||
.toc{list-style:none;}
|
||||
.footnotetext{margin:0ex; padding:0ex;}
|
||||
div.footnotetext P{margin:0px; text-indent:1em;}
|
||||
.thefootnotes{text-align:left;margin:0ex;}
|
||||
.dt-thefootnotes{margin:0em;}
|
||||
.dd-thefootnotes{margin:0em 0em 0em 2em;}
|
||||
.footnoterule{margin:1em auto 1em 0px;width:50%;}
|
||||
.caption{padding-left:2ex; padding-right:2ex; margin-left:auto; margin-right:auto}
|
||||
.title{margin:2ex auto;text-align:center}
|
||||
.titlemain{margin:1ex 2ex 2ex 1ex;}
|
||||
.titlerest{margin:0ex 2ex;}
|
||||
.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;margin-bottom:2px;margin-top:2px}
|
||||
td table{margin:auto;}
|
||||
table{border-collapse:collapse;}
|
||||
td{padding:0;}
|
||||
.cellpadding0 tr td{padding:0;}
|
||||
.cellpadding1 tr td{padding:1px;}
|
||||
pre{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
blockquote{margin-left:4ex;margin-right:4ex;text-align:left;}
|
||||
td p{margin:0px;}
|
||||
.boxed{border:1px solid black}
|
||||
.textboxed{border:1px solid black}
|
||||
.vbar{border:none;width:2px;background-color:black;}
|
||||
.hbar{border:none;height:2px;width:100%;background-color:black;}
|
||||
.hfill{border:none;height:1px;width:200%;background-color:black;}
|
||||
.vdisplay{border-collapse:separate;border-spacing:2px;width:auto; empty-cells:show; border:2px solid red;}
|
||||
.vdcell{white-space:nowrap;padding:0px; border:2px solid green;}
|
||||
.display{border-collapse:separate;border-spacing:2px;width:auto; border:none;}
|
||||
.dcell{white-space:nowrap;padding:0px; border:none;}
|
||||
.dcenter{margin:0ex auto;}
|
||||
.vdcenter{border:solid #FF8000 2px; margin:0ex auto;}
|
||||
.minipage{text-align:left; margin-left:0em; margin-right:auto;}
|
||||
.marginpar{border:solid thin black; width:20%; text-align:left;}
|
||||
.marginparleft{float:left; margin-left:0ex; margin-right:1ex;}
|
||||
.marginparright{float:right; margin-left:1ex; margin-right:0ex;}
|
||||
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
|
||||
.part{margin:2ex auto;text-align:center}
|
||||
</style>
|
||||
<title>Ejabberd community 14.05-120-gedfb5fc Developers Guide
|
||||
</title>
|
||||
</head>
|
||||
<body >
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic dev.tex -->
|
||||
<!--CUT STYLE article--><!--CUT DEF section 1 --><p><a id="titlepage"></a>
|
||||
|
||||
</p><table class="title"><tr><td style="padding:1ex"><h1 class="titlemain">Ejabberd community 14.05-120-gedfb5fc Developers Guide</h1><h3 class="titlerest">Alexey Shchepin <br>
|
||||
<a href="mailto:alexey@sevcom.net"><span style="font-family:monospace">mailto:alexey@sevcom.net</span></a> <br>
|
||||
<a href="xmpp:aleksey@jabber.ru"><span style="font-family:monospace">xmpp:aleksey@jabber.ru</span></a></h3></td></tr>
|
||||
</table><div class="center">
|
||||
|
||||
<img src="logo.png" alt="logo.png">
|
||||
|
||||
|
||||
</div><blockquote class="quotation"><span style="font-style:italic">I can thoroughly recommend ejabberd for ease of setup –
|
||||
Kevin Smith, Current maintainer of the Psi project</span></blockquote><!--TOC section id="intro" Contents-->
|
||||
<h2 id="intro" class="section">Contents</h2><!--SEC END --><ul class="toc"><li class="li-toc">
|
||||
<a href="#sec2">1  Key Features</a>
|
||||
</li><li class="li-toc"><a href="#sec3">2  Additional Features</a>
|
||||
</li><li class="li-toc"><a href="#sec4">3  How it Works</a>
|
||||
<ul class="toc"><li class="li-toc">
|
||||
<a href="#sec5">3.1  Router</a>
|
||||
</li><li class="li-toc"><a href="#sec6">3.2  Local Router</a>
|
||||
</li><li class="li-toc"><a href="#sec7">3.3  Session Manager</a>
|
||||
</li><li class="li-toc"><a href="#sec8">3.4  S2S Manager</a>
|
||||
</li></ul>
|
||||
</li><li class="li-toc"><a href="#sec9">4  Authentication</a>
|
||||
<ul class="toc">
|
||||
<ul class="toc"><li class="li-toc">
|
||||
<a href="#sec10">4.0.1  External</a>
|
||||
</li></ul>
|
||||
</ul>
|
||||
</li><li class="li-toc"><a href="#sec11">5  XML Representation</a>
|
||||
</li><li class="li-toc"><a href="#sec12">6  Module <span style="font-family:monospace">xml</span></a>
|
||||
</li><li class="li-toc"><a href="#sec13">7  Module <span style="font-family:monospace">xml_stream</span></a>
|
||||
</li><li class="li-toc"><a href="#sec14">8  Modules</a>
|
||||
<ul class="toc"><li class="li-toc">
|
||||
<a href="#sec15">8.1  Module gen_iq_handler</a>
|
||||
</li><li class="li-toc"><a href="#sec16">8.2  Services</a>
|
||||
</li></ul>
|
||||
</li></ul><p>Introduction
|
||||
</p><p><span style="font-family:monospace">ejabberd</span> is a free and open source instant messaging server written in <a href="http://www.erlang.org/">Erlang/OTP</a>.</p><p><span style="font-family:monospace">ejabberd</span> is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication.</p><p><span style="font-family:monospace">ejabberd</span> is designed to be a rock-solid and feature rich XMPP server.</p><p><span style="font-family:monospace">ejabberd</span> is suitable for small deployments, whether they need to be scalable or not, as well as extremely big deployments.</p>
|
||||
<!--TOC section id="sec2" Key Features-->
|
||||
<h2 id="sec2" class="section">1  Key Features</h2><!--SEC END --><p>
|
||||
<a id="keyfeatures"></a>
|
||||
</p><p><span style="font-family:monospace">ejabberd</span> is:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
Cross-platform: <span style="font-family:monospace">ejabberd</span> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.</li><li class="li-itemize">Distributed: You can run <span style="font-family:monospace">ejabberd</span> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.</li><li class="li-itemize">Fault-tolerant: You can deploy an <span style="font-family:monospace">ejabberd</span> 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’.</li><li class="li-itemize">Administrator Friendly: <span style="font-family:monospace">ejabberd</span> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Comprehensive documentation.
|
||||
</li><li class="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows. </li><li class="li-itemize">Web Administration.
|
||||
</li><li class="li-itemize">Shared Roster Groups.
|
||||
</li><li class="li-itemize">Command line administration tool. </li><li class="li-itemize">Can integrate with existing authentication mechanisms.
|
||||
</li><li class="li-itemize">Capability to send announce messages.
|
||||
</li></ul></li><li class="li-itemize">Internationalized: <span style="font-family:monospace">ejabberd</span> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Translated to 25 languages. </li><li class="li-itemize">Support for <a href="http://www.ietf.org/rfc/rfc3490.txt">IDNA</a>.
|
||||
</li></ul></li><li class="li-itemize">Open Standards: <span style="font-family:monospace">ejabberd</span> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Fully XMPP compliant.
|
||||
</li><li class="li-itemize">XML-based protocol.
|
||||
</li><li class="li-itemize"><a href="http://www.ejabberd.im/protocols">Many protocols supported</a>.
|
||||
</li></ul></li></ul>
|
||||
<!--TOC section id="sec3" Additional Features-->
|
||||
<h2 id="sec3" class="section">2  Additional Features</h2><!--SEC END --><p>
|
||||
<a id="addfeatures"></a>
|
||||
</p><p>Moreover, <span style="font-family:monospace">ejabberd</span> comes with a wide range of other state-of-the-art features:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
Modular
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Load only the modules you want.
|
||||
</li><li class="li-itemize">Extend <span style="font-family:monospace">ejabberd</span> with your own custom modules.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Security
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
SASL and STARTTLS for c2s and s2s connections.
|
||||
</li><li class="li-itemize">STARTTLS and Dialback s2s connections.
|
||||
</li><li class="li-itemize">Web Admin accessible via HTTPS secure access.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Databases
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Internal database for fast deployment (Mnesia).
|
||||
</li><li class="li-itemize">Native MySQL support.
|
||||
</li><li class="li-itemize">Native PostgreSQL support.
|
||||
</li><li class="li-itemize">ODBC data storage support.
|
||||
</li><li class="li-itemize">Microsoft SQL Server support. </li><li class="li-itemize">Riak NoSQL database support.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Authentication
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Internal Authentication.
|
||||
</li><li class="li-itemize">PAM, LDAP, ODBC and Riak. </li><li class="li-itemize">External Authentication script.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Others
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Support for virtual hosting.
|
||||
</li><li class="li-itemize">Compressing XML streams with Stream Compression (<a href="http://www.xmpp.org/extensions/xep-0138.html">XEP-0138</a>).
|
||||
</li><li class="li-itemize">Statistics via Statistics Gathering (<a href="http://www.xmpp.org/extensions/xep-0039.html">XEP-0039</a>).
|
||||
</li><li class="li-itemize">IPv6 support both for c2s and s2s connections.
|
||||
</li><li class="li-itemize"><a href="http://www.xmpp.org/extensions/xep-0045.html">Multi-User Chat</a> module with support for clustering and HTML logging. </li><li class="li-itemize">Users Directory based on users vCards.
|
||||
</li><li class="li-itemize"><a href="http://www.xmpp.org/extensions/xep-0060.html">Publish-Subscribe</a> component with support for <a href="http://www.xmpp.org/extensions/xep-0163.html">Personal Eventing via Pubsub</a>.
|
||||
</li><li class="li-itemize">Support for web clients: <a href="http://www.xmpp.org/extensions/xep-0025.html">HTTP Polling</a> and <a href="http://www.xmpp.org/extensions/xep-0206.html">HTTP Binding (BOSH)</a> services.
|
||||
</li><li class="li-itemize">IRC transport.
|
||||
</li><li class="li-itemize">SIP support.
|
||||
</li><li class="li-itemize">Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
|
||||
</li></ul>
|
||||
</li></ul>
|
||||
<!--TOC section id="sec4" How it Works-->
|
||||
<h2 id="sec4" class="section">3  How it Works</h2><!--SEC END --><p>
|
||||
<a id="howitworks"></a></p><p>A XMPP domain is served by one or more <span style="font-family:monospace">ejabberd</span> 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
|
||||
<span style="font-family:monospace">~ejabberd/.erlang.cookie</span> must be the same on all nodes). This is
|
||||
needed because all nodes exchange information about connected users, S2S
|
||||
connections, registered services, etc…</p><p>Each <span style="font-family:monospace">ejabberd</span> node have following modules:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
router;
|
||||
</li><li class="li-itemize">local router.
|
||||
</li><li class="li-itemize">session manager;
|
||||
</li><li class="li-itemize">S2S manager;
|
||||
</li></ul>
|
||||
<!--TOC subsection id="sec5" Router-->
|
||||
<h3 id="sec5" class="subsection">3.1  Router</h3><!--SEC END --><p>This module is the main router of XMPP packets on each node. It routes
|
||||
them based on their destinations domains. It has two tables: local and global
|
||||
routes. First, domain of packet destination searched in local table, and if it
|
||||
found, then the packet is routed to appropriate process. If no, then it
|
||||
searches in global table, and is routed to the appropriate <span style="font-family:monospace">ejabberd</span> node or
|
||||
process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.</p>
|
||||
<!--TOC subsection id="sec6" Local Router-->
|
||||
<h3 id="sec6" class="subsection">3.2  Local Router</h3><!--SEC END --><p>This module routes packets which have a destination domain equal to this server
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it’s content.</p>
|
||||
<!--TOC subsection id="sec7" Session Manager-->
|
||||
<h3 id="sec7" class="subsection">3.3  Session Manager</h3><!--SEC END --><p>This module routes packets to local users. It searches for what user resource
|
||||
packet must be sent via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.</p>
|
||||
<!--TOC subsection id="sec8" S2S Manager-->
|
||||
<h3 id="sec8" class="subsection">3.4  S2S Manager</h3><!--SEC END --><p>This module routes packets to other XMPP servers. First, it checks if an
|
||||
open S2S connection from the domain of the packet source to the domain of
|
||||
packet destination already exists. If it is open on another node, then it
|
||||
routes the packet to S2S manager on that node, if it is open on this node, then
|
||||
it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.</p>
|
||||
<!--TOC section id="sec9" Authentication-->
|
||||
<h2 id="sec9" class="section">4  Authentication</h2><!--SEC END -->
|
||||
<!--TOC subsubsection id="sec10" External-->
|
||||
<h4 id="sec10" class="subsubsection">4.0.1  External</h4><!--SEC END --><p>
|
||||
<a id="externalauth"></a>
|
||||
</p><p>The external authentication script follows
|
||||
<a href="http://www.erlang.org/doc/tutorial/c_portdriver.html">the erlang port driver API</a>.</p><p>That script is supposed to do theses actions, in an infinite loop:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
read from stdin: AABBBBBBBBB.....
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
A: 2 bytes of length data (a short in network byte order)
|
||||
</li><li class="li-itemize">B: a string of length found in A that contains operation in plain text
|
||||
operation are as follows:
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
auth:User:Server:Password (check if a username/password pair is correct)
|
||||
</li><li class="li-itemize">isuser:User:Server (check if it’s a valid user)
|
||||
</li><li class="li-itemize">setpass:User:Server:Password (set user’s password)
|
||||
</li><li class="li-itemize">tryregister:User:Server:Password (try to register an account)
|
||||
</li><li class="li-itemize">removeuser:User:Server (remove this account)
|
||||
</li><li class="li-itemize">removeuser3:User:Server:Password (remove this account if the password is correct)
|
||||
</li></ul>
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">write to stdout: AABB
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
A: the number 2 (coded as a short, which is bytes length of following result)
|
||||
</li><li class="li-itemize">B: the result code (coded as a short), should be 1 for success/valid, or 0 for failure/invalid
|
||||
</li></ul>
|
||||
</li></ul><p>Example python script
|
||||
</p><pre class="verbatim">#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
from struct import *
|
||||
|
||||
def from_ejabberd():
|
||||
input_length = sys.stdin.read(2)
|
||||
(size,) = unpack('>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)
|
||||
</pre>
|
||||
<!--TOC section id="sec11" XML Representation-->
|
||||
<h2 id="sec11" class="section">5  XML Representation</h2><!--SEC END --><p>
|
||||
<a id="xmlrepr"></a></p><p>Each XML stanza is represented as the following tuple:
|
||||
</p><pre class="verbatim">XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
Name = string()
|
||||
Attrs = [Attr]
|
||||
Attr = {Key, Val}
|
||||
Key = string()
|
||||
Val = string()
|
||||
ElementOrCDATA = XMLElement | CDATA
|
||||
CDATA = {xmlcdata, string()}
|
||||
</pre><p>E. g. this stanza:
|
||||
</p><pre class="verbatim"><message to='test@conference.example.org' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
</pre><p>is represented as the following structure:
|
||||
</p><pre class="verbatim">{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
[{xmlcdata, "test"}]}]}}
|
||||
</pre>
|
||||
<!--TOC section id="sec12" Module <span style="font-family:monospace">xml</span>-->
|
||||
<h2 id="sec12" class="section">6  Module <span style="font-family:monospace">xml</span></h2><!--SEC END --><p>
|
||||
<a id="xmlmod"></a></p><dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>element_to_string(El) -> string()</code>
|
||||
<pre class="verbatim">El = XMLElement
|
||||
</pre>Returns string representation of XML stanza <span style="font-family:monospace">El</span>.</dd><dt class="dt-description"></dt><dd class="dd-description"><code>crypt(S) -> string()</code>
|
||||
<pre class="verbatim">S = string()
|
||||
</pre>Returns string which correspond to <span style="font-family:monospace">S</span> with encoded XML special
|
||||
characters.</dd><dt class="dt-description"></dt><dd class="dd-description"><code>remove_cdata(ECList) -> EList</code>
|
||||
<pre class="verbatim">ECList = [ElementOrCDATA]
|
||||
EList = [XMLElement]
|
||||
</pre><span style="font-family:monospace">EList</span> is a list of all non-CDATA elements of ECList.</dd><dt class="dt-description"></dt><dd class="dd-description"><code>get_path_s(El, Path) -> Res</code>
|
||||
<pre class="verbatim">El = XMLElement
|
||||
Path = [PathItem]
|
||||
PathItem = PathElem | PathAttr | PathCDATA
|
||||
PathElem = {elem, Name}
|
||||
PathAttr = {attr, Name}
|
||||
PathCDATA = cdata
|
||||
Name = string()
|
||||
Res = string() | XMLElement
|
||||
</pre>If <span style="font-family:monospace">Path</span> is empty, then returns <span style="font-family:monospace">El</span>. Else sequentially
|
||||
consider elements of <span style="font-family:monospace">Path</span>. Each element is one of:
|
||||
<dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>{elem, Name}</code> <span style="font-family:monospace">Name</span> is name of subelement of
|
||||
<span style="font-family:monospace">El</span>, if such element exists, then this element considered in
|
||||
following steps, else returns empty string.
|
||||
</dd><dt class="dt-description"></dt><dd class="dd-description"><code>{attr, Name}</code> If <span style="font-family:monospace">El</span> have attribute <span style="font-family:monospace">Name</span>, then
|
||||
returns value of this attribute, else returns empty string.
|
||||
</dd><dt class="dt-description"></dt><dd class="dd-description"><code>cdata</code> Returns CDATA of <span style="font-family:monospace">El</span>.
|
||||
</dd></dl></dd><dt class="dt-description"></dt><dd class="dd-description">TODO:
|
||||
<pre class="verbatim"> get_cdata/1, get_tag_cdata/1
|
||||
get_attr/2, get_attr_s/2
|
||||
get_tag_attr/2, get_tag_attr_s/2
|
||||
get_subtag/2
|
||||
</pre></dd></dl>
|
||||
<!--TOC section id="sec13" Module <span style="font-family:monospace">xml_stream</span>-->
|
||||
<h2 id="sec13" class="section">7  Module <span style="font-family:monospace">xml_stream</span></h2><!--SEC END --><p>
|
||||
<a id="xmlstreammod"></a></p><dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>parse_element(Str) -> XMLElement | {error, Err}</code>
|
||||
<pre class="verbatim">Str = string()
|
||||
Err = term()
|
||||
</pre>Parses <span style="font-family:monospace">Str</span> using XML parser, returns either parsed element or error
|
||||
tuple.
|
||||
</dd></dl>
|
||||
<!--TOC section id="sec14" Modules-->
|
||||
<h2 id="sec14" class="section">8  Modules</h2><!--SEC END --><p>
|
||||
<a id="emods"></a></p>
|
||||
<!--TOC subsection id="sec15" Module gen_iq_handler-->
|
||||
<h3 id="sec15" class="subsection">8.1  Module gen_iq_handler</h3><!--SEC END --><p>
|
||||
<a id="geniqhandl"></a></p><p>The module <code>gen_iq_handler</code> allows to easily write handlers for IQ packets
|
||||
of particular XML namespaces that addressed to server or to users bare JIDs.</p><p>In this module the following functions are defined:
|
||||
</p><dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>add_iq_handler(Component, Host, NS, Module, Function, Type)</code>
|
||||
<pre class="verbatim">Component = Module = Function = atom()
|
||||
Host = NS = string()
|
||||
Type = no_queue | one_queue | parallel
|
||||
</pre>Registers function <code>Module:Function</code> as handler for IQ packets on
|
||||
virtual host <code>Host</code> that contain child of namespace <code>NS</code> in
|
||||
<code>Component</code>. Queueing discipline is <code>Type</code>. There are at least
|
||||
two components defined:
|
||||
<dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>ejabberd_local</code> Handles packets that addressed to server JID;
|
||||
</dd><dt class="dt-description"></dt><dd class="dd-description"><code>ejabberd_sm</code> Handles packets that addressed to users bare JIDs.
|
||||
</dd></dl>
|
||||
</dd><dt class="dt-description"></dt><dd class="dd-description"><code>remove_iq_handler(Component, Host, NS)</code>
|
||||
<pre class="verbatim">Component = atom()
|
||||
Host = NS = string()
|
||||
</pre>Removes IQ handler on virtual host <code>Host</code> for namespace <code>NS</code> from
|
||||
<code>Component</code>.
|
||||
</dd></dl><p>Handler function must have the following type:
|
||||
</p><dl class="description"><dt class="dt-description">
|
||||
</dt><dd class="dd-description"><code>Module:Function(From, To, IQ)</code>
|
||||
<pre class="verbatim">From = To = jid()
|
||||
</pre></dd></dl><pre class="verbatim">-module(mod_cputime).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
process_local_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(NS_CPUTIME, "ejabberd:cputime").
|
||||
|
||||
start(Host, Opts) ->
|
||||
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.
|
||||
</pre>
|
||||
<!--TOC subsection id="sec16" Services-->
|
||||
<h3 id="sec16" class="subsection">8.2  Services</h3><!--SEC END --><p>
|
||||
<a id="services"></a></p><pre class="verbatim">-module(mod_echo).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, init/1, stop/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
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}.
|
||||
</pre><!--CUT END -->
|
||||
<!--HTMLFOOT-->
|
||||
<!--ENDHTML-->
|
||||
<!--FOOTER-->
|
||||
<hr style="height:2"><blockquote class="quote"><em>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
|
||||
</em><a href="http://hevea.inria.fr/index.html"><em>H</em><em><span style="font-size:small"><sup>E</sup></span></em><em>V</em><em><span style="font-size:small"><sup>E</sup></span></em><em>A</em></a><em>.</em></blockquote></body>
|
||||
</html>
|
||||
@@ -1,470 +0,0 @@
|
||||
\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{\modlastodbc}{\module{mod\_last\_odbc}}
|
||||
\newcommand{\modmuc}{\module{mod\_muc}}
|
||||
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
|
||||
\newcommand{\modprivacy}{\module{mod\_privacy}}
|
||||
\newcommand{\modprivate}{\module{mod\_private}}
|
||||
\newcommand{\modpubsub}{\module{mod\_pubsub}}
|
||||
\newcommand{\modregister}{\module{mod\_register}}
|
||||
\newcommand{\modroster}{\module{mod\_roster}}
|
||||
\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}}
|
||||
\newcommand{\modservicelog}{\module{mod\_service\_log}}
|
||||
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
|
||||
\newcommand{\modstats}{\module{mod\_stats}}
|
||||
\newcommand{\modtime}{\module{mod\_time}}
|
||||
\newcommand{\modvcard}{\module{mod\_vcard}}
|
||||
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
|
||||
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
|
||||
\newcommand{\modversion}{\module{mod\_version}}
|
||||
|
||||
%% Title page
|
||||
\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 sent via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.
|
||||
|
||||
|
||||
\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}
|
||||
|
Before Width: | Height: | Size: 9.8 KiB |
@@ -1,143 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
<meta name="generator" content="hevea 2.09">
|
||||
<style type="text/css">
|
||||
.li-itemize{margin:1ex 0ex;}
|
||||
.li-enumerate{margin:1ex 0ex;}
|
||||
.dd-description{margin:0ex 0ex 1ex 4ex;}
|
||||
.dt-description{margin:0ex;}
|
||||
.toc{list-style:none;}
|
||||
.footnotetext{margin:0ex; padding:0ex;}
|
||||
div.footnotetext P{margin:0px; text-indent:1em;}
|
||||
.thefootnotes{text-align:left;margin:0ex;}
|
||||
.dt-thefootnotes{margin:0em;}
|
||||
.dd-thefootnotes{margin:0em 0em 0em 2em;}
|
||||
.footnoterule{margin:1em auto 1em 0px;width:50%;}
|
||||
.caption{padding-left:2ex; padding-right:2ex; margin-left:auto; margin-right:auto}
|
||||
.title{margin:2ex auto;text-align:center}
|
||||
.titlemain{margin:1ex 2ex 2ex 1ex;}
|
||||
.titlerest{margin:0ex 2ex;}
|
||||
.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;margin-bottom:2px;margin-top:2px}
|
||||
td table{margin:auto;}
|
||||
table{border-collapse:collapse;}
|
||||
td{padding:0;}
|
||||
.cellpadding0 tr td{padding:0;}
|
||||
.cellpadding1 tr td{padding:1px;}
|
||||
pre{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
blockquote{margin-left:4ex;margin-right:4ex;text-align:left;}
|
||||
td p{margin:0px;}
|
||||
.boxed{border:1px solid black}
|
||||
.textboxed{border:1px solid black}
|
||||
.vbar{border:none;width:2px;background-color:black;}
|
||||
.hbar{border:none;height:2px;width:100%;background-color:black;}
|
||||
.hfill{border:none;height:1px;width:200%;background-color:black;}
|
||||
.vdisplay{border-collapse:separate;border-spacing:2px;width:auto; empty-cells:show; border:2px solid red;}
|
||||
.vdcell{white-space:nowrap;padding:0px; border:2px solid green;}
|
||||
.display{border-collapse:separate;border-spacing:2px;width:auto; border:none;}
|
||||
.dcell{white-space:nowrap;padding:0px; border:none;}
|
||||
.dcenter{margin:0ex auto;}
|
||||
.vdcenter{border:solid #FF8000 2px; margin:0ex auto;}
|
||||
.minipage{text-align:left; margin-left:0em; margin-right:auto;}
|
||||
.marginpar{border:solid thin black; width:20%; text-align:left;}
|
||||
.marginparleft{float:left; margin-left:0ex; margin-right:1ex;}
|
||||
.marginparright{float:right; margin-left:1ex; margin-right:0ex;}
|
||||
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
|
||||
.part{margin:2ex auto;text-align:center}
|
||||
SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
|
||||
</style>
|
||||
<title>Ejabberd community 14.05-120-gedfb5fc Feature Sheet
|
||||
</title>
|
||||
</head>
|
||||
<body >
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic features.tex -->
|
||||
<!--CUT STYLE article--><!--CUT DEF section 1 --><p><a id="titlepage"></a>
|
||||
|
||||
</p><table class="title"><tr><td style="padding:1ex"><h1 class="titlemain">Ejabberd community 14.05-120-gedfb5fc Feature Sheet</h1><h3 class="titlerest">Sander Devrieze <br>
|
||||
<a href="mailto:s.devrieze@pandora.be"><span style="font-family:monospace">mailto:s.devrieze@pandora.be</span></a> <br>
|
||||
<a href="xmpp:sander@devrieze.dyndns.org"><span style="font-family:monospace">xmpp:sander@devrieze.dyndns.org</span></a></h3></td></tr>
|
||||
</table><div class="center">
|
||||
|
||||
<img src="logo.png" alt="logo.png">
|
||||
|
||||
|
||||
</div><blockquote class="quotation"><span style="color:#921700"><span style="font-style:italic">I can thoroughly recommend ejabberd for ease of setup –
|
||||
Kevin Smith, Current maintainer of the Psi project</span></span></blockquote><p>Introduction
|
||||
<a id="intro"></a></p><blockquote class="quotation"><span style="color:#921700"><span style="font-style:italic">I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. —
|
||||
Joeri</span></span></blockquote><p><span style="font-family:monospace">ejabberd</span> is a <span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">free and open source</span></span></span> instant messaging server written in <a href="http://www.erlang.org/">Erlang/OTP</a>.</p><p><span style="font-family:monospace">ejabberd</span> is <span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">cross-platform</span></span></span>, distributed, fault-tolerant, and based on open standards to achieve real-time communication.</p><p><span style="font-family:monospace">ejabberd</span> is designed to be a <span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">rock-solid and feature rich</span></span></span> XMPP server.</p><p><span style="font-family:monospace">ejabberd</span> is suitable for small deployments, whether they need to be <span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">scalable</span></span></span> or not, as well as extremely big deployments.</p>
|
||||
<!--TOC section id="sec1" Key Features-->
|
||||
<h2 id="sec1" class="section">Key Features</h2><!--SEC END --><p>
|
||||
<a id="keyfeatures"></a>
|
||||
</p><blockquote class="quotation"><span style="color:#921700"><span style="font-style:italic">Erlang seems to be tailor-made for writing stable, robust servers. —
|
||||
Peter Saint-André, Executive Director of the Jabber Software Foundation</span></span></blockquote><p><span style="font-family:monospace">ejabberd</span> is:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
<span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Cross-platform:</span></span></span> <span style="font-family:monospace">ejabberd</span> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.</li><li class="li-itemize"><span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Distributed:</span></span></span> You can run <span style="font-family:monospace">ejabberd</span> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.</li><li class="li-itemize"><span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Fault-tolerant:</span></span></span> You can deploy an <span style="font-family:monospace">ejabberd</span> 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’.</li><li class="li-itemize"><span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Administrator Friendly:</span></span></span> <span style="font-family:monospace">ejabberd</span> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Comprehensive documentation.
|
||||
</li><li class="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows. </li><li class="li-itemize">Web Administration.
|
||||
</li><li class="li-itemize">Shared Roster Groups.
|
||||
</li><li class="li-itemize">Command line administration tool. </li><li class="li-itemize">Can integrate with existing authentication mechanisms.
|
||||
</li><li class="li-itemize">Capability to send announce messages.
|
||||
</li></ul></li><li class="li-itemize"><span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Internationalized:</span></span></span> <span style="font-family:monospace">ejabberd</span> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Translated to 25 languages. </li><li class="li-itemize">Support for <a href="http://www.ietf.org/rfc/rfc3490.txt">IDNA</a>.
|
||||
</li></ul></li><li class="li-itemize"><span style="font-weight:bold"><span style="font-size:large"><span style="color:#001376">Open Standards:</span></span></span> <span style="font-family:monospace">ejabberd</span> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Fully XMPP compliant.
|
||||
</li><li class="li-itemize">XML-based protocol.
|
||||
</li><li class="li-itemize"><a href="http://www.ejabberd.im/protocols">Many protocols supported</a>.
|
||||
</li></ul></li></ul>
|
||||
<!--TOC section id="sec2" Additional Features-->
|
||||
<h2 id="sec2" class="section">Additional Features</h2><!--SEC END --><p>
|
||||
<a id="addfeatures"></a>
|
||||
</p><blockquote class="quotation"><span style="color:#921700"><span style="font-style:italic">ejabberd is making inroads to solving the "buggy incomplete server" problem —
|
||||
Justin Karneges, Founder of the Psi and the Delta projects</span></span></blockquote><p>Moreover, <span style="font-family:monospace">ejabberd</span> comes with a wide range of other state-of-the-art features:
|
||||
</p><ul class="itemize"><li class="li-itemize">
|
||||
Modular
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Load only the modules you want.
|
||||
</li><li class="li-itemize">Extend <span style="font-family:monospace">ejabberd</span> with your own custom modules.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Security
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
SASL and STARTTLS for c2s and s2s connections.
|
||||
</li><li class="li-itemize">STARTTLS and Dialback s2s connections.
|
||||
</li><li class="li-itemize">Web Admin accessible via HTTPS secure access.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Databases
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Internal database for fast deployment (Mnesia).
|
||||
</li><li class="li-itemize">Native MySQL support.
|
||||
</li><li class="li-itemize">Native PostgreSQL support.
|
||||
</li><li class="li-itemize">ODBC data storage support.
|
||||
</li><li class="li-itemize">Microsoft SQL Server support. </li><li class="li-itemize">Riak NoSQL database support.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Authentication
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Internal Authentication.
|
||||
</li><li class="li-itemize">PAM, LDAP, ODBC and Riak. </li><li class="li-itemize">External Authentication script.
|
||||
</li></ul>
|
||||
</li><li class="li-itemize">Others
|
||||
<ul class="itemize"><li class="li-itemize">
|
||||
Support for virtual hosting.
|
||||
</li><li class="li-itemize">Compressing XML streams with Stream Compression (<a href="http://www.xmpp.org/extensions/xep-0138.html">XEP-0138</a>).
|
||||
</li><li class="li-itemize">Statistics via Statistics Gathering (<a href="http://www.xmpp.org/extensions/xep-0039.html">XEP-0039</a>).
|
||||
</li><li class="li-itemize">IPv6 support both for c2s and s2s connections.
|
||||
</li><li class="li-itemize"><a href="http://www.xmpp.org/extensions/xep-0045.html">Multi-User Chat</a> module with support for clustering and HTML logging. </li><li class="li-itemize">Users Directory based on users vCards.
|
||||
</li><li class="li-itemize"><a href="http://www.xmpp.org/extensions/xep-0060.html">Publish-Subscribe</a> component with support for <a href="http://www.xmpp.org/extensions/xep-0163.html">Personal Eventing via Pubsub</a>.
|
||||
</li><li class="li-itemize">Support for web clients: <a href="http://www.xmpp.org/extensions/xep-0025.html">HTTP Polling</a> and <a href="http://www.xmpp.org/extensions/xep-0206.html">HTTP Binding (BOSH)</a> services.
|
||||
</li><li class="li-itemize">IRC transport.
|
||||
</li><li class="li-itemize">SIP support.
|
||||
</li><li class="li-itemize">Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
|
||||
</li></ul>
|
||||
</li></ul><!--CUT END -->
|
||||
<!--HTMLFOOT-->
|
||||
<!--ENDHTML-->
|
||||
<!--FOOTER-->
|
||||
<hr style="height:2"><blockquote class="quote"><em>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
|
||||
</em><a href="http://hevea.inria.fr/index.html"><em>H</em><em><span style="font-size:small"><sup>E</sup></span></em><em>V</em><em><span style="font-size:small"><sup>E</sup></span></em><em>A</em></a><em>.</em></blockquote></body>
|
||||
</html>
|
||||
@@ -1,136 +0,0 @@
|
||||
\documentclass[a4paper,10pt]{article}
|
||||
|
||||
%% Packages
|
||||
\usepackage{epsfig}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphics}
|
||||
\usepackage{hevea}
|
||||
\usepackage[pdftex,colorlinks,unicode,urlcolor=blue,linkcolor=blue,pdftitle=Ejabberd\
|
||||
Feature\ Sheet,pdfauthor=Sander\
|
||||
Devrieze,pdfsubject=ejabberd,pdfkeywords=ejabberd]{hyperref}
|
||||
\usepackage{verbatim}
|
||||
\usepackage{color}
|
||||
|
||||
%% Index
|
||||
% Remove the index anchors from the HTML version to save size and bandwith.
|
||||
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
|
||||
|
||||
%% Images
|
||||
\newcommand{\logoscale}{0.7}
|
||||
\newcommand{\imgscale}{0.58}
|
||||
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
|
||||
\newcommand{\insscaleimg}[2]{
|
||||
\imgsrc{#2}{}
|
||||
\begin{latexonly}
|
||||
\scalebox{#1}{\includegraphics{#2}}
|
||||
\end{latexonly}
|
||||
}
|
||||
|
||||
%% Various
|
||||
\newcommand{\bracehack}{\def\{{\char"7B}\def\}{\char"7D}}
|
||||
\newcommand{\titem}[1]{\item[\bracehack\texttt{#1}]}
|
||||
\newcommand{\ns}[1]{\texttt{#1}}
|
||||
\newcommand{\jid}[1]{\texttt{#1}}
|
||||
\newcommand{\option}[1]{\texttt{#1}}
|
||||
\newcommand{\poption}[1]{{\bracehack\texttt{#1}}}
|
||||
\newcommand{\node}[1]{\texttt{#1}}
|
||||
\newcommand{\term}[1]{\texttt{#1}}
|
||||
\newcommand{\shell}[1]{\texttt{#1}}
|
||||
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
|
||||
%% Title page
|
||||
\include{version}
|
||||
\title{Ejabberd \version\ Feature Sheet}
|
||||
\author{Sander Devrieze \\
|
||||
\ahrefurl{mailto:s.devrieze@pandora.be} \\
|
||||
\ahrefurl{xmpp:sander@devrieze.dyndns.org}}
|
||||
|
||||
% Options
|
||||
\newcommand{\marking}[1]{\textbf{\begin{large}\textcolor{ejblue}{#1}\end{large}}} % Marking enabled
|
||||
\newcommand{\quoting}[2][yozhik]{\begin{quotation}\textcolor{#1}{\textit{#2}}\end{quotation}} % Quotes enabled
|
||||
\newcommand{\new}{\marginpar{\textcolor{red}{\textsc{new}}}} % Highlight new features
|
||||
\newcommand{\improved}{\marginpar{\textcolor{orange}{\textsc{improved}}}} % Highlight improved features
|
||||
\setcounter{secnumdepth}{-1} % Disable section numbering
|
||||
|
||||
%% To by-pass errors in the HTML version.
|
||||
\newstyle{SPAN}{width:20\%; float:right; text-align:left; margin-left:auto;}
|
||||
\definecolor{orange} {cmyk}{0.000,0.333,1.000,0.000}
|
||||
|
||||
%% Footnotes
|
||||
\begin{latexonly}
|
||||
\global\parskip=9pt plus 3pt minus 1pt
|
||||
\global\parindent=0pt
|
||||
\gdef\ahrefurl#1{\href{#1}{\texttt{#1}}}
|
||||
\gdef\footahref#1#2{#2\footnote{\href{#1}{\texttt{#1}}}}
|
||||
\end{latexonly}
|
||||
\newcommand{\txepref}[2]{\footahref{http://www.xmpp.org/extensions/xep-#1.html}{#2}}
|
||||
\newcommand{\xepref}[1]{\txepref{#1}{XEP-#1}}
|
||||
|
||||
%% Fancy header
|
||||
\fancyhf{}
|
||||
\pagestyle{fancy}
|
||||
\rhead{\textcolor{ejblue}{The Expandable Jabber/XMPP Daemon.}}
|
||||
\renewcommand{\headrule}{{\color{ejblue}%
|
||||
\hrule width\headwidth height\headrulewidth \vskip-\headrulewidth}}
|
||||
\lhead{\setlength{\unitlength}{-6mm}
|
||||
\begin{picture}(0,0)
|
||||
\put(5.8,3.25){\includegraphics[width=1.3\textwidth]{yozhikheader.png}}
|
||||
\end{picture}}
|
||||
|
||||
% Official ejabberd colours
|
||||
\definecolor{ejblue} {cmyk}{1.000,0.831,0.000,0.537} %RGB: 0,0,118 HTML: 000076
|
||||
\definecolor{ejgreenyellow}{cmyk}{0.079,0.000,0.275,0.102} %RGB: 209,229,159 HTML: d1e59f
|
||||
\definecolor{ejgreendark} {cmyk}{0.131,0.000,0.146,0.220} %RGB: 166,199,162 HTML: a6c7a2
|
||||
\definecolor{ejgreen} {cmyk}{0.077,0.000,0.081,0.078} %RGB: 216,236,215 HTML: d8ecd7
|
||||
\definecolor{ejgreenwhite} {cmyk}{0.044,0.000,0.048,0.020} %RGB: 239,250,238 HTML: effaee
|
||||
\definecolor{yozhik} {cmyk}{0.000,0.837,1.000,0.424} %RGB: 147,0,0 HTML: 930000
|
||||
|
||||
\begin{document}
|
||||
|
||||
\label{titlepage}
|
||||
\begin{titlepage}
|
||||
\maketitle{}
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\begin{center}
|
||||
{\insscaleimg{\logoscale}{logo.png}
|
||||
\par
|
||||
}
|
||||
\end{center}
|
||||
\quoting{I can thoroughly recommend ejabberd for ease of setup --
|
||||
Kevin Smith, Current maintainer of the Psi project}
|
||||
|
||||
\end{titlepage}
|
||||
|
||||
\newpage
|
||||
% Set the page counter to 2 so that the titlepage and the second page do not
|
||||
% have the same page number. This fixes the PDFLaTeX warning "destination with
|
||||
% the same identifier".
|
||||
\begin{latexonly}
|
||||
\setcounter{page}{2}
|
||||
\pagecolor{ejgreenwhite}
|
||||
\end{latexonly}
|
||||
|
||||
% Input introduction.tex
|
||||
\input{introduction}
|
||||
|
||||
\end{document}
|
||||
|
||||
%% TODO
|
||||
% * illustrations (e.g. screenshot from web interface)
|
||||
% * commented parts
|
||||
% * slides, guide and html version
|
||||
% * cleaning and improving LaTeX code
|
||||
% * key features: something like this (shorter)? (more focussed on Erlang now): "To reach the goal of high
|
||||
% availability, performance and clustering, ejabberd is written in Erlang, a programming language perfectly
|
||||
% suited for this. Besides that, some parts are written in C to also incude the advantages of this language. In
|
||||
% short, ejabberd is a perfect mix of mainly Erlang code, peppered with some C code to get the final touch!"
|
||||
% <picture of a cocktail>
|
||||
% * key features: saying that ejabberd the only XMPP server is that can do real clustering:
|
||||
% http://www.jivesoftware.org/forums/thread.jspa?threadID=14602
|
||||
% "What I find interesting is that *no* XMPP servers truly provide clustering. This includes all the commercial
|
||||
% servers. The one partial exception appears to be ejabberd, which can cluster certain data such as sessions,
|
||||
% but not all services such as MUC."
|
||||
% * try it today: links to migration tutorials
|
||||
@@ -1,105 +0,0 @@
|
||||
digraph messages {
|
||||
//concentrate=true;
|
||||
subgraph clusterclients {
|
||||
client1 [shape = box];
|
||||
client2 [shape = box];
|
||||
client3 [shape = box];
|
||||
|
||||
style = dashed;
|
||||
label = "Clients";
|
||||
}
|
||||
|
||||
subgraph clusternode1 {
|
||||
subgraph clusterc2s1 {
|
||||
c2s11;
|
||||
c2s12;
|
||||
style = invis;
|
||||
}
|
||||
subgraph clusterservices1 {
|
||||
service11;
|
||||
service12;
|
||||
service13;
|
||||
style = invis;
|
||||
}
|
||||
//subgraph clusters2s1 {
|
||||
//s2s11;
|
||||
//s2s12;
|
||||
//style = invis;
|
||||
//}
|
||||
c2s11 -> auth1;
|
||||
c2s12 -> auth1;
|
||||
auth1 -> c2s11;
|
||||
auth1 -> c2s12;
|
||||
c2s11 -> sm1;
|
||||
c2s11 -> router1;
|
||||
c2s12 -> sm1;
|
||||
c2s12 -> router1;
|
||||
router1 -> local1;
|
||||
router1 -> service11;
|
||||
router1 -> service12;
|
||||
router1 -> service13;
|
||||
router1 -> s2s11;
|
||||
router1 -> s2s12;
|
||||
service11 -> router1;
|
||||
service12 -> router1;
|
||||
service13 -> router1;
|
||||
s2s11 -> router1;
|
||||
s2s12 -> router1;
|
||||
local1 -> sm1;
|
||||
sm1 -> c2s11;
|
||||
sm1 -> c2s12;
|
||||
|
||||
style = dashed;
|
||||
label = "Node1";
|
||||
}
|
||||
|
||||
subgraph clusternode2 {
|
||||
c2s2 -> auth2;
|
||||
auth2 -> c2s2;
|
||||
c2s2 -> sm2;
|
||||
c2s2 -> router2;
|
||||
router2 -> local2;
|
||||
router2 -> service21;
|
||||
router2 -> s2s21;
|
||||
service21 -> router2;
|
||||
s2s21 -> router2;
|
||||
local2 -> sm2;
|
||||
sm2 -> c2s2;
|
||||
|
||||
style = dashed;
|
||||
label = "Node2";
|
||||
}
|
||||
|
||||
|
||||
|
||||
subgraph clusterservers {
|
||||
server1 [shape = box];
|
||||
server2 [shape = box];
|
||||
server3 [shape = box];
|
||||
|
||||
style = dashed;
|
||||
label = "Servers";
|
||||
}
|
||||
|
||||
|
||||
client1 -> c2s11;
|
||||
client2 -> c2s12;
|
||||
client3 -> c2s2;
|
||||
c2s11 -> client1 [constraint=false];
|
||||
c2s12 -> client2 [constraint=false];
|
||||
c2s2 -> client3 [constraint=false];
|
||||
|
||||
s2s11 -> server1 [minlen = 2];
|
||||
s2s12 -> server2 [minlen = 2];
|
||||
s2s21 -> server3 [minlen = 2];
|
||||
server1 -> s2s11 [constraint=false];
|
||||
server2 -> s2s12 [constraint=false];
|
||||
server3 -> s2s21 [constraint=false];
|
||||
|
||||
router1 -> router2;
|
||||
router2 -> router1;
|
||||
sm1 -> sm2;
|
||||
sm2 -> sm1;
|
||||
|
||||
label = "Data Flows";
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
\chapter{Introduction}
|
||||
\label{intro}
|
||||
|
||||
%% TODO: improve the feature sheet with a nice table to highlight new features.
|
||||
|
||||
\quoting{I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. ---
|
||||
Joeri}
|
||||
|
||||
%ejabberd is a free and open source instant messaging server written in Erlang. ejabberd is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication (Jabber/XMPP).
|
||||
|
||||
\ejabberd{} is a \marking{free and open source} instant messaging server written in \footahref{http://www.erlang.org/}{Erlang/OTP}.
|
||||
|
||||
\ejabberd{} is \marking{cross-platform}, distributed, fault-tolerant, and based on open standards to achieve real-time communication.
|
||||
|
||||
\ejabberd{} is designed to be a \marking{rock-solid and feature rich} XMPP server.
|
||||
|
||||
\ejabberd{} is suitable for small deployments, whether they need to be \marking{scalable} or not, as well as extremely big deployments.
|
||||
|
||||
%\subsection{Layout with example deployment (title needs a better name)}
|
||||
%\label{layout}
|
||||
|
||||
%In this section there will be a graphical overview like these:\\
|
||||
%\verb|http://www.tipic.com/var/timp/timp_dep.gif| \\
|
||||
%\verb|http://www.jabber.com/images/jabber_Com_Platform.jpg| \\
|
||||
%\verb|http://www.antepo.com/files/OPN45systemdatasheet.pdf| \\
|
||||
|
||||
%A page full with names of Jabber client that are known to work with ejabberd. \begin{tiny}tiny font\end{tiny}
|
||||
|
||||
%\subsection{Try It Today}
|
||||
%\label{trytoday}
|
||||
|
||||
%(Not sure if I will include/finish this section for the next version.)
|
||||
|
||||
%\begin{itemize}
|
||||
%\item Erlang REPOS
|
||||
%\item Packages in distributions
|
||||
%\item Windows binary
|
||||
%\item source tar.gz
|
||||
%\item Migration from Jabberd14 (and so also Jabberd2 because you can migrate from version 2 back to 14) and Jabber Inc. XCP possible.
|
||||
%\end{itemize}
|
||||
|
||||
\newpage
|
||||
\section{Key Features}
|
||||
\label{keyfeatures}
|
||||
\ind{features!key features}
|
||||
|
||||
\quoting{Erlang seems to be tailor-made for writing stable, robust servers. ---
|
||||
Peter Saint-Andr\'e, Executive Director of the Jabber Software Foundation}
|
||||
|
||||
\ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item \marking{Cross-platform:} \ejabberd{} runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.
|
||||
|
||||
\item \marking{Distributed:} You can run \ejabberd{} on a cluster of machines and all of them will serve the same \Jabber{} domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.
|
||||
|
||||
\item \marking{Fault-tolerant:} You can deploy an \ejabberd{} cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.
|
||||
|
||||
\item \marking{Administrator Friendly:} \ejabberd{} is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
\begin{itemize}
|
||||
\item Comprehensive documentation.
|
||||
\item Straightforward installers for Linux, Mac OS X, and Windows. %%\improved{}
|
||||
\item Web Administration.
|
||||
\item Shared Roster Groups.
|
||||
\item Command line administration tool. %%\improved{}
|
||||
\item Can integrate with existing authentication mechanisms.
|
||||
\item Capability to send announce messages.
|
||||
\end{itemize}
|
||||
|
||||
\item \marking{Internationalized:} \ejabberd{} leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
\begin{itemize}
|
||||
\item Translated to 25 languages. %%\improved{}
|
||||
\item Support for \footahref{http://tools.ietf.org/html/rfc3490}{IDNA}.
|
||||
\end{itemize}
|
||||
|
||||
\item \marking{Open Standards:} \ejabberd{} is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
\begin{itemize}
|
||||
\item Fully XMPP compliant.
|
||||
\item XML-based protocol.
|
||||
\item \footahref{http://www.ejabberd.im/protocols}{Many protocols supported}.
|
||||
\end{itemize}
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\newpage
|
||||
|
||||
\section{Additional Features}
|
||||
\label{addfeatures}
|
||||
\ind{features!additional features}
|
||||
|
||||
\quoting{ejabberd is making inroads to solving the "buggy incomplete server" problem ---
|
||||
Justin Karneges, Founder of the Psi and the Delta projects}
|
||||
|
||||
Moreover, \ejabberd{} comes with a wide range of other state-of-the-art features:
|
||||
\begin{itemize}
|
||||
\item Modular
|
||||
\begin{itemize}
|
||||
\item Load only the modules you want.
|
||||
\item Extend \ejabberd{} with your own custom modules.
|
||||
\end{itemize}
|
||||
\item Security
|
||||
\begin{itemize}
|
||||
\item SASL and STARTTLS for c2s and s2s connections.
|
||||
\item STARTTLS and Dialback s2s connections.
|
||||
\item Web Admin accessible via HTTPS secure access.
|
||||
\end{itemize}
|
||||
\item Databases
|
||||
\begin{itemize}
|
||||
\item Internal database for fast deployment (Mnesia).
|
||||
\item Native MySQL support.
|
||||
\item Native PostgreSQL support.
|
||||
\item ODBC data storage support.
|
||||
\item Microsoft SQL Server support. %%\new{}
|
||||
\item Riak NoSQL database support.
|
||||
\end{itemize}
|
||||
\item Authentication
|
||||
\begin{itemize}
|
||||
\item Internal Authentication.
|
||||
\item PAM, LDAP, ODBC and Riak. %%\improved{}
|
||||
\item External Authentication script.
|
||||
\end{itemize}
|
||||
\item Others
|
||||
\begin{itemize}
|
||||
\item Support for virtual hosting.
|
||||
\item Compressing XML streams with Stream Compression (\xepref{0138}).
|
||||
\item Statistics via Statistics Gathering (\xepref{0039}).
|
||||
\item IPv6 support both for c2s and s2s connections.
|
||||
\item \txepref{0045}{Multi-User Chat} module with support for clustering and HTML logging. %%\improved{}
|
||||
\item Users Directory based on users vCards.
|
||||
\item \txepref{0060}{Publish-Subscribe} component with support for \txepref{0163}{Personal Eventing via Pubsub}.
|
||||
\item Support for web clients: \txepref{0025}{HTTP Polling} and \txepref{0206}{HTTP Binding (BOSH)} services.
|
||||
\item IRC transport.
|
||||
\item SIP support.
|
||||
\item Component support: interface with networks such as AIM, ICQ and MSN installing special tranports.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 157 KiB |
@@ -1,5 +1,5 @@
|
||||
###
|
||||
### ejabberd configuration file
|
||||
###' ejabberd configuration file
|
||||
###
|
||||
###
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
### > Art thou not Romeo,
|
||||
### and a Montague?
|
||||
|
||||
### =======
|
||||
### LOGGING
|
||||
###. =======
|
||||
###' LOGGING
|
||||
|
||||
##
|
||||
## loglevel: Verbosity of log files generated by ejabberd.
|
||||
@@ -72,9 +72,18 @@ log_rate_limit: 100
|
||||
## watchdog_admins:
|
||||
## - "bob@example.com"
|
||||
|
||||
###. ===============
|
||||
###' NODE PARAMETERS
|
||||
|
||||
### ================
|
||||
### SERVED HOSTNAMES
|
||||
##
|
||||
## net_ticktime: Specifies net_kernel tick time in seconds. This options must have
|
||||
## identical value on all nodes, and in most cases shouldn't be changed at all from
|
||||
## default value.
|
||||
##
|
||||
## net_ticktime: 60
|
||||
|
||||
###. ================
|
||||
###' SERVED HOSTNAMES
|
||||
|
||||
##
|
||||
## hosts: Domains served by ejabberd.
|
||||
@@ -94,8 +103,8 @@ hosts:
|
||||
##
|
||||
## route_subdomains: s2s
|
||||
|
||||
### ===============
|
||||
### LISTENING PORTS
|
||||
###. ===============
|
||||
###' LISTENING PORTS
|
||||
|
||||
##
|
||||
## listen: The ports ejabberd will listen on, which service each is handled
|
||||
@@ -169,6 +178,9 @@ listen:
|
||||
## register: true
|
||||
captcha: true
|
||||
|
||||
###. ==================
|
||||
###' S2S GLOBAL OPTIONS
|
||||
|
||||
##
|
||||
## s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
|
||||
## Allowed values are: false optional required required_trusted
|
||||
@@ -214,8 +226,8 @@ listen:
|
||||
## - ipv6
|
||||
## outgoing_s2s_timeout: 10000
|
||||
|
||||
### ==============
|
||||
### AUTHENTICATION
|
||||
###. ==============
|
||||
###' AUTHENTICATION
|
||||
|
||||
##
|
||||
## auth_method: Method used to authenticate the users.
|
||||
@@ -305,8 +317,8 @@ auth_method: internal
|
||||
## - internal
|
||||
## - anonymous
|
||||
|
||||
### ==============
|
||||
### DATABASE SETUP
|
||||
###. ==============
|
||||
###' DATABASE SETUP
|
||||
|
||||
## ejabberd by default uses the internal Mnesia database,
|
||||
## so you do not necessarily need this section.
|
||||
@@ -366,8 +378,8 @@ auth_method: internal
|
||||
##
|
||||
## odbc_keepalive_interval: undefined
|
||||
|
||||
### ===============
|
||||
### TRAFFIC SHAPERS
|
||||
###. ===============
|
||||
###' TRAFFIC SHAPERS
|
||||
|
||||
shaper:
|
||||
##
|
||||
@@ -448,8 +460,8 @@ acl:
|
||||
## user:
|
||||
## - "bob-local": "localhost"
|
||||
|
||||
### ============
|
||||
### ACCESS RULES
|
||||
###. ============
|
||||
###' ACCESS RULES
|
||||
access:
|
||||
## Maximum number of simultaneous sessions allowed for a single user:
|
||||
max_user_sessions:
|
||||
@@ -518,8 +530,8 @@ access:
|
||||
## register:
|
||||
## all: deny
|
||||
|
||||
### ================
|
||||
### DEFAULT LANGUAGE
|
||||
###. ================
|
||||
###' DEFAULT LANGUAGE
|
||||
|
||||
##
|
||||
## language: Default language used for server messages.
|
||||
@@ -533,8 +545,8 @@ language: "en"
|
||||
## "localhost":
|
||||
## language: "ru"
|
||||
|
||||
### =======
|
||||
### CAPTCHA
|
||||
###. =======
|
||||
###' CAPTCHA
|
||||
|
||||
##
|
||||
## Full path to a script that generates the image.
|
||||
@@ -551,8 +563,8 @@ language: "en"
|
||||
##
|
||||
## captcha_limit: 5
|
||||
|
||||
### =======
|
||||
### MODULES
|
||||
###. =======
|
||||
###' MODULES
|
||||
|
||||
##
|
||||
## Modules enabled in all ejabberd virtual hosts.
|
||||
@@ -668,7 +680,9 @@ modules:
|
||||
|
||||
allow_contrib_modules: true
|
||||
|
||||
###.
|
||||
###'
|
||||
### Local Variables:
|
||||
### mode: yaml
|
||||
### End:
|
||||
### vim: set filetype=yaml tabstop=8
|
||||
### vim: set filetype=yaml tabstop=8 foldmarker=###',###. foldmethod=marker:
|
||||
|
||||
@@ -16,9 +16,6 @@ IEX={{bindir}}/iex
|
||||
EPMD={{bindir}}/epmd
|
||||
INSTALLUSER={{installuser}}
|
||||
|
||||
# Compatibility in ZSH
|
||||
#setopt shwordsplit 2>/dev/null
|
||||
|
||||
# check the proper system user is used if defined
|
||||
if [ "$INSTALLUSER" != "" ] ; then
|
||||
EXEC_CMD="false"
|
||||
@@ -112,6 +109,8 @@ DATETIME=`date "+%Y%m%d-%H%M%S"`
|
||||
ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
|
||||
ERL_INETRC=$ETC_DIR/inetrc
|
||||
|
||||
# define mnesia options
|
||||
MNESIA_OPTS="-mnesia dir \"\\\"$SPOOL_DIR\\\"\" $MNESIA_OPTIONS"
|
||||
# define erl parameters
|
||||
ERL_OPTIONS=$(echo $ERL_OPTIONS | sed 's/ /\\ /g')
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
@@ -177,7 +176,7 @@ start()
|
||||
$NAME $ERLANG_NODE \
|
||||
-noinput -detached \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOL_DIR\\\"\" \
|
||||
$MNESIA_OPTS \
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
@@ -218,7 +217,7 @@ live()
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME $ERLANG_NODE \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOL_DIR\\\"\" \
|
||||
$MNESIA_OPTS \
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
@@ -239,14 +238,6 @@ iexlive()
|
||||
--erl \"$ERLANG_OPTS\" --erl $ARGS --erl \"$@\""
|
||||
}
|
||||
|
||||
etop()
|
||||
{
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME debug-${TTY}-${ERLANG_NODE} \
|
||||
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
|
||||
}
|
||||
|
||||
# TODO: refactor debug warning and livewarning
|
||||
debugwarning()
|
||||
{
|
||||
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
||||
@@ -268,7 +259,7 @@ debugwarning()
|
||||
echo "Press return to continue"
|
||||
read foo
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
livewarning()
|
||||
@@ -295,7 +286,26 @@ livewarning()
|
||||
fi
|
||||
}
|
||||
|
||||
# TODO: Make iex command display only if ejabberd Elixir support has been enabled
|
||||
etop()
|
||||
{
|
||||
TTY=`tty | sed -e 's/.*\///g'`
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME debug-${TTY}-${ERLANG_NODE} \
|
||||
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
|
||||
}
|
||||
|
||||
ping()
|
||||
{
|
||||
TTY=`tty | sed -e 's/.*\///g'`
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME ping-${TTY}-${ERLANG_NODE} \
|
||||
-hidden \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
$KERNEL_OPTS $ERLANG_OPTS \
|
||||
-eval 'io:format(\"~p~n\",[net_adm:ping($1)])' \
|
||||
-s erlang halt -output text -noinput"
|
||||
}
|
||||
|
||||
help()
|
||||
{
|
||||
echo ""
|
||||
@@ -326,9 +336,9 @@ ctl()
|
||||
# flock that can lock a file descriptor.
|
||||
MAXCONNID=100
|
||||
CONNLOCKDIR={{localstatedir}}/lock/ejabberdctl
|
||||
FLOCK='/usr/bin/flock'
|
||||
FLOCK=/usr/bin/flock
|
||||
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
|
||||
JOT='/usr/bin/jot'
|
||||
JOT=/usr/bin/jot
|
||||
if [ ! -x "$JOT" ] ; then
|
||||
# no flock or jot, simply invoke ctlexec()
|
||||
CTL_CONN="ctl-${ERLANG_NODE}"
|
||||
@@ -395,7 +405,7 @@ ctl()
|
||||
ctlexec()
|
||||
{
|
||||
CONN_NAME=$1; shift
|
||||
COMMAND=$@
|
||||
COMMAND=$(echo $@ | sed 's/;/\\;/g')
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME ${CONN_NAME} \
|
||||
-noinput \
|
||||
@@ -408,7 +418,7 @@ ctlexec()
|
||||
# stop epmd if there is no other running node
|
||||
stop_epmd()
|
||||
{
|
||||
$EPMD -names 2>/dev/null | grep -q name || epmd -kill >/dev/null
|
||||
$EPMD -names 2>/dev/null | grep -q name || $EPMD -kill >/dev/null
|
||||
}
|
||||
|
||||
# make sure node not already running and node name unregistered
|
||||
@@ -468,6 +478,7 @@ case $ARGS in
|
||||
' iexdebug') iexdebug;;
|
||||
' live') live;;
|
||||
' iexlive') iexlive;;
|
||||
' ping'*) ping ${ARGS# ping};;
|
||||
' etop') etop;;
|
||||
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
|
||||
' stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
-ifndef(EJABBERD_SM_HRL).
|
||||
-define(EJABBERD_SM_HRL, true).
|
||||
|
||||
-record(session, {sid, usr, us, priority, info}).
|
||||
-record(session_counter, {vhost, count}).
|
||||
-type sid() :: {erlang:timestamp(), pid()}.
|
||||
@@ -5,3 +8,5 @@
|
||||
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
|
||||
| {oor, boolean()} | {auth_module, atom()}].
|
||||
-type prio() :: undefined | integer().
|
||||
|
||||
-endif.
|
||||
|
||||
@@ -110,8 +110,8 @@
|
||||
|
||||
-record(muc_online_users, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
resource = <<>> :: binary() | '_',
|
||||
room = <<>> :: binary() | '_',
|
||||
host = <<>> :: binary() | '_'}).
|
||||
room = <<>> :: binary() | '_' | '$1',
|
||||
host = <<>> :: binary() | '_' | '$2'}).
|
||||
|
||||
-type muc_online_users() :: #muc_online_users{}.
|
||||
|
||||
|
||||
@@ -35,12 +35,10 @@
|
||||
-define(NS_BLOCKING, <<"urn:xmpp:blocking">>).
|
||||
-define(NS_PRIVATE, <<"jabber:iq:private">>).
|
||||
-define(NS_VERSION, <<"jabber:iq:version">>).
|
||||
-define(NS_TIME90, <<"jabber:iq:time">>).
|
||||
-define(NS_TIME, <<"urn:xmpp:time">>).
|
||||
-define(NS_LAST, <<"jabber:iq:last">>).
|
||||
-define(NS_XDATA, <<"jabber:x:data">>).
|
||||
-define(NS_IQDATA, <<"jabber:iq:data">>).
|
||||
-define(NS_DELAY91, <<"jabber:x:delay">>).
|
||||
-define(NS_DELAY, <<"urn:xmpp:delay">>).
|
||||
-define(NS_HINTS, <<"urn:xmpp:hints">>).
|
||||
-define(NS_EXPIRE, <<"jabber:x:expire">>).
|
||||
@@ -143,6 +141,8 @@
|
||||
-define(NS_CAPTCHA, <<"urn:xmpp:captcha">>).
|
||||
-define(NS_MEDIA, <<"urn:xmpp:media-element">>).
|
||||
-define(NS_BOB, <<"urn:xmpp:bob">>).
|
||||
-define(NS_MAM_TMP, <<"urn:xmpp:mam:tmp">>).
|
||||
-define(NS_MAM_0, <<"urn:xmpp:mam:0">>).
|
||||
-define(NS_PING, <<"urn:xmpp:ping">>).
|
||||
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
|
||||
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "15.03.0",
|
||||
version: "15.06.0",
|
||||
elixir: "~> 1.0",
|
||||
elixirc_paths: ["lib"],
|
||||
compile_path: ".",
|
||||
@@ -25,21 +25,21 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:p1_xml, github: "processone/xml"},
|
||||
{:p1_logger, github: "processone/p1_logger"},
|
||||
{:p1_yaml, github: "processone/p1_yaml"},
|
||||
{:p1_tls, github: "processone/tls"},
|
||||
{:p1_stringprep, github: "processone/stringprep"},
|
||||
{:p1_zlib, github: "processone/zlib"},
|
||||
{:p1_cache_tab, github: "processone/cache_tab"},
|
||||
{:p1_utils, github: "processone/p1_utils"},
|
||||
{:p1_iconv, github: "processone/eiconv"},
|
||||
{:esip, github: "processone/p1_sip"},
|
||||
{:p1_stun, github: "processone/stun"},
|
||||
{:ehyperloglog, github: "vaxelfel/eHyperLogLog"},
|
||||
{:p1_mysql, github: "processone/mysql"},
|
||||
{:p1_pgsql, github: "processone/pgsql"},
|
||||
{:eredis, github: "wooga/eredis"}
|
||||
{:p1_xml, git: "https://github.com/processone/xml"},
|
||||
{:p1_logger, git: "https://github.com/processone/p1_logger"},
|
||||
{:p1_yaml, git: "https://github.com/processone/p1_yaml"},
|
||||
{:p1_tls, git: "https://github.com/processone/tls"},
|
||||
{:p1_stringprep, git: "https://github.com/processone/stringprep"},
|
||||
{:p1_zlib, git: "https://github.com/processone/zlib"},
|
||||
{:p1_cache_tab, git: "https://github.com/processone/cache_tab"},
|
||||
{:p1_utils, git: "https://github.com/processone/p1_utils"},
|
||||
{:p1_iconv, git: "https://github.com/processone/eiconv"},
|
||||
{:esip, git: "https://github.com/processone/p1_sip"},
|
||||
{:p1_stun, git: "https://github.com/processone/stun"},
|
||||
{:ehyperloglog, git: "https://github.com/vaxelfel/eHyperLogLog"},
|
||||
{:p1_mysql, git: "https://github.com/processone/mysql"},
|
||||
{:p1_pgsql, git: "https://github.com/processone/pgsql"},
|
||||
{:eredis, git: "https://github.com/wooga/eredis"}
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -65,7 +65,7 @@ defmodule Mix.Tasks.Compile.Asn1 do
|
||||
options = options ++ [:noobj, outdir: Erlang.to_erl_file(Path.dirname(output))]
|
||||
case :asn1ct.compile(Erlang.to_erl_file(input), options) do
|
||||
:ok -> {:ok, :done}
|
||||
error -> error
|
||||
error -> error
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
{"Database Tables Configuration at ","Configuración de tablas de la base de datos en "}.
|
||||
{"December","diciembre"}.
|
||||
{"Default users as participants","Los usuarios son participantes por defecto"}.
|
||||
{"Delete","Eliminar"}.
|
||||
{"Delete message of the day","Borrar mensaje del dia"}.
|
||||
{"Delete message of the day on all hosts","Borrar el mensaje del día en todos los dominios"}.
|
||||
{"Delete Selected","Eliminar los seleccionados"}.
|
||||
@@ -77,6 +76,7 @@
|
||||
{"Either approve or decline the voice request.","Aprueba o rechaza la petición de voz."}.
|
||||
{"ejabberd IRC module","Módulo de IRC para ejabberd"}.
|
||||
{"ejabberd MUC module","Módulo de MUC para ejabberd"}.
|
||||
{"ejabberd Multicast service","Servicio Multicast de ejabberd"}.
|
||||
{"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}.
|
||||
{"ejabberd vCard module","Módulo vCard para ejabberd"}.
|
||||
@@ -164,6 +164,7 @@
|
||||
{"Listened Ports at ","Puertos de escucha en "}.
|
||||
{"Listened Ports","Puertos de escucha"}.
|
||||
{"List of modules to start","Lista de módulos a iniciar"}.
|
||||
{"List of rooms","Lista de salas"}.
|
||||
{"Low level update script","Script de actualización a bajo nivel"}.
|
||||
{"Make participants list public","La lista de participantes es pública"}.
|
||||
{"Make room CAPTCHA protected","Proteger la sala con CAPTCHA"}.
|
||||
@@ -189,6 +190,8 @@
|
||||
{"Modules at ~p","Módulos en ~p"}.
|
||||
{"Modules","Módulos"}.
|
||||
{"Monday","lunes"}.
|
||||
{"Multicast","Multicast"}.
|
||||
{"Multi-User Chat","Salas de Charla"}.
|
||||
{"Name:","Nombre:"}.
|
||||
{"Name","Nombre"}.
|
||||
{"Never","Nunca"}.
|
||||
@@ -241,6 +244,7 @@
|
||||
{"Path to File","Ruta al fichero"}.
|
||||
{"Pending","Pendiente"}.
|
||||
{"Period: ","Periodo: "}.
|
||||
{"Permanent rooms","Salas permanentes"}.
|
||||
{"Persist items to storage","Persistir elementos al almacenar"}.
|
||||
{"Ping","Ping"}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ten en cuenta que estas opciones solo harán copia de seguridad de la base de datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia de seguridad de tu base de datos SQL."}.
|
||||
@@ -258,6 +262,7 @@
|
||||
{"Raw","Crudo"}.
|
||||
{"Really delete message of the day?","¿Está seguro de quere borrar el mensaje del dia?"}.
|
||||
{"Register a Jabber account","Registrar una cuenta Jabber"}.
|
||||
{"Registered nicknames","Apodos registrados"}.
|
||||
{"Registered Users:","Usuarios registrados:"}.
|
||||
{"Registered Users","Usuarios registrados"}.
|
||||
{"Register","Registrar"}.
|
||||
@@ -338,7 +343,6 @@
|
||||
{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","No importa si usas mayúsculas: macbeth es lo mismo que MacBeth y Macbeth."}.
|
||||
{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta página te permite crear una cuenta Jabber este servidor Jabber. Tu JID (Jabber IDentificador) será de la forma: nombredeusuario@servidor. Por favor lee detenidamente las instrucciones para rellenar correctamente los campos."}.
|
||||
{"This page allows to unregister a Jabber account in this Jabber server.","Esta página te permite borrar tu cuenta Jabber en este servidor Jabber."}.
|
||||
{"This room is not anonymous","Sala no anónima"}.
|
||||
{"Thursday","jueves"}.
|
||||
{"Time delay","Retraso temporal"}.
|
||||
{"Time","Fecha"}.
|
||||
@@ -346,6 +350,7 @@
|
||||
{"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}.
|
||||
{"To","Para"}.
|
||||
{"To ~s","A ~s"}.
|
||||
{"Total rooms","Salas totales"}.
|
||||
{"Transactions Aborted:","Transacciones abortadas:"}.
|
||||
{"Transactions Committed:","Transacciones finalizadas:"}.
|
||||
{"Transactions Logged:","Transacciones registradas:"}.
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
{"Add User","הוסף משתמש"}.
|
||||
{"Administration of ","ניהול של "}.
|
||||
{"Administration","הנהלה"}.
|
||||
{"A friendly name for the node","שם ידידותי עבור הממסר"}.
|
||||
{"A friendly name for the node","שם ידידותי עבור הצומת"}.
|
||||
{"All activity","כל פעילות"}.
|
||||
{"Allow this Jabber ID to subscribe to this pubsub node?","להתיר לכתובת JID זו להירשם אל ממסר PubSub זה?"}.
|
||||
{"Allow this Jabber ID to subscribe to this pubsub node?","להתיר לכתובת JID זו להירשם לצומת PubSub זה?"}.
|
||||
{"Allow users to change the subject","התר למשתמשים לשנות את הנושא"}.
|
||||
{"Allow users to query other users","התר למשתמשים לתשאל משתמשים אחרים"}.
|
||||
{"Allow users to send invites","התר למשתמשים לשלוח הזמנות"}.
|
||||
@@ -23,13 +23,14 @@
|
||||
{"Allow visitors to send status text in presence updates","התר למבקרים לשלוח טקסט מצב בעדכוני נוכחות"}.
|
||||
{"Allow visitors to send voice requests","התר למבקרים לשלוח בקשות ביטוי"}.
|
||||
{"All Users","כל המשתמשים"}.
|
||||
{"Announcements","מודעות"}.
|
||||
{"Announcements","בשורות"}.
|
||||
{"anyone","לכל אחד"}.
|
||||
{"April","אפריל"}.
|
||||
{"August","אוגוסט"}.
|
||||
{"Backup Management","ניהול גיבוי"}.
|
||||
{"Backup to File at ","גבה אל קובץ אצל "}.
|
||||
{"Backup","גבה"}.
|
||||
{"Backup of ~p","גיבוי של ~p"}.
|
||||
{"Backup to File at ","גבה לקובץ אצל "}.
|
||||
{"Backup","גיבוי"}.
|
||||
{"Bad format","פורמט רע"}.
|
||||
{"Birthday","יום הולדת"}.
|
||||
{"CAPTCHA web page","עמוד רשת CAPTCHA"}.
|
||||
@@ -37,10 +38,10 @@
|
||||
{"Change User Password","שנה סיסמת משתמש"}.
|
||||
{"Characters not allowed:","תווים לא מורשים:"}.
|
||||
{"Chatroom configuration modified","תצורת חדר שיחה שונתה"}.
|
||||
{"Chatroom is created","חדר שיחה נוצר"}.
|
||||
{"Chatroom is destroyed","חדר שיחה הרוס"}.
|
||||
{"Chatroom is started","חדר שיחה מותחל"}.
|
||||
{"Chatroom is stopped","חדר שיחה הופסק"}.
|
||||
{"Chatroom is created","חדר שיחה הינו נוצר"}.
|
||||
{"Chatroom is destroyed","חדר שיחה הינו הרוס"}.
|
||||
{"Chatroom is started","חדר שיחה הינו מותחל"}.
|
||||
{"Chatroom is stopped","חדר שיחה הינו מופסק"}.
|
||||
{"Chatrooms","חדרי שיחה"}.
|
||||
{"Choose a username and password to register with this server","בחר שם משתמש וסיסמה להירשם עם שרת זה"}.
|
||||
{"Choose modules to stop","בחר מודולים להפסקה"}.
|
||||
@@ -54,27 +55,28 @@
|
||||
{"Connections parameters","פרמטרים של חיבור"}.
|
||||
{"Country","ארץ"}.
|
||||
{"CPU Time:","זמן מחשב (CPU):"}.
|
||||
{"Database Tables at ~p","טבלאות מסד נתונים אצל ~p"}.
|
||||
{"Database Tables Configuration at ","תצורת טבלאות מסד נתונים אצל "}.
|
||||
{"Database","מסד נתונים"}.
|
||||
{"December","דצמבר"}.
|
||||
{"Default users as participants","משתמשים משתמטים כמשתתפים"}.
|
||||
{"Delete message of the day on all hosts","מחק הודעת היום בכל המארחים"}.
|
||||
{"Delete message of the day","מחק הודעת היום"}.
|
||||
{"Delete message of the day on all hosts","מחק את בשורת היום בכל המארחים"}.
|
||||
{"Delete message of the day","מחק את בשורת היום"}.
|
||||
{"Delete Selected","מחק נבחרות"}.
|
||||
{"Delete User","מחק משתמש"}.
|
||||
{"Delete","מחק"}.
|
||||
{"Deliver event notifications","מסירת התראות אירוע"}.
|
||||
{"Deliver payloads with event notifications","מסירת מטען ייעוד (מטע״ד) יחד עם התראות אירוע"}.
|
||||
{"Deliver payloads with event notifications","מסירת מטעני ייעוד (מטע״ד) יחד עם התראות אירוע"}.
|
||||
{"Description:","תיאור:"}.
|
||||
{"Disc only copy","העתק של תקליטור בלבד"}.
|
||||
{"Displayed Groups:","קבוצות מוצגות:"}.
|
||||
{"Don't tell your password to anybody, not even the administrators of the Jabber server.","אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber"}.
|
||||
{"Dump Backup to Text File at ","השלך גיבוי אל קובץ טקסט אצל "}.
|
||||
{"Dump to Text File","השלך אל קובץ טקסט"}.
|
||||
{"Don't tell your password to anybody, not even the administrators of the Jabber server.","אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber."}.
|
||||
{"Dump Backup to Text File at ","השלך גיבוי לקובץ טקסט אצל "}.
|
||||
{"Dump to Text File","השלך לקובץ טקסט"}.
|
||||
{"Edit Properties","ערוך מאפיינים"}.
|
||||
{"Either approve or decline the voice request.","או שתאשר או שתדחה את בקשת הביטוי."}.
|
||||
{"ejabberd IRC module","מודול IRC של ejabberd"}.
|
||||
{"ejabberd MUC module","מודול MUC של ejabberd"}.
|
||||
{"ejabberd Multicast service","שירות שידור מרובב של ejabberd"}.
|
||||
{"ejabberd Publish-Subscribe module","מודול Publish-Subscribe של ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","מודול SOCKS5 Bytestreams של ejabberd"}.
|
||||
{"ejabberd vCard module","מודול vCard של ejabberd"}.
|
||||
@@ -86,25 +88,27 @@
|
||||
{"End User Session","סיים סשן משתמש"}.
|
||||
{"Enter list of {Module, [Options]}","הזן רשימה של {מודול, [אפשרויות]}"}.
|
||||
{"Enter nickname you want to register","הזן שם כינוי אשר ברצונך לרושמו"}.
|
||||
{"Enter path to backup file","הזן נתיב אל קובץ גיבוי"}.
|
||||
{"Enter path to jabberd14 spool dir","הזן נתיב אל מדור סליל (spool dir) של jabberd14"}.
|
||||
{"Enter path to jabberd14 spool file","הזן נתיב אל קובץ סליל (spool file) של jabberd14"}.
|
||||
{"Enter path to text file","הזן נתיב אל קובץ טקסט"}.
|
||||
{"Enter path to backup file","הזן נתיב לקובץ גיבוי"}.
|
||||
{"Enter path to jabberd14 spool dir","הזן נתיב למדור סליל (spool dir) של jabberd14"}.
|
||||
{"Enter path to jabberd14 spool file","הזן נתיב לקובץ סליל (spool file) של jabberd14"}.
|
||||
{"Enter path to text file","הזן נתיב לקובץ טקסט"}.
|
||||
{"Enter the text you see","הזן את הטקסט אותו הינך רואה"}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","הזן שם משתמש וקידודים בהם ברצונך להשתמש לשם התחברות אל שרתים של IRC. לחץ 'הבא' כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות."}.
|
||||
{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לשם התחברות אל שרתים של IRC"}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","הזן שם משתמש וקידודים בהם ברצונך להשתמש לצורך התחברות לשרתי IRC. לחץ 'הבא' כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות."}.
|
||||
{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לצורך התחברות לשרתי IRC"}.
|
||||
{"Erlang Jabber Server","שרת ג׳אבּר Erlang"}.
|
||||
{"Error","שגיאה"}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","דוגמא: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}.
|
||||
{"Exclude Jabber IDs from CAPTCHA challenge","הוצא כתובות של Jabber מתוך אתגר CAPTCHA"}.
|
||||
{"Export data of all users in the server to PIEFXIS files (XEP-0227):","ייצוא מידע של כל המשתמשים אשר מצויים בשרת זה אל קבצי PIEFXIS (XEP-0227):"}.
|
||||
{"Export data of users in a host to PIEFXIS files (XEP-0227):","ייצוא מידע של כל המשתמשים בתוך מארח אל קבצי PIEFXIS (XEP-0227):"}.
|
||||
{"Export all tables as SQL queries to a file:","יצא את כל טבלאות בתור שאילתות SQL לתוך קובץ:"}.
|
||||
{"Export data of all users in the server to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך בשרת זה לתוך קבצי PIEFXIS (XEP-0227):"}.
|
||||
{"Export data of users in a host to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך מארח לתוך קבצי PIEFXIS (XEP-0227):"}.
|
||||
{"Family Name","שם משפחה"}.
|
||||
{"February","פברואר"}.
|
||||
{"Fill in fields to search for any matching Jabber User","מלא את שדות אלו כדי לחפש עבור כל משתמש Jabber מבוקש"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","מלא את התבניות כדי לחפש עבור כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה כדי להתאים אל מחרוזת-משנה)"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","מלא את התבניות כדי לחפש עבור כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה כדי להתאים למחרוזת-משנה)"}.
|
||||
{"Friday","יום שישי"}.
|
||||
{"From ~s","מאת ~s"}.
|
||||
{"From","מן"}.
|
||||
{"From","מאת"}.
|
||||
{"Full Name","שם מלא"}.
|
||||
{"Get Number of Online Users","השג מספר של משתמשים מקוונים"}.
|
||||
{"Get Number of Registered Users","השג מספר של משתמשים רשומים"}.
|
||||
@@ -125,12 +129,12 @@
|
||||
{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","אם ברצונך לציין פורטים, סיסמאות, קידודים אחרים עבור שרתים של IRC, מלא את רשימה זו עם ערכים בפורמט '{\"irc server\", \"encoding\", port, \"password\"}'. באופן משתמט שירות זה משתמש בקידוד \"~s\", פורט ~p, סיסמה ריקה."}.
|
||||
{"Import Directory","ייבוא מדור"}.
|
||||
{"Import File","ייבוא קובץ"}.
|
||||
{"Import user data from jabberd14 spool file:","ייבוא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:"}.
|
||||
{"Import user data from jabberd14 spool file:","יבא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:"}.
|
||||
{"Import User from File at ","ייבוא משתמש מתוך קובץ אצל "}.
|
||||
{"Import users data from a PIEFXIS file (XEP-0227):","ייבוא מידע משתמשים מתוך קובץ PIEFXIS (XEP-0227):"}.
|
||||
{"Import users data from jabberd14 spool directory:","ייבוא נתוני משתמשים מתוך מדור סליל (spool directory) של jabberd14:"}.
|
||||
{"Import users data from a PIEFXIS file (XEP-0227):","יבא מידע משתמשים מתוך קובץ PIEFXIS (XEP-0227):"}.
|
||||
{"Import users data from jabberd14 spool directory:","יבא נתוני משתמשים מתוך מדור סליל (spool directory) של jabberd14:"}.
|
||||
{"Import Users from Dir at ","ייבוא משתמשים מתוך מדור אצל "}.
|
||||
{"Import Users From jabberd14 Spool Files","ייבוא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14"}.
|
||||
{"Import Users From jabberd14 Spool Files","יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14"}.
|
||||
{"Invalid affiliation: ~s","סינוף שגוי: ~s"}.
|
||||
{"Invalid role: ~s","תפקיד שגוי: ~s"}.
|
||||
{"IP addresses","כתובות IP"}.
|
||||
@@ -146,18 +150,22 @@
|
||||
{"Jabber ID ~s is invalid","מזהה Jabber ~s הינו שגוי"}.
|
||||
{"Jabber ID","מזהה Jabber"}.
|
||||
{"January","ינואר"}.
|
||||
{"Join IRC channel","הצטרף אל ערוץ IRC"}.
|
||||
{"Join IRC channel","הצטרף לערוץ IRC"}.
|
||||
{"joins the room","נכנס/ת אל החדר"}.
|
||||
{"Join the IRC channel here.","הצטרף אל ערוץ IRC כאן."}.
|
||||
{"Join the IRC channel in this Jabber ID: ~s","הצטרף אל ערוץ IRC במזהה Jabber זה: ~s"}.
|
||||
{"Join the IRC channel here.","הצטרף לערוץ IRC כאן."}.
|
||||
{"Join the IRC channel in this Jabber ID: ~s","הצטרף לערוץ IRC במזהה Jabber זה: ~s"}.
|
||||
{"July","יולי"}.
|
||||
{"June","יוני"}.
|
||||
{"Last Activity","פעילות אחרונה"}.
|
||||
{"Last login","כניסה אחרונה"}.
|
||||
{"Last month","חודש אחרון"}.
|
||||
{"Last year","שנה אחרונה"}.
|
||||
{"leaves the room","עוזב/ת אל החדר"}.
|
||||
{"leaves the room","עוזב/ת את החדר"}.
|
||||
{"Listened Ports at ","פורטים מואזנים אצל "}.
|
||||
{"Listened Ports","פורטים מואזנים"}.
|
||||
{"List of modules to start","רשימה של מודולים להפעלה"}.
|
||||
{"List of rooms","רשימה של חדרים"}.
|
||||
{"Low level update script","תסריט עדכון Low level"}.
|
||||
{"Make participants list public","הפוך רשימת משתתפים אל פומבית"}.
|
||||
{"Make room CAPTCHA protected","הפוך חדר אל מוגן CAPTCHA"}.
|
||||
{"Make room members-only","הפוך חדר אל חברים-בלבד"}.
|
||||
@@ -168,7 +176,7 @@
|
||||
{"March","מרץ"}.
|
||||
{"Maximum Number of Occupants","מספר מרבי של נוכחים"}.
|
||||
{"Max # of items to persist","מספר מרבי של פריטים לקיבוע"}.
|
||||
{"Max payload size in bytes","גודל מרבי של מטען הייעוד ביחידות מידה של byte"}.
|
||||
{"Max payload size in bytes","גודל מרבי של מטען ייעוד (payload) ביחידות מידה של byte"}.
|
||||
{"May","מאי"}.
|
||||
{"Members:","חברים:"}.
|
||||
{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","שנן את הסיסמה שלך, או רשום אותה בנייר שמור במקום בטוח. אצל Jabber אין דרך אוטומטית לשחזר את הסיסמה שלך במידה וזו תישמט מתוך זיכרונך."}.
|
||||
@@ -178,9 +186,12 @@
|
||||
{"Minimum interval between voice requests (in seconds)","תדירות מינימלית בין בקשות ביטוי (בשניות)"}.
|
||||
{"moderators only","לאחראים בלבד"}.
|
||||
{"Modified modules","מודולים שהותאמו"}.
|
||||
{"Modules at ~p","מודולים אצל ~p"}.
|
||||
{"Modules","מודולים"}.
|
||||
{"Module","מודול"}.
|
||||
{"Monday","יום שני"}.
|
||||
{"Multicast","שידור מרובב"}.
|
||||
{"Multi-User Chat","שיחה מרובת משתמשים"}.
|
||||
{"Name:","שם:"}.
|
||||
{"Name","שם"}.
|
||||
{"Never","אף פעם"}.
|
||||
@@ -188,17 +199,20 @@
|
||||
{"Nickname Registration at ","רישום שם כינוי אצל "}.
|
||||
{"Nickname ~s does not exist in the room","שם כינוי ~s לא קיים בחדר"}.
|
||||
{"Nickname","שם כינוי"}.
|
||||
{"No body provided for announce message","לא סופק גוף עבור הודעת בשורה"}.
|
||||
{"nobody","אף אחד"}.
|
||||
{"No Data","אין מידע"}.
|
||||
{"Node ID","מזהה ממסר (NID)"}.
|
||||
{"Node not found","ממסר לא נמצא"}.
|
||||
{"Nodes","ממסרים"}.
|
||||
{"Node ID","מזהה צומת (NID)"}.
|
||||
{"Node not found","צומת לא נמצא"}.
|
||||
{"Node ~p","צומת ~p"}.
|
||||
{"Nodes","צמתים"}.
|
||||
{"No limit","ללא הגבלה"}.
|
||||
{"None","אין"}.
|
||||
{"No resource provided","לא סופק משאב"}.
|
||||
{"Not Found","לא נמצא"}.
|
||||
{"Notify subscribers when items are removed from the node","הודע מנויים כאשר פריטים מוסרים מתוך הממסר"}.
|
||||
{"Notify subscribers when the node configuration changes","הודע מנויים כאשר תצורת הממסר משתנה"}.
|
||||
{"Notify subscribers when the node is deleted","הודע מנויים כאשר הממסר נמחק"}.
|
||||
{"Notify subscribers when items are removed from the node","הודע מנויים כאשר פריטים מוסרים מתוך הצומת"}.
|
||||
{"Notify subscribers when the node configuration changes","הודע מנויים כאשר תצורת הצומת משתנה"}.
|
||||
{"Notify subscribers when the node is deleted","הודע מנויים כאשר הצומת נמחק"}.
|
||||
{"November","נובמבר"}.
|
||||
{"Number of occupants","מספר של נוכחים"}.
|
||||
{"Number of online users","מספר של משתמשים מקוונים"}.
|
||||
@@ -211,7 +225,7 @@
|
||||
{"Online Users:","משתמשים מקוונים:"}.
|
||||
{"Online Users","משתמשים מקוונים"}.
|
||||
{"Online","מקוון"}.
|
||||
{"Only deliver notifications to available users","מסור התראות אל משתמשים זמינים בלבד"}.
|
||||
{"Only deliver notifications to available users","מסור התראות למשתמשים זמינים בלבד"}.
|
||||
{"Only moderators and participants are allowed to change the subject in this room","רק אחראים ומשתתפים רשאים לשנות את הנושא בחדר זה"}.
|
||||
{"Only moderators are allowed to change the subject in this room","רק אחראים רשאים לשנות את הנושא בחדר זה"}.
|
||||
{"Options","אפשרויות"}.
|
||||
@@ -220,34 +234,40 @@
|
||||
{"Outgoing s2s Connections:","חיבורי s2s יוצאים:"}.
|
||||
{"Outgoing s2s Connections","חיבורי s2s יוצאים"}.
|
||||
{"Outgoing s2s Servers:","שרתי s2s יוצאים:"}.
|
||||
{"Packet","חבילת מידע"}.
|
||||
{"Password ~b","סיסמה ~b"}.
|
||||
{"Password Verification:","אימות סיסמה:"}.
|
||||
{"Password Verification","אימות סיסמה"}.
|
||||
{"Password:","סיסמה:"}.
|
||||
{"Password","סיסמה"}.
|
||||
{"Path to Dir","נתיב אל מדור"}.
|
||||
{"Path to File","נתיב אל קובץ"}.
|
||||
{"Path to Dir","נתיב למדור"}.
|
||||
{"Path to File","נתיב לקובץ"}.
|
||||
{"Pending","ממתינות"}.
|
||||
{"Period: ","משך זמן: "}.
|
||||
{"Permanent rooms","חדרים קבועים"}.
|
||||
{"Persist items to storage","פריטים קבועים לאחסון"}.
|
||||
{"Ping","פינג"}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","נא לשים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך בנפרד."}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","אנא שים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך בנפרד."}.
|
||||
{"Pong","פונג"}.
|
||||
{"Port ~b","פורט ~b"}.
|
||||
{"Port","פורט"}.
|
||||
{"Present real Jabber IDs to","הצג כתובות JID ממשיות"}.
|
||||
{"private, ","פרטי, "}.
|
||||
{"Protocol","פרוטוקול"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","בקשת מנוי PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","טיהור כל הפריטים כאשר המפרסם הרלוונטי "}.
|
||||
{"RAM and disc copy","העתק RAM וגם תקליטור"}.
|
||||
{"RAM copy","העתק RAM"}.
|
||||
{"Really delete message of the day?","באמת למחוק את הודעת היום?"}.
|
||||
{"Raw","גולמי"}.
|
||||
{"Really delete message of the day?","באמת למחוק את בשורת היום?"}.
|
||||
{"Register a Jabber account","רשום חשבון Jabber"}.
|
||||
{"Registered nicknames","שמות כינוי רשומים"}.
|
||||
{"Registered Users:","משתמשים רשומים:"}.
|
||||
{"Registered Users","משתמשים רשומים"}.
|
||||
{"Register","הרשם"}.
|
||||
{"Registration in mod_irc for ","רישום בתוך mod_irc עבור "}.
|
||||
{"Remote copy","עותק מרוחק"}.
|
||||
{"Remote copy","העתק מרוחק"}.
|
||||
{"Remove All Offline Messages","הסר את כל ההודעות הלא מקוונות"}.
|
||||
{"Remove User","הסר משתמש"}.
|
||||
{"Remove","הסר"}.
|
||||
@@ -269,69 +289,81 @@
|
||||
{"Roster size","גודל רשימה"}.
|
||||
{"Roster","רשימה"}.
|
||||
{"RPC Call Error","שגיאת קריאת RPC"}.
|
||||
{"Running Nodes","ממסרים שמורצים כעת"}.
|
||||
{"Running Nodes","צמתים מורצים"}.
|
||||
{"~s access rule configuration","~s תצורת כללי גישה"}.
|
||||
{"Saturday","יום שבת"}.
|
||||
{"Script check","בדיקת תסריט"}.
|
||||
{"Search Results for ","תוצאות חיפוש עבור "}.
|
||||
{"Search users in ","חיפוש משתמשים אצל "}.
|
||||
{"Send announcement to all online users on all hosts","שלח מודעות אל כל המשתמשים המקוונים בכל המארחים"}.
|
||||
{"Send announcement to all online users","שלח מודעות אל כל המשתמשים המקוונים"}.
|
||||
{"Send announcement to all users on all hosts","שלח מודעות אל כל המשתמשים בכל המארחים"}.
|
||||
{"Send announcement to all users","שלח מודעות אל כל המשתמשים"}.
|
||||
{"Send announcement to all online users on all hosts","שלח בשורה לכל המשתמשים המקוונים בכל המארחים"}.
|
||||
{"Send announcement to all online users","שלח בשורה לכל המשתמשים המקוונים"}.
|
||||
{"Send announcement to all users on all hosts","שלח בשורה לכל המשתמשים בכל המארחים"}.
|
||||
{"Send announcement to all users","שלח בשורה לכל המשתמשים"}.
|
||||
{"September","ספטמבר"}.
|
||||
{"Server ~b","שרת ~b"}.
|
||||
{"Server:","שרת:"}.
|
||||
{"Set message of the day and send to online users","קבע הודעת היום ושלח אל משתמשים מקוונים"}.
|
||||
{"Set message of the day on all hosts and send to online users","קבע הודעת היום בכל המארחים ושלח אל משתמשים מקוונים"}.
|
||||
{"Set message of the day and send to online users","קבע את בשורת היום ושלח למשתמשים מקוונים"}.
|
||||
{"Set message of the day on all hosts and send to online users","קבע את בשורת היום בכל המארחים ושלח למשתמשים מקוונים"}.
|
||||
{"Shared Roster Groups","קבוצות רשימה משותפות"}.
|
||||
{"Show Integral Table","הצג טבלה אינטגרלית"}.
|
||||
{"Show Ordinary Table","הצג טבלה רגילה"}.
|
||||
{"Shut Down Service","כבה שירות"}.
|
||||
{"~s invites you to the room ~s","~s מזמינך אל החדר ~s"}.
|
||||
{"~s invites you to the room ~s","~s מזמינך לחדר ~s"}.
|
||||
{"Some Jabber clients can store your password in your computer. Use that feature only if you trust your computer is safe.","לקוחות Jabber מסוימים יכולים לאחסן את הסיסמה שלך על המחשב שלך. השתמש בתכונה זו רק אם אתה סמוך כי המחשב שלך הינו מוגן."}.
|
||||
{"Specify the access model","ציין את מודל הגישה"}.
|
||||
{"Specify the event message type","ציין את טיפוס הודעת האירוע"}.
|
||||
{"Specify the publisher model","ציין את מודל הפרסום"}.
|
||||
{"~s's Offline Messages Queue","תור הודעות לא מקוונות של ~s"}.
|
||||
{"Start Modules at ","התחל מודולים אצל "}.
|
||||
{"Start Modules","התחל מודולים"}.
|
||||
{"Start","התחל"}.
|
||||
{"Statistics of ~p","סטטיסטיקות עבור ~p"}.
|
||||
{"Statistics of ~p","סטטיסטיקות של ~p"}.
|
||||
{"Statistics","סטטיסטיקה"}.
|
||||
{"Stop Modules at ","הפסק מודולים אצל "}.
|
||||
{"Stop Modules","הפסק מודולים"}.
|
||||
{"Stopped Nodes","ממסרים שנפסקו"}.
|
||||
{"Stopped Nodes","צמתים שנפסקו"}.
|
||||
{"Stop","הפסק"}.
|
||||
{"Storage Type","טיפוס אחסון"}.
|
||||
{"Store binary backup:","אחסן גיבוי בינארי:"}.
|
||||
{"Store plain text backup:","אחסן גיבוי טקסט גלוי (plain text):"}.
|
||||
{"Subject","נושא"}.
|
||||
{"Submit","שליחה"}.
|
||||
{"Submitted","נשלח"}.
|
||||
{"Submit","שלח"}.
|
||||
{"Subscriber Address","כתובת מנוי"}.
|
||||
{"Subscription","מִנּוּי"}.
|
||||
{"Subscription","הרשמה"}.
|
||||
{"Sunday","יום ראשון"}.
|
||||
{"The CAPTCHA is valid.","CAPTCHA הינה בתוקף."}.
|
||||
{"The collections with which a node is affiliated","האוספים עמם צומת מסונף"}.
|
||||
{"the password is","הסיסמה היא"}.
|
||||
{"The password of your Jabber account was successfully changed.","סיסמת חשבון Jabber שונתה בהצלחה."}.
|
||||
{"There was an error changing the password: ","אירעה שגיאה בשינוי הסיסמה: "}.
|
||||
{"There was an error creating the account: ","אירעה שגיאה ביצירת החשבון: "}.
|
||||
{"There was an error deleting the account: ","אירעה שגיאה במחיקת החשבון: "}.
|
||||
{"This IP address is blacklisted in ~s","כתובת IP זו רשומה ברשימה שחורה בתוך ~s"}.
|
||||
{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","חלק זה אינו ער לרישיות: macbeth הינה זהה כשם MacBeth וגם Macbeth."}.
|
||||
{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","עמוד זה מתיר ליצור חשבון Jabber בשרת Jabber זה. כתובת JID (Jabber IDentifier) תגובש באופן של: username@server. נא לקרוא בזהירות את ההוראות למילוי השדות באופן נכון."}.
|
||||
{"This page allows to unregister a Jabber account in this Jabber server.","עמוד זה מתיר לך לבטל רישום של חשבון Jabber בשרת Jabber זה."}.
|
||||
{"This room is not anonymous","חדר זה אינו אנונימי"}.
|
||||
{"Thursday","יום חמישי"}.
|
||||
{"Time delay","זמן שיהוי"}.
|
||||
{"Time","זמן"}.
|
||||
{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל גישה בשעה ~s UTC"}.
|
||||
{"Too many unacked stanzas","יותר מדי סטנזות בלי אישורי קבלה"}.
|
||||
{"To ~s","אל ~s"}.
|
||||
{"To","אל"}.
|
||||
{"Transactions Aborted:","טרנזקציות בוטלו:"}.
|
||||
{"Transactions Committed:","טרנזקציות בוצעו:"}.
|
||||
{"Transactions Logged:","טרנזקציות נרשמו:"}.
|
||||
{"Transactions Restarted:","טרנזקציות הותחלו מחדש:"}.
|
||||
{"Total rooms","חדרים סה״כ"}.
|
||||
{"To","לכבוד"}.
|
||||
{"Transactions Aborted:","טרנזקציות שבוטלו:"}.
|
||||
{"Transactions Committed:","טרנזקציות שבוצעו:"}.
|
||||
{"Transactions Logged:","טרנזקציות שנרשמו:"}.
|
||||
{"Transactions Restarted:","טרנזקציות שהותחלו מחדש:"}.
|
||||
{"Tuesday","יום שלישי"}.
|
||||
{"Unauthorized","לא מורשה"}.
|
||||
{"Unregister a Jabber account","בטל רישום חשבון Jabber"}.
|
||||
{"Unregister","בטל רישום"}.
|
||||
{"Update message of the day (don't send)","עדכן הודעת היום (אל תשלח)"}.
|
||||
{"Update message of the day on all hosts (don't send)","עדכן הודעת היום בכל המארחים (אל תשלח)"}.
|
||||
{"Update message of the day (don't send)","עדכן את בשורת היום (אל תשלח)"}.
|
||||
{"Update message of the day on all hosts (don't send)","עדכן את בשורת היום בכל המארחים (אל תשלח)"}.
|
||||
{"Update plan","תכנית עדכון"}.
|
||||
{"Update ~p","עדכון ~p"}.
|
||||
{"Update script","תסריט עדכון"}.
|
||||
{"Update","עדכן"}.
|
||||
{"Uptime:","זמן פעילות:"}.
|
||||
{"Use of STARTTLS required","נדרש שימוש של STARTTLS"}.
|
||||
@@ -339,6 +371,7 @@
|
||||
{"User Management","ניהול משתמשים"}.
|
||||
{"Username:","שם משתמש:"}.
|
||||
{"Users Last Activity","פעילות משתמשים אחרונה"}.
|
||||
{"User ~s","משתמש ~s"}.
|
||||
{"Users","משתמשים"}.
|
||||
{"User","משתמש"}.
|
||||
{"Validate","הענק תוקף"}.
|
||||
@@ -356,4 +389,4 @@
|
||||
{"You need an x:data capable client to search","עליך להשתמש בלקוח אשר מסוגל להבין x:data בכדי לחפש"}.
|
||||
{"Your Jabber account was successfully created.","חשבון Jabber נוצר בהצלחה."}.
|
||||
{"Your Jabber account was successfully deleted.","חשבון Jabber נמחק בהצלחה."}.
|
||||
{"Your messages to ~s are being blocked. To unblock them, visit ~s","ההודעות שלך לערוץ ~s הינן חסומות. כדי למנוע את חסימתן, בקר בכתובת ~s"}.
|
||||
{"Your messages to ~s are being blocked. To unblock them, visit ~s","ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s"}.
|
||||
|
||||
@@ -54,15 +54,14 @@ SrcDirs = lists:foldl(
|
||||
Acc
|
||||
end, [], Cfg),
|
||||
|
||||
Deps = [{p1_cache_tab, ".*", {git, "git://github.com/processone/cache_tab"}},
|
||||
{p1_tls, ".*", {git, "git://github.com/processone/tls"}},
|
||||
{p1_stringprep, ".*", {git, "git://github.com/processone/stringprep"}},
|
||||
{p1_xml, ".*", {git, "git://github.com/processone/xml"}},
|
||||
{esip, ".*", {git, "git://github.com/processone/p1_sip"}},
|
||||
{p1_stun, ".*", {git, "git://github.com/processone/stun"}},
|
||||
{p1_yaml, ".*", {git, "git://github.com/processone/p1_yaml"}},
|
||||
{ehyperloglog, ".*", {git, "https://github.com/vaxelfel/eHyperLogLog.git"}},
|
||||
{p1_utils, ".*", {git, "git://github.com/processone/p1_utils"}}],
|
||||
Deps = [{p1_cache_tab, ".*", {git, "https://github.com/processone/cache_tab"}},
|
||||
{p1_tls, ".*", {git, "https://github.com/processone/tls"}},
|
||||
{p1_stringprep, ".*", {git, "https://github.com/processone/stringprep"}},
|
||||
{p1_xml, ".*", {git, "https://github.com/processone/xml"}},
|
||||
{esip, ".*", {git, "https://github.com/processone/p1_sip"}},
|
||||
{p1_stun, ".*", {git, "https://github.com/processone/stun"}},
|
||||
{p1_yaml, ".*", {git, "https://github.com/processone/p1_yaml"}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils"}}],
|
||||
|
||||
ConfigureCmd = fun(Pkg, Flags) ->
|
||||
{'get-deps',
|
||||
@@ -87,32 +86,32 @@ PostHooks = [ConfigureCmd("p1_tls", ""),
|
||||
|
||||
CfgDeps = lists:flatmap(
|
||||
fun({mysql, true}) ->
|
||||
[{p1_mysql, ".*", {git, "git://github.com/processone/mysql"}}];
|
||||
[{p1_mysql, ".*", {git, "https://github.com/processone/mysql"}}];
|
||||
({pgsql, true}) ->
|
||||
[{p1_pgsql, ".*", {git, "git://github.com/processone/pgsql"}}];
|
||||
[{p1_pgsql, ".*", {git, "https://github.com/processone/pgsql"}}];
|
||||
({sqlite, true}) ->
|
||||
[{sqlite3, ".*", {git, "git://github.com/alexeyr/erlang-sqlite3"}}];
|
||||
[{sqlite3, ".*", {git, "https://github.com/alexeyr/erlang-sqlite3"}}];
|
||||
({pam, true}) ->
|
||||
[{p1_pam, ".*", {git, "git://github.com/processone/epam"}}];
|
||||
[{p1_pam, ".*", {git, "https://github.com/processone/epam"}}];
|
||||
({zlib, true}) ->
|
||||
[{p1_zlib, ".*", {git, "git://github.com/processone/zlib"}}];
|
||||
[{p1_zlib, ".*", {git, "https://github.com/processone/zlib"}}];
|
||||
({riak, true}) ->
|
||||
[{riakc, ".*", {git, "git://github.com/basho/riak-erlang-client", {tag, "1.4.2"}}}];
|
||||
[{riakc, ".*", {git, "https://github.com/basho/riak-erlang-client", {tag, "1.4.2"}}}];
|
||||
({json, true}) ->
|
||||
[{jiffy, ".*", {git, "git://github.com/davisp/jiffy"}}];
|
||||
[{jiffy, ".*", {git, "https://github.com/davisp/jiffy"}}];
|
||||
({elixir, true}) ->
|
||||
[{rebar_elixir_plugin, ".*", {git, "git://github.com/yrashk/rebar_elixir_plugin"}},
|
||||
{elixir, ".*", {git, "git://github.com/elixir-lang/elixir", {branch, "v1.0"}}}];
|
||||
[{rebar_elixir_plugin, ".*", {git, "https://github.com/yrashk/rebar_elixir_plugin"}},
|
||||
{elixir, ".*", {git, "https://github.com/elixir-lang/elixir", {branch, "v1.0"}}}];
|
||||
({iconv, true}) ->
|
||||
[{p1_iconv, ".*", {git, "git://github.com/processone/eiconv"}}];
|
||||
[{p1_iconv, ".*", {git, "https://github.com/processone/eiconv"}}];
|
||||
({lager, true}) ->
|
||||
[{lager, ".*", {git, "git://github.com/basho/lager"}}];
|
||||
[{lager, ".*", {git, "https://github.com/basho/lager"}}];
|
||||
({lager, false}) ->
|
||||
[{p1_logger, ".*", {git, "git://github.com/processone/p1_logger"}}];
|
||||
[{p1_logger, ".*", {git, "https://github.com/processone/p1_logger"}}];
|
||||
({tools, true}) ->
|
||||
[{meck, "0.*", {git, "https://github.com/eproxus/meck"}}];
|
||||
({redis, true}) ->
|
||||
[{eredis, ".*", {git, "git://github.com/wooga/eredis"}}];
|
||||
({redis, true}) ->
|
||||
[{eredis, ".*", {git, "https://github.com/wooga/eredis"}}];
|
||||
(_) ->
|
||||
[]
|
||||
end, Cfg),
|
||||
|
||||
@@ -270,3 +270,27 @@ CREATE TABLE caps_features (
|
||||
);
|
||||
|
||||
CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode);
|
||||
|
||||
CREATE TABLE archive (
|
||||
username text NOT NULL,
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer text NOT NULL,
|
||||
bare_peer text NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX i_username ON archive(username);
|
||||
CREATE INDEX i_timestamp ON archive(timestamp);
|
||||
CREATE INDEX i_peer ON archive(peer);
|
||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
def text NOT NULL,
|
||||
always text NOT NULL,
|
||||
never text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
@@ -85,6 +85,31 @@ CREATE TABLE spool (
|
||||
CREATE INDEX i_despool USING BTREE ON spool(username);
|
||||
CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at);
|
||||
|
||||
CREATE TABLE archive (
|
||||
username varchar(250) NOT NULL,
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer varchar(250) NOT NULL,
|
||||
bare_peer varchar(250) NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
||||
CREATE INDEX i_username USING BTREE ON archive(username);
|
||||
CREATE INDEX i_timestamp USING BTREE ON archive(timestamp);
|
||||
CREATE INDEX i_peer USING BTREE ON archive(peer);
|
||||
CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username varchar(250) NOT NULL PRIMARY KEY,
|
||||
def text NOT NULL,
|
||||
always text NOT NULL,
|
||||
never text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE vcard (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
vcard mediumtext NOT NULL,
|
||||
|
||||
@@ -85,6 +85,29 @@ CREATE TABLE spool (
|
||||
|
||||
CREATE INDEX i_despool ON spool USING btree (username);
|
||||
|
||||
CREATE TABLE archive (
|
||||
username text NOT NULL,
|
||||
timestamp BIGINT NOT NULL,
|
||||
peer text NOT NULL,
|
||||
bare_peer text NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
id SERIAL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX i_username ON archive USING btree (username);
|
||||
CREATE INDEX i_timestamp ON archive USING btree (timestamp);
|
||||
CREATE INDEX i_peer ON archive USING btree (peer);
|
||||
CREATE INDEX i_bare_peer ON archive USING btree (bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
def text NOT NULL,
|
||||
always text NOT NULL,
|
||||
never text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE vcard (
|
||||
username text PRIMARY KEY,
|
||||
@@ -288,3 +311,17 @@ CREATE TABLE caps_features (
|
||||
);
|
||||
|
||||
CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (node, subnode);
|
||||
|
||||
CREATE TABLE sm (
|
||||
usec bigint NOT NULL,
|
||||
pid text NOT NULL,
|
||||
node text NOT NULL,
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
priority text NOT NULL,
|
||||
info text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_sm_sid ON sm USING btree (usec, pid);
|
||||
CREATE INDEX i_sm_node ON sm USING btree (node);
|
||||
CREATE INDEX i_sm_username ON sm USING btree (username);
|
||||
|
||||
@@ -25,11 +25,14 @@
|
||||
|
||||
-module(acl).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0, to_record/3, add/3, add_list/3,
|
||||
add_local/3, add_list_local/3, load_from_config/0,
|
||||
match_rule/3, match_acl/3, transform_options/1]).
|
||||
add_local/3, add_list_local/3, load_from_config/0,
|
||||
match_rule/3, match_acl/3, transform_options/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -476,3 +479,7 @@ transform_options({access, Name, Rules}, Opts) ->
|
||||
[{access, [{Name, NewRules}]}|Opts];
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(access) -> fun (V) -> V end;
|
||||
opt_type(acl) -> fun (V) -> V end;
|
||||
opt_type(_) -> [access, acl].
|
||||
|
||||
@@ -25,10 +25,13 @@
|
||||
|
||||
-module(cyrsasl).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0, register_mechanism/3, listmech/1,
|
||||
server_new/7, server_start/3, server_step/2]).
|
||||
server_new/7, server_start/3, server_step/2,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -237,3 +240,10 @@ is_disabled(Mechanism) ->
|
||||
[str:to_upper(V)]
|
||||
end, []),
|
||||
lists:member(Mechanism, Disabled).
|
||||
|
||||
opt_type(disable_sasl_mechanisms) ->
|
||||
fun (V) when is_list(V) ->
|
||||
lists:map(fun (M) -> str:to_upper(M) end, V);
|
||||
(V) -> [str:to_upper(V)]
|
||||
end;
|
||||
opt_type(_) -> [disable_sasl_mechanisms].
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
-module(cyrsasl_anonymous).
|
||||
|
||||
-protocol({xep, 175, '1.2'}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
@@ -25,9 +25,12 @@
|
||||
|
||||
-module(cyrsasl_digest).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2,
|
||||
parse/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -256,3 +259,6 @@ response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
opt_type(fqdn) -> fun iolist_to_binary/1;
|
||||
opt_type(_) -> [fqdn].
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
-author('stephen.roettger@googlemail.com').
|
||||
|
||||
-protocol({rfc, 5802}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
-module(ejabberd).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 4, '2.9'}).
|
||||
-protocol({xep, 86, '1.0'}).
|
||||
-protocol({xep, 106, '1.1'}).
|
||||
-protocol({xep, 170, '1.0'}).
|
||||
-protocol({xep, 205, '1.0'}).
|
||||
-protocol({xep, 212, '1.0'}).
|
||||
-protocol({xep, 216, '1.0'}).
|
||||
-protocol({xep, 243, '1.0'}).
|
||||
-protocol({xep, 270, '1.0'}).
|
||||
|
||||
-export([start/0, stop/0, start_app/1, start_app/2,
|
||||
get_pid_file/0, check_app/1]).
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
%% Purge DB
|
||||
delete_expired_messages/0, delete_old_messages/1,
|
||||
%% Mnesia
|
||||
export2odbc/2,
|
||||
set_master/1,
|
||||
backup_mnesia/1, restore_mnesia/1,
|
||||
dump_mnesia/1, dump_table/2, load_mnesia/1,
|
||||
@@ -168,6 +167,10 @@ commands() ->
|
||||
desc = "Export all tables as SQL queries to a file",
|
||||
module = ejd2odbc, function = export,
|
||||
args = [{host, string}, {file, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = convert_to_scram, tags = [odbc],
|
||||
desc = "Convert the passwords in 'users' ODBC table to SCRAM",
|
||||
module = ejabberd_auth_odbc, function = convert_to_scram,
|
||||
args = [{host, binary}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = convert_to_yaml, tags = [config],
|
||||
desc = "Convert the input file from Erlang to YAML format",
|
||||
@@ -191,7 +194,7 @@ commands() ->
|
||||
|
||||
#ejabberd_commands{name = export2odbc, tags = [mnesia],
|
||||
desc = "Export virtual host information from Mnesia tables to SQL files",
|
||||
module = ?MODULE, function = export2odbc,
|
||||
module = ejd2odbc, function = export,
|
||||
args = [{host, string}, {directory, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = set_master, tags = [mnesia],
|
||||
@@ -408,23 +411,6 @@ delete_old_messages(Days) ->
|
||||
%%% Mnesia management
|
||||
%%%
|
||||
|
||||
export2odbc(Host, Directory) ->
|
||||
Tables = [{export_last, last},
|
||||
{export_offline, offline},
|
||||
{export_private_storage, private_storage},
|
||||
{export_roster, roster},
|
||||
{export_vcard, vcard},
|
||||
{export_vcard_search, vcard_search},
|
||||
{export_passwd, passwd}],
|
||||
Export = fun({TableFun, Table}) ->
|
||||
Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
|
||||
io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
|
||||
Res = (catch ejd2odbc:TableFun(Host, Filename)),
|
||||
io:format(" Result: ~p~n", [Res])
|
||||
end,
|
||||
lists:foreach(Export, Tables),
|
||||
ok.
|
||||
|
||||
set_master("self") ->
|
||||
set_master(node());
|
||||
set_master(NodeString) when is_list(NodeString) ->
|
||||
|
||||
@@ -24,11 +24,14 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_app).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-export([start_modules/0,start/2, prep_stop/1, stop/1, init/0]).
|
||||
-export([start_modules/0, start/2, prep_stop/1, stop/1,
|
||||
init/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -51,7 +54,7 @@ start(normal, _Args) ->
|
||||
ejabberd_admin:start(),
|
||||
gen_mod:start(),
|
||||
ejabberd_config:start(),
|
||||
set_loglevel_from_config(),
|
||||
set_settings_from_config(),
|
||||
acl:start(),
|
||||
shaper:start(),
|
||||
connect_nodes(),
|
||||
@@ -59,13 +62,13 @@ start(normal, _Args) ->
|
||||
ejabberd_rdbms:start(),
|
||||
ejabberd_riak_sup:start(),
|
||||
ejabberd_sm:start(),
|
||||
ejabberd_auth:start(),
|
||||
cyrsasl:start(),
|
||||
% Profiling
|
||||
%ejabberd_debug:eprof_start(),
|
||||
%ejabberd_debug:fprof_start(),
|
||||
maybe_add_nameservers(),
|
||||
ext_mod:start(),
|
||||
ejabberd_auth:start(),
|
||||
start_modules(),
|
||||
ejabberd_listener:start_listeners(),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
|
||||
@@ -231,12 +234,17 @@ delete_pid_file() ->
|
||||
file:delete(PidFilename)
|
||||
end.
|
||||
|
||||
set_loglevel_from_config() ->
|
||||
set_settings_from_config() ->
|
||||
Level = ejabberd_config:get_option(
|
||||
loglevel,
|
||||
fun(P) when P>=0, P=<5 -> P end,
|
||||
4),
|
||||
ejabberd_logger:set(Level).
|
||||
ejabberd_logger:set(Level),
|
||||
Ticktime = ejabberd_config:get_option(
|
||||
net_ticktime,
|
||||
opt_type(net_ticktime),
|
||||
60),
|
||||
net_kernel:set_net_ticktime(Ticktime).
|
||||
|
||||
start_apps() ->
|
||||
crypto:start(),
|
||||
@@ -248,3 +256,18 @@ start_apps() ->
|
||||
ejabberd:start_app(p1_stringprep),
|
||||
ejabberd:start_app(p1_zlib),
|
||||
ejabberd:start_app(p1_cache_tab).
|
||||
|
||||
opt_type(net_ticktime) ->
|
||||
fun (P) when is_integer(P), P > 0 -> P end;
|
||||
opt_type(cluster_nodes) ->
|
||||
fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end;
|
||||
opt_type(loglevel) ->
|
||||
fun (P) when P >= 0, P =< 5 -> P end;
|
||||
opt_type(modules) ->
|
||||
fun (Mods) ->
|
||||
lists:map(fun ({M, A}) when is_atom(M), is_list(A) ->
|
||||
{M, A}
|
||||
end,
|
||||
Mods)
|
||||
end;
|
||||
opt_type(_) -> [cluster_nodes, loglevel, modules, net_ticktime].
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
-module(ejabberd_auth).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% External exports
|
||||
@@ -42,7 +44,7 @@
|
||||
remove_user/2, remove_user/3, plain_password_required/1,
|
||||
store_type/1, entropy/1]).
|
||||
|
||||
-export([auth_modules/1]).
|
||||
-export([auth_modules/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -453,3 +455,10 @@ import(Server, riak, Passwd) ->
|
||||
ejabberd_auth_riak:import(Server, riak, Passwd);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
|
||||
opt_type(auth_method) ->
|
||||
fun (V) when is_list(V) ->
|
||||
true = lists:all(fun is_atom/1, V), V;
|
||||
(V) when is_atom(V) -> [V]
|
||||
end;
|
||||
opt_type(_) -> [auth_method].
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_anonymous).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-export([start/1,
|
||||
@@ -36,16 +38,15 @@
|
||||
unregister_connection/3
|
||||
]).
|
||||
|
||||
|
||||
%% Function used by ejabberd_auth:
|
||||
-export([login/2, set_password/3, check_password/3,
|
||||
check_password/5, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2, get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password_s/2,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password_s/2,
|
||||
get_password/2, get_password/3, is_user_exists/2,
|
||||
remove_user/2, remove_user/3, store_type/0,
|
||||
plain_password_required/0]).
|
||||
plain_password_required/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -141,7 +142,7 @@ remove_connection(SID, LUser, LServer) ->
|
||||
%% Register connection
|
||||
register_connection(SID,
|
||||
#jid{luser = LUser, lserver = LServer}, Info) ->
|
||||
AuthModule = list_to_atom(binary_to_list(xml:get_attr_s(<<"auth_module">>, Info))),
|
||||
AuthModule = proplists:get_value(auth_module, Info, undefined),
|
||||
case AuthModule == (?MODULE) of
|
||||
true ->
|
||||
ejabberd_hooks:run(register_user, LServer,
|
||||
@@ -269,3 +270,13 @@ plain_password_required() -> false.
|
||||
|
||||
store_type() ->
|
||||
plain.
|
||||
|
||||
opt_type(allow_multiple_connections) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(anonymous_protocol) ->
|
||||
fun (sasl_anon) -> sasl_anon;
|
||||
(login_anon) -> login_anon;
|
||||
(both) -> both
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[allow_multiple_connections, anonymous_protocol].
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
|
||||
-module(ejabberd_auth_external).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% 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,
|
||||
@@ -37,8 +38,8 @@
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0,
|
||||
plain_password_required/0]).
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -311,3 +312,11 @@ get_mod_last_configured(Server) ->
|
||||
|
||||
is_configured(Host, Module) ->
|
||||
gen_mod:is_loaded(Host, Module).
|
||||
|
||||
opt_type(extauth_cache) ->
|
||||
fun (false) -> undefined;
|
||||
(I) when is_integer(I), I >= 0 -> I
|
||||
end;
|
||||
opt_type(extauth_program) ->
|
||||
fun (V) -> binary_to_list(iolist_to_binary(V)) end;
|
||||
opt_type(_) -> [extauth_cache, extauth_program].
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
|
||||
-module(ejabberd_auth_internal).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% 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,
|
||||
@@ -38,7 +39,7 @@
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, export/1, import/1,
|
||||
import/3, plain_password_required/0]).
|
||||
import/3, plain_password_required/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -484,3 +485,6 @@ import(_LServer, mnesia, #passwd{} = P) ->
|
||||
mnesia:dirty_write(P);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
|
||||
opt_type(auth_password_format) -> fun (V) -> V end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_auth_ldap).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -34,16 +36,15 @@
|
||||
-export([init/1, handle_info/2, handle_call/3,
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
%% External exports
|
||||
-export([start/1, stop/1, start_link/1, set_password/3,
|
||||
check_password/3, check_password/5, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0,
|
||||
plain_password_required/0]).
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -360,7 +361,7 @@ parse_options(Host) ->
|
||||
Eldap_ID = jlib:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||
Bind_Eldap_ID = jlib:atom_to_binary(
|
||||
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||
UIDsTemp = eldap_utils:get_opt(
|
||||
UIDsTemp = gen_mod:get_opt(
|
||||
{ldap_uids, Host}, [],
|
||||
fun(Us) ->
|
||||
lists:map(
|
||||
@@ -375,7 +376,7 @@ parse_options(Host) ->
|
||||
end, [{<<"uid">>, <<"%u">>}]),
|
||||
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||
UserFilter = case eldap_utils:get_opt(
|
||||
UserFilter = case gen_mod:get_opt(
|
||||
{ldap_filter, Host}, [],
|
||||
fun check_filter/1, <<"">>) of
|
||||
<<"">> ->
|
||||
@@ -386,7 +387,7 @@ parse_options(Host) ->
|
||||
SearchFilter = eldap_filter:do_sub(UserFilter,
|
||||
[{<<"%u">>, <<"*">>}]),
|
||||
{DNFilter, DNFilterAttrs} =
|
||||
eldap_utils:get_opt({ldap_dn_filter, Host}, [],
|
||||
gen_mod:get_opt({ldap_dn_filter, Host}, [],
|
||||
fun([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
undefined ->
|
||||
@@ -398,7 +399,7 @@ parse_options(Host) ->
|
||||
NewDNF = check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end, {undefined, []}),
|
||||
LocalFilter = eldap_utils:get_opt(
|
||||
LocalFilter = gen_mod:get_opt(
|
||||
{ldap_local_filter, Host}, [], fun(V) -> V end),
|
||||
#state{host = Host, eldap_id = Eldap_ID,
|
||||
bind_eldap_id = Bind_Eldap_ID,
|
||||
@@ -418,3 +419,27 @@ check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
opt_type(ldap_dn_filter) ->
|
||||
fun ([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
undefined -> [];
|
||||
_ -> [iolist_to_binary(A) || A <- DNFA]
|
||||
end,
|
||||
NewDNF = check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end;
|
||||
opt_type(ldap_filter) -> fun check_filter/1;
|
||||
opt_type(ldap_local_filter) -> fun (V) -> V end;
|
||||
opt_type(ldap_uids) ->
|
||||
fun (Us) ->
|
||||
lists:map(fun ({U, P}) ->
|
||||
{iolist_to_binary(U), iolist_to_binary(P)};
|
||||
({U}) -> {iolist_to_binary(U)};
|
||||
(U) -> {iolist_to_binary(U)}
|
||||
end,
|
||||
lists:flatten(Us))
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[ldap_dn_filter, ldap_filter, ldap_local_filter,
|
||||
ldap_uids].
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
|
||||
-module(ejabberd_auth_odbc).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% 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,
|
||||
@@ -37,9 +38,8 @@
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0,
|
||||
plain_password_required/0,
|
||||
convert_to_scram/1]).
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
convert_to_scram/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -467,3 +467,6 @@ convert_to_scram(Server) ->
|
||||
Error -> Error
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(auth_password_format) -> fun (V) -> V end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
||||
@@ -24,22 +24,21 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_auth_pam).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('xram@jabber.ru').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% External exports
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
-export([start/1, set_password/3, check_password/3,
|
||||
check_password/5, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2, get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2,
|
||||
get_password/2, get_password_s/2, is_user_exists/2,
|
||||
remove_user/2, remove_user/3, store_type/0,
|
||||
plain_password_required/0]).
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
opt_type/1]).
|
||||
|
||||
start(_Host) ->
|
||||
ejabberd:start_app(p1_pam).
|
||||
@@ -118,3 +117,10 @@ get_pam_userinfotype(Host) ->
|
||||
(jid) -> jid
|
||||
end,
|
||||
username).
|
||||
|
||||
opt_type(pam_service) -> fun iolist_to_binary/1;
|
||||
opt_type(pam_userinfotype) ->
|
||||
fun (username) -> username;
|
||||
(jid) -> jid
|
||||
end;
|
||||
opt_type(_) -> [pam_service, pam_userinfotype].
|
||||
|
||||
@@ -25,8 +25,14 @@
|
||||
|
||||
-module(ejabberd_c2s).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 78, '2.5'}).
|
||||
-protocol({xep, 138, '2.0'}).
|
||||
-protocol({xep, 198, '1.3'}).
|
||||
|
||||
-update_info({update, 0}).
|
||||
|
||||
-define(GEN_FSM, p1_fsm).
|
||||
@@ -50,23 +56,12 @@
|
||||
get_subscribed/1,
|
||||
transform_listen_option/2]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_stream/2,
|
||||
wait_for_auth/2,
|
||||
wait_for_feature_request/2,
|
||||
wait_for_bind/2,
|
||||
wait_for_session/2,
|
||||
wait_for_sasl_response/2,
|
||||
wait_for_resume/2,
|
||||
session_established/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
print_state/1
|
||||
]).
|
||||
-export([init/1, wait_for_stream/2, wait_for_auth/2,
|
||||
wait_for_feature_request/2, wait_for_bind/2,
|
||||
wait_for_session/2, wait_for_sasl_response/2,
|
||||
wait_for_resume/2, session_established/2,
|
||||
handle_event/3, handle_sync_event/4, code_change/4,
|
||||
handle_info/3, terminate/3, print_state/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -169,14 +164,14 @@
|
||||
%% XEP-0198:
|
||||
|
||||
-define(IS_STREAM_MGMT_TAG(Name),
|
||||
Name == <<"enable">>;
|
||||
Name == <<"resume">>;
|
||||
Name == <<"a">>;
|
||||
Name == <<"r">>).
|
||||
(Name == <<"enable">>) or
|
||||
(Name == <<"resume">>) or
|
||||
(Name == <<"a">>) or
|
||||
(Name == <<"r">>)).
|
||||
|
||||
-define(IS_SUPPORTED_MGMT_XMLNS(Xmlns),
|
||||
Xmlns == ?NS_STREAM_MGMT_2;
|
||||
Xmlns == ?NS_STREAM_MGMT_3).
|
||||
(Xmlns == ?NS_STREAM_MGMT_2) or
|
||||
(Xmlns == ?NS_STREAM_MGMT_3)).
|
||||
|
||||
-define(MGMT_FAILED(Condition, Xmlns),
|
||||
#xmlel{name = <<"failed">>,
|
||||
@@ -288,6 +283,7 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
StartTLSRequired orelse TLSEnabled,
|
||||
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
||||
({ciphers, _}) -> true;
|
||||
({dhfile, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
Opts),
|
||||
@@ -312,7 +308,7 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
MaxAckQueue = case proplists:get_value(max_ack_queue, Opts) of
|
||||
Limit when is_integer(Limit), Limit > 0 -> Limit;
|
||||
infinity -> infinity;
|
||||
_ -> 500
|
||||
_ -> 1000
|
||||
end,
|
||||
ResumeTimeout = case proplists:get_value(resume_timeout, Opts) of
|
||||
Timeout when is_integer(Timeout), Timeout >= 0 -> Timeout;
|
||||
@@ -457,14 +453,14 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
StreamFeatures1 = TLSFeature ++ CompressFeature ++ Mechs,
|
||||
StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features,
|
||||
Server, StreamFeatures1, [Server]),
|
||||
send_element(StateData,
|
||||
#xmlel{name = <<"stream:features">>,
|
||||
attrs = [],
|
||||
children =
|
||||
TLSFeature ++ CompressFeature ++ Mechs
|
||||
++
|
||||
ejabberd_hooks:run_fold(c2s_stream_features,
|
||||
Server, [], [Server])}),
|
||||
StreamFeatures}),
|
||||
fsm_next_state(wait_for_feature_request,
|
||||
StateData#state{
|
||||
server = Server,
|
||||
@@ -489,7 +485,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
StreamFeatures = [#xmlel{name = <<"bind">>,
|
||||
StreamFeatures1 = [#xmlel{name = <<"bind">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_BIND}],
|
||||
children = []},
|
||||
#xmlel{name = <<"session">>,
|
||||
@@ -499,9 +495,9 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
RosterVersioningFeature ++
|
||||
StreamManagementFeature ++
|
||||
ejabberd_hooks:run_fold(c2s_post_auth_features,
|
||||
Server, [], [Server]) ++
|
||||
ejabberd_hooks:run_fold(c2s_stream_features,
|
||||
Server, [], [Server]),
|
||||
StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features,
|
||||
Server, StreamFeatures1, [Server]),
|
||||
send_element(StateData,
|
||||
#xmlel{name = <<"stream:features">>,
|
||||
attrs = [],
|
||||
@@ -643,7 +639,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p from ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID), AuthModule,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[true, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -684,7 +680,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("(~w) Failed legacy authentication for ~s from ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID),
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[false, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -705,7 +701,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
"for ~s from ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID),
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[false, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -760,7 +756,7 @@ wait_for_feature_request({xmlstreamelement, El},
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s "
|
||||
"by ~p from ~s",
|
||||
[StateData#state.socket, U, AuthModule,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[true, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -787,7 +783,7 @@ wait_for_feature_request({xmlstreamelement, El},
|
||||
?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s",
|
||||
[StateData#state.socket,
|
||||
Username, StateData#state.server,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[false, Username, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -913,7 +909,7 @@ wait_for_sasl_response({xmlstreamelement, El},
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s "
|
||||
"by ~p from ~s",
|
||||
[StateData#state.socket, U, AuthModule,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[true, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -936,7 +932,7 @@ wait_for_sasl_response({xmlstreamelement, El},
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s "
|
||||
"by ~p from ~s",
|
||||
[StateData#state.socket, U, AuthModule,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[true, U, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -965,7 +961,7 @@ wait_for_sasl_response({xmlstreamelement, El},
|
||||
?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s",
|
||||
[StateData#state.socket,
|
||||
Username, StateData#state.server,
|
||||
jlib:ip_to_list(StateData#state.ip)]),
|
||||
ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
|
||||
ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
|
||||
[false, Username, StateData#state.server,
|
||||
StateData#state.ip]),
|
||||
@@ -1264,12 +1260,14 @@ session_established2(El, StateData) ->
|
||||
_ ->
|
||||
case Name of
|
||||
<<"presence">> ->
|
||||
PresenceEl =
|
||||
PresenceEl0 =
|
||||
ejabberd_hooks:run_fold(c2s_update_presence,
|
||||
Server, NewEl,
|
||||
[User, Server]),
|
||||
ejabberd_hooks:run(user_send_packet, Server,
|
||||
[FromJID, ToJID, PresenceEl]),
|
||||
PresenceEl =
|
||||
ejabberd_hooks:run_fold(
|
||||
user_send_packet, Server, PresenceEl0,
|
||||
[NewStateData, FromJID, ToJID]),
|
||||
case ToJID of
|
||||
#jid{user = User, server = Server,
|
||||
resource = <<"">>} ->
|
||||
@@ -1289,16 +1287,18 @@ session_established2(El, StateData) ->
|
||||
process_privacy_iq(FromJID, ToJID, IQ,
|
||||
NewStateData);
|
||||
_ ->
|
||||
ejabberd_hooks:run(user_send_packet, Server,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
NewEl0 = ejabberd_hooks:run_fold(
|
||||
user_send_packet, Server, NewEl,
|
||||
[NewStateData, FromJID, ToJID]),
|
||||
check_privacy_route(FromJID, NewStateData,
|
||||
FromJID, ToJID, NewEl)
|
||||
FromJID, ToJID, NewEl0)
|
||||
end;
|
||||
<<"message">> ->
|
||||
ejabberd_hooks:run(user_send_packet, Server,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
NewEl0 = ejabberd_hooks:run_fold(
|
||||
user_send_packet, Server, NewEl,
|
||||
[NewStateData, FromJID, ToJID]),
|
||||
check_privacy_route(FromJID, NewStateData, FromJID,
|
||||
ToJID, NewEl);
|
||||
ToJID, NewEl0);
|
||||
_ -> NewStateData
|
||||
end
|
||||
end,
|
||||
@@ -1696,11 +1696,13 @@ handle_info({route, From, To,
|
||||
Attrs2 =
|
||||
jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To), NewAttrs),
|
||||
FixedPacket = #xmlel{name = Name, attrs = Attrs2, children = Els},
|
||||
FixedPacket0 = #xmlel{name = Name, attrs = Attrs2, children = Els},
|
||||
FixedPacket = ejabberd_hooks:run_fold(
|
||||
user_receive_packet,
|
||||
NewState#state.server,
|
||||
FixedPacket0,
|
||||
[NewState, NewState#state.jid, From, To]),
|
||||
SentStateData = send_packet(NewState, FixedPacket),
|
||||
ejabberd_hooks:run(user_receive_packet,
|
||||
SentStateData#state.server,
|
||||
[SentStateData#state.jid, From, To, FixedPacket]),
|
||||
ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
|
||||
fsm_next_state(StateName, SentStateData);
|
||||
true ->
|
||||
@@ -1891,7 +1893,7 @@ send_text(StateData, Text) when StateData#state.mgmt_state == active ->
|
||||
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
|
||||
{'EXIT', _} ->
|
||||
(StateData#state.sockmod):close(StateData#state.socket),
|
||||
error;
|
||||
{error, closed};
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
@@ -1912,12 +1914,7 @@ send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive ->
|
||||
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == pending ->
|
||||
mgmt_queue_add(StateData, Stanza);
|
||||
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == active ->
|
||||
NewStateData = case send_stanza_and_ack_req(StateData, Stanza) of
|
||||
ok ->
|
||||
StateData;
|
||||
error ->
|
||||
StateData#state{mgmt_state = pending}
|
||||
end,
|
||||
NewStateData = send_stanza_and_ack_req(StateData, Stanza),
|
||||
mgmt_queue_add(NewStateData, Stanza);
|
||||
send_stanza(StateData, Stanza) ->
|
||||
send_element(StateData, Stanza),
|
||||
@@ -2028,6 +2025,7 @@ get_conn_type(StateData) ->
|
||||
p1_tls -> c2s_compressed_tls
|
||||
end;
|
||||
ejabberd_http_bind -> http_bind;
|
||||
ejabberd_http_ws -> websocket;
|
||||
_ -> unknown
|
||||
end.
|
||||
|
||||
@@ -2830,11 +2828,12 @@ send_stanza_and_ack_req(StateData, Stanza) ->
|
||||
AckReq = #xmlel{name = <<"r">>,
|
||||
attrs = [{<<"xmlns">>, StateData#state.mgmt_xmlns}],
|
||||
children = []},
|
||||
case send_element(StateData, Stanza) of
|
||||
ok ->
|
||||
send_element(StateData, AckReq);
|
||||
error ->
|
||||
error
|
||||
case send_element(StateData, Stanza) == ok andalso
|
||||
send_element(StateData, AckReq) == ok of
|
||||
true ->
|
||||
StateData;
|
||||
false ->
|
||||
StateData#state{mgmt_state = pending}
|
||||
end.
|
||||
|
||||
mgmt_queue_add(StateData, El) ->
|
||||
@@ -3012,7 +3011,7 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
|
||||
end.
|
||||
|
||||
resume_session({Time, PID}) ->
|
||||
(?GEN_FSM):sync_send_all_state_event(PID, {resume_session, Time}, 3000).
|
||||
(?GEN_FSM):sync_send_all_state_event(PID, {resume_session, Time}, 5000).
|
||||
|
||||
make_resume_id(StateData) ->
|
||||
{Time, _} = StateData#state.sid,
|
||||
@@ -3126,3 +3125,15 @@ pack_string(String, Pack) ->
|
||||
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(max_fsm_queue) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(resource_conflict) ->
|
||||
fun (setresource) -> setresource;
|
||||
(closeold) -> closeold;
|
||||
(closenew) -> closenew;
|
||||
(acceptnew) -> acceptnew
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[domain_certfile, max_fsm_queue, resource_conflict].
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
|
||||
-module(ejabberd_c2s_config).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('mremond@process-one.net').
|
||||
|
||||
-export([get_c2s_limits/0]).
|
||||
-export([get_c2s_limits/0, opt_type/1]).
|
||||
|
||||
%% Get first c2s configuration limitations to apply it to other c2s
|
||||
%% connectors.
|
||||
@@ -63,3 +65,6 @@ select_opts_values([{max_stanza_size, Value} | Opts],
|
||||
[{max_stanza_size, Value} | SelectedValues]);
|
||||
select_opts_values([_Opt | Opts], SelectedValues) ->
|
||||
select_opts_values(Opts, SelectedValues).
|
||||
|
||||
opt_type(listen) -> fun (V) -> V end;
|
||||
opt_type(_) -> [listen].
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
-module(ejabberd_captcha).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-protocol({xep, 158, '1.0'}).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
@@ -37,7 +41,7 @@
|
||||
-export([create_captcha/6, build_captcha_html/2,
|
||||
check_captcha/2, process_reply/1, process/2,
|
||||
is_feature_available/0, create_captcha_x/5,
|
||||
create_captcha_x/6]).
|
||||
create_captcha_x/6, opt_type/1]).
|
||||
|
||||
-include("jlib.hrl").
|
||||
|
||||
@@ -693,3 +697,14 @@ clean_treap(Treap, CleanPriority) ->
|
||||
now_priority() ->
|
||||
{MSec, Sec, USec} = now(),
|
||||
-((MSec * 1000000 + Sec) * 1000000 + USec).
|
||||
|
||||
opt_type(captcha_cmd) ->
|
||||
fun (FileName) ->
|
||||
F = iolist_to_binary(FileName), if F /= <<"">> -> F end
|
||||
end;
|
||||
opt_type(captcha_host) -> fun iolist_to_binary/1;
|
||||
opt_type(captcha_limit) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(listen) -> fun (V) -> V end;
|
||||
opt_type(_) ->
|
||||
[captcha_cmd, captcha_host, captcha_limit, listen].
|
||||
|
||||
@@ -376,6 +376,15 @@ check_access_commands(AccessCommands, Auth, Method, Command, Arguments) ->
|
||||
AccessCommandsAllowed =
|
||||
lists:filter(
|
||||
fun({Access, Commands, ArgumentRestrictions}) ->
|
||||
case check_access(Access, Auth) of
|
||||
true ->
|
||||
check_access_command(Commands, Command, ArgumentRestrictions,
|
||||
Method, Arguments);
|
||||
false ->
|
||||
false
|
||||
end;
|
||||
({Access, Commands}) ->
|
||||
ArgumentRestrictions = [],
|
||||
case check_access(Access, Auth) of
|
||||
true ->
|
||||
check_access_command(Commands, Command, ArgumentRestrictions,
|
||||
|
||||
@@ -36,13 +36,14 @@
|
||||
prepare_opt_val/4, convert_table_to_binary/5,
|
||||
transform_options/1, collect_options/1,
|
||||
convert_to_yaml/1, convert_to_yaml/2,
|
||||
env_binary_to_list/2]).
|
||||
env_binary_to_list/2, opt_type/1, may_hide_data/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_config.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
-callback opt_type(atom()) -> function() | [atom()].
|
||||
|
||||
%% @type macro() = {macro_key(), macro_value()}
|
||||
|
||||
@@ -65,7 +66,8 @@ start() ->
|
||||
{attributes, record_info(fields, local_config)}]),
|
||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||
Config = get_ejabberd_config_path(),
|
||||
State = read_file(Config),
|
||||
State0 = read_file(Config),
|
||||
State = validate_opts(State0),
|
||||
%% This start time is used by mod_last:
|
||||
{MegaSecs, Secs, _} = now(),
|
||||
UnixTime = MegaSecs*1000000 + Secs,
|
||||
@@ -114,7 +116,8 @@ get_env_config() ->
|
||||
%% @spec (File::string()) -> #state{}.
|
||||
read_file(File) ->
|
||||
read_file(File, [{replace_macros, true},
|
||||
{include_files, true}]).
|
||||
{include_files, true},
|
||||
{include_modules_configs, true}]).
|
||||
|
||||
read_file(File, Opts) ->
|
||||
Terms1 = get_plain_terms_file(File, Opts),
|
||||
@@ -200,7 +203,14 @@ get_plain_terms_file(File1, Opts) ->
|
||||
File = get_absolute_path(File1),
|
||||
case consult(File) of
|
||||
{ok, Terms} ->
|
||||
BinTerms = strings_to_binary(Terms),
|
||||
BinTerms1 = strings_to_binary(Terms),
|
||||
ModInc = case proplists:get_bool(include_modules_configs, Opts) of
|
||||
true ->
|
||||
filelib:wildcard(ext_mod:modules_dir() ++ "/*/conf/*.{yml,yaml}");
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
BinTerms = BinTerms1 ++ [{include_config_file, list_to_binary(V)} || V <- ModInc],
|
||||
case proplists:get_bool(include_files, Opts) of
|
||||
true ->
|
||||
include_config_files(BinTerms);
|
||||
@@ -374,7 +384,26 @@ include_config_files(Terms) ->
|
||||
fun({File, Opts}) ->
|
||||
include_config_file(File, Opts)
|
||||
end, lists:flatten(FileOpts)),
|
||||
Terms1 ++ Terms2.
|
||||
|
||||
SpecialTerms = dict:from_list([{hosts, []}, {listen, []}, {modules, []}]),
|
||||
PartDict = dict:store(rest, [], SpecialTerms),
|
||||
Partition = fun(L) ->
|
||||
lists:foldr(fun({Name, Val} = Pair, Dict) ->
|
||||
case dict:find(Name, SpecialTerms) of
|
||||
{ok, _} ->
|
||||
dict:append_list(Name, Val, Dict);
|
||||
_ ->
|
||||
dict:append(rest, Pair, Dict)
|
||||
end;
|
||||
(Tuple, Dict2) ->
|
||||
dict:append(rest, Tuple, Dict2)
|
||||
end, PartDict, L)
|
||||
end,
|
||||
|
||||
Merged = dict:merge(fun(_Name, V1, V2) -> V1 ++ V2 end,
|
||||
Partition(Terms1), Partition(Terms2)),
|
||||
Rest = dict:fetch(rest, Merged),
|
||||
dict:to_list(dict:erase(rest, Merged)) ++ Rest.
|
||||
|
||||
transform_include_option({include_config_file, File}) when is_list(File) ->
|
||||
case is_string(File) of
|
||||
@@ -452,11 +481,11 @@ split_terms_macros(Terms) ->
|
||||
lists:foldl(
|
||||
fun(Term, {TOs, Ms}) ->
|
||||
case Term of
|
||||
{define_macro, Key, Value} ->
|
||||
{define_macro, Key, Value} ->
|
||||
case is_correct_macro({Key, Value}) of
|
||||
true ->
|
||||
true ->
|
||||
{TOs, Ms++[{Key, Value}]};
|
||||
false ->
|
||||
false ->
|
||||
exit({macro_not_properly_defined, Term})
|
||||
end;
|
||||
{define_macro, KeyVals} ->
|
||||
@@ -686,6 +715,45 @@ get_option(Opt, F, Default) ->
|
||||
end
|
||||
end.
|
||||
|
||||
get_modules_with_options() ->
|
||||
{ok, Mods} = application:get_key(ejabberd, modules),
|
||||
lists:foldl(
|
||||
fun(Mod, D) ->
|
||||
case catch Mod:opt_type('') of
|
||||
Opts when is_list(Opts) ->
|
||||
lists:foldl(
|
||||
fun(Opt, Acc) ->
|
||||
dict:append(Opt, Mod, Acc)
|
||||
end, D, Opts);
|
||||
{'EXIT', {undef, _}} ->
|
||||
D
|
||||
end
|
||||
end, dict:new(), [?MODULE|Mods]).
|
||||
|
||||
validate_opts(#state{opts = Opts} = State) ->
|
||||
ModOpts = get_modules_with_options(),
|
||||
NewOpts = lists:filter(
|
||||
fun(#local_config{key = {Opt, _Host}, value = Val}) ->
|
||||
case dict:find(Opt, ModOpts) of
|
||||
{ok, [Mod|_]} ->
|
||||
VFun = Mod:opt_type(Opt),
|
||||
case catch VFun(Val) of
|
||||
{'EXIT', _} ->
|
||||
?ERROR_MSG("ignoring option '~s' with "
|
||||
"invalid value: ~p",
|
||||
[Opt, Val]),
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
_ ->
|
||||
?ERROR_MSG("unknown option '~s' will be likely"
|
||||
" ignored", [Opt]),
|
||||
true
|
||||
end
|
||||
end, Opts),
|
||||
State#state{opts = NewOpts}.
|
||||
|
||||
-spec get_vh_by_auth_method(atom()) -> [binary()].
|
||||
|
||||
%% Return the list of hosts handled by a given module
|
||||
@@ -745,14 +813,24 @@ replace_module(Module) ->
|
||||
false -> Module
|
||||
end.
|
||||
|
||||
replace_modules(Modules) -> lists:map( fun({Module, Opts}) -> case
|
||||
replace_module(Module) of {NewModule, DBType} ->
|
||||
emit_deprecation_warning(Module, NewModule, DBType), NewOpts =
|
||||
[{db_type, DBType} | lists:keydelete(db_type, 1, Opts)],
|
||||
{NewModule, transform_module_options(Module, NewOpts)}; NewModule
|
||||
-> if Module /= NewModule -> emit_deprecation_warning(Module,
|
||||
NewModule); true -> ok end, {NewModule,
|
||||
transform_module_options(Module, Opts)} end end, Modules).
|
||||
replace_modules(Modules) ->
|
||||
lists:map(
|
||||
fun({Module, Opts}) ->
|
||||
case replace_module(Module) of
|
||||
{NewModule, DBType} ->
|
||||
emit_deprecation_warning(Module, NewModule, DBType),
|
||||
NewOpts = [{db_type, DBType} |
|
||||
lists:keydelete(db_type, 1, Opts)],
|
||||
{NewModule, transform_module_options(Module, NewOpts)};
|
||||
NewModule ->
|
||||
if Module /= NewModule ->
|
||||
emit_deprecation_warning(Module, NewModule);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{NewModule, transform_module_options(Module, Opts)}
|
||||
end
|
||||
end, Modules).
|
||||
|
||||
%% Elixir module naming
|
||||
%% ====================
|
||||
@@ -1062,3 +1140,31 @@ emit_deprecation_warning(Module, NewModule) ->
|
||||
?WARNING_MSG("Module ~s is deprecated, use ~s instead",
|
||||
[Module, NewModule])
|
||||
end.
|
||||
|
||||
opt_type(hosts) ->
|
||||
fun(L) when is_list(L) ->
|
||||
lists:map(
|
||||
fun(H) ->
|
||||
iolist_to_binary(H)
|
||||
end, L)
|
||||
end;
|
||||
opt_type(language) ->
|
||||
fun iolist_to_binary/1;
|
||||
opt_type(_) ->
|
||||
[hosts, language].
|
||||
|
||||
-spec may_hide_data(string()) -> string();
|
||||
(binary()) -> binary().
|
||||
|
||||
may_hide_data(Data) ->
|
||||
case ejabberd_config:get_option(
|
||||
hide_sensitive_log_data,
|
||||
fun(false) -> false;
|
||||
(true) -> true
|
||||
end,
|
||||
false) of
|
||||
false ->
|
||||
Data;
|
||||
true ->
|
||||
"hidden_by_ejabberd"
|
||||
end.
|
||||
|
||||
@@ -44,14 +44,13 @@
|
||||
|
||||
|
||||
-module(ejabberd_ctl).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0,
|
||||
init/0,
|
||||
process/1,
|
||||
process2/2,
|
||||
register_commands/3,
|
||||
unregister_commands/3]).
|
||||
-export([start/0, init/0, process/1, process2/2,
|
||||
register_commands/3, unregister_commands/3,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd_ctl.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
@@ -353,6 +352,15 @@ format_result(String, {_Name, string}) when is_list(String) ->
|
||||
format_result(Binary, {_Name, string}) when is_binary(Binary) ->
|
||||
io_lib:format("~s", [binary_to_list(Binary)]);
|
||||
|
||||
format_result(Atom, {_Name, string}) when is_atom(Atom) ->
|
||||
io_lib:format("~s", [atom_to_list(Atom)]);
|
||||
|
||||
format_result(Integer, {_Name, string}) when is_integer(Integer) ->
|
||||
io_lib:format("~s", [integer_to_list(Integer)]);
|
||||
|
||||
format_result(Other, {_Name, string}) ->
|
||||
io_lib:format("~p", [Other]);
|
||||
|
||||
format_result(Code, {_Name, rescode}) ->
|
||||
make_status(Code);
|
||||
|
||||
@@ -792,3 +800,7 @@ print(Format, Args) ->
|
||||
%%format_usage_xmlrpc(ArgsDef, ResultDef) ->
|
||||
%% ["aaaa bbb ccc"].
|
||||
|
||||
|
||||
opt_type(ejabberdctl_access_commands) ->
|
||||
fun (V) when is_list(V) -> V end;
|
||||
opt_type(_) -> [ejabberdctl_access_commands].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_http).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% External exports
|
||||
@@ -32,8 +34,7 @@
|
||||
socket_type/0, receive_headers/1, url_encode/1,
|
||||
transform_listen_option/2]).
|
||||
|
||||
%% Callbacks
|
||||
-export([init/2]).
|
||||
-export([init/2, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -95,6 +96,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
||||
({ciphers, _}) -> true;
|
||||
({dhfile, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
Opts),
|
||||
@@ -870,3 +872,7 @@ transform_listen_option({request_handlers, Hs}, Opts) ->
|
||||
[{request_handlers, Hs1} | Opts];
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(trusted_proxies) ->
|
||||
fun (TPs) -> [iolist_to_binary(TP) || TP <- TPs] end;
|
||||
opt_type(_) -> [trusted_proxies].
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
-module(ejabberd_http_bind).
|
||||
|
||||
-protocol({xep, 124, '1.11'}).
|
||||
-protocol({xep, 206, '1.4'}).
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
|
||||
@@ -24,16 +24,17 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
-module(ejabberd_http_ws).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('ecestari@process-one.net').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
% External exports
|
||||
-export([start/1, start_link/1, init/1, handle_event/3,
|
||||
handle_sync_event/4, code_change/4, handle_info/3,
|
||||
terminate/3, send_xml/2, setopts/2, sockname/1, peername/1,
|
||||
controlling_process/2, become_controller/2, close/1,
|
||||
socket_handoff/6]).
|
||||
terminate/3, send_xml/2, setopts/2, sockname/1,
|
||||
peername/1, controlling_process/2, become_controller/2,
|
||||
close/1, socket_handoff/6, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -353,3 +354,10 @@ parsed_items(List) ->
|
||||
after 0 ->
|
||||
lists:reverse(List)
|
||||
end.
|
||||
|
||||
opt_type(websocket_ping_interval) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(websocket_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(_) ->
|
||||
[websocket_ping_interval, websocket_timeout].
|
||||
|
||||
@@ -24,20 +24,15 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_listener).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start_link/0, init/1, start/3,
|
||||
init/3,
|
||||
start_listeners/0,
|
||||
start_listener/3,
|
||||
stop_listeners/0,
|
||||
stop_listener/2,
|
||||
parse_listener_portip/2,
|
||||
add_listener/3,
|
||||
delete_listener/2,
|
||||
transform_options/1,
|
||||
validate_cfg/1
|
||||
]).
|
||||
-export([start_link/0, init/1, start/3, init/3,
|
||||
start_listeners/0, start_listener/3, stop_listeners/0,
|
||||
stop_listener/2, parse_listener_portip/2,
|
||||
add_listener/3, delete_listener/2, transform_options/1,
|
||||
validate_cfg/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -302,7 +297,7 @@ accept(ListenSocket, Module, Opts) ->
|
||||
case {inet:sockname(Socket), inet:peername(Socket)} of
|
||||
{{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
|
||||
?INFO_MSG("(~w) Accepted connection ~s:~p -> ~s:~p",
|
||||
[Socket, inet_parse:ntoa(PAddr), PPort,
|
||||
[Socket, ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), PPort,
|
||||
inet_parse:ntoa(Addr), Port]);
|
||||
_ ->
|
||||
ok
|
||||
@@ -660,3 +655,6 @@ prepare_mod(sip) ->
|
||||
esip_socket;
|
||||
prepare_mod(Mod) when is_atom(Mod) ->
|
||||
Mod.
|
||||
|
||||
opt_type(listen) -> fun validate_cfg/1;
|
||||
opt_type(_) -> [listen].
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_logger).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start/0, reopen_log/0, get/0, set/1, get_log_path/0]).
|
||||
-export([start/0, reopen_log/0, get/0, set/1, get_log_path/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -61,6 +63,17 @@ get_log_path() ->
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(log_rotate_date) ->
|
||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
||||
opt_type(log_rotate_size) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(log_rotate_count) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(log_rate_limit) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(_) ->
|
||||
[log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit].
|
||||
|
||||
-ifdef(LAGER).
|
||||
|
||||
get_integer_env(Name, Default) ->
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_node_groups).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -35,9 +37,8 @@
|
||||
get_members/1,
|
||||
get_closest_node/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
||||
|
||||
-define(PG2, pg2).
|
||||
|
||||
@@ -163,3 +164,10 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
opt_type(node_type) ->
|
||||
fun (frontend) -> frontend;
|
||||
(backend) -> backend;
|
||||
(generic) -> generic
|
||||
end;
|
||||
opt_type(_) -> [node_type].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_odbc).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-define(GEN_FSM, p1_fsm).
|
||||
@@ -51,9 +53,9 @@
|
||||
handle_info/3, terminate/3, print_state/1,
|
||||
code_change/4]).
|
||||
|
||||
%% gen_fsm states
|
||||
-export([connecting/2, connecting/3,
|
||||
session_established/2, session_established/3]).
|
||||
session_established/2, session_established/3,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -193,7 +195,8 @@ to_bool(_) -> false.
|
||||
|
||||
encode_term(Term) ->
|
||||
escape(list_to_binary(
|
||||
erl_prettypr:format(erl_syntax:abstract(Term)))).
|
||||
erl_prettypr:format(erl_syntax:abstract(Term),
|
||||
[{paper, 65535}, {ribbon, 65535}]))).
|
||||
|
||||
decode_term(Bin) ->
|
||||
Str = binary_to_list(<<Bin/binary, ".">>),
|
||||
@@ -509,7 +512,8 @@ abort_on_driver_error(Reply, From) ->
|
||||
%% Open an ODBC database connection
|
||||
odbc_connect(SQLServer) ->
|
||||
ejabberd:start_app(odbc),
|
||||
odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off}]).
|
||||
odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off},
|
||||
{binary_strings, on}]).
|
||||
|
||||
%% == Native SQLite code
|
||||
|
||||
@@ -634,7 +638,7 @@ mysql_item_to_odbc(Columns, Recs) ->
|
||||
{selected, [element(2, Column) || Column <- Columns], Recs}.
|
||||
|
||||
to_odbc({selected, Columns, Recs}) ->
|
||||
{selected, Columns, [tuple_to_list(Rec) || Rec <- Recs]};
|
||||
{selected, [list_to_binary(Column) || Column <- Columns], [tuple_to_list(Rec) || Rec <- Recs]};
|
||||
to_odbc(Res) ->
|
||||
Res.
|
||||
|
||||
@@ -690,3 +694,24 @@ fsm_limit_opts() ->
|
||||
N when is_integer(N) -> [{max_queue, N}];
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
opt_type(max_fsm_queue) ->
|
||||
fun (N) when is_integer(N), N > 0 -> N end;
|
||||
opt_type(odbc_database) -> fun iolist_to_binary/1;
|
||||
opt_type(odbc_keepalive_interval) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(odbc_password) -> fun iolist_to_binary/1;
|
||||
opt_type(odbc_port) ->
|
||||
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
||||
opt_type(odbc_server) -> fun iolist_to_binary/1;
|
||||
opt_type(odbc_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
(sqlite) -> sqlite;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
opt_type(odbc_username) -> fun iolist_to_binary/1;
|
||||
opt_type(_) ->
|
||||
[max_fsm_queue, odbc_database, odbc_keepalive_interval,
|
||||
odbc_password, odbc_port, odbc_server, odbc_type,
|
||||
odbc_username].
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
|
||||
-module(ejabberd_odbc_sup).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% API
|
||||
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
||||
get_pids/1, get_random_pid/1, transform_options/1]).
|
||||
get_pids/1, get_random_pid/1, transform_options/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -205,3 +207,16 @@ read_lines(Fd, File, Acc) ->
|
||||
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
|
||||
[]
|
||||
end.
|
||||
|
||||
opt_type(odbc_pool_size) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(odbc_start_interval) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(odbc_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
(sqlite) -> sqlite;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[odbc_pool_size, odbc_start_interval, odbc_type].
|
||||
|
||||
@@ -38,8 +38,12 @@
|
||||
|
||||
-module(ejabberd_piefxis).
|
||||
|
||||
%% API
|
||||
-export([import_file/1, export_server/1, export_host/2]).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-protocol({xep, 227, '1.0'}).
|
||||
|
||||
-export([import_file/1, export_server/1, export_host/2,
|
||||
opt_type/1]).
|
||||
|
||||
-define(CHUNK_SIZE, 1024*20). %20k
|
||||
|
||||
@@ -735,3 +739,6 @@ print(Fd, String) ->
|
||||
%%%==================================
|
||||
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%%,%%%= foldmethod=marker:
|
||||
file:write(Fd, String).
|
||||
|
||||
opt_type(auth_password_format) -> fun (X) -> X end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
|
||||
-module(ejabberd_rdbms).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0]).
|
||||
-export([start/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -83,3 +85,11 @@ needs_odbc(Host) ->
|
||||
odbc -> {true, odbc};
|
||||
undefined -> false
|
||||
end.
|
||||
|
||||
opt_type(odbc_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
(sqlite) -> sqlite;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
opt_type(_) -> [odbc_type].
|
||||
|
||||
@@ -24,17 +24,13 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_riak_sup).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% API
|
||||
-export([start/0,
|
||||
start_link/0,
|
||||
init/1,
|
||||
get_pids/0,
|
||||
transform_options/1,
|
||||
get_random_pid/0,
|
||||
get_random_pid/1
|
||||
]).
|
||||
-export([start/0, start_link/0, init/1, get_pids/0,
|
||||
transform_options/1, get_random_pid/0, get_random_pid/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -158,3 +154,14 @@ transform_options({riak_server, {S, P}}, Opts) ->
|
||||
[{riak_server, S}, {riak_port, P}|Opts];
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(modules) -> fun (L) when is_list(L) -> L end;
|
||||
opt_type(riak_pool_size) ->
|
||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
||||
opt_type(riak_port) -> fun (_) -> true end;
|
||||
opt_type(riak_server) -> fun (_) -> true end;
|
||||
opt_type(riak_start_interval) ->
|
||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
||||
opt_type(_) ->
|
||||
[modules, riak_pool_size, riak_port, riak_server,
|
||||
riak_start_interval].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_router).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -43,9 +45,8 @@
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -406,3 +407,7 @@ update_tables() ->
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
|
||||
opt_type(domain_balancing_component_number) ->
|
||||
fun (N) when is_integer(N), N > 1 -> N end;
|
||||
opt_type(_) -> [domain_balancing_component_number].
|
||||
|
||||
@@ -202,14 +202,18 @@ do_route(From, Domain, Destinations, Packet) ->
|
||||
%% If no multicast service is available in this server, send manually
|
||||
[] -> do_route_normal(From, Destinations, Packet);
|
||||
|
||||
%% If available, send the packet using multicast service
|
||||
[R] ->
|
||||
case R#route_multicast.pid of
|
||||
Pid when is_pid(Pid) ->
|
||||
Pid ! {route_trusted, From, Destinations, Packet};
|
||||
_ -> do_route_normal(From, Destinations, Packet)
|
||||
end
|
||||
%% If some is available, send the packet using multicast service
|
||||
Rs when is_list(Rs) ->
|
||||
Pid = pick_multicast_pid(Rs),
|
||||
Pid ! {route_trusted, From, Destinations, Packet}
|
||||
end.
|
||||
|
||||
pick_multicast_pid(Rs) ->
|
||||
List = case [R || R <- Rs, node(R#route_multicast.pid) == node()] of
|
||||
[] -> Rs;
|
||||
RLocals -> RLocals
|
||||
end,
|
||||
(hd(List))#route_multicast.pid.
|
||||
|
||||
do_route_normal(From, Destinations, Packet) ->
|
||||
[ejabberd_router:route(From, To, Packet) || To <- Destinations].
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
-module(ejabberd_s2s).
|
||||
|
||||
-protocol({xep, 220, '1.1'}).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -44,8 +48,8 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
|
||||
%% ejabberd API
|
||||
-export([get_info_s2s_connections/1, transform_options/1]).
|
||||
-export([get_info_s2s_connections/1,
|
||||
transform_options/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -771,3 +775,11 @@ match_labels([DL | DLabels], [PL | PLabels]) ->
|
||||
end;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
opt_type(route_subdomains) ->
|
||||
fun (s2s) -> s2s;
|
||||
(local) -> local
|
||||
end;
|
||||
opt_type(s2s_access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
opt_type(_) -> [route_subdomains, s2s_access].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_s2s_in).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(p1_fsm).
|
||||
@@ -32,11 +34,10 @@
|
||||
%% External exports
|
||||
-export([start/2, start_link/2, socket_type/0]).
|
||||
|
||||
%% 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, code_change/4,
|
||||
handle_info/3, print_state/1, terminate/3]).
|
||||
handle_info/3, print_state/1, terminate/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -184,9 +185,14 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
undefined -> TLSOpts2;
|
||||
ProtocolOpts -> [{protocol_options, ProtocolOpts} | TLSOpts2]
|
||||
end,
|
||||
TLSOpts4 = case ejabberd_config:get_option(
|
||||
s2s_dhfile, fun iolist_to_binary/1) of
|
||||
undefined -> TLSOpts3;
|
||||
DHFile -> [{dhfile, DHFile} | TLSOpts3]
|
||||
end,
|
||||
TLSOpts = case proplists:get_bool(tls_compression, Opts) of
|
||||
false -> [compression_none | TLSOpts3];
|
||||
true -> TLSOpts3
|
||||
false -> [compression_none | TLSOpts4];
|
||||
true -> TLSOpts4
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, wait_for_stream,
|
||||
@@ -710,3 +716,32 @@ fsm_limit_opts(Opts) ->
|
||||
N -> [{max_queue, N}]
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(max_fsm_queue) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(s2s_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_dhfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_protocol_options) ->
|
||||
fun (Options) ->
|
||||
[_ | O] = lists:foldl(fun (X, Acc) -> X ++ Acc end, [],
|
||||
[["|" | binary_to_list(Opt)]
|
||||
|| Opt <- Options, is_binary(Opt)]),
|
||||
iolist_to_binary(O)
|
||||
end;
|
||||
opt_type(s2s_tls_compression) ->
|
||||
fun (true) -> true;
|
||||
(false) -> false
|
||||
end;
|
||||
opt_type(s2s_use_starttls) ->
|
||||
fun (false) -> false;
|
||||
(true) -> true;
|
||||
(optional) -> optional;
|
||||
(required) -> required;
|
||||
(required_trusted) -> required_trusted
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[domain_certfile, max_fsm_queue, s2s_certfile,
|
||||
s2s_ciphers, s2s_dhfile, s2s_protocol_options,
|
||||
s2s_tls_compression, s2s_use_starttls].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_s2s_out).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(p1_fsm).
|
||||
@@ -37,26 +39,14 @@
|
||||
stop_connection/1,
|
||||
transform_options/1]).
|
||||
|
||||
%% p1_fsm callbacks (same as gen_fsm)
|
||||
-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,
|
||||
relay_to_bridge/2,
|
||||
reopen_socket/2,
|
||||
wait_before_retry/2,
|
||||
stream_established/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
print_state/1,
|
||||
code_change/4,
|
||||
test_get_addr_port/1,
|
||||
get_addr_port/1]).
|
||||
-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,
|
||||
relay_to_bridge/2, reopen_socket/2, wait_before_retry/2,
|
||||
stream_established/2, handle_event/3,
|
||||
handle_sync_event/4, handle_info/3, terminate/3,
|
||||
print_state/1, code_change/4, test_get_addr_port/1,
|
||||
get_addr_port/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -207,13 +197,18 @@ init([From, Server, Type]) ->
|
||||
undefined -> TLSOpts2;
|
||||
ProtocolOpts -> [{protocol_options, ProtocolOpts} | TLSOpts2]
|
||||
end,
|
||||
TLSOpts4 = case ejabberd_config:get_option(
|
||||
s2s_dhfile, fun iolist_to_binary/1) of
|
||||
undefined -> TLSOpts3;
|
||||
DHFile -> [{dhfile, DHFile} | TLSOpts3]
|
||||
end,
|
||||
TLSOpts = case ejabberd_config:get_option(
|
||||
{s2s_tls_compression, From},
|
||||
fun(true) -> true;
|
||||
(false) -> false
|
||||
end, true) of
|
||||
false -> [compression_none | TLSOpts3];
|
||||
true -> TLSOpts3
|
||||
false -> [compression_none | TLSOpts4];
|
||||
true -> TLSOpts4
|
||||
end,
|
||||
{New, Verify} = case Type of
|
||||
{new, Key} -> {Key, false};
|
||||
@@ -1365,3 +1360,55 @@ fsm_limit_opts() ->
|
||||
undefined -> [];
|
||||
N -> [{max_queue, N}]
|
||||
end.
|
||||
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(max_fsm_queue) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(outgoing_s2s_families) ->
|
||||
fun (Families) ->
|
||||
true = lists:all(fun (ipv4) -> true;
|
||||
(ipv6) -> true
|
||||
end,
|
||||
Families),
|
||||
Families
|
||||
end;
|
||||
opt_type(outgoing_s2s_port) ->
|
||||
fun (I) when is_integer(I), I > 0, I =< 65536 -> I end;
|
||||
opt_type(outgoing_s2s_timeout) ->
|
||||
fun (TimeOut) when is_integer(TimeOut), TimeOut > 0 ->
|
||||
TimeOut;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
opt_type(s2s_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_dhfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_dns_retries) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(s2s_dns_timeout) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(s2s_max_retry_delay) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(s2s_protocol_options) ->
|
||||
fun (Options) ->
|
||||
[_ | O] = lists:foldl(fun (X, Acc) -> X ++ Acc end, [],
|
||||
[["|" | binary_to_list(Opt)]
|
||||
|| Opt <- Options, is_binary(Opt)]),
|
||||
iolist_to_binary(O)
|
||||
end;
|
||||
opt_type(s2s_tls_compression) ->
|
||||
fun (true) -> true;
|
||||
(false) -> false
|
||||
end;
|
||||
opt_type(s2s_use_starttls) ->
|
||||
fun (true) -> true;
|
||||
(false) -> false;
|
||||
(optional) -> optional;
|
||||
(required) -> required;
|
||||
(required_trusted) -> required_trusted
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[domain_certfile, max_fsm_queue, outgoing_s2s_families,
|
||||
outgoing_s2s_port, outgoing_s2s_timeout, s2s_certfile,
|
||||
s2s_ciphers, s2s_dhfile, s2s_dns_retries, s2s_dns_timeout,
|
||||
s2s_max_retry_delay, s2s_protocol_options,
|
||||
s2s_tls_compression, s2s_use_starttls].
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
|
||||
-module(ejabberd_service).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 114, '1.6'}).
|
||||
|
||||
-define(GEN_FSM, p1_fsm).
|
||||
|
||||
-behaviour(?GEN_FSM).
|
||||
@@ -35,11 +39,10 @@
|
||||
-export([start/2, start_link/2, send_text/2,
|
||||
send_element/2, socket_type/0, transform_listen_option/2]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, wait_for_stream/2,
|
||||
wait_for_handshake/2, stream_established/2,
|
||||
handle_event/3, handle_sync_event/4, code_change/4,
|
||||
handle_info/3, terminate/3, print_state/1]).
|
||||
handle_info/3, terminate/3, print_state/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -402,3 +405,7 @@ fsm_limit_opts(Opts) ->
|
||||
N -> [{max_queue, N}]
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(max_fsm_queue) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(_) -> [max_fsm_queue].
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_sm).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -64,9 +66,8 @@
|
||||
is_existing_resource/3
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -539,6 +540,8 @@ do_route(From, To, #xmlel{} = Packet) ->
|
||||
<<"message">> ->
|
||||
case xml:get_attr_s(<<"type">>, Attrs) of
|
||||
<<"chat">> -> route_message(From, To, Packet, chat);
|
||||
<<"normal">> -> route_message(From, To, Packet, normal);
|
||||
<<"">> -> route_message(From, To, Packet, normal);
|
||||
<<"error">> -> ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
@@ -803,3 +806,11 @@ kick_user(User, Server) ->
|
||||
PID ! kick
|
||||
end, Resources),
|
||||
length(Resources).
|
||||
|
||||
opt_type(sm_db_type) ->
|
||||
fun (mnesia) -> mnesia;
|
||||
(internal) -> mnesia;
|
||||
(odbc) -> odbc;
|
||||
(redis) -> redis
|
||||
end;
|
||||
opt_type(_) -> [sm_db_type].
|
||||
|
||||
@@ -8,16 +8,13 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_sm_redis).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-behaviour(ejabberd_sm).
|
||||
|
||||
%% API
|
||||
-export([init/0,
|
||||
set_session/1,
|
||||
delete_session/4,
|
||||
get_sessions/0,
|
||||
get_sessions/1,
|
||||
get_sessions/2,
|
||||
get_sessions/3]).
|
||||
-export([init/0, set_session/1, delete_session/4,
|
||||
get_sessions/0, get_sessions/1, get_sessions/2,
|
||||
get_sessions/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
@@ -207,3 +204,17 @@ clean_table() ->
|
||||
"server ~s: ~p", [LServer, Err])
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
|
||||
opt_type(redis_connect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(redis_db) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(redis_password) -> fun iolist_to_list/1;
|
||||
opt_type(redis_port) ->
|
||||
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
||||
opt_type(redis_reconnect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(redis_server) -> fun iolist_to_list/1;
|
||||
opt_type(_) ->
|
||||
[redis_connect_timeout, redis_db, redis_password,
|
||||
redis_port, redis_reconnect_timeout, redis_server].
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_stun).
|
||||
|
||||
%% API
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2, socket_type/0]).
|
||||
-protocol({rfc, 5766}).
|
||||
-protocol({xep, 176, '1.0'}).
|
||||
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
|
||||
socket_type/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
-module(ejabberd_system_monitor).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@@ -33,9 +35,8 @@
|
||||
-export([start_link/0, process_command/3,
|
||||
process_remote_command/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -331,3 +332,12 @@ process_remote_command([setlh, NewValue]) ->
|
||||
io_lib:format("Result of set large heap: ~p --> ~p",
|
||||
[OldLH, NewLH]);
|
||||
process_remote_command(_) -> throw(unknown_command).
|
||||
|
||||
opt_type(watchdog_admins) ->
|
||||
fun (JIDs) ->
|
||||
[jlib:jid_tolower(jlib:string_to_jid(iolist_to_binary(S)))
|
||||
|| S <- JIDs]
|
||||
end;
|
||||
opt_type(watchdog_large_heap) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(_) -> [watchdog_admins, watchdog_large_heap].
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
|
||||
-module(ejabberd_web_admin).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% External exports
|
||||
-export([process/2, list_users/4,
|
||||
list_users_in_diapason/4, pretty_print_xml/1,
|
||||
term_to_id/1]).
|
||||
term_to_id/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -166,15 +167,15 @@ process([<<"doc">>, LocalFile], _Request) ->
|
||||
?DEBUG("Delivering content.", []),
|
||||
{200, [{<<"Server">>, <<"ejabberd">>}], FileContents};
|
||||
{error, Error} ->
|
||||
?DEBUG("Delivering error: ~p", [Error]),
|
||||
Help = <<" ", FileName/binary,
|
||||
" - Try to specify the path to ejabberd "
|
||||
"documentation with the environment variable "
|
||||
"EJABBERD_DOC_PATH. Check the ejabberd "
|
||||
"Guide for more information.">>,
|
||||
?INFO_MSG("Problem '~p' accessing the local Guide file ~s", [Error, Help]),
|
||||
case Error of
|
||||
eacces -> {403, [], <<"Forbidden", Help/binary>>};
|
||||
enoent -> {404, [], <<"Not found", Help/binary>>};
|
||||
enoent -> {307, [{<<"Location">>, <<"http://docs.ejabberd.im/admin/guide/configuration/">>}], <<"Not found", Help/binary>>};
|
||||
_Else ->
|
||||
{404, [], <<(iolist_to_binary(atom_to_list(Error)))/binary, Help/binary>>}
|
||||
end
|
||||
@@ -202,7 +203,7 @@ process([<<"server">>, SHost | RPath] = Path,
|
||||
{unauthorized, Error} ->
|
||||
{BadUser, _BadPass} = Auth,
|
||||
{IPT, _Port} = Request#request.ip,
|
||||
IPS = jlib:ip_to_list(IPT),
|
||||
IPS = ejabberd_config:may_hide_data(jlib:ip_to_list(IPT)),
|
||||
?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
|
||||
[BadUser, IPS, Error]),
|
||||
{401,
|
||||
@@ -234,7 +235,7 @@ process(RPath,
|
||||
{unauthorized, Error} ->
|
||||
{BadUser, _BadPass} = Auth,
|
||||
{IPT, _Port} = Request#request.ip,
|
||||
IPS = jlib:ip_to_list(IPT),
|
||||
IPS = ejabberd_config:may_hide_data(jlib:ip_to_list(IPT)),
|
||||
?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
|
||||
[BadUser, IPS, Error]),
|
||||
{401,
|
||||
@@ -295,7 +296,7 @@ make_xhtml(Els, Host, Node, Lang, JID) ->
|
||||
#xmlel{name = <<"html">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>},
|
||||
{<<"xml:lang">>, Lang}, {<<"lang">>, Lang}],
|
||||
{<<"xml:lang">>, Lang}, {<<"lang">>, Lang}]++direction(Lang),
|
||||
children =
|
||||
[#xmlel{name = <<"head">>, attrs = [],
|
||||
children =
|
||||
@@ -339,8 +340,15 @@ make_xhtml(Els, Host, Node, Lang, JID) ->
|
||||
[{xmlcdata, <<"">>}])]),
|
||||
?XAE(<<"div">>, [{<<"id">>, <<"copyrightouter">>}],
|
||||
[?XAE(<<"div">>, [{<<"id">>, <<"copyright">>}],
|
||||
[?XC(<<"p">>,
|
||||
<<"ejabberd (c) 2002-2015 ProcessOne">>)])])])]}}.
|
||||
[?XE(<<"p">>,
|
||||
[?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>),
|
||||
?C(<<" (c) 2002-2015 ">>),
|
||||
?AC(<<"https://www.process-one.net/">>, <<"ProcessOne">>)]
|
||||
)])])])]}}.
|
||||
|
||||
direction(ltr) -> [{<<"dir">>, <<"ltr">>}];
|
||||
direction(<<"he">>) -> [{<<"dir">>, <<"rtl">>}];
|
||||
direction(_) -> [].
|
||||
|
||||
get_base_path(global, cluster) -> <<"/admin/">>;
|
||||
get_base_path(Host, cluster) ->
|
||||
@@ -509,7 +517,7 @@ css(Host) ->
|
||||
"0px;\n}\n\nh3 {\n color: #000044;\n "
|
||||
" font-family: Verdana, Arial, Helvetica, "
|
||||
"sans-serif; \n font-size: 10pt;\n "
|
||||
"font-weight: bold;\n text-align: left;\n "
|
||||
"font-weight: bold;\n "
|
||||
" padding-top: 20px;\n padding-bottom: "
|
||||
"2px;\n margin-top: 0px;\n margin-bottom: "
|
||||
"0px;\n}\n\n#content a:link {\n color: "
|
||||
@@ -589,7 +597,7 @@ logo_fill() ->
|
||||
process_admin(global,
|
||||
#request{path = [], auth = {_, _, AJID},
|
||||
lang = Lang}) ->
|
||||
make_xhtml((?H1GL((?T(<<"Administration">>)), <<"toc">>,
|
||||
make_xhtml((?H1GL((?T(<<"Administration">>)), <<"">>,
|
||||
<<"Contents">>))
|
||||
++
|
||||
[?XE(<<"ul">>,
|
||||
@@ -658,7 +666,7 @@ process_admin(Host,
|
||||
[{{acl, '$1', '$2'}}]}])),
|
||||
{NumLines, ACLsP} = term_to_paragraph(ACLs, 80),
|
||||
make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
|
||||
<<"ACLDefinition">>, <<"ACL Definition">>))
|
||||
<<"acl-definition">>, <<"ACL Definition">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(<<"Submitted">>)];
|
||||
@@ -667,7 +675,7 @@ process_admin(Host,
|
||||
end
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
|
||||
[?TEXTAREA(<<"acls">>,
|
||||
(iolist_to_binary(integer_to_list(lists:max([16,
|
||||
NumLines])))),
|
||||
@@ -694,7 +702,7 @@ process_admin(Host,
|
||||
[{{acl, {'$1', Host}, '$2'}, [],
|
||||
[{{acl, '$1', '$2'}}]}])),
|
||||
make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
|
||||
<<"ACLDefinition">>, <<"ACL Definition">>))
|
||||
<<"acl-definition">>, <<"ACL Definition">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(<<"Submitted">>)];
|
||||
@@ -702,9 +710,9 @@ process_admin(Host,
|
||||
nothing -> []
|
||||
end
|
||||
++
|
||||
[?XE(<<"p">>, [?ACT(<<"../acls-raw/">>, <<"Raw">>)])] ++
|
||||
[?XAE(<<"p">>, direction(ltr), [?ACT(<<"../acls-raw/">>, <<"Raw">>)])] ++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
|
||||
[acls_to_xhtml(ACLs), ?BR,
|
||||
?INPUTT(<<"submit">>, <<"delete">>,
|
||||
<<"Delete Selected">>),
|
||||
@@ -760,7 +768,7 @@ process_admin(Host,
|
||||
[{{access, '$1', '$2'}}]}]),
|
||||
{NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80),
|
||||
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
|
||||
<<"AccessRights">>, <<"Access Rights">>))
|
||||
<<"access-rights">>, <<"Access Rights">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(<<"Submitted">>)];
|
||||
@@ -769,7 +777,7 @@ process_admin(Host,
|
||||
end
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
|
||||
[?TEXTAREA(<<"access">>,
|
||||
(iolist_to_binary(integer_to_list(lists:max([16,
|
||||
NumLines])))),
|
||||
@@ -793,7 +801,7 @@ process_admin(Host,
|
||||
[{{access, {'$1', Host}, '$2'}, [],
|
||||
[{{access, '$1', '$2'}}]}]),
|
||||
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
|
||||
<<"AccessRights">>, <<"Access Rights">>))
|
||||
<<"access-rights">>, <<"Access Rights">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(<<"Submitted">>)];
|
||||
@@ -801,10 +809,10 @@ process_admin(Host,
|
||||
nothing -> []
|
||||
end
|
||||
++
|
||||
[?XE(<<"p">>, [?ACT(<<"../access-raw/">>, <<"Raw">>)])]
|
||||
[?XAE(<<"p">>, direction(ltr), [?ACT(<<"../access-raw/">>, <<"Raw">>)])]
|
||||
++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
|
||||
[access_rules_to_xhtml(AccessRules, Lang), ?BR,
|
||||
?INPUTT(<<"submit">>, <<"delete">>,
|
||||
<<"Delete Selected">>)])],
|
||||
@@ -852,7 +860,7 @@ process_admin(global,
|
||||
lang = Lang}) ->
|
||||
Res = list_vhosts(Lang, AJID),
|
||||
make_xhtml((?H1GL((?T(<<"Virtual Hosts">>)),
|
||||
<<"virtualhost">>, <<"Virtual Hosting">>))
|
||||
<<"virtual-hosting">>, <<"Virtual Hosting">>))
|
||||
++ Res,
|
||||
global, Lang, AJID);
|
||||
process_admin(Host,
|
||||
@@ -1549,18 +1557,24 @@ user_info(User, Server, Query, Lang) ->
|
||||
c2s_compressed_tls ->
|
||||
<<"tls+zlib">>;
|
||||
http_bind ->
|
||||
<<"http-bind">>
|
||||
<<"http-bind">>;
|
||||
websocket ->
|
||||
<<"websocket">>;
|
||||
_ ->
|
||||
<<"unknown">>
|
||||
end,
|
||||
<<" (", ConnS/binary,
|
||||
<<ConnS/binary,
|
||||
"://",
|
||||
(jlib:ip_to_list(IP))/binary,
|
||||
":",
|
||||
(jlib:integer_to_binary(Port))/binary,
|
||||
"#",
|
||||
(jlib:atom_to_binary(Node))/binary,
|
||||
")">>
|
||||
(jlib:atom_to_binary(Node))/binary>>
|
||||
end,
|
||||
?LI([?C((<<R/binary, FIP/binary>>))])
|
||||
case direction(Lang) of
|
||||
[{_, <<"rtl">>}] -> ?LI([?C((<<FIP/binary, " - ", R/binary>>))]);
|
||||
_ -> ?LI([?C((<<R/binary, " - ", FIP/binary>>))])
|
||||
end
|
||||
end,
|
||||
lists:sort(Resources))))]
|
||||
end,
|
||||
@@ -1871,9 +1885,7 @@ get_node(global, Node, [<<"backup">>], Query, Lang) ->
|
||||
[?XRES(<<(?T(<<"Error">>))/binary, ": ",
|
||||
(list_to_binary(io_lib:format("~p", [Error])))/binary>>)]
|
||||
end,
|
||||
(?H1GL(list_to_binary(io_lib:format(?T(<<"Backup of ~p">>), [Node])),
|
||||
<<"list-eja-commands">>,
|
||||
<<"List of ejabberd Commands">>))
|
||||
[?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Backup of ~p">>), [Node])))]
|
||||
++
|
||||
ResS ++
|
||||
[?XCT(<<"p">>,
|
||||
@@ -2035,7 +2047,7 @@ get_node(global, Node, [<<"ports">>], Query, Lang) ->
|
||||
[]])),
|
||||
H1String = <<(?T(<<"Listened Ports at ">>))/binary,
|
||||
(iolist_to_binary(atom_to_list(Node)))/binary>>,
|
||||
(?H1GL(H1String, <<"listened">>, <<"Listening Ports">>))
|
||||
(?H1GL(H1String, <<"listening-ports">>, <<"Listening Ports">>))
|
||||
++
|
||||
case Res of
|
||||
ok -> [?XREST(<<"Submitted">>)];
|
||||
@@ -2063,7 +2075,7 @@ get_node(Host, Node, [<<"modules">>], Query, Lang)
|
||||
NewModules = lists:sort(rpc:call(Node, gen_mod,
|
||||
loaded_modules_with_opts, [Host])),
|
||||
H1String = list_to_binary(io_lib:format(?T(<<"Modules at ~p">>), [Node])),
|
||||
(?H1GL(H1String, <<"modoverview">>,
|
||||
(?H1GL(H1String, <<"modules-overview">>,
|
||||
<<"Modules Overview">>))
|
||||
++
|
||||
case Res of
|
||||
@@ -2380,18 +2392,18 @@ node_ports_to_xhtml(Ports, Lang) ->
|
||||
[?INPUTS(<<"text">>,
|
||||
<<"module", SSPort/binary>>,
|
||||
SModule, <<"15">>)]),
|
||||
?XE(<<"td">>,
|
||||
?XAE(<<"td">>, direction(ltr),
|
||||
[?TEXTAREA(<<"opts", SSPort/binary>>,
|
||||
(iolist_to_binary(integer_to_list(NumLines))),
|
||||
<<"35">>, SOptsClean)]),
|
||||
?XE(<<"td">>,
|
||||
[?INPUTT(<<"submit">>,
|
||||
<<"add", SSPort/binary>>,
|
||||
<<"Update">>)]),
|
||||
<<"Restart">>)]),
|
||||
?XE(<<"td">>,
|
||||
[?INPUTT(<<"submit">>,
|
||||
<<"delete", SSPort/binary>>,
|
||||
<<"Delete">>)])])
|
||||
<<"Stop">>)])])
|
||||
end,
|
||||
Ports)
|
||||
++
|
||||
@@ -2406,12 +2418,12 @@ node_ports_to_xhtml(Ports, Lang) ->
|
||||
?XE(<<"td">>,
|
||||
[?INPUTS(<<"text">>, <<"modulenew">>, <<"">>,
|
||||
<<"15">>)]),
|
||||
?XE(<<"td">>,
|
||||
?XAE(<<"td">>, direction(ltr),
|
||||
[?TEXTAREA(<<"optsnew">>, <<"2">>, <<"35">>,
|
||||
<<"[]">>)]),
|
||||
?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
|
||||
[?INPUTT(<<"submit">>, <<"addnew">>,
|
||||
<<"Add New">>)])])]))]).
|
||||
<<"Start">>)])])]))]).
|
||||
|
||||
make_netprot_html(NetProt) ->
|
||||
?XAE(<<"select">>, [{<<"name">>, <<"netprotnew">>}],
|
||||
@@ -2523,7 +2535,7 @@ node_modules_to_xhtml(Modules, Lang) ->
|
||||
40),
|
||||
?XE(<<"tr">>,
|
||||
[?XC(<<"td">>, SModule),
|
||||
?XE(<<"td">>,
|
||||
?XAE(<<"td">>, direction(ltr),
|
||||
[?TEXTAREA(<<"opts", SModule/binary>>,
|
||||
(iolist_to_binary(integer_to_list(NumLines))),
|
||||
<<"40">>, SOpts)]),
|
||||
@@ -2542,7 +2554,7 @@ node_modules_to_xhtml(Modules, Lang) ->
|
||||
[?XE(<<"tr">>,
|
||||
[?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>, <<"modulenew">>, <<"">>)]),
|
||||
?XE(<<"td">>,
|
||||
?XAE(<<"td">>, direction(ltr),
|
||||
[?TEXTAREA(<<"optsnew">>, <<"2">>, <<"40">>,
|
||||
<<"[]">>)]),
|
||||
?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
|
||||
@@ -2877,3 +2889,6 @@ make_menu_item(item, 3, URI, Name, Lang) ->
|
||||
%%%==================================
|
||||
|
||||
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
|
||||
|
||||
opt_type(access) -> fun (V) -> V end;
|
||||
opt_type(_) -> [access].
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
-module(ejabberd_websocket).
|
||||
|
||||
-protocol({rfc, 6455}).
|
||||
|
||||
-author('ecestari@process-one.net').
|
||||
|
||||
-export([check/2, socket_handoff/8]).
|
||||
|
||||
@@ -2,8 +2,26 @@
|
||||
%%% File : ejabberd_xmlrpc.erl
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : XML-RPC server that frontends ejabberd commands
|
||||
%%% Created : 21 Aug 2007 by Badlop <badlop@ono.com>
|
||||
%%% Id : $Id: ejabberd_xmlrpc.erl 595 2008-05-20 11:39:31Z badlop $
|
||||
%%% Created : 21 Aug 2007 by Badlop <badlop@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%% TODO: Implement a command in ejabberdctl 'help COMMAND LANGUAGE' that shows
|
||||
@@ -440,7 +458,7 @@ format_arg({array, Elements}, {list, ElementsDef})
|
||||
[format_arg(Element, ElementsDef)
|
||||
|| Element <- Elements];
|
||||
format_arg(Arg, integer) when is_integer(Arg) -> Arg;
|
||||
format_arg(Arg, binary) when is_list(Arg) -> list_to_binary(Arg);
|
||||
format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg);
|
||||
format_arg(Arg, binary) when is_binary(Arg) -> Arg;
|
||||
format_arg(Arg, string) when is_list(Arg) -> Arg;
|
||||
format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg);
|
||||
@@ -448,7 +466,12 @@ format_arg(undefined, binary) -> <<>>;
|
||||
format_arg(undefined, string) -> "";
|
||||
format_arg(Arg, Format) ->
|
||||
?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]),
|
||||
throw({error_formatting_argument, Arg, Format}).
|
||||
error.
|
||||
|
||||
process_unicode_codepoints(Str) ->
|
||||
iolist_to_binary(lists:map(fun(X) when X > 255 -> unicode:characters_to_binary([X]);
|
||||
(Y) -> Y
|
||||
end, Str)).
|
||||
|
||||
%% -----------------------------
|
||||
%% Result
|
||||
@@ -466,6 +489,12 @@ format_result(String, {Name, string}) when is_list(String) ->
|
||||
{struct, [{Name, lists:flatten(String)}]};
|
||||
format_result(Binary, {Name, string}) when is_binary(Binary) ->
|
||||
{struct, [{Name, binary_to_list(Binary)}]};
|
||||
format_result(Atom, {Name, string}) when is_atom(Atom) ->
|
||||
{struct, [{Name, atom_to_list(Atom)}]};
|
||||
format_result(Integer, {Name, string}) when is_integer(Integer) ->
|
||||
{struct, [{Name, integer_to_list(Integer)}]};
|
||||
format_result(Other, {Name, string}) ->
|
||||
{struct, [{Name, io_lib:format("~p", [Other])}]};
|
||||
format_result(String, {Name, binary}) when is_list(String) ->
|
||||
{struct, [{Name, lists:flatten(String)}]};
|
||||
format_result(Binary, {Name, binary}) when is_binary(Binary) ->
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-export([export/2, export/3, import_file/2, import/2, import/3]).
|
||||
-export([export/2, export/3, import_file/2, import/2,
|
||||
import/3]).
|
||||
|
||||
-define(MAX_RECORDS_PER_TRANSACTION, 100).
|
||||
|
||||
@@ -54,7 +55,7 @@ modules() ->
|
||||
mod_offline,
|
||||
mod_privacy,
|
||||
mod_private,
|
||||
mod_pubsub,
|
||||
%% mod_pubsub,
|
||||
mod_roster,
|
||||
mod_shared_roster,
|
||||
mod_vcard,
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
|
||||
-export([get_status/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, connecting/2, connecting/3,
|
||||
wait_bind_response/3, active/3, active_bind/3,
|
||||
handle_event/3, handle_sync_event/4, handle_info/3,
|
||||
|
||||
@@ -24,20 +24,14 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(eldap_utils).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('mremond@process-one.net').
|
||||
|
||||
-export([generate_subfilter/1,
|
||||
find_ldap_attrs/2,
|
||||
get_ldap_attr/2,
|
||||
get_user_part/2,
|
||||
make_filter/2,
|
||||
get_state/2,
|
||||
case_insensitive_match/2,
|
||||
get_opt/3,
|
||||
get_opt/4,
|
||||
get_config/2,
|
||||
decode_octet_string/3,
|
||||
uids_domain_subst/2]).
|
||||
-export([generate_subfilter/1, find_ldap_attrs/2,
|
||||
get_ldap_attr/2, get_user_part/2, make_filter/2,
|
||||
get_state/2, case_insensitive_match/2, get_config/2,
|
||||
decode_octet_string/3, uids_domain_subst/2, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -171,64 +165,48 @@ uids_domain_subst(Host, UIDs) ->
|
||||
end,
|
||||
UIDs).
|
||||
|
||||
-spec get_opt({atom(), binary()}, list(), fun()) -> any().
|
||||
|
||||
get_opt({Key, Host}, Opts, F) ->
|
||||
get_opt({Key, Host}, Opts, F, undefined).
|
||||
|
||||
-spec get_opt({atom(), binary()}, list(), fun(), any()) -> any().
|
||||
|
||||
get_opt({Key, Host}, Opts, F, Default) ->
|
||||
case gen_mod:get_opt(Key, Opts, F, undefined) of
|
||||
undefined ->
|
||||
ejabberd_config:get_option(
|
||||
{Key, Host}, F, Default);
|
||||
Val ->
|
||||
Val
|
||||
end.
|
||||
|
||||
-spec get_config(binary(), list()) -> eldap_config().
|
||||
|
||||
get_config(Host, Opts) ->
|
||||
Servers = get_opt({ldap_servers, Host}, Opts,
|
||||
Servers = gen_mod:get_opt({ldap_servers, Host}, Opts,
|
||||
fun(L) ->
|
||||
[iolist_to_binary(H) || H <- L]
|
||||
end, [<<"localhost">>]),
|
||||
Backups = get_opt({ldap_backups, Host}, Opts,
|
||||
Backups = gen_mod:get_opt({ldap_backups, Host}, Opts,
|
||||
fun(L) ->
|
||||
[iolist_to_binary(H) || H <- L]
|
||||
end, []),
|
||||
Encrypt = get_opt({ldap_encrypt, Host}, Opts,
|
||||
Encrypt = gen_mod:get_opt({ldap_encrypt, Host}, Opts,
|
||||
fun(tls) -> tls;
|
||||
(starttls) -> starttls;
|
||||
(none) -> none
|
||||
end, none),
|
||||
TLSVerify = get_opt({ldap_tls_verify, Host}, Opts,
|
||||
TLSVerify = gen_mod:get_opt({ldap_tls_verify, Host}, Opts,
|
||||
fun(hard) -> hard;
|
||||
(soft) -> soft;
|
||||
(false) -> false
|
||||
end, false),
|
||||
TLSCAFile = get_opt({ldap_tls_cacertfile, Host}, Opts,
|
||||
TLSCAFile = gen_mod:get_opt({ldap_tls_cacertfile, Host}, Opts,
|
||||
fun iolist_to_binary/1),
|
||||
TLSDepth = get_opt({ldap_tls_depth, Host}, Opts,
|
||||
TLSDepth = gen_mod:get_opt({ldap_tls_depth, Host}, Opts,
|
||||
fun(I) when is_integer(I), I>=0 -> I end),
|
||||
Port = get_opt({ldap_port, Host}, Opts,
|
||||
Port = gen_mod:get_opt({ldap_port, Host}, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
case Encrypt of
|
||||
tls -> ?LDAPS_PORT;
|
||||
starttls -> ?LDAP_PORT;
|
||||
_ -> ?LDAP_PORT
|
||||
end),
|
||||
RootDN = get_opt({ldap_rootdn, Host}, Opts,
|
||||
RootDN = gen_mod:get_opt({ldap_rootdn, Host}, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"">>),
|
||||
Password = get_opt({ldap_password, Host}, Opts,
|
||||
Password = gen_mod:get_opt({ldap_password, Host}, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"">>),
|
||||
Base = get_opt({ldap_base, Host}, Opts,
|
||||
Base = gen_mod:get_opt({ldap_base, Host}, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"">>),
|
||||
OldDerefAliases = get_opt({deref_aliases, Host}, Opts,
|
||||
OldDerefAliases = gen_mod:get_opt({deref_aliases, Host}, Opts,
|
||||
fun(never) -> never;
|
||||
(searching) -> searching;
|
||||
(finding) -> finding;
|
||||
@@ -236,7 +214,7 @@ get_config(Host, Opts) ->
|
||||
end, unspecified),
|
||||
DerefAliases =
|
||||
if OldDerefAliases == unspecified ->
|
||||
get_opt({ldap_deref_aliases, Host}, Opts,
|
||||
gen_mod:get_opt({ldap_deref_aliases, Host}, Opts,
|
||||
fun(never) -> never;
|
||||
(searching) -> searching;
|
||||
(finding) -> finding;
|
||||
@@ -367,3 +345,43 @@ collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
|
||||
collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
|
||||
collect_parts_bit([],Acc,Uacc) ->
|
||||
list_to_binary([Uacc|lists:reverse(Acc)]).
|
||||
|
||||
opt_type(deref_aliases) ->
|
||||
fun (never) -> never;
|
||||
(searching) -> searching;
|
||||
(finding) -> finding;
|
||||
(always) -> always
|
||||
end;
|
||||
opt_type(ldap_backups) ->
|
||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
||||
opt_type(ldap_base) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_deref_aliases) ->
|
||||
fun (never) -> never;
|
||||
(searching) -> searching;
|
||||
(finding) -> finding;
|
||||
(always) -> always
|
||||
end;
|
||||
opt_type(ldap_encrypt) ->
|
||||
fun (tls) -> tls;
|
||||
(starttls) -> starttls;
|
||||
(none) -> none
|
||||
end;
|
||||
opt_type(ldap_password) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_port) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(ldap_rootdn) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_servers) ->
|
||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
||||
opt_type(ldap_tls_cacertfile) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_tls_depth) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(ldap_tls_verify) ->
|
||||
fun (hard) -> hard;
|
||||
(soft) -> soft;
|
||||
(false) -> false
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[deref_aliases, ldap_backups, ldap_base,
|
||||
ldap_deref_aliases, ldap_encrypt, ldap_password,
|
||||
ldap_port, ldap_rootdn, ldap_servers,
|
||||
ldap_tls_cacertfile, ldap_tls_depth, ldap_tls_verify].
|
||||
|
||||
@@ -24,15 +24,16 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ext_mod).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author("Christophe Romain <christophe.romain@process-one.net>").
|
||||
|
||||
%% Packaging service
|
||||
-export([start/0, stop/0, update/0, check/1,
|
||||
available_command/0, available/0, available/1,
|
||||
installed_command/0, installed/0, installed/1,
|
||||
install/1, uninstall/1,
|
||||
upgrade/0, upgrade/1,
|
||||
add_sources/2, del_sources/1]).
|
||||
available_command/0, available/0, available/1,
|
||||
installed_command/0, installed/0, installed/1,
|
||||
install/1, uninstall/1, upgrade/0, upgrade/1,
|
||||
add_sources/2, del_sources/1, modules_dir/0,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
@@ -163,6 +164,7 @@ install(Package) when is_binary(Package) ->
|
||||
case compile_and_install(Module, Attrs) of
|
||||
ok ->
|
||||
code:add_patha(module_ebin_dir(Module)),
|
||||
ejabberd_config:reload_file(),
|
||||
ok;
|
||||
Error ->
|
||||
delete_path(module_lib_dir(Module)),
|
||||
@@ -181,7 +183,8 @@ uninstall(Package) when is_binary(Package) ->
|
||||
code:purge(Module),
|
||||
code:delete(Module),
|
||||
code:del_path(module_ebin_dir(Module)),
|
||||
delete_path(module_lib_dir(Module));
|
||||
delete_path(module_lib_dir(Module)),
|
||||
ejabberd_config:reload_file();
|
||||
false ->
|
||||
{error, not_installed}
|
||||
end.
|
||||
@@ -302,7 +305,15 @@ extract_url(Path, DestDir) ->
|
||||
++[{error, unsupported_source}]).
|
||||
|
||||
extract_github_master(Repos, DestDir) ->
|
||||
case extract(zip, geturl(Repos ++ "/archive/master.zip"), DestDir) of
|
||||
Base = case string:tokens(Repos, ":") of
|
||||
["git@github.com", T1] -> "https://github.com/"++T1;
|
||||
_ -> Repos
|
||||
end,
|
||||
Url = case lists:reverse(Base) of
|
||||
[$t,$i,$g,$.|T2] -> lists:reverse(T2);
|
||||
_ -> Base
|
||||
end,
|
||||
case extract(zip, geturl(Url++"/archive/master.zip"), DestDir) of
|
||||
ok ->
|
||||
RepDir = filename:join(DestDir, module_name(Repos)),
|
||||
file:rename(RepDir++"-master", RepDir);
|
||||
@@ -310,9 +321,23 @@ extract_github_master(Repos, DestDir) ->
|
||||
Error
|
||||
end.
|
||||
|
||||
copy_file(From, To) ->
|
||||
filelib:ensure_dir(To),
|
||||
file:copy(From, To).
|
||||
copy(From, To) ->
|
||||
case filelib:is_dir(From) of
|
||||
true ->
|
||||
Copy = fun(F) ->
|
||||
SubFrom = filename:join(From, F),
|
||||
SubTo = filename:join(To, F),
|
||||
copy(SubFrom, SubTo)
|
||||
end,
|
||||
lists:foldl(fun({ok, C2}, {ok, C1}) -> {ok, C1+C2};
|
||||
({ok, _}, Error) -> Error;
|
||||
(Error, _) -> Error
|
||||
end, {ok, 0},
|
||||
[Copy(filename:basename(X)) || X<-filelib:wildcard(From++"/*")]);
|
||||
false ->
|
||||
filelib:ensure_dir(To),
|
||||
file:copy(From, To)
|
||||
end.
|
||||
|
||||
delete_path(Path) ->
|
||||
case filelib:is_dir(Path) of
|
||||
@@ -422,9 +447,14 @@ compile_and_install(Module, Spec) ->
|
||||
true ->
|
||||
{ok, Dir} = file:get_cwd(),
|
||||
file:set_cwd(SrcDir),
|
||||
Result = case compile(Module, Spec, LibDir) of
|
||||
ok -> install(Module, Spec, LibDir);
|
||||
Error -> Error
|
||||
Result = case compile_deps(Module, Spec, LibDir) of
|
||||
ok ->
|
||||
case compile(Module, Spec, LibDir) of
|
||||
ok -> install(Module, Spec, LibDir);
|
||||
Error -> Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end,
|
||||
file:set_cwd(Dir),
|
||||
Result;
|
||||
@@ -436,19 +466,47 @@ compile_and_install(Module, Spec) ->
|
||||
end
|
||||
end.
|
||||
|
||||
compile_deps(_Module, _Spec, DestDir) ->
|
||||
case filelib:is_dir("deps") of
|
||||
true -> ok;
|
||||
false -> fetch_rebar_deps()
|
||||
end,
|
||||
Ebin = filename:join(DestDir, "ebin"),
|
||||
filelib:ensure_dir(filename:join(Ebin, ".")),
|
||||
Result = lists:foldl(fun(Dep, Acc) ->
|
||||
Inc = filename:join(Dep, "include"),
|
||||
Src = filename:join(Dep, "src"),
|
||||
Options = [{outdir, Ebin}, {i, Inc}],
|
||||
[file:copy(App, Ebin) || App <- filelib:wildcard(Src++"/*.app")],
|
||||
Acc++[case compile:file(File, Options) of
|
||||
{ok, _} -> ok;
|
||||
{ok, _, _} -> ok;
|
||||
{ok, _, _, _} -> ok;
|
||||
error -> {error, {compilation_failed, File}};
|
||||
Error -> Error
|
||||
end
|
||||
|| File <- filelib:wildcard(Src++"/*.erl")]
|
||||
end, [], filelib:wildcard("deps/*")),
|
||||
case lists:dropwhile(
|
||||
fun(ok) -> true;
|
||||
(_) -> false
|
||||
end, Result) of
|
||||
[] -> ok;
|
||||
[Error|_] -> Error
|
||||
end.
|
||||
|
||||
compile(_Module, _Spec, DestDir) ->
|
||||
Ebin = filename:join(DestDir, "ebin"),
|
||||
filelib:ensure_dir(filename:join(Ebin, ".")),
|
||||
EjabBin = filename:dirname(code:which(ejabberd)),
|
||||
EjabInc = filename:join(filename:dirname(EjabBin), "include"),
|
||||
Logger = case code:is_loaded(lager) of
|
||||
{file, _} -> [{d, 'LAGER'}];
|
||||
_ -> []
|
||||
end,
|
||||
XmlHrl = filename:join(EjabInc, "xml.hrl"),
|
||||
Logger = [{d, 'LAGER'} || code:is_loaded(lager)=/=false],
|
||||
ExtLib = [{d, 'NO_EXT_LIB'} || filelib:is_file(XmlHrl)],
|
||||
Options = [{outdir, Ebin}, {i, "include"}, {i, EjabInc},
|
||||
{d, 'NO_EXT_LIB'}, %% use include instead of include_lib
|
||||
verbose, report_errors, report_warnings]
|
||||
++ Logger,
|
||||
++ Logger ++ ExtLib,
|
||||
[file:copy(App, Ebin) || App <- filelib:wildcard("src/*.app")],
|
||||
Result = [case compile:file(File, Options) of
|
||||
{ok, _} -> ok;
|
||||
{ok, _, _} -> ok;
|
||||
@@ -465,11 +523,59 @@ compile(_Module, _Spec, DestDir) ->
|
||||
[Error|_] -> Error
|
||||
end.
|
||||
|
||||
install(Module, _Spec, DestDir) ->
|
||||
SpecFile = filename:flatten([Module, ".spec"]),
|
||||
[copy_file(File, filename:join(DestDir, File))
|
||||
|| File <- [SpecFile | filelib:wildcard("{ebin,priv,conf,include}/**")]],
|
||||
ok.
|
||||
install(Module, Spec, DestDir) ->
|
||||
Errors = lists:dropwhile(fun({_, {ok, _}}) -> true;
|
||||
(_) -> false
|
||||
end, [{File, copy(File, filename:join(DestDir, File))}
|
||||
|| File <- filelib:wildcard("{ebin,priv,conf,include}/**")]),
|
||||
Result = case Errors of
|
||||
[{F, {error, E}}|_] ->
|
||||
{error, {F, E}};
|
||||
[] ->
|
||||
SpecPath = proplists:get_value(path, Spec),
|
||||
SpecFile = filename:flatten([Module, ".spec"]),
|
||||
copy(filename:join(SpecPath, SpecFile), filename:join(DestDir, SpecFile))
|
||||
end,
|
||||
case Result of
|
||||
{ok, _} -> ok;
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
%% -- minimalist rebar spec parser, only support git
|
||||
|
||||
fetch_rebar_deps() ->
|
||||
case rebar_deps("rebar.config")++rebar_deps("rebar.config.script") of
|
||||
[] ->
|
||||
ok;
|
||||
Deps ->
|
||||
filelib:ensure_dir(filename:join("deps", ".")),
|
||||
lists:foreach(fun({_App, Cmd}) ->
|
||||
os:cmd("cd deps; "++Cmd++"; cd ..")
|
||||
end, Deps)
|
||||
end.
|
||||
rebar_deps(Script) ->
|
||||
case file:script(Script) of
|
||||
{ok, Config} when is_list(Config) ->
|
||||
[rebar_dep(Dep) || Dep <- proplists:get_value(deps, Config, [])];
|
||||
{ok, {deps, Deps}} ->
|
||||
[rebar_dep(Dep) || Dep <- Deps];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
rebar_dep({App, _, {git, Url}}) ->
|
||||
{App, "git clone "++Url++" "++filename:basename(App)};
|
||||
rebar_dep({App, _, {git, Url, {branch, Ref}}}) ->
|
||||
{App, "git clone -n "++Url++" "++filename:basename(App)++
|
||||
"; (cd "++filename:basename(App)++
|
||||
"; git checkout -q origin/"++Ref++")"};
|
||||
rebar_dep({App, _, {git, Url, {tag, Ref}}}) ->
|
||||
{App, "git clone -n "++Url++" "++filename:basename(App)++
|
||||
"; (cd "++filename:basename(App)++
|
||||
"; git checkout -q "++Ref++")"};
|
||||
rebar_dep({App, _, {git, Url, Ref}}) ->
|
||||
{App, "git clone -n "++Url++" "++filename:basename(App)++
|
||||
"; (cd "++filename:basename(App)++
|
||||
"; git checkout -q "++Ref++")"}.
|
||||
|
||||
%% -- YAML spec parser
|
||||
|
||||
@@ -484,3 +590,10 @@ format({Key, Val}) when is_binary(Val) ->
|
||||
{Key, binary_to_list(Val)};
|
||||
format({Key, Val}) -> % TODO: improve Yaml parsing
|
||||
{Key, Val}.
|
||||
|
||||
opt_type(allow_contrib_modules) ->
|
||||
fun (false) -> false;
|
||||
(no) -> false;
|
||||
(_) -> true
|
||||
end;
|
||||
opt_type(_) -> [allow_contrib_modules].
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
|
||||
-module(extauth).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('leifj@it.su.se').
|
||||
|
||||
-export([start/2, stop/1, init/2, check_password/3,
|
||||
set_password/3, try_register/3, remove_user/2,
|
||||
remove_user/3, is_user_exists/2]).
|
||||
remove_user/3, is_user_exists/2, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -157,3 +159,7 @@ encode(L) -> str:join(L, <<":">>).
|
||||
|
||||
decode([0, 0]) -> false;
|
||||
decode([0, 1]) -> true.
|
||||
|
||||
opt_type(extauth_instances) ->
|
||||
fun (V) when is_integer(V), V > 0 -> V end;
|
||||
opt_type(_) -> [extauth_instances].
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : gen_mod.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose :
|
||||
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
@@ -18,22 +17,26 @@
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(gen_mod).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0, start_module/2, start_module/3, stop_module/2,
|
||||
stop_module_keep_config/2, get_opt/3, get_opt/4,
|
||||
get_opt_host/3, db_type/1, db_type/2, get_module_opt/5,
|
||||
get_module_opt_host/3, loaded_modules/1,
|
||||
loaded_modules_with_opts/1, get_hosts/2,
|
||||
get_module_proc/2, is_loaded/2, default_db/1]).
|
||||
-export([start/0, start_module/2, start_module/3,
|
||||
stop_module/2, stop_module_keep_config/2, get_opt/3,
|
||||
get_opt/4, get_opt_host/3, db_type/1, db_type/2,
|
||||
get_module_opt/4, get_module_opt/5, get_module_opt_host/3,
|
||||
loaded_modules/1, loaded_modules_with_opts/1,
|
||||
get_hosts/2, get_module_proc/2, is_loaded/2,
|
||||
start_modules/1, default_db/1, v_db/1, opt_type/1]).
|
||||
|
||||
%%-export([behaviour_info/1]).
|
||||
|
||||
@@ -45,11 +48,13 @@
|
||||
opts = [] :: opts() | '_' | '$2'}).
|
||||
|
||||
-type opts() :: [{atom(), any()}].
|
||||
-type db_type() :: odbc | mnesia | riak.
|
||||
|
||||
-callback start(binary(), opts()) -> any().
|
||||
-callback stop(binary()) -> any().
|
||||
|
||||
-export_type([opts/0]).
|
||||
-export_type([db_type/0]).
|
||||
|
||||
%%behaviour_info(callbacks) -> [{start, 2}, {stop, 1}];
|
||||
%%behaviour_info(_Other) -> undefined.
|
||||
@@ -60,6 +65,17 @@ start() ->
|
||||
{keypos, #ejabberd_module.module_host}]),
|
||||
ok.
|
||||
|
||||
-spec start_modules(binary()) -> any().
|
||||
|
||||
start_modules(Host) ->
|
||||
Modules = ejabberd_config:get_option(
|
||||
{modules, Host},
|
||||
fun(L) when is_list(L) -> L end, []),
|
||||
lists:foreach(
|
||||
fun({Module, Opts}) ->
|
||||
start_module(Host, Module, Opts)
|
||||
end, Modules).
|
||||
|
||||
-spec start_module(binary(), atom()) -> any().
|
||||
|
||||
start_module(Host, Module) ->
|
||||
@@ -75,7 +91,8 @@ start_module(Host, Module) ->
|
||||
|
||||
-spec start_module(binary(), atom(), opts()) -> any().
|
||||
|
||||
start_module(Host, Module, Opts) ->
|
||||
start_module(Host, Module, Opts0) ->
|
||||
Opts = validate_opts(Module, Opts0),
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}),
|
||||
@@ -107,18 +124,12 @@ is_app_running(AppName) ->
|
||||
|
||||
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
|
||||
|
||||
%% @doc Stop the module in a host, and forget its configuration.
|
||||
stop_module(Host, Module) ->
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
error -> error;
|
||||
ok -> ok
|
||||
end.
|
||||
|
||||
%% @doc Stop the module in a host, but keep its configuration.
|
||||
%% As the module configuration is kept in the Mnesia local_config table,
|
||||
%% when ejabberd is restarted the module will be started again.
|
||||
%% This function is useful when ejabberd is being stopped
|
||||
%% and it stops all modules.
|
||||
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
|
||||
|
||||
stop_module_keep_config(Host, Module) ->
|
||||
@@ -155,13 +166,20 @@ wait_for_stop1(MonitorReference) ->
|
||||
|
||||
-type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
|
||||
|
||||
-spec get_opt(atom(), opts(), check_fun()) -> any().
|
||||
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun()) -> any().
|
||||
|
||||
get_opt(Opt, Opts, F) ->
|
||||
get_opt(Opt, Opts, F, undefined).
|
||||
|
||||
-spec get_opt(atom(), opts(), check_fun(), any()) -> any().
|
||||
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun(), any()) -> any().
|
||||
|
||||
get_opt({Opt, Host}, Opts, F, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
ejabberd_config:get_option({Opt, Host}, F, Default);
|
||||
{value, {_, Val}} ->
|
||||
ejabberd_config:prepare_opt_val(Opt, Val, F, Default)
|
||||
end;
|
||||
get_opt(Opt, Opts, F, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
@@ -170,6 +188,11 @@ get_opt(Opt, Opts, F, Default) ->
|
||||
ejabberd_config:prepare_opt_val(Opt, Val, F, Default)
|
||||
end.
|
||||
|
||||
-spec get_module_opt(global | binary(), atom(), atom(), check_fun()) -> any().
|
||||
|
||||
get_module_opt(Host, Module, Opt, F) ->
|
||||
get_module_opt(Host, Module, Opt, F, undefined).
|
||||
|
||||
-spec get_module_opt(global | binary(), atom(), atom(), check_fun(), any()) -> any().
|
||||
|
||||
get_module_opt(global, Module, Opt, F, Default) ->
|
||||
@@ -209,39 +232,60 @@ get_opt_host(Host, Opts, Default) ->
|
||||
Val = get_opt(host, Opts, fun iolist_to_binary/1, Default),
|
||||
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
|
||||
|
||||
-spec db_type(opts()) -> odbc | mnesia | riak.
|
||||
validate_opts(Module, Opts) ->
|
||||
lists:filter(
|
||||
fun({Opt, Val}) ->
|
||||
case catch Module:mod_opt_type(Opt) of
|
||||
VFun when is_function(VFun) ->
|
||||
case catch VFun(Val) of
|
||||
{'EXIT', _} ->
|
||||
?ERROR_MSG("ignoring invalid value '~p' for "
|
||||
"option '~s' of module '~s'",
|
||||
[Val, Opt, Module]),
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
L when is_list(L) ->
|
||||
SOpts = str:join([[$', atom_to_list(A), $'] || A <- L], <<", ">>),
|
||||
?ERROR_MSG("unknown option '~s' for module '~s' will be"
|
||||
" likely ignored, available options are: ~s",
|
||||
[Opt, Module, SOpts]),
|
||||
true;
|
||||
{'EXIT', {undef, _}} ->
|
||||
?WARNING_MSG("module '~s' doesn't export mod_opt_type/1",
|
||||
[Module]),
|
||||
true
|
||||
end;
|
||||
(Junk) ->
|
||||
?ERROR_MSG("failed to understand option ~p for module '~s'",
|
||||
[Junk, Module]),
|
||||
false
|
||||
end, Opts).
|
||||
|
||||
-spec v_db(db_type() | internal) -> db_type().
|
||||
|
||||
v_db(odbc) -> odbc;
|
||||
v_db(internal) -> mnesia;
|
||||
v_db(mnesia) -> mnesia;
|
||||
v_db(riak) -> riak.
|
||||
|
||||
-spec db_type(opts()) -> db_type().
|
||||
|
||||
db_type(Opts) ->
|
||||
db_type(global, Opts).
|
||||
|
||||
-spec db_type(binary() | global, atom() | opts()) -> odbc | mnesia | riak.
|
||||
-spec db_type(binary() | global, atom() | opts()) -> db_type().
|
||||
|
||||
db_type(Host, Module) when is_atom(Module) ->
|
||||
get_module_opt(Host, Module, db_type,
|
||||
fun(odbc) -> odbc;
|
||||
(internal) -> mnesia;
|
||||
(mnesia) -> mnesia;
|
||||
(riak) -> riak
|
||||
end,
|
||||
default_db(Host));
|
||||
get_module_opt(Host, Module, db_type, fun v_db/1, default_db(Host));
|
||||
db_type(Host, Opts) when is_list(Opts) ->
|
||||
get_opt(db_type, Opts,
|
||||
fun(odbc) -> odbc;
|
||||
(internal) -> mnesia;
|
||||
(mnesia) -> mnesia;
|
||||
(riak) -> riak
|
||||
end,
|
||||
default_db(Host)).
|
||||
get_opt(db_type, Opts, fun v_db/1, default_db(Host)).
|
||||
|
||||
-spec default_db(binary() | global) -> odbc | mnesia | riak.
|
||||
-spec default_db(binary() | global) -> db_type().
|
||||
|
||||
default_db(Host) ->
|
||||
ejabberd_config:get_option({default_db, Host},
|
||||
fun(odbc) -> odbc;
|
||||
(mnesia) -> mnesia;
|
||||
(riak) -> riak;
|
||||
(internal) -> mnesia
|
||||
end, mnesia).
|
||||
ejabberd_config:get_option({default_db, Host}, fun v_db/1, mnesia).
|
||||
|
||||
-spec loaded_modules(binary()) -> [atom()].
|
||||
|
||||
@@ -288,3 +332,7 @@ get_module_proc(Host, Base) ->
|
||||
|
||||
is_loaded(Host, Module) ->
|
||||
ets:member(ejabberd_modules, {Module, Host}).
|
||||
|
||||
opt_type(default_db) -> fun v_db/1;
|
||||
opt_type(modules) -> fun (L) when is_list(L) -> L end;
|
||||
opt_type(_) -> [default_db, modules].
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 59, '1.0'}).
|
||||
-protocol({xep, 82, '1.1'}).
|
||||
-protocol({xep, 203, '2.0'}).
|
||||
|
||||
-compile({no_auto_import, [atom_to_binary/2,
|
||||
binary_to_integer/1,
|
||||
integer_to_binary/1]}).
|
||||
@@ -54,9 +58,6 @@
|
||||
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
|
||||
l2i/1, i2l/1, i2l/2, queue_drop_while/2]).
|
||||
|
||||
%% TODO: Remove once XEP-0091 is Obsolete
|
||||
%% TODO: Remove once XEP-0091 is Obsolete
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
@@ -612,19 +613,10 @@ add_delay_info(El, From, Time) ->
|
||||
binary()) -> xmlel().
|
||||
|
||||
add_delay_info(El, From, Time, Desc) ->
|
||||
%% TODO: Remove support for <x/>, XEP-0091 is obsolete.
|
||||
El1 = add_delay_info(El, From, Time, Desc, <<"delay">>, ?NS_DELAY),
|
||||
El2 = add_delay_info(El1, From, Time, Desc, <<"x">>, ?NS_DELAY91),
|
||||
El2.
|
||||
|
||||
-spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp(),
|
||||
binary(), binary(), binary()) -> xmlel().
|
||||
|
||||
add_delay_info(El, From, Time, Desc, Name, XMLNS) ->
|
||||
case xml:get_subtag_with_xmlns(El, Name, XMLNS) of
|
||||
case xml:get_subtag_with_xmlns(El, <<"delay">>, ?NS_DELAY) of
|
||||
false ->
|
||||
%% Add new tag
|
||||
DelayTag = create_delay_tag(Time, From, Desc, XMLNS),
|
||||
DelayTag = create_delay_tag(Time, From, Desc),
|
||||
xml:append_subtags(El, [DelayTag]);
|
||||
DelayTag ->
|
||||
%% Update existing tag
|
||||
@@ -645,34 +637,28 @@ add_delay_info(El, From, Time, Desc, Name, XMLNS) ->
|
||||
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]}
|
||||
end
|
||||
end,
|
||||
NewEl = xml:remove_subtags(El, Name, {<<"xmlns">>, XMLNS}),
|
||||
NewEl = xml:remove_subtags(El, <<"delay">>, {<<"xmlns">>, ?NS_DELAY}),
|
||||
xml:append_subtags(NewEl, [NewDelayTag])
|
||||
end.
|
||||
|
||||
-spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary(),
|
||||
binary()) -> xmlel() | error.
|
||||
-spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary())
|
||||
-> xmlel() | error.
|
||||
|
||||
create_delay_tag(TimeStamp, FromJID, Desc, XMLNS) when is_tuple(FromJID) ->
|
||||
create_delay_tag(TimeStamp, FromJID, Desc) when is_tuple(FromJID) ->
|
||||
From = jlib:jid_to_string(FromJID),
|
||||
{Name, Stamp} = case XMLNS of
|
||||
?NS_DELAY ->
|
||||
{<<"delay">>, now_to_utc_string(TimeStamp, 3)};
|
||||
?NS_DELAY91 ->
|
||||
DateTime = calendar:now_to_universal_time(TimeStamp),
|
||||
{<<"x">>, timestamp_to_iso(DateTime)}
|
||||
end,
|
||||
Stamp = now_to_utc_string(TimeStamp, 3),
|
||||
Children = case Desc of
|
||||
<<"">> -> [];
|
||||
_ -> [{xmlcdata, Desc}]
|
||||
end,
|
||||
#xmlel{name = Name,
|
||||
#xmlel{name = <<"delay">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, XMLNS}, {<<"from">>, From},
|
||||
[{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From},
|
||||
{<<"stamp">>, Stamp}],
|
||||
children = Children};
|
||||
create_delay_tag(DateTime, Host, Desc, XMLNS) when is_binary(Host) ->
|
||||
create_delay_tag(DateTime, Host, Desc) when is_binary(Host) ->
|
||||
FromJID = jlib:make_jid(<<"">>, Host, <<"">>),
|
||||
create_delay_tag(DateTime, FromJID, Desc, XMLNS).
|
||||
create_delay_tag(DateTime, FromJID, Desc).
|
||||
|
||||
-type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc.
|
||||
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
|
||||
-protocol({xep, 50, '1.2'}).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, process_local_iq/3,
|
||||
process_sm_iq/3, get_local_commands/5,
|
||||
get_local_identity/5, get_local_features/5,
|
||||
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
|
||||
ping_item/4, ping_command/4]).
|
||||
ping_item/4, ping_command/4, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -278,3 +280,8 @@ ping_command(_Acc, _From, _To,
|
||||
true -> {error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
ping_command(Acc, _From, _To, _Request) -> Acc.
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(report_commands_node) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) -> [iqdisc, report_commands_node].
|
||||
|
||||
@@ -30,75 +30,32 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-export([start/2, stop/1,
|
||||
%% Node
|
||||
compile/1,
|
||||
get_cookie/0,
|
||||
remove_node/1,
|
||||
export2odbc/2,
|
||||
%% Accounts
|
||||
set_password/3,
|
||||
check_password_hash/4,
|
||||
delete_old_users/1,
|
||||
delete_old_users_vhost/2,
|
||||
ban_account/3,
|
||||
num_active_users/2,
|
||||
%% Sessions
|
||||
num_resources/2,
|
||||
resource_num/3,
|
||||
kick_session/4,
|
||||
status_num/2, status_num/1,
|
||||
status_list/2, status_list/1,
|
||||
connected_users_info/0,
|
||||
connected_users_vhost/1,
|
||||
set_presence/7,
|
||||
user_sessions_info/2,
|
||||
%% Vcard
|
||||
set_nickname/3,
|
||||
get_vcard/3,
|
||||
get_vcard/4,
|
||||
get_vcard_multi/4,
|
||||
set_vcard/4,
|
||||
set_vcard/5,
|
||||
%% Roster
|
||||
add_rosteritem/7,
|
||||
delete_rosteritem/4,
|
||||
process_rosteritems/5,
|
||||
get_roster/2,
|
||||
push_roster/3,
|
||||
push_roster_all/1,
|
||||
push_alltoall/2,
|
||||
%% mod_last
|
||||
get_last/2,
|
||||
set_last/4,
|
||||
%% mod_private
|
||||
private_get/4,
|
||||
private_set/3,
|
||||
%% mod_shared_roster
|
||||
srg_create/5,
|
||||
srg_delete/2,
|
||||
srg_list/1,
|
||||
srg_get_info/2,
|
||||
srg_get_members/2,
|
||||
srg_user_add/4,
|
||||
srg_user_del/4,
|
||||
%% Stanza
|
||||
send_message/5,
|
||||
send_stanza_c2s/4,
|
||||
privacy_set/3,
|
||||
%% Stats
|
||||
stats/1, stats/2
|
||||
]).
|
||||
-export([start/2, stop/1, compile/1, get_cookie/0,
|
||||
remove_node/1, set_password/3,
|
||||
check_password_hash/4, delete_old_users/1,
|
||||
delete_old_users_vhost/2, ban_account/3,
|
||||
num_active_users/2, num_resources/2, resource_num/3,
|
||||
kick_session/4, status_num/2, status_num/1,
|
||||
status_list/2, status_list/1, connected_users_info/0,
|
||||
connected_users_vhost/1, set_presence/7,
|
||||
user_sessions_info/2, set_nickname/3, get_vcard/3,
|
||||
get_vcard/4, get_vcard_multi/4, set_vcard/4,
|
||||
set_vcard/5, add_rosteritem/7, delete_rosteritem/4,
|
||||
process_rosteritems/5, get_roster/2, push_roster/3,
|
||||
push_roster_all/1, push_alltoall/2, get_last/2,
|
||||
private_get/4, private_set/3, srg_create/5,
|
||||
srg_delete/2, srg_list/1, srg_get_info/2,
|
||||
srg_get_members/2, srg_user_add/4, srg_user_del/4,
|
||||
send_message/5, send_stanza_c2s/4, privacy_set/3,
|
||||
stats/1, stats/2, mod_opt_type/1]).
|
||||
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
%% Copied from ejabberd_sm.erl
|
||||
-record(session, {sid, usr, us, priority, info}).
|
||||
|
||||
|
||||
%%%
|
||||
%%% gen_mod
|
||||
%%%
|
||||
@@ -155,11 +112,6 @@ commands() ->
|
||||
module = ?MODULE, function = remove_node,
|
||||
args = [{node, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = export2odbc, tags = [mnesia], %% Copied to ejabberd 2.1.x after 11
|
||||
desc = "Export Mnesia tables to files in directory",
|
||||
module = ?MODULE, function = export2odbc,
|
||||
args = [{host, string}, {path, string}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = num_active_users, tags = [accounts, stats],
|
||||
desc = "Get number of users active in the last days",
|
||||
@@ -191,7 +143,7 @@ commands() ->
|
||||
desc = "Check if the password hash is correct",
|
||||
longdesc = "Allowed hash methods: md5, sha.",
|
||||
module = ?MODULE, function = check_password_hash,
|
||||
args = [{user, binary}, {host, binary}, {passwordhash, binary}, {hashmethod, binary}],
|
||||
args = [{user, binary}, {host, binary}, {passwordhash, string}, {hashmethod, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = change_password, tags = [accounts],
|
||||
desc = "Change the password of an account",
|
||||
@@ -276,7 +228,7 @@ commands() ->
|
||||
tags = [session],
|
||||
desc = "Get the list of established sessions in a vhost",
|
||||
module = ?MODULE, function = connected_users_vhost,
|
||||
args = [{host, string}],
|
||||
args = [{host, binary}],
|
||||
result = {connected_users_vhost, {list, {sessions, string}}}},
|
||||
#ejabberd_commands{name = user_sessions_info,
|
||||
tags = [session],
|
||||
@@ -421,21 +373,21 @@ commands() ->
|
||||
#ejabberd_commands{name = push_roster, tags = [roster],
|
||||
desc = "Push template roster from file to a user",
|
||||
module = ?MODULE, function = push_roster,
|
||||
args = [{file, string}, {user, string}, {host, string}],
|
||||
args = [{file, binary}, {user, binary}, {host, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = push_roster_all, tags = [roster],
|
||||
desc = "Push template roster from file to all those users",
|
||||
module = ?MODULE, function = push_roster_all,
|
||||
args = [{file, string}],
|
||||
args = [{file, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = push_alltoall, tags = [roster],
|
||||
desc = "Add all the users to all the users of Host in Group",
|
||||
module = ?MODULE, function = push_alltoall,
|
||||
args = [{host, string}, {group, string}],
|
||||
args = [{host, binary}, {group, binary}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = get_last, tags = [last],
|
||||
desc = "Get last activity information",
|
||||
desc = "Get last activity information (timestamp and status)",
|
||||
longdesc = "Timestamp is the seconds since"
|
||||
"1970-01-01 00:00:00 UTC, for example: date +%s",
|
||||
module = ?MODULE, function = get_last,
|
||||
@@ -445,8 +397,8 @@ commands() ->
|
||||
desc = "Set last activity information",
|
||||
longdesc = "Timestamp is the seconds since"
|
||||
"1970-01-01 00:00:00 UTC, for example: date +%s",
|
||||
module = ?MODULE, function = set_last,
|
||||
args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}],
|
||||
module = mod_last, function = store_last_info,
|
||||
args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = private_get, tags = [private],
|
||||
@@ -548,25 +500,6 @@ remove_node(Node) ->
|
||||
mnesia:del_table_copy(schema, list_to_atom(Node)),
|
||||
ok.
|
||||
|
||||
export2odbc(Host, Directory) ->
|
||||
Tables = [
|
||||
{export_last, last},
|
||||
{export_offline, offline},
|
||||
{export_passwd, passwd},
|
||||
{export_private_storage, private_storage},
|
||||
{export_roster, roster},
|
||||
{export_vcard, vcard},
|
||||
{export_vcard_search, vcard_search}],
|
||||
Export = fun({TableFun, Table}) ->
|
||||
Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
|
||||
io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
|
||||
Res = (catch ejd2odbc:TableFun(Host, Filename)),
|
||||
io:format(" Result: ~p~n", [Res])
|
||||
end,
|
||||
lists:foreach(Export, Tables),
|
||||
ok.
|
||||
|
||||
|
||||
%%%
|
||||
%%% Accounts
|
||||
%%%
|
||||
@@ -582,12 +515,16 @@ set_password(User, Host, Password) ->
|
||||
%% Copied some code from ejabberd_commands.erl
|
||||
check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
||||
AccountPass = ejabberd_auth:get_password_s(User, Host),
|
||||
AccountPassHash = case HashMethod of
|
||||
"md5" -> get_md5(AccountPass);
|
||||
"sha" -> get_sha(AccountPass);
|
||||
AccountPassHash = case {AccountPass, HashMethod} of
|
||||
{A, _} when is_tuple(A) -> scrammed;
|
||||
{_, "md5"} -> get_md5(AccountPass);
|
||||
{_, "sha"} -> get_sha(AccountPass);
|
||||
_ -> undefined
|
||||
end,
|
||||
case AccountPassHash of
|
||||
scrammed ->
|
||||
?ERROR_MSG("Passwords are scrammed, and check_password_hash can not work.", []),
|
||||
throw(passwords_scrammed_command_cannot_work);
|
||||
undefined -> error;
|
||||
PasswordHash -> ok;
|
||||
_ -> error
|
||||
@@ -680,7 +617,7 @@ delete_old_users(Days, Users) ->
|
||||
%% If it isnt
|
||||
[] ->
|
||||
%% Look for his last_activity
|
||||
case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of
|
||||
case mod_last:get_last_info(LUser, LServer) of
|
||||
%% If it is
|
||||
%% existent:
|
||||
{ok, TimeStamp, _Status} ->
|
||||
@@ -714,13 +651,6 @@ delete_old_users(Days, Users) ->
|
||||
Users_removed = lists:filter(F, Users),
|
||||
{removed, length(Users_removed), Users_removed}.
|
||||
|
||||
get_lastactivity_module(Server) ->
|
||||
case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
|
||||
true -> mod_last;
|
||||
_ -> mod_last_odbc
|
||||
end.
|
||||
|
||||
|
||||
%%
|
||||
%% Ban account
|
||||
|
||||
@@ -1076,7 +1006,7 @@ subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
|
||||
mod_roster:set_items(
|
||||
LU, LS,
|
||||
{xmlel, <<"query">>,
|
||||
[{<<"xmlns">>, <<"jabber:iq:roster">>}],
|
||||
[{<<"xmlns">>, ?NS_ROSTER}],
|
||||
[ItemEl]}).
|
||||
|
||||
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
|
||||
@@ -1093,7 +1023,7 @@ unsubscribe(LU, LS, User, Server) ->
|
||||
mod_roster:set_items(
|
||||
LU, LS,
|
||||
{xmlel, <<"query">>,
|
||||
[{<<"xmlns">>, <<"jabber:iq:roster">>}],
|
||||
[{<<"xmlns">>, ?NS_ROSTER}],
|
||||
[ItemEl]}).
|
||||
|
||||
%% -----------------------------
|
||||
@@ -1220,13 +1150,12 @@ build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
|
||||
%%%
|
||||
|
||||
get_last(User, Server) ->
|
||||
Mod = get_lastactivity_module(Server),
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
case Mod:get_last_info(User, Server) of
|
||||
case mod_last:get_last_info(User, Server) of
|
||||
not_found ->
|
||||
"Never";
|
||||
{ok, Shift, _Status} ->
|
||||
{ok, Shift, Status} ->
|
||||
TimeStamp = {Shift div 1000000,
|
||||
Shift rem 1000000,
|
||||
0},
|
||||
@@ -1234,17 +1163,13 @@ get_last(User, Server) ->
|
||||
calendar:now_to_local_time(TimeStamp),
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
|
||||
[Year, Month, Day, Hour, Minute, Second]))
|
||||
"~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ~s",
|
||||
[Year, Month, Day, Hour, Minute, Second, Status]))
|
||||
end;
|
||||
_ ->
|
||||
"Online"
|
||||
end.
|
||||
|
||||
set_last(User, Server, Timestamp, Status) ->
|
||||
Mod = get_lastactivity_module(Server),
|
||||
Mod:store_last_info(User, Server, Timestamp, Status).
|
||||
|
||||
%%%
|
||||
%%% Private Storage
|
||||
%%%
|
||||
@@ -1263,7 +1188,7 @@ private_get(Username, Host, Element, Ns) ->
|
||||
[{xmlel, Element, [{<<"xmlns">>, Ns}], []}]}},
|
||||
ResIq = mod_private:process_sm_iq(From, To, IQ),
|
||||
[{xmlel, <<"query">>,
|
||||
[{<<"xmlns">>, <<"jabber:iq:private">>}],
|
||||
[{<<"xmlns">>, ?NS_PRIVATE}],
|
||||
[SubEl]}] = ResIq#iq.sub_el,
|
||||
binary_to_list(xml:element_to_binary(SubEl)).
|
||||
|
||||
@@ -1572,3 +1497,6 @@ is_glob_match(String, <<"!", Glob/binary>>) ->
|
||||
not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob));
|
||||
is_glob_match(String, Glob) ->
|
||||
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
|
||||
|
||||
mod_opt_type(module_resource) -> fun (A) -> A end;
|
||||
mod_opt_type(_) -> [module_resource].
|
||||
|
||||
@@ -31,20 +31,11 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
init/0,
|
||||
stop/1,
|
||||
export/1,
|
||||
import/1,
|
||||
import/3,
|
||||
announce/3,
|
||||
send_motd/1,
|
||||
disco_identity/5,
|
||||
disco_features/5,
|
||||
disco_items/5,
|
||||
send_announcement_to_all/3,
|
||||
announce_commands/4,
|
||||
announce_items/4]).
|
||||
-export([start/2, init/0, stop/1, export/1, import/1,
|
||||
import/3, announce/3, send_motd/1, disco_identity/5,
|
||||
disco_features/5, disco_items/5,
|
||||
send_announcement_to_all/3, announce_commands/4,
|
||||
announce_items/4, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -1152,3 +1143,8 @@ import(_LServer, riak, #motd_users{us = {_, S}} = Users) ->
|
||||
[{'2i', [{<<"server">>, S}]}]);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
|
||||
mod_opt_type(access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(_) -> [access, db_type].
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-protocol({xep, 191, '1.2'}).
|
||||
|
||||
-export([start/2, stop/1, process_iq/3,
|
||||
process_iq_set/4, process_iq_get/5]).
|
||||
process_iq_set/4, process_iq_get/5, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -449,3 +451,6 @@ process_blocklist_get(LUser, LServer, odbc) ->
|
||||
end;
|
||||
{'EXIT', _} -> error
|
||||
end.
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [iqdisc].
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
|
||||
-protocol({xep, 115, '1.5'}).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
@@ -45,10 +47,9 @@
|
||||
-export([init/1, handle_info/2, handle_call/3,
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
%% hook handlers
|
||||
-export([user_send_packet/3, user_receive_packet/4,
|
||||
-export([user_send_packet/4, user_receive_packet/5,
|
||||
c2s_presence_in/2, c2s_filter_packet/6,
|
||||
c2s_broadcast_recipients/6]).
|
||||
c2s_broadcast_recipients/6, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -142,11 +143,12 @@ read_caps([_ | Tail], Result) ->
|
||||
read_caps(Tail, Result);
|
||||
read_caps([], Result) -> Result.
|
||||
|
||||
user_send_packet(#jid{luser = User, lserver = Server} = From,
|
||||
user_send_packet(#xmlel{name = <<"presence">>, attrs = Attrs,
|
||||
children = Els} = Pkt,
|
||||
_C2SState,
|
||||
#jid{luser = User, lserver = Server} = From,
|
||||
#jid{luser = User, lserver = Server,
|
||||
lresource = <<"">>},
|
||||
#xmlel{name = <<"presence">>, attrs = Attrs,
|
||||
children = Els} = Pkt) ->
|
||||
lresource = <<"">>}) ->
|
||||
Type = xml:get_attr_s(<<"type">>, Attrs),
|
||||
if Type == <<"">>; Type == <<"available">> ->
|
||||
case read_caps(Els) of
|
||||
@@ -157,13 +159,14 @@ user_send_packet(#jid{luser = User, lserver = Server} = From,
|
||||
true -> ok
|
||||
end,
|
||||
Pkt;
|
||||
user_send_packet( _From, _To, Pkt) ->
|
||||
user_send_packet(Pkt, _C2SState, _From, _To) ->
|
||||
Pkt.
|
||||
|
||||
user_receive_packet(#jid{lserver = Server},
|
||||
From, _To,
|
||||
#xmlel{name = <<"presence">>, attrs = Attrs,
|
||||
children = Els} = Pkt) ->
|
||||
user_receive_packet(#xmlel{name = <<"presence">>, attrs = Attrs,
|
||||
children = Els} = Pkt,
|
||||
_C2SState,
|
||||
#jid{lserver = Server},
|
||||
From, _To) ->
|
||||
Type = xml:get_attr_s(<<"type">>, Attrs),
|
||||
IsRemote = not lists:member(From#jid.lserver, ?MYHOSTS),
|
||||
if IsRemote and
|
||||
@@ -176,7 +179,7 @@ user_receive_packet(#jid{lserver = Server},
|
||||
true -> ok
|
||||
end,
|
||||
Pkt;
|
||||
user_receive_packet( _JID, _From, _To, Pkt) ->
|
||||
user_receive_packet(Pkt, _C2SState, _JID, _From, _To) ->
|
||||
Pkt.
|
||||
|
||||
-spec caps_stream_features([xmlel()], binary()) -> [xmlel()].
|
||||
@@ -240,27 +243,24 @@ c2s_presence_in(C2SState,
|
||||
error -> gb_trees:empty()
|
||||
end,
|
||||
Caps = read_caps(Els),
|
||||
{CapsUpdated, NewRs} = case Caps of
|
||||
nothing when Insert == true -> {false, Rs};
|
||||
_ when Insert == true ->
|
||||
case gb_trees:lookup(LFrom, Rs) of
|
||||
{value, Caps} -> {false, Rs};
|
||||
none ->
|
||||
{true,
|
||||
gb_trees:insert(LFrom, Caps,
|
||||
Rs)};
|
||||
_ ->
|
||||
{true,
|
||||
gb_trees:update(LFrom, Caps, Rs)}
|
||||
end;
|
||||
_ -> {false, gb_trees:delete_any(LFrom, Rs)}
|
||||
end,
|
||||
if CapsUpdated ->
|
||||
ejabberd_hooks:run(caps_update, To#jid.lserver,
|
||||
[From, To,
|
||||
get_features(To#jid.lserver, Caps)]);
|
||||
true -> ok
|
||||
end,
|
||||
NewRs = case Caps of
|
||||
nothing when Insert == true -> Rs;
|
||||
_ when Insert == true ->
|
||||
case gb_trees:lookup(LFrom, Rs) of
|
||||
{value, Caps} -> Rs;
|
||||
none ->
|
||||
ejabberd_hooks:run(caps_add, To#jid.lserver,
|
||||
[From, To,
|
||||
get_features(To#jid.lserver, Caps)]),
|
||||
gb_trees:insert(LFrom, Caps, Rs);
|
||||
_ ->
|
||||
ejabberd_hooks:run(caps_update, To#jid.lserver,
|
||||
[From, To,
|
||||
get_features(To#jid.lserver, Caps)]),
|
||||
gb_trees:update(LFrom, Caps, Rs)
|
||||
end;
|
||||
_ -> gb_trees:delete_any(LFrom, Rs)
|
||||
end,
|
||||
ejabberd_c2s:set_aux_field(caps_resources, NewRs,
|
||||
C2SState);
|
||||
true -> C2SState
|
||||
@@ -752,3 +752,11 @@ import_next(LServer, DBType, NodePair) ->
|
||||
ok
|
||||
end,
|
||||
import_next(LServer, DBType, ets:next(caps_features_tmp, NodePair)).
|
||||
|
||||
mod_opt_type(cache_life_time) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
mod_opt_type(cache_size) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(_) ->
|
||||
[cache_life_time, cache_size, db_type].
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
-module (mod_carboncopy).
|
||||
-author ('ecestari@process-one.net').
|
||||
-protocol({xep, 280, '0.8'}).
|
||||
|
||||
-behavior(gen_mod).
|
||||
|
||||
@@ -33,13 +34,9 @@
|
||||
-export([start/2,
|
||||
stop/1]).
|
||||
|
||||
%% Hooks:
|
||||
-export([user_send_packet/3,
|
||||
user_receive_packet/4,
|
||||
iq_handler2/3,
|
||||
iq_handler1/3,
|
||||
remove_connection/4,
|
||||
is_carbon_copy/1]).
|
||||
-export([user_send_packet/4, user_receive_packet/5,
|
||||
iq_handler2/3, iq_handler1/3, remove_connection/4,
|
||||
is_carbon_copy/1, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -127,10 +124,10 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children
|
||||
iq_handler(_From, _To, IQ, _CC)->
|
||||
IQ#iq{type=error, sub_el = [?ERR_NOT_ALLOWED]}.
|
||||
|
||||
user_send_packet(From, To, Packet) ->
|
||||
user_send_packet(Packet, _C2SState, From, To) ->
|
||||
check_and_forward(From, To, Packet, sent).
|
||||
|
||||
user_receive_packet(JID, _From, To, Packet) ->
|
||||
user_receive_packet(Packet, _C2SState, JID, _From, To) ->
|
||||
check_and_forward(JID, To, Packet, received).
|
||||
|
||||
% verifier si le trafic est local
|
||||
@@ -145,14 +142,15 @@ check_and_forward(JID, To, Packet, Direction)->
|
||||
true ->
|
||||
case is_carbon_copy(Packet) of
|
||||
false ->
|
||||
send_copies(JID, To, Packet, Direction);
|
||||
send_copies(JID, To, Packet, Direction),
|
||||
Packet;
|
||||
true ->
|
||||
%% stop the hook chain, we don't want mod_logdb to register
|
||||
%% this message (duplicate)
|
||||
stop
|
||||
{stop, Packet}
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
Packet
|
||||
end.
|
||||
|
||||
remove_connection(User, Server, Resource, _Status)->
|
||||
@@ -287,3 +285,6 @@ is_chat_or_normal_message(_Packet) -> false.
|
||||
list(User, Server)->
|
||||
mnesia:dirty_select(?TABLE, [{#carboncopy{us = {User, Server}, resource = '$2', version = '$3'}, [], [{{'$2','$3'}}]}]).
|
||||
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [iqdisc].
|
||||
|
||||
@@ -25,11 +25,14 @@
|
||||
|
||||
-module(mod_client_state).
|
||||
-author('holger@zedat.fu-berlin.de').
|
||||
-protocol({xep, 85, '2.1'}).
|
||||
-protocol({xep, 352, '0.1'}).
|
||||
|
||||
-behavior(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, add_stream_feature/2, filter_presence/2,
|
||||
filter_chat_states/2]).
|
||||
-export([start/2, stop/1, add_stream_feature/2,
|
||||
filter_presence/2, filter_chat_states/2,
|
||||
mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -107,3 +110,13 @@ filter_chat_states(_Action, #xmlel{name = <<"message">>} = Stanza) ->
|
||||
{stop, send}
|
||||
end;
|
||||
filter_chat_states(Action, _Stanza) -> Action.
|
||||
|
||||
mod_opt_type(drop_chat_states) ->
|
||||
fun (true) -> true;
|
||||
(false) -> false
|
||||
end;
|
||||
mod_opt_type(queue_presence) ->
|
||||
fun (true) -> true;
|
||||
(false) -> false
|
||||
end;
|
||||
mod_opt_type(_) -> [drop_chat_states, queue_presence].
|
||||
|
||||
@@ -23,20 +23,19 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%% Implements most of XEP-0133: Service Administration Version 1.1
|
||||
%%% (2005-08-19)
|
||||
|
||||
-module(mod_configure).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 133, '1.1'}).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, get_local_identity/5,
|
||||
get_local_features/5, get_local_items/5,
|
||||
adhoc_local_items/4, adhoc_local_commands/4,
|
||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||
adhoc_sm_items/4, adhoc_sm_commands/4]).
|
||||
adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -2023,14 +2022,14 @@ stop_node(From, Host, ENode, Action, XData) ->
|
||||
#xmlel{name = <<"x">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>,
|
||||
<<"jabber:x:data">>},
|
||||
?NS_XDATA},
|
||||
{<<"type">>, <<"submit">>}],
|
||||
children = SubEls},
|
||||
others =
|
||||
[#xmlel{name = <<"x">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>,
|
||||
<<"jabber:x:data">>},
|
||||
?NS_XDATA},
|
||||
{<<"type">>, <<"submit">>}],
|
||||
children = SubEls}]},
|
||||
To = jlib:make_jid(<<"">>, Host, <<"">>),
|
||||
@@ -2149,3 +2148,5 @@ set_sm_form(User, Server, <<"config">>,
|
||||
end;
|
||||
set_sm_form(_User, _Server, _Node, _Request, _Fields) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
mod_opt_type(_) -> [].
|
||||
|
||||
@@ -25,11 +25,14 @@
|
||||
|
||||
-module(mod_configure2).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, process_local_iq/3]).
|
||||
-export([start/2, stop/1, process_local_iq/3,
|
||||
mod_opt_type/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -195,3 +198,21 @@ process_get(#xmlel{name = <<"last">>, attrs = Attrs}) ->
|
||||
%%process_get({xmlelement, Name, Attrs, SubEls}) ->
|
||||
%% {result, };
|
||||
process_get(_) -> {error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [iqdisc].
|
||||
|
||||
opt_type(registration_watchers) ->
|
||||
fun (JIDs) when is_list(JIDs) ->
|
||||
lists:map(fun (J) ->
|
||||
#xmlel{name = <<"jid">>, attrs = [],
|
||||
children =
|
||||
[{xmlcdata, iolist_to_binary(J)}]}
|
||||
end,
|
||||
JIDs)
|
||||
end;
|
||||
opt_type(welcome_message) ->
|
||||
fun ({Subj, Body}) ->
|
||||
{iolist_to_binary(Subj), iolist_to_binary(Body)}
|
||||
end;
|
||||
opt_type(_) -> [registration_watchers, welcome_message].
|
||||
|
||||