]> git.sur5r.net Git - openldap/blob - libraries/libldap/sortctrl.c
185046167210961c320f7f947c90a62eab58caa9
[openldap] / libraries / libldap / sortctrl.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2006 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
16  *
17  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
18  * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
19  * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
20  * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
21  * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
22  * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
23  * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
24  * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
25  */
26 /* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
27  * can be found in the file "build/LICENSE-2.0.1" in this distribution
28  * of OpenLDAP Software.
29  */
30 /* Portions Copyright (C) The Internet Society (1997)
31  * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
32  */
33
34 #include "portable.h"
35
36 #include <stdio.h>
37 #include <ac/stdlib.h>
38 #include <ac/string.h>
39 #include <ac/time.h>
40
41 #include "ldap-int.h"
42
43 #define LDAP_MATCHRULE_IDENTIFIER      0x80L
44 #define LDAP_REVERSEORDER_IDENTIFIER   0x81L
45 #define LDAP_ATTRTYPES_IDENTIFIER      0x80L
46
47
48
49 /* ---------------------------------------------------------------------------
50    countKeys
51    
52    Internal function to determine the number of keys in the string.
53    
54    keyString  (IN) String of items separated by whitespace.
55    ---------------------------------------------------------------------------*/
56
57 static int countKeys(char *keyString)
58 {
59         char *p = keyString;
60         int count = 0;
61
62         for (;;)
63         {
64                 while (LDAP_SPACE(*p))           /* Skip leading whitespace */
65                         p++;
66
67                 if (*p == '\0')                 /* End of string? */
68                         return count;
69
70                 count++;                                /* Found start of a key */
71
72                 while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */
73                         if (*p++ == '\0')
74                                 return count;
75         }
76 }
77
78
79 /* ---------------------------------------------------------------------------
80    readNextKey
81    
82    Internal function to parse the next sort key in the string.
83    Allocate an LDAPSortKey structure and initialize it with
84    attribute name, reverse flag, and matching rule OID.
85
86    Each sort key in the string has the format:
87           [whitespace][-]attribute[:[OID]]
88
89    pNextKey    (IN/OUT) Points to the next key in the sortkey string to parse.
90                                                 The pointer is updated to point to the next character
91                                                 after the sortkey being parsed.
92                                                 
93    key         (OUT)    Points to the address of an LDAPSortKey stucture
94                                                 which has been allocated by this routine and
95                                                 initialized with information from the next sortkey.                        
96    ---------------------------------------------------------------------------*/
97
98 static int readNextKey( char **pNextKey, LDAPSortKey **key)
99 {
100         char *p = *pNextKey;
101         int rev = 0;
102         char *attrStart;
103         int attrLen;
104         char *oidStart = NULL;
105         int oidLen = 0;
106
107         /* Skip leading white space. */
108         while (LDAP_SPACE(*p))
109                 p++;
110
111         if (*p == '-')           /* Check if the reverse flag is present. */
112         {
113                 rev=1;
114                 p++;
115         }
116
117         /* We're now positioned at the start of the attribute. */
118         attrStart = p;
119
120         /* Get the length of the attribute until the next whitespace or ":". */
121         attrLen = strcspn(p, " \t:");
122         p += attrLen;
123
124         if (attrLen == 0)        /* If no attribute name was present, quit. */
125                 return LDAP_PARAM_ERROR;
126
127         if (*p == ':')
128         {
129                 oidStart = ++p;                          /* Start of the OID, after the colon */
130                 oidLen = strcspn(p, " \t");      /* Get length of OID till next whitespace */
131                 p += oidLen;
132         }
133
134         *pNextKey = p;           /* Update argument to point to next key */
135
136         /* Allocate an LDAPSortKey structure */
137         *key = LDAP_MALLOC(sizeof(LDAPSortKey));
138         if (*key == NULL) return LDAP_NO_MEMORY;
139
140         /* Allocate memory for the attribute and copy to it. */
141         (*key)->attributeType = LDAP_MALLOC(attrLen+1);
142         if ((*key)->attributeType == NULL) {
143                 LDAP_FREE(*key);
144                 return LDAP_NO_MEMORY;
145         }
146
147         strncpy((*key)->attributeType, attrStart, attrLen);
148         (*key)->attributeType[attrLen] = 0;
149
150         /* If present, allocate memory for the OID and copy to it. */
151         if (oidLen) {
152                 (*key)->orderingRule = LDAP_MALLOC(oidLen+1);
153                 if ((*key)->orderingRule == NULL) {
154                         LDAP_FREE((*key)->attributeType);
155                         LDAP_FREE(*key);
156                         return LDAP_NO_MEMORY;
157                 }
158                 strncpy((*key)->orderingRule, oidStart, oidLen);
159                 (*key)->orderingRule[oidLen] = 0;
160
161         } else {
162                 (*key)->orderingRule = NULL;
163         }
164
165         (*key)->reverseOrder = rev;
166
167         return LDAP_SUCCESS;
168 }
169
170
171 /* ---------------------------------------------------------------------------
172    ldap_create_sort_keylist
173    
174    Create an array of pointers to LDAPSortKey structures, containing the
175    information specified by the string representation of one or more
176    sort keys.
177    
178    sortKeyList    (OUT) Points to a null-terminated array of pointers to
179                                                 LDAPSortKey structures allocated by this routine.
180                                                 This memory SHOULD be freed by the calling program
181                                                 using ldap_free_sort_keylist().
182                                                 
183    keyString      (IN)  Points to a string of one or more sort keys.                      
184    
185    ---------------------------------------------------------------------------*/
186
187 int
188 ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
189 {
190         int         numKeys, rc, i;
191         char        *nextKey;
192         LDAPSortKey **keyList = NULL;
193
194         assert( sortKeyList != NULL );
195         assert( keyString != NULL );
196
197         *sortKeyList = NULL;
198
199         /* Determine the number of sort keys so we can allocate memory. */
200         if (( numKeys = countKeys(keyString)) == 0) {
201                 return LDAP_PARAM_ERROR;
202         }
203
204         /* Allocate the array of pointers.  Initialize to NULL. */
205         keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
206         if ( keyList == NULL) return LDAP_NO_MEMORY;
207
208         /* For each sort key in the string, create an LDAPSortKey structure
209            and add it to the list.
210         */
211         nextKey = keyString;              /* Points to the next key in the string */
212         for (i=0; i < numKeys; i++) {
213                 rc = readNextKey(&nextKey, &keyList[i]);
214
215                 if (rc != LDAP_SUCCESS) {
216                         ldap_free_sort_keylist(keyList);
217                         return rc;
218                 }
219         }
220
221         *sortKeyList = keyList;
222         return LDAP_SUCCESS;
223 }
224
225
226 /* ---------------------------------------------------------------------------
227    ldap_free_sort_keylist
228    
229    Frees the sort key structures created by ldap_create_sort_keylist().
230    Frees the memory referenced by the LDAPSortKey structures,
231    the LDAPSortKey structures themselves, and the array of pointers
232    to the structures.
233    
234    keyList     (IN) Points to an array of pointers to LDAPSortKey structures.
235    ---------------------------------------------------------------------------*/
236
237 void
238 ldap_free_sort_keylist ( LDAPSortKey **keyList )
239 {
240         int i;
241         LDAPSortKey *nextKeyp;
242
243         if (keyList == NULL) return;
244
245         i=0;
246         while ( 0 != (nextKeyp = keyList[i++]) ) {
247                 if (nextKeyp->attributeType) {
248                         LBER_FREE(nextKeyp->attributeType);
249                 }
250
251                 if (nextKeyp->orderingRule != NULL) {
252                         LBER_FREE(nextKeyp->orderingRule);
253                 }
254
255                 LBER_FREE(nextKeyp);
256         }
257
258         LBER_FREE(keyList);
259 }
260
261
262 /* ---------------------------------------------------------------------------
263    ldap_create_sort_control_value
264    
265    Create and encode the value of the server-side sort control.
266    
267    ld          (IN) An LDAP session handle, as obtained from a call to
268                                         ldap_init().
269
270    keyList     (IN) Points to a null-terminated array of pointers to
271                                         LDAPSortKey structures, containing a description of
272                                         each of the sort keys to be used.  The description
273                                         consists of an attribute name, ascending/descending flag,
274                                         and an optional matching rule (OID) to use.
275                            
276    value      (OUT) Contains the control value; the bv_val member of the berval structure
277                                         SHOULD be freed by calling ldap_memfree() when done.
278    
279    
280    Ber encoding
281    
282    SortKeyList ::= SEQUENCE OF SEQUENCE {
283                    attributeType   AttributeDescription,
284                    orderingRule    [0] MatchingRuleId OPTIONAL,
285                    reverseOrder    [1] BOOLEAN DEFAULT FALSE }
286    
287    ---------------------------------------------------------------------------*/
288
289 int
290 ldap_create_sort_control_value(
291         LDAP *ld,
292         LDAPSortKey **keyList,
293         struct berval *value )
294 {
295         int             i;
296         BerElement      *ber = NULL;
297         ber_tag_t       tag;
298
299         assert( ld != NULL );
300         assert( LDAP_VALID( ld ) );
301
302         if ( ld == NULL ) return LDAP_PARAM_ERROR;
303         if ( keyList == NULL || value == NULL ) {
304                 ld->ld_errno = LDAP_PARAM_ERROR;
305                 return LDAP_PARAM_ERROR;
306         }
307
308         value->bv_val = NULL;
309         value->bv_len = 0;
310
311         ber = ldap_alloc_ber_with_options( ld );
312         if ( ber == NULL) {
313                 ld->ld_errno = LDAP_NO_MEMORY;
314                 return ld->ld_errno;
315         }
316
317         tag = ber_printf( ber, "{" /*}*/ );
318         if ( tag == LBER_ERROR ) {
319                 goto error_return;
320         }
321
322         for ( i = 0; keyList[i] != NULL; i++ ) {
323                 tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );
324                 if ( tag == LBER_ERROR ) {
325                         goto error_return;
326                 }
327
328                 if ( keyList[i]->orderingRule != NULL ) {
329                         tag = ber_printf( ber, "ts",
330                                 LDAP_MATCHRULE_IDENTIFIER,
331                                 keyList[i]->orderingRule );
332
333                         if ( tag == LBER_ERROR ) {
334                                 goto error_return;
335                         }
336                 }
337
338                 if ( keyList[i]->reverseOrder ) {
339                         tag = ber_printf( ber, "tb",
340                                 LDAP_REVERSEORDER_IDENTIFIER,
341                                 keyList[i]->reverseOrder );
342
343                         if ( tag == LBER_ERROR ) {
344                                 goto error_return;
345                         }
346                 }
347
348                 tag = ber_printf( ber, /*{*/ "N}" );
349                 if ( tag == LBER_ERROR ) {
350                         goto error_return;
351                 }
352         }
353
354         tag = ber_printf( ber, /*{*/ "N}" );
355         if ( tag == LBER_ERROR ) {
356                 goto error_return;
357         }
358
359         if ( ber_flatten2( ber, value, 1 ) == -1 ) {
360                 ld->ld_errno = LDAP_NO_MEMORY;
361         }
362
363         if ( 0 ) {
364 error_return:;
365                 ld->ld_errno =  LDAP_ENCODING_ERROR;
366         }
367
368         if ( ber != NULL ) {
369                 ber_free( ber, 1 );
370         }
371
372         return ld->ld_errno;
373 }
374
375
376 /* ---------------------------------------------------------------------------
377    ldap_create_sort_control
378    
379    Create and encode the server-side sort control.
380    
381    ld          (IN) An LDAP session handle, as obtained from a call to
382                                         ldap_init().
383
384    keyList     (IN) Points to a null-terminated array of pointers to
385                                         LDAPSortKey structures, containing a description of
386                                         each of the sort keys to be used.  The description
387                                         consists of an attribute name, ascending/descending flag,
388                                         and an optional matching rule (OID) to use.
389                            
390    isCritical  (IN) 0 - Indicates the control is not critical to the operation.
391                                         non-zero - The control is critical to the operation.
392                                          
393    ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
394                                         SHOULD be freed by calling ldap_control_free() when done.
395    
396    
397    Ber encoding
398    
399    SortKeyList ::= SEQUENCE OF SEQUENCE {
400                    attributeType   AttributeDescription,
401                    orderingRule    [0] MatchingRuleId OPTIONAL,
402                    reverseOrder    [1] BOOLEAN DEFAULT FALSE }
403    
404    ---------------------------------------------------------------------------*/
405
406 int
407 ldap_create_sort_control(
408         LDAP *ld,
409         LDAPSortKey **keyList,
410         int isCritical,
411         LDAPControl **ctrlp )
412 {
413         struct berval   value;
414         BerElement      *ber;
415
416         assert( ld != NULL );
417         assert( LDAP_VALID( ld ) );
418
419         if ( ld == NULL ) return LDAP_PARAM_ERROR;
420         if ( ctrlp == NULL ) {
421                 ld->ld_errno = LDAP_PARAM_ERROR;
422                 return ld->ld_errno;
423         }
424
425         ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
426         if ( ld->ld_errno == LDAP_SUCCESS ) {
427                 if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
428                         ld->ld_errno = LDAP_NO_MEMORY;
429                         return LDAP_NO_MEMORY;
430                 }
431         
432                 ld->ld_errno = ldap_create_control( LDAP_CONTROL_SORTREQUEST,
433                         ber, isCritical, ctrlp );
434                 if ( ld->ld_errno == LDAP_SUCCESS ) {
435                         (*ctrlp)->ldctl_value = value;
436                 } else {
437                         LDAP_FREE( value.bv_val );
438                 }
439                 ber_free(ber, 1);
440         }
441
442         return ld->ld_errno;
443 }
444
445
446 /* ---------------------------------------------------------------------------
447    ldap_parse_sortedresult_control
448    
449    Decode the server-side sort control return information.
450
451    ld          (IN) An LDAP session handle, as obtained from a call to
452                                         ldap_init().
453
454    ctrl        (IN) The address of the LDAP Control Structure.
455                                   
456    returnCode (OUT) This result parameter is filled in with the sort control
457                                         result code.  This parameter MUST not be NULL.
458                                   
459    attribute  (OUT) If an error occured the server may return a string
460                                         indicating the first attribute in the sortkey list
461                                         that was in error.  If a string is returned, the memory
462                                         should be freed with ldap_memfree.  If this parameter is
463                                         NULL, no string is returned.
464    
465                            
466    Ber encoding for sort control
467          
468          SortResult ::= SEQUENCE {
469                 sortResult  ENUMERATED {
470                         success                   (0), -- results are sorted
471                         operationsError           (1), -- server internal failure
472                         timeLimitExceeded         (3), -- timelimit reached before
473                                                                                    -- sorting was completed
474                         strongAuthRequired        (8), -- refused to return sorted
475                                                                                    -- results via insecure
476                                                                                    -- protocol
477                         adminLimitExceeded       (11), -- too many matching entries
478                                                                                    -- for the server to sort
479                         noSuchAttribute          (16), -- unrecognized attribute
480                                                                                    -- type in sort key
481                         inappropriateMatching    (18), -- unrecognized or inappro-
482                                                                                    -- priate matching rule in
483                                                                                    -- sort key
484                         insufficientAccessRights (50), -- refused to return sorted
485                                                                                    -- results to this client
486                         busy                     (51), -- too busy to process
487                         unwillingToPerform       (53), -- unable to sort
488                         other                    (80)
489                         },
490           attributeType [0] AttributeDescription OPTIONAL }
491    ---------------------------------------------------------------------------*/
492
493 int
494 ldap_parse_sortresponse_control(
495         LDAP *ld,
496         LDAPControl *ctrl,
497         ber_int_t *returnCode,
498         char **attribute )
499 {
500         BerElement *ber;
501         ber_tag_t tag, berTag;
502         ber_len_t berLen;
503
504         assert( ld != NULL );
505         assert( LDAP_VALID( ld ) );
506
507         if (ld == NULL) {
508                 return LDAP_PARAM_ERROR;
509         }
510
511         if (ctrl == NULL) {
512                 ld->ld_errno =  LDAP_PARAM_ERROR;
513                 return(ld->ld_errno);
514         }
515
516         if (attribute) {
517                 *attribute = NULL;
518         }
519
520         if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {
521                 /* Not sort result control */
522                 ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
523                 return(ld->ld_errno);
524         }
525
526         /* Create a BerElement from the berval returned in the control. */
527         ber = ber_init(&ctrl->ldctl_value);
528
529         if (ber == NULL) {
530                 ld->ld_errno = LDAP_NO_MEMORY;
531                 return(ld->ld_errno);
532         }
533
534         /* Extract the result code from the control. */
535         tag = ber_scanf(ber, "{e" /*}*/, returnCode);
536
537         if( tag == LBER_ERROR ) {
538                 ber_free(ber, 1);
539                 ld->ld_errno = LDAP_DECODING_ERROR;
540                 return(ld->ld_errno);
541         }
542
543         /* If caller wants the attribute name, and if it's present in the control,
544            extract the attribute name which caused the error. */
545         if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
546         {
547                 tag = ber_scanf(ber, "ta", &berTag, attribute);
548
549                 if (tag == LBER_ERROR ) {
550                         ber_free(ber, 1);
551                         ld->ld_errno = LDAP_DECODING_ERROR;
552                         return(ld->ld_errno);
553                 }
554         }
555
556         ber_free(ber,1);
557
558         ld->ld_errno = LDAP_SUCCESS;
559         return(ld->ld_errno);
560 }