r10917: copy the element name in a ldb_msg_rename_attr() and ldb_msg_copy_attr()...
[jelmer/samba4-debian.git] / source / 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         (*res) = NULL;
117
118         /* replace any attributes in the parse tree that are
119            searchable, but are stored using a different name in the
120            backend */
121         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
122                 ldb_parse_tree_attr_replace(tree, 
123                                             parse_tree_sub[i].attr, 
124                                             parse_tree_sub[i].replace);
125         }
126
127         /* in the list of attributes we are looking for, rename any
128            attributes to the alias for any hidden attributes that can
129            be fetched directly using non-hidden names */
130         for (i=0;i<ARRAY_SIZE(search_sub);i++) {
131                 for (a=0;attrs && attrs[a];a++) {
132                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0) {
133                                 if (!search_attrs) {
134                                         search_attrs = ldb_attr_list_copy(module, attrs);
135                                         if (search_attrs == NULL) {
136                                                 goto oom;
137                                         }
138                                 }
139                                 search_attrs[a] = search_sub[i].replace;
140                         }
141                 }
142         }
143         
144
145         /* perform the search */
146         ret = ldb_next_search_bytree(module, base, scope, tree, 
147                                      search_attrs?search_attrs:attrs, res);
148         if (ret <= 0) {
149                 return ret;
150         }
151
152         /* for each record returned see if we have added any
153            attributes to the search, and if we have then either copy
154            them (if the aliased name was also asked for) or rename
155            them (if the aliased entry was not asked for) */
156         for (r=0;r<ret;r++) {
157                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
158                         for (a=0;attrs && attrs[a];a++) {
159                                 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
160                                         continue;
161                                 }
162                                 if (ldb_attr_in_list(attrs, search_sub[i].replace) ||
163                                     ldb_attr_in_list(attrs, "*")) {
164                                         if (ldb_msg_copy_attr((*res)[r], 
165                                                               search_sub[i].replace,
166                                                               search_sub[i].attr) != 0) {
167                                                 goto oom;
168                                         }
169                                 } else {
170                                         if (ldb_msg_rename_attr((*res)[r], 
171                                                               search_sub[i].replace,
172                                                               search_sub[i].attr) != 0) {
173                                                 goto oom;
174                                         }
175                                 }
176                         }
177                 }
178         }
179
180         /* all done */
181         talloc_free(search_attrs);
182         return ret;
183
184 oom:
185         talloc_free(search_attrs);
186         talloc_free(*res);
187         ldb_oom(module->ldb);
188         return -1;
189 }
190
191 /*
192   add a time element to a record
193 */
194 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
195 {
196         struct ldb_message_element *el;
197         char *s;
198
199         if (ldb_msg_find_element(msg, attr) != NULL) {
200                 return 0;
201         }
202
203         s = ldb_timestring(msg, t);
204         if (s == NULL) {
205                 return -1;
206         }
207
208         if (ldb_msg_add_string(msg, attr, s) != 0) {
209                 return -1;
210         }
211
212         el = ldb_msg_find_element(msg, attr);
213         /* always set as replace. This works because on add ops, the flag
214            is ignored */
215         el->flags = LDB_FLAG_MOD_REPLACE;
216
217         return 0;
218 }
219
220
221 /*
222   hook add record ops
223 */
224 static int operational_add_record(struct ldb_module *module, 
225                                   const struct ldb_message *msg)
226 {
227         time_t t = time(NULL);
228         struct ldb_message *msg2;
229         int ret;
230
231         if (ldb_dn_is_special(msg->dn)) {
232                 return ldb_next_add_record(module, msg);
233         }
234
235         /* we have to copy the message as the caller might have it as a const */
236         msg2 = ldb_msg_copy_shallow(module, msg);
237         if (msg2 == NULL) {
238                 return -1;
239         }
240         if (add_time_element(msg2, "whenCreated", t) != 0 ||
241             add_time_element(msg2, "whenChanged", t) != 0) {
242                 talloc_free(msg2);
243                 return -1;
244         }
245         ret = ldb_next_add_record(module, msg2);
246         talloc_free(msg2);
247         return ret;
248 }
249
250 /*
251   hook modify record ops
252 */
253 static int operational_modify_record(struct ldb_module *module, 
254                                      const struct ldb_message *msg)
255 {
256         time_t t = time(NULL);
257         struct ldb_message *msg2;
258         int ret;
259
260         if (ldb_dn_is_special(msg->dn)) {
261                 return ldb_next_modify_record(module, msg);
262         }
263
264         /* we have to copy the message as the caller might have it as a const */
265         msg2 = ldb_msg_copy_shallow(module, msg);
266         if (msg2 == NULL) {
267                 return -1;
268         }
269         if (add_time_element(msg2, "whenChanged", t) != 0) {
270                 talloc_free(msg2);
271                 return -1;
272         }
273         ret = ldb_next_modify_record(module, msg2);
274         talloc_free(msg2);
275         return ret;
276 }
277
278 static const struct ldb_module_ops operational_ops = {
279         .name              = "operational",
280         .search_bytree     = operational_search_bytree,
281         .add_record        = operational_add_record,
282         .modify_record     = operational_modify_record
283 };
284
285
286 /* the init function */
287 #ifdef HAVE_DLOPEN_DISABLED
288  struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
289 #else
290 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
291 #endif
292 {
293         struct ldb_module *ctx;
294
295         ctx = talloc(ldb, struct ldb_module);
296         if (!ctx)
297                 return NULL;
298
299         ctx->private_data = NULL;
300         ctx->ldb = ldb;
301         ctx->prev = ctx->next = NULL;
302         ctx->ops = &operational_ops;
303
304         /* setup some standard attribute handlers */
305         ldb_set_attrib_handler_syntax(ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
306         ldb_set_attrib_handler_syntax(ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
307         ldb_set_attrib_handler_syntax(ldb, "subschemaSubentry", LDB_SYNTAX_DN);
308         ldb_set_attrib_handler_syntax(ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
309
310         return ctx;
311 }