]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/sssvlv.c
Add sssvlv to build system
[openldap] / servers / slapd / overlays / sssvlv.c
1 /* sssvlv.c - server side sort / virtual list view */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2009 The OpenLDAP Foundation.
6  * Portions copyright 2009 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Howard Chu for inclusion in
19  * OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_SSSVLV
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/ctype.h>
30
31 #include <avl.h>
32
33 #include "slap.h"
34 #include "lutil.h"
35
36 /* RFC2891: Server Side Sorting
37  * RFC2696: Paged Results
38  */
39 #ifndef LDAP_MATCHRULE_IDENTIFIER
40 #define LDAP_MATCHRULE_IDENTIFIER      0x80L
41 #define LDAP_REVERSEORDER_IDENTIFIER   0x81L
42 #define LDAP_ATTRTYPES_IDENTIFIER      0x80L
43 #endif
44
45 /* draft-ietf-ldapext-ldapv3-vlv-09.txt: Virtual List Views
46  */
47 #ifndef LDAP_VLVBYINDEX_IDENTIFIER
48 #define LDAP_VLVBYINDEX_IDENTIFIER         0xa0L
49 #define LDAP_VLVBYVALUE_IDENTIFIER     0x81L
50 #define LDAP_VLVCONTEXT_IDENTIFIER     0x04L
51
52 #define LDAP_VLV_SSS_MISSING    0x4C
53 #define LDAP_VLV_RANGE_ERROR    0x4D
54 #endif
55
56 #define SAFESTR(macro_str, macro_def) ((macro_str) ? (macro_str) : (macro_def))
57
58 typedef struct vlv_ctrl {
59         int vc_before;
60         int vc_after;
61         int     vc_offset;
62         int vc_count;
63         struct berval vc_value;
64         unsigned long vc_context;
65 } vlv_ctrl;
66
67 typedef struct sort_key
68 {
69         AttributeDescription    *sk_ad;
70         MatchingRule                    *sk_ordering;
71         int                                             sk_direction;   /* 1=normal, -1=reverse */
72 } sort_key;
73
74 typedef struct sort_ctrl {
75         int sc_nkeys;
76         sort_key sc_keys[0];
77 } sort_ctrl;
78
79
80 typedef struct sort_node
81 {
82         int sn_conn;
83         struct berval sn_dn;
84         struct berval *sn_vals;
85 } sort_node;
86
87 typedef struct sssvlv_info
88 {
89         int svi_max;    /* max concurrent sorts */
90         int svi_num;    /* current # sorts */
91         int svi_max_keys;       /* max sort keys per request */
92 } sssvlv_info;
93
94 typedef struct sort_op
95 {
96         Avlnode *so_tree;
97         sort_ctrl *so_ctrl;
98         sssvlv_info *so_info;
99         int so_paged;
100         int so_page_size;
101         int so_nentries;
102         int so_vlv;
103         int so_vlv_rc;
104         int so_vlv_target;
105         unsigned long so_vcontext;
106 } sort_op;
107
108 /* There is only one conn table for all overlay instances */
109 static sort_op **sort_conns;
110 static ldap_pvt_thread_mutex_t sort_conns_mutex;
111 static const char *debug_header = "sssvlv";
112
113 static int sss_cid;
114 static int vlv_cid;
115
116 /* RFC 2981 Section 2.2
117  * If a sort key is a multi-valued attribute, and an entry happens to
118  * have multiple values for that attribute and no other controls are
119  * present that affect the sorting order, then the server SHOULD use the
120  * least value (according to the ORDERING rule for that attribute).
121  */
122 static struct berval* select_value(
123         Attribute               *attr,
124         sort_key                        *key )
125 {
126         struct berval* ber1, *ber2;
127         MatchingRule *mr = key->sk_ordering;
128         int i, cmp;
129
130         ber1 = &(attr->a_nvals[0]);
131         ber2 = ber1+1;
132         for ( i = 1; i < attr->a_numvals; i++,ber2++ ) {
133                 mr->smr_match( &cmp, 0, mr->smr_syntax, mr, ber1, ber2 );
134                 if ( cmp > 0 ) {
135                         ber1 = ber2;
136                 }
137         }
138
139         Debug(LDAP_DEBUG_TRACE, "%s: value selected for compare: %s\n",
140                 debug_header,
141                 SAFESTR(ber1->bv_val, "<Empty>"),
142                 0);
143
144         return ber1;
145 }
146
147 static int node_cmp( const void* val1, const void* val2 )
148 {
149         sort_node *sn1 = (sort_node *)val1;
150         sort_node *sn2 = (sort_node *)val2;
151         sort_ctrl *sc = sort_conns[sn1->sn_conn]->so_ctrl;
152         MatchingRule *mr;
153         int i, cmp = 0;
154
155         for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) {
156                 if ( BER_BVISNULL( &sn1->sn_vals[i] )) {
157                         if ( BER_BVISNULL( &sn2->sn_vals[i] ))
158                                 cmp = 0;
159                         else
160                                 cmp = sc->sc_keys[i].sk_direction;
161                 } else if ( BER_BVISNULL( &sn2->sn_vals[i] )) {
162                         cmp = sc->sc_keys[i].sk_direction * -1;
163                 } else {
164                         mr = sc->sc_keys[i].sk_ordering;
165                         mr->smr_match( &cmp, 0, mr->smr_syntax, mr,
166                                 &sn1->sn_vals[i], &sn2->sn_vals[i] );
167                         if ( cmp )
168                                 cmp *= sc->sc_keys[i].sk_direction;
169                 }
170         }
171         return cmp;
172 }
173
174 static int node_insert( const void *val1, const void *val2 )
175 {
176         /* Never return equal so that new entries are always inserted */
177         return node_cmp( val1, val2 ) < 0 ? -1 : 1;
178 }
179
180 static int pack_vlv_response_control(
181         Operation               *op,
182         SlapReply               *rs,
183         sort_op                 *so,
184         LDAPControl     **ctrlsp )
185 {
186         LDAPControl                     *ctrl;
187         BerElementBuffer        berbuf;
188         BerElement                      *ber            = (BerElement *)&berbuf;
189         PagedResultsCookie      resp_cookie;
190         struct berval           cookie, bv;
191         int                                     rc;
192
193         ber_init2( ber, NULL, LBER_USE_DER );
194         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
195
196         rc = ber_printf( ber, "{iii", so->so_vlv_target, so->so_nentries,
197                 so->so_vlv_rc );
198
199         if ( rc != -1 && so->so_vcontext ) {
200                 cookie.bv_val = (char *)&so->so_vcontext;
201                 cookie.bv_len = sizeof(so->so_vcontext);
202                 rc = ber_printf( ber, "tO", LDAP_VLVCONTEXT_IDENTIFIER, &cookie );
203         }
204
205         if ( rc != -1 ) {
206                 rc = ber_printf( ber, "}" );
207         }
208
209         if ( rc != -1 ) {
210                 rc = ber_flatten2( ber, &bv, 0 );
211         }
212
213         if ( rc != -1 ) {
214                 ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
215                         bv.bv_len, op->o_tmpmemctx );
216                 ctrl->ldctl_oid                 = LDAP_CONTROL_VLVRESPONSE;
217                 ctrl->ldctl_iscritical  = 0;
218                 ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
219                 ctrl->ldctl_value.bv_len = bv.bv_len;
220                 AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
221                 ctrlsp[0] = ctrl;
222         } else {
223                 ctrlsp[0] = NULL;
224                 rs->sr_err = LDAP_OTHER;
225         }
226
227         ber_free_buf( ber );
228
229         return rs->sr_err;
230 }
231
232 static int pack_pagedresult_response_control(
233         Operation               *op,
234         SlapReply               *rs,
235         sort_op                 *so,
236         LDAPControl     **ctrlsp )
237 {
238         LDAPControl                     *ctrl;
239         BerElementBuffer        berbuf;
240         BerElement                      *ber            = (BerElement *)&berbuf;
241         PagedResultsCookie      resp_cookie;
242         struct berval           cookie, bv;
243         int                                     rc;
244
245         ber_init2( ber, NULL, LBER_USE_DER );
246         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
247
248         if ( so->so_nentries > 0 ) {
249                 resp_cookie             = ( PagedResultsCookie )so->so_tree;
250                 cookie.bv_len   = sizeof( PagedResultsCookie );
251                 cookie.bv_val   = (char *)&resp_cookie;
252         } else {
253                 resp_cookie             = ( PagedResultsCookie )0;
254                 BER_BVZERO( &cookie );
255         }
256
257         op->o_conn->c_pagedresults_state.ps_cookie = resp_cookie;
258         op->o_conn->c_pagedresults_state.ps_count
259                 = ((PagedResultsState *)op->o_pagedresults_state)->ps_count
260                   + rs->sr_nentries;
261
262         rc = ber_printf( ber, "{iO}", so->so_nentries, &cookie );
263         if ( rc != -1 ) {
264                 rc = ber_flatten2( ber, &bv, 0 );
265         }
266
267         if ( rc != -1 ) {
268                 ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
269                         bv.bv_len, op->o_tmpmemctx );
270                 ctrl->ldctl_oid                 = LDAP_CONTROL_PAGEDRESULTS;
271                 ctrl->ldctl_iscritical  = 0;
272                 ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
273                 ctrl->ldctl_value.bv_len = bv.bv_len;
274                 AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
275                 ctrlsp[0] = ctrl;
276         } else {
277                 ctrlsp[0] = NULL;
278                 rs->sr_err = LDAP_OTHER;
279         }
280
281         ber_free_buf( ber );
282
283         return rs->sr_err;
284 }
285
286 static int pack_sss_response_control(
287         Operation               *op,
288         SlapReply               *rs,
289         LDAPControl     **ctrlsp )
290 {
291         LDAPControl                     *ctrl;
292         BerElementBuffer        berbuf;
293         BerElement                      *ber            = (BerElement *)&berbuf;
294         struct berval           bv;
295         int                                     rc;
296
297         ber_init2( ber, NULL, LBER_USE_DER );
298         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
299
300         /* Pack error code */
301         rc = ber_printf(ber, "{e}", rs->sr_err);
302
303         if ( rc != -1)
304                 rc = ber_flatten2( ber, &bv, 0 );
305
306         if ( rc != -1 ) {
307                 ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
308                         bv.bv_len, op->o_tmpmemctx );
309                 ctrl->ldctl_oid                 = LDAP_CONTROL_SORTRESPONSE;
310                 ctrl->ldctl_iscritical  = 0;
311                 ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
312                 ctrl->ldctl_value.bv_len = bv.bv_len;
313                 AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
314                 ctrlsp[0] = ctrl;
315         } else {
316                 ctrlsp[0] = NULL;
317                 rs->sr_err = LDAP_OTHER;
318         }
319
320         ber_free_buf( ber );
321
322         return rs->sr_err;
323 }
324
325 static void free_sort_op( Connection *conn, sort_op *so )
326 {
327         if ( so->so_tree ) {
328                 tavl_free( so->so_tree, ch_free );
329                 so->so_tree = NULL;
330         }
331
332         ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
333         sort_conns[conn->c_conn_idx] = NULL;
334         so->so_info->svi_num--;
335         ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
336
337         ch_free( so );
338 }
339
340 static int send_list(
341         Operation               *op,
342         SlapReply               *rs,
343         sort_op                 *so)
344 {
345         Avlnode *cur_node, *tmp_node;
346         vlv_ctrl *vc = op->o_controls[vlv_cid];
347         int i, j, dir, rc;
348         BackendDB *be;
349         Entry *e;
350         LDAPControl *ctrls[2];
351
352         /* FIXME: it may be better to just flatten the tree into
353          * an array before doing all of this...
354          */
355
356         /* Are we just counting an offset? */
357         if ( BER_BVISNULL( &vc->vc_value )) {
358                 if ( vc->vc_offset == vc->vc_count ) {
359                         /* wants the last entry in the list */
360                         cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
361                         so->so_vlv_target = so->so_nentries;
362                 } else if ( vc->vc_offset == 1 ) {
363                         /* wants the first entry in the list */
364                         cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
365                         so->so_vlv_target = 1;
366                 } else {
367                         int target;
368                         /* Just iterate to the right spot */
369                         if ( vc->vc_count && vc->vc_count != so->so_nentries ) {
370                                 if ( vc->vc_offset > vc->vc_count )
371                                         goto range_err;
372                                 target = so->so_nentries * vc->vc_offset / vc->vc_count;
373                         } else {
374                                 if ( vc->vc_offset > so->so_nentries ) {
375 range_err:
376                                         so->so_vlv_rc = LDAP_VLV_RANGE_ERROR;
377                                         pack_vlv_response_control( op, rs, so, ctrls );
378                                         ctrls[1] = NULL;
379                                         slap_add_ctrls( op, rs, ctrls );
380                                         rs->sr_err = LDAP_VLV_ERROR;
381                                         return;
382                                 }
383                                 target = vc->vc_offset;
384                         }
385                         so->so_vlv_target = target;
386                         /* Start at left and go right, or start at right and go left? */
387                         if ( target < so->so_nentries / 2 ) {
388                                 cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
389                                 dir = TAVL_DIR_RIGHT;
390                         } else {
391                                 cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
392                                 dir = TAVL_DIR_LEFT;
393                                 target = so->so_nentries - target + 1;
394                         }
395                         for ( i=1; i<target; i++ )
396                                 cur_node = tavl_next( cur_node, dir );
397                 }
398         } else {
399         /* we're looking for a specific value */
400                 sort_ctrl *sc = so->so_ctrl;
401                 MatchingRule *mr = sc->sc_keys[0].sk_ordering;
402                 sort_node *sn;
403                 struct berval bv;
404
405                 if ( mr->smr_normalize ) {
406                         rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX,
407                                 mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx );
408                         if ( rc ) {
409                                 so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING;
410                                 pack_vlv_response_control( op, rs, so, ctrls );
411                                 ctrls[1] = NULL;
412                                 slap_add_ctrls( op, rs, ctrls );
413                                 rs->sr_err = LDAP_VLV_ERROR;
414                                 return;
415                         }
416                 } else {
417                         bv = vc->vc_value;
418                 }
419
420                 sn = op->o_tmpalloc( sizeof(sort_node) +
421                         sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
422                 sn->sn_vals = (struct berval *)(sn+1);
423                 sn->sn_conn = op->o_conn->c_conn_idx;
424                 sn->sn_vals[0] = bv;
425                 for (i=1; i<sc->sc_nkeys; i++) {
426                         BER_BVZERO( &sn->sn_vals[i] );
427                 }
428                 cur_node = tavl_find2( so->so_tree, sn, node_cmp );
429                 op->o_tmpfree( sn, op->o_tmpmemctx );
430
431                 if ( !cur_node ) {
432                         so->so_vlv_target = so->so_nentries + 1;
433                 } else {
434                         sort_node *sn = so->so_tree->avl_data;
435                         /* start from the left or the right side? */
436                         mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] );
437                         if ( i > 0 ) {
438                                 tmp_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
439                                 dir = TAVL_DIR_LEFT;
440                         } else {
441                                 tmp_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
442                                 dir = TAVL_DIR_RIGHT;
443                         }
444                         for (i=0; tmp_node != cur_node;
445                                 tmp_node = tavl_next( tmp_node, dir ), i++);
446                         so->so_vlv_target = i;
447                 }
448                 if ( bv.bv_val != vc->vc_value.bv_val )
449                         op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
450         }
451         if ( !cur_node ) {
452                 i = 1;
453                 cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
454         } else {
455                 i = 0;
456         }
457         for ( ; i<vc->vc_before; i++ ) {
458                 tmp_node = tavl_next( cur_node, TAVL_DIR_LEFT );
459                 if ( !tmp_node ) break;
460                 cur_node = tmp_node;
461         }
462         j = i + vc->vc_after + 1;
463         be = op->o_bd;
464         for ( i=0; i<j; i++ ) {
465                 sort_node *sn = cur_node->avl_data;
466                 
467                 op->o_bd = select_backend( &sn->sn_dn, 0 );
468                 e = NULL;
469                 rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
470
471                 if ( e && rc == LDAP_SUCCESS ) {
472                         rs->sr_entry = e;
473                         rs->sr_flags = REP_ENTRY_MUSTRELEASE;
474                         rs->sr_err = send_search_entry( op, rs );
475                         if ( rs->sr_err == LDAP_UNAVAILABLE )
476                                 break;
477                 }
478                 cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
479                 if ( !cur_node ) break;
480         }
481         so->so_vlv_rc = LDAP_SUCCESS;
482
483         op->o_bd = be;
484 }
485
486 static void send_page( Operation *op, SlapReply *rs, sort_op *so )
487 {
488         Avlnode         *cur_node               = so->so_tree;
489         Avlnode         *next_node              = NULL;
490         BackendDB *be = op->o_bd;
491         sort_node *sn;
492         Entry *e;
493         int rc;
494
495         while ( cur_node && rs->sr_nentries < so->so_page_size ) {
496                 sort_node *sn = cur_node->avl_data;
497
498                 next_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
499
500                 op->o_bd = select_backend( &sn->sn_dn, 0 );
501                 e = NULL;
502                 rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
503
504                 ch_free( cur_node->avl_data );
505                 ber_memfree( cur_node );
506
507                 cur_node = next_node;
508                 so->so_nentries--;
509
510                 if ( e && rc == LDAP_SUCCESS ) {
511                         rs->sr_entry = e;
512                         rs->sr_flags = REP_ENTRY_MUSTRELEASE;
513                         rs->sr_err = send_search_entry( op, rs );
514                         if ( rs->sr_err == LDAP_UNAVAILABLE )
515                                 break;
516                 }
517         }
518
519         /* Set the first entry to send for the next page */
520         so->so_tree = next_node;
521
522         op->o_bd = be;
523 }
524
525 static void send_entry(
526         Operation               *op,
527         SlapReply               *rs,
528         sort_op                 *so)
529 {
530         Debug(LDAP_DEBUG_TRACE,
531                 "%s: response control: status=%d, text=%s\n",
532                 debug_header, rs->sr_err, SAFESTR(rs->sr_text, "<None>"));
533
534         /* RFC 2891: If critical then send the entries iff they were
535          * succesfully sorted.  If non-critical send all entries
536          * whether they were sorted or not.
537          */
538         if ( (op->o_ctrlflag[sss_cid] != SLAP_CONTROL_CRITICAL) ||
539                  (rs->sr_err == LDAP_SUCCESS) )
540         {
541                 if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
542                         send_list( op, rs, so );
543                 } else {
544                         /* Get the first node to send */
545                         Avlnode *start_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
546                         so->so_tree = start_node;
547
548                         if ( so->so_paged <= SLAP_CONTROL_IGNORED ) {
549                                 /* Not paged result search.  Send all entries.
550                                  * Set the page size to the number of entries
551                                  * so that send_page() will send all entries.
552                                  */
553                                 so->so_page_size = so->so_nentries;
554                         }
555
556                         send_page( op, rs, so );
557                 }
558         }
559 }
560
561 static void send_result(
562         Operation               *op,
563         SlapReply               *rs,
564         sort_op                 *so)
565 {
566         LDAPControl *ctrls[3];
567         int rc, i = 0;
568
569         rc = pack_sss_response_control( op, rs, ctrls );
570         if ( rc == LDAP_SUCCESS ) {
571                 i++;
572                 rc = -1;
573                 if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
574                         rc = pack_pagedresult_response_control( op, rs, so, ctrls+1 );
575                 } else if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
576                         rc = pack_vlv_response_control( op, rs, so, ctrls+1 );
577                 }
578                 if ( rc == LDAP_SUCCESS )
579                         i++;
580         }
581         ctrls[i] = NULL;
582
583         if ( ctrls[0] != NULL )
584                 slap_add_ctrls( op, rs, ctrls );
585         send_ldap_result( op, rs );
586
587         if ( so->so_tree == NULL ) {
588                 /* Search finished, so clean up */
589                 free_sort_op( op->o_conn, so );
590         }
591 }
592
593 static int sssvlv_op_response(
594         Operation       *op,
595         SlapReply       *rs )
596 {
597         sort_ctrl *sc = op->o_controls[sss_cid];
598         sort_op *so = op->o_callback->sc_private;
599
600         if ( rs->sr_type == REP_SEARCH ) {
601                 int i;
602                 size_t len;
603                 sort_node *sn, *sn2;
604                 struct berval *bv;
605                 char *ptr;
606
607                 len = sizeof(sort_node) + sc->sc_nkeys * sizeof(struct berval) +
608                         rs->sr_entry->e_nname.bv_len + 1;
609                 sn = op->o_tmpalloc( len, op->o_tmpmemctx );
610                 sn->sn_vals = (struct berval *)(sn+1);
611
612                 /* Build tmp list of key values */
613                 for ( i=0; i<sc->sc_nkeys; i++ ) {
614                         Attribute *a = attr_find( rs->sr_entry->e_attrs,
615                                 sc->sc_keys[i].sk_ad );
616                         if ( a ) {
617                                 if ( a->a_numvals > 1 ) {
618                                         bv = select_value( a, &sc->sc_keys[i] );
619                                 } else {
620                                         bv = a->a_nvals;
621                                 }
622                                 sn->sn_vals[i] = *bv;
623                                 len += bv->bv_len + 1;
624                         } else {
625                                 BER_BVZERO( &sn->sn_vals[i] );
626                         }
627                 }
628
629                 /* Now dup into regular memory */
630                 sn2 = ch_malloc( len );
631                 sn2->sn_vals = (struct berval *)(sn2+1);
632                 AC_MEMCPY( sn2->sn_vals, sn->sn_vals,
633                                 sc->sc_nkeys * sizeof(struct berval));
634                 sn = sn2;
635
636                 ptr = (char *)(sn->sn_vals + sc->sc_nkeys);
637                 sn->sn_dn.bv_val = ptr;
638                 sn->sn_dn.bv_len = rs->sr_entry->e_nname.bv_len;
639                 AC_MEMCPY( ptr, rs->sr_entry->e_nname.bv_val,
640                         rs->sr_entry->e_nname.bv_len );
641                 ptr += rs->sr_entry->e_nname.bv_len;
642                 *ptr++ = '\0';
643                 for ( i=0; i<sc->sc_nkeys; i++ ) {
644                         if ( !BER_BVISNULL( &sn->sn_vals[i] )) {
645                                 AC_MEMCPY( ptr, sn->sn_vals[i].bv_val, sn->sn_vals[i].bv_len );
646                                 sn->sn_vals[i].bv_val = ptr;
647                                 ptr += sn->sn_vals[i].bv_len;
648                                 *ptr++ = '\0';
649                         }
650                 }
651                 sn->sn_conn = op->o_conn->c_conn_idx;
652
653                 /* Insert into the AVL tree */
654                 tavl_insert(&(so->so_tree), sn, node_insert, avl_dup_error);
655
656                 so->so_nentries++;
657
658                 /* Collected the keys so that they can be sorted.  Thus, stop
659                  * the entry from propagating.
660                  */
661                 rs->sr_err = LDAP_SUCCESS;
662         }
663         else if ( rs->sr_type == REP_RESULT ) {
664                 /* Remove serversort response callback.
665                  * We don't want the entries that we are about to send to be
666                  * processed by serversort response again.
667                  */
668                 if ( op->o_callback->sc_response == sssvlv_op_response ) {
669                         op->o_callback = op->o_callback->sc_next;
670                 }
671
672                 send_entry( op, rs, so );
673                 send_result( op, rs, so );
674         }
675
676         return rs->sr_err;
677 }
678
679 static int sssvlv_op_search(
680         Operation               *op,
681         SlapReply               *rs)
682 {
683         slap_overinst                   *on                     = (slap_overinst *)op->o_bd->bd_info;
684         sssvlv_info                             *si                     = on->on_bi.bi_private;
685         int                                             rc                      = SLAP_CB_CONTINUE;
686         int     ok;
687         sort_op *so, so2;
688         sort_ctrl *sc = op->o_controls[sss_cid];
689         PagedResultsState *ps;
690         vlv_ctrl *vc;
691
692         if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
693                 if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
694                         LDAPControl *ctrls[2];
695                         so2.so_vcontext = 0;
696                         so2.so_vlv_target = 0;
697                         so2.so_nentries = 0;
698                         so2.so_vlv_rc = LDAP_VLV_SSS_MISSING;
699                         rc = pack_vlv_response_control( op, rs, &so2, ctrls );
700                         if ( rc == LDAP_SUCCESS ) {
701                                 ctrls[1] = NULL;
702                                 slap_add_ctrls( op, rs, ctrls );
703                         }
704                         rs->sr_err = LDAP_VLV_ERROR;
705                         rs->sr_text = "Sort control is required with VLV";
706                         goto leave;
707                 }
708                 /* Not server side sort so just continue */
709                 return SLAP_CB_CONTINUE;
710         }
711
712         Debug(LDAP_DEBUG_TRACE,
713                 "==> sssvlv_search: <%s> %s, control flag: %d\n",
714                 op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
715                 op->o_ctrlflag[sss_cid]);
716
717         if ( sc->sc_nkeys > si->svi_max_keys ) {
718                 rs->sr_text = "Too many sort keys";
719                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
720                 goto leave;
721         }
722
723         ps = ( op->o_pagedresults > SLAP_CONTROL_IGNORED ) ?
724                 (PagedResultsState*)(op->o_pagedresults_state) : NULL;
725         vc = op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ?
726                 op->o_controls[vlv_cid] : NULL;
727
728         if ( ps && vc ) {
729                 rs->sr_text = "VLV incompatible with PagedResults";
730                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
731                 goto leave;
732         }
733
734         ok = 1;
735         ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
736         so = sort_conns[op->o_conn->c_conn_idx];
737         /* Is there already a sort running on this conn? */
738         if ( so ) {
739                 /* Is it a continuation of a VLV search? */
740                 if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
741                         vc->vc_context != so->so_vcontext ) {
742                         /* Is it a continuation of a paged search? */
743                         if ( !ps || so->so_paged <= SLAP_CONTROL_IGNORED ||
744                                 op->o_conn->c_pagedresults_state.ps_cookie != ps->ps_cookie ) {
745                                 ok = 0;
746                         } else if ( !ps->ps_size ) {
747                         /* Abandoning current request */
748                                 ok = 0;
749                                 so->so_nentries = 0;
750                                 rs->sr_err = LDAP_SUCCESS;
751                         }
752                 }
753                 if (( vc && so->so_paged > SLAP_CONTROL_IGNORED ) ||
754                         ( ps && so->so_vlv > SLAP_CONTROL_IGNORED )) {
755                         /* changed from paged to vlv or vice versa, abandon */
756                         ok = 0;
757                         so->so_nentries = 0;
758                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
759                 }
760         /* Are there too many running overall? */
761         } else if ( si->svi_num >= si->svi_max ) {
762                 ok = 0;
763         } else {
764                 /* OK, this connection now has a sort running */
765                 si->svi_num++;
766                 sort_conns[op->o_conn->c_conn_idx] = &so2;
767         }
768         ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
769         if ( ok ) {
770                 /* are we continuing a VLV search? */
771                 if ( vc && vc->vc_context ) {
772                         so->so_ctrl = sc;
773                         send_list( op, rs, so );
774                         send_result( op, rs, so );
775                         rc = LDAP_SUCCESS;
776                 /* are we continuing a paged search? */
777                 } else if ( ps && ps->ps_cookie ) {
778                         so->so_ctrl = sc;
779                         send_page( op, rs, so );
780                         send_result( op, rs, so );
781                         rc = LDAP_SUCCESS;
782                 } else {
783                         slap_callback *cb = op->o_tmpalloc( sizeof(slap_callback),
784                                 op->o_tmpmemctx );
785                         /* Install serversort response callback to handle a new search */
786                         if ( ps || vc ) {
787                                 so = ch_malloc( sizeof(sort_op));
788                         } else {
789                                 so = op->o_tmpalloc( sizeof(sort_op), op->o_tmpmemctx );
790                         }
791                         sort_conns[op->o_conn->c_conn_idx] = so;
792
793                         cb->sc_cleanup          = NULL;
794                         cb->sc_response         = sssvlv_op_response;
795                         cb->sc_next                     = op->o_callback;
796                         cb->sc_private          = so;
797
798                         so->so_tree = NULL;
799                         so->so_ctrl = sc;
800                         so->so_info = si;
801                         if ( ps ) {
802                                 so->so_paged = op->o_pagedresults;
803                                 so->so_page_size = ps->ps_size;
804                                 op->o_pagedresults = SLAP_CONTROL_IGNORED;
805                         } else {
806                                 so->so_paged = 0;
807                                 so->so_page_size = 0;
808                                 if ( vc )
809                                         so->so_vlv = op->o_ctrlflag[vlv_cid];
810                         }
811                         so->so_vcontext = (unsigned long)so;
812                         so->so_nentries = 0;
813
814                         op->o_callback          = cb;
815                 }
816         } else {
817                 if ( so && !so->so_nentries ) {
818                         free_sort_op( op->o_conn, so );
819                 } else {
820                         rs->sr_text = "Other sort requests already in progress";
821                         rs->sr_err = LDAP_BUSY;
822                 }
823 leave:
824                 rc = rs->sr_err;
825                 send_ldap_result( op, rs );
826         }
827
828         return rc;
829 }
830
831 static int get_ordering_rule(
832         AttributeDescription    *ad,
833         struct berval                   *matchrule,
834         SlapReply                               *rs,
835         MatchingRule                    **ordering )
836 {
837         MatchingRule* mr;
838
839         if ( matchrule && matchrule->bv_val ) {
840                 mr = mr_find( matchrule->bv_val );
841                 if ( mr == NULL ) {
842                         rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
843                         rs->sr_text = "serverSort control: No ordering rule";
844                         Debug(LDAP_DEBUG_TRACE, "%s: no ordering rule function for %s\n",
845                                 debug_header, matchrule->bv_val, 0);
846                 }
847         }
848         else {
849                 mr = ad->ad_type->sat_ordering;
850                 if ( mr == NULL ) {
851                         rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
852                         rs->sr_text = "serverSort control: No ordering rule";
853                         Debug(LDAP_DEBUG_TRACE,
854                                 "%s: no ordering rule specified and no default ordering rule for attribute %s\n",
855                                 debug_header, ad->ad_cname.bv_val, 0);
856                 }
857         }
858
859         *ordering = mr;
860         return rs->sr_err;
861 }
862
863 static int count_key(BerElement *ber)
864 {
865         char *end;
866         ber_len_t len;
867         ber_tag_t tag;
868         int count = 0;
869
870         /* Server Side Sort Control is a SEQUENCE of SEQUENCE */
871         for ( tag = ber_first_element( ber, &len, &end );
872                   tag == LBER_SEQUENCE;
873                   tag = ber_next_element( ber, &len, end ))
874         {
875                 tag = ber_skip_tag( ber, &len );
876                 ber_skip_data( ber, len );
877                 ++count;
878         }
879         ber_rewind( ber );
880
881         return count;
882 }
883
884 static int build_key(
885         BerElement              *ber,
886         SlapReply               *rs,
887         sort_key                        *key )
888 {
889         struct berval attr;
890         struct berval matchrule = BER_BVNULL;
891         ber_int_t reverse = 0;
892         ber_tag_t tag;
893         ber_len_t len;
894         MatchingRule *ordering = NULL;
895         AttributeDescription *ad = NULL;
896         const char *text;
897
898         if (( tag = ber_scanf( ber, "{" )) == LBER_ERROR ) {
899                 rs->sr_text = "serverSort control: decoding error";
900                 rs->sr_err = LDAP_PROTOCOL_ERROR;
901                 return rs->sr_err;
902         }
903
904         if (( tag = ber_scanf( ber, "m", &attr )) == LBER_ERROR ) {
905                 rs->sr_text = "serverSort control: attribute decoding error";
906                 rs->sr_err = LDAP_PROTOCOL_ERROR;
907                 return rs->sr_err;
908         }
909
910         tag = ber_peek_tag( ber, &len );
911         if ( tag == LDAP_MATCHRULE_IDENTIFIER ) {
912                 if (( tag = ber_scanf( ber, "m", &matchrule )) == LBER_ERROR ) {
913                         rs->sr_text = "serverSort control: matchrule decoding error";
914                         rs->sr_err = LDAP_PROTOCOL_ERROR;
915                         return rs->sr_err;
916                 }
917                 tag = ber_peek_tag( ber, &len );
918         }
919
920         if ( tag == LDAP_REVERSEORDER_IDENTIFIER ) {
921                 if (( tag = ber_scanf( ber, "b", &reverse )) == LBER_ERROR ) {
922                         rs->sr_text = "serverSort control: reverse decoding error";
923                         rs->sr_err = LDAP_PROTOCOL_ERROR;
924                         return rs->sr_err;
925                 }
926         }
927
928         if (( tag = ber_scanf( ber, "}" )) == LBER_ERROR ) {
929                 rs->sr_text = "serverSort control: decoding error";
930                 rs->sr_err = LDAP_PROTOCOL_ERROR;
931                 return rs->sr_err;
932         }
933
934         if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) {
935                 rs->sr_text =
936                         "serverSort control: Unrecognized attribute type in sort key";
937                 Debug(LDAP_DEBUG_TRACE,
938                         "%s: Unrecognized attribute type in sort key: %s\n",
939                         debug_header, SAFESTR(attr.bv_val, "<None>"), 0);
940                 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
941                 return rs->sr_err;
942         }
943
944         /* get_ordering_rule will set sr_err and sr_text */
945         get_ordering_rule( ad, &matchrule, rs, &ordering );
946         if ( rs->sr_err != LDAP_SUCCESS ) {
947                 return rs->sr_err;
948         }
949
950         key->sk_ad = ad;
951         key->sk_ordering = ordering;
952         key->sk_direction = reverse ? -1 : 1;
953
954         return rs->sr_err;
955 }
956
957 static int sss_parseCtrl(
958         Operation               *op,
959         SlapReply               *rs,
960         LDAPControl             *ctrl )
961 {
962         BerElementBuffer        berbuf;
963         BerElement                      *ber;
964         ber_tag_t               tag;
965         ber_len_t               len;
966         int                                     i;
967         sort_ctrl       *sc;
968
969         rs->sr_err = LDAP_PROTOCOL_ERROR;
970
971         if ( op->o_ctrlflag[sss_cid] > SLAP_CONTROL_IGNORED ) {
972                 rs->sr_text = "sorted results control specified multiple times";
973         } else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
974                 rs->sr_text = "sorted results control value is absent";
975         } else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
976                 rs->sr_text = "sorted results control value is empty";
977         } else {
978                 rs->sr_err = LDAP_SUCCESS;
979         }
980         if ( rs->sr_err != LDAP_SUCCESS )
981                 return rs->sr_err;
982
983         op->o_ctrlflag[sss_cid] = ctrl->ldctl_iscritical ?
984                 SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
985
986         ber = (BerElement *)&berbuf;
987         ber_init2( ber, &ctrl->ldctl_value, 0 );
988         i = count_key( ber );
989
990         sc = op->o_tmpalloc( sizeof(sort_ctrl) +
991                 i * sizeof(sort_key), op->o_tmpmemctx );
992         sc->sc_nkeys = i;
993         op->o_controls[sss_cid] = sc;
994
995         /* peel off initial sequence */
996         ber_scanf( ber, "{" );
997
998         i = 0;
999         do {
1000                 if ( build_key( ber, rs, &sc->sc_keys[i] ) != LDAP_SUCCESS )
1001                         break;
1002                 i++;
1003                 tag = ber_peek_tag( ber, &len );
1004         } while ( tag != LBER_DEFAULT );
1005
1006         return rs->sr_err;
1007 }
1008
1009 static int vlv_parseCtrl(
1010         Operation               *op,
1011         SlapReply               *rs,
1012         LDAPControl             *ctrl )
1013 {
1014         BerElementBuffer        berbuf;
1015         BerElement                      *ber;
1016         ber_tag_t               tag;
1017         ber_len_t               len;
1018         int                                     i;
1019         vlv_ctrl        *vc, vc2;
1020
1021         rs->sr_err = LDAP_PROTOCOL_ERROR;
1022         rs->sr_text = NULL;
1023
1024         if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
1025                 rs->sr_text = "vlv control specified multiple times";
1026         } else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
1027                 rs->sr_text = "vlv control value is absent";
1028         } else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1029                 rs->sr_text = "vlv control value is empty";
1030         }
1031         if ( rs->sr_text != NULL )
1032                 return rs->sr_err;
1033
1034         op->o_ctrlflag[vlv_cid] = ctrl->ldctl_iscritical ?
1035                 SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1036
1037         ber = (BerElement *)&berbuf;
1038         ber_init2( ber, &ctrl->ldctl_value, 0 );
1039
1040         rs->sr_err = LDAP_PROTOCOL_ERROR;
1041
1042         tag = ber_scanf( ber, "{ii", &vc2.vc_before, &vc2.vc_after );
1043         if ( tag == LBER_ERROR ) {
1044                 return rs->sr_err;
1045         }
1046
1047         tag = ber_peek_tag( ber, &len );
1048         if ( tag == LDAP_VLVBYINDEX_IDENTIFIER ) {
1049                 tag = ber_scanf( ber, "{ii}", &vc2.vc_offset, &vc2.vc_count );
1050                 if ( tag == LBER_ERROR )
1051                         return rs->sr_err;
1052                 BER_BVZERO( &vc2.vc_value );
1053         } else if ( tag == LDAP_VLVBYVALUE_IDENTIFIER ) {
1054                 tag = ber_scanf( ber, "m", &vc2.vc_value );
1055                 if ( tag == LBER_ERROR || BER_BVISNULL( &vc2.vc_value ))
1056                         return rs->sr_err;
1057         } else {
1058                 return rs->sr_err;
1059         }
1060         tag = ber_peek_tag( ber, &len );
1061         if ( tag == LDAP_VLVCONTEXT_IDENTIFIER ) {
1062                 struct berval bv;
1063                 tag = ber_scanf( ber, "m", &bv );
1064                 if ( tag == LBER_ERROR || bv.bv_len != sizeof(vc2.vc_context))
1065                         return rs->sr_err;
1066                 AC_MEMCPY( &vc2.vc_context, bv.bv_val, bv.bv_len );
1067         } else {
1068                 vc2.vc_context = 0;
1069         }
1070
1071         vc = op->o_tmpalloc( sizeof(vlv_ctrl), op->o_tmpmemctx );
1072         *vc = vc2;
1073         op->o_controls[vlv_cid] = vc;
1074         rs->sr_err = LDAP_SUCCESS;
1075
1076         return rs->sr_err;
1077 }
1078
1079 static int sssvlv_connection_destroy( BackendDB *be, Connection *conn )
1080 {
1081         slap_overinst   *on             = (slap_overinst *)be->bd_info;
1082
1083         if ( sort_conns[conn->c_conn_idx] )
1084                 free_sort_op( conn, sort_conns[conn->c_conn_idx] );
1085
1086         return LDAP_SUCCESS;
1087 }
1088
1089 static int sssvlv_db_open(
1090         BackendDB               *be,
1091         ConfigReply             *cr )
1092 {
1093         int rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
1094         if ( rc == LDAP_SUCCESS )
1095                 rc = overlay_register_control( be, LDAP_CONTROL_VLVREQUEST );
1096         return rc;
1097 }
1098
1099 static int sssvlv_db_init(
1100         BackendDB               *be,
1101         ConfigReply             *cr)
1102 {
1103         slap_overinst   *on = (slap_overinst *)be->bd_info;
1104         sssvlv_info *si;
1105         
1106         si = (sssvlv_info *)ch_malloc(sizeof(sssvlv_info));
1107         on->on_bi.bi_private = si;
1108
1109         si->svi_max = 5;
1110         si->svi_num = 0;
1111         si->svi_max_keys = 5;
1112
1113         if ( dtblsize && !sort_conns ) {
1114                 ldap_pvt_thread_mutex_init( &sort_conns_mutex );
1115                 /* accommodate for c_conn_idx == -1 */
1116                 sort_conns = ch_calloc( sizeof(sort_op *), dtblsize + 1 );
1117                 sort_conns++;
1118         }
1119
1120         return LDAP_SUCCESS;
1121 }
1122
1123 static int sssvlv_db_destroy(
1124         BackendDB               *be,
1125         ConfigReply             *cr )
1126 {
1127         slap_overinst   *on = (slap_overinst *)be->bd_info;
1128         sssvlv_info *si = (sssvlv_info *)on->on_bi.bi_private;
1129         
1130         if ( si ) {
1131                 ch_free( si );
1132                 on->on_bi.bi_private = NULL;
1133         }
1134         return LDAP_SUCCESS;
1135 }
1136
1137 static slap_overinst sssvlv;
1138
1139 int sssvlv_initialize()
1140 {
1141         int rc;
1142
1143         sssvlv.on_bi.bi_type                            = "sssvlv";
1144         sssvlv.on_bi.bi_db_init                         = sssvlv_db_init;
1145         sssvlv.on_bi.bi_db_destroy                      = sssvlv_db_destroy;
1146         sssvlv.on_bi.bi_db_open                         = sssvlv_db_open;
1147         sssvlv.on_bi.bi_connection_destroy      = sssvlv_connection_destroy;
1148         sssvlv.on_bi.bi_op_search                       = sssvlv_op_search;
1149
1150         rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
1151                         SLAP_CTRL_SEARCH,
1152                         NULL,
1153                         sss_parseCtrl,
1154                         1 /* replace */,
1155                         &sss_cid );
1156
1157         if ( rc == LDAP_SUCCESS ) {
1158                 rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
1159                         SLAP_CTRL_SEARCH,
1160                         NULL,
1161                         vlv_parseCtrl,
1162                         1 /* replace */,
1163                         &vlv_cid );
1164         }
1165
1166         if ( rc == LDAP_SUCCESS ) {
1167                 rc = overlay_register( &sssvlv );
1168                 if ( rc != LDAP_SUCCESS ) {
1169                         fprintf( stderr, "Failed to register server side sort overlay\n" );
1170                 }
1171         }
1172         else {
1173                 fprintf( stderr, "Failed to register control %d\n", rc );
1174         }
1175
1176         return rc;
1177 }
1178
1179 #if SLAPD_OVER_SSSVLV == SLAPD_MOD_DYNAMIC
1180 int init_module( int argc, char *argv[])
1181 {
1182         return sssvlv_initialize();
1183 }
1184 #endif
1185
1186 #endif /* SLAPD_OVER_SSSVLV */