Fix use of some modules (needed _PUBLIC_).
[samba.git] / source4 / dsdb / samdb / ldb_modules / objectguid.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004-2006
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Andrew Tridgell 2005
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/include/ldb_includes.h"
38 #include "librpc/gen_ndr/ndr_misc.h"
39 #include "param/param.h"
40
41 static struct ldb_message_element *objectguid_find_attribute(const struct ldb_message *msg, const char *name)
42 {
43         int i;
44
45         for (i = 0; i < msg->num_elements; i++) {
46                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
47                         return &msg->elements[i];
48                 }
49         }
50
51         return NULL;
52 }
53
54 /*
55   add a time element to a record
56 */
57 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
58 {
59         struct ldb_message_element *el;
60         char *s;
61
62         if (ldb_msg_find_element(msg, attr) != NULL) {
63                 return 0;
64         }
65
66         s = ldb_timestring(msg, t);
67         if (s == NULL) {
68                 return -1;
69         }
70
71         if (ldb_msg_add_string(msg, attr, s) != 0) {
72                 return -1;
73         }
74
75         el = ldb_msg_find_element(msg, attr);
76         /* always set as replace. This works because on add ops, the flag
77            is ignored */
78         el->flags = LDB_FLAG_MOD_REPLACE;
79
80         return 0;
81 }
82
83 /*
84   add a uint64_t element to a record
85 */
86 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
87 {
88         struct ldb_message_element *el;
89
90         if (ldb_msg_find_element(msg, attr) != NULL) {
91                 return 0;
92         }
93
94         if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
95                 return -1;
96         }
97
98         el = ldb_msg_find_element(msg, attr);
99         /* always set as replace. This works because on add ops, the flag
100            is ignored */
101         el->flags = LDB_FLAG_MOD_REPLACE;
102
103         return 0;
104 }
105
106 /* add_record: add objectGUID attribute */
107 static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
108 {
109         struct ldb_request *down_req;
110         struct ldb_message_element *attribute;
111         struct ldb_message *msg;
112         struct ldb_val v;
113         struct GUID guid;
114         uint64_t seq_num;
115         enum ndr_err_code ndr_err;
116         int ret;
117         time_t t = time(NULL);
118
119         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
120
121         /* do not manipulate our control entries */
122         if (ldb_dn_is_special(req->op.add.message->dn)) {
123                 return ldb_next_request(module, req);
124         }
125
126         if ((attribute = objectguid_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
127                 return ldb_next_request(module, req);
128         }
129
130         down_req = talloc(req, struct ldb_request);
131         if (down_req == NULL) {
132                 return LDB_ERR_OPERATIONS_ERROR;
133         }
134
135         *down_req = *req;
136
137         /* we have to copy the message as the caller might have it as a const */
138         down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
139         if (msg == NULL) {
140                 talloc_free(down_req);
141                 return LDB_ERR_OPERATIONS_ERROR;
142         }
143
144         /* a new GUID */
145         guid = GUID_random();
146
147         ndr_err = ndr_push_struct_blob(&v, msg, 
148                                        lp_iconv_convenience(ldb_get_opaque(module->ldb, "loadparm")),
149                                        &guid,
150                                        (ndr_push_flags_fn_t)ndr_push_GUID);
151         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
152                 talloc_free(down_req);
153                 return LDB_ERR_OPERATIONS_ERROR;
154         }
155
156         ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
157         if (ret) {
158                 talloc_free(down_req);
159                 return ret;
160         }
161         
162         if (add_time_element(msg, "whenCreated", t) != 0 ||
163             add_time_element(msg, "whenChanged", t) != 0) {
164                 talloc_free(down_req);
165                 return LDB_ERR_OPERATIONS_ERROR;
166         }
167
168         /* Get a sequence number from the backend */
169         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
170         if (ret == LDB_SUCCESS) {
171                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
172                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
173                         talloc_free(down_req);
174                         return LDB_ERR_OPERATIONS_ERROR;
175                 }
176         }
177
178         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
179
180         /* go on with the call chain */
181         ret = ldb_next_request(module, down_req);
182
183         /* do not free down_req as the call results may be linked to it,
184          * it will be freed when the upper level request get freed */
185         if (ret == LDB_SUCCESS) {
186                 req->handle = down_req->handle;
187         }
188
189         return ret;
190 }
191
192 /* modify_record: update timestamps */
193 static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
194 {
195         struct ldb_request *down_req;
196         struct ldb_message *msg;
197         int ret;
198         time_t t = time(NULL);
199         uint64_t seq_num;
200
201         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
202
203         /* do not manipulate our control entries */
204         if (ldb_dn_is_special(req->op.add.message->dn)) {
205                 return ldb_next_request(module, req);
206         }
207
208         down_req = talloc(req, struct ldb_request);
209         if (down_req == NULL) {
210                 return LDB_ERR_OPERATIONS_ERROR;
211         }
212
213         *down_req = *req;
214
215         /* we have to copy the message as the caller might have it as a const */
216         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
217         if (msg == NULL) {
218                 talloc_free(down_req);
219                 return LDB_ERR_OPERATIONS_ERROR;
220         }
221
222         if (add_time_element(msg, "whenChanged", t) != 0) {
223                 talloc_free(down_req);
224                 return LDB_ERR_OPERATIONS_ERROR;
225         }
226
227         /* Get a sequence number from the backend */
228         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
229         if (ret == LDB_SUCCESS) {
230                 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
231                         talloc_free(down_req);
232                         return LDB_ERR_OPERATIONS_ERROR;
233                 }
234         }
235
236         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
237
238         /* go on with the call chain */
239         ret = ldb_next_request(module, down_req);
240
241         /* do not free down_req as the call results may be linked to it,
242          * it will be freed when the upper level request get freed */
243         if (ret == LDB_SUCCESS) {
244                 req->handle = down_req->handle;
245         }
246
247         return ret;
248 }
249
250 _PUBLIC_ const struct ldb_module_ops ldb_objectguid_module_ops = {
251         .name          = "objectguid",
252         .add           = objectguid_add,
253         .modify        = objectguid_modify,
254 };