Fix the mess with ldb includes.
[sfrench/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / ranged_results.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett 2007
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb ranged results module
24  *
25  *  Description: munge AD-style 'ranged results' requests into
26  *  requests for all values in an attribute, then return the range to
27  *  the client.
28  *
29  *  Author: Andrew Bartlett
30  */
31
32 #include "ldb_module.h"
33
34 struct rr_context {
35         struct ldb_module *module;
36         struct ldb_request *req;
37 };
38
39 static struct rr_context *rr_init_context(struct ldb_module *module,
40                                           struct ldb_request *req)
41 {
42         struct rr_context *ac;
43
44         ac = talloc_zero(req, struct rr_context);
45         if (ac == NULL) {
46                 ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory");
47                 return NULL;
48         }
49
50         ac->module = module;
51         ac->req = req;
52
53         return ac;
54 }
55
56 static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
57 {
58         struct ldb_context *ldb;
59         struct rr_context *ac;
60         int i, j;
61
62         ac = talloc_get_type(req->context, struct rr_context);
63         ldb = ldb_module_get_ctx(ac->module);
64
65         if (!ares) {
66                 return ldb_module_done(ac->req, NULL, NULL,
67                                         LDB_ERR_OPERATIONS_ERROR);
68         }
69         if (ares->error != LDB_SUCCESS) {
70                 return ldb_module_done(ac->req, ares->controls,
71                                         ares->response, ares->error);
72         }
73
74         if (ares->type == LDB_REPLY_REFERRAL) {
75                 return ldb_module_send_referral(ac->req, ares->referral);
76         }
77
78         if (ares->type == LDB_REPLY_DONE) {
79                 return ldb_module_done(ac->req, ares->controls,
80                                         ares->response, ares->error);
81         }
82
83         /* LDB_REPLY_ENTRY */
84
85         /* Find those that are range requests from the attribute list */
86         for (i = 0; ac->req->op.search.attrs[i]; i++) {
87                 char *p, *new_attr;
88                 const char *end_str;
89                 unsigned int start, end, orig_num_values;
90                 struct ldb_message_element *el;
91                 struct ldb_val *orig_values;
92                 p = strchr(ac->req->op.search.attrs[i], ';');
93                 if (!p) {
94                         continue;
95                 }
96                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
97                         continue;
98                 }
99                 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
100                         if (sscanf(p, ";range=%u-*", &start) == 1) {
101                                 end = (unsigned int)-1;
102                         } else {
103                                 continue;
104                         }
105                 }
106                 new_attr = talloc_strndup(ac->req,
107                                           ac->req->op.search.attrs[i],
108                                           (size_t)(p - ac->req->op.search.attrs[i]));
109
110                 if (!new_attr) {
111                         ldb_oom(ldb);
112                         return ldb_module_done(ac->req, NULL, NULL,
113                                                 LDB_ERR_OPERATIONS_ERROR);
114                 }
115                 el = ldb_msg_find_element(ares->message, new_attr);
116                 talloc_free(new_attr);
117                 if (!el) {
118                         continue;
119                 }
120                 if (end >= (el->num_values - 1)) {
121                         /* Need to leave the requested attribute in
122                          * there (so add an empty one to match) */
123                         end_str = "*";
124                         end = el->num_values - 1;
125                 } else {
126                         end_str = talloc_asprintf(el, "%u", end);
127                         if (!end_str) {
128                                 ldb_oom(ldb);
129                                 return ldb_module_done(ac->req, NULL, NULL,
130                                                         LDB_ERR_OPERATIONS_ERROR);
131                         }
132                 }
133                 /* If start is greater then where we are find the end to be */
134                 if (start > end) {
135                         el->num_values = 0;
136                         el->values = NULL;
137                 } else {
138                         orig_values = el->values;
139                         orig_num_values = el->num_values;
140                         
141                         if ((start + end < start) || (start + end < end)) {
142                                 ldb_asprintf_errstring(ldb,
143                                         "range request error: start or end would overflow!");
144                                 return ldb_module_done(ac->req, NULL, NULL,
145                                                         LDB_ERR_UNWILLING_TO_PERFORM);
146                         }
147                         
148                         el->num_values = 0;
149                         
150                         el->values = talloc_array(el, struct ldb_val, (end - start) + 1);
151                         if (!el->values) {
152                                 ldb_oom(ldb);
153                                 return ldb_module_done(ac->req, NULL, NULL,
154                                                         LDB_ERR_OPERATIONS_ERROR);
155                         }
156                         for (j=start; j <= end; j++) {
157                                 el->values[el->num_values] = orig_values[j];
158                                 el->num_values++;
159                         }
160                 }
161                 el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str);
162                 if (!el->name) {
163                         ldb_oom(ldb);
164                         return ldb_module_done(ac->req, NULL, NULL,
165                                                 LDB_ERR_OPERATIONS_ERROR);
166                 }
167         }
168
169         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
170 }
171
172 /* search */
173 static int rr_search(struct ldb_module *module, struct ldb_request *req)
174 {
175         struct ldb_context *ldb;
176         int i;
177         unsigned int start, end;
178         const char **new_attrs = NULL;
179         bool found_rr = false;
180         struct ldb_request *down_req;
181         struct rr_context *ac;
182         int ret;
183
184         ldb = ldb_module_get_ctx(module);
185
186         /* Strip the range request from the attribute */
187         for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
188                 char *p;
189                 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
190                 new_attrs[i] = req->op.search.attrs[i];
191                 new_attrs[i+1] = NULL;
192                 p = strchr(new_attrs[i], ';');
193                 if (!p) {
194                         continue;
195                 }
196                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
197                         continue;
198                 }
199                 end = (unsigned int)-1;
200                 if (sscanf(p, ";range=%u-*", &start) != 1) {
201                         if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
202                                 ldb_asprintf_errstring(ldb,
203                                         "range request error: "
204                                         "range request malformed");
205                                 return LDB_ERR_UNWILLING_TO_PERFORM;
206                         }
207                 }
208                 if (start > end) {
209                         ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
210                         return LDB_ERR_UNWILLING_TO_PERFORM;
211                 }
212
213                 found_rr = true;
214                 new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
215                                               (size_t)(p - new_attrs[i]));
216
217                 if (!new_attrs[i]) {
218                         ldb_oom(ldb);
219                         return LDB_ERR_OPERATIONS_ERROR;
220                 }
221         }
222
223         if (found_rr) {
224                 ac = rr_init_context(module, req);
225                 if (!ac) {
226                         return LDB_ERR_OPERATIONS_ERROR;
227                 }
228
229                 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
230                                               req->op.search.base,
231                                               req->op.search.scope,
232                                               req->op.search.tree,
233                                               new_attrs,
234                                               req->controls,
235                                               ac, rr_search_callback,
236                                               req);
237                 if (ret != LDB_SUCCESS) {
238                         return ret;
239                 }
240                 return ldb_next_request(module, down_req);
241         }
242
243         /* No change, just run the original request as if we were never here */
244         talloc_free(new_attrs);
245         return ldb_next_request(module, req);
246 }
247
248 const struct ldb_module_ops ldb_ranged_results_module_ops = {
249         .name              = "ranged_results",
250         .search            = rr_search,
251 };