2007-12-24 14:57:53 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
2006-09-14 04:54:21 +02:00
|
|
|
%%% File: eldap_filter.erl
|
|
|
|
%%% Purpose: Converts String Representation of
|
|
|
|
%%% LDAP Search Filter (RFC 2254)
|
|
|
|
%%% to eldap's representation of filter
|
2010-04-19 06:08:00 +02:00
|
|
|
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
2007-12-24 14:57:53 +01:00
|
|
|
%%%
|
|
|
|
%%%
|
2011-02-14 13:50:55 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
2007-12-24 14:57:53 +01:00
|
|
|
%%%
|
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
2009-01-12 15:44:42 +01:00
|
|
|
%%%
|
2007-12-24 14:57:53 +01:00
|
|
|
%%% You should have received a copy of the GNU General Public License
|
|
|
|
%%% along with this program; if not, write to the Free Software
|
|
|
|
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
%%% 02111-1307 USA
|
|
|
|
%%%
|
|
|
|
%%%----------------------------------------------------------------------
|
2006-09-14 04:54:21 +02:00
|
|
|
-module(eldap_filter).
|
|
|
|
|
2010-04-19 06:08:00 +02:00
|
|
|
%% TODO: remove this when new regexp module will be used
|
|
|
|
-compile({nowarn_deprecated_function, {regexp, sub, 3}}).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2010-04-19 06:08:00 +02:00
|
|
|
-export([parse/1, parse/2, do_sub/2]).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2010-04-19 06:08:00 +02:00
|
|
|
%%====================================================================
|
|
|
|
%% API
|
|
|
|
%%====================================================================
|
|
|
|
%%%-------------------------------------------------------------------
|
2006-09-14 04:54:21 +02:00
|
|
|
%%% Arity: parse/1
|
|
|
|
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} |
|
|
|
|
%%% {error, bad_filter}
|
|
|
|
%%%
|
|
|
|
%%% RFC2254_Filter = string().
|
|
|
|
%%%
|
|
|
|
%%% Description: Converts String Representation of LDAP Search Filter (RFC 2254)
|
|
|
|
%%% to eldap's representation of filter.
|
|
|
|
%%%
|
|
|
|
%%% Example:
|
2010-04-19 06:08:00 +02:00
|
|
|
%%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))").
|
2006-09-14 04:54:21 +02:00
|
|
|
%%%
|
2010-04-19 06:08:00 +02:00
|
|
|
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
|
|
|
|
%%% {present,"mail"}]}}
|
|
|
|
%%%-------------------------------------------------------------------
|
|
|
|
parse(L) when is_list(L) ->
|
|
|
|
parse(L, []).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2010-04-19 06:08:00 +02:00
|
|
|
%%%-------------------------------------------------------------------
|
2006-09-14 04:54:21 +02:00
|
|
|
%%% Arity: parse/2
|
|
|
|
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) ->
|
|
|
|
%%% {ok, EldapFilter} |
|
|
|
|
%%% {error, bad_filter} |
|
|
|
|
%%% {error, bad_regexp} |
|
|
|
|
%%% {error, max_substitute_recursion}
|
|
|
|
%%%
|
|
|
|
%%% SubstValue = {RegExp, Value} | {RegExp, Value, N},
|
|
|
|
%%% RFC2254_Filter = RegExp = Value = string(),
|
|
|
|
%%% N = integer().
|
|
|
|
%%%
|
|
|
|
%%% Description: The same as parse/1, but substitutes N or all occurences
|
|
|
|
%%% of RegExp with Value *after* parsing.
|
|
|
|
%%%
|
|
|
|
%%% Example:
|
|
|
|
%%% > eldap_filter:parse(
|
|
|
|
%%% "(|(mail=%u@%d)(jid=%u@%d))",
|
|
|
|
%%% [{"%u", "xramtsov"},{"%d","gmail.com"}]).
|
|
|
|
%%%
|
|
|
|
%%% {ok,{'or',[{equalityMatch,{'AttributeValueAssertion',
|
|
|
|
%%% "mail",
|
|
|
|
%%% "xramtsov@gmail.com"}},
|
|
|
|
%%% {equalityMatch,{'AttributeValueAssertion',
|
|
|
|
%%% "jid",
|
|
|
|
%%% "xramtsov@gmail.com"}}]}}
|
2010-04-19 06:08:00 +02:00
|
|
|
%%%-------------------------------------------------------------------
|
|
|
|
parse(L, SList) when is_list(L), is_list(SList) ->
|
|
|
|
case catch eldap_filter_yecc:parse(scan(L, SList)) of
|
2011-06-15 12:06:32 +02:00
|
|
|
{'EXIT', _} = Err ->
|
|
|
|
{error, Err};
|
2010-04-19 06:08:00 +02:00
|
|
|
{error, {_, _, Msg}} ->
|
|
|
|
{error, Msg};
|
|
|
|
{ok, Result} ->
|
|
|
|
{ok, Result};
|
|
|
|
{regexp, Err} ->
|
|
|
|
{error, Err}
|
2006-09-14 04:54:21 +02:00
|
|
|
end.
|
|
|
|
|
2010-04-19 06:08:00 +02:00
|
|
|
%%====================================================================
|
|
|
|
%% Internal functions
|
|
|
|
%%====================================================================
|
|
|
|
-define(do_scan(L), scan(Rest, [], [{L, 1} | check(Buf, S) ++ Result], L, S)).
|
|
|
|
|
|
|
|
scan(L, SList) ->
|
|
|
|
scan(L, "", [], undefined, SList).
|
|
|
|
|
|
|
|
scan("=*)" ++ Rest, Buf, Result, '(', S) ->
|
|
|
|
scan(Rest, [], [{')', 1}, {'=*', 1} | check(Buf, S) ++ Result], ')', S);
|
|
|
|
scan(":dn" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':dn');
|
|
|
|
scan(":=" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':=');
|
|
|
|
scan(":=" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':=');
|
|
|
|
scan(":=" ++ Rest, Buf, Result, ':', S) -> ?do_scan(':=');
|
|
|
|
scan("~=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('~=');
|
|
|
|
scan(">=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('>=');
|
|
|
|
scan("<=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('<=');
|
|
|
|
scan("=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('=');
|
|
|
|
scan(":" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':');
|
|
|
|
scan(":" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':');
|
|
|
|
scan("&" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('&');
|
|
|
|
scan("|" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('|');
|
|
|
|
scan("!" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('!');
|
|
|
|
scan("*" ++ Rest, Buf, Result, '*', S) -> ?do_scan('*');
|
|
|
|
scan("*" ++ Rest, Buf, Result, '=', S) -> ?do_scan('*');
|
|
|
|
scan("(" ++ Rest, Buf, Result, _, S) -> ?do_scan('(');
|
|
|
|
scan(")" ++ Rest, Buf, Result, _, S) -> ?do_scan(')');
|
|
|
|
scan([Letter | Rest], Buf, Result, PreviosAtom, S) ->
|
|
|
|
scan(Rest, [Letter|Buf], Result, PreviosAtom, S);
|
|
|
|
scan([], Buf, Result, _, S) ->
|
|
|
|
lists:reverse(check(Buf, S) ++ Result).
|
|
|
|
|
|
|
|
check([], _) ->
|
|
|
|
[];
|
|
|
|
check(Buf, S) ->
|
|
|
|
[{str, 1, do_sub(lists:reverse(Buf), S)}].
|
2006-09-14 04:54:21 +02:00
|
|
|
|
|
|
|
-define(MAX_RECURSION, 100).
|
|
|
|
|
|
|
|
do_sub(S, []) ->
|
|
|
|
S;
|
|
|
|
|
|
|
|
do_sub([], _) ->
|
|
|
|
[];
|
|
|
|
|
2006-09-23 06:28:03 +02:00
|
|
|
do_sub(S, [{RegExp, New} | T]) ->
|
|
|
|
Result = do_sub(S, {RegExp, replace_amps(New)}, 1),
|
|
|
|
do_sub(Result, T);
|
|
|
|
|
|
|
|
do_sub(S, [{RegExp, New, Times} | T]) ->
|
|
|
|
Result = do_sub(S, {RegExp, replace_amps(New), Times}, 1),
|
2006-09-14 04:54:21 +02:00
|
|
|
do_sub(Result, T).
|
|
|
|
|
|
|
|
do_sub(S, {RegExp, New}, Iter) ->
|
|
|
|
case regexp:sub(S, RegExp, New) of
|
|
|
|
{ok, NewS, 0} ->
|
|
|
|
NewS;
|
|
|
|
{ok, NewS, _} when Iter =< ?MAX_RECURSION ->
|
|
|
|
do_sub(NewS, {RegExp, New}, Iter+1);
|
|
|
|
{ok, _, _} when Iter > ?MAX_RECURSION ->
|
2010-04-19 06:08:00 +02:00
|
|
|
erlang:error(max_substitute_recursion);
|
2006-09-14 04:54:21 +02:00
|
|
|
_ ->
|
2010-04-19 06:08:00 +02:00
|
|
|
erlang:error(bad_regexp)
|
2006-09-14 04:54:21 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
do_sub(S, {_, _, N}, _) when N<1 ->
|
|
|
|
S;
|
|
|
|
|
|
|
|
do_sub(S, {RegExp, New, Times}, Iter) ->
|
|
|
|
case regexp:sub(S, RegExp, New) of
|
|
|
|
{ok, NewS, 0} ->
|
|
|
|
NewS;
|
|
|
|
{ok, NewS, _} when Iter < Times ->
|
|
|
|
do_sub(NewS, {RegExp, New, Times}, Iter+1);
|
|
|
|
{ok, NewS, _} ->
|
|
|
|
NewS;
|
|
|
|
_ ->
|
2010-04-19 06:08:00 +02:00
|
|
|
erlang:error(bad_regexp)
|
2006-09-14 04:54:21 +02:00
|
|
|
end.
|
|
|
|
|
2006-09-23 06:28:03 +02:00
|
|
|
replace_amps(String) ->
|
2010-08-19 08:28:31 +02:00
|
|
|
lists:flatmap(
|
2010-04-19 06:08:00 +02:00
|
|
|
fun($&) -> "\\&";
|
2010-08-19 08:28:31 +02:00
|
|
|
(Chr) -> [Chr]
|
2010-04-19 06:08:00 +02:00
|
|
|
end, String).
|