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