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