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