~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/lib/ldb/modules/sort.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   ldb database library
 
3
 
 
4
   Copyright (C) Simo Sorce  2005-2008
 
5
 
 
6
     ** NOTE! The following LGPL license applies to the ldb
 
7
     ** library. This does NOT imply that all of Samba is released
 
8
     ** under the LGPL
 
9
   
 
10
   This library is free software; you can redistribute it and/or
 
11
   modify it under the terms of the GNU Lesser General Public
 
12
   License as published by the Free Software Foundation; either
 
13
   version 3 of the License, or (at your option) any later version.
 
14
 
 
15
   This library is distributed in the hope that it will be useful,
 
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
   Lesser General Public License for more details.
 
19
 
 
20
   You should have received a copy of the GNU Lesser General Public
 
21
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
22
*/
 
23
 
 
24
/*
 
25
 *  Name: ldb
 
26
 *
 
27
 *  Component: ldb server side sort control module
 
28
 *
 
29
 *  Description: this module sorts the results of a search
 
30
 *
 
31
 *  Author: Simo Sorce
 
32
 */
 
33
 
 
34
#include "ldb_module.h"
 
35
 
 
36
struct opaque {
 
37
        struct ldb_context *ldb;
 
38
        const struct ldb_attrib_handler *h;
 
39
        const char *attribute;
 
40
        int reverse;
 
41
        int result;
 
42
};
 
43
 
 
44
struct sort_context {
 
45
        struct ldb_module *module;
 
46
 
 
47
        char *attributeName;
 
48
        char *orderingRule;
 
49
        int reverse;
 
50
 
 
51
        struct ldb_request *req;
 
52
        struct ldb_message **msgs;
 
53
        char **referrals;
 
54
        int num_msgs;
 
55
        int num_refs;
 
56
 
 
57
        const struct ldb_schema_attribute *a;
 
58
        int sort_result;
 
59
};
 
60
 
 
61
static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
 
62
{
 
63
        struct ldb_control **controls;
 
64
        struct ldb_sort_resp_control *resp;
 
65
        int i;
 
66
 
 
67
        if (*ctrls) {
 
68
                controls = *ctrls;
 
69
                for (i = 0; controls[i]; i++);
 
70
                controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
 
71
        } else {
 
72
                i = 0;
 
73
                controls = talloc_array(mem_ctx, struct ldb_control *, 2);
 
74
        }
 
75
        if (! controls )
 
76
                return LDB_ERR_OPERATIONS_ERROR;
 
77
 
 
78
        *ctrls = controls;
 
79
 
 
80
        controls[i+1] = NULL;
 
81
        controls[i] = talloc(controls, struct ldb_control);
 
82
        if (! controls[i] )
 
83
                return LDB_ERR_OPERATIONS_ERROR;
 
84
 
 
85
        controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
 
86
        controls[i]->critical = 0;
 
87
 
 
88
        resp = talloc(controls[i], struct ldb_sort_resp_control);
 
89
        if (! resp )
 
90
                return LDB_ERR_OPERATIONS_ERROR;
 
91
 
 
92
        resp->result = result;
 
93
        resp->attr_desc = talloc_strdup(resp, desc);
 
94
 
 
95
        if (! resp->attr_desc )
 
96
                return LDB_ERR_OPERATIONS_ERROR;
 
97
        
 
98
        controls[i]->data = resp;
 
99
 
 
100
        return LDB_SUCCESS;
 
101
}
 
102
 
 
103
static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
 
104
{
 
105
        struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
 
106
        struct ldb_message_element *el1, *el2;
 
107
        struct ldb_context *ldb;
 
108
 
 
109
        ldb = ldb_module_get_ctx(ac->module);
 
110
 
 
111
        if (ac->sort_result != 0) {
 
112
                /* an error occurred previously,
 
113
                 * let's exit the sorting by returning always 0 */
 
114
                return 0;
 
115
        }
 
116
 
 
117
        el1 = ldb_msg_find_element(*msg1, ac->attributeName);
 
118
        el2 = ldb_msg_find_element(*msg2, ac->attributeName);
 
119
 
 
120
        if (!el1 || !el2) {
 
121
                /* the attribute was not found return and
 
122
                 * set an error */
 
123
                ac->sort_result = LDB_ERR_UNWILLING_TO_PERFORM;
 
124
                return 0;
 
125
        }
 
126
 
 
127
        if (ac->reverse)
 
128
                return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
 
129
 
 
130
        return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
 
131
}
 
132
 
 
133
static int server_sort_results(struct sort_context *ac)
 
134
{
 
135
        struct ldb_context *ldb;
 
136
        struct ldb_reply *ares;
 
137
        int i, ret;
 
138
 
 
139
        ldb = ldb_module_get_ctx(ac->module);
 
140
 
 
141
        ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
 
142
        ac->sort_result = 0;
 
143
 
 
144
        ldb_qsort(ac->msgs, ac->num_msgs,
 
145
                  sizeof(struct ldb_message *),
 
146
                  ac, (ldb_qsort_cmp_fn_t)sort_compare);
 
147
 
 
148
        if (ac->sort_result != LDB_SUCCESS) {
 
149
                return ac->sort_result;
 
150
        }
 
151
 
 
152
        for (i = 0; i < ac->num_msgs; i++) {
 
153
                ares = talloc_zero(ac, struct ldb_reply);
 
154
                if (!ares) {
 
155
                        return LDB_ERR_OPERATIONS_ERROR;
 
156
                }
 
157
 
 
158
                ares->type = LDB_REPLY_ENTRY;
 
159
                ares->message = talloc_move(ares, &ac->msgs[i]);
 
160
 
 
161
                ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
 
162
                if (ret != LDB_SUCCESS) {
 
163
                        return ret;
 
164
                }
 
165
        }
 
166
 
 
167
        for (i = 0; i < ac->num_refs; i++) {
 
168
                ares = talloc_zero(ac, struct ldb_reply);
 
169
                if (!ares) {
 
170
                        return LDB_ERR_OPERATIONS_ERROR;
 
171
                }
 
172
 
 
173
                ares->type = LDB_REPLY_REFERRAL;
 
174
                ares->referral = talloc_move(ares, &ac->referrals[i]);
 
175
 
 
176
                ret = ldb_module_send_referral(ac->req, ares->referral);
 
177
                if (ret != LDB_SUCCESS) {
 
178
                        return ret;
 
179
                }
 
180
        }
 
181
 
 
182
        return LDB_SUCCESS;
 
183
}
 
184
 
 
185
static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 
186
{
 
187
        struct sort_context *ac;
 
188
        struct ldb_context *ldb;
 
189
        int ret;
 
190
 
 
191
        ac = talloc_get_type(req->context, struct sort_context);
 
192
        ldb = ldb_module_get_ctx(ac->module);
 
193
 
 
194
        if (!ares) {
 
195
                return ldb_module_done(ac->req, NULL, NULL,
 
196
                                        LDB_ERR_OPERATIONS_ERROR);
 
197
        }
 
198
        if (ares->error != LDB_SUCCESS) {
 
199
                return ldb_module_done(ac->req, ares->controls,
 
200
                                        ares->response, ares->error);
 
201
        }
 
202
 
 
203
        switch (ares->type) {
 
204
        case LDB_REPLY_ENTRY:
 
205
                ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
 
206
                if (! ac->msgs) {
 
207
                        talloc_free(ares);
 
208
                        ldb_oom(ldb);
 
209
                        return ldb_module_done(ac->req, NULL, NULL,
 
210
                                                LDB_ERR_OPERATIONS_ERROR);
 
211
                }
 
212
 
 
213
                ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
 
214
                ac->num_msgs++;
 
215
                ac->msgs[ac->num_msgs] = NULL;
 
216
 
 
217
                break;
 
218
 
 
219
        case LDB_REPLY_REFERRAL:
 
220
                ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
 
221
                if (! ac->referrals) {
 
222
                        talloc_free(ares);
 
223
                        ldb_oom(ldb);
 
224
                        return ldb_module_done(ac->req, NULL, NULL,
 
225
                                                LDB_ERR_OPERATIONS_ERROR);
 
226
                }
 
227
 
 
228
                ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
 
229
                ac->num_refs++;
 
230
                ac->referrals[ac->num_refs] = NULL;
 
231
 
 
232
                break;
 
233
 
 
234
        case LDB_REPLY_DONE:
 
235
 
 
236
                ret = server_sort_results(ac);
 
237
                return ldb_module_done(ac->req, ares->controls,
 
238
                                        ares->response, ret);
 
239
        }
 
240
 
 
241
        talloc_free(ares);
 
242
        return LDB_SUCCESS;
 
243
}
 
244
 
 
245
static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
 
246
{
 
247
        struct ldb_control *control;
 
248
        struct ldb_server_sort_control **sort_ctrls;
 
249
        struct ldb_control **saved_controls;
 
250
        struct ldb_control **controls;
 
251
        struct ldb_request *down_req;
 
252
        struct sort_context *ac;
 
253
        struct ldb_context *ldb;
 
254
        int ret;
 
255
 
 
256
        ldb = ldb_module_get_ctx(module);
 
257
 
 
258
        /* check if there's a paged request control */
 
259
        control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
 
260
        if (control == NULL) {
 
261
                /* not found go on */
 
262
                return ldb_next_request(module, req);
 
263
        }
 
264
 
 
265
        ac = talloc_zero(req, struct sort_context);
 
266
        if (ac == NULL) {
 
267
                ldb_oom(ldb);
 
268
                return LDB_ERR_OPERATIONS_ERROR;
 
269
        }
 
270
 
 
271
        ac->module = module;
 
272
        ac->req = req;
 
273
 
 
274
        sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
 
275
        if (!sort_ctrls) {
 
276
                return LDB_ERR_PROTOCOL_ERROR;
 
277
        }
 
278
 
 
279
        /* FIXME: we do not support more than one attribute for sorting right now */
 
280
        /* FIXME: we need to check if the attribute type exist or return an error */
 
281
                
 
282
        if (sort_ctrls[1] != NULL) {
 
283
                if (control->critical) {
 
284
 
 
285
                        /* callback immediately */
 
286
                        ret = build_response(req, &controls,
 
287
                                             LDB_ERR_UNWILLING_TO_PERFORM,
 
288
                                             "sort control is not complete yet");
 
289
                        if (ret != LDB_SUCCESS) {
 
290
                                return ldb_module_done(req, NULL, NULL,
 
291
                                                    LDB_ERR_OPERATIONS_ERROR);
 
292
                        }
 
293
 
 
294
                        return ldb_module_done(req, controls, NULL, ret);
 
295
                } else {
 
296
                        /* just pass the call down and don't do any sorting */
 
297
                        return ldb_next_request(module, req);
 
298
                }
 
299
        }
 
300
 
 
301
        ac->attributeName = sort_ctrls[0]->attributeName;
 
302
        ac->orderingRule = sort_ctrls[0]->orderingRule;
 
303
        ac->reverse = sort_ctrls[0]->reverse;
 
304
 
 
305
        ret = ldb_build_search_req_ex(&down_req, ldb, ac,
 
306
                                        req->op.search.base,
 
307
                                        req->op.search.scope,
 
308
                                        req->op.search.tree,
 
309
                                        req->op.search.attrs,
 
310
                                        req->controls,
 
311
                                        ac,
 
312
                                        server_sort_search_callback,
 
313
                                        req);
 
314
        if (ret != LDB_SUCCESS) {
 
315
                return LDB_ERR_OPERATIONS_ERROR;
 
316
        }
 
317
 
 
318
        /* save it locally and remove it from the list */
 
319
        /* we do not need to replace them later as we
 
320
         * are keeping the original req intact */
 
321
        if (!save_controls(control, down_req, &saved_controls)) {
 
322
                return LDB_ERR_OPERATIONS_ERROR;
 
323
        }
 
324
 
 
325
        return ldb_next_request(module, down_req);
 
326
}
 
327
 
 
328
static int server_sort_init(struct ldb_module *module)
 
329
{
 
330
        struct ldb_context *ldb;
 
331
        int ret;
 
332
 
 
333
        ldb = ldb_module_get_ctx(module);
 
334
 
 
335
        ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
 
336
        if (ret != LDB_SUCCESS) {
 
337
                ldb_debug(ldb, LDB_DEBUG_WARNING,
 
338
                        "server_sort:"
 
339
                        "Unable to register control with rootdse!\n");
 
340
        }
 
341
 
 
342
        return ldb_next_init(module);
 
343
}
 
344
 
 
345
const struct ldb_module_ops ldb_server_sort_module_ops = {
 
346
        .name              = "server_sort",
 
347
        .search            = server_sort_search,
 
348
        .init_context      = server_sort_init
 
349
};