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 36struct 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 44struct 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 61static 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 103static 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 return 1; 122 } 123 if (el1 && !el2) { 124 return -1; 125 } 126 if (!el1 && !el2) { 127 return 0; 128 } 129 130 if (ac->reverse) 131 return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]); 132 133 return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]); 134} 135 136static int server_sort_results(struct sort_context *ac) 137{ 138 struct ldb_context *ldb; 139 struct ldb_reply *ares; 140 int i, ret; 141 142 ldb = ldb_module_get_ctx(ac->module); 143 144 ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName); 145 ac->sort_result = 0; 146 147 ldb_qsort(ac->msgs, ac->num_msgs, 148 sizeof(struct ldb_message *), 149 ac, (ldb_qsort_cmp_fn_t)sort_compare); 150 151 if (ac->sort_result != LDB_SUCCESS) { 152 return ac->sort_result; 153 } 154 155 for (i = 0; i < ac->num_msgs; i++) { 156 ares = talloc_zero(ac, struct ldb_reply); 157 if (!ares) { 158 return LDB_ERR_OPERATIONS_ERROR; 159 } 160 161 ares->type = LDB_REPLY_ENTRY; 162 ares->message = talloc_move(ares, &ac->msgs[i]); 163 164 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls); 165 if (ret != LDB_SUCCESS) { 166 return ret; 167 } 168 } 169 170 for (i = 0; i < ac->num_refs; i++) { 171 ares = talloc_zero(ac, struct ldb_reply); 172 if (!ares) { 173 return LDB_ERR_OPERATIONS_ERROR; 174 } 175 176 ares->type = LDB_REPLY_REFERRAL; 177 ares->referral = talloc_move(ares, &ac->referrals[i]); 178 179 ret = ldb_module_send_referral(ac->req, ares->referral); 180 if (ret != LDB_SUCCESS) { 181 return ret; 182 } 183 } 184 185 return LDB_SUCCESS; 186} 187 188static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares) 189{ 190 struct sort_context *ac; 191 struct ldb_context *ldb; 192 int ret; 193 194 ac = talloc_get_type(req->context, struct sort_context); 195 ldb = ldb_module_get_ctx(ac->module); 196 197 if (!ares) { 198 return ldb_module_done(ac->req, NULL, NULL, 199 LDB_ERR_OPERATIONS_ERROR); 200 } 201 if (ares->error != LDB_SUCCESS) { 202 return ldb_module_done(ac->req, ares->controls, 203 ares->response, ares->error); 204 } 205 206 switch (ares->type) { 207 case LDB_REPLY_ENTRY: 208 ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2); 209 if (! ac->msgs) { 210 talloc_free(ares); 211 ldb_oom(ldb); 212 return ldb_module_done(ac->req, NULL, NULL, 213 LDB_ERR_OPERATIONS_ERROR); 214 } 215 216 ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message); 217 ac->num_msgs++; 218 ac->msgs[ac->num_msgs] = NULL; 219 220 break; 221 222 case LDB_REPLY_REFERRAL: 223 ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2); 224 if (! ac->referrals) { 225 talloc_free(ares); 226 ldb_oom(ldb); 227 return ldb_module_done(ac->req, NULL, NULL, 228 LDB_ERR_OPERATIONS_ERROR); 229 } 230 231 ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral); 232 ac->num_refs++; 233 ac->referrals[ac->num_refs] = NULL; 234 235 break; 236 237 case LDB_REPLY_DONE: 238 239 ret = server_sort_results(ac); 240 return ldb_module_done(ac->req, ares->controls, 241 ares->response, ret); 242 } 243 244 talloc_free(ares); 245 return LDB_SUCCESS; 246} 247 248static int server_sort_search(struct ldb_module *module, struct ldb_request *req) 249{ 250 struct ldb_control *control; 251 struct ldb_server_sort_control **sort_ctrls; 252 struct ldb_control **saved_controls; 253 struct ldb_control **controls; 254 struct ldb_request *down_req; 255 struct sort_context *ac; 256 struct ldb_context *ldb; 257 int ret; 258 259 ldb = ldb_module_get_ctx(module); 260 261 /* check if there's a server sort control */ 262 control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID); 263 if (control == NULL) { 264 /* not found go on */ 265 return ldb_next_request(module, req); 266 } 267 268 ac = talloc_zero(req, struct sort_context); 269 if (ac == NULL) { 270 ldb_oom(ldb); 271 return LDB_ERR_OPERATIONS_ERROR; 272 } 273 274 ac->module = module; 275 ac->req = req; 276 277 sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *); 278 if (!sort_ctrls) { 279 return LDB_ERR_PROTOCOL_ERROR; 280 } 281 282 /* FIXME: we do not support more than one attribute for sorting right now */ 283 /* FIXME: we need to check if the attribute type exist or return an error */ 284 285 if (sort_ctrls[1] != NULL) { 286 if (control->critical) { 287 288 /* callback immediately */ 289 ret = build_response(req, &controls, 290 LDB_ERR_UNWILLING_TO_PERFORM, 291 "sort control is not complete yet"); 292 if (ret != LDB_SUCCESS) { 293 return ldb_module_done(req, NULL, NULL, 294 LDB_ERR_OPERATIONS_ERROR); 295 } 296 297 return ldb_module_done(req, controls, NULL, ret); 298 } else { 299 /* just pass the call down and don't do any sorting */ 300 return ldb_next_request(module, req); 301 } 302 } 303 304 ac->attributeName = sort_ctrls[0]->attributeName; 305 ac->orderingRule = sort_ctrls[0]->orderingRule; 306 ac->reverse = sort_ctrls[0]->reverse; 307 308 ret = ldb_build_search_req_ex(&down_req, ldb, ac, 309 req->op.search.base, 310 req->op.search.scope, 311 req->op.search.tree, 312 req->op.search.attrs, 313 req->controls, 314 ac, 315 server_sort_search_callback, 316 req); 317 if (ret != LDB_SUCCESS) { 318 return LDB_ERR_OPERATIONS_ERROR; 319 } 320 321 /* save it locally and remove it from the list */ 322 /* we do not need to replace them later as we 323 * are keeping the original req intact */ 324 if (!save_controls(control, down_req, &saved_controls)) { 325 return LDB_ERR_OPERATIONS_ERROR; 326 } 327 328 return ldb_next_request(module, down_req); 329} 330 331static int server_sort_init(struct ldb_module *module) 332{ 333 struct ldb_context *ldb; 334 int ret; 335 336 ldb = ldb_module_get_ctx(module); 337 338 ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID); 339 if (ret != LDB_SUCCESS) { 340 ldb_debug(ldb, LDB_DEBUG_WARNING, 341 "server_sort:" 342 "Unable to register control with rootdse!"); 343 } 344 345 return ldb_next_init(module); 346} 347 348const struct ldb_module_ops ldb_server_sort_module_ops = { 349 .name = "server_sort", 350 .search = server_sort_search, 351 .init_context = server_sort_init 352}; 353