r14364: operational -> async (untested)
[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   hook search operations
174 */
175 static int operational_search_bytree(struct ldb_module *module, struct ldb_request *req)
176 {
177         int i, r, a;
178         int ret;
179         const char * const *attrs = req->op.search.attrs;
180         const char **search_attrs = NULL;
181
182         req->op.search.res = NULL;
183
184         /* replace any attributes in the parse tree that are
185            searchable, but are stored using a different name in the
186            backend */
187         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
188                 ldb_parse_tree_attr_replace(req->op.search.tree, 
189                                             parse_tree_sub[i].attr, 
190                                             parse_tree_sub[i].replace);
191         }
192
193         /* in the list of attributes we are looking for, rename any
194            attributes to the alias for any hidden attributes that can
195            be fetched directly using non-hidden names */
196         for (a=0;attrs && attrs[a];a++) {
197                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
198                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0 &&
199                             search_sub[i].replace) {
200                                 if (!search_attrs) {
201                                         search_attrs = ldb_attr_list_copy(req, attrs);
202                                         if (search_attrs == NULL) {
203                                                 goto failed;
204                                         }
205                                 }
206                                 search_attrs[a] = search_sub[i].replace;
207                         }
208                 }
209         }
210         
211         /* use new set of attrs if any */
212         if (search_attrs) req->op.search.attrs = search_attrs;
213         /* perform the search */
214         ret = ldb_next_request(module, req);
215         /* set back saved attrs if needed */
216         if (search_attrs) req->op.search.attrs = attrs;
217
218         /* check operation result */
219         if (ret != LDB_SUCCESS) {
220                 return ret;
221         }
222
223         /* for each record returned post-process to add any derived
224            attributes that have been asked for */
225         for (r = 0; r < req->op.search.res->count; r++) {
226                 if (operational_search_post_process(module, req->op.search.res->msgs[r], attrs) != 0) {
227                         goto failed;
228                 }
229         }
230
231         /* all done */
232         talloc_free(search_attrs);
233         return ret;
234
235 failed:
236         talloc_free(search_attrs);
237         talloc_free(req->op.search.res);
238         ldb_oom(module->ldb);
239         return LDB_ERR_OTHER;
240 }
241
242 /*
243   add a time element to a record
244 */
245 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
246 {
247         struct ldb_message_element *el;
248         char *s;
249
250         if (ldb_msg_find_element(msg, attr) != NULL) {
251                 return 0;
252         }
253
254         s = ldb_timestring(msg, t);
255         if (s == NULL) {
256                 return -1;
257         }
258
259         if (ldb_msg_add_string(msg, attr, s) != 0) {
260                 return -1;
261         }
262
263         el = ldb_msg_find_element(msg, attr);
264         /* always set as replace. This works because on add ops, the flag
265            is ignored */
266         el->flags = LDB_FLAG_MOD_REPLACE;
267
268         return 0;
269 }
270
271 /*
272   add a uint64_t element to a record
273 */
274 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
275 {
276         struct ldb_message_element *el;
277
278         if (ldb_msg_find_element(msg, attr) != NULL) {
279                 return 0;
280         }
281
282         if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
283                 return -1;
284         }
285
286         el = ldb_msg_find_element(msg, attr);
287         /* always set as replace. This works because on add ops, the flag
288            is ignored */
289         el->flags = LDB_FLAG_MOD_REPLACE;
290
291         return 0;
292 }
293
294
295 /*
296   hook add record ops
297 */
298 static int operational_add(struct ldb_module *module, struct ldb_request *req)
299 {
300         const struct ldb_message *msg = req->op.add.message;
301         time_t t = time(NULL);
302         struct ldb_message *msg2;
303         int ret;
304
305         if (ldb_dn_is_special(msg->dn)) {
306                 return ldb_next_request(module, req);
307         }
308
309         /* we have to copy the message as the caller might have it as a const */
310         msg2 = ldb_msg_copy_shallow(module, msg);
311         if (msg2 == NULL) {
312                 return -1;
313         }
314         if (add_time_element(msg2, "whenCreated", t) != 0 ||
315             add_time_element(msg2, "whenChanged", t) != 0) {
316                 talloc_free(msg2);
317                 return -1;
318         }
319
320         /* see if the backend can give us the USN */
321         if (module->ldb->sequence_number != NULL) {
322                 uint64_t seq_num = module->ldb->sequence_number(module->ldb);
323                 if (add_uint64_element(msg2, "uSNCreated", seq_num) != 0 ||
324                     add_uint64_element(msg2, "uSNChanged", seq_num) != 0) {
325                         talloc_free(msg2);
326                         return -1;
327                 }
328         }
329
330         /* use the new structure for the call chain below this point */
331         req->op.add.message = msg2;
332         /* go on with the call chain */
333         ret = ldb_next_request(module, req);
334         /* put back saved message */
335         req->op.add.message = msg;
336         /* free temproary compy */
337         talloc_free(msg2);
338         return ret;
339 }
340
341 /*
342   hook modify record ops
343 */
344 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
345 {
346         const struct ldb_message *msg = req->op.mod.message;
347         time_t t = time(NULL);
348         struct ldb_message *msg2;
349         int ret;
350
351         if (ldb_dn_is_special(msg->dn)) {
352                 return ldb_next_request(module, req);
353         }
354
355         /* we have to copy the message as the caller might have it as a const */
356         msg2 = ldb_msg_copy_shallow(module, msg);
357         if (msg2 == NULL) {
358                 return -1;
359         }
360         if (add_time_element(msg2, "whenChanged", t) != 0) {
361                 talloc_free(msg2);
362                 return -1;
363         }
364
365         /* update the records USN if possible */
366         if (module->ldb->sequence_number != NULL &&
367             add_uint64_element(msg2, "uSNChanged", 
368                                module->ldb->sequence_number(module->ldb)) != 0) {
369                 talloc_free(msg2);
370                 return -1;
371         }
372
373         /* use the new structure for the call chain below this point */
374         req->op.mod.message = msg2;
375         /* go on with the call chain */
376         ret = ldb_next_request(module, req);
377         /* put back saved message */
378         req->op.mod.message = msg;
379         /* free temproary compy */
380         talloc_free(msg2);
381         return ret;
382 }
383
384 /*
385   hook search operations
386 */
387
388 struct operational_async_context {
389
390         struct ldb_module *module;
391         void *up_context;
392         int (*up_callback)(struct ldb_context *, void *, struct ldb_async_result *);
393         int timeout;
394
395         const char * const *attrs;
396 };
397
398 static int operational_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares) {
399
400         struct operational_async_context *ac;
401
402         if (!context || !ares) {
403                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
404                 goto error;
405         }
406
407         ac = talloc_get_type(context, struct operational_async_context);
408
409         if (ares->type == LDB_REPLY_ENTRY) {
410                 /* for each record returned post-process to add any derived
411                    attributes that have been asked for */
412                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
413                         goto error;
414                 }
415         }
416
417         return ac->up_callback(ldb, ac->up_context, ares);
418
419 error:
420         talloc_free(ares);
421         return LDB_ERR_OPERATIONS_ERROR;
422 }
423
424 static int operational_search_async(struct ldb_module *module, struct ldb_request *req)
425 {
426         struct operational_async_context *ac;
427         struct ldb_request *down_req;
428         const char **search_attrs = NULL;
429         int i, a, ret;
430
431         req->async.handle = NULL;
432
433         ac = talloc(req, struct operational_async_context);
434         if (ac == NULL) {
435                 return LDB_ERR_OPERATIONS_ERROR;
436         }
437
438         ac->module = module;
439         ac->up_context = req->async.context;
440         ac->up_callback = req->async.callback;
441         ac->timeout = req->async.timeout;
442         ac->attrs = req->op.search.attrs;
443
444         down_req = talloc_zero(req, struct ldb_request);
445         if (down_req == NULL) {
446                 return LDB_ERR_OPERATIONS_ERROR;
447         }
448
449         down_req->operation = req->operation;
450         down_req->op.search.base = req->op.search.base;
451         down_req->op.search.scope = req->op.search.scope;
452         down_req->op.search.tree = req->op.search.tree;
453
454         /*  FIXME: I hink we should copy the tree and keep the original
455          *  unmodified. SSS */
456         /* replace any attributes in the parse tree that are
457            searchable, but are stored using a different name in the
458            backend */
459         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
460                 ldb_parse_tree_attr_replace(req->op.search.tree, 
461                                             parse_tree_sub[i].attr, 
462                                             parse_tree_sub[i].replace);
463         }
464
465         /* in the list of attributes we are looking for, rename any
466            attributes to the alias for any hidden attributes that can
467            be fetched directly using non-hidden names */
468         for (a=0;ac->attrs && ac->attrs[a];a++) {
469                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
470                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
471                             search_sub[i].replace) {
472                                 if (!search_attrs) {
473                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
474                                         if (search_attrs == NULL) {
475                                                 return LDB_ERR_OPERATIONS_ERROR;
476                                         }
477                                 }
478                                 search_attrs[a] = search_sub[i].replace;
479                         }
480                 }
481         }
482         
483         /* use new set of attrs if any */
484         if (search_attrs) down_req->op.search.attrs = search_attrs;
485         else down_req->op.search.attrs = req->op.search.attrs;
486         
487         down_req->controls = req->controls;
488         down_req->creds = req->creds;
489
490         down_req->async.context = ac;
491         down_req->async.callback = operational_async_callback;
492         down_req->async.timeout = req->async.timeout;
493
494         /* perform the search */
495         ret = ldb_next_request(module, down_req);
496
497         /* do not free down_req as the call results may be linked to it,
498          * it will be freed when the upper level request get freed */
499         if (ret == LDB_SUCCESS) {
500                 req->async.handle = down_req->async.handle;
501         }
502
503         return ret;
504 }
505
506 /*
507   hook add record ops
508 */
509 static int operational_add_async(struct ldb_module *module, struct ldb_request *req)
510 {
511         struct ldb_request *down_req;
512         struct ldb_message *msg;
513         time_t t = time(NULL);
514         int ret;
515
516         if (ldb_dn_is_special(req->op.add.message->dn)) {
517                 return ldb_next_request(module, req);
518         }
519
520         down_req = talloc(req, struct ldb_request);
521         if (down_req == NULL) {
522                 return LDB_ERR_OPERATIONS_ERROR;
523         }
524
525         /* we have to copy the message as the caller might have it as a const */
526         msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
527         if (msg == NULL) {
528                 return LDB_ERR_OPERATIONS_ERROR;
529         }
530         if (add_time_element(msg, "whenCreated", t) != 0 ||
531             add_time_element(msg, "whenChanged", t) != 0) {
532                 talloc_free(down_req);
533                 return LDB_ERR_OPERATIONS_ERROR;
534         }
535
536         /* see if the backend can give us the USN */
537         if (module->ldb->sequence_number != NULL) {
538                 uint64_t seq_num = module->ldb->sequence_number(module->ldb);
539                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
540                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
541                         talloc_free(down_req);
542                         return LDB_ERR_OPERATIONS_ERROR;
543                 }
544         }
545
546         down_req->op.add.message = msg;
547         
548         down_req->controls = req->controls;
549         down_req->creds = req->creds;
550
551         down_req->async.context = req->async.context;
552         down_req->async.callback = req->async.callback;
553         down_req->async.timeout = req->async.timeout;
554
555         /* go on with the call chain */
556         ret = ldb_next_request(module, down_req);
557
558         /* do not free down_req as the call results may be linked to it,
559          * it will be freed when the upper level request get freed */
560         if (ret == LDB_SUCCESS) {
561                 req->async.handle = down_req->async.handle;
562         }
563
564         return ret;
565 }
566
567 /*
568   hook modify record ops
569 */
570 static int operational_modify_async(struct ldb_module *module, struct ldb_request *req)
571 {
572         struct ldb_request *down_req;
573         struct ldb_message *msg;
574         time_t t = time(NULL);
575         int ret;
576
577         if (ldb_dn_is_special(req->op.mod.message->dn)) {
578                 return ldb_next_request(module, req);
579         }
580
581         down_req = talloc(req, struct ldb_request);
582         if (down_req == NULL) {
583                 return LDB_ERR_OPERATIONS_ERROR;
584         }
585
586         /* we have to copy the message as the caller might have it as a const */
587         msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
588         if (msg == NULL) {
589                 return LDB_ERR_OPERATIONS_ERROR;
590         }
591         if (add_time_element(msg, "whenChanged", t) != 0) {
592                 talloc_free(down_req);
593                 return LDB_ERR_OPERATIONS_ERROR;
594         }
595
596         /* update the records USN if possible */
597         if (module->ldb->sequence_number != NULL &&
598             add_uint64_element(msg, "uSNChanged", 
599                                module->ldb->sequence_number(module->ldb)) != 0) {
600                 talloc_free(down_req);
601                 return -1;
602         }
603
604         down_req->op.mod.message = msg;
605         
606         down_req->controls = req->controls;
607         down_req->creds = req->creds;
608
609         down_req->async.context = req->async.context;
610         down_req->async.callback = req->async.callback;
611         down_req->async.timeout = req->async.timeout;
612
613         /* go on with the call chain */
614         ret = ldb_next_request(module, down_req);
615
616         /* do not free down_req as the call results may be linked to it,
617          * it will be freed when the upper level request get freed */
618         if (ret == LDB_SUCCESS) {
619                 req->async.handle = down_req->async.handle;
620         }
621
622         return ret;
623 }
624
625
626 static int operational_request(struct ldb_module *module, struct ldb_request *req)
627 {
628         switch (req->operation) {
629
630         case LDB_REQ_SEARCH:
631                 return operational_search_bytree(module, req);
632
633         case LDB_REQ_ADD:
634                 return operational_add(module, req);
635
636         case LDB_REQ_MODIFY:
637                 return operational_modify(module, req);
638
639         case LDB_ASYNC_SEARCH:
640                 return operational_search_async(module, req);
641
642         case LDB_ASYNC_ADD:
643                 return operational_add_async(module, req);
644
645         case LDB_ASYNC_MODIFY:
646                 return operational_modify_async(module, req);
647
648         default:
649                 return ldb_next_request(module, req);
650
651         }
652 }
653
654 static int operational_init(struct ldb_module *ctx)
655 {
656         /* setup some standard attribute handlers */
657         ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
658         ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
659         ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
660         ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
661
662         return ldb_next_init(ctx);
663 }
664
665 static const struct ldb_module_ops operational_ops = {
666         .name              = "operational",
667         .request           = operational_request,
668         .init_context      = operational_init
669 };
670
671 int ldb_operational_init(void)
672 {
673         return ldb_register_module(&operational_ops);
674 }