Convert mtime from a time_t to a struct timespec.
[Samba.git] / source4 / dsdb / samdb / ldb_modules / ranged_results.c
blob3b82de87447e2fede68fa6f6695ea8a34c51ee18
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett 2007
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.
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.
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/>.
21 * Name: ldb
23 * Component: ldb ranged results module
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.
29 * Author: Andrew Bartlett
32 #include "includes.h"
33 #include "ldb_module.h"
35 struct rr_context {
36 struct ldb_module *module;
37 struct ldb_request *req;
40 static struct rr_context *rr_init_context(struct ldb_module *module,
41 struct ldb_request *req)
43 struct rr_context *ac;
45 ac = talloc_zero(req, struct rr_context);
46 if (ac == NULL) {
47 ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory");
48 return NULL;
51 ac->module = module;
52 ac->req = req;
54 return ac;
57 static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
59 struct ldb_context *ldb;
60 struct rr_context *ac;
61 unsigned int i, j;
62 TALLOC_CTX *temp_ctx;
64 ac = talloc_get_type(req->context, struct rr_context);
65 ldb = ldb_module_get_ctx(ac->module);
67 if (!ares) {
68 return ldb_module_done(ac->req, NULL, NULL,
69 LDB_ERR_OPERATIONS_ERROR);
71 if (ares->error != LDB_SUCCESS) {
72 return ldb_module_done(ac->req, ares->controls,
73 ares->response, ares->error);
76 if (ares->type == LDB_REPLY_REFERRAL) {
77 return ldb_module_send_referral(ac->req, ares->referral);
80 if (ares->type == LDB_REPLY_DONE) {
81 return ldb_module_done(ac->req, ares->controls,
82 ares->response, ares->error);
85 /* LDB_REPLY_ENTRY */
87 temp_ctx = talloc_new(ac->req);
88 if (!temp_ctx) {
89 ldb_module_oom(ac->module);
90 return ldb_module_done(ac->req, NULL, NULL,
91 LDB_ERR_OPERATIONS_ERROR);
94 /* Find those that are range requests from the attribute list */
95 for (i = 0; ac->req->op.search.attrs[i]; i++) {
96 char *p, *new_attr;
97 const char *end_str;
98 unsigned int start, end, orig_num_values;
99 struct ldb_message_element *el;
100 struct ldb_val *orig_values;
102 p = strchr(ac->req->op.search.attrs[i], ';');
103 if (!p) {
104 continue;
106 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
107 continue;
109 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
110 if (sscanf(p, ";range=%u-*", &start) == 1) {
111 end = (unsigned int)-1;
112 } else {
113 continue;
116 new_attr = talloc_strndup(temp_ctx,
117 ac->req->op.search.attrs[i],
118 (size_t)(p - ac->req->op.search.attrs[i]));
120 if (!new_attr) {
121 ldb_oom(ldb);
122 return ldb_module_done(ac->req, NULL, NULL,
123 LDB_ERR_OPERATIONS_ERROR);
125 el = ldb_msg_find_element(ares->message, new_attr);
126 talloc_free(new_attr);
127 if (!el) {
128 continue;
130 if (end >= (el->num_values - 1)) {
131 /* Need to leave the requested attribute in
132 * there (so add an empty one to match) */
133 end_str = "*";
134 end = el->num_values - 1;
135 } else {
136 end_str = talloc_asprintf(temp_ctx, "%u", end);
137 if (!end_str) {
138 ldb_oom(ldb);
139 return ldb_module_done(ac->req, NULL, NULL,
140 LDB_ERR_OPERATIONS_ERROR);
143 /* If start is greater then where we are find the end to be */
144 if (start > end) {
145 el->num_values = 0;
146 el->values = NULL;
147 } else {
148 orig_values = el->values;
149 orig_num_values = el->num_values;
151 if ((start + end < start) || (start + end < end)) {
152 ldb_asprintf_errstring(ldb,
153 "range request error: start or end would overflow!");
154 return ldb_module_done(ac->req, NULL, NULL,
155 LDB_ERR_UNWILLING_TO_PERFORM);
158 el->num_values = 0;
160 el->values = talloc_array(ares->message->elements,
161 struct ldb_val,
162 (end - start) + 1);
163 if (!el->values) {
164 ldb_oom(ldb);
165 return ldb_module_done(ac->req, NULL, NULL,
166 LDB_ERR_OPERATIONS_ERROR);
168 for (j=start; j <= end; j++) {
169 el->values[el->num_values] = orig_values[j];
170 el->num_values++;
173 el->name = talloc_asprintf(ares->message->elements,
174 "%s;range=%u-%s", el->name, start,
175 end_str);
176 if (!el->name) {
177 ldb_oom(ldb);
178 return ldb_module_done(ac->req, NULL, NULL,
179 LDB_ERR_OPERATIONS_ERROR);
183 talloc_free(temp_ctx);
185 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
188 /* search */
189 static int rr_search(struct ldb_module *module, struct ldb_request *req)
191 struct ldb_context *ldb;
192 unsigned int i;
193 unsigned int start, end;
194 const char **new_attrs = NULL;
195 bool found_rr = false;
196 struct ldb_request *down_req;
197 struct rr_context *ac;
198 int ret;
200 ldb = ldb_module_get_ctx(module);
202 /* Strip the range request from the attribute */
203 for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
204 char *p;
205 new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
206 new_attrs[i] = req->op.search.attrs[i];
207 new_attrs[i+1] = NULL;
208 p = strchr(new_attrs[i], ';');
209 if (!p) {
210 continue;
212 if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
213 continue;
215 end = (unsigned int)-1;
216 if (sscanf(p, ";range=%u-*", &start) != 1) {
217 if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
218 ldb_asprintf_errstring(ldb,
219 "range request error: "
220 "range request malformed");
221 return LDB_ERR_UNWILLING_TO_PERFORM;
224 if (start > end) {
225 ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
226 return LDB_ERR_UNWILLING_TO_PERFORM;
229 found_rr = true;
230 new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
231 (size_t)(p - new_attrs[i]));
233 if (!new_attrs[i]) {
234 return ldb_oom(ldb);
238 if (found_rr) {
239 ac = rr_init_context(module, req);
240 if (!ac) {
241 return ldb_operr(ldb);
244 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
245 req->op.search.base,
246 req->op.search.scope,
247 req->op.search.tree,
248 new_attrs,
249 req->controls,
250 ac, rr_search_callback,
251 req);
252 LDB_REQ_SET_LOCATION(down_req);
253 if (ret != LDB_SUCCESS) {
254 return ret;
256 return ldb_next_request(module, down_req);
259 /* No change, just run the original request as if we were never here */
260 talloc_free(new_attrs);
261 return ldb_next_request(module, req);
264 static const struct ldb_module_ops ldb_ranged_results_module_ops = {
265 .name = "ranged_results",
266 .search = rr_search,
269 int ldb_ranged_results_module_init(const char *version)
271 LDB_MODULE_CHECK_VERSION(version);
272 return ldb_register_module(&ldb_ranged_results_module_ops);