* src/aclocal.m4: Updated for zlib support

* src/configure.ac: Likewise

* src/mod_muc/mod_muc_room.erl: Weakened presence filtering, added
warning in non-anonymous rooms, room destroying updated to latest
JEP-0045, added a number of occupants and room name in room's
disco#info reply, miscellaneous internal changes (thanks to Sergei
Golovan)

* src/mod_muc/mod_muc.erl: Better support for nick unregistration
(thanks to Sergei Golovan)

* src/ejabberd_zlib/ejabberd_zlib.erl: Zlib support (thanks to
Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib_drv.c: Likewise
* src/ejabberd_zlib/Makefile.in: Likewise
* src/ejabberd_c2s.erl: Stream compression support (JEP-0138)
* src/ejabberd_receiver.erl: Likewise

* src/mod_disco.erl: Don't split node name before calling hooks
(thanks to Sergei Golovan)

* src/mod_configure.erl: Support for configuration using ad-hoc
commands (thanks to Sergei Golovan)

* src/mod_announce.erl: Support for sending announce messages
using ad-hoc commands (thanks to Sergei Golovan)

* src/mod_adhoc.erl: Ad-hoc support (JEP-0050) (thanks to Magnus
Henoch)
* src/adhoc.erl: Likewise
* src/adhoc.hrl: Likewise

* src/jlib.hrl: Updated (thanks to Sergei Golovan)

* src/gen_mod.erl: Added function is_loaded/2 (thanks to Sergei
Golovan)

* src/ejabberd_service.erl: Changed error message on handshake
error (thanks to Sergei Golovan)

* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)

SVN Revision: 486
This commit is contained in:
Alexey Shchepin 2006-01-19 02:17:31 +00:00
parent c7bafe0056
commit 568909d5bb
22 changed files with 2264 additions and 501 deletions

View File

@ -1,4 +1,49 @@
2006-01-13 Mickaël Rémond <mickael.remond@process-one.net>
2006-01-19 Alexey Shchepin <alexey@sevcom.net>
* src/aclocal.m4: Updated for zlib support
* src/configure.ac: Likewise
* src/mod_muc/mod_muc_room.erl: Weakened presence filtering, added
warning in non-anonymous rooms, room destroying updated to latest
JEP-0045, added a number of occupants and room name in room's
disco#info reply, miscellaneous internal changes (thanks to Sergei
Golovan)
* src/mod_muc/mod_muc.erl: Better support for nick unregistration
(thanks to Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib.erl: Zlib support (thanks to
Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib_drv.c: Likewise
* src/ejabberd_zlib/Makefile.in: Likewise
* src/ejabberd_c2s.erl: Stream compression support (JEP-0138)
* src/ejabberd_receiver.erl: Likewise
* src/mod_disco.erl: Don't split node name before calling hooks
(thanks to Sergei Golovan)
* src/mod_configure.erl: Support for configuration using ad-hoc
commands (thanks to Sergei Golovan)
* src/mod_announce.erl: Support for sending announce messages
using ad-hoc commands (thanks to Sergei Golovan)
* src/mod_adhoc.erl: Ad-hoc support (JEP-0050) (thanks to Magnus
Henoch)
* src/adhoc.erl: Likewise
* src/adhoc.hrl: Likewise
* src/jlib.hrl: Updated (thanks to Sergei Golovan)
* src/gen_mod.erl: Added function is_loaded/2 (thanks to Sergei
Golovan)
* src/ejabberd_service.erl: Changed error message on handshake
error (thanks to Sergei Golovan)
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
2006-01-13 Mickael Remond <mickael.remond@process-one.net>
* src/odbc/ejabberd_odbc.erl: underscore and percent are now only
escaped in like queries. MySQL where not escaping those escaped

View File

@ -23,7 +23,7 @@ endif
prefix = @prefix@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
ERLSHLIBS = expat_erl.so
SOURCES = $(wildcard *.erl)
BEAMS = $(SOURCES:.erl=.beam)

31
src/aclocal.m4 vendored
View File

@ -29,6 +29,37 @@ AC_DEFUN(AM_WITH_EXPAT,
AC_SUBST(EXPAT_LIBS)
])
AC_DEFUN(AM_WITH_ZLIB,
[ AC_ARG_WITH(zlib,
[ --with-zlib=PREFIX prefix where zlib is installed])
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
AC_CHECK_LIB(z, gzgets,
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes ],
[ zlib_found=no ],
"$ZLIB_LIBS")
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find the zlib library])
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find zlib.h])
fi
CFLAGS="$zlib_save_CFLAGS"
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
])
AC_DEFUN(AM_WITH_ERLANG,
[ AC_ARG_WITH(erlang,
[ --with-erlang=PREFIX path to erlc and erl ])

111
src/adhoc.erl Normal file
View File

@ -0,0 +1,111 @@
%%%----------------------------------------------------------------------
%%% File : adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision$ ').
-export([parse_request/1,
produce_response/2,
produce_response/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
%% Parse an ad-hoc request. Return either an adhoc_request record or
%% an {error, ErrorType} tuple.
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
Node = xml:get_tag_attr_s("node", SubEl),
SessionID = xml:get_tag_attr_s("sessionid", SubEl),
Action = xml:get_tag_attr_s("action", SubEl),
XData = find_xdata_el(SubEl),
{xmlelement, _, _, AllEls} = SubEl,
if XData ->
Others = lists:delete(XData, AllEls);
true ->
Others = AllEls
end,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData,
others = Others};
parse_request(_) ->
{error, ?ERR_BAD_REQUEST}.
%% Borrowed from mod_vcard.erl
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
find_xdata_el1([]) ->
false;
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_XDATA ->
{xmlelement, Name, Attrs, SubEls};
_ ->
find_xdata_el1(Els)
end;
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).
%% Produce a <command/> node to use as response from an adhoc_response
%% record, filling in values for language, node and session id from
%% the request.
produce_response(#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID},
Response) ->
produce_response(Response#adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID}).
%% Produce a <command/> node to use as response from an adhoc_response
%% record.
produce_response(#adhoc_response{lang = _Lang,
node = Node,
sessionid = ProvidedSessionID,
status = Status,
defaultaction = DefaultAction,
actions = Actions,
notes = Notes,
elements = Elements}) ->
SessionID = if is_list(ProvidedSessionID), ProvidedSessionID /= "" ->
ProvidedSessionID;
true ->
jlib:now_to_utc_string(now())
end,
case Actions of
[] ->
ActionsEls = [];
_ ->
case DefaultAction of
"" ->
ActionsElAttrs = [];
_ ->
ActionsElAttrs = [{"execute", DefaultAction}]
end,
ActionsEls = [{xmlelement, "actions",
ActionsElAttrs,
[{xmlelement, Action, [], []} || Action <- Actions]}]
end,
NotesEls = lists:map(fun({Type, Text}) ->
{xmlelement, "note",
[{"type", Type}],
[{xmlcdata, Text}]}
end, Notes),
{xmlelement, "command",
[{"xmlns", ?NS_COMMANDS},
{"sessionid", SessionID},
{"node", Node},
{"status", atom_to_list(Status)}],
ActionsEls ++ NotesEls ++ Elements}.

15
src/adhoc.hrl Normal file
View File

@ -0,0 +1,15 @@
-record(adhoc_request, {lang,
node,
sessionid,
action,
xdata,
others}).
-record(adhoc_response, {lang,
node,
sessionid,
status,
defaultaction = "",
actions = [],
notes = [],
elements = []}).

286
src/configure vendored
View File

@ -310,7 +310,7 @@ ac_includes_default="\
# include <unistd.h>
#endif"
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE ERLC ac_pt_ERLC ERL ac_pt_ERL ERLANG_CFLAGS ERLANG_LIBS LIBICONV CPP EGREP EXPAT_CFLAGS EXPAT_LIBS LIBOBJS mod_pubsub make_mod_pubsub mod_irc make_mod_irc mod_muc make_mod_muc eldap make_eldap web make_web tls make_tls odbc make_odbc roster_gateway_workaround SSL_LIBS SSL_CFLAGS LTLIBOBJS'
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE ERLC ac_pt_ERLC ERL ac_pt_ERL ERLANG_CFLAGS ERLANG_LIBS LIBICONV CPP EGREP EXPAT_CFLAGS EXPAT_LIBS ZLIB_CFLAGS ZLIB_LIBS LIBOBJS mod_pubsub make_mod_pubsub mod_irc make_mod_irc mod_muc make_mod_muc eldap make_eldap web make_web tls make_tls odbc make_odbc ejabberd_zlib make_ejabberd_zlib roster_gateway_workaround SSL_LIBS SSL_CFLAGS LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@ -850,6 +850,7 @@ Optional Features:
--enable-web enable web (default: yes)
--enable-tls enable tls (default: yes)
--enable-odbc enable odbc (default: no)
--enable-ejabberd_zlib enable ejabberd_zlib (default: yes)
--enable-roster-gateway-workaround Turn on workaround for processing gateway subscriptions
Optional Packages:
@ -858,6 +859,7 @@ Optional Packages:
--with-erlang=PREFIX path to erlc and erl
--with-libiconv-prefix=PREFIX prefix where libiconv is installed
--with-expat=PREFIX prefix where EXPAT is installed
--with-zlib=PREFIX prefix where zlib is installed
--with-openssl=PREFIX prefix where OPENSSL is installed
Some influential environment variables:
@ -3563,6 +3565,259 @@ echo "$as_me: error: Could not find expat.h" >&2;}
#locating zlib
# Check whether --with-zlib or --without-zlib was given.
if test "${with_zlib+set}" = set; then
withval="$with_zlib"
fi;
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
echo "$as_me:$LINENO: checking for gzgets in -lz" >&5
echo $ECHO_N "checking for gzgets in -lz... $ECHO_C" >&6
if test "${ac_cv_lib_z_gzgets+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz "$ZLIB_LIBS" $LIBS"
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Override any gcc2 internal prototype to avoid an error. */
#ifdef __cplusplus
extern "C"
#endif
/* We use char because int might match the return type of a gcc2
builtin and then its argument prototype would still apply. */
char gzgets ();
int
main ()
{
gzgets ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; } &&
{ ac_try='test -s conftest$ac_exeext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_cv_lib_z_gzgets=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_lib_z_gzgets=no
fi
rm -f conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
echo "$as_me:$LINENO: result: $ac_cv_lib_z_gzgets" >&5
echo "${ECHO_T}$ac_cv_lib_z_gzgets" >&6
if test $ac_cv_lib_z_gzgets = yes; then
ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes
else
zlib_found=no
fi
if test $zlib_found = no; then
{ { echo "$as_me:$LINENO: error: Could not find the zlib library" >&5
echo "$as_me: error: Could not find the zlib library" >&2;}
{ (exit 1); exit 1; }; }
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
for ac_header in zlib.h
do
as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
if eval "test \"\${$as_ac_Header+set}\" = set"; then
echo "$as_me:$LINENO: checking for $ac_header" >&5
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
if eval "test \"\${$as_ac_Header+set}\" = set"; then
echo $ECHO_N "(cached) $ECHO_C" >&6
fi
echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
else
# Is the header compilable?
echo "$as_me:$LINENO: checking $ac_header usability" >&5
echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
$ac_includes_default
#include <$ac_header>
_ACEOF
rm -f conftest.$ac_objext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; } &&
{ ac_try='test -s conftest.$ac_objext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_header_compiler=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_compiler=no
fi
rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
echo "${ECHO_T}$ac_header_compiler" >&6
# Is the header present?
echo "$as_me:$LINENO: checking $ac_header presence" >&5
echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <$ac_header>
_ACEOF
if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
(eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } >/dev/null; then
if test -s conftest.err; then
ac_cpp_err=$ac_c_preproc_warn_flag
ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
else
ac_cpp_err=
fi
else
ac_cpp_err=yes
fi
if test -z "$ac_cpp_err"; then
ac_header_preproc=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_preproc=no
fi
rm -f conftest.err conftest.$ac_ext
echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
echo "${ECHO_T}$ac_header_preproc" >&6
# So? What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
yes:no: )
{ echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
ac_header_preproc=yes
;;
no:yes:* )
{ echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
{ echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
(
cat <<\_ASBOX
## --------------------------------- ##
## Report this to BUG-REPORT-ADDRESS ##
## --------------------------------- ##
_ASBOX
) |
sed "s/^/$as_me: WARNING: /" >&2
;;
esac
echo "$as_me:$LINENO: checking for $ac_header" >&5
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
if eval "test \"\${$as_ac_Header+set}\" = set"; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
eval "$as_ac_Header=\$ac_header_preproc"
fi
echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
fi
if test `eval echo '${'$as_ac_Header'}'` = yes; then
cat >>confdefs.h <<_ACEOF
#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
else
zlib_found=no
fi
done
if test $zlib_found = no; then
{ { echo "$as_me:$LINENO: error: Could not find zlib.h" >&5
echo "$as_me: error: Could not find zlib.h" >&2;}
{ (exit 1); exit 1; }; }
fi
CFLAGS="$zlib_save_CFLAGS"
# Checks for typedefs, structures, and compiler characteristics.
echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
@ -4224,6 +4479,28 @@ echo "${ECHO_T}$mr_enable_odbc" >&6
ejabberd_zlib=
make_ejabberd_zlib=
echo "$as_me:$LINENO: checking whether build ejabberd_zlib" >&5
echo $ECHO_N "checking whether build ejabberd_zlib... $ECHO_C" >&6
# Check whether --enable-ejabberd_zlib or --disable-ejabberd_zlib was given.
if test "${enable_ejabberd_zlib+set}" = set; then
enableval="$enable_ejabberd_zlib"
mr_enable_ejabberd_zlib="$enableval"
else
mr_enable_ejabberd_zlib=yes
fi;
if test "$mr_enable_ejabberd_zlib" = "yes"; then
ejabberd_zlib=ejabberd_zlib
make_ejabberd_zlib=ejabberd_zlib/Makefile
fi
echo "$as_me:$LINENO: result: $mr_enable_ejabberd_zlib" >&5
echo "${ECHO_T}$mr_enable_ejabberd_zlib" >&6
# Check whether --enable-roster_gateway_workaround or --disable-roster_gateway_workaround was given.
if test "${enable_roster_gateway_workaround+set}" = set; then
enableval="$enable_roster_gateway_workaround"
@ -4239,7 +4516,7 @@ else
fi;
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls $make_odbc"
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls $make_odbc $make_ejabberd_zlib"
#openssl
@ -5056,6 +5333,7 @@ do
"stringprep/Makefile" ) CONFIG_FILES="$CONFIG_FILES stringprep/Makefile" ;;
"$make_tls" ) CONFIG_FILES="$CONFIG_FILES $make_tls" ;;
"$make_odbc" ) CONFIG_FILES="$CONFIG_FILES $make_odbc" ;;
"$make_ejabberd_zlib" ) CONFIG_FILES="$CONFIG_FILES $make_ejabberd_zlib" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
{ (exit 1); exit 1; }; };;
@ -5158,6 +5436,8 @@ s,@CPP@,$CPP,;t t
s,@EGREP@,$EGREP,;t t
s,@EXPAT_CFLAGS@,$EXPAT_CFLAGS,;t t
s,@EXPAT_LIBS@,$EXPAT_LIBS,;t t
s,@ZLIB_CFLAGS@,$ZLIB_CFLAGS,;t t
s,@ZLIB_LIBS@,$ZLIB_LIBS,;t t
s,@LIBOBJS@,$LIBOBJS,;t t
s,@mod_pubsub@,$mod_pubsub,;t t
s,@make_mod_pubsub@,$make_mod_pubsub,;t t
@ -5173,6 +5453,8 @@ s,@tls@,$tls,;t t
s,@make_tls@,$make_tls,;t t
s,@odbc@,$odbc,;t t
s,@make_odbc@,$make_odbc,;t t
s,@ejabberd_zlib@,$ejabberd_zlib,;t t
s,@make_ejabberd_zlib@,$make_ejabberd_zlib,;t t
s,@roster_gateway_workaround@,$roster_gateway_workaround,;t t
s,@SSL_LIBS@,$SSL_LIBS,;t t
s,@SSL_CFLAGS@,$SSL_CFLAGS,;t t

View File

@ -14,6 +14,8 @@ AM_WITH_ERLANG
AM_ICONV
#locating libexpat
AM_WITH_EXPAT
#locating zlib
AM_WITH_ZLIB
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@ -29,6 +31,7 @@ AC_MOD_ENABLE(eldap, yes)
AC_MOD_ENABLE(web, yes)
AC_MOD_ENABLE(tls, yes)
AC_MOD_ENABLE(odbc, no)
AC_MOD_ENABLE(ejabberd_zlib, yes)
AC_ARG_ENABLE(roster_gateway_workaround,
[ --enable-roster-gateway-workaround Turn on workaround for processing gateway subscriptions],
@ -47,7 +50,8 @@ AC_CONFIG_FILES([Makefile
$make_web
stringprep/Makefile
$make_tls
$make_odbc])
$make_odbc
$make_ejabberd_zlib])
#openssl
AM_WITH_OPENSSL
AC_OUTPUT

View File

@ -134,13 +134,14 @@
{mod_register, [{access, register}]},
{mod_roster, []},
{mod_privacy, []},
{mod_configure, []},
{mod_adhoc, []},
{mod_configure, []}, % Depends on mod_adhoc
{mod_configure2, []},
{mod_disco, []},
{mod_stats, []},
{mod_vcard, []},
{mod_offline, []},
{mod_announce, [{access, announce}]},
{mod_announce, [{access, announce}]}, % Depends on mod_adhoc
{mod_echo, [{host, "echo.localhost"}]},
{mod_private, []},
{mod_irc, []},

View File

@ -46,6 +46,7 @@
sasl_state,
access,
shaper,
zlib = false,
tls = false,
tls_required = false,
tls_enabled = false,
@ -125,6 +126,7 @@ init([{SockMod, Socket}, Opts]) ->
{value, {_, S}} -> S;
_ -> none
end,
Zlib = lists:member(zlib, Opts),
StartTLS = lists:member(starttls, Opts),
StartTLSRequired = lists:member(starttls_required, Opts),
TLSEnabled = lists:member(tls, Opts),
@ -145,6 +147,7 @@ init([{SockMod, Socket}, Opts]) ->
{ok, wait_for_stream, #state{socket = Socket1,
sockmod = SockMod1,
receiver = ReceiverPid,
zlib = Zlib,
tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled,
@ -200,10 +203,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{xmlelement, "mechanism", [],
[{xmlcdata, S}]}
end, cyrsasl:listmech(Server)),
SockMod = StateData#state.sockmod,
Zlib = StateData#state.zlib,
CompressFeature =
case Zlib andalso
(SockMod /= ejabberd_zlib) of
true ->
[{xmlelement, "compression",
[{"xmlns", ?NS_FEATURE_COMPRESS}],
[{xmlelement, "method",
[], [{xmlcdata, "zlib"}]}]}];
_ ->
[]
end,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
SockMod = StateData#state.sockmod,
TLSFeature =
case (TLS == true) andalso
(TLSEnabled == false) andalso
@ -224,7 +239,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
end,
send_element(StateData,
{xmlelement, "stream:features", [],
TLSFeature ++
TLSFeature ++ CompressFeature ++
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL}],
Mechs}] ++
@ -337,7 +352,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
end,
send_element(StateData, Res),
{next_state, wait_for_auth, StateData};
{auth, _ID, set, {U, P, D, ""}} ->
{auth, _ID, set, {_U, _P, _D, ""}} ->
Err = jlib:make_error_reply(
El,
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
@ -434,6 +449,7 @@ wait_for_auth(closed, StateData) ->
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
Zlib = StateData#state.zlib,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
@ -497,6 +513,19 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
streamid = new_id(),
tls_enabled = true
}};
{?NS_COMPRESS, "compress"} when Zlib == true,
SockMod /= ejabberd_zlib ->
Socket = StateData#state.socket,
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(SockMod, Socket),
ejabberd_receiver:compress(StateData#state.receiver, ZlibSocket),
send_element(StateData,
{xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []}),
{next_state, wait_for_stream,
StateData#state{sockmod = ejabberd_zlib,
socket = ZlibSocket,
streamid = new_id()
}};
_ ->
if
(SockMod == gen_tcp) and TLSRequired ->

View File

@ -17,6 +17,7 @@
change_shaper/2,
reset_stream/1,
starttls/2,
compress/2,
become_controller/1,
close/1]).
@ -54,6 +55,9 @@ reset_stream(Pid) ->
starttls(Pid, TLSSocket) ->
gen_server:call(Pid, {starttls, TLSSocket}).
compress(Pid, ZlibSocket) ->
gen_server:call(Pid, {compress, ZlibSocket}).
become_controller(Pid) ->
gen_server:call(Pid, become_controller).
@ -110,6 +114,20 @@ handle_call({starttls, TLSSocket}, _From,
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call({compress, ZlibSocket}, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid} = State) ->
xml_stream:close(XMLStreamState),
NewXMLStreamState = xml_stream:new(C2SPid),
NewState = State#state{socket = ZlibSocket,
sock_mod = ejabberd_zlib,
xml_stream_state = NewXMLStreamState},
case ejabberd_zlib:recv_data(ZlibSocket, "") of
{ok, ZlibData} ->
{reply, ok, process_data(ZlibData, NewState)};
{error, _Reason} ->
{stop, normal, ok, NewState}
end;
handle_call(reset_stream, _From,
#state{xml_stream_state = XMLStreamState,
c2s_pid = C2SPid} = State) ->
@ -157,6 +175,13 @@ handle_info({Tag, _TCPSocket, Data},
{error, _Reason} ->
{stop, normal, State}
end;
ejabberd_zlib ->
case ejabberd_zlib:recv_data(Socket, Data) of
{ok, ZlibData} ->
{noreply, process_data(ZlibData, State)};
{error, _Reason} ->
{stop, normal, State}
end;
_ ->
{noreply, process_data(Data, State)}
end;
@ -211,7 +236,6 @@ activate_socket(#state{socket = Socket,
SockMod:setopts(Socket, [{active, once}])
end.
process_data(Data,
#state{xml_stream_state = XMLStreamState,
shaper_state = ShaperState} = State) ->

View File

@ -179,7 +179,7 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
end, StateData#state.hosts),
{next_state, stream_established, StateData};
_ ->
send_text(StateData, ?INVALID_HEADER_ERR),
send_text(StateData, ?INVALID_HANDSHAKE_ERR),
{stop, normal, StateData}
end;
_ ->

View File

@ -0,0 +1,38 @@
# $Id$
CC = @CC@
CFLAGS = @CFLAGS@ @ZLIB_CFLAGS@ @ERLANG_CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ @ZLIB_LIBS@ @ERLANG_LIBS@
SUBDIRS =
ERLSHLIBS = ../ejabberd_zlib_drv.so
OUTDIR = ..
EFLAGS = -I .. -pz ..
OBJS = \
$(OUTDIR)/ejabberd_zlib.beam
all: $(OBJS) $(ERLSHLIBS)
$(OUTDIR)/%.beam: %.erl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
#all: $(ERLSHLIBS)
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
$(ERLSHLIBS): ../%.so: %.c
$(CC) -Wall $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
-o $@ -fpic -shared
clean:
rm -f $(OBJS) $(ERLSHLIBS)
distclean: clean
rm -f Makefile
TAGS:
etags *.erl

View File

@ -0,0 +1,134 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_zlib.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Interface to zlib
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(ejabberd_zlib).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-behaviour(gen_server).
-export([start/0, start_link/0,
enable_zlib/2, disable_zlib/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
controlling_process/2,
close/1]).
%% Internal exports, call-back functions.
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2]).
-define(DEFLATE, 1).
-define(INFLATE, 2).
-record(zlibsock, {sockmod, socket, zlibport}).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, Port}.
%%% --------------------------------------------------------
%%% The call-back functions.
%%% --------------------------------------------------------
handle_call(_, _, State) ->
{noreply, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Port, Reason}, Port) ->
{stop, {port_died, Reason}, Port};
handle_info({'EXIT', _Pid, _Reason}, Port) ->
{noreply, Port};
handle_info(_, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, Port) ->
Port ! {self, close},
ok.
enable_zlib(SockMod, Socket) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
port_close(Port),
{SockMod, Socket}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).
recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock,
Length, Timeout) ->
case SockMod:recv(Socket, Length, Timeout) of
{ok, Packet} ->
recv_data(ZlibSock, Packet);
{error, _Reason} = Error ->
Error
end.
recv_data(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
case port_control(Port, ?INFLATE, Packet) of
<<0, In/binary>> ->
{ok, In};
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
Packet) ->
case port_control(Port, ?DEFLATE, Packet) of
<<0, Out/binary>> ->
SockMod:send(Socket, Out);
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, Opts);
_ ->
SockMod:setopts(Socket, Opts)
end.
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) ->
SockMod:controlling_process(Socket, Pid).
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
SockMod:close(Socket),
port_close(Port).

View File

@ -0,0 +1,171 @@
/* $Id$ */
#include <stdio.h>
#include <string.h>
#include <erl_driver.h>
#include <zlib.h>
#define BUF_SIZE 1024
typedef struct {
ErlDrvPort port;
z_stream *d_stream;
z_stream *i_stream;
} ejabberd_zlib_data;
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
{
ejabberd_zlib_data *d =
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
d->port = port;
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
d->d_stream->zalloc = (alloc_func)0;
d->d_stream->zfree = (free_func)0;
d->d_stream->opaque = (voidpf)0;
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
d->i_stream->zalloc = (alloc_func)0;
d->i_stream->zfree = (free_func)0;
d->i_stream->opaque = (voidpf)0;
inflateInit(d->i_stream);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)d;
}
static void ejabberd_zlib_drv_stop(ErlDrvData handle)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
deflateEnd(d->d_stream);
free(d->d_stream);
inflateEnd(d->i_stream);
free(d->i_stream);
driver_free((char *)handle);
}
#define DEFLATE 1
#define INFLATE 2
#define die_unless(cond, errstr) \
if (!(cond)) \
{ \
rlen = strlen(errstr) + 1; \
b = driver_realloc_binary(b, rlen); \
b->orig_bytes[0] = 1; \
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
*rbuf = (char *)b; \
return rlen; \
}
static int ejabberd_zlib_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
int err;
int size;
ErlDrvBinary *b;
switch (command)
{
case DEFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
d->d_stream->next_in = buf;
d->d_stream->avail_in = len;
d->d_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->d_stream->avail_out == 0)
{
d->d_stream->next_out = b->orig_bytes + rlen;
d->d_stream->avail_out = BUF_SIZE;
err = deflate(d->d_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Deflate error");
rlen += (BUF_SIZE - d->d_stream->avail_out);
size += (BUF_SIZE - d->d_stream->avail_out);
b = driver_realloc_binary(b, size);
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
case INFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
if (len > 0) {
d->i_stream->next_in = buf;
d->i_stream->avail_in = len;
d->i_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->i_stream->avail_out == 0)
{
d->i_stream->next_out = b->orig_bytes + rlen;
d->i_stream->avail_out = BUF_SIZE;
err = inflate(d->i_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Inflate error");
rlen += (BUF_SIZE - d->i_stream->avail_out);
size += (BUF_SIZE - d->i_stream->avail_out);
b = driver_realloc_binary(b, size);
}
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
}
b = driver_alloc_binary(1);
b->orig_bytes[0] = 0;
*rbuf = (char *)b;
return 1;
}
ErlDrvEntry ejabberd_zlib_driver_entry = {
NULL, /* F_PTR init, N/A */
ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */
ejabberd_zlib_drv_stop, /* F_PTR stop, called when port is closed */
NULL, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* handle */
ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */
{
return &ejabberd_zlib_driver_entry;
}

View File

@ -19,7 +19,8 @@
loaded_modules/1,
loaded_modules_with_opts/1,
get_hosts/2,
get_module_proc/2]).
get_module_proc/2,
is_loaded/2]).
-export([behaviour_info/1]).
@ -144,3 +145,6 @@ get_hosts(Opts, Prefix) ->
get_module_proc(Host, Base) ->
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).

View File

@ -33,6 +33,7 @@
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
@ -48,6 +49,9 @@
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
% TODO: remove "code" attribute (currently it used for backward-compatibility)
-define(STANZA_ERROR(Code, Type, Condition),

250
src/mod_adhoc.erl Normal file
View File

@ -0,0 +1,250 @@
%%%----------------------------------------------------------------------
%%% File : mod_adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Handle incoming ad-doc requests (JEP-0050)
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(mod_adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision$ ').
-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]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS,
?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 99),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 99),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_commands, 99),
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, ping_item, 100),
ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, ping_command, 100).
stop(Host) ->
ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, ping_command, 100),
ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, ping_item, 100),
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_commands, 99),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 99),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 99),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS).
%-------------------------------------------------------------------------
get_local_commands(Acc, _From, #jid{server = Server, lserver = LServer} = _To, "", Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
case Display of
false ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [{xmlelement,
"item",
[{"jid", Server},
{"node", ?NS_COMMANDS},
{"name", translate:translate(Lang, "Commands")}],
[]}],
{result, Items ++ Nodes}
end;
get_local_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
ejabberd_hooks:run_fold(adhoc_local_items, LServer, {result, []}, [From, To, Lang]);
get_local_commands(_Acc, _From, _To, "ping", _Lang) ->
{result, []};
get_local_commands(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_sm_commands(Acc, _From, #jid{lserver = LServer} = To, "", Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
case Display of
false ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [{xmlelement,
"item",
[{"jid", jlib:jid_to_string(To)},
{"node", ?NS_COMMANDS},
{"name", translate:translate(Lang, "Commands")}],
[]}],
{result, Items ++ Nodes}
end;
get_sm_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
ejabberd_hooks:run_fold(adhoc_sm_items, LServer, {result, []}, [From, To, Lang]);
get_sm_commands(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
%% On disco info request to the ad-hoc node, return automation/command-list.
get_local_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-list"},
{"name", translate:translate(Lang, "Commands")}], []} | Acc];
get_local_identity(Acc, _From, _To, "ping", Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-node"},
{"name", translate:translate(Lang, "Ping")}], []} | Acc];
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
%% On disco info request to the ad-hoc node, return automation/command-list.
get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-list"},
{"name", translate:translate(Lang, "Commands")}], []} | Acc];
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_local_features(Acc, _From, _To, "", _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Feats ++ [?NS_COMMANDS]};
get_local_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
%% override all lesser features...
{result, []};
get_local_features(_Acc, _From, _To, "ping", _Lang) ->
%% override all lesser features...
{result, [?NS_COMMANDS]};
get_local_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
get_sm_features(Acc, _From, _To, "", _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Feats ++ [?NS_COMMANDS]};
get_sm_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
%% override all lesser features...
{result, []};
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
%-------------------------------------------------------------------------
process_local_iq(From, To, IQ) ->
process_adhoc_request(From, To, IQ, adhoc_local_commands).
process_sm_iq(From, To, IQ) ->
process_adhoc_request(From, To, IQ, adhoc_sm_commands).
process_adhoc_request(From, To, #iq{sub_el = SubEl} = IQ, Hook) ->
?DEBUG("About to parse ~p...", [IQ]),
case adhoc:parse_request(IQ) of
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]};
#adhoc_request{} = AdhocRequest ->
Host = To#jid.lserver,
case ejabberd_hooks:run_fold(Hook, Host, empty,
[From, To, AdhocRequest]) of
ignore ->
ignore;
empty ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]};
Command ->
IQ#iq{type = result, sub_el = [Command]}
end
end.
ping_item(Acc, _From, #jid{server = Server} = _To, Lang) ->
Items = case Acc of
{result, I} ->
I;
_ ->
[]
end,
Nodes = [{xmlelement, "item",
[{"jid", Server},
{"node", "ping"},
{"name", translate:translate(Lang, "Ping")}],
[]}],
{result, Items ++ Nodes}.
ping_command(_Acc, _From, _To,
#adhoc_request{lang = Lang,
node = "ping",
sessionid = _Sessionid,
action = Action} = Request) ->
if
Action == ""; Action == "execute" ->
adhoc:produce_response(
Request,
#adhoc_response{status = completed,
notes = [{"info", translate:translate(
Lang,
"Pong")}]});
true ->
{error, ?ERR_BAD_REQUEST}
end;
ping_command(Acc, _From, _To, _Request) ->
Acc.

View File

@ -15,10 +15,16 @@
init/0,
stop/1,
announce/3,
send_motd/1]).
send_motd/1,
disco_identity/5,
disco_features/5,
disco_items/5,
announce_commands/4,
announce_items/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
-record(motd, {server, packet}).
-record(motd_users, {us, dummy = []}).
@ -33,6 +39,11 @@ start(Host, _Opts) ->
update_tables(),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 50),
ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, announce_items, 50),
ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
ejabberd_hooks:add(user_available_hook, Host,
?MODULE, send_motd, 50),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
@ -66,6 +77,11 @@ loop() ->
end.
stop(Host) ->
ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, announce_items, 50),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50),
ejabberd_hooks:delete(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:delete(sm_register_connection_hook, Host,
@ -74,6 +90,7 @@ stop(Host) ->
exit(whereis(Proc), stop),
{wait, Proc}.
% Announcing via messages to a custom resource
announce(From, To, Packet) ->
case To of
#jid{luser = "", lresource = Res} ->
@ -105,12 +122,422 @@ announce(From, To, Packet) ->
ok
end.
%-------------------------------------------------------------------------
% Announcing via ad-hoc commands
-define(INFO_COMMAND(Lang, Node),
[{xmlelement, "identity",
[{"category", "automation"},
{"type", "command-node"},
{"name", get_title(Lang, Node)}], []}]).
disco_identity(Acc, _From, _To, Node, Lang) ->
case Node of
"announce/all" ->
?INFO_COMMAND(Lang, Node);
"announce/all-hosts/online" ->
?INFO_COMMAND(Lang, Node);
"announce/online" ->
?INFO_COMMAND(Lang, Node);
"announce/motd" ->
?INFO_COMMAND(Lang, Node);
"announce/motd/delete" ->
?INFO_COMMAND(Lang, Node);
"announce/motd/update" ->
?INFO_COMMAND(Lang, Node);
_ ->
Acc
end.
%-------------------------------------------------------------------------
-define(INFO_RESULT(Allow, Feats),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
{result, Feats}
end).
disco_features(Acc, From, #jid{lserver = LServer} = _To,
"announce", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
case {acl:match_rule(LServer, Access1, From),
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
{error, ?ERR_FORBIDDEN};
_ ->
{result, []}
end
end;
disco_features(Acc, From, #jid{lserver = LServer} = _To,
"announce/all-hosts/online", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?INFO_RESULT(Allow, [?NS_COMMANDS])
end;
disco_features(Acc, From, #jid{lserver = LServer} = _To,
Node, _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/online" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd/delete" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
"announce/motd/update" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
_ ->
Acc
end
end.
%-------------------------------------------------------------------------
-define(NODE_TO_ITEM(Lang, Server, Node),
{xmlelement, "item",
[{"jid", Server},
{"node", Node},
{"name", get_title(Lang, Node)}],
[]}).
-define(ITEMS_RESULT(Allow, Items),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
{result, Items}
end).
disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To,
"", Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
case {acl:match_rule(LServer, Access1, From),
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
Nodes = [?NODE_TO_ITEM(Lang, Server, "announce")],
{result, Items ++ Nodes}
end
end;
disco_items(Acc, From, #jid{lserver = LServer} = To, "announce", Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
announce_items(Acc, From, To, Lang)
end;
disco_items(Acc, From, #jid{lserver = LServer} = _To,
"announce/all-hosts/online", _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?ITEMS_RESULT(Allow, [])
end;
disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?ITEMS_RESULT(Allow, []);
"announce/online" ->
?ITEMS_RESULT(Allow, []);
"announce/motd" ->
?ITEMS_RESULT(Allow, []);
"announce/motd/delete" ->
?ITEMS_RESULT(Allow, []);
"announce/motd/update" ->
?ITEMS_RESULT(Allow, []);
_ ->
Acc
end
end.
%-------------------------------------------------------------------------
announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang) ->
Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Nodes1 = case acl:match_rule(LServer, Access1, From) of
allow ->
[?NODE_TO_ITEM(Lang, Server, "announce/all"),
?NODE_TO_ITEM(Lang, Server, "announce/online"),
?NODE_TO_ITEM(Lang, Server, "announce/motd"),
?NODE_TO_ITEM(Lang, Server, "announce/motd/delete"),
?NODE_TO_ITEM(Lang, Server, "announce/motd/update")];
deny ->
[]
end,
Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
Nodes2 = case acl:match_rule(global, Access2, From) of
allow ->
[?NODE_TO_ITEM(Lang, Server, "announce/all-hosts/online")];
deny ->
[]
end,
case {Nodes1, Nodes2} of
{[], []} ->
Acc;
_ ->
Items = case Acc of
{result, I} -> I;
_ -> []
end,
{result, Items ++ Nodes1 ++ Nodes2}
end.
%-------------------------------------------------------------------------
-define(COMMANDS_RESULT(Allow, From, To, Request),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
announce_commands(From, To, Request)
end).
announce_commands(_Acc, From, To,
#adhoc_request{
node = "announce/all-hosts/online"} = Request) ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
Allow = acl:match_rule(global, Access, From),
?COMMANDS_RESULT(Allow, From, To, Request);
announce_commands(Acc, From, #jid{lserver = LServer} = To,
#adhoc_request{node = Node} = Request) ->
Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
Allow = acl:match_rule(LServer, Access, From),
case Node of
"announce/all" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/online" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd/delete" ->
?COMMANDS_RESULT(Allow, From, To, Request);
"announce/motd/update" ->
?COMMANDS_RESULT(Allow, From, To, Request);
_ ->
Acc
end.
%-------------------------------------------------------------------------
announce_commands(From, To,
#adhoc_request{lang = Lang,
node = Node,
action = Action,
xdata = XData} = Request) ->
%% If the "action" attribute is not present, it is
%% understood as "execute". If there was no <actions/>
%% element in the first response (which there isn't in our
%% case), "execute" and "complete" are equivalent.
ActionIsExecute = lists:member(Action,
["", "execute", "complete"]),
if Action == "cancel" ->
%% User cancels request
adhoc:produce_response(Request,
#adhoc_response{status = canceled});
XData == false, ActionIsExecute ->
%% User requests form
adhoc:produce_response(
Request,
#adhoc_response{status = executing,
elements = [generate_adhoc_form(Lang, Node)]});
XData /= false, ActionIsExecute ->
%% User returns form.
case jlib:parse_xdata_submit(XData) of
invalid ->
{error, ?ERR_BAD_REQUEST};
Fields ->
handle_adhoc_form(From, To, Request, Fields)
end;
true ->
{error, ?ERR_BAD_REQUEST}
end.
generate_adhoc_form(Lang, Node) ->
{xmlelement, "x",
[{"xmlns", ?NS_XDATA},
{"type", "form"}],
[{xmlelement, "title", [], [{xmlcdata, get_title(Lang, Node)}]}]
++
if Node == "announce/motd/delete" ->
[{xmlelement, "field",
[{"var", "confirm"},
{"type", "boolean"},
{"label", translate:translate(Lang, "Really delete message of the day?")}],
[{xmlelement, "value",
[],
[{xmlcdata, "true"}]}]}];
true ->
[{xmlelement, "field",
[{"var", "subject"},
{"type", "text-single"},
{"label", translate:translate(Lang, "Subject")}],
[]},
{xmlelement, "field",
[{"var", "body"},
{"type", "text-multi"},
{"label", translate:translate(Lang, "Message body")}],
[]}]
end}.
join_lines([]) ->
[];
join_lines(Lines) ->
join_lines(Lines, []).
join_lines([Line|Lines], Acc) ->
join_lines(Lines, ["\n",Line|Acc]);
join_lines([], Acc) ->
%% Remove last newline
lists:flatten(lists:reverse(tl(Acc))).
handle_adhoc_form(From, #jid{lserver = LServer} = To,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID},
Fields) ->
Confirm = case lists:keysearch("confirm", 1, Fields) of
{value, {"confirm", ["true"]}} ->
true;
{value, {"confirm", ["1"]}} ->
true;
_ ->
false
end,
Subject = case lists:keysearch("subject", 1, Fields) of
{value, {"subject", SubjectLines}} ->
%% There really shouldn't be more than one
%% subject line, but can we stop them?
join_lines(SubjectLines);
_ ->
[]
end,
Body = case lists:keysearch("body", 1, Fields) of
{value, {"body", BodyLines}} ->
join_lines(BodyLines);
_ ->
[]
end,
Response = #adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID,
status = completed},
Packet = {xmlelement, "message", [{"type", "normal"}],
if Subject /= [] ->
[{xmlelement, "subject", [],
[{xmlcdata, Subject}]}];
true ->
[]
end ++
if Body /= [] ->
[{xmlelement, "body", [],
[{xmlcdata, Body}]}];
true ->
[]
end},
Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
case {Node, Body} of
{"announce/motd/delete", _} ->
if Confirm ->
Proc ! {announce_motd_delete, From, To, Packet},
adhoc:produce_response(Response);
true ->
adhoc:produce_response(Response)
end;
{_, []} ->
%% An announce message with no body is definitely an operator error.
%% Throw an error and give him/her a chance to send message again.
{error, ?ERRT_NOT_ACCEPTABLE(
Lang,
"No body provided for announce message")};
%% Now send the packet to ?PROCNAME.
%% We don't use direct announce_* functions because it
%% leads to large delay in response and <iq/> queries processing
{"announce/all", _} ->
Proc ! {announce_all, From, To, Packet},
adhoc:produce_response(Response);
{"announce/online", _} ->
Proc ! {announce_online, From, To, Packet},
adhoc:produce_response(Response);
{"announce/all-hosts/online", _} ->
Proc ! {announce_all_hosts_online, From, To, Packet},
adhoc:produce_response(Response);
{"announce/motd", _} ->
Proc ! {announce_motd, From, To, Packet},
adhoc:produce_response(Response);
{"announce/motd/update", _} ->
Proc ! {announce_motd_update, From, To, Packet},
adhoc:produce_response(Response);
_ ->
%% This can't happen, as we haven't registered any other
%% command nodes.
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
get_title(Lang, "announce") ->
translate:translate(Lang, "Announcements");
get_title(Lang, "announce/all") ->
translate:translate(Lang, "Send announcement to all users");
get_title(Lang, "announce/online") ->
translate:translate(Lang, "Send announcement to all online users");
get_title(Lang, "announce/all-hosts/online") ->
translate:translate(Lang, "Send announcement to all online users on all hosts");
get_title(Lang, "announce/motd") ->
translate:translate(Lang, "Set message of the day and send to online users");
get_title(Lang, "announce/motd/update") ->
translate:translate(Lang, "Update message of the day (don't send)");
get_title(Lang, "announce/motd/delete") ->
translate:translate(Lang, "Delete message of the day").
%-------------------------------------------------------------------------
announce_all(From, To, Packet) ->
Host = To#jid.lserver,
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
Local = jlib:make_jid("", To#jid.server, ""),
@ -126,7 +553,7 @@ announce_online(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:get_vh_session_list(Host),
@ -138,7 +565,7 @@ announce_all_hosts_online(From, To, Packet) ->
Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
case acl:match_rule(global, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
@ -159,7 +586,7 @@ announce_motd(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(To#jid.lserver, Packet),
@ -179,7 +606,7 @@ announce_motd_update(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(Host, Packet)
@ -197,7 +624,7 @@ announce_motd_delete(From, To, Packet) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
case acl:match_rule(Host, Access, From) of
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_delete(Host)
@ -237,6 +664,7 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
ok
end.
%-------------------------------------------------------------------------
update_tables() ->
update_motd_table(),
@ -326,3 +754,4 @@ update_motd_users_table() ->
?INFO_MSG("Recreating motd_users table", []),
mnesia:transform_table(motd_users, ignore, Fields)
end.

View File

@ -17,134 +17,210 @@
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,
process_local_iq/3,
process_sm_iq/3]).
adhoc_sm_items/4,
adhoc_sm_commands/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_EJABBERD_CONFIG,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG,
?MODULE, process_sm_iq, IQDisc),
start(Host, _Opts) ->
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_items, 50),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 50),
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 50),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50),
ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50),
ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50),
ejabberd_hooks:add(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50),
ejabberd_hooks:add(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50),
ok.
stop(Host) ->
ejabberd_hooks:delete(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50),
ejabberd_hooks:delete(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50),
ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50),
ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50),
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50),
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 50),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50),
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_items, 50),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_EJABBERD_CONFIG),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG).
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS).
%%%-----------------------------------------------------------------------
-define(EMPTY_INFO_RESULT, {result, Feats}).
-define(INFO_IDENTITY(Category, Type, Name, Lang),
[{xmlelement, "identity",
[{"category", Category},
{"type", Type},
{"name", translate:translate(Lang, Name)}], []}]).
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
-define(INFO_COMMAND(Name, Lang),
?INFO_IDENTITY("automation", "command-node", Name, Lang)).
get_sm_identity(Acc, _From, _To, Node, Lang) ->
case Node of
"config" ->
?INFO_COMMAND("Configuration", Lang);
_ ->
Acc
end.
get_local_identity(Acc, _From, _To, Node, Lang) ->
LNode = string:tokens(Node, "/"),
case LNode of
["running nodes", ENode] ->
?INFO_IDENTITY("ejabberd", "node", ENode, Lang);
["running nodes", _ENode, "DB"] ->
?INFO_COMMAND("DB", Lang);
["running nodes", _ENode, "modules", "start"] ->
?INFO_COMMAND("Start Modules", Lang);
["running nodes", _ENode, "modules", "stop"] ->
?INFO_COMMAND("Stop Modules", Lang);
["running nodes", _ENode, "backup", "backup"] ->
?INFO_COMMAND("Backup", Lang);
["running nodes", _ENode, "backup", "restore"] ->
?INFO_COMMAND("Restore", Lang);
["running nodes", _ENode, "backup", "textfile"] ->
?INFO_COMMAND("Dump to Text File", Lang);
["running nodes", _ENode, "import", "file"] ->
?INFO_COMMAND("Import File", Lang);
["running nodes", _ENode, "import", "dir"] ->
?INFO_COMMAND("Import Directory", Lang);
["config", "hostname"] ->
?INFO_COMMAND("Host Name", Lang);
["config", "acls"] ->
?INFO_COMMAND("Access Control Lists", Lang);
["config", "access"] ->
?INFO_COMMAND("Access Rules", Lang);
_ ->
Acc
end.
%%%-----------------------------------------------------------------------
-define(INFO_RESULT(Allow, Feats),
case Allow of
deny ->
{error, ?ERR_FORBIDDEN};
allow ->
{result, Feats}
end).
get_sm_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
case {acl:match_rule(LServer, configure, From), Node} of
{allow, []} ->
case Acc of
{result, Features} ->
{result, [?NS_EJABBERD_CONFIG | Features]};
empty ->
{result, [?NS_EJABBERD_CONFIG]}
end;
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Acc
Allow = acl:match_rule(LServer, configure, From),
case Node of
"config" ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
_ ->
Acc
end
end.
get_local_identity(Acc, _From, _To, Node, _Lang) ->
case Node of
["running nodes", ENode] ->
[{xmlelement, "identity",
[{"category", "ejabberd"},
{"type", "node"},
{"name", ENode}], []}];
_ ->
Acc
end.
get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
get_local_features(Acc, _From, _To, [], _Lang) ->
Acc;
get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
Feats = case Acc of
{result, Features} -> Features;
empty -> []
end,
case {acl:match_rule(LServer, configure, From), Node} of
{deny, _} ->
{error, ?ERR_NOT_ALLOWED};
{allow, ["config"]} ->
?EMPTY_INFO_RESULT;
{allow, ["online users"]} ->
?EMPTY_INFO_RESULT;
{allow, ["all users"]} ->
?EMPTY_INFO_RESULT;
{allow, ["all users", [$@ | _]]} ->
?EMPTY_INFO_RESULT;
{allow, ["outgoing s2s" | _]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes"]} ->
?EMPTY_INFO_RESULT;
{allow, ["stopped nodes"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", _ENode]} ->
{result, [?NS_STATS | Feats]};
{allow, ["running nodes", _ENode, "DB"]} ->
{result, [?NS_EJABBERD_CONFIG | Feats]};
{allow, ["running nodes", _ENode, "modules"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", _ENode, "modules", _]} ->
{result, [?NS_EJABBERD_CONFIG | Feats]};
{allow, ["running nodes", _ENode, "backup"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", _ENode, "backup", _]} ->
{result, [?NS_EJABBERD_CONFIG | Feats]};
{allow, ["running nodes", _ENode, "import"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", _ENode, "import", _]} ->
{result, [?NS_EJABBERD_CONFIG | Feats]};
{allow, ["config", _]} ->
{result, [?NS_EJABBERD_CONFIG | Feats]};
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
LNode = string:tokens(Node, "/"),
Allow = acl:match_rule(LServer, configure, From),
case LNode of
["config"] ->
?INFO_RESULT(Allow, []);
["online users"] ->
?INFO_RESULT(Allow, []);
["all users"] ->
?INFO_RESULT(Allow, []);
["all users", [$@ | _]] ->
?INFO_RESULT(Allow, []);
["outgoing s2s" | _] ->
?INFO_RESULT(Allow, []);
["running nodes"] ->
?INFO_RESULT(Allow, []);
["stopped nodes"] ->
?INFO_RESULT(Allow, []);
["running nodes", _ENode] ->
?INFO_RESULT(Allow, [?NS_STATS]);
["running nodes", _ENode, "DB"] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
["running nodes", _ENode, "modules"] ->
?INFO_RESULT(Allow, []);
["running nodes", _ENode, "modules", _] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
["running nodes", _ENode, "backup"] ->
?INFO_RESULT(Allow, []);
["running nodes", _ENode, "backup", _] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
["running nodes", _ENode, "import"] ->
?INFO_RESULT(Allow, []);
["running nodes", _ENode, "import", _] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
["config", _] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]);
_ ->
Acc
end
end.
%%%-----------------------------------------------------------------------
adhoc_sm_items(Acc, From, #jid{lserver = LServer} = To, Lang) ->
case acl:match_rule(LServer, configure, From) of
allow ->
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
Nodes = [{xmlelement, "item",
[{"jid", jlib:jid_to_string(To)},
{"name", translate:translate(Lang, "Configuration")},
{"node", "config"}], []}],
{result, Items ++ Nodes};
_ ->
Acc
end.
get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
%%%-----------------------------------------------------------------------
get_sm_items(Acc, From,
#jid{user = User, server = Server, lserver = LServer} = _To,
Node, _Lang) ->
case {acl:match_rule(LServer, configure, From), Node} of
{allow, []} ->
Items = case Acc of
{result, Its} ->
Its;
empty ->
[]
end,
{result, Items ++ get_user_resources(User, Server)};
#jid{user = User, server = Server, lserver = LServer} = To,
Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Acc
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
case {acl:match_rule(LServer, configure, From), Node} of
{allow, ""} ->
Nodes = [{xmlelement, "item",
[{"jid", jlib:jid_to_string(To)},
{"name", translate:translate(Lang, "Configuration")},
{"node", "config"}], []}],
{result, Items ++ Nodes ++ get_user_resources(User, Server)};
{allow, "config"} ->
{result, []};
{_, "config"} ->
{error, ?ERR_FORBIDDEN};
_ ->
Acc
end
end.
get_user_resources(User, Server) ->
@ -155,31 +231,149 @@ get_user_resources(User, Server) ->
{"name", User}], []}
end, lists:sort(Rs)).
get_local_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
%%%-----------------------------------------------------------------------
get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
Items = case Acc of
{result, Its} ->
Its;
empty ->
adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To,
Lang) ->
case acl:match_rule(LServer, configure, From) of
allow ->
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
%% Recursively get all configure commands
Nodes = recursively_get_local_items(LServer, "", Server, Lang),
Nodes1 = lists:filter(
fun(N) ->
Nd = xml:get_tag_attr_s("node", N),
F = get_local_features([], From, To, Nd, Lang),
case F of
{result, [?NS_COMMANDS]} ->
true;
_ ->
false
end
end, Nodes),
{result, Items ++ Nodes1};
_ ->
Acc
end.
recursively_get_local_items(_LServer, "online users", _Server, _Lang) ->
[];
recursively_get_local_items(_LServer, "all users", _Server, _Lang) ->
[];
recursively_get_local_items(LServer, Node, Server, Lang) ->
LNode = string:tokens(Node, "/"),
Items = case get_local_items(LServer, LNode, Server, Lang) of
{result, Res} ->
Res;
{error, _Error} ->
[]
end,
case acl:match_rule(LServer, configure, From) of
deny when Node /= [] ->
{error, ?ERR_NOT_ALLOWED};
Nodes = lists:flatten(
lists:map(
fun(N) ->
S = xml:get_tag_attr_s("jid", N),
Nd = xml:get_tag_attr_s("node", N),
if (S /= Server) or (Nd == "") ->
[];
true ->
[N, recursively_get_local_items(
LServer, Nd, Server, Lang)]
end
end, Items)),
Nodes.
%%%-----------------------------------------------------------------------
-define(ITEMS_RESULT(Allow, LNode, Fallback),
case Allow of
deny ->
{result, Items};
_ ->
case get_local_items(To#jid.lserver, Node,
Fallback;
allow ->
case get_local_items(LServer, LNode,
jlib:jid_to_string(To), Lang) of
{result, Res} ->
{result, Items ++ Res};
{result, Res};
{error, Error} ->
{error, Error}
{error, Error}
end
end).
get_local_items(Acc, From, #jid{lserver = LServer} = To, "", Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
Allow = acl:match_rule(LServer, configure, From),
case Allow of
deny ->
{result, Items};
allow ->
case get_local_items(LServer, [],
jlib:jid_to_string(To), Lang) of
{result, Res} ->
{result, Items ++ Res};
{error, _Error} ->
{result, Items}
end
end
end;
get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
_ ->
LNode = string:tokens(Node, "/"),
Allow = acl:match_rule(LServer, configure, From),
case LNode of
["config"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["online users"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["all users"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["all users", [$@ | _]] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["outgoing s2s" | _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["stopped nodes"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "DB"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "modules"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "modules", _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "backup"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "backup", _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "import"] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["running nodes", _ENode, "import", _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["config", _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
_ ->
Acc
end
end.
%%%-----------------------------------------------------------------------
-define(NODE(Name, Node),
{xmlelement, "item",
[{"jid", Server},
@ -422,72 +616,86 @@ get_stopped_nodes(_Lang) ->
end, lists:sort(DBNodes))
end.
%-------------------------------------------------------------------------
process_local_iq(From, To, #iq{type = Type, xmlns = XMLNS,
lang = Lang, sub_el = SubEl} = IQ) ->
case acl:match_rule(To#jid.lserver, configure, From) of
-define(COMMANDS_RESULT(Allow, From, To, Request),
case Allow of
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
{error, ?ERR_FORBIDDEN};
allow ->
case Type of
set ->
XDataEl = find_xdata_el(SubEl),
case XDataEl of
false ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
{xmlelement, _Name, Attrs, _SubEls} ->
case xml:get_attr_s("type", Attrs) of
"cancel" ->
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}], []}]};
"submit" ->
XData = jlib:parse_xdata_submit(XDataEl),
case XData of
invalid ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
_ ->
Node =
string:tokens(
xml:get_tag_attr_s("node", SubEl),
"/"),
case set_form(Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error,
sub_el = [SubEl, Error]}
end
end;
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end
end;
get ->
Node =
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
case get_form(Node, Lang) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query", [{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error,
sub_el = [SubEl, Error]}
end
end
adhoc_local_commands(From, To, Request)
end).
adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To,
#adhoc_request{node = Node} = Request) ->
LNode = string:tokens(Node, "/"),
Allow = acl:match_rule(LServer, configure, From),
case LNode of
["running nodes", _ENode, "DB"] ->
?COMMANDS_RESULT(Allow, From, To, Request);
["running nodes", _ENode, "modules", _] ->
?COMMANDS_RESULT(Allow, From, To, Request);
["running nodes", _ENode, "backup", _] ->
?COMMANDS_RESULT(Allow, From, To, Request);
["running nodes", _ENode, "import", _] ->
?COMMANDS_RESULT(Allow, From, To, Request);
["config", _] ->
?COMMANDS_RESULT(Allow, From, To, Request);
_ ->
Acc
end.
adhoc_local_commands(_From, _To,
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID,
action = Action,
xdata = XData} = Request) ->
LNode = string:tokens(Node, "/"),
%% If the "action" attribute is not present, it is
%% understood as "execute". If there was no <actions/>
%% element in the first response (which there isn't in our
%% case), "execute" and "complete" are equivalent.
ActionIsExecute = lists:member(Action,
["", "execute", "complete"]),
if Action == "cancel" ->
%% User cancels request
adhoc:produce_response(
Request,
#adhoc_response{status = canceled});
XData == false, ActionIsExecute ->
%% User requests form
case get_form(LNode, Lang) of
{result, Form} ->
adhoc:produce_response(
Request,
#adhoc_response{status = executing,
elements = Form});
{error, Error} ->
{error, Error}
end;
XData /= false, ActionIsExecute ->
%% User returns form.
case jlib:parse_xdata_submit(XData) of
invalid ->
{error, ?ERR_BAD_REQUEST};
Fields ->
case set_form(LNode, Lang, Fields) of
{result, _Res} ->
adhoc:produce_response(
#adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID,
status = completed});
{error, Error} ->
{error, Error}
end
end;
true ->
{error, ?ERR_BAD_REQUEST}
end.
-define(TLFIELD(Type, Label, Var),
{xmlelement, "field", [{"type", Type},
{"label", translate:translate(Lang, Label)},
@ -1107,83 +1315,60 @@ search_running_node(SNode, [Node | Nodes]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
process_sm_iq(From, To,
#iq{type = Type, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ) ->
case acl:match_rule(To#jid.lserver, configure, From) of
adhoc_sm_commands(_Acc, From,
#jid{user = User, server = Server, lserver = LServer} = _To,
#adhoc_request{lang = Lang,
node = "config",
action = Action,
xdata = XData} = Request) ->
case acl:match_rule(LServer, configure, From) of
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
{error, ?ERR_FORBIDDEN};
allow ->
#jid{user = User, server = Server} = To,
case Type of
set ->
XDataEl = find_xdata_el(SubEl),
case XDataEl of
false ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
{xmlelement, _Name, Attrs, _SubEls} ->
case xml:get_attr_s("type", Attrs) of
"cancel" ->
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}], []}]};
"submit" ->
XData = jlib:parse_xdata_submit(XDataEl),
case XData of
invalid ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]};
_ ->
Node =
string:tokens(
xml:get_tag_attr_s("node", SubEl),
"/"),
case set_sm_form(
User, Server, Node,
Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error,
sub_el = [SubEl, Error]}
end
end;
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end
end;
get ->
Node =
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
case get_sm_form(User, Server, Node, Lang) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query", [{"xmlns", XMLNS}],
Res
}]};
%% If the "action" attribute is not present, it is
%% understood as "execute". If there was no <actions/>
%% element in the first response (which there isn't in our
%% case), "execute" and "complete" are equivalent.
ActionIsExecute = lists:member(Action,
["", "execute", "complete"]),
if Action == "cancel" ->
%% User cancels request
adhoc:produce_response(
Request,
#adhoc_response{status = canceled});
XData == false, ActionIsExecute ->
%% User requests form
case get_sm_form(User, Server, "config", Lang) of
{result, Form} ->
adhoc:produce_response(
Request,
#adhoc_response{status = executing,
elements = Form});
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]}
end
{error, Error}
end;
XData /= false, ActionIsExecute ->
%% User returns form.
case jlib:parse_xdata_submit(XData) of
invalid ->
{error, ?ERR_BAD_REQUEST};
Fields ->
set_sm_form(User, Server, "config", Request, Fields)
end;
true ->
{error, ?ERR_BAD_REQUEST}
end
end.
end;
adhoc_sm_commands(Acc, _From, _To, _Request) ->
Acc.
get_sm_form(User, Server, [], Lang) ->
get_sm_form(User, Server, "config", Lang) ->
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlelement, "title", [],
[{xmlcdata,
translate:translate(
Lang, "Administration of ") ++ User}]},
%{xmlelement, "instructions", [],
% [{xmlcdata,
% translate:translate(
% Lang, "Choose host name")}]},
{xmlelement, "field",
[{"type", "list-single"},
{"label", translate:translate(Lang, "Action on user")},
@ -1198,49 +1383,36 @@ get_sm_form(User, Server, [], Lang) ->
]},
?XFIELD("text-private", "Password", "password",
ejabberd_auth:get_password_s(User, Server))
%{xmlelement, "field", [{"type", "text-single"},
% {"label",
% translate:translate(Lang, "Host name")},
% {"var", "hostname"}],
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
]}]};
get_sm_form(_User, _Server, _Node, _Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
set_sm_form(User, Server, [], _Lang, XData) ->
set_sm_form(User, Server, "config",
#adhoc_request{lang = Lang,
node = Node,
sessionid = SessionID}, XData) ->
Response = #adhoc_response{lang = Lang,
node = Node,
sessionid = SessionID,
status = completed},
case lists:keysearch("action", 1, XData) of
{value, {_, ["edit"]}} ->
case lists:keysearch("password", 1, XData) of
{value, {_, [Password]}} ->
ejabberd_auth:set_password(User, Server, Password),
{result, []};
adhoc:produce_response(Response);
_ ->
{error, ?ERR_BAD_REQUEST}
{error, ?ERR_NOT_ACCEPTABLE}
end;
{value, {_, ["remove"]}} ->
catch ejabberd_auth:remove_user(User, Server),
{result, []};
adhoc:produce_response(Response);
_ ->
{error, ?ERR_BAD_REQUEST}
{error, ?ERR_NOT_ACCEPTABLE}
end;
set_sm_form(_User, _Server, _Node, _Lang, _XData) ->
set_sm_form(_User, _Server, _Node, _Request, _Fields) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
find_xdata_el1([]) ->
false;
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_XDATA ->
{xmlelement, Name, Attrs, SubEls};
_ ->
find_xdata_el1(Els)
end;
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).

View File

@ -113,8 +113,7 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Host = To#jid.lserver,
case ejabberd_hooks:run_fold(disco_local_items,
@ -123,8 +122,8 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
[From, To, Node, Lang]) of
{result, Items} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@ -144,8 +143,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Identity = ejabberd_hooks:run_fold(disco_local_identity,
Host,
[],
@ -156,8 +154,8 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
[From, To, Node, Lang]) of
{result, Features} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@ -252,7 +250,7 @@ get_vh_services(Host) ->
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
case Type of
set ->
#jid{user = User, luser = LTo, lserver = ToServer} = To,
#jid{luser = LTo, lserver = ToServer} = To,
#jid{luser = LFrom, lserver = LServer} = From,
Self = (LTo == LFrom) andalso (ToServer == LServer),
Node = xml:get_tag_attr_s("node", SubEl),
@ -272,16 +270,15 @@ process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ
end;
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
case ejabberd_hooks:run_fold(disco_sm_items,
Host,
empty,
[From, To, Node, Lang]) of
{result, Items} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@ -329,8 +326,7 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
Node = string:tokens(SNode, "/"),
Node = xml:get_tag_attr_s("node", SubEl),
Identity = ejabberd_hooks:run_fold(disco_sm_identity,
Host,
[],
@ -341,8 +337,8 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
[From, To, Node, Lang]) of
{result, Features} ->
ANode = case Node of
[] -> [];
_ -> [{"node", SNode}]
"" -> [];
_ -> [{"node", Node}]
end,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@ -384,24 +380,17 @@ get_user_resources(User, Server) ->
get_publish_items(empty,
#jid{luser = LFrom, lserver = LSFrom},
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
#jid{luser = LTo, lserver = LSTo} = _To,
Node, _Lang) ->
if
(LFrom == LTo) and (LSFrom == LSTo) ->
% Hack
SNode = join(Node, "/"),
retrieve_disco_publish({LTo, LSTo}, SNode);
retrieve_disco_publish({LTo, LSTo}, Node);
true ->
empty
end;
get_publish_items(Acc, _From, _To, _Node, _Lang) ->
Acc.
join(List, Sep) ->
lists:foldl(fun(A, "") -> A;
(A, Acc) -> Acc ++ Sep ++ A
end, "", List).
process_disco_publish(User, Node, Items) ->
F = fun() ->
lists:foreach(

View File

@ -107,7 +107,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
"iq" ->
case jlib:iq_query_info(Packet) of
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}],
@ -123,7 +123,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
#iq{type = get,
xmlns = ?NS_REGISTER = XMLNS,
lang = Lang,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
@ -155,7 +155,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
#iq{type = get,
xmlns = ?NS_VCARD = XMLNS,
lang = Lang,
sub_el = SubEl} = IQ ->
sub_el = _SubEl} = IQ ->
Res = IQ#iq{type = result,
sub_el =
[{xmlelement, "vCard",
@ -380,76 +380,81 @@ iq_get_register_info(Host, From, Lang) ->
Lang, "Enter nickname you want to register")}]},
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
iq_set_register_info(Host, From, XData, Lang) ->
iq_set_register_info(Host, From, Nick, Lang) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
LUS = {LUser, LServer},
case lists:keysearch("nick", 1, XData) of
false ->
ErrText = "You must fill in field \"nick\" in the form",
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
{value, {_, [Nick]}} ->
F = fun() ->
case Nick of
"" ->
mnesia:delete({muc_registered, {LUS, Host}}),
F = fun() ->
case Nick of
"" ->
mnesia:delete({muc_registered, {LUS, Host}}),
ok;
_ ->
Allow =
case mnesia:select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end,
if
Allow ->
mnesia:write(
#muc_registered{us_host = {LUS, Host},
nick = Nick}),
ok;
_ ->
Allow =
case mnesia:select(
muc_registered,
[{#muc_registered{us_host = '$1',
nick = Nick,
_ = '_'},
[{'==', {element, 2, '$1'}, Host}],
['$_']}]) of
[] ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
end,
if
Allow ->
mnesia:write(
#muc_registered{us_host = {LUS, Host},
nick = Nick}),
ok;
true ->
false
end
true ->
false
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
{result, []};
{atomic, false} ->
ErrText = "Specified nickname is already registered",
{error, ?ERRT_CONFLICT(Lang, ErrText)};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end
end
end,
case mnesia:transaction(F) of
{atomic, ok} ->
{result, []};
{atomic, false} ->
ErrText = "Specified nickname is already registered",
{error, ?ERRT_CONFLICT(Lang, ErrText)};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_iq_register_set(Host, From, SubEl, Lang) ->
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:remove_cdata(Els) of
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
case {xml:get_tag_attr_s("xmlns", XEl),
xml:get_tag_attr_s("type", XEl)} of
{?NS_XDATA, "cancel"} ->
{result, []};
{?NS_XDATA, "submit"} ->
XData = jlib:parse_xdata_submit(XEl),
case XData of
invalid ->
{error, ?ERR_BAD_REQUEST};
case xml:get_subtag(SubEl, "remove") of
false ->
case xml:remove_cdata(Els) of
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
case {xml:get_tag_attr_s("xmlns", XEl),
xml:get_tag_attr_s("type", XEl)} of
{?NS_XDATA, "cancel"} ->
{result, []};
{?NS_XDATA, "submit"} ->
XData = jlib:parse_xdata_submit(XEl),
case XData of
invalid ->
{error, ?ERR_BAD_REQUEST};
_ ->
case lists:keysearch("nick", 1, XData) of
false ->
ErrText = "You must fill in field \"Nickname\" in the form",
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
{value, {_, [Nick]}} ->
iq_set_register_info(Host, From, Nick, Lang)
end
end;
_ ->
iq_set_register_info(Host, From, XData, Lang)
{error, ?ERR_BAD_REQUEST}
end;
_ ->
{error, ?ERR_BAD_REQUEST}
end;
_ ->
{error, ?ERR_BAD_REQUEST}
iq_set_register_info(Host, From, "", Lang)
end.
iq_get_vcard(Lang) ->

View File

@ -101,7 +101,7 @@ start(Host, ServerHost, Access, Room, Opts) ->
%% ignore |
%% {stop, StopReason}
%%----------------------------------------------------------------------
init([Host, ServerHost, Access, Room, Creator, Nick]) ->
init([Host, ServerHost, Access, Room, Creator, _Nick]) ->
State = set_affiliation(Creator, owner,
#state{host = Host,
server_host = ServerHost,
@ -559,16 +559,11 @@ normal_state({route, From, ToNick,
end,
{next_state, normal_state, StateData};
normal_state(Event, StateData) ->
normal_state(_Event, StateData) ->
{next_state, normal_state, StateData}.
%%----------------------------------------------------------------------
%% Func: handle_event/3
%% Returns: {next_state, NextStateName, NextStateData} |
@ -862,15 +857,13 @@ filter_presence({xmlelement, "presence", Attrs, Els}) ->
case El of
{xmlcdata, _} ->
false;
{xmlelement, Name1, Attrs1, _Els1} ->
{xmlelement, _Name1, Attrs1, _Els1} ->
XMLNS = xml:get_attr_s("xmlns", Attrs1),
case {Name1, XMLNS} of
{"show", ""} ->
true;
{"status", ""} ->
true;
case XMLNS of
?NS_MUC ++ _ ->
false;
_ ->
false
true
end
end
end, Els),
@ -971,6 +964,20 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
add_user_presence(
From, Packet,
add_online_user(From, Nick, Role, StateData)),
if not (NewState#state.config)#config.anonymous ->
WPacket = {xmlelement, "message", [{"type", "groupchat"}],
[{xmlelement, "body", [],
[{xmlcdata, translate:translate(
Lang,
"This room is not anonymous")}]},
{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "status", [{"code", "100"}], []}]}]},
ejabberd_router:route(
StateData#state.jid,
From, WPacket);
true ->
ok
end,
send_new_presence(From, NewState),
send_existing_presences(From, NewState),
Shift = count_stanza_shift(Nick, Els, NewState),
@ -1550,23 +1557,23 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
set_affiliation_and_reason(
JID, outcast, Reason,
set_role(JID, none, SD));
{JID, affiliation, A, Reason} when
{JID, affiliation, A, _Reason} when
(A == admin) or (A == owner) ->
SD1 = set_affiliation(JID, A, SD),
SD2 = set_role(JID, moderator, SD1),
send_update_presence(JID, SD2),
SD2;
{JID, affiliation, member, Reason} ->
{JID, affiliation, member, _Reason} ->
SD1 = set_affiliation(
JID, member, SD),
SD2 = set_role(JID, participant, SD1),
send_update_presence(JID, SD2),
SD2;
{JID, role, R, Reason} ->
{JID, role, R, _Reason} ->
SD1 = set_role(JID, R, SD),
catch send_new_presence(JID, SD1),
SD1;
{JID, affiliation, A, Reason} ->
{JID, affiliation, A, _Reason} ->
SD1 = set_affiliation(JID, A, SD),
send_update_presence(JID, SD1),
SD1
@ -1593,7 +1600,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
end.
find_changed_items(UJID, UAffiliation, URole, [], _Lang, StateData, Res) ->
find_changed_items(_UJID, _UAffiliation, _URole, [], _Lang, _StateData, Res) ->
{result, Res};
find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
Lang, StateData, Res) ->
@ -1721,143 +1728,143 @@ find_changed_items(_UJID, _UAffiliation, _URole, _Items,
{error, ?ERR_BAD_REQUEST}.
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
can_change_ra(_FAffiliation, _FRole,
TAffiliation, _TRole,
affiliation, Value)
when (TAffiliation == Value) ->
nothing;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, TRole,
role, Value)
when (TRole == Value) ->
nothing;
can_change_ra(FAffiliation, FRole,
outcast, TRole,
can_change_ra(FAffiliation, _FRole,
outcast, _TRole,
affiliation, none)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
outcast, TRole,
can_change_ra(FAffiliation, _FRole,
outcast, _TRole,
affiliation, member)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
outcast, TRole,
can_change_ra(owner, _FRole,
outcast, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
outcast, TRole,
can_change_ra(owner, _FRole,
outcast, _TRole,
affiliation, owner) ->
true;
can_change_ra(FAffiliation, FRole,
none, TRole,
can_change_ra(FAffiliation, _FRole,
none, _TRole,
affiliation, outcast)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
none, TRole,
can_change_ra(FAffiliation, _FRole,
none, _TRole,
affiliation, member)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
none, TRole,
can_change_ra(owner, _FRole,
none, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
none, TRole,
can_change_ra(owner, _FRole,
none, _TRole,
affiliation, owner) ->
true;
can_change_ra(FAffiliation, FRole,
member, TRole,
can_change_ra(FAffiliation, _FRole,
member, _TRole,
affiliation, outcast)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
member, TRole,
can_change_ra(FAffiliation, _FRole,
member, _TRole,
affiliation, none)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(owner, FRole,
member, TRole,
can_change_ra(owner, _FRole,
member, _TRole,
affiliation, admin) ->
true;
can_change_ra(owner, FRole,
member, TRole,
can_change_ra(owner, _FRole,
member, _TRole,
affiliation, owner) ->
true;
can_change_ra(owner, FRole,
admin, TRole,
can_change_ra(owner, _FRole,
admin, _TRole,
affiliation, _Affiliation) ->
true;
can_change_ra(owner, FRole,
owner, TRole,
can_change_ra(owner, _FRole,
owner, _TRole,
affiliation, _Affiliation) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
affiliation, Value) ->
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, _TRole,
affiliation, _Value) ->
false;
can_change_ra(FAffiliation, moderator,
TAffiliation, visitor,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, visitor,
role, none) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, visitor,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, visitor,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, visitor,
can_change_ra(FAffiliation, _FRole,
_TAffiliation, visitor,
role, moderator)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, participant,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, participant,
role, none) ->
true;
can_change_ra(FAffiliation, moderator,
TAffiliation, participant,
can_change_ra(_FAffiliation, moderator,
_TAffiliation, participant,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, participant,
can_change_ra(FAffiliation, _FRole,
_TAffiliation, participant,
role, moderator)
when (FAffiliation == owner) or (FAffiliation == admin) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
owner, moderator,
role, visitor) ->
false;
can_change_ra(owner, FRole,
TAffiliation, moderator,
can_change_ra(owner, _FRole,
_TAffiliation, moderator,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
admin, moderator,
role, visitor) ->
false;
can_change_ra(admin, FRole,
TAffiliation, moderator,
can_change_ra(admin, _FRole,
_TAffiliation, moderator,
role, visitor) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
owner, moderator,
role, participant) ->
false;
can_change_ra(owner, FRole,
TAffiliation, moderator,
can_change_ra(owner, _FRole,
_TAffiliation, moderator,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
can_change_ra(_FAffiliation, _FRole,
admin, moderator,
role, participant) ->
false;
can_change_ra(admin, FRole,
TAffiliation, moderator,
can_change_ra(admin, _FRole,
_TAffiliation, moderator,
role, participant) ->
true;
can_change_ra(FAffiliation, FRole,
TAffiliation, TRole,
role, Value) ->
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, _TRole,
role, _Value) ->
false.
@ -1887,13 +1894,13 @@ send_kickban_presence(JID, Reason, Code, StateData) ->
end, LJIDs).
send_kickban_presence1(UJID, Reason, Code, StateData) ->
{ok, #user{jid = RealJID,
{ok, #user{jid = _RealJID,
nick = Nick}} =
?DICT:find(jlib:jid_tolower(UJID), StateData#state.users),
Affiliation = get_affiliation(UJID, StateData),
SAffiliation = affiliation_to_list(Affiliation),
lists:foreach(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
ItemAttrs = [{"affiliation", SAffiliation},
{"role", "none"}],
ItemEls = case Reason of
@ -1922,9 +1929,9 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
FAffiliation = get_affiliation(From, StateData),
case FAffiliation of
owner ->
{xmlelement, Name, Attrs, Els} = SubEl,
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:remove_cdata(Els) of
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
case {xml:get_tag_attr_s("xmlns", XEl),
xml:get_tag_attr_s("type", XEl)} of
{?NS_XDATA, "cancel"} ->
@ -1934,8 +1941,8 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
_ ->
{error, ?ERR_BAD_REQUEST}
end;
[{xmlelement, "destroy", Attrs1, Els1}] ->
destroy_room(Els1, StateData);
[{xmlelement, "destroy", _Attrs1, _Els1} = SubEl1] ->
destroy_room(SubEl1, StateData);
Items ->
process_admin_items_set(From, Items, Lang, StateData)
end;
@ -1948,7 +1955,7 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
FAffiliation = get_affiliation(From, StateData),
case FAffiliation of
owner ->
{xmlelement, Name, Attrs, Els} = SubEl,
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:remove_cdata(Els) of
[] ->
get_config(Lang, StateData);
@ -2009,39 +2016,18 @@ get_config(Lang, StateData) ->
[{xmlcdata, translate:translate(Lang, "Configuration for ") ++
jlib:jid_to_string(StateData#state.jid)}]},
?STRINGXFIELD("Room title",
"title",
Config#config.title),
?BOOLXFIELD("Allow users to change subject?",
"allow_change_subj",
Config#config.allow_change_subj),
?BOOLXFIELD("Allow users to query other users?",
"allow_query_users",
Config#config.allow_query_users),
?BOOLXFIELD("Allow users to send private messages?",
"allow_private_messages",
Config#config.allow_private_messages),
?BOOLXFIELD("Make room public searchable?",
"public",
Config#config.public),
?BOOLXFIELD("Make participants list public?",
"public_list",
Config#config.public_list),
?BOOLXFIELD("Make room persistent?",
"title",
Config#config.title),
?BOOLXFIELD("Make room persistent",
"persistent",
Config#config.persistent),
?BOOLXFIELD("Make room moderated?",
"moderated",
Config#config.moderated),
?BOOLXFIELD("Default users as members?",
"members_by_default",
Config#config.members_by_default),
?BOOLXFIELD("Make room members only?",
"members_only",
Config#config.members_only),
?BOOLXFIELD("Allow users to send invites?",
"allow_user_invites",
Config#config.allow_user_invites),
?BOOLXFIELD("Make room password protected?",
?BOOLXFIELD("Make room public searchable",
"public",
Config#config.public),
?BOOLXFIELD("Make participants list public",
"public_list",
Config#config.public_list),
?BOOLXFIELD("Make room password protected",
"password_protected",
Config#config.password_protected),
?PRIVATEXFIELD("Password",
@ -2050,10 +2036,31 @@ get_config(Lang, StateData) ->
true -> Config#config.password;
false -> ""
end),
?BOOLXFIELD("Make room anonymous?",
?BOOLXFIELD("Make room semianonymous",
"anonymous",
Config#config.anonymous),
?BOOLXFIELD("Enable logging?",
?BOOLXFIELD("Make room members-only",
"members_only",
Config#config.members_only),
?BOOLXFIELD("Make room moderated",
"moderated",
Config#config.moderated),
?BOOLXFIELD("Default users as participants",
"members_by_default",
Config#config.members_by_default),
?BOOLXFIELD("Allow users to change subject",
"allow_change_subj",
Config#config.allow_change_subj),
?BOOLXFIELD("Allow users to send private messages",
"allow_private_messages",
Config#config.allow_private_messages),
?BOOLXFIELD("Allow users to query other users",
"allow_query_users",
Config#config.allow_query_users),
?BOOLXFIELD("Allow users to send invites",
"allow_user_invites",
Config#config.allow_user_invites),
?BOOLXFIELD("Enable logging",
"logging",
Config#config.logging)
],
@ -2125,7 +2132,7 @@ set_xoption([{"anonymous", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(anonymous, Val);
set_xoption([{"logging", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(logging, Val);
set_xoption([_ | Opts], Config) ->
set_xoption([_ | _Opts], _Config) ->
{error, ?ERR_BAD_REQUEST}.
@ -2203,17 +2210,15 @@ make_opts(StateData) ->
destroy_room(DEls, StateData) ->
destroy_room(DEl, StateData) ->
lists:foreach(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
Nick = Info#user.nick,
ItemAttrs = [{"affiliation", "none"},
{"role", "none"}],
Packet = {xmlelement, "presence", [{"type", "unavailable"}],
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "item", ItemAttrs, []},
{xmlelement, "destroy", [],
DEls}]}]},
[{xmlelement, "item", ItemAttrs, []}, DEl]}]},
ejabberd_router:route(
jlib:jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid,
@ -2243,10 +2248,10 @@ destroy_room(DEls, StateData) ->
?FEATURE(Fiffalse)
end).
process_iq_disco_info(From, set, Lang, StateData) ->
process_iq_disco_info(_From, set, _Lang, _StateData) ->
{error, ?ERR_NOT_ALLOWED};
process_iq_disco_info(From, get, Lang, StateData) ->
process_iq_disco_info(_From, get, Lang, StateData) ->
Config = StateData#state.config,
{result, [{xmlelement, "identity",
[{"category", "conference"},
@ -2266,13 +2271,33 @@ process_iq_disco_info(From, get, Lang, StateData) ->
"muc_moderated", "muc_unmoderated"),
?CONFIG_OPT_TO_FEATURE(Config#config.password_protected,
"muc_passwordprotected", "muc_unsecured")
], StateData}.
] ++ iq_disco_info_extras(Lang, StateData), StateData}.
-define(RFIELDT(Type, Var, Val),
{xmlelement, "field", [{"type", Type}, {"var", Var}],
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
process_iq_disco_items(From, set, Lang, StateData) ->
-define(RFIELD(Label, Var, Val),
{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
{"var", Var}],
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
iq_disco_info_extras(Lang, StateData) ->
Len = length(?DICT:to_list(StateData#state.users)),
[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}],
[?RFIELDT("hidden", "FORM_TYPE",
"http://jabber.org/protocol/muc#roominfo"),
?RFIELD("Description", "muc#roominfo_description",
(StateData#state.config)#config.title),
%?RFIELD("Subject", "muc#roominfo_subject", StateData#state.subject),
?RFIELD("Number of occupants", "muc#roominfo_occupants",
integer_to_list(Len))
]}].
process_iq_disco_items(_From, set, _Lang, _StateData) ->
{error, ?ERR_NOT_ALLOWED};
process_iq_disco_items(From, get, Lang, StateData) ->
process_iq_disco_items(From, get, _Lang, StateData) ->
FAffiliation = get_affiliation(From, StateData),
FRole = get_role(From, StateData),
case ((StateData#state.config)#config.public_list == true) orelse
@ -2282,7 +2307,7 @@ process_iq_disco_items(From, get, Lang, StateData) ->
true ->
UList =
lists:map(
fun({LJID, Info}) ->
fun({_LJID, Info}) ->
Nick = Info#user.nick,
{xmlelement, "item",
[{"jid", jlib:jid_to_string(
@ -2314,11 +2339,11 @@ check_invitation(From, Els, StateData) ->
CanInvite = (StateData#state.config)#config.allow_user_invites
orelse (FAffiliation == admin) orelse (FAffiliation == owner),
case xml:remove_cdata(Els) of
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
[{xmlelement, "x", _Attrs1, Els1} = XEl] ->
case xml:get_tag_attr_s("xmlns", XEl) of
?NS_MUC_USER ->
case xml:remove_cdata(Els1) of
[{xmlelement, "invite", Attrs2, Els2} = InviteEl] ->
[{xmlelement, "invite", Attrs2, _Els2} = InviteEl] ->
case jlib:string_to_jid(
xml:get_attr_s("to", Attrs2)) of
error ->