📄 eldap.erl
字号:
%%%%%% A substring filter seem to be based on a pattern:%%%%%% InitValue*AnyValue*FinalValue%%%%%% where all three parts seem to be optional (at least when%%% talking with an OpenLDAP server). Thus, the arguments%%% to substrings/2 looks like this:%%%%%% Type ::= string( <attribute> )%%% SubStr ::= listof( {initial,Value} | {any,Value}, {final,Value})%%%%%% Example: substrings("sn",[{initial,"To"},{any,"kv"},{final,"st"}])%%% will match entries containing: 'sn: Tornkvist'%%%substrings(Type, SubStr) when list(Type), list(SubStr) -> Ss = {'SubstringFilter_substrings',v_substr(SubStr)}, {substrings,#'SubstringFilter'{type = Type, substrings = Ss}}.get_handle(Pid) when pid(Pid) -> Pid;get_handle(Atom) when atom(Atom) -> Atom;get_handle(Name) when list(Name) -> list_to_atom("eldap_" ++ Name).%%%----------------------------------------------------------------------%%% Callback functions from gen_fsm%%%----------------------------------------------------------------------%%----------------------------------------------------------------------%% Func: init/1%% Returns: {ok, StateName, StateData} |%% {ok, StateName, StateData, Timeout} |%% ignore |%% {stop, StopReason} %% I use the trick of setting a timeout of 0 to pass control into the%% process. %%----------------------------------------------------------------------init([]) -> case get_config() of {ok, Hosts, Rootdn, Passwd, Log} -> init({Hosts, Rootdn, Passwd, Log}); {error, Reason} -> {stop, Reason} end;init({Hosts, Port, Rootdn, Passwd, Log}) -> {ok, connecting, #eldap{hosts = Hosts, port = Port, rootdn = Rootdn, passwd = Passwd, id = 0, log = Log, dict = dict:new(), debug_level = 0}, 0}.%%----------------------------------------------------------------------%% Func: StateName/2%% Called when gen_fsm:send_event/2,3 is invoked (async)%% Returns: {next_state, NextStateName, NextStateData} |%% {next_state, NextStateName, NextStateData, Timeout} |%% {stop, Reason, NewStateData} %%----------------------------------------------------------------------connecting(timeout, S) -> {ok, NextState, NewS} = connect_bind(S), {next_state, NextState, NewS}.%%----------------------------------------------------------------------%% Func: StateName/3%% Called when gen_fsm:sync_send_event/2,3 is invoked.%% Returns: {next_state, NextStateName, NextStateData} |%% {next_state, NextStateName, NextStateData, Timeout} |%% {reply, Reply, NextStateName, NextStateData} |%% {reply, Reply, NextStateName, NextStateData, Timeout} |%% {stop, Reason, NewStateData} |%% {stop, Reason, Reply, NewStateData} %%----------------------------------------------------------------------connecting(Event, From, S) -> Reply = {error, connecting}, {reply, Reply, connecting, S}.wait_bind_response(Event, From, S) -> Reply = {error, wait_bind_response}, {reply, Reply, wait_bind_response, S}.active(Event, From, S) -> case catch send_command(Event, From, S) of {ok, NewS} -> {next_state, active, NewS}; {error, Reason} -> {reply, {error, Reason}, active, S}; {'EXIT', Reason} -> {reply, {error, Reason}, active, S} end.%%----------------------------------------------------------------------%% Func: handle_event/3%% Called when gen_fsm:send_all_state_event/2 is invoked.%% Returns: {next_state, NextStateName, NextStateData} |%% {next_state, NextStateName, NextStateData, Timeout} |%% {stop, Reason, NewStateData} %%----------------------------------------------------------------------handle_event(close, StateName, S) -> gen_tcp:close(S#eldap.fd), {stop, closed, S};handle_event(Event, StateName, S) -> {next_state, StateName, S}.%%----------------------------------------------------------------------%% Func: handle_sync_event/4%% Called when gen_fsm:sync_send_all_state_event/2,3 is invoked%% Returns: {next_state, NextStateName, NextStateData} |%% {next_state, NextStateName, NextStateData, Timeout} |%% {reply, Reply, NextStateName, NextStateData} |%% {reply, Reply, NextStateName, NextStateData, Timeout} |%% {stop, Reason, NewStateData} |%% {stop, Reason, Reply, NewStateData} %%----------------------------------------------------------------------handle_sync_event({debug_level, N}, From, StateName, S) -> {reply, ok, StateName, S#eldap{debug_level = N}};handle_sync_event(Event, From, StateName, S) -> {reply, {StateName, S}, StateName, S};handle_sync_event(Event, From, StateName, S) -> Reply = ok, {reply, Reply, StateName, S}.%%----------------------------------------------------------------------%% Func: handle_info/3%% Returns: {next_state, NextStateName, NextStateData} |%% {next_state, NextStateName, NextStateData, Timeout} |%% {stop, Reason, NewStateData} %%----------------------------------------------------------------------%%%% Packets arriving in various states%%handle_info({tcp, Socket, Data}, connecting, S) -> log1("eldap. tcp packet received when disconnected!~n~p~n", [Data], S), {next_state, connecting, S};handle_info({tcp, Socket, Data}, wait_bind_response, S) -> cancel_timer(S#eldap.bind_timer), case catch recvd_wait_bind_response(Data, S) of bound -> {next_state, active, S}; {fail_bind, Reason} -> close_and_retry(S), {next_state, connecting, S#eldap{fd = null}}; {'EXIT', Reason} -> close_and_retry(S), {next_state, connecting, S#eldap{fd = null}}; {error, Reason} -> close_and_retry(S), {next_state, connecting, S#eldap{fd = null}} end;handle_info({tcp, Socket, Data}, active, S) -> case catch recvd_packet(Data, S) of {reply, Reply, To, NewS} -> gen_fsm:reply(To, Reply), {next_state, active, NewS}; {ok, NewS} -> {next_state, active, NewS}; {'EXIT', Reason} -> {next_state, active, S}; {error, Reason} -> {next_state, active, S} end;handle_info({tcp_closed, Socket}, All_fsm_states, S) -> F = fun(Id, [{Timer, From, Name}|Res]) -> gen_fsm:reply(From, {error, tcp_closed}), cancel_timer(Timer) end, dict:map(F, S#eldap.dict), retry_connect(), {next_state, connecting, S#eldap{fd = null, dict = dict:new()}};handle_info({tcp_error, Socket, Reason}, Fsm_state, S) -> log1("eldap received tcp_error: ~p~nIn State: ~p~n", [Reason, Fsm_state], S), {next_state, Fsm_state, S};%%%% Timers%%handle_info({timeout, Timer, {cmd_timeout, Id}}, active, S) -> case cmd_timeout(Timer, Id, S) of {reply, To, Reason, NewS} -> gen_fsm:reply(To, Reason), {next_state, active, NewS}; {error, Reason} -> {next_state, active, S} end;handle_info({timeout, retry_connect}, connecting, S) -> {ok, NextState, NewS} = connect_bind(S), {next_state, NextState, NewS};handle_info({timeout, Timer, bind_timeout}, wait_bind_response, S) -> close_and_retry(S), {next_state, connecting, S#eldap{fd = null}};%%%% Make sure we don't fill the message queue with rubbish%%handle_info(Info, StateName, S) -> log1("eldap. Unexpected Info: ~p~nIn state: ~p~n when StateData is: ~p~n", [Info, StateName, S], S), {next_state, StateName, S}.%%----------------------------------------------------------------------%% Func: terminate/3%% Purpose: Shutdown the fsm%% Returns: any%%----------------------------------------------------------------------terminate(Reason, StateName, StatData) -> ok.%%----------------------------------------------------------------------%% Func: code_change/4%% Purpose: Convert process state when code is changed%% Returns: {ok, NewState, NewStateData}%%----------------------------------------------------------------------code_change(OldVsn, StateName, S, Extra) -> {ok, StateName, S}.%%%----------------------------------------------------------------------%%% Internal functions%%%----------------------------------------------------------------------send_command(Command, From, S) -> Id = bump_id(S), {Name, Request} = gen_req(Command), Message = #'LDAPMessage'{messageID = Id, protocolOp = {Name, Request}}, log2("~p~n",[{Name, Request}], S), {ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), ok = gen_tcp:send(S#eldap.fd, Bytes), Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}), New_dict = dict:store(Id, [{Timer, From, Name}], S#eldap.dict), {ok, S#eldap{id = Id, dict = New_dict}}.gen_req({search, A}) -> {searchRequest, #'SearchRequest'{baseObject = A#eldap_search.base, scope = v_scope(A#eldap_search.scope), derefAliases = neverDerefAliases, sizeLimit = 0, % no size limit timeLimit = v_timeout(A#eldap_search.timeout), typesOnly = v_bool(A#eldap_search.types_only), filter = v_filter(A#eldap_search.filter), attributes = v_attributes(A#eldap_search.attributes) }};gen_req({add, Entry, Attrs}) -> {addRequest, #'AddRequest'{entry = Entry, attributes = Attrs}};gen_req({delete, Entry}) -> {delRequest, Entry};gen_req({modify, Obj, Mod}) -> v_modifications(Mod), {modifyRequest, #'ModifyRequest'{object = Obj, modification = Mod}};gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) -> {modDNRequest, #'ModifyDNRequest'{entry = Entry, newrdn = NewRDN, deleteoldrdn = DelOldRDN, newSuperior = NewSup}};gen_req({bind, RootDN, Passwd}) -> {bindRequest, #'BindRequest'{version = ?LDAP_VERSION, name = RootDN, authentication = {simple, Passwd}}}.%%-----------------------------------------------------------------------%% recvd_packet%% Deals with incoming packets in the active state%% Will return one of:%% {ok, NewS} - Don't reply to client yet as this is part of a search %% result and we haven't got all the answers yet.%% {reply, Result, From, NewS} - Reply with result to client From%% {error, Reason}%% {'EXIT', Reason} - Broke%%-----------------------------------------------------------------------recvd_packet(Pkt, S) -> check_tag(Pkt), case asn1rt:decode('ELDAPv3', 'LDAPMessage', Pkt) of {ok,Msg} -> Op = Msg#'LDAPMessage'.protocolOp, log2("~p~n",[Op], S), Dict = S#eldap.dict, Id = Msg#'LDAPMessage'.messageID, {Timer, From, Name, Result_so_far} = get_op_rec(Id, Dict), case {Name, Op} of {searchRequest, {searchResEntry, R}} when record(R,'SearchResultEntry') -> New_dict = dict:append(Id, R, Dict), {ok, S#eldap{dict = New_dict}}; {searchRequest, {searchResDone, Result}} -> case Result#'LDAPResult'.resultCode of success -> {Res, Ref} = polish(Result_so_far), New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, #eldap_search_result{entries = Res, referrals = Ref}, From, S#eldap{dict = New_dict}}; Reason -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, {error, Reason}, From, S#eldap{dict = New_dict}} end; {searchRequest, {searchResRef, R}} -> New_dict = dict:append(Id, R, Dict), {ok, S#eldap{dict = New_dict}}; {addRequest, {addResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {delRequest, {delResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {modifyRequest, {modifyResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {modDNRequest, {modDNResponse, Result}} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), Reply = check_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {bindRequest, {bindResponse, Result}} -> New_dict = dict:erase(Id, Dict),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -