r22681: Fix standalone ldb build when parent directory name != ldb.
[metze/samba/wip.git] / source4 / lib / ldb / modules / operational.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 /*
26   handle operational attributes
27  */
28
29 /*
30   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
31   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
32
33      for the above two, we do the search as normal, and if
34      createTimestamp or modifyTimestamp is asked for, then do
35      additional searches for whenCreated and whenChanged and fill in
36      the resulting values
37
38      we also need to replace these with the whenCreated/whenChanged
39      equivalent in the search expression trees
40
41   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43
44      on init we need to setup attribute handlers for these so
45      comparisons are done correctly. The resolution is 1 second.
46
47      on add we need to add both the above, for current time
48
49      on modify we need to change whenChanged
50
51
52   subschemaSubentry: HIDDEN, not-searchable, 
53                      points at DN CN=Aggregate,$SCHEMADN
54
55      for this one we do the search as normal, then add the static
56      value if requested. How do we work out the $BASEDN from inside a
57      module?
58      
59
60   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61
62      for this one we do the search as normal, then if requested ask
63      for objectclass, change the attribute name, and add it
64
65   allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
66      list of attributes that can be modified - requires schema lookup
67
68
69   attributeTypes: in schema only
70   objectClasses: in schema only
71   matchingRules: in schema only
72   matchingRuleUse: in schema only
73   creatorsName: not supported by w2k3?
74   modifiersName: not supported by w2k3?
75 */
76
77 #include "ldb_includes.h"
78
79 /*
80   construct a canonical name from a message
81 */
82 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
83 {
84         char *canonicalName;
85         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
86         if (canonicalName == NULL) {
87                 return -1;
88         }
89         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
90 }
91
92 /*
93   a list of attribute names that should be substituted in the parse
94   tree before the search is done
95 */
96 static const struct {
97         const char *attr;
98         const char *replace;
99 } parse_tree_sub[] = {
100         { "createTimestamp", "whenCreated" },
101         { "modifyTimestamp", "whenChanged" }
102 };
103
104
105 /*
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
108 */
109 static const struct {
110         const char *attr;
111         const char *replace;
112         int (*constructor)(struct ldb_module *, struct ldb_message *);
113 } search_sub[] = {
114         { "createTimestamp", "whenCreated", NULL },
115         { "modifyTimestamp", "whenChanged", NULL },
116         { "structuralObjectClass", "objectClass", NULL },
117         { "canonicalName", "distinguishedName", construct_canonical_name }
118 };
119
120 /*
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
125 */
126 static int operational_search_post_process(struct ldb_module *module,
127                                            struct ldb_message *msg, 
128                                            const char * const *attrs)
129 {
130         int i, a=0;
131
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) {
135                                 continue;
136                         }
137
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) {
142                                         goto failed;
143                                 }
144                         } else if (ldb_msg_copy_attr(msg,
145                                                      search_sub[i].replace,
146                                                      search_sub[i].attr) != 0) {
147                                 goto failed;
148                         }
149
150                         /* remove the added search attribute, unless it was asked for 
151                            by the user */
152                         if (search_sub[i].replace == NULL ||
153                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
154                             ldb_attr_in_list(attrs, "*")) {
155                                 continue;
156                         }
157
158                         ldb_msg_remove_attr(msg, search_sub[i].replace);
159                 }
160         }
161
162         return 0;
163
164 failed:
165         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
166                       "operational_search_post_process failed for attribute '%s'\n", 
167                       attrs[a]);
168         return -1;
169 }
170
171
172 /*
173   hook search operations
174 */
175
176 struct operational_context {
177
178         struct ldb_module *module;
179         void *up_context;
180         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
181
182         const char * const *attrs;
183 };
184
185 static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
186 {
187         struct operational_context *ac;
188
189         if (!context || !ares) {
190                 ldb_set_errstring(ldb, "NULL Context or Result in callback");
191                 goto error;
192         }
193
194         ac = talloc_get_type(context, struct operational_context);
195
196         if (ares->type == LDB_REPLY_ENTRY) {
197                 /* for each record returned post-process to add any derived
198                    attributes that have been asked for */
199                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
200                         goto error;
201                 }
202         }
203
204         return ac->up_callback(ldb, ac->up_context, ares);
205
206 error:
207         talloc_free(ares);
208         return LDB_ERR_OPERATIONS_ERROR;
209 }
210
211 static int operational_search(struct ldb_module *module, struct ldb_request *req)
212 {
213         struct operational_context *ac;
214         struct ldb_request *down_req;
215         const char **search_attrs = NULL;
216         int i, a, ret;
217
218         req->handle = NULL;
219
220         ac = talloc(req, struct operational_context);
221         if (ac == NULL) {
222                 return LDB_ERR_OPERATIONS_ERROR;
223         }
224
225         ac->module = module;
226         ac->up_context = req->context;
227         ac->up_callback = req->callback;
228         ac->attrs = req->op.search.attrs;
229
230         down_req = talloc_zero(req, struct ldb_request);
231         if (down_req == NULL) {
232                 return LDB_ERR_OPERATIONS_ERROR;
233         }
234
235         down_req->operation = req->operation;
236         down_req->op.search.base = req->op.search.base;
237         down_req->op.search.scope = req->op.search.scope;
238         down_req->op.search.tree = req->op.search.tree;
239
240         /*  FIXME: I hink we should copy the tree and keep the original
241          *  unmodified. SSS */
242         /* replace any attributes in the parse tree that are
243            searchable, but are stored using a different name in the
244            backend */
245         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
246                 ldb_parse_tree_attr_replace(req->op.search.tree, 
247                                             parse_tree_sub[i].attr, 
248                                             parse_tree_sub[i].replace);
249         }
250
251         /* in the list of attributes we are looking for, rename any
252            attributes to the alias for any hidden attributes that can
253            be fetched directly using non-hidden names */
254         for (a=0;ac->attrs && ac->attrs[a];a++) {
255                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
256                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
257                             search_sub[i].replace) {
258                                 if (!search_attrs) {
259                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
260                                         if (search_attrs == NULL) {
261                                                 return LDB_ERR_OPERATIONS_ERROR;
262                                         }
263                                 }
264                                 search_attrs[a] = search_sub[i].replace;
265                         }
266                 }
267         }
268         
269         /* use new set of attrs if any */
270         if (search_attrs) down_req->op.search.attrs = search_attrs;
271         else down_req->op.search.attrs = req->op.search.attrs;
272         
273         down_req->controls = req->controls;
274
275         down_req->context = ac;
276         down_req->callback = operational_callback;
277         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
278
279         /* perform the search */
280         ret = ldb_next_request(module, down_req);
281
282         /* do not free down_req as the call results may be linked to it,
283          * it will be freed when the upper level request get freed */
284         if (ret == LDB_SUCCESS) {
285                 req->handle = down_req->handle;
286         }
287
288         return ret;
289 }
290
291 static int operational_init(struct ldb_module *ctx)
292 {
293         int ret = 0;
294
295         /* setup some standard attribute handlers */
296         ret |= ldb_schema_attribute_add(ctx->ldb, "whenCreated", 0, LDB_SYNTAX_UTC_TIME);
297         ret |= ldb_schema_attribute_add(ctx->ldb, "whenChanged", 0, LDB_SYNTAX_UTC_TIME);
298         ret |= ldb_schema_attribute_add(ctx->ldb, "subschemaSubentry", 0, LDB_SYNTAX_DN);
299         ret |= ldb_schema_attribute_add(ctx->ldb, "structuralObjectClass", 0, LDB_SYNTAX_OBJECTCLASS);
300
301         if (ret != 0) {
302                 return ret;
303         }
304
305         return ldb_next_init(ctx);
306 }
307
308 static const struct ldb_module_ops operational_ops = {
309         .name              = "operational",
310         .search            = operational_search,
311         .init_context      = operational_init
312 };
313
314 int ldb_operational_init(void)
315 {
316         return ldb_register_module(&operational_ops);
317 }