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