From 7df146166a7229a66e5d7d4fe8476a5a4c646e0b Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 9 Oct 2003 18:09:05 +0000 Subject: [PATCH] * src/ejabberd_c2s.erl: Added authentification logging * src/ejabberd_listener.erl: Added logging of accepted connections * src/stringprep/stringprep_drv.c: Cleanup * src/jd2ejd.erl: Added support for iq:private importing * src/mod_configure.erl: Fixed user removal * src/mod_private.erl: Added remove_user/1 * doc/guide.tex: Updated * src/mod_disco.erl: Added "extra_domains" option SVN Revision: 146 --- ChangeLog | 18 +++++++++ doc/guide.html | 65 ++++++++++++++++++++++----------- doc/guide.tex | 65 +++++++++++++++++++++------------ src/ejabberd_c2s.erl | 26 +++++++++++++ src/ejabberd_listener.erl | 16 ++++++++ src/jd2ejd.erl | 42 +++++++++++++++------ src/mod_configure.erl | 28 ++++++++++++-- src/mod_disco.erl | 24 ++++++++++-- src/mod_private.erl | 20 +++++++++- src/stringprep/stringprep_drv.c | 53 +-------------------------- src/stringprep/uni_norm.c | 21 ++++++----- src/stringprep/uni_parse2.tcl | 21 ++++++----- 12 files changed, 263 insertions(+), 136 deletions(-) diff --git a/ChangeLog b/ChangeLog index f25c17841..f462de5ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2003-10-09 Alexey Shchepin + + * src/ejabberd_c2s.erl: Added authentification logging + + * src/ejabberd_listener.erl: Added logging of accepted connections + + * src/stringprep/stringprep_drv.c: Cleanup + + * src/jd2ejd.erl: Added support for iq:private importing + + * src/mod_configure.erl: Fixed user removal + + * src/mod_private.erl: Added remove_user/1 + + * doc/guide.tex: Updated + + * src/mod_disco.erl: Added "extra_domains" option + 2003-10-08 Alexey Shchepin * src/ejabberd_c2s.erl: Added support for "jid-malformed" error diff --git a/doc/guide.html b/doc/guide.html index 241412549..b67031a26 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -65,7 +65,7 @@
  • 3.2.1  Node config: Global Configuration
  • 3.2.2  Node online users: List of Online Users -
  • 3.2.3  Node all users: List of Registered User +
  • 3.2.3  Node all users: List of Registered Users
  • 3.2.4  Node outgoing s2s: List of Outgoing S2S connections
  • 3.2.5  Node running nodes: List of Running ejabberd Nodes
  • 3.2.6  Node stopped nodes: List of Stopped Nodes @@ -411,15 +411,13 @@ runned on them. Each element of list is a tuple with following elements:
    • Port number;
    • Module that serves this port; -
    • Function in this module that starts connection (likely will be removed in - future versions of ejabberd);
    • Options to this module.
    Currently three modules are implemented:
    ejabberd_c2s
    This module serves C2S connections.

    -Following options defined: +The following options are defined:
    {access, <access rule>}
    This option defines access of users to this C2S port. Default value is ``all''. @@ -432,10 +430,10 @@ Following options defined: services (i. e. that use the jabber:component:accept namespace).
    For example, the following configuration defines that C2S connections are -listened on port 5222 and denied for user ``bad'', S2S on port 5269 -and that service conference.jabber.org must be connected to port 8888 -with a password ``secret''. Also all users except admins have traffic -limit 1000 b/s. +listened on port 5222 and 5223 (SSL) and denied for user ``bad'', S2S +on port 5269, and that service conference.example.org must be +connected to port 8888 with a password ``secret''. Also all users +except admins have traffic limit 1000 b/s.
     {acl, blocked, {user, "bad"}}.
     {access, c2s, [{deny, blocked},
    @@ -443,11 +441,13 @@ limit 1000 b/s.
     {shaper, normal, {maxrate, 1000}}.
     {access, c2s_shaper, [{none, admin},
                           {normal, all}]}.
    -{listen, [{5222, ejabberd_c2s,     start, [{access, c2s},
    -                                           {shaper, c2s_shaper}]},
    -          {5269, ejabberd_s2s_in,  start, []},
    -          {8888, ejabberd_service, start,
    -           [{host, "conference.jabber.org", [{password, "secret"}]}]}
    +{listen, [{5222, ejabberd_c2s,     [{access, c2s},
    +                                    {shaper, c2s_shaper}]},
    +          {5223, ejabberd_c2s,     [{access, c2s},
    +                                    {ssl, [{certfile, "/path/to/ssl.pem"}]}]},
    +          {5269, ejabberd_s2s_in,  []},
    +          {8888, ejabberd_service,
    +           [{host, "conference.example.org", [{password, "secret"}]}]}
              ]}.
     
    @@ -565,9 +565,9 @@ removed user is online, then he will be disconnected. Also user-related data

    3.2.2  Node online users: List of Online Users

    - + -

    3.2.3  Node all users: List of Registered User

    +

    3.2.3  Node all users: List of Registered Users


    @@ -678,15 +678,16 @@ does not exist, then it is opened and registered.

    A.1  Common Options

    -Following options used by many modules, so they described in separate section.
    +The following options are used by many modules, so they are described in +separate section.

    A.1.1  Option iqdisc

    Many modules define handlers for processing IQ queries of different namespaces -to this server or to user (e. g. to myjabber.org or to -user@myjabber.org). This option defines processing discipline of +to this server or to user (e. g. to example.org or to +user@example.org). This option defines processing discipline of these queries. Possible values are:
    no_queue
    All queries of namespace with this processing @@ -721,7 +722,7 @@ Example:
     {modules, [
                ...
    -           {mod_echo,      [{host, "echo.myjabber.org"}]},
    +           {mod_echo,      [{host, "echo.example.org"}]},
                ...
               ]}.
     
    @@ -745,12 +746,32 @@ Example:

    A.5  mod_disco

    +This module adds support for +JEP-0030 (Service +Discovery).
    +
    +Options: +
    +iqdisc
    http://jabber.org/protocol/disco#items and + http://jabber.org/protocol/disco#info IQ queries processing discipline. +
    extra_domains
    List of domains that will be added to server + items reply +
    +Example: +
    +{modules, [
    +           ...
    +           {mod_disco, [[{extra_domains, ["jit.example.com",
    +                                          "etc.example.com"]}]]},
    +           ...
    +          ]}.
    +

    A.6  mod_stats

    -This module adds support of +This module adds support for JEP-0039 (Statistics Gathering).

    Options: @@ -816,8 +837,8 @@ Options: Many modules supports xml:lang attribute inside IQ queries. E. g. -on figure 6 (compare with figure 1) showed reply -on following query: +on figure 6 (compare it with figure 1) showed +reply on following query:
     <iq id='5'
         to='e.localhost'
    diff --git a/doc/guide.tex b/doc/guide.tex
    index 0c0a6d0fe..e07f93401 100644
    --- a/doc/guide.tex
    +++ b/doc/guide.tex
    @@ -390,8 +390,6 @@ runned on them.  Each element of list is a tuple with following elements:
     \begin{itemize}
     \item Port number;
     \item Module that serves this port;
    -\item Function in this module that starts connection (likely will be removed in
    -  future versions of \ejabberd);
     \item Options to this module.
     \end{itemize}
     
    @@ -399,7 +397,7 @@ Currently three modules are implemented:
     \begin{description}
     \item[\texttt{ejabberd\_c2s}] This module serves C2S connections.
       
    -  Following options defined:
    +  The following options are defined:
       \begin{description}
       \item[\texttt{\{access, \}}] This option defines access of users
         to this C2S port.  Default value is ``\texttt{all}''.
    @@ -413,10 +411,10 @@ Currently three modules are implemented:
     \end{description}
     
     For example, the following configuration defines that C2S connections are
    -listened on port 5222 and denied for user ``\texttt{bad}'', S2S on port 5269
    -and that service \texttt{conference.jabber.org} must be connected to port 8888
    -with a password ``\texttt{secret}''.  Also all users except admins have traffic
    -limit 1000\,b/s.
    +listened on port 5222 and 5223 (SSL) and denied for user ``\texttt{bad}'', S2S
    +on port 5269, and that service \texttt{conference.example.org} must be
    +connected to port 8888 with a password ``\texttt{secret}''.  Also all users
    +except admins have traffic limit 1000\,b/s.
     \begin{verbatim}
     {acl, blocked, {user, "bad"}}.
     {access, c2s, [{deny, blocked},
    @@ -424,11 +422,13 @@ limit 1000\,b/s.
     {shaper, normal, {maxrate, 1000}}.
     {access, c2s_shaper, [{none, admin},
                           {normal, all}]}.
    -{listen, [{5222, ejabberd_c2s,     start, [{access, c2s},
    -                                           {shaper, c2s_shaper}]},
    -          {5269, ejabberd_s2s_in,  start, []},
    -          {8888, ejabberd_service, start,
    -           [{host, "conference.jabber.org", [{password, "secret"}]}]}
    +{listen, [{5222, ejabberd_c2s,     [{access, c2s},
    +                                    {shaper, c2s_shaper}]},
    +          {5223, ejabberd_c2s,     [{access, c2s},
    +                                    {ssl, [{certfile, "/path/to/ssl.pem"}]}]},
    +          {5269, ejabberd_s2s_in,  []},
    +          {8888, ejabberd_service,
    +           [{host, "conference.example.org", [{password, "secret"}]}]}
              ]}.
     \end{verbatim}
     
    @@ -539,7 +539,7 @@ removed user is online, then he will be disconnected.  Also user-related data
     
     
     
    -\subsubsection{Node \texttt{all users}: List of Registered User}
    +\subsubsection{Node \texttt{all users}: List of Registered Users}
     
     \begin{figure}[htbp]
       \centering
    @@ -640,19 +640,17 @@ does not exist, then it is opened and registered.
     \section{Built-in Modules}
     \label{sec:modules}
     
    -
    -
     \subsection{Common Options}
     \label{sec:modcommonopts}
     
    -Following options used by many modules, so they described in separate section.
    -
    +The following options are used by many modules, so they are described in
    +separate section.
     
     \subsubsection{Option \texttt{iqdisc}}
     
     Many modules define handlers for processing IQ queries of different namespaces
    -to this server or to user (e.\,g.\ to \texttt{myjabber.org} or to
    -\texttt{user@myjabber.org}).  This option defines processing discipline of
    +to this server or to user (e.\,g.\ to \texttt{example.org} or to
    +\texttt{user@example.org}).  This option defines processing discipline of
     these queries.  Possible values are:
     \begin{description}
     \item[\texttt{no\_queue}] All queries of namespace with this processing
    @@ -688,7 +686,7 @@ Example:
     \begin{verbatim}
     {modules, [
                ...
    -           {mod_echo,      [{host, "echo.myjabber.org"}]},
    +           {mod_echo,      [{host, "echo.example.org"}]},
                ...
               ]}.
     \end{verbatim}
    @@ -713,12 +711,33 @@ Example:
     \subsection{\moddisco{}}
     \label{sec:moddisco}
     
    +This module adds support for
    +\footahref{http://www.jabber.org/jeps/jep-0030.html}{JEP-0030} (Service
    +Discovery).
    +
    +Options:
    +\begin{description}
    +\item[\texttt{iqdisc}] \ns{http://jabber.org/protocol/disco#items} and
    +  \ns{http://jabber.org/protocol/disco#info} IQ queries processing discipline.
    +\item[\texttt{extra\_domains}] List of domains that will be added to server
    +  items reply
    +\end{description}
    +
    +Example:
    +\begin{verbatim}
    +{modules, [
    +           ...
    +           {mod_disco, [[{extra_domains, ["jit.example.com",
    +                                          "etc.example.com"]}]]},
    +           ...
    +          ]}.
    +\end{verbatim}
     
     
     \subsection{\modstats{}}
     \label{sec:modstats}
     
    -This module adds support of
    +This module adds support for
     \footahref{http://www.jabber.org/jeps/jep-0039.html}{JEP-0039} (Statistics Gathering).
     
     Options:
    @@ -784,8 +803,8 @@ Options:
     \label{sec:i18nl10n}
     
     Many modules supports \texttt{xml:lang} attribute inside IQ queries.  E.\,g.\ 
    -on figure~\ref{fig:discorus} (compare with figure~\ref{fig:disco}) showed reply
    -on following query:
    +on figure~\ref{fig:discorus} (compare it with figure~\ref{fig:disco}) showed
    +reply on following query:
     \begin{verbatim}
     
     		    case ejabberd_auth:check_password(
     			   U, P, StateData#state.streamid, D) of
     			true ->
    +			    ?INFO_MSG(
    +			       "(~w) Accepted legacy authentification for ~s",
    +			       [StateData#state.socket,
    +				jlib:jid_to_string(JID)]),
     			    ejabberd_sm:open_session(U, R),
     			    Res = jlib:make_result_iq_reply(El),
     			    send_element(StateData, Res),
    @@ -230,12 +234,19 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
     					     pres_t = ?SETS:from_list(Ts),
     					     privacy_list = PrivList}};
     			_ ->
    +			    ?INFO_MSG(
    +			       "(~w) Failed legacy authentification for ~s",
    +			       [StateData#state.socket,
    +				jlib:jid_to_string(JID)]),
     			    Err = jlib:make_error_reply(
     				    El, ?ERR_FORBIDDEN),
     			    send_element(StateData, Err),
     			    {next_state, wait_for_auth, StateData}
     		    end;
     		_ ->
    +		    ?INFO_MSG("(~w) Forbidden legacy authentification for ~s",
    +			      [StateData#state.socket,
    +			       jlib:jid_to_string(JID)]),
     		    Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
     		    send_element(StateData, Err),
     		    {next_state, wait_for_auth, StateData}
    @@ -286,6 +297,9 @@ wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
     		    JID = #jid{user = U, resource = R} =
     			jlib:string_to_jid(
     			  xml:get_attr_s(authzid, Props)),
    +		    ?INFO_MSG("(~w) Accepted authentification for ~s",
    +			      [StateData#state.socket,
    +			       jlib:jid_to_string(JID)]),
     		    {next_state, wait_for_stream,
     		     StateData#state{authentificated = true,
     				     user = U,
    @@ -350,6 +364,9 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
     				  [{"xmlns", ?NS_SASL}], []}),
     		    JID = #jid{user = U, resource = R} =
     			jlib:string_to_jid(xml:get_attr_s(authzid, Props)),
    +		    ?INFO_MSG("(~w) Accepted authentification for ~s",
    +			      [StateData#state.socket,
    +			       jlib:jid_to_string(JID)]),
     		    {next_state, wait_for_stream,
     		     StateData#state{authentificated = true,
     				     user = U,
    @@ -410,6 +427,9 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
     	    JID = jlib:make_jid(U, StateData#state.server, R),
     	    case acl:match_rule(StateData#state.access, JID) of
     		allow ->
    +		    ?INFO_MSG("(~w) Opened session for ~s",
    +			      [StateData#state.socket,
    +			       jlib:jid_to_string(JID)]),
     		    ejabberd_sm:open_session(U, R),
     		    Res = jlib:make_result_iq_reply(El),
     		    send_element(StateData, Res),
    @@ -425,6 +445,9 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
     				     pres_t = ?SETS:from_list(Ts),
     				     privacy_list = PrivList}};
     		_ ->
    +		    ?INFO_MSG("(~w) Forbidden session for ~s",
    +			      [StateData#state.socket,
    +			       jlib:jid_to_string(JID)]),
     		    Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
     		    send_element(StateData, Err),
     		    {next_state, wait_for_session, StateData}
    @@ -700,6 +723,9 @@ terminate(Reason, StateName, StateData) ->
     	"" ->
     	    ok;
     	_ ->
    +	    ?INFO_MSG("(~w) Close session for ~s",
    +		      [StateData#state.socket,
    +		       jlib:jid_to_string(StateData#state.jid)]),
     	    ejabberd_sm:close_session(StateData#state.user,
     				      StateData#state.resource),
                 From = StateData#state.jid,
    diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl
    index 73ffc9a21..9de102be0 100644
    --- a/src/ejabberd_listener.erl
    +++ b/src/ejabberd_listener.erl
    @@ -15,6 +15,8 @@
     	 init_ssl/4
     	]).
     
    +-include("ejabberd.hrl").
    +
     start_link() ->
         supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []).
     
    @@ -56,6 +58,13 @@ init(Port, Module, Opts) ->
     accept(ListenSocket, Module, Opts) ->
         case gen_tcp:accept(ListenSocket) of
     	{ok, Socket} ->
    +	    case {inet:sockname(Socket), inet:peername(Socket)} of
    +		{{ok, Addr}, {ok, PAddr}} ->
    +		    ?INFO_MSG("(~w) Accepted connection ~w -> ~w",
    +			      [Socket, PAddr, Addr]);
    +		_ ->
    +		    ok
    +	    end,
     	    {ok, Pid} = Module:start({gen_tcp, Socket}, Opts),
     	    %{ok, Pid} =
     	    %    supervisor:start_child(
    @@ -82,6 +91,13 @@ init_ssl(Port, Module, Opts, SSLOpts) ->
     accept_ssl(ListenSocket, Module, Opts) ->
         case ssl:accept(ListenSocket) of
     	{ok, Socket} ->
    +	    case {ssl:sockname(Socket), ssl:peername(Socket)} of
    +		{{ok, Addr}, {ok, PAddr}} ->
    +		    ?INFO_MSG("(~w) Accepted SSL connection ~w -> ~w",
    +			      [Socket, PAddr, Addr]);
    +		_ ->
    +		    ok
    +	    end,
     	    apply(Module, start_link, [{ssl, Socket}, Opts]),
     	    accept_ssl(ListenSocket, Module, Opts)
         end.
    diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
    index 456fc0a65..6c3815d3b 100644
    --- a/src/jd2ejd.erl
    +++ b/src/jd2ejd.erl
    @@ -93,9 +93,7 @@ wait_for_xdb(closed, StateData) ->
     xdb_data({xmlstreamelement, El}, StateData) ->
         {xmlelement, Name, Attrs, Els} = El,
         Server = StateData#state.server,
    -    From = {StateData#state.user,
    -	    Server,
    -	    ""},
    +    From = jlib:make_jid(StateData#state.user, Server, ""),
         NewState =
     	case xml:get_attr_s("xmlns", Attrs) of
     	    ?NS_AUTH ->
    @@ -109,9 +107,9 @@ xdb_data({xmlstreamelement, El}, StateData) ->
     		mod_roster:set_items(StateData#state.user, El),
     		StateData;
     	    ?NS_VCARD ->
    -		Res = mod_vcard:process_local_iq(From,
    -						 {"", ?MYNAME, ""},
    -						 {iq, "", set, ?NS_VCARD, El}),
    +		Res = mod_vcard:process_sm_iq(From,
    +					      jlib:make_jid("", ?MYNAME, ""),
    +					      {iq, "", set, ?NS_VCARD, El}),
     		StateData;
     	    "jabber:x:offline" ->
     		process_offline(From, El),
    @@ -124,8 +122,22 @@ xdb_data({xmlstreamelement, El}, StateData) ->
     	    %    io:format("user ~s~n", [User]),
     	    %    StateData;
     	    XMLNS ->
    -		io:format("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS]),
    -		StateData
    +		case xml:get_attr_s("j_private_flag", Attrs) of
    +		    "1" ->
    +			mod_private:process_local_iq(
    +			  From,
    +			  jlib:make_jid("", ?MYNAME, ""),
    +			  {iq, "", set, ?NS_PRIVATE,
    +			   {xmlelement, "query", [],
    +			    [jlib:remove_attr(
    +			       "j_private_flag",
    +			       jlib:remove_attr("xdbns", El))]}}),
    +			StateData;
    +		    _ ->
    +			io:format("jd2ejd: Unknown namespace \"~s\"~n",
    +				  [XMLNS]),
    +			StateData
    +		end
     	end,
         {next_state, xdb_data, NewState};
     
    @@ -191,7 +203,7 @@ handle_info(_, StateName, StateData) ->
     %% Returns: any
     %%----------------------------------------------------------------------
     terminate(Reason, StateName, StateData) ->
    -    StateData#state.pid ! {jd2ejd, exited},
    +    StateData#state.pid ! {jd2ejd, Reason},
         ok.
     
     %%%----------------------------------------------------------------------
    @@ -217,12 +229,20 @@ process_offline(To, {xmlelement, _, _, Els}) ->
     
     
     import_file(File) ->
    +    clear_queue(),
         start(File),
         receive
    -	M -> M
    -	after 1000 -> ok
    +	{jd2ejd, Result} -> Result
    +	after 4000 -> timeout
         end.
     
    +clear_queue() ->
    +    receive
    +	{jd2ejd, _Result} -> clear_queue
    +	after 0 -> ok
    +    end.
    +    
    +
     import_dir(Dir) ->
         {ok, Files} = file:list_dir(Dir),
         MsgFiles = lists:filter(
    diff --git a/src/mod_configure.erl b/src/mod_configure.erl
    index 95bf4e417..18cdbdc90 100644
    --- a/src/mod_configure.erl
    +++ b/src/mod_configure.erl
    @@ -665,13 +665,16 @@ set_form(["config", "remusers"], Lang, XData) ->
           fun({Var, Vals}) ->
     	      case Vals of
     		  ["1"] ->
    -		      ejabberd_sm ! {route, {"", "", ""}, {Var, "", ""},
    +		      ejabberd_sm ! {route,
    +				     jlib:make_jid("", "", ""),
    +				     jlib:make_jid(Var, "", ""),
     				     {xmlelement, "broadcast", [],
     				      [{exit, "User removed"}]}},
     		      catch ejabberd_auth:remove_user(Var),
     		      catch mod_roster:remove_user(Var),
     		      catch mod_offline:remove_user(Var),
    -		      catch mod_vcard:remove_user(Var);
    +		      catch mod_vcard:remove_user(Var),
    +		      catch mod_private:remove_user(Var);
     		  _ ->
     		      ok
     	      end
    @@ -703,7 +706,7 @@ process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
     	deny ->
     	    {iq, ID, error, XMLNS, [SubEl, ?ERR_NOT_ALLOWED]};
     	allow ->
    -	    {User, _, _} = To,
    +	    #jid{user = User} = To,
     	    Lang = xml:get_tag_attr_s("xml:lang", SubEl),
     	    case Type of
     		set ->
    @@ -790,5 +793,24 @@ get_sm_form(_, _, Lang) ->
         {error, ?ERR_SERVICE_UNAVAILABLE}.
     
     
    +set_sm_form(User, [], Lang, XData) ->
    +    case lists:keysearch("action", 1, XData) of
    +	{value, {_, ["edit"]}} ->
    +	    {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
    +	{value, {_, ["remove"]}} ->
    +	    ejabberd_sm ! {route,
    +			   jlib:make_jid("", "", ""),
    +			   jlib:make_jid(User, "", ""),
    +			   {xmlelement, "broadcast", [],
    +			    [{exit, "User removed"}]}},
    +	    catch ejabberd_auth:remove_user(User),
    +	    catch mod_roster:remove_user(User),
    +	    catch mod_offline:remove_user(User),
    +	    catch mod_vcard:remove_user(User),
    +	    catch mod_private:remove_user(User),
    +	    {result, []};
    +	_ ->
    +	    {error, ?ERR_BAD_REQUEST}
    +    end;
     set_sm_form(_, _, Lang, XData) ->
         {error, ?ERR_SERVICE_UNAVAILABLE}.
    diff --git a/src/mod_disco.erl b/src/mod_disco.erl
    index a85553d3b..f658b58f8 100644
    --- a/src/mod_disco.erl
    +++ b/src/mod_disco.erl
    @@ -19,7 +19,9 @@
     	 process_sm_iq_items/3,
     	 process_sm_iq_info/3,
     	 register_feature/1,
    -	 unregister_feature/1]).
    +	 unregister_feature/1,
    +	 register_extra_domain/1,
    +	 unregister_extra_domain/1]).
     
     -include("ejabberd.hrl").
     -include("jlib.hrl").
    @@ -42,6 +44,9 @@ start(Opts) ->
         register_feature("iq"),
         register_feature("presence"),
         register_feature("presence-invisible"),
    +    catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
    +    ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
    +    lists:foreach(fun register_extra_domain/1, ExtraDomains),
         ok.
     
     stop() ->
    @@ -59,6 +64,14 @@ unregister_feature(Feature) ->
         catch ets:new(disco_features, [named_table, ordered_set, public]),
         ets:delete(disco_features, Feature).
     
    +register_extra_domain(Domain) ->
    +    catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
    +    ets:insert(disco_extra_domains, {Domain}).
    +
    +unregister_extra_domain(Domain) ->
    +    catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
    +    ets:delete(disco_extra_domains, Domain).
    +
     process_local_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
         Lang = xml:get_tag_attr_s("xml:lang", SubEl),
         case Type of
    @@ -180,10 +193,11 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
     feature_to_xml({Feature}) ->
         {xmlelement, "feature", [{"var", Feature}], []}.
     
    +domain_to_xml({Domain}) ->
    +    {xmlelement, "item", [{"jid", Domain}], []};
     domain_to_xml(Domain) ->
         {xmlelement, "item", [{"jid", Domain}], []}.
     
    -
     -define(NODE(Name, Node),
     	{xmlelement, "item",
     	 [{"jid", Server},
    @@ -193,12 +207,14 @@ domain_to_xml(Domain) ->
     
     get_services_only() ->
         lists:map(fun domain_to_xml/1,
    -	      ejabberd_router:dirty_get_all_routes()).
    +	      ejabberd_router:dirty_get_all_routes()) ++
    +	lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)).
     
     get_local_items([], Server, Lang) ->
         Domains =
     	lists:map(fun domain_to_xml/1,
    -		  ejabberd_router:dirty_get_all_routes()),
    +		  ejabberd_router:dirty_get_all_routes()) ++
    +	lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)),
         {result,
          Domains ++
          [?NODE("Configuration",            "config"),
    diff --git a/src/mod_private.erl b/src/mod_private.erl
    index 8b091433f..a4ec97d1d 100644
    --- a/src/mod_private.erl
    +++ b/src/mod_private.erl
    @@ -14,7 +14,8 @@
     
     -export([start/1,
     	 stop/0,
    -	 process_local_iq/3]).
    +	 process_local_iq/3,
    +	 remove_user/1]).
     
     -include("ejabberd.hrl").
     -include("jlib.hrl").
    @@ -95,3 +96,20 @@ get_data(LUser, [El | Els], Res) ->
     	_ ->
     	    get_data(LUser, Els, Res)
         end.
    +
    +
    +remove_user(User) ->
    +    LUser = jlib:nodeprep(User),
    +    F = fun() ->
    +		lists:foreach(
    +		  fun({U, _} = Key) ->
    +			  if
    +			      U == LUser ->
    +				  mnesia:delete({private_storage, Key});
    +			      true ->
    +				  ok
    +			  end
    +		  end, mnesia:all_keys(private_storage))
    +        end,
    +    mnesia:transaction(F).
    +
    diff --git a/src/stringprep/stringprep_drv.c b/src/stringprep/stringprep_drv.c
    index 906ea6207..43dd2b87a 100644
    --- a/src/stringprep/stringprep_drv.c
    +++ b/src/stringprep/stringprep_drv.c
    @@ -37,7 +37,7 @@ static void stringprep_erl_stop(ErlDrvData handle)
      * library
      */
     
    -void canonical_ordering(int *str, int len)
    +static void canonical_ordering(int *str, int len)
     {
        int i, j, t;
        int last, next;
    @@ -232,13 +232,6 @@ static int stringprep_erl_control(ErlDrvData drv_data,
           }
           
           info = GetUniCharInfo(uc);
    -      //if(info & prohibit) {
    -      //   *rbuf = rstring;
    -      //   driver_free(str32);
    -      //   return 1;
    -      //}
    -
    -      //printf("Got %x\r\n", uc);
     
           if(!(info & B1Mask)) 
           {
    @@ -247,34 +240,10 @@ static int stringprep_erl_control(ErlDrvData drv_data,
     	    {
     	       ruc = uc + GetDelta(info);
     	       ADD_DECOMP(ruc);
    -
    -	       //info = GetUniCharDecompInfo(ruc);
    -	       //if(info >= 0) {
    -	       //   decomp_len = GetDecompLen(info);
    -	       //   decomp_shift = GetDecompShift(info);
    -	       //   for(j = 0; j < decomp_len; j++) {
    -	       //      ADD_UCHAR32(str32, str32pos, str32len,
    -	       // 		 decompList[decomp_shift + j]);
    -	       //   }
    -	       //} else {
    -	       //   ADD_UCHAR32(str32, str32pos, str32len, ruc);
    -	       //}
    -
    -	       //info = GetUniCharDecompInfo(ruc);
    -	       //if(info >= 0) {
    -	       //   printf("Decomposition %x: ", ruc);
    -	       //   for(j = 0; j < GetDecompLen(info); j++) {
    -	       //      printf("%x ", decompList[GetDecompShift(info) + j]);
    -	       //   }
    -	       //   printf("\r\n");
    -	       //}
    -	       
    -	       //ADD_UCHAR(ruc);
     	    } else {
     	       mc = GetMC(info);
     	       for(j = 1; j <= mc[0]; j++) {
     		  ruc = mc[j];
    -		  //printf("Char %x cclass %d\r\n", ruc, GetUniCharCClass(ruc));
     		  ADD_DECOMP(ruc);
     	       }
     	    }
    @@ -292,19 +261,8 @@ static int stringprep_erl_control(ErlDrvData drv_data,
           return 1;
        }
     
    -   //printf("\r\n");
    -   //printf("DECOMPOSED:\t");
    -   //for(i = 0; i < str32pos; i++)
    -   //   printf("%4x ", str32[i]);
    -   //printf("\r\n");
    -
        canonical_ordering(str32, str32pos);
     
    -   //printf("ORDERED:\t");
    -   //for(i = 0; i < str32pos; i++)
    -   //   printf("%4x ", str32[i]);
    -   //printf("\r\n");
    -
        comp_pos = 1;
        comp_starter_pos = 0;
        ch1 = str32[0];
    @@ -314,7 +272,6 @@ static int stringprep_erl_control(ErlDrvData drv_data,
        {
           ch2 = str32[i];
           cclass2 = GetUniCharCClass(ch2);
    -      //printf("Compose: %x + %x = %x\r\n", ch1, ch2, compose(ch1, ch2));
           if(cclass1 == 0 && cclass2 > cclass_prev && (ruc = compose(ch1, ch2))) {
     	 ch1 = ruc;
           } else {
    @@ -332,11 +289,6 @@ static int stringprep_erl_control(ErlDrvData drv_data,
        str32[comp_starter_pos] = ch1;
        str32pos = comp_pos;
        
    -   //printf("COMPOSED:\t");
    -   //for(i = 0; i < str32pos; i++)
    -   //   printf("%4x ", str32[i]);
    -   //printf("\r\n");
    -   
        for(i = 0; i < str32pos; i++)
        {
           ruc = str32[i];
    @@ -349,9 +301,6 @@ static int stringprep_erl_control(ErlDrvData drv_data,
           ADD_UCHAR(ruc);
        }
        
    -   //printf("Compose: %x\r\n", compose(0x438, 0x301));
    -   //printf("Pos: %d\r\n", pos);
    -
        rstring[0] = 1;
        *rbuf = rstring;
        driver_free(str32);
    diff --git a/src/stringprep/uni_norm.c b/src/stringprep/uni_norm.c
    index d95ad5ba0..fe7281b46 100644
    --- a/src/stringprep/uni_norm.c
    +++ b/src/stringprep/uni_norm.c
    @@ -77,9 +77,8 @@ static unsigned char cclassPageMap[] = {
     };
     
     /*
    - * The groupMap is indexed by combining the alternate page number with
    - * the page offset and returns a group number that identifies a unique
    - * set of character attributes.
    + * The cclassGroupMap is indexed by combining the alternate page number with
    + * the page offset and returns a combining class number.
      */
     
     static unsigned char cclassGroupMap[] = {
    @@ -297,9 +296,9 @@ static unsigned char decompPageMap[] = {
     };
     
     /*
    - * The groupMap is indexed by combining the alternate page number with
    - * the page offset and returns a group number that identifies a unique
    - * set of character attributes.
    + * The decompGroupMap is indexed by combining the alternate page number with
    + * the page offset and returns a group number that identifies a length and
    + * shift of decomposition sequence in decompList
      */
     
     static int decompGroupMap[] = {
    @@ -839,7 +838,7 @@ static int decompGroupMap[] = {
     };
     
     /*
    - * Each group represents a unique set of character attributes.  The attributes...
    + * List of decomposition sequences
      */
     
     static int decompList[] = {
    @@ -1295,8 +1294,6 @@ static int decompList[] = {
      * Unicode character tables.
      */
     
    -//#define GetUniCharInfo(ch) (groups[groupMap[(pageMap[(((int)(ch)) & 0xffff) >> CCLASS_OFFSET_BITS] << CCLASS_OFFSET_BITS) | ((ch) & ((1 << CCLASS_OFFSET_BITS)-1))]])
    -
     #define GetUniCharDecompInfo(ch) (decompGroupMap[(decompPageMap[(((int)(ch)) & 0xffff) >> DECOMP_OFFSET_BITS] << DECOMP_OFFSET_BITS) | ((ch) & ((1 << DECOMP_OFFSET_BITS)-1))])
     
     #define GetDecompShift(info) ((info) & 0xffff)
    @@ -1556,7 +1553,7 @@ static int compGroupMap[] = {
     };
     
     /*
    - * ...
    + * Lists of compositions for characters that appears only in one composition
      */
     
     static int compFirstList[][2] = {
    @@ -1612,6 +1609,10 @@ static int compSecondList[][2] = {
         {1575, 1570}, {1575, 1573}
     };
     
    +/*
    + * Compositions matrix
    + */
    +
     static int compBothList[144][37] = {
         {
             8179, 8060, 974, 0, 8032, 0, 8033, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    diff --git a/src/stringprep/uni_parse2.tcl b/src/stringprep/uni_parse2.tcl
    index 2f3ef7ecd..968f262b7 100644
    --- a/src/stringprep/uni_parse2.tcl
    +++ b/src/stringprep/uni_parse2.tcl
    @@ -423,9 +423,8 @@ static unsigned char cclassPageMap\[\] = {"
         puts $f "};
     
     /*
    - * The groupMap is indexed by combining the alternate page number with
    - * the page offset and returns a group number that identifies a unique
    - * set of character attributes.
    + * The cclassGroupMap is indexed by combining the alternate page number with
    + * the page offset and returns a combining class number.
      */
     
     static unsigned char cclassGroupMap\[\] = {"
    @@ -476,9 +475,9 @@ static unsigned char decompPageMap\[\] = {"
         puts $f "};
     
     /*
    - * The groupMap is indexed by combining the alternate page number with
    - * the page offset and returns a group number that identifies a unique
    - * set of character attributes.
    + * The decompGroupMap is indexed by combining the alternate page number with
    + * the page offset and returns a group number that identifies a length and
    + * shift of decomposition sequence in decompList
      */
     
     static int decompGroupMap\[\] = {"
    @@ -502,7 +501,7 @@ static int decompGroupMap\[\] = {"
         puts $f "};
     
     /*
    - * Each group represents a unique set of character attributes.  The attributes...
    + * List of decomposition sequences
      */
     
     static int decompList\[\] = {"
    @@ -529,8 +528,6 @@ static int decompList\[\] = {"
      * Unicode character tables.
      */
     
    -//#define GetUniCharInfo(ch) (groups\[groupMap\[(pageMap\[(((int)(ch)) & 0xffff) >> CCLASS_OFFSET_BITS\] << CCLASS_OFFSET_BITS) | ((ch) & ((1 << CCLASS_OFFSET_BITS)-1))\]\])
    -
     #define GetUniCharDecompInfo(ch) (decompGroupMap\[(decompPageMap\[(((int)(ch)) & 0xffff) >> DECOMP_OFFSET_BITS\] << DECOMP_OFFSET_BITS) | ((ch) & ((1 << DECOMP_OFFSET_BITS)-1))\])
     
     #define GetDecompShift(info) ((info) & 0xffff)
    @@ -588,7 +585,7 @@ static int compGroupMap\[\] = {"
         puts $f "};
     
     /*
    - * ...
    + * Lists of compositions for characters that appears only in one composition
      */
     
     static int compFirstList\[\]\[2\] = {"
    @@ -627,6 +624,10 @@ static int compSecondList\[\]\[2\] = {"
         puts $f $line
         puts $f "};
     
    +/*
    + * Compositions matrix
    + */
    +
     static int compBothList\[[llength $comp_x_list]\]\[[llength $comp_y_list]\] = {"
         set lastx [expr {[llength $comp_x_list] - 1}]
         set lasty [expr {[llength $comp_y_list] - 1}]