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