Source file ldap_schemaparser.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
open Ldap_schemalexer;;
module Oid =
(struct
type t = string
let of_string s = s
let to_string oid = oid
let compare x y = String.compare (to_string x) (to_string y)
end
:
sig
type t
val of_string: string -> t
val to_string: t -> string
val compare: t -> t -> int
end);;
let format_oid id =
Format.open_box 0;
Format.print_string ("<oid " ^ Oid.to_string id ^ ">");
Format.close_box ()
module Lcstring =
(struct
type t = string
let of_string s = String.lowercase s
let to_string x = x
let compare x y = String.compare x y
end
:
sig
type t
val of_string: string -> t
val to_string: t -> string
val compare: t -> t -> int
end);;
let format_lcstring id =
Format.open_box 0;
Format.print_string ("<lcstring " ^ Lcstring.to_string id ^ ">");
Format.close_box ()
type octype = Abstract | Structural | Auxiliary;;
type objectclass = {oc_name: string list;
oc_oid:Oid.t;
oc_desc:string;
oc_obsolete:bool;
oc_sup:Lcstring.t list;
oc_must:Lcstring.t list;
oc_may:Lcstring.t list;
oc_type:octype;
oc_xattr:string list}
type attribute = {at_name:string list;
at_desc:string;
at_oid:Oid.t;
at_equality:string;
at_ordering:string;
at_substr:Oid.t;
at_syntax:Oid.t;
at_length: Int64.t;
at_obsolete:bool;
at_single_value:bool;
at_collective:bool;
at_no_user_modification:bool;
at_usage:string;
at_sup:Lcstring.t list;
at_xattr:string list};;
type schema = {objectclasses: (Lcstring.t, objectclass) Hashtbl.t;
objectclasses_byoid: (Oid.t, objectclass) Hashtbl.t;
attributes: (Lcstring.t, attribute) Hashtbl.t;
attributes_byoid: (Oid.t, attribute) Hashtbl.t};;
type schema_error = Undefined_attr_reference of string
| Undefined_oc_reference of string
| Cross_linked_oid of string list
let typecheck_schema schema =
let attribute_exists_p schema attr =
if Hashtbl.mem schema.attributes attr then true
else
Hashtbl.fold
(fun _ {at_name=names} b ->
if b then b
else
List.exists
(fun name -> (Lcstring.of_string name) = attr)
names)
schema.attributes
false
in
let errors =
Hashtbl.fold
(fun oc {oc_must=musts;oc_may=mays} errors ->
let check_error errors attr =
if not (attribute_exists_p schema attr) then
(Lcstring.to_string oc,
Undefined_attr_reference (Lcstring.to_string attr)) :: errors
else errors
in
(List.rev_append
errors
(List.rev_append
(List.fold_left check_error [] musts)
(List.fold_left check_error [] mays))))
schema.objectclasses
[]
in
let errors =
let oids = Hashtbl.create 100 in
let seen = Hashtbl.create 100 in
Hashtbl.iter
(fun oid {at_name=n} -> Hashtbl.add oids oid (List.hd n))
schema.attributes_byoid;
Hashtbl.iter
(fun oid {oc_name=n} -> Hashtbl.add oids oid (List.hd n))
schema.objectclasses_byoid;
Hashtbl.fold
(fun oid name errors ->
if List.length (Hashtbl.find_all oids oid) > 1 then
if Hashtbl.mem seen oid then
errors
else (
Hashtbl.add seen oid ();
(name, Cross_linked_oid (Hashtbl.find_all oids oid)) :: errors
)
else
errors
)
oids
errors
in
let errors =
Hashtbl.fold
(fun oc {oc_sup=sups} errors ->
List.rev_append
errors
(List.rev_map
(fun missing -> (missing, Undefined_oc_reference missing))
(List.filter
(fun oc ->
not
(Hashtbl.mem
schema.objectclasses
(Lcstring.of_string oc)))
(List.rev_map Lcstring.to_string sups))))
schema.objectclasses
errors
in
errors
let schema_print_depth = ref 10
let format_schema s =
let indent = 3 in
let printtbl tbl =
let i = ref 0 in
try
Hashtbl.iter
(fun aname aval ->
if !i < !schema_print_depth then begin
Format.print_string ("<KEY " ^ (Lcstring.to_string aname) ^ ">");
Format.print_break 1 indent;
i := !i + 1
end
else failwith "depth")
tbl
with Failure "depth" -> Format.print_string "..."
in
Format.open_box 0;
Format.print_string "{objectclasses = <HASHTBL ";
Format.print_break 0 indent;
printtbl s.objectclasses;
Format.print_string ">;";
Format.print_break 0 1;
Format.print_string "objectclasses_byoid = <HASHTBL ...>;";
Format.print_break 0 1;
Format.print_string "attributes = <HASHTBL ";
Format.print_break 0 indent;
printtbl s.attributes;
Format.print_string ">;";
Format.print_break 0 1;
Format.print_string "attributes_byoid = <HASHTBL ...>}";
Format.close_box ()
exception Parse_error_oc of Lexing.lexbuf * objectclass * string;;
exception Parse_error_at of Lexing.lexbuf * attribute * string;;
exception Syntax_error_oc of Lexing.lexbuf * objectclass * string;;
exception Syntax_error_at of Lexing.lexbuf * attribute * string;;
let rec readSchema oclst attrlst =
let empty_oc = {oc_name=[];oc_oid=Oid.of_string "";oc_desc="";oc_obsolete=false;oc_sup=[];
oc_must=[];oc_may=[];oc_type=Abstract;oc_xattr=[]}
in
let empty_attr = {at_name=[];at_oid=Oid.of_string "";at_desc="";at_equality="";at_ordering="";
at_usage=""; at_substr=Oid.of_string "";at_syntax=Oid.of_string "";
at_length=0L;at_obsolete=false;at_single_value=false;
at_collective=false;at_no_user_modification=false;at_sup=[];at_xattr=[]}
in
let readOc lxbuf oc =
let rec readOptionalFields lxbuf oc =
try match (lexoc lxbuf) with
Name s -> readOptionalFields lxbuf {oc with oc_name=s}
| Desc s -> readOptionalFields lxbuf {oc with oc_desc=s}
| Obsolete -> readOptionalFields lxbuf {oc with oc_obsolete=true}
| Sup s -> (readOptionalFields
lxbuf
{oc with oc_sup=(List.rev_map (Lcstring.of_string) s)})
| Ldap_schemalexer.Abstract -> readOptionalFields lxbuf {oc with oc_type=Abstract}
| Ldap_schemalexer.Structural -> readOptionalFields lxbuf {oc with oc_type=Structural}
| Ldap_schemalexer.Auxiliary -> readOptionalFields lxbuf {oc with oc_type=Auxiliary}
| Must s -> (readOptionalFields
lxbuf
{oc with oc_must=(List.rev_map (Lcstring.of_string) s)})
| May s -> (readOptionalFields
lxbuf
{oc with oc_may=(List.rev_map (Lcstring.of_string) s)})
| Xstring t -> (readOptionalFields
lxbuf
{oc with oc_xattr=(t :: oc.oc_xattr)})
| Rparen -> oc
| _ -> raise (Parse_error_oc (lxbuf, oc, "unexpected token"))
with Failure(_) -> raise (Parse_error_oc (lxbuf, oc, "Expected right parenthesis"))
in
let readOid lxbuf oc =
try match (lexoc lxbuf) with
Numericoid(s) -> readOptionalFields lxbuf {oc with oc_oid=Oid.of_string s}
| _ -> raise (Parse_error_oc (lxbuf, oc, "missing required field, numericoid"))
with Failure(_) -> raise (Syntax_error_oc (lxbuf, oc, "Syntax error"))
in
let readLparen lxbuf oc =
try match (lexoc lxbuf) with
Lparen -> readOid lxbuf oc
| _ -> raise (Parse_error_oc (lxbuf, oc, "Expected left paren"))
with Failure(_) -> raise (Syntax_error_oc (lxbuf, oc, "Syntax error"))
in
readLparen lxbuf oc
in
let rec readOcs oclst schema =
match oclst with
a :: l -> let oc = readOc (Lexing.from_string a) empty_oc in
List.iter (fun n -> Hashtbl.add schema.objectclasses (Lcstring.of_string n) oc) oc.oc_name;
Hashtbl.add schema.objectclasses_byoid oc.oc_oid oc;readOcs l schema
| [] -> ()
in
let rec readAttr lxbuf attr =
let rec readOptionalFields lxbuf attr =
try match (lexattr lxbuf) with
Name s -> readOptionalFields lxbuf {attr with at_name=s}
| Desc s -> readOptionalFields lxbuf {attr with at_desc=s}
| Obsolete -> readOptionalFields lxbuf {attr with at_obsolete=true}
| Sup s ->
readOptionalFields lxbuf {attr with at_sup=(List.rev_map (Lcstring.of_string) s)}
| Equality s -> readOptionalFields lxbuf {attr with at_equality=s}
| Substr s -> readOptionalFields lxbuf {attr with at_substr=Oid.of_string s}
| Ordering s -> readOptionalFields lxbuf {attr with at_ordering=s}
| Syntax (s, l) ->
readOptionalFields lxbuf {attr with at_syntax=Oid.of_string s;at_length=l}
| Single_value -> readOptionalFields lxbuf {attr with at_single_value=true}
| Collective -> readOptionalFields lxbuf {attr with at_collective=true}
| No_user_modification -> readOptionalFields lxbuf {attr with at_no_user_modification=true}
| Usage s -> readOptionalFields lxbuf {attr with at_usage=s}
| Rparen -> attr
| Xstring t -> (readOptionalFields
lxbuf
{attr with at_xattr=(t :: attr.at_xattr)})
| _ -> raise (Parse_error_at (lxbuf, attr, "unexpected token"))
with Failure(f) -> raise (Parse_error_at (lxbuf, attr, f))
in
let readOid lxbuf attr =
try match (lexoc lxbuf) with
Numericoid(s) -> readOptionalFields lxbuf {attr with at_oid=Oid.of_string s}
| _ -> raise (Parse_error_at (lxbuf, attr, "missing required field, numericoid"))
with Failure(_) -> raise (Syntax_error_at (lxbuf, attr, "Syntax error"))
in
let readLparen lxbuf attr =
try match (lexoc lxbuf) with
Lparen -> readOid lxbuf attr
| _ -> raise (Parse_error_at (lxbuf, attr, "Expected left paren"))
with Failure(_) -> raise (Syntax_error_at (lxbuf, attr, "Syntax error"))
in
readLparen lxbuf attr
in
let rec readAttrs attrlst schema =
match attrlst with
a :: l -> let attr = readAttr (Lexing.from_string a) empty_attr in
List.iter (fun n -> Hashtbl.add schema.attributes (Lcstring.of_string n) attr) attr.at_name;
Hashtbl.add schema.attributes_byoid attr.at_oid attr;readAttrs l schema
| [] -> ()
in
let schema = {objectclasses=Hashtbl.create 500;
objectclasses_byoid=Hashtbl.create 500;
attributes=Hashtbl.create 5000;
attributes_byoid=Hashtbl.create 5000} in
readAttrs attrlst schema;
readOcs oclst schema;
schema;;