Fix the mess with ldb includes.
[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-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_module.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         struct ldb_context *ldb;
130         int i, a=0;
131
132         ldb = ldb_module_get_ctx(module);
133
134         for (a=0;attrs && attrs[a];a++) {
135                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
136                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
137                                 continue;
138                         }
139
140                         /* construct the new attribute, using either a supplied 
141                            constructor or a simple copy */
142                         if (search_sub[i].constructor) {
143                                 if (search_sub[i].constructor(module, msg) != 0) {
144                                         goto failed;
145                                 }
146                         } else if (ldb_msg_copy_attr(msg,
147                                                      search_sub[i].replace,
148                                                      search_sub[i].attr) != 0) {
149                                 goto failed;
150                         }
151
152                         /* remove the added search attribute, unless it was asked for 
153                            by the user */
154                         if (search_sub[i].replace == NULL ||
155                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
156                             ldb_attr_in_list(attrs, "*")) {
157                                 continue;
158                         }
159
160                         ldb_msg_remove_attr(msg, search_sub[i].replace);
161                 }
162         }
163
164         return 0;
165
166 failed:
167         ldb_debug_set(ldb, LDB_DEBUG_WARNING, 
168                       "operational_search_post_process failed for attribute '%s'\n", 
169                       attrs[a]);
170         return -1;
171 }
172
173
174 /*
175   hook search operations
176 */
177
178 struct operational_context {
179         struct ldb_module *module;
180         struct ldb_request *req;
181
182         const char * const *attrs;
183 };
184
185 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
186 {
187         struct operational_context *ac;
188         int ret;
189
190         ac = talloc_get_type(req->context, struct operational_context);
191
192         if (!ares) {
193                 return ldb_module_done(ac->req, NULL, NULL,
194                                         LDB_ERR_OPERATIONS_ERROR);
195         }
196         if (ares->error != LDB_SUCCESS) {
197                 return ldb_module_done(ac->req, ares->controls,
198                                         ares->response, ares->error);
199         }
200
201         switch (ares->type) {
202         case LDB_REPLY_ENTRY:
203                 /* for each record returned post-process to add any derived
204                    attributes that have been asked for */
205                 ret = operational_search_post_process(ac->module,
206                                                         ares->message,
207                                                         ac->attrs);
208                 if (ret != 0) {
209                         return ldb_module_done(ac->req, NULL, NULL,
210                                                 LDB_ERR_OPERATIONS_ERROR);
211                 }
212                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
213
214         case LDB_REPLY_REFERRAL:
215                 /* ignore referrals */
216                 break;
217
218         case LDB_REPLY_DONE:
219
220                 return ldb_module_done(ac->req, ares->controls,
221                                         ares->response, LDB_SUCCESS);
222         }
223
224         talloc_free(ares);
225         return LDB_SUCCESS;
226 }
227
228 static int operational_search(struct ldb_module *module, struct ldb_request *req)
229 {
230         struct ldb_context *ldb;
231         struct operational_context *ac;
232         struct ldb_request *down_req;
233         const char **search_attrs = NULL;
234         int i, a;
235         int ret;
236
237         ldb = ldb_module_get_ctx(module);
238
239         ac = talloc(req, struct operational_context);
240         if (ac == NULL) {
241                 return LDB_ERR_OPERATIONS_ERROR;
242         }
243
244         ac->module = module;
245         ac->req = req;
246         ac->attrs = req->op.search.attrs;
247
248         /*  FIXME: We must copy the tree and keep the original
249          *  unmodified. SSS */
250         /* replace any attributes in the parse tree that are
251            searchable, but are stored using a different name in the
252            backend */
253         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
254                 ldb_parse_tree_attr_replace(req->op.search.tree, 
255                                             parse_tree_sub[i].attr, 
256                                             parse_tree_sub[i].replace);
257         }
258
259         /* in the list of attributes we are looking for, rename any
260            attributes to the alias for any hidden attributes that can
261            be fetched directly using non-hidden names */
262         for (a=0;ac->attrs && ac->attrs[a];a++) {
263                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
264                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
265                             search_sub[i].replace) {
266                                 if (!search_attrs) {
267                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
268                                         if (search_attrs == NULL) {
269                                                 return LDB_ERR_OPERATIONS_ERROR;
270                                         }
271                                 }
272                                 search_attrs[a] = search_sub[i].replace;
273                         }
274                 }
275         }
276
277         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
278                                         req->op.search.base,
279                                         req->op.search.scope,
280                                         req->op.search.tree,
281                                         /* use new set of attrs if any */
282                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
283                                         req->controls,
284                                         ac, operational_callback,
285                                         req);
286         if (ret != LDB_SUCCESS) {
287                 return LDB_ERR_OPERATIONS_ERROR;
288         }
289
290         /* perform the search */
291         return ldb_next_request(module, down_req);
292 }
293
294 static int operational_init(struct ldb_module *ctx)
295 {
296         int ret = 0;
297
298         if (ret != 0) {
299                 return ret;
300         }
301
302         return ldb_next_init(ctx);
303 }
304
305 const struct ldb_module_ops ldb_operational_module_ops = {
306         .name              = "operational",
307         .search            = operational_search,
308         .init_context      = operational_init
309 };