r15942: Remove the sync internal ldb calls altogether.
[metze/samba/wip.git] / source4 / lib / ldb / modules / operational.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 /*
26   handle operational attributes
27  */
28
29 /*
30   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
31   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
32
33      for the above two, we do the search as normal, and if
34      createTimestamp or modifyTimestamp is asked for, then do
35      additional searches for whenCreated and whenChanged and fill in
36      the resulting values
37
38      we also need to replace these with the whenCreated/whenChanged
39      equivalent in the search expression trees
40
41   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43
44      on init we need to setup attribute handlers for these so
45      comparisons are done correctly. The resolution is 1 second.
46
47      on add we need to add both the above, for current time
48
49      on modify we need to change whenChanged
50
51
52   subschemaSubentry: HIDDEN, not-searchable, 
53                      points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
54
55      for this one we do the search as normal, then add the static
56      value if requested. How do we work out the $BASEDN from inside a
57      module?
58      
59
60   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61
62      for this one we do the search as normal, then if requested ask
63      for objectclass, change the attribute name, and add it
64
65   allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
66      list of attributes that can be modified - requires schema lookup
67
68
69   attributeTypes: in schema only
70   objectClasses: in schema only
71   matchingRules: in schema only
72   matchingRuleUse: in schema only
73   creatorsName: not supported by w2k3?
74   modifiersName: not supported by w2k3?
75 */
76
77 #include "includes.h"
78 #include "ldb/include/includes.h"
79
80 /*
81   construct a canonical name from a message
82 */
83 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
84 {
85         char *canonicalName;
86         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
87         if (canonicalName == NULL) {
88                 return -1;
89         }
90         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
91 }
92
93 /*
94   a list of attribute names that should be substituted in the parse
95   tree before the search is done
96 */
97 static const struct {
98         const char *attr;
99         const char *replace;
100 } parse_tree_sub[] = {
101         { "createTimestamp", "whenCreated" },
102         { "modifyTimestamp", "whenChanged" }
103 };
104
105
106 /*
107   a list of attribute names that are hidden, but can be searched for
108   using another (non-hidden) name to produce the correct result
109 */
110 static const struct {
111         const char *attr;
112         const char *replace;
113         int (*constructor)(struct ldb_module *, struct ldb_message *);
114 } search_sub[] = {
115         { "createTimestamp", "whenCreated", NULL },
116         { "modifyTimestamp", "whenChanged", NULL },
117         { "structuralObjectClass", "objectClass", NULL },
118         { "canonicalName", "distinguishedName", construct_canonical_name }
119 };
120
121 /*
122   post process a search result record. For any search_sub[] attributes that were
123   asked for, we need to call the appropriate copy routine to copy the result
124   into the message, then remove any attributes that we added to the search but were
125   not asked for by the user
126 */
127 static int operational_search_post_process(struct ldb_module *module,
128                                            struct ldb_message *msg, 
129                                            const char * const *attrs)
130 {
131         int i, a=0;
132
133         for (a=0;attrs && attrs[a];a++) {
134                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
135                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
136                                 continue;
137                         }
138
139                         /* construct the new attribute, using either a supplied 
140                            constructor or a simple copy */
141                         if (search_sub[i].constructor) {
142                                 if (search_sub[i].constructor(module, msg) != 0) {
143                                         goto failed;
144                                 }
145                         } else if (ldb_msg_copy_attr(msg,
146                                                      search_sub[i].replace,
147                                                      search_sub[i].attr) != 0) {
148                                 goto failed;
149                         }
150
151                         /* remove the added search attribute, unless it was asked for 
152                            by the user */
153                         if (search_sub[i].replace == NULL ||
154                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
155                             ldb_attr_in_list(attrs, "*")) {
156                                 continue;
157                         }
158
159                         ldb_msg_remove_attr(msg, search_sub[i].replace);
160                 }
161         }
162
163         return 0;
164
165 failed:
166         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
167                       "operational_search_post_process failed for attribute '%s'\n", 
168                       attrs[a]);
169         return -1;
170 }
171
172 /*
173   add a time element to a record
174 */
175 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
176 {
177         struct ldb_message_element *el;
178         char *s;
179
180         if (ldb_msg_find_element(msg, attr) != NULL) {
181                 return 0;
182         }
183
184         s = ldb_timestring(msg, t);
185         if (s == NULL) {
186                 return -1;
187         }
188
189         if (ldb_msg_add_string(msg, attr, s) != 0) {
190                 return -1;
191         }
192
193         el = ldb_msg_find_element(msg, attr);
194         /* always set as replace. This works because on add ops, the flag
195            is ignored */
196         el->flags = LDB_FLAG_MOD_REPLACE;
197
198         return 0;
199 }
200
201 /*
202   add a uint64_t element to a record
203 */
204 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
205 {
206         struct ldb_message_element *el;
207
208         if (ldb_msg_find_element(msg, attr) != NULL) {
209                 return 0;
210         }
211
212         if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
213                 return -1;
214         }
215
216         el = ldb_msg_find_element(msg, attr);
217         /* always set as replace. This works because on add ops, the flag
218            is ignored */
219         el->flags = LDB_FLAG_MOD_REPLACE;
220
221         return 0;
222 }
223
224
225 /*
226   hook search operations
227 */
228
229 struct operational_async_context {
230
231         struct ldb_module *module;
232         void *up_context;
233         int (*up_callback)(struct ldb_context *, void *, struct ldb_async_result *);
234         int timeout;
235
236         const char * const *attrs;
237 };
238
239 static int operational_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
240 {
241         struct operational_async_context *ac;
242
243         if (!context || !ares) {
244                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
245                 goto error;
246         }
247
248         ac = talloc_get_type(context, struct operational_async_context);
249
250         if (ares->type == LDB_REPLY_ENTRY) {
251                 /* for each record returned post-process to add any derived
252                    attributes that have been asked for */
253                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
254                         goto error;
255                 }
256         }
257
258         return ac->up_callback(ldb, ac->up_context, ares);
259
260 error:
261         talloc_free(ares);
262         return LDB_ERR_OPERATIONS_ERROR;
263 }
264
265 static int operational_search(struct ldb_module *module, struct ldb_request *req)
266 {
267         struct operational_async_context *ac;
268         struct ldb_request *down_req;
269         const char **search_attrs = NULL;
270         int i, a, ret;
271
272         req->async.handle = NULL;
273
274         ac = talloc(req, struct operational_async_context);
275         if (ac == NULL) {
276                 return LDB_ERR_OPERATIONS_ERROR;
277         }
278
279         ac->module = module;
280         ac->up_context = req->async.context;
281         ac->up_callback = req->async.callback;
282         ac->timeout = req->async.timeout;
283         ac->attrs = req->op.search.attrs;
284
285         down_req = talloc_zero(req, struct ldb_request);
286         if (down_req == NULL) {
287                 return LDB_ERR_OPERATIONS_ERROR;
288         }
289
290         down_req->operation = req->operation;
291         down_req->op.search.base = req->op.search.base;
292         down_req->op.search.scope = req->op.search.scope;
293         down_req->op.search.tree = req->op.search.tree;
294
295         /*  FIXME: I hink we should copy the tree and keep the original
296          *  unmodified. SSS */
297         /* replace any attributes in the parse tree that are
298            searchable, but are stored using a different name in the
299            backend */
300         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
301                 ldb_parse_tree_attr_replace(req->op.search.tree, 
302                                             parse_tree_sub[i].attr, 
303                                             parse_tree_sub[i].replace);
304         }
305
306         /* in the list of attributes we are looking for, rename any
307            attributes to the alias for any hidden attributes that can
308            be fetched directly using non-hidden names */
309         for (a=0;ac->attrs && ac->attrs[a];a++) {
310                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
311                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
312                             search_sub[i].replace) {
313                                 if (!search_attrs) {
314                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
315                                         if (search_attrs == NULL) {
316                                                 return LDB_ERR_OPERATIONS_ERROR;
317                                         }
318                                 }
319                                 search_attrs[a] = search_sub[i].replace;
320                         }
321                 }
322         }
323         
324         /* use new set of attrs if any */
325         if (search_attrs) down_req->op.search.attrs = search_attrs;
326         else down_req->op.search.attrs = req->op.search.attrs;
327         
328         down_req->controls = req->controls;
329
330         down_req->async.context = ac;
331         down_req->async.callback = operational_async_callback;
332         down_req->async.timeout = req->async.timeout;
333
334         /* perform the search */
335         ret = ldb_next_request(module, down_req);
336
337         /* do not free down_req as the call results may be linked to it,
338          * it will be freed when the upper level request get freed */
339         if (ret == LDB_SUCCESS) {
340                 req->async.handle = down_req->async.handle;
341         }
342
343         return ret;
344 }
345
346 /*
347   hook add record ops
348 */
349 static int operational_add(struct ldb_module *module, struct ldb_request *req)
350 {
351         struct ldb_request *down_req;
352         struct ldb_message *msg;
353         time_t t = time(NULL);
354         int ret;
355
356         if (ldb_dn_is_special(req->op.add.message->dn)) {
357                 return ldb_next_request(module, req);
358         }
359
360         down_req = talloc(req, struct ldb_request);
361         if (down_req == NULL) {
362                 return LDB_ERR_OPERATIONS_ERROR;
363         }
364
365         *down_req = *req;
366
367         /* we have to copy the message as the caller might have it as a const */
368         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
369         if (msg == NULL) {
370                 return LDB_ERR_OPERATIONS_ERROR;
371         }
372         if (add_time_element(msg, "whenCreated", t) != 0 ||
373             add_time_element(msg, "whenChanged", t) != 0) {
374                 talloc_free(down_req);
375                 return LDB_ERR_OPERATIONS_ERROR;
376         }
377
378         /* see if the backend can give us the USN */
379         if (module->ldb->sequence_number != NULL) {
380                 uint64_t seq_num = module->ldb->sequence_number(module->ldb);
381                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
382                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
383                         talloc_free(down_req);
384                         return LDB_ERR_OPERATIONS_ERROR;
385                 }
386         }
387
388         /* go on with the call chain */
389         ret = ldb_next_request(module, down_req);
390
391         /* do not free down_req as the call results may be linked to it,
392          * it will be freed when the upper level request get freed */
393         if (ret == LDB_SUCCESS) {
394                 req->async.handle = down_req->async.handle;
395         }
396
397         return ret;
398 }
399
400 /*
401   hook modify record ops
402 */
403 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
404 {
405         struct ldb_request *down_req;
406         struct ldb_message *msg;
407         time_t t = time(NULL);
408         int ret;
409
410         if (ldb_dn_is_special(req->op.mod.message->dn)) {
411                 return ldb_next_request(module, req);
412         }
413
414         down_req = talloc(req, struct ldb_request);
415         if (down_req == NULL) {
416                 return LDB_ERR_OPERATIONS_ERROR;
417         }
418
419         *down_req = *req;
420
421         /* we have to copy the message as the caller might have it as a const */
422         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
423         if (msg == NULL) {
424                 return LDB_ERR_OPERATIONS_ERROR;
425         }
426         if (add_time_element(msg, "whenChanged", t) != 0) {
427                 talloc_free(down_req);
428                 return LDB_ERR_OPERATIONS_ERROR;
429         }
430
431         /* update the records USN if possible */
432         if (module->ldb->sequence_number != NULL &&
433             add_uint64_element(msg, "uSNChanged", 
434                                module->ldb->sequence_number(module->ldb)) != 0) {
435                 talloc_free(down_req);
436                 return -1;
437         }
438         
439         /* go on with the call chain */
440         ret = ldb_next_request(module, down_req);
441
442         /* do not free down_req as the call results may be linked to it,
443          * it will be freed when the upper level request get freed */
444         if (ret == LDB_SUCCESS) {
445                 req->async.handle = down_req->async.handle;
446         }
447
448         return ret;
449 }
450
451 static int operational_init(struct ldb_module *ctx)
452 {
453         /* setup some standard attribute handlers */
454         ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
455         ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
456         ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
457         ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
458
459         return ldb_next_init(ctx);
460 }
461
462 static const struct ldb_module_ops operational_ops = {
463         .name              = "operational",
464         .search            = operational_search,
465         .add               = operational_add,
466         .modify            = operational_modify,
467         .init_context      = operational_init
468 };
469
470 int ldb_operational_init(void)
471 {
472         return ldb_register_module(&operational_ops);
473 }