]> granicus.if.org Git - ejabberd/commitdiff
* src/aclocal.m4: Updated for zlib support
authorAlexey Shchepin <alexey@process-one.net>
Thu, 19 Jan 2006 02:17:31 +0000 (02:17 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Thu, 19 Jan 2006 02:17:31 +0000 (02:17 +0000)
* 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

22 files changed:
ChangeLog
src/Makefile.in
src/aclocal.m4
src/adhoc.erl [new file with mode: 0644]
src/adhoc.hrl [new file with mode: 0644]
src/configure
src/configure.ac
src/ejabberd.cfg.example
src/ejabberd_c2s.erl
src/ejabberd_receiver.erl
src/ejabberd_service.erl
src/ejabberd_zlib/Makefile.in [new file with mode: 0644]
src/ejabberd_zlib/ejabberd_zlib.erl [new file with mode: 0644]
src/ejabberd_zlib/ejabberd_zlib_drv.c [new file with mode: 0644]
src/gen_mod.erl
src/jlib.hrl
src/mod_adhoc.erl [new file with mode: 0644]
src/mod_announce.erl
src/mod_configure.erl
src/mod_disco.erl
src/mod_muc/mod_muc.erl
src/mod_muc/mod_muc_room.erl

index 6bb7b3e48a4a4b0aadf1361cf2642fa3406943ba..204f3634932c71fab3b1db32d88da68b11c15b17 100644 (file)
--- 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
index fe36ea2e70f486e44220b487688b45ab782bcf76..ae9bc210d0edd63e3e20917bcef4b2b4782403dc 100644 (file)
@@ -23,7 +23,7 @@ endif
 
 prefix = @prefix@
 
-SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@
+SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
 ERLSHLIBS = expat_erl.so
 SOURCES = $(wildcard *.erl)
 BEAMS = $(SOURCES:.erl=.beam)
index 09a6921999c114089c385121fa6b186d2103196a..362c703872a575419cda6e3ebf3476dca41db285 100644 (file)
@@ -29,6 +29,37 @@ AC_DEFUN(AM_WITH_EXPAT,
   AC_SUBST(EXPAT_LIBS)
 ])
 
+AC_DEFUN(AM_WITH_ZLIB,
+[ AC_ARG_WITH(zlib,
+             [  --with-zlib=PREFIX     prefix where zlib is installed])
+
+  ZLIB_CFLAGS=
+  ZLIB_LIBS=
+       if test x"$with_zlib" != x; then
+               ZLIB_CFLAGS="-I$with_zlib/include"
+               ZLIB_LIBS="-L$with_zlib/lib"
+       fi
+       
+       AC_CHECK_LIB(z, gzgets,
+                    [ ZLIB_LIBS="$ZLIB_LIBS -lz"
+                      zlib_found=yes ],
+                    [ zlib_found=no ],
+                    "$ZLIB_LIBS")
+       if test $zlib_found = no; then
+               AC_MSG_ERROR([Could not find the zlib library])
+       fi
+       zlib_save_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS $ZLIB_CFLAGS"
+       AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
+       if test $zlib_found = no; then
+               AC_MSG_ERROR([Could not find zlib.h])
+       fi
+       CFLAGS="$zlib_save_CFLAGS"
+
+  AC_SUBST(ZLIB_CFLAGS)
+  AC_SUBST(ZLIB_LIBS)
+])
+
 AC_DEFUN(AM_WITH_ERLANG,
 [ AC_ARG_WITH(erlang,
              [  --with-erlang=PREFIX    path to erlc and erl ])
diff --git a/src/adhoc.erl b/src/adhoc.erl
new file mode 100644 (file)
index 0000000..b6954d2
--- /dev/null
@@ -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 (file)
index 0000000..2888a66
--- /dev/null
@@ -0,0 +1,15 @@
+-record(adhoc_request, {lang,
+                       node,
+                       sessionid,
+                       action,
+                       xdata,
+                       others}).
+
+-record(adhoc_response, {lang,
+                        node,
+                        sessionid,
+                        status,
+                        defaultaction = "",
+                        actions = [],
+                        notes = [],
+                        elements = []}).
index 132cbc9279404fa33d4ae4fa5b78df4cff1702f1..06b982cfd7f0de86a45d0a2af0d758322a98ff44 100755 (executable)
@@ -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
index 166eb438a5c6188f8b453b2ad2db756c0f28cef9..04033f6b5e13ee55641afdac67b6f60e7ca373a4 100644 (file)
@@ -14,6 +14,8 @@ AM_WITH_ERLANG
 AM_ICONV
 #locating libexpat
 AM_WITH_EXPAT
+#locating zlib
+AM_WITH_ZLIB
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -29,6 +31,7 @@ AC_MOD_ENABLE(eldap, yes)
 AC_MOD_ENABLE(web, yes)
 AC_MOD_ENABLE(tls, yes)
 AC_MOD_ENABLE(odbc, no)
+AC_MOD_ENABLE(ejabberd_zlib, yes)
 
 AC_ARG_ENABLE(roster_gateway_workaround,
 [  --enable-roster-gateway-workaround    Turn on workaround for processing gateway subscriptions],
@@ -47,7 +50,8 @@ AC_CONFIG_FILES([Makefile
                  $make_web
                  stringprep/Makefile
                  $make_tls
-                 $make_odbc])
+                 $make_odbc
+                 $make_ejabberd_zlib])
 #openssl
 AM_WITH_OPENSSL
 AC_OUTPUT
index 2f7fdfe9ad566fe51e769c07e0e1bb46f8fba1f9..749ba52763ff7ca831dddbd876c0953394dfca04 100644 (file)
   {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,        []},
index 891756acf3ceb115aa5c1a19c56a1e0ed10e06d7..4a21772cb9223c6634ab09b3b156fd5c45864ab8 100644 (file)
@@ -46,6 +46,7 @@
                sasl_state,
                access,
                shaper,
+               zlib = false,
                tls = false,
                tls_required = false,
                tls_enabled = false,
@@ -125,6 +126,7 @@ init([{SockMod, Socket}, Opts]) ->
                 {value, {_, S}} -> S;
                 _ -> none
             end,
+    Zlib = lists:member(zlib, Opts),
     StartTLS = lists:member(starttls, Opts),
     StartTLSRequired = lists:member(starttls_required, Opts),
     TLSEnabled = lists:member(tls, Opts),
@@ -145,6 +147,7 @@ init([{SockMod, Socket}, Opts]) ->
     {ok, wait_for_stream, #state{socket       = Socket1,
                                 sockmod      = SockMod1,
                                 receiver     = ReceiverPid,
+                                zlib         = Zlib,
                                 tls          = TLS,
                                 tls_required = StartTLSRequired,
                                 tls_enabled  = TLSEnabled,
@@ -200,10 +203,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
                                                      {xmlelement, "mechanism", [],
                                                       [{xmlcdata, S}]}
                                              end, cyrsasl:listmech(Server)),
+                                   SockMod = StateData#state.sockmod,
+                                   Zlib = StateData#state.zlib,
+                                   CompressFeature =
+                                       case Zlib andalso
+                                           (SockMod /= ejabberd_zlib) of
+                                           true ->
+                                               [{xmlelement, "compression",
+                                                 [{"xmlns", ?NS_FEATURE_COMPRESS}],
+                                                 [{xmlelement, "method",
+                                                   [], [{xmlcdata, "zlib"}]}]}];
+                                           _ ->
+                                               []
+                                       end,
                                    TLS = StateData#state.tls,
                                    TLSEnabled = StateData#state.tls_enabled,
                                    TLSRequired = StateData#state.tls_required,
-                                   SockMod = StateData#state.sockmod,
                                    TLSFeature =
                                        case (TLS == true) andalso
                                            (TLSEnabled == false) andalso
@@ -224,7 +239,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
                                        end,
                                    send_element(StateData,
                                                 {xmlelement, "stream:features", [],
-                                                 TLSFeature ++
+                                                 TLSFeature ++ CompressFeature ++
                                                  [{xmlelement, "mechanisms",
                                                    [{"xmlns", ?NS_SASL}],
                                                    Mechs}] ++
@@ -337,7 +352,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
                  end,
            send_element(StateData, Res),
            {next_state, wait_for_auth, StateData};
-       {auth, _ID, set, {U, P, D, ""}} ->
+       {auth, _ID, set, {_U, _P, _D, ""}} ->
            Err = jlib:make_error_reply(
                    El,
                    ?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
@@ -434,6 +449,7 @@ wait_for_auth(closed, StateData) ->
 
 wait_for_feature_request({xmlstreamelement, El}, StateData) ->
     {xmlelement, Name, Attrs, Els} = El,
+    Zlib = StateData#state.zlib,
     TLS = StateData#state.tls,
     TLSEnabled = StateData#state.tls_enabled,
     TLSRequired = StateData#state.tls_required,
@@ -497,6 +513,19 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
                             streamid = new_id(),
                             tls_enabled = true
                            }};
+       {?NS_COMPRESS, "compress"} when Zlib == true,
+                                       SockMod /= ejabberd_zlib ->
+           Socket = StateData#state.socket,
+           {ok, ZlibSocket} = ejabberd_zlib:enable_zlib(SockMod, Socket),
+           ejabberd_receiver:compress(StateData#state.receiver, ZlibSocket),
+           send_element(StateData,
+                        {xmlelement, "compressed",
+                         [{"xmlns", ?NS_COMPRESS}], []}),
+           {next_state, wait_for_stream,
+            StateData#state{sockmod = ejabberd_zlib,
+                            socket = ZlibSocket,
+                            streamid = new_id()
+                           }};
        _ ->
            if
                (SockMod == gen_tcp) and TLSRequired ->
index d09bef666d472008659a07bb35d307250dee7377..466a5c401f8b76347e7b02efca014b31df566b78 100644 (file)
@@ -17,6 +17,7 @@
         change_shaper/2,
         reset_stream/1,
         starttls/2,
+        compress/2,
         become_controller/1,
         close/1]).
 
@@ -54,6 +55,9 @@ reset_stream(Pid) ->
 starttls(Pid, TLSSocket) ->
     gen_server:call(Pid, {starttls, TLSSocket}).
 
+compress(Pid, ZlibSocket) ->
+    gen_server:call(Pid, {compress, ZlibSocket}).
+
 become_controller(Pid) ->
     gen_server:call(Pid, become_controller).
 
@@ -110,6 +114,20 @@ handle_call({starttls, TLSSocket}, _From,
        {error, _Reason} ->
            {stop, normal, ok, NewState}
     end;
+handle_call({compress, ZlibSocket}, _From,
+           #state{xml_stream_state = XMLStreamState,
+                  c2s_pid = C2SPid} = State) ->
+    xml_stream:close(XMLStreamState),
+    NewXMLStreamState = xml_stream:new(C2SPid),
+    NewState = State#state{socket = ZlibSocket,
+                          sock_mod = ejabberd_zlib,
+                          xml_stream_state = NewXMLStreamState},
+    case ejabberd_zlib:recv_data(ZlibSocket, "") of
+       {ok, ZlibData} ->
+           {reply, ok, process_data(ZlibData, NewState)};
+       {error, _Reason} ->
+           {stop, normal, ok, NewState}
+    end;
 handle_call(reset_stream, _From,
            #state{xml_stream_state = XMLStreamState,
                   c2s_pid = C2SPid} = State) ->
@@ -157,6 +175,13 @@ handle_info({Tag, _TCPSocket, Data},
                {error, _Reason} ->
                    {stop, normal, State}
            end;
+       ejabberd_zlib ->
+           case ejabberd_zlib:recv_data(Socket, Data) of
+               {ok, ZlibData} ->
+                   {noreply, process_data(ZlibData, State)};
+               {error, _Reason} ->
+                   {stop, normal, State}
+           end;
        _ ->
            {noreply, process_data(Data, State)}
     end;
@@ -211,7 +236,6 @@ activate_socket(#state{socket = Socket,
            SockMod:setopts(Socket, [{active, once}])
     end.
 
-
 process_data(Data,
             #state{xml_stream_state = XMLStreamState,
                    shaper_state = ShaperState} = State) ->
index a8c1989b4a4c1d947ad492d5d89b55fdb77e28cc..2a207b12911e8e991c0d75c2d7b3708c72719c2e 100644 (file)
@@ -179,7 +179,7 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
                      end, StateData#state.hosts),
                    {next_state, stream_established, StateData};
                _ ->
-                   send_text(StateData, ?INVALID_HEADER_ERR),
+                   send_text(StateData, ?INVALID_HANDSHAKE_ERR),
                    {stop, normal, StateData}
            end;
        _ ->
diff --git a/src/ejabberd_zlib/Makefile.in b/src/ejabberd_zlib/Makefile.in
new file mode 100644 (file)
index 0000000..1b4b5a0
--- /dev/null
@@ -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 (file)
index 0000000..2f74108
--- /dev/null
@@ -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 (file)
index 0000000..075e311
--- /dev/null
@@ -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;
+}
+
+
index 70d1b0af69a84d5e8da4d804255777f874d585e1..5f96a2fc6bc18d21eeb06b61036c5609acee75fe 100644 (file)
@@ -19,7 +19,8 @@
         loaded_modules/1,
         loaded_modules_with_opts/1,
         get_hosts/2,
-        get_module_proc/2]).
+        get_module_proc/2,
+        is_loaded/2]).
 
 -export([behaviour_info/1]).
 
@@ -144,3 +145,6 @@ get_hosts(Opts, Prefix) ->
 get_module_proc(Host, Base) ->
     list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
 
+is_loaded(Host, Module) ->
+    ets:member(ejabberd_modules, {Module, Host}).
+
index 5f9e17b50afed1e4e1d0ca028c915614567df45e..ca261320fe8416c298e0feeb22783537ae9b43e3 100644 (file)
@@ -33,6 +33,7 @@
 -define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
 -define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
 -define(NS_PUBSUB_NMI,   "http://jabber.org/protocol/pubsub#node-meta-info").
+-define(NS_COMMANDS,     "http://jabber.org/protocol/commands").
 
 -define(NS_EJABBERD_CONFIG, "ejabberd:config").
 
@@ -48,6 +49,9 @@
 
 -define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
 -define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
+-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
+
+-define(NS_COMPRESS,     "http://jabber.org/protocol/compress").
 
 % TODO: remove "code" attribute (currently it used for backward-compatibility)
 -define(STANZA_ERROR(Code, Type, Condition),
diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl
new file mode 100644 (file)
index 0000000..4288524
--- /dev/null
@@ -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.
+
index 2426ff37195974608f3cef9ff90442388ec1b6cf..8cabf748aa9c1f983c0f70b6db5c78896ef715df 100644 (file)
         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.
+
index 0786867470a6ae34d06a631fc7617e0a76d9f524..0a21e02bbd9f6c76404308923c5f715536d77dc1 100644 (file)
         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).
index 14df47ca2745f439f66abdd8f59a8ad4a9b611e2..22f67da214479b611d2a482a4da0a59b3057eaf4 100644 (file)
@@ -113,8 +113,7 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
        set ->
            IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
        get ->
-           SNode = xml:get_tag_attr_s("node", SubEl),
-           Node = string:tokens(SNode, "/"),
+           Node = xml:get_tag_attr_s("node", SubEl),
            Host = To#jid.lserver,
 
            case ejabberd_hooks:run_fold(disco_local_items,
@@ -123,8 +122,8 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
                                         [From, To, Node, Lang]) of
                {result, Items} ->
                    ANode = case Node of
-                               [] -> [];
-                               _ -> [{"node", SNode}]
+                               "" -> [];
+                               _ -> [{"node", Node}]
                    end,
                    IQ#iq{type = result,
                          sub_el = [{xmlelement, "query",
@@ -144,8 +143,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
            IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
        get ->
            Host = To#jid.lserver,
-           SNode = xml:get_tag_attr_s("node", SubEl),
-           Node = string:tokens(SNode, "/"),
+           Node = xml:get_tag_attr_s("node", SubEl),
            Identity = ejabberd_hooks:run_fold(disco_local_identity,
                                               Host,
                                               [],
@@ -156,8 +154,8 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
                                         [From, To, Node, Lang]) of
                {result, Features} ->
                    ANode = case Node of
-                               [] -> [];
-                               _ -> [{"node", SNode}]
+                               "" -> [];
+                               _ -> [{"node", Node}]
                            end,
                    IQ#iq{type = result,
                          sub_el = [{xmlelement, "query",
@@ -252,7 +250,7 @@ get_vh_services(Host) ->
 process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
     case Type of
        set ->
-           #jid{user = User, luser = LTo, lserver = ToServer} = To,
+           #jid{luser = LTo, lserver = ToServer} = To,
            #jid{luser = LFrom, lserver = LServer} = From,
            Self = (LTo == LFrom) andalso (ToServer == LServer),
            Node = xml:get_tag_attr_s("node", SubEl),
@@ -272,16 +270,15 @@ process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ
            end;
        get ->
            Host = To#jid.lserver,
-           SNode = xml:get_tag_attr_s("node", SubEl),
-           Node = string:tokens(SNode, "/"),
+           Node = xml:get_tag_attr_s("node", SubEl),
            case ejabberd_hooks:run_fold(disco_sm_items,
                                         Host,
                                         empty,
                                         [From, To, Node, Lang]) of
                {result, Items} ->
                    ANode = case Node of
-                               [] -> [];
-                               _ -> [{"node", SNode}]
+                               "" -> [];
+                               _ -> [{"node", Node}]
                            end,
                    IQ#iq{type = result,
                          sub_el = [{xmlelement, "query",
@@ -329,8 +326,7 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
            IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
        get ->
            Host = To#jid.lserver,
-           SNode = xml:get_tag_attr_s("node", SubEl),
-           Node = string:tokens(SNode, "/"),
+           Node = xml:get_tag_attr_s("node", SubEl),
            Identity = ejabberd_hooks:run_fold(disco_sm_identity,
                                               Host,
                                               [],
@@ -341,8 +337,8 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
                                         [From, To, Node, Lang]) of
                {result, Features} ->
                    ANode = case Node of
-                               [] -> [];
-                               _ -> [{"node", SNode}]
+                               "" -> [];
+                               _ -> [{"node", Node}]
                            end,
                    IQ#iq{type = result,
                          sub_el = [{xmlelement, "query",
@@ -384,24 +380,17 @@ get_user_resources(User, Server) ->
 
 get_publish_items(empty,
                  #jid{luser = LFrom, lserver = LSFrom},
-                 #jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
+                 #jid{luser = LTo, lserver = LSTo} = _To,
                  Node, _Lang) ->
     if
        (LFrom == LTo) and (LSFrom == LSTo) ->
-           % Hack
-           SNode = join(Node, "/"),
-           retrieve_disco_publish({LTo, LSTo}, SNode);
+           retrieve_disco_publish({LTo, LSTo}, Node);
        true ->
            empty
     end;
 get_publish_items(Acc, _From, _To, _Node, _Lang) ->
     Acc.
 
-join(List, Sep) ->
-    lists:foldl(fun(A, "") -> A;
-                  (A, Acc) -> Acc ++ Sep ++ A
-               end, "", List).
-
 process_disco_publish(User, Node, Items) ->
     F = fun() ->
                lists:foreach(
index 5ebecf7208edf409f8c83c1b675db06d0490f824..b3112472dc0d66dc2c97c7f028daf489b6160de0 100644 (file)
@@ -107,7 +107,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
                        "iq" ->
                            case jlib:iq_query_info(Packet) of
                                #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
-                                   sub_el = SubEl} = IQ ->
+                                   sub_el = _SubEl} = IQ ->
                                    Res = IQ#iq{type = result,
                                                sub_el = [{xmlelement, "query",
                                                           [{"xmlns", XMLNS}],
@@ -123,7 +123,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
                                #iq{type = get,
                                    xmlns = ?NS_REGISTER = XMLNS,
                                    lang = Lang,
-                                   sub_el = SubEl} = IQ ->
+                                   sub_el = _SubEl} = IQ ->
                                    Res = IQ#iq{type = result,
                                                sub_el =
                                                [{xmlelement, "query",
@@ -155,7 +155,7 @@ do_route1(Host, ServerHost, Access, From, To, Packet) ->
                                #iq{type = get,
                                    xmlns = ?NS_VCARD = XMLNS,
                                    lang = Lang,
-                                   sub_el = SubEl} = IQ ->
+                                   sub_el = _SubEl} = IQ ->
                                    Res = IQ#iq{type = result,
                                                sub_el =
                                                [{xmlelement, "vCard",
@@ -380,76 +380,81 @@ iq_get_register_info(Host, From, Lang) ->
                Lang, "Enter nickname you want to register")}]},
           ?XFIELD("text-single", "Nickname", "nick", Nick)]}].
 
-iq_set_register_info(Host, From, XData, Lang) ->
+iq_set_register_info(Host, From, Nick, Lang) ->
     {LUser, LServer, _} = jlib:jid_tolower(From),
     LUS = {LUser, LServer},
-    case lists:keysearch("nick", 1, XData) of
-       false ->
-           ErrText = "You must fill in field \"nick\" in the form",
-           {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
-       {value, {_, [Nick]}} ->
-           F = fun() ->
-                       case Nick of
-                           "" ->
-                               mnesia:delete({muc_registered, {LUS, Host}}),
+    F = fun() ->
+               case Nick of
+                   "" ->
+                       mnesia:delete({muc_registered, {LUS, Host}}),
+                       ok;
+                   _ ->
+                       Allow =
+                           case mnesia:select(
+                                  muc_registered,
+                                  [{#muc_registered{us_host = '$1',
+                                                    nick = Nick,
+                                                    _ = '_'},
+                                    [{'==', {element, 2, '$1'}, Host}],
+                                    ['$_']}]) of
+                               [] ->
+                                   true;
+                               [#muc_registered{us_host = {U, _Host}}] ->
+                                   U == LUS
+                           end,
+                       if
+                           Allow ->
+                               mnesia:write(
+                                 #muc_registered{us_host = {LUS, Host},
+                                                 nick = Nick}),
                                ok;
-                           _ ->
-                               Allow =
-                                   case mnesia:select(
-                                          muc_registered,
-                                          [{#muc_registered{us_host = '$1',
-                                                            nick = Nick,
-                                                            _ = '_'},
-                                            [{'==', {element, 2, '$1'}, Host}],
-                                            ['$_']}]) of
-                                       [] ->
-                                           true;
-                                       [#muc_registered{us_host = {U, _Host}}] ->
-                                           U == LUS
-                                   end,
-                               if
-                                   Allow ->
-                                       mnesia:write(
-                                         #muc_registered{us_host = {LUS, Host},
-                                                         nick = Nick}),
-                                       ok;
-                                   true ->
-                                       false
-                               end
+                           true ->
+                               false
                        end
-               end,
-           case mnesia:transaction(F) of
-               {atomic, ok} ->
-                   {result, []};
-               {atomic, false} ->
-                   ErrText = "Specified nickname is already registered",
-                   {error, ?ERRT_CONFLICT(Lang, ErrText)};
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end
+               end
+       end,
+    case mnesia:transaction(F) of
+       {atomic, ok} ->
+           {result, []};
+       {atomic, false} ->
+           ErrText = "Specified nickname is already registered",
+           {error, ?ERRT_CONFLICT(Lang, ErrText)};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
     end.
 
 process_iq_register_set(Host, From, SubEl, Lang) ->
     {xmlelement, _Name, _Attrs, Els} = SubEl,
-    case xml:remove_cdata(Els) of
-       [{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
-           case {xml:get_tag_attr_s("xmlns", XEl),
-                 xml:get_tag_attr_s("type", XEl)} of
-               {?NS_XDATA, "cancel"} ->
-                   {result, []};
-               {?NS_XDATA, "submit"} ->
-                   XData = jlib:parse_xdata_submit(XEl),
-                   case XData of
-                       invalid ->
-                           {error, ?ERR_BAD_REQUEST};
+    case xml:get_subtag(SubEl, "remove") of
+       false ->
+           case xml:remove_cdata(Els) of
+               [{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
+                   case {xml:get_tag_attr_s("xmlns", XEl),
+                         xml:get_tag_attr_s("type", XEl)} of
+                       {?NS_XDATA, "cancel"} ->
+                           {result, []};
+                       {?NS_XDATA, "submit"} ->
+                           XData = jlib:parse_xdata_submit(XEl),
+                           case XData of
+                               invalid ->
+                                   {error, ?ERR_BAD_REQUEST};
+                               _ ->
+                                   case lists:keysearch("nick", 1, XData) of
+                                       false ->
+                                           ErrText = "You must fill in field \"Nickname\" in the form",
+                                           {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
+                                       {value, {_, [Nick]}} ->
+                                           iq_set_register_info(Host, From, Nick, Lang)
+                                   end
+                           end;
                        _ ->
-                           iq_set_register_info(Host, From, XData, Lang)
+                           {error, ?ERR_BAD_REQUEST}
                    end;
                _ ->
                    {error, ?ERR_BAD_REQUEST}
            end;
        _ ->
-           {error, ?ERR_BAD_REQUEST}
+           iq_set_register_info(Host, From, "", Lang)
     end.
 
 iq_get_vcard(Lang) ->
index 15bacbfe2749b9333cbbf20d527fe7004125e327..9cbdc6874c3a8f1b6fc406487038a17be9460f5e 100644 (file)
@@ -101,7 +101,7 @@ start(Host, ServerHost, Access, Room, Opts) ->
 %%          ignore                              |
 %%          {stop, StopReason}                   
 %%----------------------------------------------------------------------
-init([Host, ServerHost, Access, Room, Creator, Nick]) ->
+init([Host, ServerHost, Access, Room, Creator, _Nick]) ->
     State = set_affiliation(Creator, owner,
                            #state{host = Host,
                                   server_host = ServerHost,
@@ -559,16 +559,11 @@ normal_state({route, From, ToNick,
     end,
     {next_state, normal_state, StateData};
 
-normal_state(Event, StateData) ->
+normal_state(_Event, StateData) ->
     {next_state, normal_state, StateData}.
 
 
 
-
-
-
-
-
 %%----------------------------------------------------------------------
 %% Func: handle_event/3
 %% Returns: {next_state, NextStateName, NextStateData}          |
@@ -862,15 +857,13 @@ filter_presence({xmlelement, "presence", Attrs, Els}) ->
                     case El of
                         {xmlcdata, _} ->
                             false;
-                        {xmlelement, Name1, Attrs1, _Els1} ->
+                        {xmlelement, _Name1, Attrs1, _Els1} ->
                             XMLNS = xml:get_attr_s("xmlns", Attrs1),
-                            case {Name1, XMLNS} of
-                                {"show", ""} ->
-                                    true;
-                                {"status", ""} ->
-                                    true;
+                            case XMLNS of
+                                ?NS_MUC ++ _ ->
+                                    false;
                                 _ ->
-                                    false
+                                    true
                             end
                     end
             end, Els),
@@ -971,6 +964,20 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
                                add_user_presence(
                                  From, Packet,
                                  add_online_user(From, Nick, Role, StateData)),
+                           if not (NewState#state.config)#config.anonymous ->
+                                   WPacket = {xmlelement, "message", [{"type", "groupchat"}],
+                                              [{xmlelement, "body", [],
+                                                [{xmlcdata, translate:translate(
+                                                              Lang,
+                                                              "This room is not anonymous")}]},
+                                               {xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
+                                                [{xmlelement, "status", [{"code", "100"}], []}]}]},
+                                   ejabberd_router:route(
+                                     StateData#state.jid,
+                                     From, WPacket);
+                               true ->
+                                   ok
+                           end,
                            send_new_presence(From, NewState),
                            send_existing_presences(From, NewState),
                            Shift = count_stanza_shift(Nick, Els, NewState),
@@ -1550,23 +1557,23 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
                                         set_affiliation_and_reason(
                                           JID, outcast, Reason,
                                           set_role(JID, none, SD));
-                                    {JID, affiliation, A, Reason} when
+                                    {JID, affiliation, A, _Reason} when
                                           (A == admin) or (A == owner) ->
                                         SD1 = set_affiliation(JID, A, SD),
                                         SD2 = set_role(JID, moderator, SD1),
                                         send_update_presence(JID, SD2),
                                         SD2;
-                                    {JID, affiliation, member, Reason} ->
+                                    {JID, affiliation, member, _Reason} ->
                                         SD1 = set_affiliation(
                                                 JID, member, SD),
                                         SD2 = set_role(JID, participant, SD1),
                                         send_update_presence(JID, SD2),
                                         SD2;
-                                    {JID, role, R, Reason} ->
+                                    {JID, role, R, _Reason} ->
                                         SD1 = set_role(JID, R, SD),
                                         catch send_new_presence(JID, SD1),
                                         SD1;
-                                    {JID, affiliation, A, Reason} ->
+                                    {JID, affiliation, A, _Reason} ->
                                         SD1 = set_affiliation(JID, A, SD),
                                         send_update_presence(JID, SD1),
                                         SD1
@@ -1593,7 +1600,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
     end.
 
     
-find_changed_items(UJID, UAffiliation, URole, [], _Lang, StateData, Res) ->
+find_changed_items(_UJID, _UAffiliation, _URole, [], _Lang, _StateData, Res) ->
     {result, Res};
 find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
                   Lang, StateData, Res) ->
@@ -1721,143 +1728,143 @@ find_changed_items(_UJID, _UAffiliation, _URole, _Items,
     {error, ?ERR_BAD_REQUEST}.
 
 
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, TRole,
+can_change_ra(_FAffiliation, _FRole,
+             TAffiliation, _TRole,
              affiliation, Value)
   when (TAffiliation == Value) ->
     nothing;
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, TRole,
+can_change_ra(_FAffiliation, _FRole,
+             _TAffiliation, TRole,
              role, Value)
   when (TRole == Value) ->
     nothing;
-can_change_ra(FAffiliation, FRole,
-             outcast, TRole,
+can_change_ra(FAffiliation, _FRole,
+             outcast, _TRole,
              affiliation, none)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             outcast, TRole,
+can_change_ra(FAffiliation, _FRole,
+             outcast, _TRole,
              affiliation, member)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(owner, FRole,
-             outcast, TRole,
+can_change_ra(owner, _FRole,
+             outcast, _TRole,
              affiliation, admin) ->
     true;
-can_change_ra(owner, FRole,
-             outcast, TRole,
+can_change_ra(owner, _FRole,
+             outcast, _TRole,
              affiliation, owner) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             none, TRole,
+can_change_ra(FAffiliation, _FRole,
+             none, _TRole,
              affiliation, outcast)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             none, TRole,
+can_change_ra(FAffiliation, _FRole,
+             none, _TRole,
              affiliation, member)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(owner, FRole,
-             none, TRole,
+can_change_ra(owner, _FRole,
+             none, _TRole,
              affiliation, admin) ->
     true;
-can_change_ra(owner, FRole,
-             none, TRole,
+can_change_ra(owner, _FRole,
+             none, _TRole,
              affiliation, owner) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             member, TRole,
+can_change_ra(FAffiliation, _FRole,
+             member, _TRole,
              affiliation, outcast)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             member, TRole,
+can_change_ra(FAffiliation, _FRole,
+             member, _TRole,
              affiliation, none)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(owner, FRole,
-             member, TRole,
+can_change_ra(owner, _FRole,
+             member, _TRole,
              affiliation, admin) ->
     true;
-can_change_ra(owner, FRole,
-             member, TRole,
+can_change_ra(owner, _FRole,
+             member, _TRole,
              affiliation, owner) ->
     true;
-can_change_ra(owner, FRole,
-             admin, TRole,
+can_change_ra(owner, _FRole,
+             admin, _TRole,
              affiliation, _Affiliation) ->
     true;
-can_change_ra(owner, FRole,
-             owner, TRole,
+can_change_ra(owner, _FRole,
+             owner, _TRole,
              affiliation, _Affiliation) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, TRole,
-             affiliation, Value) ->
+can_change_ra(_FAffiliation, _FRole,
+             _TAffiliation, _TRole,
+             affiliation, _Value) ->
     false;
-can_change_ra(FAffiliation, moderator,
-             TAffiliation, visitor,
+can_change_ra(_FAffiliation, moderator,
+             _TAffiliation, visitor,
              role, none) ->
     true;
-can_change_ra(FAffiliation, moderator,
-             TAffiliation, visitor,
+can_change_ra(_FAffiliation, moderator,
+             _TAffiliation, visitor,
              role, participant) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, visitor,
+can_change_ra(FAffiliation, _FRole,
+             _TAffiliation, visitor,
              role, moderator)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(FAffiliation, moderator,
-             TAffiliation, participant,
+can_change_ra(_FAffiliation, moderator,
+             _TAffiliation, participant,
              role, none) ->
     true;
-can_change_ra(FAffiliation, moderator,
-             TAffiliation, participant,
+can_change_ra(_FAffiliation, moderator,
+             _TAffiliation, participant,
              role, visitor) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, participant,
+can_change_ra(FAffiliation, _FRole,
+             _TAffiliation, participant,
              role, moderator)
   when (FAffiliation == owner) or (FAffiliation == admin) ->
     true;
-can_change_ra(FAffiliation, FRole,
+can_change_ra(_FAffiliation, _FRole,
              owner, moderator,
              role, visitor) ->
     false;
-can_change_ra(owner, FRole,
-             TAffiliation, moderator,
+can_change_ra(owner, _FRole,
+             _TAffiliation, moderator,
              role, visitor) ->
     true;
-can_change_ra(FAffiliation, FRole,
+can_change_ra(_FAffiliation, _FRole,
              admin, moderator,
              role, visitor) ->
     false;
-can_change_ra(admin, FRole,
-             TAffiliation, moderator,
+can_change_ra(admin, _FRole,
+             _TAffiliation, moderator,
              role, visitor) ->
     true;
-can_change_ra(FAffiliation, FRole,
+can_change_ra(_FAffiliation, _FRole,
              owner, moderator,
              role, participant) ->
     false;
-can_change_ra(owner, FRole,
-             TAffiliation, moderator,
+can_change_ra(owner, _FRole,
+             _TAffiliation, moderator,
              role, participant) ->
     true;
-can_change_ra(FAffiliation, FRole,
+can_change_ra(_FAffiliation, _FRole,
              admin, moderator,
              role, participant) ->
     false;
-can_change_ra(admin, FRole,
-             TAffiliation, moderator,
+can_change_ra(admin, _FRole,
+             _TAffiliation, moderator,
              role, participant) ->
     true;
-can_change_ra(FAffiliation, FRole,
-             TAffiliation, TRole,
-             role, Value) ->
+can_change_ra(_FAffiliation, _FRole,
+             _TAffiliation, _TRole,
+             role, _Value) ->
     false.
 
 
@@ -1887,13 +1894,13 @@ send_kickban_presence(JID, Reason, Code, StateData) ->
                  end, LJIDs).
 
 send_kickban_presence1(UJID, Reason, Code, StateData) ->
-    {ok, #user{jid = RealJID,
+    {ok, #user{jid = _RealJID,
               nick = Nick}} =
        ?DICT:find(jlib:jid_tolower(UJID), StateData#state.users),
     Affiliation = get_affiliation(UJID, StateData),
     SAffiliation = affiliation_to_list(Affiliation),
     lists:foreach(
-      fun({LJID, Info}) ->
+      fun({_LJID, Info}) ->
              ItemAttrs = [{"affiliation", SAffiliation},
                           {"role", "none"}],
              ItemEls = case Reason of
@@ -1922,9 +1929,9 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
     FAffiliation = get_affiliation(From, StateData),
     case FAffiliation of
        owner ->
-           {xmlelement, Name, Attrs, Els} = SubEl,
+           {xmlelement, _Name, _Attrs, Els} = SubEl,
            case xml:remove_cdata(Els) of
-               [{xmlelement, "x", Attrs1, Els1} = XEl] ->
+               [{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
                    case {xml:get_tag_attr_s("xmlns", XEl),
                          xml:get_tag_attr_s("type", XEl)} of
                        {?NS_XDATA, "cancel"} ->
@@ -1934,8 +1941,8 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
                        _ ->
                            {error, ?ERR_BAD_REQUEST}
                    end;
-               [{xmlelement, "destroy", Attrs1, Els1}] ->
-                   destroy_room(Els1, StateData);
+               [{xmlelement, "destroy", _Attrs1, _Els1} = SubEl1] ->
+                   destroy_room(SubEl1, StateData);
                Items ->
                    process_admin_items_set(From, Items, Lang, StateData)
            end;
@@ -1948,7 +1955,7 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
     FAffiliation = get_affiliation(From, StateData),
     case FAffiliation of
        owner ->
-           {xmlelement, Name, Attrs, Els} = SubEl,
+           {xmlelement, _Name, _Attrs, Els} = SubEl,
            case xml:remove_cdata(Els) of
                [] ->
                    get_config(Lang, StateData);
@@ -2009,39 +2016,18 @@ get_config(Lang, StateData) ->
          [{xmlcdata, translate:translate(Lang, "Configuration for ") ++
            jlib:jid_to_string(StateData#state.jid)}]},
         ?STRINGXFIELD("Room title",
-                    "title",
-                    Config#config.title),
-        ?BOOLXFIELD("Allow users to change subject?",
-                    "allow_change_subj",
-                    Config#config.allow_change_subj),
-        ?BOOLXFIELD("Allow users to query other users?",
-                    "allow_query_users",
-                    Config#config.allow_query_users),
-        ?BOOLXFIELD("Allow users to send private messages?",
-                    "allow_private_messages",
-                    Config#config.allow_private_messages),
-        ?BOOLXFIELD("Make room public searchable?",
+                      "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 ->