1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File: eldap_filter.erl |
3 |
|
%%% Purpose: Converts String Representation of |
4 |
|
%%% LDAP Search Filter (RFC 2254) |
5 |
|
%%% to eldap's representation of filter |
6 |
|
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net> |
7 |
|
%%% |
8 |
|
%%% |
9 |
|
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne |
10 |
|
%%% |
11 |
|
%%% This program is free software; you can redistribute it and/or |
12 |
|
%%% modify it under the terms of the GNU General Public License as |
13 |
|
%%% published by the Free Software Foundation; either version 2 of the |
14 |
|
%%% License, or (at your option) any later version. |
15 |
|
%%% |
16 |
|
%%% This program is distributed in the hope that it will be useful, |
17 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 |
|
%%% General Public License for more details. |
20 |
|
%%% |
21 |
|
%%% You should have received a copy of the GNU General Public License |
22 |
|
%%% along with this program; if not, write to the Free Software |
23 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
24 |
|
%%% |
25 |
|
%%%---------------------------------------------------------------------- |
26 |
|
-module(eldap_filter). |
27 |
|
|
28 |
|
%% TODO: remove this when new regexp module will be used |
29 |
|
-export([parse/1, parse/2, do_sub/2]). |
30 |
|
|
31 |
|
%%==================================================================== |
32 |
|
%% API |
33 |
|
%%==================================================================== |
34 |
|
%%%------------------------------------------------------------------- |
35 |
|
%%% Arity: parse/1 |
36 |
|
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} | |
37 |
|
%%% {error, bad_filter} |
38 |
|
%%% |
39 |
|
%%% RFC2254_Filter = string(). |
40 |
|
%%% |
41 |
|
%%% Description: Converts String Representation of LDAP Search Filter (RFC 2254) |
42 |
|
%%% to eldap's representation of filter. |
43 |
|
%%% |
44 |
|
%%% Example: |
45 |
|
%%% > eldap_filter:parse("(&(!(uid<=100))(mail=*))"). |
46 |
|
%%% |
47 |
|
%%% {ok, {'and', [{'not', {lessOrEqual, {'AttributeValueAssertion', "uid", "100"}}}, |
48 |
|
%%% {present, "mail"}]}} |
49 |
|
%%%------------------------------------------------------------------- |
50 |
|
-spec parse(binary()) -> {error, any()} | {ok, eldap_utils:filter()}. |
51 |
|
parse(L) -> |
52 |
:-( |
parse(L, []). |
53 |
|
|
54 |
|
%%%------------------------------------------------------------------- |
55 |
|
%%% Arity: parse/2 |
56 |
|
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) -> |
57 |
|
%%% {ok, EldapFilter} | |
58 |
|
%%% {error, bad_filter} | |
59 |
|
%%% {error, bad_regexp} | |
60 |
|
%%% {error, max_substitute_recursion} |
61 |
|
%%% |
62 |
|
%%% SubstValue = {RegExp, Value} | {RegExp, Value, N}, |
63 |
|
%%% RFC2254_Filter = RegExp = Value = string(), |
64 |
|
%%% N = integer(). |
65 |
|
%%% |
66 |
|
%%% Description: The same as parse/1, but substitutes N or all occurences |
67 |
|
%%% of RegExp with Value *after* parsing. |
68 |
|
%%% |
69 |
|
%%% Example: |
70 |
|
%%% > eldap_filter:parse( |
71 |
|
%%% "(|(mail=%u@%d)(jid=%u@%d))", |
72 |
|
%%% [{"%u", "xramtsov"}, {"%d", "gmail.com"}]). |
73 |
|
%%% |
74 |
|
%%% {ok, {'or', [{equalityMatch, {'AttributeValueAssertion', |
75 |
|
%%% "mail", |
76 |
|
%%% "xramtsov@gmail.com"}}, |
77 |
|
%%% {equalityMatch, {'AttributeValueAssertion', |
78 |
|
%%% "jid", |
79 |
|
%%% "xramtsov@gmail.com"}}]}} |
80 |
|
%%%------------------------------------------------------------------- |
81 |
|
-spec parse(binary(), [{binary(), binary()} | |
82 |
|
{binary(), binary(), pos_integer()}]) -> |
83 |
|
{error, any()} | {ok, eldap_utils:filter()}. |
84 |
|
|
85 |
|
parse(L, SList) -> |
86 |
:-( |
case catch eldap_filter_yecc:parse(scan(binary_to_list(L), SList)) of |
87 |
|
{'EXIT', _} = Err -> |
88 |
:-( |
{error, Err}; |
89 |
|
{error, {_, _, Msg}} -> |
90 |
:-( |
{error, Msg}; |
91 |
|
{ok, Result} -> |
92 |
:-( |
{ok, Result}; |
93 |
|
{regexp, Err} -> |
94 |
:-( |
{error, Err} |
95 |
|
end. |
96 |
|
|
97 |
|
%%==================================================================== |
98 |
|
%% Internal functions |
99 |
|
%%==================================================================== |
100 |
|
-define(do_scan(L), scan(Rest, <<>>, [{L, 1} | check(Buf, S) ++ Result], L, S)). |
101 |
|
|
102 |
|
|
103 |
|
-spec scan([byte()], _) -> [{atom(), 1} | {'str', 1, [any()]}]. |
104 |
|
scan(L, SList) -> |
105 |
:-( |
scan(L, <<"">>, [], undefined, SList). |
106 |
|
|
107 |
|
|
108 |
|
-spec scan([byte()], Buf :: binary(), Result :: [{atom(), 1} | {'str', 1, [any()]}], |
109 |
|
atom(), S :: any()) -> [{atom(), 1} | {'str', 1, [any()]}]. |
110 |
|
scan("=*)" ++ Rest, Buf, Result, '(', S) -> |
111 |
:-( |
scan(Rest, <<>>, [{')', 1}, {'=*', 1} | check(Buf, S) ++ Result], ')', S); |
112 |
:-( |
scan(":dn" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':dn'); |
113 |
:-( |
scan(":=" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':='); |
114 |
:-( |
scan(":=" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':='); |
115 |
:-( |
scan(":=" ++ Rest, Buf, Result, ':', S) -> ?do_scan(':='); |
116 |
:-( |
scan("~=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('~='); |
117 |
:-( |
scan(">=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('>='); |
118 |
:-( |
scan("<=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('<='); |
119 |
:-( |
scan("=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('='); |
120 |
:-( |
scan(":" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':'); |
121 |
:-( |
scan(":" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':'); |
122 |
:-( |
scan("&" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('&'); |
123 |
:-( |
scan("|" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('|'); |
124 |
:-( |
scan("!" ++ Rest, Buf, Result, '(', S) when Buf==<<"">> -> ?do_scan('!'); |
125 |
:-( |
scan("*" ++ Rest, Buf, Result, '*', S) -> ?do_scan('*'); |
126 |
:-( |
scan("*" ++ Rest, Buf, Result, '=', S) -> ?do_scan('*'); |
127 |
:-( |
scan("(" ++ Rest, Buf, Result, _, S) -> ?do_scan('('); |
128 |
:-( |
scan(")" ++ Rest, Buf, Result, _, S) -> ?do_scan(')'); |
129 |
|
scan([Letter | Rest], Buf, Result, PreviosAtom, S) -> |
130 |
:-( |
scan(Rest, <<Buf/binary, Letter>>, Result, PreviosAtom, S); |
131 |
|
scan([], Buf, Result, _, S) -> |
132 |
:-( |
lists:reverse(check(Buf, S) ++ Result). |
133 |
|
|
134 |
|
|
135 |
|
-spec check(binary(), _) -> [{'str', 1, [byte()]}]. |
136 |
|
check(<<>>, _) -> |
137 |
:-( |
[]; |
138 |
|
check(Buf, S) -> |
139 |
:-( |
[{str, 1, binary_to_list(do_sub(Buf, S))}]. |
140 |
|
|
141 |
|
|
142 |
|
-define(MAX_RECURSION, 100). |
143 |
|
|
144 |
|
|
145 |
|
-spec do_sub(binary(), [{binary(), binary()} | |
146 |
|
{binary(), binary(), pos_integer()}]) -> binary(). |
147 |
|
do_sub(S, []) -> |
148 |
:-( |
S; |
149 |
|
do_sub(<<>>, _) -> |
150 |
:-( |
<<>>; |
151 |
|
do_sub(S, [{RegExp, New} | T]) -> |
152 |
:-( |
Result = do_sub(S, {RegExp, replace_amps(New)}, 1), |
153 |
:-( |
do_sub(Result, T); |
154 |
|
do_sub(S, [{RegExp, New, Times} | T]) -> |
155 |
:-( |
Result = do_sub(S, {RegExp, replace_amps(New), Times}, 1), |
156 |
:-( |
do_sub(Result, T). |
157 |
|
|
158 |
|
|
159 |
|
do_sub(S, {RegExp, New}, _Iter) -> |
160 |
:-( |
re:replace(S, RegExp, New, [global, {return, binary}]); |
161 |
|
do_sub(S, {_, _, N}, _) when N<1 -> |
162 |
:-( |
S; |
163 |
|
do_sub(S, {RegExp, New, _Times}, _Iter) -> |
164 |
:-( |
re:replace(S, RegExp, New, [global, {return, binary}]). |
165 |
|
|
166 |
|
|
167 |
|
-spec replace_amps(binary()) -> binary(). |
168 |
|
replace_amps(Bin) -> |
169 |
:-( |
list_to_binary( |
170 |
|
lists:flatmap( |
171 |
:-( |
fun($&) -> "\\&"; |
172 |
:-( |
($\\) -> "\\\\"; |
173 |
:-( |
(Chr) -> [Chr] |
174 |
|
end, eldap_utils:maybe_b2list(Bin))). |