public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ejabberd] f45: auto-import ejabberd-1.1.1-7.fc5 on branch devel from
@ 2026-09-23 19:06 Jeffrey C. Ollie
  0 siblings, 0 replies; only message in thread
From: Jeffrey C. Ollie @ 2026-09-23 19:06 UTC (permalink / raw)
  To: git-commits

        A new commit has been pushed.

        Repo   : rpms/ejabberd
        Branch : f45
        Commit : 73b15bb9533c40510176c67b8d29d61755e6aee7
        Author : Jeffrey C. Ollie <jcollie@fedoraproject.org>
        Date   : 2006-06-23T12:40:03+00:00
        Stats  : +1984/-0 in 10 file(s)
        URL    : https://src.fedoraproject.org/rpms/ejabberd/c/73b15bb9533c40510176c67b8d29d61755e6aee7?branch=f45

        Log:
        auto-import ejabberd-1.1.1-7.fc5 on branch devel from
ejabberd-1.1.1-7.fc5.src.rpm

---
diff --git a/.cvsignore b/.cvsignore
index e69de29..d206494 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -0,0 +1 @@
+ejabberd-1.1.1.tar.gz

diff --git a/ejabberd.init b/ejabberd.init
new file mode 100644
index 0000000..6384a6d
--- /dev/null
+++ b/ejabberd.init
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# ejabberd    Starts, Stops and Reloads ejabberd.
+#
+# chkconfig: - 40 60
+# description: ejabberd
+# processname: ejabberd
+# pidfile: /var/run/ejabberd.pid
+
+. /etc/rc.d/init.d/functions
+
+start() {
+        echo -n $"Starting ejabberd: "
+        daemon --user=ejabberd --check=ejabberd \
+	    "erl" "-pa @libdir@/ejabberd-@version@/ebin \
+                   -sname ejabberd \
+                   -s ejabberd \
+                   -ejabberd config \\\"/etc/ejabberd/ejabberd.cfg\\\" \
+                             log_path \\\"/var/log/ejabberd/ejabberd.log\\\" \
+                   -sasl sasl_error_logger \\{file,\\\"/var/log/ejabberd/sasl.log\\\"\\} \
+                   -mnesia dir \\\"/var/lib/ejabberd/spool\\\" \
+                   -kernel inetrc \\\"/etc/ejabberd/inetrc\\\" \
+                   -detached"
+        RETVAL=$?
+        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ejabberd
+        echo
+        return $RETVAL
+}
+
+stop() {
+        # Stop daemons.
+        echo -n "Shutting down ejabberd: "
+        runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` stop" && success || failure
+        RETVAL=$?
+        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ejabberd
+        echo
+        return $RETVAL
+}
+
+restart() {
+        stop
+        start
+}
+
+# See how we were called.
+case "$1" in
+  start)
+        start
+        ;;
+  stop)
+        stop
+        ;;
+  restart)
+        restart
+        ;;
+  condrestart)
+        [ -f /var/lock/subsys/ejabberd ] && restart || :
+        ;;
+  status)
+        runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` status"
+        ;;
+  *)
+        echo "Usage: ejabberd {start|stop|restart|reload|condrestart|status}"
+        exit 1
+esac
+
+exit $?
+
+

diff --git a/ejabberd.logrotate b/ejabberd.logrotate
new file mode 100644
index 0000000..37319d8
--- /dev/null
+++ b/ejabberd.logrotate
@@ -0,0 +1,9 @@
+/var/log/ejabberd/ejabberd.log /var/log/ejabberd/sasl.log {
+    missingok
+    notifempty
+    create 0640 ejabberd ejabberd
+    sharedscripts
+    postrotate
+        runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` reopen-log >/dev/null 2>/dev/null || true
+    endscript
+}

diff --git a/ejabberd.spec b/ejabberd.spec
new file mode 100644
index 0000000..2137a77
--- /dev/null
+++ b/ejabberd.spec
@@ -0,0 +1,175 @@
+Name:           ejabberd
+Version:        1.1.1
+Release:        7%{?dist}
+Summary:        A distributed, fault-tolerant Jabber/XMPP server
+
+Group:          Applications/Internet
+License:        GPL
+URL:            http://ejabberd.jabber.ru/
+Source0:        http://www.process-one.net/en/projects/ejabberd/download/%{version}/ejabberd-%{version}.tar.gz
+Source1:	ejabberd.init
+Source2:	ejabberd.logrotate
+Source3:	inetrc
+
+# http://ejabberd.jabber.ru/ejabberdctl-extra
+Source4:        http://ejabberd.jabber.ru/files/efiles/mod_ctlextra.erl
+
+# The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html
+Source5:        ejabberd_auth_ad.erl
+Source6:        mod_shared_roster_ad.erl
+Source7:        mod_vcard_ad.erl
+
+BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires:  expat-devel
+BuildRequires:	openssl-devel
+BuildRequires:  erlang
+BuildRequires:  hevea
+
+Requires:       erlang
+
+Requires(pre): fedora-usermgmt
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+
+%description
+ejabberd is a Free and Open Source distributed fault-tolerant
+Jabber/XMPP server. It is mostly written in Erlang, and runs on many
+platforms (tested on Linux, FreeBSD, NetBSD, Solaris, Mac OS X and
+Windows NT/2000/XP).
+
+%package doc
+Summary: Documentation for ejabberd
+Group: Documentation
+
+%description doc
+Documentation for ejabberd.
+
+%pre
+/usr/sbin/fedora-groupadd 27 -r ejabberd &>/dev/null || :
+/usr/sbin/fedora-useradd  27 -r -s /sbin/nologin -d /var/lib/ejabberd -M \
+                             -c 'ejabberd' -g ejabberd ejabberd &>/dev/null || :
+
+%post
+/sbin/chkconfig --add ejabberd
+
+%preun
+if [ $1 = 0 ]; then
+        /sbin/service ejabberd stop >/dev/null 2>&1
+        /sbin/chkconfig --del ejabberd
+fi
+
+%postun
+if [ "$1" -ge "1" ]; then
+        /sbin/service ejabberd condrestart >/dev/null 2>&1
+fi
+
+%prep
+%setup -q
+
+%{__perl} -pi -e "s!/var/lib/ejabberd!%{_libdir}/ejabberd-%{version}!g" src/Makefile.in
+%{__perl} -pi -e "s!/etc!%{_sysconfdir}!g" src/Makefile.in
+%{__perl} -pi -e "s!\@prefix\@!!g" src/Makefile.in
+
+cp %{S:4} src
+cp %{S:5} src
+cp %{S:6} src
+cp %{S:7} src
+
+%build
+pushd src
+%configure --enable-odbc
+make %{?_smp_mflags}
+popd
+pushd doc
+make html pdf
+popd
+
+%install
+rm -rf %{buildroot}
+
+pushd src
+make install DESTDIR=%{buildroot}
+popd
+
+chmod a+x %{buildroot}%{_libdir}/ejabberd-%{version}/priv/lib/*.so
+
+%{__perl} -pi -e 's!./ssl.pem!/etc/ejabberd/ejabberd.pem!g' %{buildroot}/etc/ejabberd/ejabberd.cfg
+
+mkdir -p %{buildroot}/var/log/ejabberd
+mkdir -p %{buildroot}/var/lib/ejabberd/spool
+
+mkdir -p %{buildroot}%{_initrddir}
+cp %{S:1} %{buildroot}%{_initrddir}/ejabberd
+chmod a+x %{buildroot}%{_initrddir}/ejabberd
+
+mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d
+cp %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+
+%{__perl} -pi -e 's!\@libdir\@!%{_libdir}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+%{__perl} -pi -e 's!\@version\@!%{version}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+
+cp %{S:3} %{buildroot}%{_sysconfdir}/ejabberd/inetrc
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING
+
+%attr(750,ejabberd,ejabberd) %dir %{_sysconfdir}/ejabberd
+%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberd.cfg
+%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/inetrc
+
+%{_initrddir}/ejabberd
+%config(noreplace) %{_sysconfdir}/logrotate.d/ejabberd
+
+%dir %{_libdir}/ejabberd-%{version}
+%dir %{_libdir}/ejabberd-%{version}/ebin
+%{_libdir}/ejabberd-%{version}/ebin/*.app
+%{_libdir}/ejabberd-%{version}/ebin/*.beam
+
+%dir %{_libdir}/ejabberd-%{version}/priv
+
+%dir %{_libdir}/ejabberd-%{version}/priv/lib
+%{_libdir}/ejabberd-%{version}/priv/lib/*.so
+
+%dir %{_libdir}/ejabberd-%{version}/priv/msgs
+%{_libdir}/ejabberd-%{version}/priv/msgs/*.msg
+
+%attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd
+%attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd/spool
+
+%attr(750,ejabberd,ejabberd) %dir /var/log/ejabberd
+
+%files doc
+%defattr(-,root,root,-)
+%doc ChangeLog COPYING TODO doc/*.pdf doc/*.html doc/*.png doc/release_notes_*
+
+%changelog
+* Thu Jun 22 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-7
+- Oops drop bad patch.
+
+* Thu Jun 22 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-6
+- Split documentation off to a subpackage.
+- Own %{_libdir}/ejabberd-%{version}
+- Mark %{_sysconfdir}/logrotate.d/ejabberd as %config
+
+* Thu Jun  8 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-5
+- Patch the makefile so that it adds a soname to shared libs.
+
+* Fri May 26 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-4
+- Modify AD modules not to check for group membership.
+
+* Thu May 25 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-3
+- Add some extra modules
+
+* Wed May 24 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-2
+- Munge Makefile.in a bit more...
+- Change ownership/permissions - not *everything* needs to be owned by ejabberd
+
+* Wed May 24 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-1
+- First version for Fedora Extras

diff --git a/ejabberd_auth_ad.erl b/ejabberd_auth_ad.erl
new file mode 100644
index 0000000..5e9e452
--- /dev/null
+++ b/ejabberd_auth_ad.erl
@@ -0,0 +1,167 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_auth_ad.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Author  : Alex Gorbachenko <agent_007@immo.ru>
+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Purpose : Authentification via Active Directory
+%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id: ejabberd_auth_ad.erl 386 2005-12-20 10:06:37Z agent_007 $
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_auth_ad).
+-author('alexey@sevcom.net').
+-author('agent_007@immo.ru').
+-author('realloc@realloc.spb.ru').
+-vsn('$Revision: 386 $ ').
+
+%% External exports
+-export([start/1,
+	 set_password/3,
+	 check_password/3,
+	 check_password/5,
+	 try_register/3,
+	 dirty_get_registered_users/0,
+	 get_vh_registered_users/1,
+	 get_password/2,
+	 get_password_s/2,
+	 is_user_exists/2,
+	 remove_user/2,
+	 remove_user/3,
+	 plain_password_required/0
+	]).
+
+-include("ejabberd.hrl").
+-include("eldap/eldap.hrl").
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start(Host) ->
+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+    Password = ejabberd_config:get_local_option({ad_password, Host}),
+    eldap:start_link(get_eldap_id(Host, ejabberd),
+		     LDAPServers, 389, RootDN, Password),
+    eldap:start_link(get_eldap_id(Host, ejabberd_bind),
+		     LDAPServers, 389, RootDN, Password),
+    ok.
+
+plain_password_required() ->
+    true.
+
+check_password(User, Server, Password) ->
+    case find_user_dn(User, Server) of
+	false ->
+	    false;
+	DN ->
+	    LServer = jlib:nameprep(Server),
+	    case eldap:bind(get_eldap_id(LServer, ejabberd_bind),
+			    DN, Password) of
+		ok ->
+		    true;
+		_ ->
+		    false
+	    end
+    end.
+
+check_password(User, Server, Password, _StreamID, _Digest) ->
+    check_password(User, Server, Password).
+
+set_password(_User, _Server, _Password) ->
+    {error, not_allowed}.
+
+try_register(_User, _Server, _Password) ->
+    {error, not_allowed}.
+
+dirty_get_registered_users() ->
+    get_vh_registered_users(?MYNAME).
+
+get_vh_registered_users(Server) ->
+    LServer = jlib:nameprep(Server),
+    Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+%    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+    FilterPerson = eldap:equalityMatch("objectCategory", "person"),
+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
+    FilterHidden = eldap:equalityMatch("description", "hidden"),
+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+    FilterDef = eldap:present(Attr),
+    Filter = eldap:'and'([
+			  FilterDef,
+			  FilterPerson,
+%			  FilterGroup,
+			  eldap:'not'(FilterComp),
+			  eldap:'not'(FilterHidden),
+			  eldap:'not'(FilterLive)]),
+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
+    case eldap:search(get_eldap_id(LServer, ejabberd),
+		      [{base, Base},
+		       {filter, Filter},
+		       {attributes, [Attr]}]) of
+	#eldap_search_result{entries = Es} ->
+	    lists:flatmap(
+	      fun(E) ->
+		      case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
+			  {value, {_, [U]}} ->
+			      case jlib:nodeprep(U) of
+				  error ->
+				      [];
+				  LU ->
+				      [{LU, LServer}]
+			      end;
+			  _ ->
+			      []
+		      end
+	      end, Es);
+	_ ->
+	    []
+    end.
+
+get_password(_User, _Server) ->
+    false.
+
+get_password_s(_User, _Server) ->
+    "".
+
+is_user_exists(User, Server) ->
+    case find_user_dn(User, Server) of
+	false ->
+	    false;
+	_DN ->
+	    true
+    end.
+
+remove_user(_User, _Server) ->
+    {error, not_allowed}.
+
+remove_user(_User, _Server, _Password) ->
+    not_allowed.
+
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+find_user_dn(User, Server) ->
+    LServer = jlib:nameprep(Server),
+    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+    Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+    FilterAttr = eldap:equalityMatch(Attr, User),
+    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+    Filter = eldap:'and'([
+                          FilterAttr,
+                          FilterGroup
+                          ]),
+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
+    case eldap:search(get_eldap_id(LServer, ejabberd),
+		      [{base, Base},
+		       {filter, Filter},
+		       {attributes, []}]) of
+	#eldap_search_result{entries = [E | _]} ->
+	    E#eldap_entry.object_name;
+	_ ->
+	    false
+    end.
+
+get_eldap_id(Host, Name) ->
+    atom_to_list(gen_mod:get_module_proc(Host, Name)).

diff --git a/inetrc b/inetrc
new file mode 100644
index 0000000..2d2f635
--- /dev/null
+++ b/inetrc
@@ -0,0 +1 @@
+{file, resolv, "/etc/resolv.conf"}.

diff --git a/mod_ctlextra.erl b/mod_ctlextra.erl
new file mode 100644
index 0000000..dfff6ce
--- /dev/null
+++ b/mod_ctlextra.erl
@@ -0,0 +1,502 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_ctlextra.erl
+%%% Author  :
+%%% Purpose : Adds more options for ejabberd_ctl
+%%% Created :
+%%% Id      :
+%%%----------------------------------------------------------------------
+
+%%% mod_ctlextra v0.2.2 for ejabberd 1.1.1 (18/May/2006) 
+%%%
+%%% INSTALL:
+%%%  1. Copy mod_ctlextra.erl to your ejabberd/src
+%%%  2. Add to your ejabberd.cfg, on the modules section:
+%%%         {mod_ctlextra, []},
+%%%  3. Recompile and restart ejabberd 
+%%%
+%%% USAGE:
+%%%  - Now you have several new options for ejabberd-ctl
+%%%  - Example for vcard: ejabberdctl eja@host vcard-get joe myjab.net email
+%%%  - The file used by 'pushroster' and 'pushroster-all' must be placed on 
+%%%      the same directory where the .beam files are. Example content:
+%%%         [{"bob", "myserver", "workers", "Bob"},
+%%%          {"mart", "myserver", "workers", "Mart"},
+%%%          {"Rich", "myserver", "bosses", "Rich"}].
+
+-module(mod_ctlextra).
+-author('').
+-vsn('Version 0.2.1').
+
+-behaviour(gen_mod).
+
+-export([
+	start/2,
+	stop/1,
+	ctl_process/2,
+	ctl_process/3
+	]).
+
+-include("ejabberd_ctl.hrl").
+-include("jlib.hrl").
+
+start(Host, _Opts) ->
+	ejabberd_ctl:register_commands([
+		{"compile file", "recompile and reload file"},
+		{"load-config file", "load config from file"},
+		{"remove-node nodename", "remove an ejabberd node from the database"},
+
+		% ejabberd_auth
+		{"delete-older-users days", "delete users that have not logged in the last 'days'"},
+		{"set-password user server password", "set password to user@server"},
+
+		% ejd2odbc
+		{"export2odbc server output", "export all possible tables on server to output"},
+
+		% mod_offline
+		{"delete-older-messages days", "delete offline messages older than 'days'"},
+
+		% mod_shared_roster
+		{"srg-create group host name description display", "create the group with options"},
+		{"srg-delete group host", "delete the group"},
+		{"srg-user-add user server group host", "add user@server to group on host"},
+		{"srg-user-del user server group host", "delete user@server from group on host"},
+			
+		% mod_vcard
+		{"vcard-get user host data [data2]", "get data from the vCard of the user"},
+		{"vcard-set user host data [data2] content", "set data to content on the vCard"},
+
+		% mod_announce
+		% announce_send_online host message
+		% announce_send_all host, message
+
+		% mod_muc
+		% muc-add room opts
+		% muc-del room
+		% muc-del-older 90 : delete rooms older than X days (with no activity (chat, presence, logins) in 6 months)
+
+		% mod_roster
+		{"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1"},
+		%{"", "subs= none, from, to or both"},
+		%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
+		%{"", "will add mike@server.com to peter@localhost roster"},
+		{"pushroster file user server", "push template roster in file to user@server"},
+		{"pushroster-all file", "push template roster in file to all those users"},
+		{"push-alltoall server group", "adds all the users to all the users in Group"},
+
+		{"stats registeredusers", "number of registered users"},
+		{"stats onlineusers", "number of logged users"},
+
+		% misc
+		{"killsession user server resource", "kill a user session"}
+	], ?MODULE, ctl_process),
+    ejabberd_ctl:register_commands(Host, [
+		% mod_last
+		{"num-active-users days", "number of users active in the last 'days'"},
+		{"stats registeredusers", "number of registered users"},
+		{"stats onlineusers", "number of logged users"}
+	], ?MODULE, ctl_process),
+	ok.
+
+stop(_Host) ->
+	ok.
+
+
+ctl_process(_Val, ["blo"]) ->
+	FResources = "eeeaaa aaa",
+	io:format("~s", [FResources]),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["delete-older-messages", Days]) ->
+    mod_offline:remove_old_messages(list_to_integer(Days)),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["delete-older-users", Days]) ->
+	{removed, N, UR} = delete_older_users(list_to_integer(Days)),
+	io:format("Deleted ~p users: ~p~n", [N, UR]),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["export2odbc", Server, Output1]) ->
+	Output = list_to_atom(Output1),
+	ejd2odbc:export_passwd(Server, Output),
+	ejd2odbc:export_roster(Server, Output),
+	ejd2odbc:export_offline(Server, Output),
+	ejd2odbc:export_last(Server, Output),
+	ejd2odbc:export_vcard(Server, Output),
+	ejd2odbc:export_vcard_search(Server, Output),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["set-password", User, Server, Password]) ->
+    ejabberd_auth:set_password(User, Server, Password),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
+	{ok, Res} = vcard_get(User, Server, {Data}),
+    io:format("~s~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
+	{ok, Res} = vcard_get(User, Server, {Data1, Data2}),
+    io:format("~s~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-set", User, Server, Data, Content]) ->
+	{ok, Res} = vcard_set(User, Server, Data, Content),
+    io:format("~s~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
+	{ok, Res} = vcard_set(User, Server, Data1, Data2, Content),
+    io:format("~s~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["compile", Module]) ->
+    compile:file(Module),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["remove-node", Node]) ->
+	mnesia:del_table_copy(schema, list_to_atom(Node)),
+	?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-create", Group, Host, Name, Description, Display]) ->
+	Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}],
+	{atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-delete", Group, Host]) ->
+	{atomic, ok} = mod_shared_roster:delete_group(Host, Group),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
+	{atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
+	{atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
+    case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
+	{atomic, ok} ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't add ~p@~p to ~p@~p: ~p~n",
+		      [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't add roster item to user ~p: ~p~n",
+		      [LocalUser, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+ctl_process(_Val, ["pushroster", File, User, Server]) ->
+    case pushroster(File, User, Server) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push roster ~p to ~p@~p: ~p~n",
+		      [File, User, Server, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push roster ~p: ~p~n",
+		      [File, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+ctl_process(_Val, ["pushroster-all", File]) ->
+    case pushroster_all([File]) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push roster ~p: ~p~n",
+		      [File, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push roster ~p: ~p~n",
+		      [File, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+ctl_process(_Val, ["push-alltoall", Server, Group]) ->
+    case push_alltoall(Server, Group) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push all to all: ~p~n",
+		      [Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push all to all: ~p~n",
+		      [Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+ctl_process(_Val, ["load-config", Path]) ->
+    case ejabberd_config:load_file(Path) of
+        {atomic, ok} ->
+            ?STATUS_SUCCESS;
+        {error, Reason} ->
+            io:format("Can't load config file ~p: ~p~n",
+                      [filename:absname(Path), Reason]),
+	    ?STATUS_ERROR;
+        {badrpc, Reason} ->
+            io:format("Can't load config file ~p: ~p~n",
+                      [filename:absname(Path), Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+ctl_process(_Val, ["stats", Stat]) ->
+	Res = case Stat of
+		"registeredusers" -> mnesia:table_info(passwd, size);
+		"onlineusers" -> mnesia:table_info(session, size)
+	end,
+    io:format("~p~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["killsession", User, Server, Resource]) ->
+    ejabberd_router:route(
+        jlib:make_jid("", "", ""), 
+        jlib:make_jid(User, Server, Resource),
+        {xmlelement, "broadcast", [], [{exit, "killed"}]}),
+    ?STATUS_SUCCESS;
+
+ctl_process(Val, _Args) ->
+	Val.
+
+
+ctl_process(_Val, Host, ["num-active-users", Days]) ->
+	Number = num_active_users(Host, list_to_integer(Days)),
+    io:format("~p~n", [Number]),
+    ?STATUS_SUCCESS;
+
+ctl_process(_Val, Host, ["stats", Stat]) ->
+	Res = case Stat of
+		"registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
+		"onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
+	end,
+    io:format("~p~n", [Res]),
+    ?STATUS_SUCCESS;
+
+ctl_process(Val, _Host, _Args) ->
+	Val.
+
+
+%%-------------
+%% UTILS
+%%-------------
+
+add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
+	subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
+	% TODO: if the server is not local and Subs=to or both: send subscription request
+	% TODO: check if the 'remote server' is a virtual host here, else do nothing
+	%add_rosteritem2(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs, Host).
+	subscribe(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs).
+
+invert_subs(none) -> none;
+invert_subs(to) -> none;
+invert_subs(from) -> to;
+invert_subs(both) -> both.
+
+subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
+	mnesia:transaction(
+		fun() ->
+			mnesia:write({
+				roster,
+				{LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}}, % usj
+				{LocalUser,LocalServer},                 % us
+				{RemoteUser,RemoteServer,[]},      % jid
+				Nick,                  % name: "Mom", []
+				Subscription,  % subscription: none, to=you see him, from=he sees you, both
+				none,          % ask: out=send request, in=somebody requests you, none
+				[Group],       % groups: ["Family"]
+				Xattrs,        % xattrs: [{"category","conference"}]
+				[]             % xs: []
+		})
+	end).
+
+pushroster(File, User, Server) ->
+	{ok, [Roster]} = file:consult(File),
+	subscribe_roster({User, Server, "", User}, Roster).
+
+pushroster_all(File) ->
+	{ok, [Roster]} = file:consult(File),
+	subscribe_all(Roster).
+
+subscribe_all(Roster) ->
+	subscribe_all(Roster, Roster).
+subscribe_all([], _) ->
+	ok;
+subscribe_all([User1 | Users], Roster) ->
+	subscribe_roster(User1, Roster),
+	subscribe_all(Users, Roster).
+
+subscribe_roster(_, []) ->
+	ok;
+% Do not subscribe a user to itself
+subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
+	subscribe_roster({Name, Server, Group, Nick}, Roster);
+% Subscribe Name2 to Name1
+subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
+	subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
+	subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
+
+push_alltoall(S, G) ->
+	Users = ejabberd_auth:get_vh_registered_users(S),
+	Users2 = build_list_users(G, Users, []),
+	subscribe_all(Users2).
+
+build_list_users(_Group, [], Res) ->
+	Res;
+build_list_users(Group, [{User, Server}|Users], Res) ->
+	build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
+
+vcard_get(User, Server, DataX) ->
+    [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}),
+    Elem = vcard_get(DataX, A1),
+    {ok, xml:get_tag_cdata(Elem)}.
+
+vcard_get({Data1, Data2}, A1) ->
+    A2 = xml:get_subtag(A1, Data1),
+    A3 = xml:get_subtag(A2, Data2),
+    case A3 of
+		"" -> A2;
+		_ -> A3
+	end;
+
+vcard_get({Data}, A1) ->
+    xml:get_subtag(A1, Data).
+
+vcard_set(User, Server, Data1, Data2, Content) ->
+	Content2 = {xmlelement, Data2, [], [{xmlcdata,Content}]},
+	R = {xmlelement, Data1, [], [Content2]},
+	vcard_set2(User, Server, R, Data1).
+
+vcard_set(User, Server, Data, Content) ->
+	R = {xmlelement, Data, [], [{xmlcdata,Content}]},
+	vcard_set2(User, Server, R, Data).
+
+vcard_set2(User, Server, R, Data) ->
+	% Get old vcard
+    [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}),
+	{_, _, _, A2} = A1,
+
+    A3 = lists:keydelete(Data, 2, A2),
+    A4 = [R | A3],
+
+	% Build new vcard
+	SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
+	IQ = #iq{type=set, sub_el = SubEl},
+	JID = jlib:make_jid(User, Server, ""),
+
+	mod_vcard:process_sm_iq(JID, JID, IQ),
+	{ok, "done"}.
+
+
+-record(last_activity, {us, timestamp, status}).
+
+delete_older_users(Days) ->
+	% Convert older time
+	SecOlder = Days*24*60*60,
+
+	% Get current time
+	{MegaSecs, Secs, _MicroSecs} = now(),
+	TimeStamp_now = MegaSecs * 1000000 + Secs,
+
+	% Get the list of registered users
+	Users = ejabberd_auth:dirty_get_registered_users(),
+
+	% For a user, remove if required and answer true
+	F = fun({LUser, LServer}) ->
+		% Check if the user is logged
+		case ejabberd_sm:get_user_resources(LUser, LServer) of
+			% If it isn't
+			[] ->
+				% Look for his last_activity
+				case mnesia:dirty_read(last_activity, {LUser, LServer}) of
+				% If it is
+					% existent:
+					[#last_activity{timestamp = TimeStamp}] ->
+						% get his age
+						Sec = TimeStamp_now - TimeStamp,
+						% If he is
+						if 
+							% younger than SecOlder: 
+							Sec < SecOlder ->
+								% do nothing
+								false;
+							% older: 
+							true ->
+								% remove the user
+								ejabberd_auth:remove_user(LUser, LServer),
+								true
+						end;
+					% nonexistent:
+					[] ->
+						% remove the user
+						ejabberd_auth:remove_user(LUser, LServer),
+						true
+				end;
+			% Else
+			_ ->
+				% do nothing
+				false
+		end
+	end,
+	% Apply the function to every user in the list
+	Users_removed = lists:filter(F, Users),
+	{removed, length(Users_removed), Users_removed}.
+
+num_active_users(Host, Days) ->
+	list_last_activity(Host, true, Days).
+
+% Code based on ejabberd/src/web/ejabberd_web_admin.erl
+list_last_activity(Host, Integral, Days) ->
+    {MegaSecs, Secs, _MicroSecs} = now(),
+    TimeStamp = MegaSecs * 1000000 + Secs,
+    TS = TimeStamp - Days * 86400,
+    case catch mnesia:dirty_select(
+		 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
+				  [{'>', '$1', TS}],
+				  [{'trunc', {'/',
+					      {'-', TimeStamp, '$1'},
+					      86400}}]}]) of
+	{'EXIT', _Reason} ->
+	    [];
+	Vals ->
+	    Hist = histogram(Vals, Integral),
+	    if
+		Hist == [] ->
+		    0;
+		true ->
+		    Left = if
+			       Days == infinity ->
+				   0;
+			       true ->
+				   Days - length(Hist)
+			   end,
+		    Tail = if
+			       Integral ->
+				   lists:duplicate(Left, lists:last(Hist));
+			       true ->
+				   lists:duplicate(Left, 0)
+			   end,
+		    lists:nth(Days, Hist ++ Tail)
+	    end
+    end.
+histogram(Values, Integral) ->
+    histogram(lists:sort(Values), Integral, 0, 0, []).
+histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
+    histogram(T, Integral, Current, Count + 1, Hist);
+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
+    if
+	Integral ->
+	    histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
+	true ->
+	    histogram(Values, Integral, Current + 1, 0, [Count | Hist])
+    end;
+histogram([], _Integral, _Current, Count, Hist) ->
+    if
+	Count > 0 ->
+	    lists:reverse([Count | Hist]);
+	true ->
+	    lists:reverse(Hist)
+    end.

diff --git a/mod_shared_roster_ad.erl b/mod_shared_roster_ad.erl
new file mode 100644
index 0000000..8dc09f1
--- /dev/null
+++ b/mod_shared_roster_ad.erl
@@ -0,0 +1,428 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_shared_roster.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Purpose : Shared roster management
+%%% Created :  5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id: mod_shared_roster_ad.erl,v 1.1 2006/06/23 12:40:03 jcollie Exp $
+%%%----------------------------------------------------------------------
+
+-module(mod_shared_roster_ad).
+-author('alexey@sevcom.net').
+-author('realloc@realloc.spb.ru').
+-vsn('$Revision: 1.1 $ ').
+
+-behaviour(gen_mod).
+
+-export([start/2, stop/1,
+	 get_user_roster/2,
+	 get_subscription_lists/3,
+	 get_jid_info/4,
+	 in_subscription/5,
+	 out_subscription/4,
+	 list_groups/1,
+	 create_group/2,
+	 create_group/3,
+	 delete_group/2,
+	 get_group_opts/2,
+	 set_group_opts/3,
+	 get_group_users/2,
+	 get_group_explicit_users/2,
+	 add_user_to_group/3,
+	 remove_user_from_group/3]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+-include("eldap/eldap.hrl").
+
+-record(sr_group, {group_host, opts}).
+-record(sr_user, {us, group_host}).
+
+start(Host, _Opts) ->
+    mnesia:create_table(sr_group,
+			[{disc_copies, [node()]},
+			 {attributes, record_info(fields, sr_group)}]),
+    mnesia:create_table(sr_user,
+			[{disc_copies, [node()]},
+			 {type, bag},
+			 {attributes, record_info(fields, sr_user)}]),
+    mnesia:add_table_index(sr_user, group_host),
+    ejabberd_hooks:add(roster_get, Host,
+		       ?MODULE, get_user_roster, 70),
+    ejabberd_hooks:add(roster_in_subscription, Host,
+        	       ?MODULE, in_subscription, 30),
+    ejabberd_hooks:add(roster_out_subscription, Host,
+        	       ?MODULE, out_subscription, 30),
+    ejabberd_hooks:add(roster_get_subscription_lists, Host,
+		       ?MODULE, get_subscription_lists, 70),
+    ejabberd_hooks:add(roster_get_jid_info, Host,
+        	       ?MODULE, get_jid_info, 70),
+    
+    %ejabberd_hooks:add(remove_user, Host,
+    %    	       ?MODULE, remove_user, 50),
+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+    Password = ejabberd_config:get_local_option({ad_password, Host}),
+    eldap:start_link("mod_shared_roster_ad", LDAPServers, 389, RootDN, Password).
+   
+
+  
+stop(Host) ->
+    ejabberd_hooks:delete(roster_get, Host,
+			  ?MODULE, get_user_roster, 70),
+    ejabberd_hooks:delete(roster_in_subscription, Host,
+        		  ?MODULE, in_subscription, 30),
+    ejabberd_hooks:delete(roster_out_subscription, Host,
+        		  ?MODULE, out_subscription, 30),
+    ejabberd_hooks:delete(roster_get_subscription_lists, Host,
+        		  ?MODULE, get_subscription_lists, 70),
+    ejabberd_hooks:delete(roster_get_jid_info, Host,
+        		  ?MODULE, get_jid_info, 70).
+    %ejabberd_hooks:delete(remove_user, Host,
+    %    		  ?MODULE, remove_user, 50),
+
+
+get_user_roster(Items, US) ->
+    {U, S} = US,
+    DisplayedGroups = get_user_displayed_groups_ad(US),
+    SRUsers = 
+	lists:foldl(
+	  fun(Group, Acc1) ->
+		  lists:foldl(
+		    fun(User, Acc2) ->
+			    dict:append(User, Group, Acc2)
+		    end, Acc1, get_group_users_ad(S, Group))
+	  end, dict:new(), DisplayedGroups),
+    {NewItems1, SRUsersRest} =
+	lists:mapfoldl(
+	  fun(Item, SRUsers1) ->
+		  {_, _, {U1, S1, _}} = Item#roster.usj,
+		  US1 = {U1, S1},
+		  case dict:find(US1, SRUsers1) of
+		      {ok, _GroupNames} ->
+			  {Item#roster{subscription = both, ask = none},
+			   dict:erase(US1, SRUsers1)};
+		      error ->
+			  {Item, SRUsers1}
+		  end
+	  end, SRUsers, Items),
+    SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
+		       us = US,
+		       jid = {U1, S1, ""},
+		       name = get_user_fn(U1,S1),
+		       subscription = both,
+		       ask = none,
+		       groups = GroupNames} ||
+		  {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
+    SRItems ++ NewItems1.
+
+get_subscription_lists({F, T}, User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    DisplayedGroups = get_user_displayed_groups_ad(US),
+    SRUsers =
+	lists:usort(
+	  lists:flatmap(
+	    fun(Group) ->
+		    get_group_users_ad(LServer, Group)
+	    end, DisplayedGroups)),
+    SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
+    {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
+
+get_jid_info({Subscription, Groups}, User, Server, JID) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    {U1, S1, _} = jlib:jid_tolower(JID),
+    US1 = {U1, S1},
+    DisplayedGroups = get_user_displayed_groups_ad(US),
+    SRUsers = 
+	lists:foldl(
+	  fun(Group, Acc1) ->
+		  lists:foldl(
+		    fun(User1, Acc2) ->
+			    dict:append(
+			      User1, Group, Acc2)
+		    end, Acc1, get_group_users_ad(LServer, Group))
+	  end, dict:new(), DisplayedGroups),
+    case dict:find(US1, SRUsers) of
+	{ok, GroupNames} ->
+	    NewGroups = if
+			    Groups == [] -> GroupNames;
+			    true -> Groups
+			end,
+	    {both, NewGroups};
+	error ->
+	    {Subscription, Groups}
+    end.
+
+in_subscription(Acc, User, Server, JID, Type) ->
+    process_subscription(in, User, Server, JID, Type, Acc).
+
+out_subscription(User, Server, JID, Type) ->
+    process_subscription(out, User, Server, JID, Type, false).
+
+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+    US1 = {U1, S1},
+    DisplayedGroups = get_user_displayed_groups_ad(US),
+    SRUsers =
+	lists:usort(
+	  lists:flatmap(
+	    fun(Group) ->
+		    get_group_users_ad(LServer, Group)
+	    end, DisplayedGroups)),
+    case lists:member(US1, SRUsers) of
+	true ->
+	    case Direction of
+		in ->
+		    {stop, false};
+		out ->
+		    stop
+	    end;
+	false ->
+	    Acc
+    end.
+
+list_groups(Host) ->
+    get_user_displayed_groups_ad({"",Host}).
+
+
+create_group(Host, Group) ->
+    create_group(Host, Group, []).
+
+create_group(Host, Group, Opts) ->
+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
+    F = fun() ->
+		mnesia:write(R)
+	end,
+    mnesia:transaction(F).
+
+delete_group(Host, Group) ->
+    F = fun() ->
+		mnesia:delete({sr_group, {Group, Host}})
+	end,
+    mnesia:transaction(F).
+
+get_group_opts(Host, Group) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+	[#sr_group{opts = Opts}] ->
+	    Opts;
+	_ ->
+	    error
+    end.
+
+set_group_opts(Host, Group, Opts) ->
+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
+    F = fun() ->
+		mnesia:write(R)
+	end,
+    mnesia:transaction(F).
+
+
+
+get_user_groups(US) ->
+    Host = element(2, US),
+    case catch mnesia:dirty_read(sr_user, US) of
+	Rs when is_list(Rs) ->
+	    [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
+	_ ->
+	    []
+    end ++ get_all_users_groups(Host).
+
+is_group_enabled(Host, Group) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+	[#sr_group{opts = Opts}] ->
+	    not lists:member(disabled, Opts);
+	_ ->
+	    false
+    end.
+
+get_group_opt(Host, Group, Opt, Default) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+	[#sr_group{opts = Opts}] ->
+	    case lists:keysearch(Opt, 1, Opts) of
+		{value, {_, Val}} ->
+		    Val;
+		false ->
+		    Default
+	    end;
+	_ ->
+	    false
+    end.
+
+get_group_users(Host, Group) ->
+    case get_group_opt(Host, Group, all_users, false) of
+	true ->
+	    ejabberd_auth:get_vh_registered_users(Host);
+	false ->
+	    []
+    end ++ get_group_explicit_users(Host, Group).
+
+get_group_explicit_users(Host, Group) ->
+    case catch mnesia:dirty_index_read(
+		 sr_user, {Group, Host}, #sr_user.group_host) of
+	Rs when is_list(Rs) ->
+	    [R#sr_user.us || R <- Rs];
+	_ ->
+	    []
+    end.
+
+get_group_name(Host, Group) ->
+    get_group_opt(Host, Group, name, Group).
+
+get_all_users_groups(Host) ->
+    lists:filter(
+      fun(Group) -> get_group_opt(Host, Group, all_users, false) end,
+      list_groups(Host)).
+
+get_user_displayed_groups(US) ->
+    Host = element(2, US),
+    DisplayedGroups1 =
+	lists:usort(
+	  lists:flatmap(
+	    fun(Group) ->
+		    case is_group_enabled(Host, Group) of
+			true ->
+			    get_group_opt(Host, Group, displayed_groups, []);
+			false ->
+			    []
+		    end
+	    end, get_user_groups(US))),
+    [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
+
+
+
+
+add_user_to_group(Host, US, Group) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+		mnesia:write(R)
+	end,
+    mnesia:transaction(F).
+
+remove_user_from_group(Host, US, Group) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+		mnesia:delete_object(R)
+	end,
+    mnesia:transaction(F).
+
+
+
+find_user_attr(User, Host) ->
+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+    Filter = eldap:equalityMatch(Attr, User),
+    Base = ejabberd_config:get_local_option({ad_base, Host}),
+    
+    case eldap:search("mod_shared_roster_ad",
+                      [{base, Base},
+                       {filter, Filter},
+                       {attributes, []}]) of
+        #eldap_search_result{entries = [E | _]} ->
+            E;
+        _ ->
+            false
+    end.
+
+get_user_displayed_groups_ad(US) ->
+    {_, Host} = US,
+    AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
+    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+    Base = ejabberd_config:get_local_option({ad_base, Host}),
+    
+    case eldap:search("mod_shared_roster_ad",
+                      [{base, Base},
+                       {filter, FilterGroup},
+                       {attributes, []}]) of
+        #eldap_search_result{entries = E} ->
+	    lists:usort(lists:map(
+	      fun(X) ->
+		      case X of
+			  #eldap_entry{attributes = Attributes} ->
+			      ldap_get_value(Attributes,"department");
+			  false ->
+			      ""	    
+		      end
+		      end, E
+	     ));
+	    
+        _ ->
+            []
+    end.
+
+get_eldap_id(Host, Name) ->
+    atom_to_list(gen_mod:get_module_proc(Host, Name)).
+
+
+get_group_users_ad(Host, Group) ->
+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+%    AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
+    FilterPerson = eldap:equalityMatch("objectCategory", "person"),
+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
+    FilterHidden = eldap:equalityMatch("description", "hidden"),
+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+    FilterDep = eldap:equalityMatch("department", Group),
+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+    FilterDef = eldap:present(Attr),
+    Filter = eldap:'and'([
+			  FilterDef,
+			  FilterPerson,
+%			  FilterGroup,
+			  FilterDep,
+			  eldap:'not'(FilterComp),
+			  eldap:'not'(FilterHidden),
+			  eldap:'not'(FilterLive)]),
+    Base = ejabberd_config:get_local_option({ad_base, Host}),
+    case eldap:search(get_eldap_id(Host, ejabberd),
+		      [{base, Base},
+		       {filter, Filter},
+		       {attributes, [Attr]}]) of
+	#eldap_search_result{entries = Es} ->
+	    lists:flatmap(
+	      fun(E) ->
+		      case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
+			  {value, {_, [U]}} ->
+			      case jlib:nodeprep(U) of
+				  error ->
+				      [];
+				  LU ->
+				      [{LU, Host}]
+			      end;
+			  _ ->
+			      []
+		      end
+	      end, Es);
+	_ ->
+	    []
+    end.
+
+
+
+
+
+ldap_get_value(E,Attribute) ->
+    case lists:filter(fun({A,_}) ->
+			      string:equal(A,Attribute)
+		      end,E) of
+	[{_,[Value|_]}] ->
+	    Value;
+	_ -> 
+	    none
+    end.
+
+get_user_fn(User, Host) ->
+    case find_user_attr(User,Host) of
+	#eldap_entry{attributes = Attributes} ->
+	    ldap_get_value(Attributes,"cn");
+	
+	false ->
+	    ""	    
+    end.
+
+

diff --git a/mod_vcard_ad.erl b/mod_vcard_ad.erl
new file mode 100644
index 0000000..52ddbec
--- /dev/null
+++ b/mod_vcard_ad.erl
@@ -0,0 +1,631 @@
+
+%%%----------------------------------------------------------------------
+%%% File    : mod_vcard_ad.erl
+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Author  : Alex <agent_007> Gorbachenko (agent_007@immo.ru)
+%%% Purpose : 
+%%% Created :  2 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id: mod_vcard_ad.erl 437 2005-11-19 01:20:05Z agent_007 $
+%%%----------------------------------------------------------------------
+
+-module(mod_vcard_ad).
+-author('realloc@realloc.spb.ru').
+-author('alexey@sevcom.net').
+-author('agent_007@immo.ru').
+-vsn('$Revision: 437 $ ').
+
+-behaviour(gen_mod).
+
+-export([start/2, init/3, stop/1,
+ 	 get_sm_features/5,
+	 process_local_iq/3,
+	 process_sm_iq/3,
+	 remove_user/1]).
+
+-include("ejabberd.hrl").
+-include("eldap/eldap.hrl").
+-include("jlib.hrl").
+
+-define(PROCNAME, ejabberd_mod_vcard_ad).
+
+start(Host, Opts) ->
+    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
+    gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
+				  ?MODULE, process_local_iq, IQDisc),
+    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
+				  ?MODULE, process_sm_iq, IQDisc),
+    ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+    Password = ejabberd_config:get_local_option({ad_password, Host}),
+    eldap:start_link("mod_vcard_ad", LDAPServers, 389, RootDN, Password),
+    MyHost = gen_mod:get_opt(host, Opts, "vjud." ++ Host),
+    Search = gen_mod:get_opt(search, Opts, true),
+    register(gen_mod:get_module_proc(Host, ?PROCNAME),
+	     spawn(?MODULE, init, [MyHost, Host, Search])).
+
+init(Host, ServerHost, Search) ->
+    case Search of
+	false ->
+	    loop(Host, ServerHost);
+	_ ->
+	    ejabberd_router:register_route(Host),
+	    loop(Host, ServerHost)
+    end.
+
+loop(Host, ServerHost) ->
+    receive
+	{route, From, To, Packet} ->
+	    case catch do_route(ServerHost, From, To, Packet) of
+		{'EXIT', Reason} ->
+		    ?ERROR_MSG("~p", [Reason]);
+		_ ->
+		    ok
+	    end,
+	    loop(Host, ServerHost);
+	stop ->
+	    ejabberd_router:unregister_route(Host),
+	    ok;
+	_ ->
+	    loop(Host, ServerHost)
+    end.
+
+stop(Host) ->
+    gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
+    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
+    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+    Proc ! stop,
+    {wait, Proc}.
+
+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+    Acc;
+get_sm_features(Acc, _From, _To, Node, _Lang) ->
+    case Node of
+ 	[] ->
+ 	    case Acc of
+ 		{result, Features} ->
+ 		    {result, [?NS_VCARD | Features]};
+ 		empty ->
+ 		    {result, [?NS_VCARD]}
+ 	    end;
+  	_ ->
+ 	    Acc
+    end.
+
+process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
+    case Type of
+	set ->
+	    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+	get ->
+	    IQ#iq{type = result,
+		  sub_el = [{xmlelement, "vCard",
+			     [{"xmlns", ?NS_VCARD}],
+			     [{xmlelement, "FN", [],
+			       [{xmlcdata, "ejabberd"}]},
+			      {xmlelement, "URL", [],
+			       [{xmlcdata,
+				 "http://ejabberd.jabberstudio.org/"}]},
+			      {xmlelement, "DESC", [],
+			       [{xmlcdata,
+				 translate:translate(
+				   Lang,
+				   "Erlang Jabber Server\n"
+				   "Copyright (c) 2002-2005 Alexey Shchepin")}]},
+			      {xmlelement, "BDAY", [],
+			       [{xmlcdata, "2002-11-16"}]}
+			     ]}]}
+    end.
+
+find_ldap_user(Host, User) ->
+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+    Filter = eldap:equalityMatch(Attr, User),
+    Base = ejabberd_config:get_local_option({ad_base, Host}),
+    case eldap:search("mod_vcard_ad", [{base, Base},
+					 {filter, Filter},
+					 {attributes, []}]) of
+	#eldap_search_result{entries = [E | _]} ->
+	    E;
+	_ ->
+	    false
+    end.
+
+is_attribute_read_allowed(Name,From,To) ->
+    true.
+
+ldap_attribute_to_vcard(Prefix,{Name,Values},From,To) ->
+    case is_attribute_read_allowed(Name,From,To) of 
+	true ->
+	    ldap_lca_to_vcard(Prefix,stringprep:tolower(Name),Values);
+	_ ->
+	    none
+    end.
+
+ldap_lca_to_vcard(vCard,"displayname",[Value|_]) ->
+    {xmlelement,"FN",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"cn",[Value|_]) ->
+    {xmlelement,"NICKNAME",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"title",[Value|_]) ->
+    {xmlelement,"TITLE",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"wwwhomepage",[Value|_]) ->
+    {xmlelement,"URL",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"description",[Value|_]) ->
+    {xmlelement,"DESC",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"telephonenumber",[Value|_]) ->
+    {xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]},
+			  {xmlelement,"WORK",[],[]},
+			  {xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]};
+
+ldap_lca_to_vcard(vCard,"mail",[Value|_]) ->
+    {xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]},
+			    {xmlelement,"PREF",[],[]},
+			    {xmlelement,"USERID",[],[{xmlcdata,Value}]}]};
+
+ldap_lca_to_vcard(vCardN,"sn",[Value|_]) ->
+    {xmlelement,"FAMILY",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardN,"givenname",[Value|_]) ->
+    {xmlelement,"GIVEN",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardN,"initials",[Value|_]) ->
+    {xmlelement,"MIDDLE",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardAdr,"streetaddress",[Value|_]) ->
+    {xmlelement,"STREET",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"co",[Value|_]) ->
+    {xmlelement,"CTRY",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"l",[Value|_]) ->
+    {xmlelement,"LOCALITY",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"st",[Value|_]) ->
+    {xmlelement,"REGION",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"postalcode",[Value|_]) ->
+    {xmlelement,"PCODE",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardO,"company",[Value|_]) ->
+    {xmlelement,"ORGNAME",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardO,"department",[Value|_]) ->
+    {xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(_,_,_) -> none.
+
+ldap_attributes_to_vcard(Attributes,From,To) ->
+    Elts = lists:map(fun(Attr) ->
+			     ldap_attribute_to_vcard(vCard,Attr,From,To)
+		     end,Attributes),
+    FElts = [ X || X <- Elts, X /= none ],
+    NElts = lists:map(fun(Attr) ->
+			      ldap_attribute_to_vcard(vCardN,Attr,From,To)
+		      end,Attributes),
+    FNElts = [ X || X <- NElts, X /= none ],
+   
+    ADRElts = lists:map(fun(Attr) ->
+			      ldap_attribute_to_vcard(vCardAdr,Attr,From,To)
+		      end,Attributes),
+    FADRElts = [ X || X <- ADRElts, X /= none ],
+    
+    OElts = lists:map(fun(Attr) ->
+			      ldap_attribute_to_vcard(vCardO,Attr,From,To)
+		      end,Attributes),
+    FOElts = [ X || X <- OElts, X /= none ],
+    [{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}],
+      lists:append(FElts,
+		   [{xmlelement,"N",[],FNElts},
+		    {xmlelement,"ADR",[],FADRElts},
+		    {xmlelement,"ORG",[],FOElts}])
+     }].
+
+process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+    case Type of
+	set ->
+	    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+	get ->
+	    #jid{luser = LUser, lserver = LServer} = To,
+	    case find_ldap_user(LServer, LUser) of
+		#eldap_entry{attributes = Attributes} ->
+		    Vcard = ldap_attributes_to_vcard(Attributes,From,To),
+		    IQ#iq{type = result, sub_el = Vcard};
+		_ ->
+		    IQ#iq{type = result, sub_el = []}
+	    end
+	end.
+
+-define(TLFIELD(Type, Label, Var),
+	{xmlelement, "field", [{"type", Type},
+			       {"label", translate:translate(Lang, Label)},
+			       {"var", Var}], []}).
+
+
+-define(FORM(JID),
+	[{xmlelement, "instructions", [],
+	  [{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
+	 {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
+	  [{xmlelement, "title", [],
+	    [{xmlcdata, translate:translate(Lang, "Search users in ") ++
+	      jlib:jid_to_string(JID)}]},
+	   {xmlelement, "instructions", [],
+	    [{xmlcdata, translate:translate(Lang, "Fill in fields to search "
+					    "for any matching Jabber User")}]},
+	   ?TLFIELD("text-single", "User", "user"),
+	   ?TLFIELD("text-single", "Full Name", "fn"),
+	   ?TLFIELD("text-single", "Given Name", "given"),
+	   ?TLFIELD("text-single", "Middle Name", "middle"),
+	   ?TLFIELD("text-single", "Family Name", "family"),
+	   ?TLFIELD("text-single", "Nickname", "nickname"),
+	   ?TLFIELD("text-single", "Birthday", "bday"),
+	   ?TLFIELD("text-single", "Country", "ctry"),
+	   ?TLFIELD("text-single", "City", "locality"),
+	   ?TLFIELD("text-single", "email", "email"),
+	   ?TLFIELD("text-single", "Organization Name", "orgname"),
+	   ?TLFIELD("text-single", "Organization Unit", "orgunit")
+	  ]}]).
+
+
+
+
+do_route(ServerHost, From, To, Packet) ->
+    #jid{user = User, resource = Resource} = To,
+    if
+	(User /= "") or (Resource /= "") ->
+	    Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
+	    ejabberd_router ! {route, To, From, Err};
+	true ->
+	    IQ = jlib:iq_query_info(Packet),
+	    case IQ of
+		#iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
+		    case Type of
+			set ->
+			    XDataEl = find_xdata_el(SubEl),
+			    case XDataEl of
+				false ->
+				    Err = jlib:make_error_reply(
+					    Packet, ?ERR_BAD_REQUEST),
+				    ejabberd_router:route(To, From, Err);
+				_ ->
+				    XData = jlib:parse_xdata_submit(XDataEl),
+				    case XData of
+					invalid ->
+					    Err = jlib:make_error_reply(
+						    Packet,
+						    ?ERR_BAD_REQUEST),
+					    ejabberd_router:route(To, From,
+								  Err);
+					_ ->
+					    ResIQ =
+						IQ#iq{
+						  type = result,
+						  sub_el =
+						  [{xmlelement,
+						    "query",
+						    [{"xmlns", ?NS_SEARCH}],
+						    [{xmlelement, "x",
+						      [{"xmlns", ?NS_XDATA},
+						       {"type", "result"}],
+						      search_result(Lang, To, ServerHost, XData)
+						     }]}]},
+					    ejabberd_router:route(
+					      To, From, jlib:iq_to_xml(ResIQ))
+				    end
+			    end;
+			get ->
+			    ResIQ = IQ#iq{type = result,
+					  sub_el = [{xmlelement,
+						     "query",
+						     [{"xmlns", ?NS_SEARCH}],
+						     ?FORM(To)
+						    }]},
+			    ejabberd_router:route(To,
+						  From,
+						  jlib:iq_to_xml(ResIQ))
+		    end;
+		#iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
+		    case Type of
+			set ->
+			    Err = jlib:make_error_reply(
+				    Packet, ?ERR_NOT_ALLOWED),
+			    ejabberd_router:route(To, From, Err);
+			get ->
+			    ResIQ =
+				IQ#iq{type = result,
+				      sub_el = [{xmlelement,
+						 "query",
+						 [{"xmlns", ?NS_DISCO_INFO}],
+						 [{xmlelement, "identity",
+						   [{"category", "directory"},
+						    {"type", "user"},
+						    {"name",
+						     "vCard User Search"}],
+						   []},
+						  {xmlelement, "feature",
+						   [{"var", ?NS_SEARCH}], []},
+						  {xmlelement, "feature",
+						   [{"var", ?NS_VCARD}], []}
+						 ]
+						}]},
+			    ejabberd_router:route(To,
+						  From,
+						  jlib:iq_to_xml(ResIQ))
+		    end;
+		#iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
+		    case Type of
+			set ->
+			    Err = jlib:make_error_reply(
+				    Packet, ?ERR_NOT_ALLOWED),
+			    ejabberd_router:route(To, From, Err);
+			get ->
+			    ResIQ = 
+				IQ#iq{type = result,
+				      sub_el = [{xmlelement,
+						 "query",
+						 [{"xmlns", ?NS_DISCO_ITEMS}],
+						 []}]},
+			    ejabberd_router:route(To,
+						  From,
+						  jlib:iq_to_xml(ResIQ))
+		    end;
+		#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
+		    ResIQ = 
+			IQ#iq{type = result,
+			      sub_el = [{xmlelement,
+					 "vCard",
+					 [{"xmlns", ?NS_VCARD}],
+					 iq_get_vcard(Lang)}]},
+		    ejabberd_router:route(To,
+					  From,
+					  jlib:iq_to_xml(ResIQ));
+		_ ->
+		    Err = jlib:make_error_reply(Packet,
+						?ERR_SERVICE_UNAVAILABLE),
+		    ejabberd_router:route(To, From, Err)
+	    end
+    end.
+
+iq_get_vcard(Lang) ->
+    [{xmlelement, "FN", [],
+      [{xmlcdata, "ejabberd/mod_vcard"}]},
+     {xmlelement, "URL", [],
+      [{xmlcdata,
+        "http://ejabberd.jabberstudio.org/"}]},
+     {xmlelement, "DESC", [],
+      [{xmlcdata, translate:translate(
+		    Lang,
+		    "ejabberd vCard module\n"
+		    "Copyright (c) 2003-2005 Alexey Shchepin")}]}].
+
+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).
+
+-define(LFIELD(Label, Var),
+	{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
+			       {"var", Var}], []}).
+
+search_result(Lang, JID, ServerHost, Data) ->
+    [{xmlelement, "title", [],
+      [{xmlcdata, translate:translate(Lang, "Results of search in ") ++
+	jlib:jid_to_string(JID)}]},
+     {xmlelement, "reported", [],
+      [?LFIELD("JID", "jid"),
+       ?LFIELD("Full Name", "fn"),
+       ?LFIELD("Given Name", "given"),
+       ?LFIELD("Middle Name", "middle"),
+       ?LFIELD("Family Name", "family"),
+       ?LFIELD("Nickname", "nickname"),
+       ?LFIELD("Birthday", "bday"),
+       ?LFIELD("Country", "ctry"),
+       ?LFIELD("City", "locality"),
+       ?LFIELD("email", "email"),
+       ?LFIELD("Organization Name", "orgname"),
+       ?LFIELD("Organization Unit", "orgunit")
+      ]}] ++ lists:map(fun(E) -> 
+			       record_to_item(E#eldap_entry.attributes)
+		       end, search(ServerHost, Data)).
+
+-define(FIELD(Var, Val),
+	{xmlelement, "field", [{"var", Var}],
+	 [{xmlelement, "value", [],
+	   [{xmlcdata, Val}]}]}).
+
+case_exact_compare(none,_) ->
+    false;
+case_exact_compare(_,none) ->
+    false;
+case_exact_compare(X,Y) ->
+    X > Y.
+
+ldap_sort_entries(L) ->
+    lists:sort(fun(E1,E2) ->
+		       case_exact_compare(ldap_get_value(E1,"cn"),ldap_get_value(E2,"cn"))
+	       end,L).
+
+ldap_get_value(E,Attribute) ->
+    #eldap_entry{attributes = Attributes} = E,
+    case lists:filter(fun({A,_}) ->
+			      string:equal(A,Attribute)
+		      end,Attributes) of
+	[{Attr,[Value|_]}] ->
+	    Value;
+	_ -> 
+	    none
+    end.
+
+ldap_attribute_to_item("samaccountname",Value) ->
+    [
+     ?FIELD("jid",Value ++ "@" ++ ?MYNAME),
+     ?FIELD("uid",Value)
+    ];
+
+ldap_attribute_to_item("cn",Value) ->
+    [
+     ?FIELD("nickname",Value)
+    ];
+
+ldap_attribute_to_item("displayname",Value) ->
+    [
+     ?FIELD("fn",Value)
+    ];
+
+ldap_attribute_to_item("sn",Value) ->
+    [
+     ?FIELD("family",Value)
+    ];
+ldap_attribute_to_item("co",Value) ->
+    [
+     ?FIELD("ctry",Value)
+    ];
+ldap_attribute_to_item("l",Value) ->
+    [
+     ?FIELD("locality",Value)
+    ];
+
+ldap_attribute_to_item("givenname",Value) ->
+    [
+     ?FIELD("given",Value)
+    ];
+
+ldap_attribute_to_item("initials",Value) ->
+    [
+     ?FIELD("middle",Value)
+    ];
+
+ldap_attribute_to_item("mail",Value) ->
+    [
+     ?FIELD("email",Value)
+    ];
+
+ldap_attribute_to_item("company",Value) ->
+    [
+     ?FIELD("orgname",Value)
+    ];
+
+ldap_attribute_to_item("department",Value) ->
+    [
+     ?FIELD("orgunit",Value)
+    ];
+
+ldap_attribute_to_item(_,_) ->
+    [none].
+
+record_to_item(Attributes) ->
+    List = lists:append(lists:map(fun({Attr,[Value|_]}) -> 
+					  ldap_attribute_to_item(stringprep:tolower(Attr),Value)
+				  end,Attributes)),
+    FList = [X || X <- List, X /= none],
+    {xmlelement, "item", [],FList}.
+
+search(LServer, Data) ->
+%    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+    FilterDef = make_filter(Data),
+    FilterPerson =  eldap:equalityMatch("objectCategory", "person"),
+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
+    FilterHidden = eldap:equalityMatch("description", "hidden"),
+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+    Filter = eldap:'and'([
+			  FilterDef,
+			  FilterPerson,
+%			  FilterGroup,
+			  eldap:'not'(FilterComp),
+			  eldap:'not'(FilterHidden),
+			  eldap:'not'(FilterLive)]),
+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
+    UIDAttr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+    case eldap:search("mod_vcard_ad",[{base, Base},
+					{filter, Filter},
+					{attributes, []}]) of
+	#eldap_search_result{entries = E} ->
+	    [X || X <- E,
+		  ejabberd_auth:is_user_exists(
+		    ldap_get_value(X, UIDAttr), LServer)];
+	Err ->
+	    ?ERROR_MSG("Bad search: ~p", [[LServer, {base, Base},
+					{filter, Filter},
+					{attributes, []}]])
+    end.
+
+
+make_filter(Data) ->
+    Filter = [X || X <- lists:map(fun(R) -> 
+					  make_assertion(R)
+				  end, Data),
+		   X /= none ],
+    case Filter of
+	[F] -> 
+	    F;
+	_ ->
+	    eldap:'and'(Filter)
+    end.
+
+
+make_assertion("givenName",Value) ->
+    eldap:substrings("givenName",[{any,Value}]);
+
+make_assertion("cn",Value) ->
+    eldap:substrings("cn",[{any,Value}]);
+
+make_assertion("sn",Value) ->
+    eldap:substrings("sn",[{any,Value}]);
+
+make_assertion(Attr, Value) ->
+    eldap:equalityMatch(Attr,Value).
+
+make_assertion({SVar, [Val]}) ->
+    LAttr = ldap_attribute(SVar),
+    case LAttr of
+	none ->
+	    none;
+	_ ->
+	    if 
+		is_list(Val) and (Val /= "") ->
+		    make_assertion(LAttr,Val);
+		true ->
+		    none
+	    end
+    end.
+
+ldap_attribute("user") ->
+    "samaccountname";
+
+ldap_attribute("fn") ->
+    "cn";
+
+ldap_attribute("family") ->
+    "sn";
+
+ldap_attribute("given") ->
+    "givenName";
+
+ldap_attribute("middle") ->
+    "initials";
+
+ldap_attribute("email") ->
+    "mail";
+
+ldap_attribute("orgname") ->
+    "company";
+
+ldap_attribute("orgunit") ->
+    "department";
+
+ldap_attribute(_) ->
+    none.
+
+remove_user(User) ->
+    true.
+

diff --git a/sources b/sources
index e69de29..b53a882 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+ef6fae4a3f9c7f807f21e9cd3dae195b  ejabberd-1.1.1.tar.gz

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-23 19:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 19:06 [rpms/ejabberd] f45: auto-import ejabberd-1.1.1-7.fc5 on branch devel from Jeffrey C. Ollie

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox