major upgrade to the ldb attribute handling
[sfrench/samba-autobuild/.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
26 /* a binary array search, where the array is an array of pointers to structures,
27    and we want to find a match for 'target' on 'field' in those structures.
28
29    Inputs:
30       array:          base pointer to an array of structures
31       arrray_size:    number of elements in the array
32       field:          the name of the field in the structure we are keying off
33       target:         the field value we are looking for
34       comparison_fn:  the comparison function
35       result:         where the result of the search is put
36
37    if the element is found, then 'result' is set to point to the found array element. If not,
38    then 'result' is set to NULL.
39
40    The array is assumed to be sorted by the same comparison_fn as the
41    search (with, for example, qsort)
42  */
43 #define BINARY_ARRAY_SEARCH(array, array_size, field, target, comparison_fn, result) do { \
44         int32_t _b, _e; \
45         (result) = NULL; \
46         for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \
47                 int32_t _i = (_b+_e)/2; \
48                 int _r = comparison_fn(target, array[_i]->field); \
49                 if (_r == 0) { (result) = array[_i]; break; } \
50                 if (_r < 0) _e = _i - 1; else _b = _i + 1; \
51         } } while (0)
52
53
54 static int uint32_cmp(uint32_t c1, uint32_t c2) 
55 {
56         return c1 - c2;
57 }
58
59
60 const struct dsdb_attribute *dsdb_attribute_by_attributeID_id(const struct dsdb_schema *schema,
61                                                               uint32_t id)
62 {
63         struct dsdb_attribute *c;
64
65         /*
66          * 0xFFFFFFFF is used as value when no mapping table is available,
67          * so don't try to match with it
68          */
69         if (id == 0xFFFFFFFF) return NULL;
70
71         BINARY_ARRAY_SEARCH(schema->attributes_by_attributeID_id, 
72                             schema->num_attributes, attributeID_id, id, uint32_cmp, c);
73         return c;
74 }
75
76 const struct dsdb_attribute *dsdb_attribute_by_attributeID_oid(const struct dsdb_schema *schema,
77                                                                const char *oid)
78 {
79         struct dsdb_attribute *c;
80
81         if (!oid) return NULL;
82
83         BINARY_ARRAY_SEARCH(schema->attributes_by_attributeID_oid, 
84                             schema->num_attributes, attributeID_oid, oid, strcasecmp, c);
85         return c;
86 }
87
88 const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName(const struct dsdb_schema *schema,
89                                                                const char *name)
90 {
91         struct dsdb_attribute *c;
92
93         if (!name) return NULL;
94
95         BINARY_ARRAY_SEARCH(schema->attributes_by_lDAPDisplayName, 
96                             schema->num_attributes, lDAPDisplayName, name, strcasecmp, c);
97         return c;
98 }
99
100 const struct dsdb_attribute *dsdb_attribute_by_linkID(const struct dsdb_schema *schema,
101                                                       int linkID)
102 {
103         struct dsdb_attribute *c;
104
105         BINARY_ARRAY_SEARCH(schema->attributes_by_linkID, 
106                             schema->num_attributes, linkID, linkID, uint32_cmp, c);
107         return c;
108 }
109
110 const struct dsdb_class *dsdb_class_by_governsID_id(const struct dsdb_schema *schema,
111                                                     uint32_t id)
112 {
113         struct dsdb_class *c;
114
115         /*
116          * 0xFFFFFFFF is used as value when no mapping table is available,
117          * so don't try to match with it
118          */
119         if (id == 0xFFFFFFFF) return NULL;
120
121         BINARY_ARRAY_SEARCH(schema->classes_by_governsID_id, 
122                             schema->num_classes, governsID_id, id, uint32_cmp, c);
123         return c;
124 }
125
126 const struct dsdb_class *dsdb_class_by_governsID_oid(const struct dsdb_schema *schema,
127                                                      const char *oid)
128 {
129         struct dsdb_class *c;
130         if (!oid) return NULL;
131         BINARY_ARRAY_SEARCH(schema->classes_by_governsID_oid, 
132                             schema->num_classes, governsID_oid, oid, strcasecmp, c);
133         return c;
134 }
135
136 const struct dsdb_class *dsdb_class_by_lDAPDisplayName(const struct dsdb_schema *schema,
137                                                        const char *name)
138 {
139         struct dsdb_class *c;
140         if (!name) return NULL;
141         BINARY_ARRAY_SEARCH(schema->classes_by_lDAPDisplayName, 
142                             schema->num_classes, lDAPDisplayName, name, strcasecmp, c);
143         return c;
144 }
145
146 const struct dsdb_class *dsdb_class_by_cn(const struct dsdb_schema *schema,
147                                           const char *cn)
148 {
149         struct dsdb_class *c;
150         if (!cn) return NULL;
151         BINARY_ARRAY_SEARCH(schema->classes_by_cn, 
152                             schema->num_classes, cn, cn, strcasecmp, c);
153         return c;
154 }
155
156 const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
157                                        uint32_t id)
158 {
159         const struct dsdb_attribute *a;
160         const struct dsdb_class *c;
161
162         a = dsdb_attribute_by_attributeID_id(schema, id);
163         if (a) {
164                 return a->lDAPDisplayName;
165         }
166
167         c = dsdb_class_by_governsID_id(schema, id);
168         if (c) {
169                 return c->lDAPDisplayName;
170         }
171
172         return NULL;
173 }
174
175 /** 
176     Return a list of linked attributes, in lDAPDisplayName format.
177
178     This may be used to determine if a modification would require
179     backlinks to be updated, for example
180 */
181
182 WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *schema, TALLOC_CTX *mem_ctx, const char ***attr_list_ret)
183 {
184         const char **attr_list = NULL;
185         struct dsdb_attribute *cur;
186         int i = 0;
187         for (cur = schema->attributes; cur; cur = cur->next) {
188                 if (cur->linkID == 0) continue;
189                 
190                 attr_list = talloc_realloc(mem_ctx, attr_list, const char *, i+2);
191                 if (!attr_list) {
192                         return WERR_NOMEM;
193                 }
194                 attr_list[i] = cur->lDAPDisplayName;
195                 i++;
196         }
197         attr_list[i] = NULL;
198         *attr_list_ret = attr_list;
199         return WERR_OK;
200 }
201
202 const char **merge_attr_list(TALLOC_CTX *mem_ctx, 
203                        const char **attrs, const char * const*new_attrs) 
204 {
205         const char **ret_attrs;
206         int i;
207         size_t new_len, orig_len = str_list_length(attrs);
208         if (!new_attrs) {
209                 return attrs;
210         }
211
212         ret_attrs = talloc_realloc(mem_ctx, 
213                                    attrs, const char *, orig_len + str_list_length(new_attrs) + 1);
214         if (ret_attrs) {
215                 for (i=0; i < str_list_length(new_attrs); i++) {
216                         ret_attrs[orig_len + i] = new_attrs[i];
217                 }
218                 new_len = orig_len + str_list_length(new_attrs);
219
220                 ret_attrs[new_len] = NULL;
221         }
222
223         return ret_attrs;
224 }
225
226 /*
227   Return a merged list of the attributes of exactly one class (not
228   considering subclasses, auxillary classes etc)
229 */
230
231 const char **dsdb_attribute_list(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass, enum dsdb_attr_list_query query)
232 {
233         const char **attr_list = NULL;
234         switch (query) {
235         case DSDB_SCHEMA_ALL_MAY:
236                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
237                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
238                 break;
239                 
240         case DSDB_SCHEMA_ALL_MUST:
241                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
242                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
243                 break;
244                 
245         case DSDB_SCHEMA_SYS_MAY:
246                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
247                 break;
248                 
249         case DSDB_SCHEMA_SYS_MUST:
250                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
251                 break;
252                 
253         case DSDB_SCHEMA_MAY:
254                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
255                 break;
256                 
257         case DSDB_SCHEMA_MUST:
258                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
259                 break;
260                 
261         case DSDB_SCHEMA_ALL:
262                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
263                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
264                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
265                 attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
266                 break;
267         }
268         return attr_list;
269 }
270
271 static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx, 
272                                                 const struct dsdb_schema *schema, 
273                                                 const char **class_list,
274                                                 enum dsdb_attr_list_query query)
275 {
276         int i;
277         const struct dsdb_class *sclass;
278         
279         const char **attr_list = NULL;
280         const char **this_class_list;
281         const char **recursive_list;
282
283         for (i=0; class_list && class_list[i]; i++) {
284                 sclass = dsdb_class_by_lDAPDisplayName(schema, class_list[i]);
285                 
286                 this_class_list = dsdb_attribute_list(mem_ctx, sclass, query);
287                 attr_list = merge_attr_list(mem_ctx, attr_list, this_class_list);
288
289                 recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
290                                                                    sclass->systemAuxiliaryClass,
291                                                                    query);
292                 
293                 attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
294                 
295                 recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema, 
296                                                                    sclass->auxiliaryClass,
297                                                                    query);
298                 
299                 attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
300                 
301         }
302         return attr_list;
303 }
304
305 const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx, 
306                                 const struct dsdb_schema *schema, 
307                                 const char **class_list,
308                                 enum dsdb_attr_list_query query)
309 {
310         const char **attr_list = dsdb_full_attribute_list_internal(mem_ctx, schema, class_list, query);
311         size_t new_len = str_list_length(attr_list);
312
313         /* Remove duplicates */
314         if (new_len > 1) {
315                 int i;
316                 qsort(attr_list, new_len,
317                       sizeof(*attr_list),
318                       (comparison_fn_t)strcasecmp);
319                 
320                 for (i=1 ; i < new_len; i++) {
321                         const char **val1 = &attr_list[i-1];
322                         const char **val2 = &attr_list[i];
323                         if (ldb_attr_cmp(*val1, *val2) == 0) {
324                                 memmove(val1, val2, (new_len - i) * sizeof( *attr_list)); 
325                                 new_len--;
326                                 i--;
327                         }
328                 }
329         }
330         return attr_list;
331 }