Merge branch 'master' of ssh://git.samba.org/data/git/samba
[metze/samba/wip.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_includes.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(module->ldb, "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 rr_context *ac;
59         int i, j;
60
61         ac = talloc_get_type(req->context, struct rr_context);
62
63         if (!ares) {
64                 return ldb_module_done(ac->req, NULL, NULL,
65                                         LDB_ERR_OPERATIONS_ERROR);
66         }
67         if (ares->error != LDB_SUCCESS) {
68                 return ldb_module_done(ac->req, ares->controls,
69                                         ares->response, ares->error);
70         }
71
72         if (ares->type == LDB_REPLY_REFERRAL) {
73                 return ldb_module_send_referral(ac->req, ares->referral);
74         }
75
76         if (ares->type == LDB_REPLY_DONE) {
77                 return ldb_module_done(ac->req, ares->controls,
78                                         ares->response, ares->error);
79         }
80
81         /* LDB_REPLY_ENTRY */
82
83         /* Find those that are range requests from the attribute list */
84         for (i = 0; ac->req->op.search.attrs[i]; i++) {
85                 char *p, *new_attr;
86                 const char *end_str;
87                 unsigned int start, end, orig_num_values;
88                 struct ldb_message_element *el;
89                 struct ldb_val *orig_values;
90                 p = strchr(ac->req->op.search.attrs[i], ';');
91                 if (!p) {
92                         continue;
93                 }
94                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
95                         continue;
96                 }
97                 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
98                         if (sscanf(p, ";range=%u-*", &start) == 1) {
99                                 end = (unsigned int)-1;
100                         } else {
101                                 continue;
102                         }
103                 }
104                 new_attr = talloc_strndup(ac->req,
105                                           ac->req->op.search.attrs[i],
106                                           (size_t)(p - ac->req->op.search.attrs[i]));
107
108                 if (!new_attr) {
109                         ldb_oom(ac->module->ldb);
110                         return ldb_module_done(ac->req, NULL, NULL,
111                                                 LDB_ERR_OPERATIONS_ERROR);
112                 }
113                 el = ldb_msg_find_element(ares->message, new_attr);
114                 talloc_free(new_attr);
115                 if (!el) {
116                         continue;
117                 }
118                 if (end >= (el->num_values - 1)) {
119                         /* Need to leave the requested attribute in
120                          * there (so add an empty one to match) */
121                         end_str = "*";
122                         end = el->num_values - 1;
123                 } else {
124                         end_str = talloc_asprintf(el, "%u", end);
125                         if (!end_str) {
126                                 ldb_oom(ac->module->ldb);
127                                 return ldb_module_done(ac->req, NULL, NULL,
128                                                         LDB_ERR_OPERATIONS_ERROR);
129                         }
130                 }
131                 /* If start is greater then where we are find the end to be */
132                 if (start > end) {
133                         el->num_values = 0;
134                         el->values = NULL;
135                 } else {
136                         orig_values = el->values;
137                         orig_num_values = el->num_values;
138                         
139                         if ((start + end < start) || (start + end < end)) {
140                                 ldb_asprintf_errstring(ac->module->ldb,
141                                         "range request error: start or end would overflow!");
142                                 return ldb_module_done(ac->req, NULL, NULL,
143                                                         LDB_ERR_UNWILLING_TO_PERFORM);
144                         }
145                         
146                         el->num_values = 0;
147                         
148                         el->values = talloc_array(el, struct ldb_val, (end - start) + 1);
149                         if (!el->values) {
150                                 ldb_oom(ac->module->ldb);
151                                 return ldb_module_done(ac->req, NULL, NULL,
152                                                         LDB_ERR_OPERATIONS_ERROR);
153                         }
154                         for (j=start; j <= end; j++) {
155                                 el->values[el->num_values] = orig_values[j];
156                                 el->num_values++;
157                         }
158                 }
159                 el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str);
160                 if (!el->name) {
161                         ldb_oom(ac->module->ldb);
162                         return ldb_module_done(ac->req, NULL, NULL,
163                                                 LDB_ERR_OPERATIONS_ERROR);
164                 }
165         }
166
167         return ldb_module_send_entry(ac->req, ares->message, ares->controls);
168 }
169
170 /* search */
171 static int rr_search(struct ldb_module *module, struct ldb_request *req)
172 {
173         int i;
174         unsigned int start, end;
175         const char **new_attrs = NULL;
176         bool found_rr = false;
177         struct ldb_request *down_req;
178         struct rr_context *ac;
179         int ret;
180
181         /* Strip the range request from the attribute */
182         for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
183                 char *p;
184                 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
185                 new_attrs[i] = req->op.search.attrs[i];
186                 new_attrs[i+1] = NULL;
187                 p = strchr(new_attrs[i], ';');
188                 if (!p) {
189                         continue;
190                 }
191                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
192                         continue;
193                 }
194                 end = (unsigned int)-1;
195                 if (sscanf(p, ";range=%u-*", &start) != 1) {
196                         if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
197                                 ldb_asprintf_errstring(module->ldb,
198                                         "range request error: "
199                                         "range request malformed");
200                                 return LDB_ERR_UNWILLING_TO_PERFORM;
201                         }
202                 }
203                 if (start > end) {
204                         ldb_asprintf_errstring(module->ldb, "range request error: start must not be greater than end");
205                         return LDB_ERR_UNWILLING_TO_PERFORM;
206                 }
207
208                 found_rr = true;
209                 new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
210                                               (size_t)(p - new_attrs[i]));
211
212                 if (!new_attrs[i]) {
213                         ldb_oom(module->ldb);
214                         return LDB_ERR_OPERATIONS_ERROR;
215                 }
216         }
217
218         if (found_rr) {
219                 ac = rr_init_context(module, req);
220                 if (!ac) {
221                         return LDB_ERR_OPERATIONS_ERROR;
222                 }
223
224                 ret = ldb_build_search_req_ex(&down_req, module->ldb, ac,
225                                               req->op.search.base,
226                                               req->op.search.scope,
227                                               req->op.search.tree,
228                                               new_attrs,
229                                               req->controls,
230                                               ac, rr_search_callback,
231                                               req);
232                 if (ret != LDB_SUCCESS) {
233                         return ret;
234                 }
235                 return ldb_next_request(module, down_req);
236         }
237
238         /* No change, just run the original request as if we were never here */
239         talloc_free(new_attrs);
240         return ldb_next_request(module, req);
241 }
242
243 const struct ldb_module_ops ldb_ranged_results_module_ops = {
244         .name              = "ranged_results",
245         .search            = rr_search,
246 };