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