LDB ASYNC: core modules
[kai/samba.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-2008
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 3 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, see <http://www.gnu.org/licenses/>.
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,$SCHEMADN
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 #include "ldb_includes.h"
77
78 /*
79   construct a canonical name from a message
80 */
81 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
82 {
83         char *canonicalName;
84         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
85         if (canonicalName == NULL) {
86                 return -1;
87         }
88         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
89 }
90
91 /*
92   a list of attribute names that should be substituted in the parse
93   tree before the search is done
94 */
95 static const struct {
96         const char *attr;
97         const char *replace;
98 } parse_tree_sub[] = {
99         { "createTimestamp", "whenCreated" },
100         { "modifyTimestamp", "whenChanged" }
101 };
102
103
104 /*
105   a list of attribute names that are hidden, but can be searched for
106   using another (non-hidden) name to produce the correct result
107 */
108 static const struct {
109         const char *attr;
110         const char *replace;
111         int (*constructor)(struct ldb_module *, struct ldb_message *);
112 } search_sub[] = {
113         { "createTimestamp", "whenCreated", NULL },
114         { "modifyTimestamp", "whenChanged", NULL },
115         { "structuralObjectClass", "objectClass", NULL },
116         { "canonicalName", "distinguishedName", construct_canonical_name }
117 };
118
119 /*
120   post process a search result record. For any search_sub[] attributes that were
121   asked for, we need to call the appropriate copy routine to copy the result
122   into the message, then remove any attributes that we added to the search but were
123   not asked for by the user
124 */
125 static int operational_search_post_process(struct ldb_module *module,
126                                            struct ldb_message *msg, 
127                                            const char * const *attrs)
128 {
129         int i, a=0;
130
131         for (a=0;attrs && attrs[a];a++) {
132                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
133                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
134                                 continue;
135                         }
136
137                         /* construct the new attribute, using either a supplied 
138                            constructor or a simple copy */
139                         if (search_sub[i].constructor) {
140                                 if (search_sub[i].constructor(module, msg) != 0) {
141                                         goto failed;
142                                 }
143                         } else if (ldb_msg_copy_attr(msg,
144                                                      search_sub[i].replace,
145                                                      search_sub[i].attr) != 0) {
146                                 goto failed;
147                         }
148
149                         /* remove the added search attribute, unless it was asked for 
150                            by the user */
151                         if (search_sub[i].replace == NULL ||
152                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
153                             ldb_attr_in_list(attrs, "*")) {
154                                 continue;
155                         }
156
157                         ldb_msg_remove_attr(msg, search_sub[i].replace);
158                 }
159         }
160
161         return 0;
162
163 failed:
164         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
165                       "operational_search_post_process failed for attribute '%s'\n", 
166                       attrs[a]);
167         return -1;
168 }
169
170
171 /*
172   hook search operations
173 */
174
175 struct operational_context {
176         struct ldb_module *module;
177         struct ldb_request *req;
178
179         const char * const *attrs;
180 };
181
182 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
183 {
184         struct operational_context *ac;
185         int ret;
186
187         ac = talloc_get_type(req->context, struct operational_context);
188
189         if (!ares) {
190                 return ldb_module_done(ac->req, NULL, NULL,
191                                         LDB_ERR_OPERATIONS_ERROR);
192         }
193         if (ares->error != LDB_SUCCESS) {
194                 return ldb_module_done(ac->req, ares->controls,
195                                         ares->response, ares->error);
196         }
197
198         switch (ares->type) {
199         case LDB_REPLY_ENTRY:
200                 /* for each record returned post-process to add any derived
201                    attributes that have been asked for */
202                 ret = operational_search_post_process(ac->module,
203                                                         ares->message,
204                                                         ac->attrs);
205                 if (ret != 0) {
206                         return ldb_module_done(ac->req, NULL, NULL,
207                                                 LDB_ERR_OPERATIONS_ERROR);
208                 }
209                 return ldb_module_send_entry(ac->req, ares->message);
210
211         case LDB_REPLY_REFERRAL:
212                 /* ignore referrals */
213                 break;
214
215         case LDB_REPLY_DONE:
216
217                 return ldb_module_done(ac->req, ares->controls,
218                                         ares->response, LDB_SUCCESS);
219         }
220
221         talloc_free(ares);
222         return LDB_SUCCESS;
223 }
224
225 static int operational_search(struct ldb_module *module, struct ldb_request *req)
226 {
227         struct operational_context *ac;
228         struct ldb_request *down_req;
229         const char **search_attrs = NULL;
230         int i, a;
231         int ret;
232
233         ac = talloc(req, struct operational_context);
234         if (ac == NULL) {
235                 return LDB_ERR_OPERATIONS_ERROR;
236         }
237
238         ac->module = module;
239         ac->req = req;
240         ac->attrs = req->op.search.attrs;
241
242         /*  FIXME: We must copy the tree and keep the original
243          *  unmodified. SSS */
244         /* replace any attributes in the parse tree that are
245            searchable, but are stored using a different name in the
246            backend */
247         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
248                 ldb_parse_tree_attr_replace(req->op.search.tree, 
249                                             parse_tree_sub[i].attr, 
250                                             parse_tree_sub[i].replace);
251         }
252
253         /* in the list of attributes we are looking for, rename any
254            attributes to the alias for any hidden attributes that can
255            be fetched directly using non-hidden names */
256         for (a=0;ac->attrs && ac->attrs[a];a++) {
257                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
258                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
259                             search_sub[i].replace) {
260                                 if (!search_attrs) {
261                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
262                                         if (search_attrs == NULL) {
263                                                 return LDB_ERR_OPERATIONS_ERROR;
264                                         }
265                                 }
266                                 search_attrs[a] = search_sub[i].replace;
267                         }
268                 }
269         }
270
271         /* use new set of attrs if any */
272         if (search_attrs == NULL) {
273                 search_attrs = req->op.search.attrs;
274         }
275
276         ret = ldb_build_search_req_ex(&down_req, module->ldb, ac,
277                                         req->op.search.base,
278                                         req->op.search.scope,
279                                         req->op.search.tree,
280                                         (const char * const *)search_attrs,
281                                         req->controls,
282                                         ac, operational_callback,
283                                         req);
284         if (ret != LDB_SUCCESS) {
285                 return LDB_ERR_OPERATIONS_ERROR;
286         }
287
288         /* perform the search */
289         return ldb_next_request(module, down_req);
290 }
291
292 static int operational_init(struct ldb_module *ctx)
293 {
294         int ret = 0;
295
296         if (ret != 0) {
297                 return ret;
298         }
299
300         return ldb_next_init(ctx);
301 }
302
303 const struct ldb_module_ops ldb_operational_module_ops = {
304         .name              = "operational",
305         .search            = operational_search,
306         .init_context      = operational_init
307 };