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?
78 #include "ldb/include/ldb.h"
79 #include "ldb/include/ldb_private.h"
83 a list of attribute names that should be substituted in the parse
84 tree before the search is done
89 } parse_tree_sub[] = {
90 { "createTimestamp", "whenCreated" },
91 { "modifyTimestamp", "whenChanged" }
95 a list of attribute names that are hidden, but can be searched for
96 using another (non-hidden) name to produce the correct result
102 { "createTimestamp", "whenCreated" },
103 { "modifyTimestamp", "whenChanged" },
104 { "structuralObjectClass", "objectClass" }
108 hook search operations
110 static int operational_search_bytree(struct ldb_module *module,
111 const struct ldb_dn *base,
112 enum ldb_scope scope, struct ldb_parse_tree *tree,
113 const char * const *attrs,
114 struct ldb_message ***res)
118 const char **search_attrs = NULL;
122 /* replace any attributes in the parse tree that are
123 searchable, but are stored using a different name in the
125 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
126 ldb_parse_tree_attr_replace(tree,
127 parse_tree_sub[i].attr,
128 parse_tree_sub[i].replace);
131 /* in the list of attributes we are looking for, rename any
132 attributes to the alias for any hidden attributes that can
133 be fetched directly using non-hidden names */
134 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
135 for (a=0;attrs && attrs[a];a++) {
136 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0) {
138 search_attrs = ldb_attr_list_copy(module, attrs);
139 if (search_attrs == NULL) {
143 search_attrs[a] = search_sub[i].replace;
149 /* perform the search */
150 ret = ldb_next_search_bytree(module, base, scope, tree,
151 search_attrs?search_attrs:attrs, res);
156 /* for each record returned see if we have added any
157 attributes to the search, and if we have then either copy
158 them (if the aliased name was also asked for) or rename
159 them (if the aliased entry was not asked for) */
160 for (r=0;r<ret;r++) {
161 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
162 for (a=0;attrs && attrs[a];a++) {
163 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
166 if (ldb_attr_in_list(attrs, search_sub[i].replace) ||
167 ldb_attr_in_list(attrs, "*")) {
168 if (ldb_msg_copy_attr((*res)[r],
169 search_sub[i].replace,
170 search_sub[i].attr) != 0) {
174 if (ldb_msg_rename_attr((*res)[r],
175 search_sub[i].replace,
176 search_sub[i].attr) != 0) {
185 talloc_free(search_attrs);
189 talloc_free(search_attrs);
191 ldb_oom(module->ldb);
196 add a time element to a record
198 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
200 struct ldb_message_element *el;
203 if (ldb_msg_find_element(msg, attr) != NULL) {
207 s = ldb_timestring(msg, t);
212 if (ldb_msg_add_string(msg, attr, s) != 0) {
216 el = ldb_msg_find_element(msg, attr);
217 /* always set as replace. This works because on add ops, the flag
219 el->flags = LDB_FLAG_MOD_REPLACE;
228 static int operational_add_record(struct ldb_module *module,
229 const struct ldb_message *msg)
231 time_t t = time(NULL);
232 struct ldb_message *msg2;
235 if (ldb_dn_is_special(msg->dn)) {
236 return ldb_next_add_record(module, msg);
239 /* we have to copy the message as the caller might have it as a const */
240 msg2 = ldb_msg_copy_shallow(module, msg);
244 if (add_time_element(msg2, "whenCreated", t) != 0 ||
245 add_time_element(msg2, "whenChanged", t) != 0) {
249 ret = ldb_next_add_record(module, msg2);
255 hook modify record ops
257 static int operational_modify_record(struct ldb_module *module,
258 const struct ldb_message *msg)
260 time_t t = time(NULL);
261 struct ldb_message *msg2;
264 if (ldb_dn_is_special(msg->dn)) {
265 return ldb_next_modify_record(module, msg);
268 /* we have to copy the message as the caller might have it as a const */
269 msg2 = ldb_msg_copy_shallow(module, msg);
273 if (add_time_element(msg2, "whenChanged", t) != 0) {
277 ret = ldb_next_modify_record(module, msg2);
282 static const struct ldb_module_ops operational_ops = {
283 .name = "operational",
284 .search_bytree = operational_search_bytree,
285 .add_record = operational_add_record,
286 .modify_record = operational_modify_record
290 /* the init function */
291 #ifdef HAVE_DLOPEN_DISABLED
292 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
294 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
297 struct ldb_module *ctx;
299 ctx = talloc(ldb, struct ldb_module);
303 ctx->private_data = NULL;
305 ctx->prev = ctx->next = NULL;
306 ctx->ops = &operational_ops;
308 /* setup some standard attribute handlers */
309 ldb_set_attrib_handler_syntax(ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
310 ldb_set_attrib_handler_syntax(ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
311 ldb_set_attrib_handler_syntax(ldb, "subschemaSubentry", LDB_SYNTAX_DN);
312 ldb_set_attrib_handler_syntax(ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);