r12658: Couple of fixes related to shared module builds.
[samba.git] / source4 / 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
77 #include "includes.h"
78 #include "ldb/include/ldb.h"
79 #include "ldb/include/ldb_errors.h"
80 #include "ldb/include/ldb_private.h"
81 #include <time.h>
82
83 /*
84   construct a canonical name from a message
85 */
86 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
87 {
88         char *canonicalName;
89         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
90         if (canonicalName == NULL) {
91                 return -1;
92         }
93         return ldb_msg_add_string(msg, "canonicalName", canonicalName);
94 }
95
96 /*
97   a list of attribute names that should be substituted in the parse
98   tree before the search is done
99 */
100 static const struct {
101         const char *attr;
102         const char *replace;
103 } parse_tree_sub[] = {
104         { "createTimestamp", "whenCreated" },
105         { "modifyTimestamp", "whenChanged" }
106 };
107
108
109 /*
110   a list of attribute names that are hidden, but can be searched for
111   using another (non-hidden) name to produce the correct result
112 */
113 static const struct {
114         const char *attr;
115         const char *replace;
116         int (*constructor)(struct ldb_module *, struct ldb_message *);
117 } search_sub[] = {
118         { "createTimestamp", "whenCreated", NULL },
119         { "modifyTimestamp", "whenChanged", NULL },
120         { "structuralObjectClass", "objectClass", NULL },
121         { "canonicalName", "distinguishedName", construct_canonical_name }
122 };
123
124 /*
125   post process a search result record. For any search_sub[] attributes that were
126   asked for, we need to call the appropriate copy routine to copy the result
127   into the message, then remove any attributes that we added to the search but were
128   not asked for by the user
129 */
130 static int operational_search_post_process(struct ldb_module *module,
131                                            struct ldb_message *msg, 
132                                            const char * const *attrs)
133 {
134         int i, a=0;
135
136         for (a=0;attrs && attrs[a];a++) {
137                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
138                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
139                                 continue;
140                         }
141
142                         /* construct the new attribute, using either a supplied 
143                            constructor or a simple copy */
144                         if (search_sub[i].constructor) {
145                                 if (search_sub[i].constructor(module, msg) != 0) {
146                                         goto failed;
147                                 }
148                         } else if (ldb_msg_copy_attr(msg,
149                                                      search_sub[i].replace,
150                                                      search_sub[i].attr) != 0) {
151                                 goto failed;
152                         }
153
154                         /* remove the added search attribute, unless it was asked for 
155                            by the user */
156                         if (search_sub[i].replace == NULL ||
157                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
158                             ldb_attr_in_list(attrs, "*")) {
159                                 continue;
160                         }
161
162                         ldb_msg_remove_attr(msg, search_sub[i].replace);
163                 }
164         }
165
166         return 0;
167
168 failed:
169         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
170                       "operational_search_post_process failed for attribute '%s'\n", 
171                       attrs[a]);
172         return -1;
173 }
174
175 /*
176   hook search operations
177 */
178 static int operational_search_bytree(struct ldb_module *module, struct ldb_request *req)
179 {
180         int i, r, a;
181         int ret;
182         const char * const *attrs = req->op.search.attrs;
183         const char **search_attrs = NULL;
184
185         req->op.search.res = NULL;
186
187         /* replace any attributes in the parse tree that are
188            searchable, but are stored using a different name in the
189            backend */
190         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
191                 ldb_parse_tree_attr_replace(req->op.search.tree, 
192                                             parse_tree_sub[i].attr, 
193                                             parse_tree_sub[i].replace);
194         }
195
196         /* in the list of attributes we are looking for, rename any
197            attributes to the alias for any hidden attributes that can
198            be fetched directly using non-hidden names */
199         for (a=0;attrs && attrs[a];a++) {
200                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
201                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0 &&
202                             search_sub[i].replace) {
203                                 if (!search_attrs) {
204                                         search_attrs = ldb_attr_list_copy(req, attrs);
205                                         if (search_attrs == NULL) {
206                                                 goto failed;
207                                         }
208                                 }
209                                 search_attrs[a] = search_sub[i].replace;
210                         }
211                 }
212         }
213         
214         /* use new set of attrs if any */
215         if (search_attrs) req->op.search.attrs = search_attrs;
216         /* perform the search */
217         ret = ldb_next_request(module, req);
218         /* set back saved attrs if needed */
219         if (search_attrs) req->op.search.attrs = attrs;
220
221         /* check operation result */
222         if (ret != LDB_SUCCESS) {
223                 return ret;
224         }
225
226         /* for each record returned post-process to add any derived
227            attributes that have been asked for */
228         for (r = 0; r < req->op.search.res->count; r++) {
229                 if (operational_search_post_process(module, req->op.search.res->msgs[r], attrs) != 0) {
230                         goto failed;
231                 }
232         }
233
234         /* all done */
235         talloc_free(search_attrs);
236         return ret;
237
238 failed:
239         talloc_free(search_attrs);
240         talloc_free(req->op.search.res);
241         ldb_oom(module->ldb);
242         return LDB_ERR_OTHER;
243 }
244
245 /*
246   add a time element to a record
247 */
248 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
249 {
250         struct ldb_message_element *el;
251         char *s;
252
253         if (ldb_msg_find_element(msg, attr) != NULL) {
254                 return 0;
255         }
256
257         s = ldb_timestring(msg, t);
258         if (s == NULL) {
259                 return -1;
260         }
261
262         if (ldb_msg_add_string(msg, attr, s) != 0) {
263                 return -1;
264         }
265
266         el = ldb_msg_find_element(msg, attr);
267         /* always set as replace. This works because on add ops, the flag
268            is ignored */
269         el->flags = LDB_FLAG_MOD_REPLACE;
270
271         return 0;
272 }
273
274
275 /*
276   hook add record ops
277 */
278 static int operational_add(struct ldb_module *module, struct ldb_request *req)
279 {
280         const struct ldb_message *msg = req->op.add.message;
281         time_t t = time(NULL);
282         struct ldb_message *msg2;
283         int ret;
284
285         if (ldb_dn_is_special(msg->dn)) {
286                 return ldb_next_request(module, req);
287         }
288
289         /* we have to copy the message as the caller might have it as a const */
290         msg2 = ldb_msg_copy_shallow(module, msg);
291         if (msg2 == NULL) {
292                 return -1;
293         }
294         if (add_time_element(msg2, "whenCreated", t) != 0 ||
295             add_time_element(msg2, "whenChanged", t) != 0) {
296                 talloc_free(msg2);
297                 return -1;
298         }
299         /* use the new structure for the call chain below this point */
300         req->op.add.message = msg2;
301         /* go on with the call chain */
302         ret = ldb_next_request(module, req);
303         /* put back saved message */
304         req->op.add.message = msg;
305         /* free temproary compy */
306         talloc_free(msg2);
307         return ret;
308 }
309
310 /*
311   hook modify record ops
312 */
313 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
314 {
315         const struct ldb_message *msg = req->op.mod.message;
316         time_t t = time(NULL);
317         struct ldb_message *msg2;
318         int ret;
319
320         if (ldb_dn_is_special(msg->dn)) {
321                 return ldb_next_request(module, req);
322         }
323
324         /* we have to copy the message as the caller might have it as a const */
325         msg2 = ldb_msg_copy_shallow(module, msg);
326         if (msg2 == NULL) {
327                 return -1;
328         }
329         if (add_time_element(msg2, "whenChanged", t) != 0) {
330                 talloc_free(msg2);
331                 return -1;
332         }
333         /* use the new structure for the call chain below this point */
334         req->op.mod.message = msg2;
335         /* go on with the call chain */
336         ret = ldb_next_request(module, req);
337         /* put back saved message */
338         req->op.mod.message = msg;
339         /* free temproary compy */
340         talloc_free(msg2);
341         return ret;
342 }
343
344
345 static int operational_request(struct ldb_module *module, struct ldb_request *req)
346 {
347         switch (req->operation) {
348
349         case LDB_REQ_SEARCH:
350                 return operational_search_bytree(module, req);
351
352         case LDB_REQ_ADD:
353                 return operational_add(module, req);
354
355         case LDB_REQ_MODIFY:
356                 return operational_modify(module, req);
357
358         default:
359                 return ldb_next_request(module, req);
360
361         }
362 }
363
364 static const struct ldb_module_ops operational_ops = {
365         .name              = "operational",
366         .request           = operational_request
367 };
368
369
370 /* the init function */
371 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
372 {
373         struct ldb_module *ctx;
374
375         ctx = talloc(ldb, struct ldb_module);
376         if (!ctx)
377                 return NULL;
378
379         ctx->private_data = NULL;
380         ctx->ldb = ldb;
381         ctx->prev = ctx->next = NULL;
382         ctx->ops = &operational_ops;
383
384         /* setup some standard attribute handlers */
385         ldb_set_attrib_handler_syntax(ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
386         ldb_set_attrib_handler_syntax(ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
387         ldb_set_attrib_handler_syntax(ldb, "subschemaSubentry", LDB_SYNTAX_DN);
388         ldb_set_attrib_handler_syntax(ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
389
390         return ctx;
391 }