r10916: - finished the 'operational' ldb module
[metze/samba/wip.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   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?
70 */
71
72
73 #include "includes.h"
74 #include "ldb/include/ldb.h"
75 #include "ldb/include/ldb_private.h"
76 #include <time.h>
77
78 /*
79   a list of attribute names that should be substituted in the parse
80   tree before the search is done
81 */
82 static const struct {
83         const char *attr;
84         const char *replace;
85 } parse_tree_sub[] = {
86         { "createTimestamp", "whenCreated" },
87         { "modifyTimestamp", "whenChanged" }
88 };
89
90 /*
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
93 */
94 static const struct {
95         const char *attr;
96         const char *replace;
97 } search_sub[] = {
98         { "createTimestamp", "whenCreated" },
99         { "modifyTimestamp", "whenChanged" },
100         { "structuralObjectClass", "objectClass" }
101 };
102
103 /*
104   hook search operations
105 */
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)
111 {
112         int i, r, a;
113         int ret;
114         const char **search_attrs = NULL;
115
116         /* replace any attributes in the parse tree that are
117            searchable, but are stored using a different name in the
118            backend */
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);
123         }
124
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) {
131                                 if (!search_attrs) {
132                                         search_attrs = ldb_attr_list_copy(module, attrs);
133                                         if (search_attrs == NULL) {
134                                                 goto oom;
135                                         }
136                                 }
137                                 search_attrs[a] = search_sub[i].replace;
138                         }
139                 }
140         }
141         
142
143         /* perform the search */
144         ret = ldb_next_search_bytree(module, base, scope, tree, 
145                                      search_attrs?search_attrs:attrs, res);
146         if (ret <= 0) {
147                 return ret;
148         }
149
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) {
158                                         continue;
159                                 }
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) {
165                                                 goto oom;
166                                         }
167                                 } else {
168                                         ldb_msg_rename_attr((*res)[r], 
169                                                             search_sub[i].replace,
170                                                             search_sub[i].attr);
171                                 }
172                         }
173                 }
174         }
175
176         /* all done */
177         talloc_free(search_attrs);
178         return ret;
179
180 oom:
181         talloc_free(search_attrs);
182         ldb_oom(module->ldb);
183         return -1;
184 }
185
186 /*
187   add a time element to a record
188 */
189 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
190 {
191         struct ldb_message_element *el;
192         char *s;
193
194         if (ldb_msg_find_element(msg, attr) != NULL) {
195                 return 0;
196         }
197
198         s = ldb_timestring(msg, t);
199         if (s == NULL) {
200                 return -1;
201         }
202
203         if (ldb_msg_add_string(msg, attr, s) != 0) {
204                 return -1;
205         }
206
207         el = ldb_msg_find_element(msg, attr);
208         /* always set as replace. This works because on add ops, the flag
209            is ignored */
210         el->flags = LDB_FLAG_MOD_REPLACE;
211
212         return 0;
213 }
214
215
216 /*
217   hook add record ops
218 */
219 static int operational_add_record(struct ldb_module *module, 
220                                   const struct ldb_message *msg)
221 {
222         time_t t = time(NULL);
223         struct ldb_message *msg2;
224         int ret;
225
226         if (ldb_dn_is_special(msg->dn)) {
227                 return ldb_next_add_record(module, msg);
228         }
229
230         /* we have to copy the message as the caller might have it as a const */
231         msg2 = ldb_msg_copy_shallow(module, msg);
232         if (msg2 == NULL) {
233                 return -1;
234         }
235         if (add_time_element(msg2, "whenCreated", t) != 0 ||
236             add_time_element(msg2, "whenChanged", t) != 0) {
237                 talloc_free(msg2);
238                 return -1;
239         }
240         ret = ldb_next_add_record(module, msg2);
241         talloc_free(msg2);
242         return ret;
243 }
244
245 /*
246   hook modify record ops
247 */
248 static int operational_modify_record(struct ldb_module *module, 
249                                      const struct ldb_message *msg)
250 {
251         time_t t = time(NULL);
252         struct ldb_message *msg2;
253         int ret;
254
255         if (ldb_dn_is_special(msg->dn)) {
256                 return ldb_next_modify_record(module, msg);
257         }
258
259         /* we have to copy the message as the caller might have it as a const */
260         msg2 = ldb_msg_copy_shallow(module, msg);
261         if (msg2 == NULL) {
262                 return -1;
263         }
264         if (add_time_element(msg2, "whenChanged", t) != 0) {
265                 talloc_free(msg2);
266                 return -1;
267         }
268         ret = ldb_next_modify_record(module, msg2);
269         talloc_free(msg2);
270         return ret;
271 }
272
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
278 };
279
280
281 /* the init function */
282 #ifdef HAVE_DLOPEN_DISABLED
283  struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
284 #else
285 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
286 #endif
287 {
288         struct ldb_module *ctx;
289
290         ctx = talloc(ldb, struct ldb_module);
291         if (!ctx)
292                 return NULL;
293
294         ctx->private_data = NULL;
295         ctx->ldb = ldb;
296         ctx->prev = ctx->next = NULL;
297         ctx->ops = &operational_ops;
298
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);
304
305         return ctx;
306 }