s4:fix the build after 380874ef863866c94c999ef53252b9d30df65e88
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / objectguid.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
5    Copyright (C) Andrew Tridgell 2005
6    Copyright (C) Simo Sorce  2004-2008
7
8      ** NOTE! The following LGPL license applies to the ldb
9      ** library. This does NOT imply that all of Samba is released
10      ** under the LGPL
11    
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 3 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public
23    License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /*
27  *  Name: ldb
28  *
29  *  Component: ldb objectguid module
30  *
31  *  Description: add a unique objectGUID onto every new record
32  *
33  *  Author: Simo Sorce
34  */
35
36 #include "includes.h"
37 #include "ldb_module.h"
38 #include "ldb_private.h"
39 #include "librpc/gen_ndr/ndr_misc.h"
40 #include "param/param.h"
41
42 static struct ldb_message_element *objectguid_find_attribute(const struct ldb_message *msg, const char *name)
43 {
44         int i;
45
46         for (i = 0; i < msg->num_elements; i++) {
47                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
48                         return &msg->elements[i];
49                 }
50         }
51
52         return NULL;
53 }
54
55 /*
56   add a time element to a record
57 */
58 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
59 {
60         struct ldb_message_element *el;
61         char *s;
62
63         if (ldb_msg_find_element(msg, attr) != NULL) {
64                 return 0;
65         }
66
67         s = ldb_timestring(msg, t);
68         if (s == NULL) {
69                 return -1;
70         }
71
72         if (ldb_msg_add_string(msg, attr, s) != 0) {
73                 return -1;
74         }
75
76         el = ldb_msg_find_element(msg, attr);
77         /* always set as replace. This works because on add ops, the flag
78            is ignored */
79         el->flags = LDB_FLAG_MOD_REPLACE;
80
81         return 0;
82 }
83
84 /*
85   add a uint64_t element to a record
86 */
87 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
88 {
89         struct ldb_message_element *el;
90
91         if (ldb_msg_find_element(msg, attr) != NULL) {
92                 return 0;
93         }
94
95         if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
96                 return -1;
97         }
98
99         el = ldb_msg_find_element(msg, attr);
100         /* always set as replace. This works because on add ops, the flag
101            is ignored */
102         el->flags = LDB_FLAG_MOD_REPLACE;
103
104         return 0;
105 }
106
107 struct og_context {
108         struct ldb_module *module;
109         struct ldb_request *req;
110 };
111
112 static int og_op_callback(struct ldb_request *req, struct ldb_reply *ares)
113 {
114         struct og_context *ac;
115
116         ac = talloc_get_type(req->context, struct og_context);
117
118         if (!ares) {
119                 return ldb_module_done(ac->req, NULL, NULL,
120                                         LDB_ERR_OPERATIONS_ERROR);
121         }
122         if (ares->error != LDB_SUCCESS) {
123                 return ldb_module_done(ac->req, ares->controls,
124                                         ares->response, ares->error);
125         }
126
127         if (ares->type != LDB_REPLY_DONE) {
128                 talloc_free(ares);
129                 return ldb_module_done(ac->req, NULL, NULL,
130                                         LDB_ERR_OPERATIONS_ERROR);
131         }
132
133         return ldb_module_done(ac->req, ares->controls,
134                                 ares->response, ares->error);
135 }
136
137 /* add_record: add objectGUID attribute */
138 static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
139 {
140         struct ldb_context *ldb;
141         struct ldb_request *down_req;
142         struct ldb_message_element *attribute;
143         struct ldb_message *msg;
144         struct ldb_val v;
145         struct GUID guid;
146         uint64_t seq_num;
147         enum ndr_err_code ndr_err;
148         int ret;
149         time_t t = time(NULL);
150         struct og_context *ac;
151
152         ldb = ldb_module_get_ctx(module);
153
154         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
155
156         /* do not manipulate our control entries */
157         if (ldb_dn_is_special(req->op.add.message->dn)) {
158                 return ldb_next_request(module, req);
159         }
160
161         if ((attribute = objectguid_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
162                 return ldb_next_request(module, req);
163         }
164
165         ac = talloc(req, struct og_context);
166         if (ac == NULL) {
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169         ac->module = module;
170         ac->req = req;
171
172         /* we have to copy the message as the caller might have it as a const */
173         msg = ldb_msg_copy_shallow(ac, req->op.add.message);
174         if (msg == NULL) {
175                 talloc_free(down_req);
176                 return LDB_ERR_OPERATIONS_ERROR;
177         }
178
179         /* a new GUID */
180         guid = GUID_random();
181
182         ndr_err = ndr_push_struct_blob(&v, msg, 
183                                        lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
184                                        &guid,
185                                        (ndr_push_flags_fn_t)ndr_push_GUID);
186         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
187                 return LDB_ERR_OPERATIONS_ERROR;
188         }
189
190         ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
191         if (ret) {
192                 return ret;
193         }
194         
195         if (add_time_element(msg, "whenCreated", t) != 0 ||
196             add_time_element(msg, "whenChanged", t) != 0) {
197                 return LDB_ERR_OPERATIONS_ERROR;
198         }
199
200         /* Get a sequence number from the backend */
201         /* FIXME: ldb_sequence_number is a semi-async call,
202          * make sure this function is split and a callback is used */
203         ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
204         if (ret == LDB_SUCCESS) {
205                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
206                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
207                         return LDB_ERR_OPERATIONS_ERROR;
208                 }
209         }
210
211         ret = ldb_build_add_req(&down_req, ldb, ac,
212                                 msg,
213                                 req->controls,
214                                 ac, og_op_callback,
215                                 req);
216         if (ret != LDB_SUCCESS) {
217                 return LDB_ERR_OPERATIONS_ERROR;
218         }
219
220         /* go on with the call chain */
221         return ldb_next_request(module, down_req);
222 }
223
224 /* modify_record: update timestamps */
225 static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
226 {
227         struct ldb_context *ldb;
228         struct ldb_request *down_req;
229         struct ldb_message *msg;
230         int ret;
231         time_t t = time(NULL);
232         uint64_t seq_num;
233         struct og_context *ac;
234
235         ldb = ldb_module_get_ctx(module);
236
237         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
238
239         /* do not manipulate our control entries */
240         if (ldb_dn_is_special(req->op.add.message->dn)) {
241                 return ldb_next_request(module, req);
242         }
243
244         ac = talloc(req, struct og_context);
245         if (ac == NULL) {
246                 return LDB_ERR_OPERATIONS_ERROR;
247         }
248         ac->module = module;
249         ac->req = req;
250
251         /* we have to copy the message as the caller might have it as a const */
252         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
253         if (msg == NULL) {
254                 return LDB_ERR_OPERATIONS_ERROR;
255         }
256
257         if (add_time_element(msg, "whenChanged", t) != 0) {
258                 return LDB_ERR_OPERATIONS_ERROR;
259         }
260
261         /* Get a sequence number from the backend */
262         ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
263         if (ret == LDB_SUCCESS) {
264                 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
265                         return LDB_ERR_OPERATIONS_ERROR;
266                 }
267         }
268
269         ret = ldb_build_mod_req(&down_req, ldb, ac,
270                                 msg,
271                                 req->controls,
272                                 ac, og_op_callback,
273                                 req);
274         if (ret != LDB_SUCCESS) {
275                 return LDB_ERR_OPERATIONS_ERROR;
276         }
277
278         /* go on with the call chain */
279         return ldb_next_request(module, down_req);
280 }
281
282 _PUBLIC_ const struct ldb_module_ops ldb_objectguid_module_ops = {
283         .name          = "objectguid",
284         .add           = objectguid_add,
285         .modify        = objectguid_modify,
286 };