Sophie

Sophie

distrib > Fedora > 15 > i386 > by-pkgid > 4570bec480fbeab0cfb20c07af297935 > files > 9

ejabberd-2.1.11-1.fc15.src.rpm

From d5880591ff8c318584df55cbb22d91eb42aa80dd Mon Sep 17 00:00:00 2001
From: Peter Lemenkov <lemenkov@gmail.com>
Date: Tue, 14 Dec 2010 18:28:44 +0300
Subject: [PATCH 09/10] Added old modules for Active Directory

These modules were extracted from a patch found on
http://realloc.spb.ru/share/ejabberdad.html

Please, note that these are unmaintained and will be removed very
soon (users should use *_ldap.erl instead).
---
 src/ejabberd_auth_ad.erl     |  167 +++++++++++
 src/mod_shared_roster_ad.erl |  428 ++++++++++++++++++++++++++++
 src/mod_vcard_ad.erl         |  631 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1226 insertions(+)
 create mode 100644 src/ejabberd_auth_ad.erl
 create mode 100644 src/mod_shared_roster_ad.erl
 create mode 100644 src/mod_vcard_ad.erl

diff --git a/src/ejabberd_auth_ad.erl b/src/ejabberd_auth_ad.erl
new file mode 100644
index 0000000..5e9e452
--- /dev/null
+++ b/src/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/src/mod_shared_roster_ad.erl b/src/mod_shared_roster_ad.erl
new file mode 100644
index 0000000..8dc09f1
--- /dev/null
+++ b/src/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/src/mod_vcard_ad.erl b/src/mod_vcard_ad.erl
new file mode 100644
index 0000000..52ddbec
--- /dev/null
+++ b/src/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.
+
-- 
1.7.10.1