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