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