4 Copyright (C) Andrew Tridgell 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
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.
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.
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
25 handle operational attributes
29 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
30 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
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
37 we also need to replace these with the whenCreated/whenChanged
38 equivalent in the search expression trees
40 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43 on init we need to setup attribute handlers for these so
44 comparisons are done correctly. The resolution is 1 second.
46 on add we need to add both the above, for current time
48 on modify we need to change whenChanged
51 subschemaSubentry: HIDDEN, not-searchable,
52 points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
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
59 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61 for this one we do the search as normal, then if requested ask
62 for objectclass, change the attribute name, and add it
64 allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
65 list of attributes that can be modified - requires schema lookup
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?
77 #include "ldb/include/includes.h"
80 construct a canonical name from a message
82 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
85 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
86 if (canonicalName == NULL) {
89 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
93 a list of attribute names that should be substituted in the parse
94 tree before the search is done
99 } parse_tree_sub[] = {
100 { "createTimestamp", "whenCreated" },
101 { "modifyTimestamp", "whenChanged" }
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
109 static const struct {
112 int (*constructor)(struct ldb_module *, struct ldb_message *);
114 { "createTimestamp", "whenCreated", NULL },
115 { "modifyTimestamp", "whenChanged", NULL },
116 { "structuralObjectClass", "objectClass", NULL },
117 { "canonicalName", "distinguishedName", construct_canonical_name }
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
126 static int operational_search_post_process(struct ldb_module *module,
127 struct ldb_message *msg,
128 const char * const *attrs)
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) {
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) {
144 } else if (ldb_msg_copy_attr(msg,
145 search_sub[i].replace,
146 search_sub[i].attr) != 0) {
150 /* remove the added search attribute, unless it was asked for
152 if (search_sub[i].replace == NULL ||
153 ldb_attr_in_list(attrs, search_sub[i].replace) ||
154 ldb_attr_in_list(attrs, "*")) {
158 ldb_msg_remove_attr(msg, search_sub[i].replace);
165 ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
166 "operational_search_post_process failed for attribute '%s'\n",
172 hook search operations
174 static int operational_search_bytree(struct ldb_module *module, struct ldb_request *req)
178 const char * const *attrs = req->op.search.attrs;
179 const char **search_attrs = NULL;
181 req->op.search.res = NULL;
183 /* replace any attributes in the parse tree that are
184 searchable, but are stored using a different name in the
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);
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) {
200 search_attrs = ldb_attr_list_copy(req, attrs);
201 if (search_attrs == NULL) {
205 search_attrs[a] = search_sub[i].replace;
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;
217 /* check operation result */
218 if (ret != LDB_SUCCESS) {
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) {
231 talloc_free(search_attrs);
235 talloc_free(search_attrs);
236 talloc_free(req->op.search.res);
237 ldb_oom(module->ldb);
238 return LDB_ERR_OTHER;
242 add a time element to a record
244 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
246 struct ldb_message_element *el;
249 if (ldb_msg_find_element(msg, attr) != NULL) {
253 s = ldb_timestring(msg, t);
258 if (ldb_msg_add_string(msg, attr, s) != 0) {
262 el = ldb_msg_find_element(msg, attr);
263 /* always set as replace. This works because on add ops, the flag
265 el->flags = LDB_FLAG_MOD_REPLACE;
271 add a uint64_t element to a record
273 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
275 struct ldb_message_element *el;
277 if (ldb_msg_find_element(msg, attr) != NULL) {
281 if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
285 el = ldb_msg_find_element(msg, attr);
286 /* always set as replace. This works because on add ops, the flag
288 el->flags = LDB_FLAG_MOD_REPLACE;
297 static int operational_add(struct ldb_module *module, struct ldb_request *req)
299 const struct ldb_message *msg = req->op.add.message;
300 time_t t = time(NULL);
301 struct ldb_message *msg2;
304 if (ldb_dn_is_special(msg->dn)) {
305 return ldb_next_request(module, req);
308 /* we have to copy the message as the caller might have it as a const */
309 msg2 = ldb_msg_copy_shallow(module, msg);
313 if (add_time_element(msg2, "whenCreated", t) != 0 ||
314 add_time_element(msg2, "whenChanged", t) != 0) {
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) {
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 */
341 hook modify record ops
343 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
345 const struct ldb_message *msg = req->op.mod.message;
346 time_t t = time(NULL);
347 struct ldb_message *msg2;
350 if (ldb_dn_is_special(msg->dn)) {
351 return ldb_next_request(module, req);
354 /* we have to copy the message as the caller might have it as a const */
355 msg2 = ldb_msg_copy_shallow(module, msg);
359 if (add_time_element(msg2, "whenChanged", t) != 0) {
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) {
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 */
384 static int operational_request(struct ldb_module *module, struct ldb_request *req)
386 switch (req->operation) {
389 return operational_search_bytree(module, req);
392 return operational_add(module, req);
395 return operational_modify(module, req);
398 return ldb_next_request(module, req);
403 static int operational_init(struct ldb_module *ctx)
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);
411 return ldb_next_init(ctx);
414 static const struct ldb_module_ops operational_ops = {
415 .name = "operational",
416 .request = operational_request,
417 .init_context = operational_init
420 int ldb_operational_init(void)
422 return ldb_register_module(&operational_ops);