aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2006-01-19 02:17:31 +0000
committerAlexey Shchepin <alexey@process-one.net>2006-01-19 02:17:31 +0000
commit568909d5bb454fab2023e7ab1a7cf62eff8dcd39 (patch)
tree562acb2d39d04393e19bda367791a2d8f04e6289
parent* src/odbc/ejabberd_odbc.erl: Added a way to retry database (diff)
* 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
-rw-r--r--ChangeLog47
-rw-r--r--src/Makefile.in2
-rw-r--r--src/aclocal.m431
-rw-r--r--src/adhoc.erl111
-rw-r--r--src/adhoc.hrl15
-rwxr-xr-xsrc/configure286
-rw-r--r--src/configure.ac6
-rw-r--r--src/ejabberd.cfg.example5
-rw-r--r--src/ejabberd_c2s.erl35
-rw-r--r--src/ejabberd_receiver.erl26
-rw-r--r--src/ejabberd_service.erl2
-rw-r--r--src/ejabberd_zlib/Makefile.in38
-rw-r--r--src/ejabberd_zlib/ejabberd_zlib.erl134
-rw-r--r--src/ejabberd_zlib/ejabberd_zlib_drv.c171
-rw-r--r--src/gen_mod.erl6
-rw-r--r--src/jlib.hrl4
-rw-r--r--src/mod_adhoc.erl250
-rw-r--r--src/mod_announce.erl443
-rw-r--r--src/mod_configure.erl674
-rw-r--r--src/mod_disco.erl41
-rw-r--r--src/mod_muc/mod_muc.erl123
-rw-r--r--src/mod_muc/mod_muc_room.erl291
22 files changed, 2252 insertions, 489 deletions
diff --git a/ChangeLog b/ChangeLog
index 6bb7b3e48..204f36349 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/src/Makefile.in b/src/Makefile.in
index fe36ea2e7..ae9bc210d 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -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)
diff --git a/src/aclocal.m4 b/src/aclocal.m4
index 09a692199..362c70387 100644
--- a/src/aclocal.m4
+++ b/src/aclocal.m4
@@ -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 ])
diff --git a/src/adhoc.erl b/src/adhoc.erl
new file mode 100644
index 000000000..b6954d2ec
--- /dev/null
+++ b/src/adhoc.erl
@@ -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}.
diff --git a/src/adhoc.hrl b/src/adhoc.hrl
new file mode 100644
index 000000000..2888a6639
--- /dev/null
+++ b/src/adhoc.hrl
@@ -0,0 +1,15 @@
+-record(adhoc_request, {lang,
+ node,
+ sessionid,
+ action,
+ xdata,
+ others}).
+
+-record(adhoc_response, {lang,
+ node,
+ sessionid,
+ status,
+ defaultaction = "",
+ actions = [],
+ notes = [],
+ elements = []}).
diff --git a/src/configure b/src/configure
index 132cbc927..06b982cfd 100755
--- a/src/configure
+++ b/src/configure
@@ -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
diff --git a/src/configure.ac b/src/configure.ac
index 166eb438a..04033f6b5 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -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
diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example
index 2f7fdfe9a..749ba5276 100644
--- a/src/ejabberd.cfg.example
+++ b/src/ejabberd.cfg.example
@@ -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, []},
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 891756acf..4a21772cb 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -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 ->
diff --git a/src/ejabberd_receiver.erl b/src/ejabberd_receiver.erl
index d09bef666..466a5c401 100644
--- a/src/ejabberd_receiver.erl
+++ b/src/ejabberd_receiver.erl
@@ -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) ->
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index a8c1989b4..2a207b129 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -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;
_ ->
diff --git a/src/ejabberd_zlib/Makefile.in b/src/ejabberd_zlib/Makefile.in
new file mode 100644
index 000000000..1b4b5a021
--- /dev/null
+++ b/src/ejabberd_zlib/Makefile.in
@@ -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
diff --git a/src/ejabberd_zlib/ejabberd_zlib.erl b/src/ejabberd_zlib/ejabberd_zlib.erl
new file mode 100644
index 000000000..2f74108df
--- /dev/null
+++ b/src/ejabberd_zlib/ejabberd_zlib.erl
@@ -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).
+
+
diff --git a/src/ejabberd_zlib/ejabberd_zlib_drv.c b/src/ejabberd_zlib/ejabberd_zlib_drv.c
new file mode 100644
index 000000000..075e31176
--- /dev/null
+++ b/src/ejabberd_zlib/ejabberd_zlib_drv.c
@@ -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;
+}
+
+
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index 70d1b0af6..5f96a2fc6 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -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}).
+
diff --git a/src/jlib.hrl b/src/jlib.hrl
index 5f9e17b50..ca261320f 100644
--- a/src/jlib.hrl
+++ b/src/jlib.hrl
@@ -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),
diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl
new file mode 100644
index 000000000..4288524ab
--- /dev/null
+++ b/src/mod_adhoc.erl
@@ -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.
+
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index 2426ff371..8cabf748a 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -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.
+
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 078686747..0a21e02bb 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -17,136 +17,212 @@
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_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;
+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) ->
- case Node of
+get_local_identity(Acc, _From, _To, Node, Lang) ->
+ LNode = string:tokens(Node, "/"),
+ case LNode of
["running nodes", ENode] ->
- [{xmlelement, "identity",
- [{"category", "ejabberd"},
- {"type", "node"},
- {"name", 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.
-get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
- Acc;
+%%%-----------------------------------------------------------------------
+
+-define(INFO_RESULT(Allow, Feats),
+ case Allow of
+ deny ->
+ {error, ?ERR_FORBIDDEN};
+ allow ->
+ {result, Feats}
+ end).
-get_local_features(Acc, _From, _To, [], _Lang) ->
- Acc;
+get_sm_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
+ case gen_mod:is_loaded(LServer, mod_adhoc) of
+ false ->
+ Acc;
+ _ ->
+ Allow = acl:match_rule(LServer, configure, From),
+ case Node of
+ "config" ->
+ ?INFO_RESULT(Allow, [?NS_COMMANDS]);
+ _ ->
+ Acc
+ end
+ end.
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;
_ ->
- 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.
-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, []} ->
+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 ->
- []
+ {result, Its} -> Its;
+ empty -> []
end,
- {result, Items ++ get_user_resources(User, Server)};
+ Nodes = [{xmlelement, "item",
+ [{"jid", jlib:jid_to_string(To)},
+ {"name", translate:translate(Lang, "Configuration")},
+ {"node", "config"}], []}],
+ {result, Items ++ Nodes};
_ ->
Acc
end.
+%%%-----------------------------------------------------------------------
+
+get_sm_items(Acc, From,
+ #jid{user = User, server = Server, lserver = LServer} = To,
+ Node, Lang) ->
+ case gen_mod:is_loaded(LServer, mod_adhoc) of
+ false ->
+ 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) ->
Rs = ejabberd_sm:get_user_resources(User, Server),
lists:map(fun(R) ->
@@ -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
- }]};
+ 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} ->
- IQ#iq{type = error,
- sub_el = [SubEl, Error]}
+ {error, Error}
end
- 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) ->
- {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;
+set_sm_form(_User, _Server, _Node, _Request, _Fields) ->
+ {error, ?ERR_SERVICE_UNAVAILABLE}.
-find_xdata_el1([_ | Els]) ->
- find_xdata_el1(Els).
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 14df47ca2..22f67da21 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -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(
diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl
index 5ebecf720..b3112472d 100644
--- a/src/mod_muc/mod_muc.erl
+++ b/src/mod_muc/mod_muc.erl
@@ -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) ->
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index 15bacbfe2..9cbdc6874 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -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?",
+ "title",
+ Config#config.title),
+ ?BOOLXFIELD("Make room persistent",
+ "persistent",
+ Config#config.persistent),
+ ?BOOLXFIELD("Make room public searchable",
"public",
Config#config.public),
- ?BOOLXFIELD("Make participants list public?",
+ ?BOOLXFIELD("Make participants list public",
"public_list",
Config#config.public_list),
- ?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 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}]}]}).
+-define(RFIELD(Label, Var, Val),
+ {xmlelement, "field", [{"label", translate:translate(Lang, Label)},
+ {"var", Var}],
+ [{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
-process_iq_disco_items(From, set, Lang, StateData) ->
+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 ->