LDB ASYNC: core modules
[kai/samba.git] / source4 / lib / ldb / modules / asq.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2005-2008
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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldb attribute scoped query control module
28  *
29  *  Description: this module searches all the the objects pointed
30  *               by the DNs contained in the references attribute
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "ldb_includes.h"
36
37 struct asq_context {
38
39         enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
40
41         struct ldb_module *module;
42         struct ldb_request *req;
43
44         struct ldb_asq_control *asq_ctrl;
45
46         const char * const *req_attrs;
47         char *req_attribute;
48         enum {
49                 ASQ_CTRL_SUCCESS                        = 0,
50                 ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX       = 21,
51                 ASQ_CTRL_UNWILLING_TO_PERFORM           = 53,
52                 ASQ_CTRL_AFFECTS_MULTIPLE_DSA           = 71
53         } asq_ret;
54
55         struct ldb_reply *base_res;
56
57         struct ldb_request **reqs;
58         int num_reqs;
59         int cur_req;
60
61         struct ldb_control **controls;
62 };
63
64 static struct asq_context *asq_context_init(struct ldb_module *module, struct ldb_request *req)
65 {
66         struct asq_context *ac;
67
68         ac = talloc_zero(req, struct asq_context);
69         if (ac == NULL) {
70                 ldb_oom(module->ldb);
71                 return NULL;
72         }
73
74         ac->module = module;
75         ac->req = req;
76
77         return ac;
78 }
79
80 static int asq_search_continue(struct asq_context *ac);
81
82 static int asq_search_terminate(struct asq_context *ac)
83 {
84         struct ldb_asq_control *asq;
85         int i;
86
87         if (ac->controls) {
88                 for (i = 0; ac->controls[i]; i++) /* count em */ ;
89         } else {
90                 i = 0;
91         }
92
93         ac->controls = talloc_realloc(ac, ac->controls, struct ldb_control *, i + 2);
94
95         if (ac->controls == NULL) {
96                 return LDB_ERR_OPERATIONS_ERROR;
97         }
98
99         ac->controls[i] = talloc(ac->controls, struct ldb_control);
100         if (ac->controls[i] == NULL) {
101                 return LDB_ERR_OPERATIONS_ERROR;
102         }
103
104         ac->controls[i]->oid = LDB_CONTROL_ASQ_OID;
105         ac->controls[i]->critical = 0;
106
107         asq = talloc_zero(ac->controls[i], struct ldb_asq_control);
108         if (asq == NULL)
109                 return LDB_ERR_OPERATIONS_ERROR;
110
111         asq->result = ac->asq_ret;
112
113         ac->controls[i]->data = asq;
114
115         ac->controls[i + 1] = NULL;
116
117         return ldb_module_done(ac->req, ac->controls, NULL, LDB_SUCCESS);
118 }
119
120 static int asq_base_callback(struct ldb_request *req, struct ldb_reply *ares)
121 {
122         struct asq_context *ac;
123         int ret;
124
125         ac = talloc_get_type(req->context, struct asq_context);
126
127         if (!ares) {
128                 return ldb_module_done(ac->req, NULL, NULL,
129                                         LDB_ERR_OPERATIONS_ERROR);
130         }
131         if (ares->error != LDB_SUCCESS) {
132                 return ldb_module_done(ac->req, ares->controls,
133                                         ares->response, ares->error);
134         }
135
136         switch (ares->type) {
137         case LDB_REPLY_ENTRY:
138                 ac->base_res = talloc_move(ac, &ares);
139                 break;
140
141         case LDB_REPLY_REFERRAL:
142                 /* ignore referrals */
143                 talloc_free(ares);
144                 break;
145
146         case LDB_REPLY_DONE:
147
148                 talloc_free(ares);
149
150                 /* next step */
151                 ret = asq_search_continue(ac);
152                 if (ret != LDB_SUCCESS) {
153                         return ldb_module_done(ac->req, NULL, NULL, ret);
154                 }
155                 break;
156
157         }
158         return LDB_SUCCESS;
159 }
160
161 static int asq_reqs_callback(struct ldb_request *req, struct ldb_reply *ares)
162 {
163         struct asq_context *ac;
164         int ret;
165
166         ac = talloc_get_type(req->context, struct asq_context);
167
168         if (!ares) {
169                 return ldb_module_done(ac->req, NULL, NULL,
170                                         LDB_ERR_OPERATIONS_ERROR);
171         }
172         if (ares->error != LDB_SUCCESS) {
173                 return ldb_module_done(ac->req, ares->controls,
174                                         ares->response, ares->error);
175         }
176
177         switch (ares->type) {
178         case LDB_REPLY_ENTRY:
179                 /* pass the message up to the original callback as we
180                  * do not have to elaborate on it any further */
181                 ret = ldb_module_send_entry(ac->req, ares->message);
182                 if (ret != LDB_SUCCESS) {
183                         return ldb_module_done(ac->req, NULL, NULL, ret);
184                 }
185                 talloc_free(ares);
186                 break;
187
188         case LDB_REPLY_REFERRAL:
189                 /* ignore referrals */
190                 talloc_free(ares);
191                 break;
192
193         case LDB_REPLY_DONE:
194
195                 talloc_free(ares);
196
197                 ret = asq_search_continue(ac);
198                 if (ret != LDB_SUCCESS) {
199                         return ldb_module_done(ac->req, NULL, NULL, ret);
200                 }
201                 break;
202         }
203
204         return LDB_SUCCESS;
205 }
206
207 static int asq_build_first_request(struct asq_context *ac, struct ldb_request **base_req)
208 {
209         const char **base_attrs;
210         int ret;
211
212         ac->req_attrs = ac->req->op.search.attrs;
213         ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute);
214         if (ac->req_attribute == NULL)
215                 return LDB_ERR_OPERATIONS_ERROR;
216
217         base_attrs = talloc_array(ac, const char *, 2);
218         if (base_attrs == NULL) return LDB_ERR_OPERATIONS_ERROR;
219
220         base_attrs[0] = talloc_strdup(base_attrs, ac->asq_ctrl->source_attribute);
221         if (base_attrs[0] == NULL) return LDB_ERR_OPERATIONS_ERROR;
222
223         base_attrs[1] = NULL;
224
225         ret = ldb_build_search_req_ex(base_req, ac->module->ldb, ac,
226                                         ac->req->op.search.base,
227                                         LDB_SCOPE_BASE,
228                                         ac->req->op.search.tree,
229                                         (const char * const *)base_attrs,
230                                         NULL,
231                                         ac, asq_base_callback,
232                                         ac->req);
233         if (ret != LDB_SUCCESS) {
234                 return LDB_ERR_OPERATIONS_ERROR;
235         }
236
237         return LDB_SUCCESS;
238 }
239
240 static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated)
241 {
242         struct ldb_control **saved_controls;
243         struct ldb_control *control;
244         struct ldb_dn *dn;
245         struct ldb_message_element *el;
246         int ret, i;
247
248         if (ac->base_res == NULL) {
249                 return LDB_ERR_NO_SUCH_OBJECT;
250         }
251
252         el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
253         /* no values found */
254         if (el == NULL) {
255                 ac->asq_ret = ASQ_CTRL_SUCCESS;
256                 *terminated = true;
257                 return asq_search_terminate(ac);
258         }
259
260         ac->num_reqs = el->num_values;
261         ac->cur_req = 0;
262         ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
263         if (ac->reqs == NULL) {
264                 return LDB_ERR_OPERATIONS_ERROR;
265         }
266
267         for (i = 0; i < el->num_values; i++) {
268
269                 dn = ldb_dn_new(ac, ac->module->ldb,
270                                 (const char *)el->values[i].data);
271                 if ( ! ldb_dn_validate(dn)) {
272                         ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
273                         *terminated = true;
274                         return asq_search_terminate(ac);
275                 }
276
277                 ret = ldb_build_search_req_ex(&ac->reqs[i],
278                                                 ac->module->ldb, ac,
279                                                 dn, LDB_SCOPE_BASE,
280                                                 ac->req->op.search.tree,
281                                                 ac->req_attrs,
282                                                 ac->req->controls,
283                                                 ac, asq_reqs_callback,
284                                                 ac->req);
285                 if (ret != LDB_SUCCESS) {
286                         return LDB_ERR_OPERATIONS_ERROR;
287                 }
288
289                 /* remove the ASQ control itself */
290                 control = ldb_request_get_control(ac->req, LDB_CONTROL_ASQ_OID);
291                 if (!save_controls(control, ac->reqs[i], &saved_controls)) {
292                         return LDB_ERR_OPERATIONS_ERROR;
293                 }
294         }
295
296         return LDB_SUCCESS;
297 }
298
299 static int asq_search_continue(struct asq_context *ac)
300 {
301         bool terminated = false;
302         int ret;
303
304         switch (ac->step) {
305         case ASQ_SEARCH_BASE:
306
307                 /* build up the requests call chain */
308                 ret = asq_build_multiple_requests(ac, &terminated);
309                 if (ret != LDB_SUCCESS || terminated) {
310                         return ret;
311                 }
312
313                 ac->step = ASQ_SEARCH_MULTI;
314
315                 return ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
316
317         case ASQ_SEARCH_MULTI:
318
319                 ac->cur_req++;
320
321                 if (ac->cur_req == ac->num_reqs) {
322                         /* done */
323                         return asq_search_terminate(ac);
324                 }
325
326                 return ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
327         }
328
329         return LDB_ERR_OPERATIONS_ERROR;
330 }
331
332 static int asq_search(struct ldb_module *module, struct ldb_request *req)
333 {
334         struct ldb_request *base_req;
335         struct ldb_control *control;
336         struct asq_context *ac;
337         int ret;
338
339         /* check if there's a paged request control */
340         control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID);
341         if (control == NULL) {
342                 /* not found go on */
343                 return ldb_next_request(module, req);
344         }
345
346         ac = asq_context_init(module, req);
347         if (!ac) {
348                 return LDB_ERR_OPERATIONS_ERROR;
349         }
350
351         /* check the search is well formed */
352         if (req->op.search.scope != LDB_SCOPE_BASE) {
353                 ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
354                 return asq_search_terminate(ac);
355         }
356
357         ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
358         if (!ac->asq_ctrl) {
359                 return LDB_ERR_PROTOCOL_ERROR;
360         }
361
362         ret = asq_build_first_request(ac, &base_req);
363         if (ret != LDB_SUCCESS) {
364                 return ret;
365         }
366
367         ac->step = ASQ_SEARCH_BASE;
368
369         return ldb_request(module->ldb, base_req);
370 }
371
372 static int asq_init(struct ldb_module *module)
373 {
374         int ret;
375
376         ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID);
377         if (ret != LDB_SUCCESS) {
378                 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n");
379         }
380
381         return ldb_next_init(module);
382 }
383
384 const struct ldb_module_ops ldb_asq_module_ops = {
385         .name              = "asq",
386         .search            = asq_search,
387         .init_context      = asq_init
388 };