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