0a58f7a23c5af3a9c43455db861d38db5291a384
[kamenim/samba.git] / source4 / dsdb / schema / schema_query.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    DSDB schema header
4    
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20    
21 */
22
23 #include "includes.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/util/binsearch.h"
26
27 static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx, 
28                                                       const struct dsdb_schema *schema, 
29                                                       const char **class_list,
30                                                       enum dsdb_attr_list_query query);
31
32 static int uint32_cmp(uint32_t c1, uint32_t c2) 
33 {
34         return c1 - c2;
35 }
36
37 static int strcasecmp_with_ldb_val(const struct ldb_val *target, const char *str)
38 {
39         int ret = strncasecmp((const char *)target->data, str, target->length);
40         if (ret == 0) {
41                 return (target->length - strlen(str));
42         }
43         return ret;
44 }
45
46 const struct dsdb_attribute *dsdb_attribute_by_attributeID_id(const struct dsdb_schema *schema,
47                                                               uint32_t id)
48 {
49         struct dsdb_attribute *c;
50
51         /*
52          * 0xFFFFFFFF is used as value when no mapping table is available,
53          * so don't try to match with it
54          */
55         if (id == 0xFFFFFFFF) return NULL;
56
57         BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_id,
58                               schema->num_attributes, attributeID_id, id, uint32_cmp, c);
59         return c;
60 }
61
62 const struct dsdb_attribute *dsdb_attribute_by_attributeID_oid(const struct dsdb_schema *schema,
63                                                                const char *oid)
64 {
65         struct dsdb_attribute *c;
66
67         if (!oid) return NULL;
68
69         BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_oid,
70                               schema->num_attributes, attributeID_oid, oid, strcasecmp, c);
71         return c;
72 }
73
74 const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName(const struct dsdb_schema *schema,
75                                                                const char *name)
76 {
77         struct dsdb_attribute *c;
78
79         if (!name) return NULL;
80
81         BINARY_ARRAY_SEARCH_P(schema->attributes_by_lDAPDisplayName,
82                               schema->num_attributes, lDAPDisplayName, name, strcasecmp, c);
83         return c;
84 }
85
86 const struct dsdb_attribute *dsdb_attribute_by_linkID(const struct dsdb_schema *schema,
87                                                       int linkID)
88 {
89         struct dsdb_attribute *c;
90
91         BINARY_ARRAY_SEARCH_P(schema->attributes_by_linkID,
92                               schema->num_attributes, linkID, linkID, uint32_cmp, c);
93         return c;
94 }
95
96 const struct dsdb_class *dsdb_class_by_governsID_id(const struct dsdb_schema *schema,
97                                                     uint32_t id)
98 {
99         struct dsdb_class *c;
100
101         /*
102          * 0xFFFFFFFF is used as value when no mapping table is available,
103          * so don't try to match with it
104          */
105         if (id == 0xFFFFFFFF) return NULL;
106
107         BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_id,
108                               schema->num_classes, governsID_id, id, uint32_cmp, c);
109         return c;
110 }
111
112 const struct dsdb_class *dsdb_class_by_governsID_oid(const struct dsdb_schema *schema,
113                                                      const char *oid)
114 {
115         struct dsdb_class *c;
116         if (!oid) return NULL;
117         BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_oid,
118                               schema->num_classes, governsID_oid, oid, strcasecmp, c);
119         return c;
120 }
121
122 const struct dsdb_class *dsdb_class_by_lDAPDisplayName(const struct dsdb_schema *schema,
123                                                        const char *name)
124 {
125         struct dsdb_class *c;
126         if (!name) return NULL;
127         BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
128                               schema->num_classes, lDAPDisplayName, name, strcasecmp, c);
129         return c;
130 }
131
132 const struct dsdb_class *dsdb_class_by_lDAPDisplayName_ldb_val(const struct dsdb_schema *schema,
133                                                                const struct ldb_val *name)
134 {
135         struct dsdb_class *c;
136         if (!name) return NULL;
137         BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
138                               schema->num_classes, lDAPDisplayName, name, strcasecmp_with_ldb_val, c);
139         return c;
140 }
141
142 const struct dsdb_class *dsdb_class_by_cn(const struct dsdb_schema *schema,
143                                           const char *cn)
144 {
145         struct dsdb_class *c;
146         if (!cn) return NULL;
147         BINARY_ARRAY_SEARCH_P(schema->classes_by_cn,
148                               schema->num_classes, cn, cn, strcasecmp, c);
149         return c;
150 }
151
152 const struct dsdb_class *dsdb_class_by_cn_ldb_val(const struct dsdb_schema *schema,
153                                                   const struct ldb_val *cn)
154 {
155         struct dsdb_class *c;
156         if (!cn) return NULL;
157         BINARY_ARRAY_SEARCH_P(schema->classes_by_cn,
158                               schema->num_classes, cn, cn, strcasecmp_with_ldb_val, c);
159         return c;
160 }
161
162 const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
163                                        uint32_t id)
164 {
165         const struct dsdb_attribute *a;
166         const struct dsdb_class *c;
167
168         a = dsdb_attribute_by_attributeID_id(schema, id);
169         if (a) {
170                 return a->lDAPDisplayName;
171         }
172
173         c = dsdb_class_by_governsID_id(schema, id);
174         if (c) {
175                 return c->lDAPDisplayName;
176         }
177
178         return NULL;
179 }
180
181 /** 
182     Return a list of linked attributes, in lDAPDisplayName format.
183
184     This may be used to determine if a modification would require
185     backlinks to be updated, for example
186 */
187
188 WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *schema, TALLOC_CTX *mem_ctx, const char ***attr_list_ret)
189 {
190         const char **attr_list = NULL;
191         struct dsdb_attribute *cur;
192         int i = 0;
193         for (cur = schema->attributes; cur; cur = cur->next) {
194                 if (cur->linkID == 0) continue;
195                 
196                 attr_list = talloc_realloc(mem_ctx, attr_list, const char *, i+2);
197                 if (!attr_list) {
198                         return WERR_NOMEM;
199                 }
200                 attr_list[i] = cur->lDAPDisplayName;
201                 i++;
202         }
203         attr_list[i] = NULL;
204         *attr_list_ret = attr_list;
205         return WERR_OK;
206 }
207
208 const char **merge_attr_list(TALLOC_CTX *mem_ctx, 
209                        const char **attrs, const char * const*new_attrs) 
210 {
211         const char **ret_attrs;
212         int i;
213         size_t new_len, orig_len = str_list_length(attrs);
214         if (!new_attrs) {
215                 return attrs;
216         }
217
218         ret_attrs = talloc_realloc(mem_ctx, 
219                                    attrs, const char *, orig_len + str_list_length(new_attrs) + 1);
220         if (ret_attrs) {
221                 for (i=0; i < str_list_length(new_attrs); i++) {
222                         ret_attrs[orig_len + i] = new_attrs[i];
223                 }
224                 new_len = orig_len + str_list_length(new_attrs);
225
226                 ret_attrs[new_len] = NULL;
227         }
228
229         return ret_attrs;
230 }
231
232 /*
233   Return a merged list of the attributes of exactly one class (not
234   considering subclasses, auxillary classes etc)
235 */
236
237 const char **dsdb_attribute_list(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass, enum dsdb_attr_list_query query)
238 {
239         const char **attr_list = NULL;
240         switch (query) {
241         case DSDB_SCHEMA_ALL_MAY:
242                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
243                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
244                 break;
245                 
246         case DSDB_SCHEMA_ALL_MUST:
247                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
248                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
249                 break;
250                 
251         case DSDB_SCHEMA_SYS_MAY:
252                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
253                 break;
254                 
255         case DSDB_SCHEMA_SYS_MUST:
256                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
257                 break;
258                 
259         case DSDB_SCHEMA_MAY:
260                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
261                 break;
262                 
263         case DSDB_SCHEMA_MUST:
264                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
265                 break;
266                 
267         case DSDB_SCHEMA_ALL:
268                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
269                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
270                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
271                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
272                 break;
273         }
274         return attr_list;
275 }
276
277 static const char **attribute_list_from_class(TALLOC_CTX *mem_ctx,
278                                               const struct dsdb_schema *schema, 
279                                               const struct dsdb_class *sclass,
280                                               enum dsdb_attr_list_query query) 
281 {
282         const char **this_class_list;
283         const char **system_recursive_list;
284         const char **recursive_list;
285         const char **attr_list;
286
287         this_class_list = dsdb_attribute_list(mem_ctx, sclass, query);
288         
289         recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
290                                                            sclass->systemAuxiliaryClass,
291                                                            query);
292         
293         system_recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
294                                                                   sclass->auxiliaryClass,
295                                                                   query);
296         
297         attr_list = this_class_list;
298         attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
299         attr_list = merge_attr_list(mem_ctx, attr_list, system_recursive_list);
300         return attr_list;
301 }
302
303 /* Return a full attribute list for a given class list (as a ldb_message_element)
304
305    Via attribute_list_from_class() this calls itself when recursing on auxiliary classes
306  */
307 static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx, 
308                                                       const struct dsdb_schema *schema, 
309                                                       const char **class_list,
310                                                       enum dsdb_attr_list_query query)
311 {
312         int i;
313         const char **attr_list = NULL;
314
315         for (i=0; class_list && class_list[i]; i++) {
316                 const char **sclass_list
317                         = attribute_list_from_class(mem_ctx, schema,
318                                                     dsdb_class_by_lDAPDisplayName(schema, class_list[i]),
319                                                     query);
320
321                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
322         }
323         return attr_list;
324 }
325
326 /* Return a full attribute list for a given class list (as a ldb_message_element)
327
328    Using the ldb_message_element ensures we do length-limited
329    comparisons, rather than casting the possibly-unterminated string
330
331    Via attribute_list_from_class() this calls 
332    dsdb_full_attribute_list_internal() when recursing on auxiliary classes
333  */
334 static const char **dsdb_full_attribute_list_internal_el(TALLOC_CTX *mem_ctx, 
335                                                          const struct dsdb_schema *schema, 
336                                                          const struct ldb_message_element *el,
337                                                          enum dsdb_attr_list_query query)
338 {
339         int i;
340         const char **attr_list = NULL;
341
342         for (i=0; i < el->num_values; i++) {
343                 const char **sclass_list
344                         = attribute_list_from_class(mem_ctx, schema,
345                                                     dsdb_class_by_lDAPDisplayName_ldb_val(schema, &el->values[i]),
346                                                     query);
347                 
348                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
349         }
350         return attr_list;
351 }
352
353 /* Helper function to remove duplicates from the attribute list to be returned */
354 static const char **dedup_attr_list(const char **attr_list) 
355 {
356         size_t new_len = str_list_length(attr_list);
357         /* Remove duplicates */
358         if (new_len > 1) {
359                 int i;
360                 qsort(attr_list, new_len,
361                       sizeof(*attr_list),
362                       (comparison_fn_t)strcasecmp);
363                 
364                 for (i=1 ; i < new_len; i++) {
365                         const char **val1 = &attr_list[i-1];
366                         const char **val2 = &attr_list[i];
367                         if (ldb_attr_cmp(*val1, *val2) == 0) {
368                                 memmove(val1, val2, (new_len - i) * sizeof( *attr_list)); 
369                                 new_len--;
370                                 i--;
371                         }
372                 }
373         }
374         return attr_list;
375 }
376
377 /* Return a full attribute list for a given class list (as a ldb_message_element)
378
379    Using the ldb_message_element ensures we do length-limited
380    comparisons, rather than casting the possibly-unterminated string
381
382    The result contains only unique values
383  */
384 const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx, 
385                                       const struct dsdb_schema *schema, 
386                                       const struct ldb_message_element *class_list,
387                                       enum dsdb_attr_list_query query)
388 {
389         const char **attr_list = dsdb_full_attribute_list_internal_el(mem_ctx, schema, class_list, query);
390         return dedup_attr_list(attr_list);
391 }
392
393 /* Return the schemaIDGUID of a class */
394
395 const struct GUID *class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
396                                                           const char *name)
397 {
398         const struct dsdb_class *object_class = dsdb_class_by_lDAPDisplayName(schema, name);
399         if (!object_class)
400                 return NULL;
401
402         return &object_class->schemaIDGUID;
403 }
404
405 const struct GUID *attribute_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
406                                                               const char *name)
407 {
408         const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, name);
409         if (!attr)
410                 return NULL;
411
412         return &attr->schemaIDGUID;
413 }