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 attributeTypes: in schema only
65 objectClasses: in schema only
66 matchingRules: in schema only
67 matchingRuleUse: in schema only
68 creatorsName: not supported by w2k3?
69 modifiersName: not supported by w2k3?
74 #include "ldb/include/ldb.h"
75 #include "ldb/include/ldb_private.h"
79 a list of attribute names that should be substituted in the parse
80 tree before the search is done
85 } parse_tree_sub[] = {
86 { "createTimestamp", "whenCreated" },
87 { "modifyTimestamp", "whenChanged" }
91 a list of attribute names that are hidden, but can be searched for
92 using another (non-hidden) name to produce the correct result
98 { "createTimestamp", "whenCreated" },
99 { "modifyTimestamp", "whenChanged" },
100 { "structuralObjectClass", "objectClass" }
104 hook search operations
106 static int operational_search_bytree(struct ldb_module *module,
107 const struct ldb_dn *base,
108 enum ldb_scope scope, struct ldb_parse_tree *tree,
109 const char * const *attrs,
110 struct ldb_message ***res)
114 const char **search_attrs = NULL;
116 /* replace any attributes in the parse tree that are
117 searchable, but are stored using a different name in the
119 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
120 ldb_parse_tree_attr_replace(tree,
121 parse_tree_sub[i].attr,
122 parse_tree_sub[i].replace);
125 /* in the list of attributes we are looking for, rename any
126 attributes to the alias for any hidden attributes that can
127 be fetched directly using non-hidden names */
128 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
129 for (a=0;attrs && attrs[a];a++) {
130 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0) {
132 search_attrs = ldb_attr_list_copy(module, attrs);
133 if (search_attrs == NULL) {
137 search_attrs[a] = search_sub[i].replace;
143 /* perform the search */
144 ret = ldb_next_search_bytree(module, base, scope, tree,
145 search_attrs?search_attrs:attrs, res);
150 /* for each record returned see if we have added any
151 attributes to the search, and if we have then either copy
152 them (if the aliased name was also asked for) or rename
153 them (if the aliased entry was not asked for) */
154 for (r=0;r<ret;r++) {
155 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
156 for (a=0;attrs && attrs[a];a++) {
157 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
160 if (ldb_attr_in_list(attrs, search_sub[i].replace) ||
161 ldb_attr_in_list(attrs, "*")) {
162 if (ldb_msg_copy_attr((*res)[r],
163 search_sub[i].replace,
164 search_sub[i].attr) != 0) {
168 ldb_msg_rename_attr((*res)[r],
169 search_sub[i].replace,
177 talloc_free(search_attrs);
181 talloc_free(search_attrs);
182 ldb_oom(module->ldb);
187 add a time element to a record
189 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
191 struct ldb_message_element *el;
194 if (ldb_msg_find_element(msg, attr) != NULL) {
198 s = ldb_timestring(msg, t);
203 if (ldb_msg_add_string(msg, attr, s) != 0) {
207 el = ldb_msg_find_element(msg, attr);
208 /* always set as replace. This works because on add ops, the flag
210 el->flags = LDB_FLAG_MOD_REPLACE;
219 static int operational_add_record(struct ldb_module *module,
220 const struct ldb_message *msg)
222 time_t t = time(NULL);
223 struct ldb_message *msg2;
226 if (ldb_dn_is_special(msg->dn)) {
227 return ldb_next_add_record(module, msg);
230 /* we have to copy the message as the caller might have it as a const */
231 msg2 = ldb_msg_copy_shallow(module, msg);
235 if (add_time_element(msg2, "whenCreated", t) != 0 ||
236 add_time_element(msg2, "whenChanged", t) != 0) {
240 ret = ldb_next_add_record(module, msg2);
246 hook modify record ops
248 static int operational_modify_record(struct ldb_module *module,
249 const struct ldb_message *msg)
251 time_t t = time(NULL);
252 struct ldb_message *msg2;
255 if (ldb_dn_is_special(msg->dn)) {
256 return ldb_next_modify_record(module, msg);
259 /* we have to copy the message as the caller might have it as a const */
260 msg2 = ldb_msg_copy_shallow(module, msg);
264 if (add_time_element(msg2, "whenChanged", t) != 0) {
268 ret = ldb_next_modify_record(module, msg2);
273 static const struct ldb_module_ops operational_ops = {
274 .name = "operational",
275 .search_bytree = operational_search_bytree,
276 .add_record = operational_add_record,
277 .modify_record = operational_modify_record
281 /* the init function */
282 #ifdef HAVE_DLOPEN_DISABLED
283 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
285 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
288 struct ldb_module *ctx;
290 ctx = talloc(ldb, struct ldb_module);
294 ctx->private_data = NULL;
296 ctx->prev = ctx->next = NULL;
297 ctx->ops = &operational_ops;
299 /* setup some standard attribute handlers */
300 ldb_set_attrib_handler_syntax(ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
301 ldb_set_attrib_handler_syntax(ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
302 ldb_set_attrib_handler_syntax(ldb, "subschemaSubentry", LDB_SYNTAX_DN);
303 ldb_set_attrib_handler_syntax(ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);