r26489: Merge fixed ranged results module to release branch.
[samba.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_request *orig_req;
36         struct ldb_request *down_req;
37 };
38
39 static int rr_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
40 {
41         struct rr_context *rr_context = talloc_get_type(context, struct rr_context);
42         struct ldb_request *orig_req = rr_context->orig_req;
43         int i, j, ret;
44         
45         if (ares->type != LDB_REPLY_ENTRY) {
46                 return rr_context->orig_req->callback(ldb, rr_context->orig_req->context, ares);
47         }
48
49         /* Find those that are range requests from the attribute list */
50         for (i = 0; orig_req->op.search.attrs[i]; i++) {
51                 char *p, *new_attr;
52                 const char *end_str;
53                 unsigned int start, end, orig_num_values;
54                 struct ldb_message_element *el;
55                 struct ldb_val *orig_values;
56                 p = strchr(orig_req->op.search.attrs[i], ';');
57                 if (!p) {
58                         continue;
59                 }
60                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
61                         continue;
62                 }
63                 if (sscanf(p, ";range=%u-%u", &start, &end) == 2) {
64                 } else if (sscanf(p, ";range=%u-*", &start) == 1) {
65                         end = (unsigned int)-1;
66                 } else {
67                         continue;
68                 }
69                 new_attr = talloc_strndup(orig_req, 
70                                           orig_req->op.search.attrs[i],
71                                           (unsigned int)(p-orig_req->op.search.attrs[i]));
72
73                 if (!new_attr) {
74                         ldb_oom(ldb);
75                         return LDB_ERR_OPERATIONS_ERROR;
76                 }
77                 el = ldb_msg_find_element(ares->message, new_attr);
78                 talloc_free(new_attr);
79                 if (!el) {
80                         continue;
81                 }
82                 if (start > end) {
83                         ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
84                         return LDB_ERR_UNWILLING_TO_PERFORM;
85                 }
86                 if (end >= (el->num_values - 1)) {
87                         /* Need to leave the requested attribute in
88                          * there (so add an empty one to match) */
89                         end_str = "*";
90                         end = el->num_values - 1;
91                 } else {
92                         end_str = talloc_asprintf(el, "%u", end);
93                         if (!end_str) {
94                                 ldb_oom(ldb);
95                                 return LDB_ERR_OPERATIONS_ERROR;
96                         }
97                 }
98                 /* If start is greater then where we noe find the end to be */
99                 if (start > end) {
100                         el->num_values = 0;
101                         el->values = NULL;
102                 } else {
103                         orig_values = el->values;
104                         orig_num_values = el->num_values;
105                         
106                         if ((start + end < start) || (start + end < end)) {
107                                 ldb_asprintf_errstring(ldb, "range request error: start or end would overflow!");
108                                 return LDB_ERR_UNWILLING_TO_PERFORM;
109                         }
110                         
111                         el->num_values = 0;
112                         
113                         el->values = talloc_array(el, struct ldb_val, (end - start) + 1);
114                         if (!el->values) {
115                                 ldb_oom(ldb);
116                                 return LDB_ERR_OPERATIONS_ERROR;
117                         }
118                         for (j=start; j <= end; j++) {
119                                 el->values[el->num_values] = orig_values[j];
120                                 el->num_values++;
121                         }
122                 }
123                 el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str);
124                 if (!el->name) {
125                         ldb_oom(ldb);
126                         return LDB_ERR_OPERATIONS_ERROR;
127                 }
128         }
129
130         return rr_context->orig_req->callback(ldb, rr_context->orig_req->context, ares);
131
132 }
133
134 /* search */
135 static int rr_search(struct ldb_module *module, struct ldb_request *req)
136 {
137         int i;
138         unsigned int start, end;
139         const char **new_attrs = NULL;
140         struct rr_context *context;
141         bool found_rr = false;
142
143         /* Strip the range request from the attribute */
144         for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
145                 char *p;
146                 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
147                 new_attrs[i] = req->op.search.attrs[i];
148                 new_attrs[i+1] = NULL;
149                 p = strchr(req->op.search.attrs[i], ';');
150                 if (!p) {
151                         continue;
152                 }
153                 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
154                         continue;
155                 }
156                 if (sscanf(p, ";range=%u-*", &start) == 1) {
157                 } else if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
158                         ldb_asprintf_errstring(module->ldb, "range request error: range requst malformed");
159                         return LDB_ERR_UNWILLING_TO_PERFORM;
160                 }
161                 if (start > end) {
162                         ldb_asprintf_errstring(module->ldb, "range request error: start must not be greater than end");
163                         return LDB_ERR_UNWILLING_TO_PERFORM;
164                 }
165
166                 found_rr = true;
167                 new_attrs[i] = talloc_strndup(new_attrs, 
168                                               req->op.search.attrs[i],
169                                               (unsigned int)(p-req->op.search.attrs[i]));
170
171                 if (!new_attrs[i]) {
172                         ldb_oom(module->ldb);
173                         return LDB_ERR_OPERATIONS_ERROR;
174                 }
175         }
176
177         if (found_rr) {
178                 int ret;
179                 context = talloc(req, struct rr_context);
180                 context->orig_req = req;
181                 context->down_req = talloc(context, struct ldb_request);
182                 *context->down_req = *req;
183                 
184                 context->down_req->op.search.attrs = new_attrs;
185                 
186                 context->down_req->callback = rr_search_callback;
187                 context->down_req->context = context;
188
189                 ret = ldb_next_request(module, context->down_req);
190                 
191                 /* We don't need to implement our own 'wait' function, so pass the handle along */
192                 if (ret == LDB_SUCCESS) {
193                         req->handle = context->down_req->handle;
194                 }
195                 return ret;
196         }
197
198         /* No change, just run the original request as if we were never here */
199         return ldb_next_request(module, req);
200 }
201
202 static const struct ldb_module_ops rr_ops = {
203         .name              = "ranged_results",
204         .search            = rr_search,
205 };
206
207 int ldb_ranged_results_init(void)
208 {
209         return ldb_register_module(&rr_ops);
210 }