]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
- allow interactive pagedResults size change
[openldap] / servers / slapd / controls.c
1 /* $OpenLDAP$ */
2 /* 
3  * Copyright 1999-2002 The OpenLDAP Foundation.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted only
7  * as authorized by the OpenLDAP Public License.  A copy of this
8  * license is available at http://www.OpenLDAP.org/license.html or
9  * in file LICENSE in the top-level directory of the distribution.
10  */
11 #include "portable.h"
12
13 #include <stdio.h>
14
15 #include <ac/string.h>
16 #include <ac/socket.h>
17
18 #include "slap.h"
19
20 #include "../../libraries/liblber/lber-int.h"
21
22 #define SLAP_CTRL_FRONTEND                      0x80000000U
23 #define SLAP_CTRL_FRONTEND_SEARCH       0x01000000U     /* for NOOP */
24
25 #define SLAP_CTRL_OPFLAGS                       0x0000FFFFU
26 #define SLAP_CTRL_ABANDON                       0x00000001U
27 #define SLAP_CTRL_ADD                           0x00002002U
28 #define SLAP_CTRL_BIND                          0x00000004U
29 #define SLAP_CTRL_COMPARE                       0x00001008U
30 #define SLAP_CTRL_DELETE                        0x00002010U
31 #define SLAP_CTRL_MODIFY                        0x00002020U
32 #define SLAP_CTRL_RENAME                        0x00002040U
33 #define SLAP_CTRL_SEARCH                        0x00001080U
34 #define SLAP_CTRL_UNBIND                        0x00000100U
35
36 #define SLAP_CTRL_INTROGATE     (SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH)
37 #define SLAP_CTRL_UPDATE \
38         (SLAP_CTRL_ADD|SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME)
39 #define SLAP_CTRL_ACCESS        (SLAP_CTRL_INTROGATE|SLAP_CTRL_UPDATE)
40
41 typedef int (SLAP_CTRL_PARSE_FN) LDAP_P((
42         Connection *conn,
43         Operation *op,
44         LDAPControl *ctrl,
45         const char **text ));
46
47 static SLAP_CTRL_PARSE_FN parseManageDSAit;
48 static SLAP_CTRL_PARSE_FN parseSubentries;
49 static SLAP_CTRL_PARSE_FN parseNoOp;
50 static SLAP_CTRL_PARSE_FN parsePagedResults;
51 static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
52
53 #ifdef LDAP_CLIENT_UPDATE
54 static SLAP_CTRL_PARSE_FN parseClientUpdate;
55 #endif /* LDAP_CLIENT_UPDATE */
56
57 #undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
58
59 static struct slap_control {
60         char *sc_oid;
61         slap_mask_t sc_mask;
62         char **sc_extendedops;
63         SLAP_CTRL_PARSE_FN *sc_parse;
64
65 } supportedControls[] = {
66         { LDAP_CONTROL_MANAGEDSAIT,
67                 SLAP_CTRL_ACCESS, NULL,
68                 parseManageDSAit },
69 #ifdef LDAP_CONTROL_SUBENTRIES
70         { LDAP_CONTROL_SUBENTRIES,
71                 SLAP_CTRL_SEARCH, NULL,
72                 parseSubentries },
73 #endif
74 #ifdef LDAP_CONTROL_NOOP
75         { LDAP_CONTROL_NOOP,
76                 SLAP_CTRL_ACCESS, NULL,
77                 parseNoOp },
78 #endif
79 #ifdef LDAP_CONTROL_PAGEDRESULTS
80         { LDAP_CONTROL_PAGEDRESULTS,
81                 SLAP_CTRL_SEARCH, NULL,
82                 parsePagedResults },
83 #endif /* LDAP_CONTROL_PAGEDRESULTS */
84 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
85         { LDAP_CONTROL_VALUESRETURNFILTER,
86                 SLAP_CTRL_SEARCH, NULL,
87                 parseValuesReturnFilter },
88 #endif
89 #ifdef LDAP_CLIENT_UPDATE
90         { LDAP_CONTROL_CLIENT_UPDATE,
91                 SLAP_CTRL_SEARCH, NULL,
92                 parseClientUpdate },
93 #endif /* LDAP_CLIENT_UPDATE */
94         { NULL }
95 };
96
97 char *
98 get_supported_ctrl(int index)
99 {
100         return supportedControls[index].sc_oid;
101 }
102
103 static struct slap_control *
104 find_ctrl( const char *oid )
105 {
106         int i;
107         for( i=0; supportedControls[i].sc_oid; i++ ) {
108                 if( strcmp( oid, supportedControls[i].sc_oid ) == 0 ) {
109                         return &supportedControls[i];
110                 }
111         }
112         return NULL;
113 }
114
115 int get_ctrls(
116         Connection *conn,
117         Operation *op,
118         int sendres )
119 {
120         int nctrls = 0;
121         ber_tag_t tag;
122         ber_len_t len;
123         char *opaque;
124         BerElement *ber = op->o_ber;
125         struct slap_control *sc;
126         int rc = LDAP_SUCCESS;
127         const char *errmsg = NULL;
128
129         len = ber_pvt_ber_remaining(ber);
130
131         if( len == 0) {
132                 /* no controls */
133                 rc = LDAP_SUCCESS;
134                 return rc;
135         }
136
137         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
138                 if( tag == LBER_ERROR ) {
139                         rc = SLAPD_DISCONNECT;
140                         errmsg = "unexpected data in PDU";
141                 }
142
143                 goto return_results;
144         }
145
146 #ifdef NEW_LOGGING
147         LDAP_LOG( OPERATION, ENTRY, "get_ctrls: conn %lu\n", conn->c_connid, 0, 0 );
148 #else
149         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls\n", 0, 0, 0 );
150 #endif
151         if( op->o_protocol < LDAP_VERSION3 ) {
152                 rc = SLAPD_DISCONNECT;
153                 errmsg = "controls require LDAPv3";
154                 goto return_results;
155         }
156
157         /* one for first control, one for termination */
158 #ifndef LDAP_CONTROL_PAGEDRESULTS
159         op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) );
160 #else /* LDAP_CONTROL_PAGEDRESULTS */
161         /* FIXME: are we sure we need this? */
162         op->o_ctrls = ch_malloc( 3 * sizeof(LDAPControl *) );
163 #endif /* LDAP_CONTROL_PAGEDRESULTS */
164
165 #if 0
166         if( op->ctrls == NULL ) {
167                 rc = LDAP_NO_MEMORY;
168                 errmsg = "no memory";
169                 goto return_results;
170         }
171 #endif
172
173         op->o_ctrls[nctrls] = NULL;
174
175         /* step through each element */
176         for( tag = ber_first_element( ber, &len, &opaque );
177                 tag != LBER_ERROR;
178                 tag = ber_next_element( ber, &len, opaque ) )
179         {
180                 LDAPControl *c;
181                 LDAPControl **tctrls;
182
183                 c = ch_calloc( 1, sizeof(LDAPControl) );
184
185 #if 0
186                 if( c == NULL ) {
187                         ldap_controls_free(op->o_ctrls);
188                         op->o_ctrls = NULL;
189
190                         rc = LDAP_NO_MEMORY;
191                         errmsg = "no memory";
192                         goto return_results;
193                 }
194 #endif
195
196                 /* allocate pointer space for current controls (nctrls)
197                  * + this control + extra NULL
198                  */
199                 tctrls = ch_realloc( op->o_ctrls,
200                         (nctrls+2) * sizeof(LDAPControl *));
201
202 #if 0
203                 if( tctrls == NULL ) {
204                         ch_free( c );
205                         ldap_controls_free(op->o_ctrls);
206                         op->o_ctrls = NULL;
207
208                         rc = LDAP_NO_MEMORY;
209                         errmsg = "no memory";
210                         goto return_results;
211                 }
212 #endif
213                 op->o_ctrls = tctrls;
214
215                 op->o_ctrls[nctrls++] = c;
216                 op->o_ctrls[nctrls] = NULL;
217
218                 tag = ber_scanf( ber, "{a" /*}*/, &c->ldctl_oid );
219
220                 if( tag == LBER_ERROR ) {
221 #ifdef NEW_LOGGING
222                         LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu get OID failed.\n",
223                                 conn->c_connid, 0, 0 );
224 #else
225                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
226                                 0, 0, 0 );
227 #endif
228                         ldap_controls_free( op->o_ctrls );
229                         op->o_ctrls = NULL;
230                         rc = SLAPD_DISCONNECT;
231                         errmsg = "decoding controls error";
232                         goto return_results;
233                 }
234
235                 tag = ber_peek_tag( ber, &len );
236
237                 if( tag == LBER_BOOLEAN ) {
238                         ber_int_t crit;
239                         tag = ber_scanf( ber, "b", &crit );
240
241                         if( tag == LBER_ERROR ) {
242 #ifdef NEW_LOGGING
243                                 LDAP_LOG( OPERATION, INFO, 
244                                         "get_ctrls: conn %lu get crit failed.\n", 
245                                         conn->c_connid, 0, 0 );
246 #else
247                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n",
248                                         0, 0, 0 );
249 #endif
250                                 ldap_controls_free( op->o_ctrls );
251                                 op->o_ctrls = NULL;
252                                 rc = SLAPD_DISCONNECT;
253                                 errmsg = "decoding controls error";
254                                 goto return_results;
255                         }
256
257                         c->ldctl_iscritical = (crit != 0);
258                         tag = ber_peek_tag( ber, &len );
259                 }
260
261                 if( tag == LBER_OCTETSTRING ) {
262                         tag = ber_scanf( ber, "o", &c->ldctl_value );
263
264                         if( tag == LBER_ERROR ) {
265 #ifdef NEW_LOGGING
266                                 LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu: "
267                                         "%s (%scritical): get value failed.\n",
268                                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
269                                         c->ldctl_iscritical ? "" : "non" );
270 #else
271                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
272                                         "%s (%scritical): get value failed.\n",
273                                         conn->c_connid,
274                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
275                                         c->ldctl_iscritical ? "" : "non" );
276 #endif
277                                 ldap_controls_free( op->o_ctrls );
278                                 op->o_ctrls = NULL;
279                                 rc = SLAPD_DISCONNECT;
280                                 errmsg = "decoding controls error";
281                                 goto return_results;
282                         }
283                 }
284
285 #ifdef NEW_LOGGING
286                 LDAP_LOG( OPERATION, INFO, 
287                         "get_ctrls: conn %lu oid=\"%s\" (%scritical)\n",
288                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
289                         c->ldctl_iscritical ? "" : "non" );
290 #else
291                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: oid=\"%s\" (%scritical)\n",
292                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
293                         c->ldctl_iscritical ? "" : "non",
294                         0 );
295 #endif
296
297                 sc = find_ctrl( c->ldctl_oid );
298                 if( sc != NULL ) {
299                         /* recognized control */
300                         slap_mask_t tagmask;
301                         switch( op->o_tag ) {
302                         case LDAP_REQ_ADD:
303                                 tagmask = SLAP_CTRL_ADD;
304                                 break;
305                         case LDAP_REQ_BIND:
306                                 tagmask = SLAP_CTRL_BIND;
307                                 break;
308                         case LDAP_REQ_COMPARE:
309                                 tagmask = SLAP_CTRL_COMPARE;
310                                 break;
311                         case LDAP_REQ_DELETE:
312                                 tagmask = SLAP_CTRL_DELETE;
313                                 break;
314                         case LDAP_REQ_MODIFY:
315                                 tagmask = SLAP_CTRL_MODIFY;
316                                 break;
317                         case LDAP_REQ_RENAME:
318                                 tagmask = SLAP_CTRL_RENAME;
319                                 break;
320                         case LDAP_REQ_SEARCH:
321                                 tagmask = SLAP_CTRL_SEARCH;
322                                 break;
323                         case LDAP_REQ_UNBIND:
324                                 tagmask = SLAP_CTRL_UNBIND;
325                                 break;
326                         case LDAP_REQ_ABANDON:
327                                 tagmask = SLAP_CTRL_ABANDON;
328                                 break;
329                         case LDAP_REQ_EXTENDED:
330                                 /* FIXME: check list of extended operations */
331                                 tagmask = ~0U;
332                                 break;
333                         default:
334                                 rc = LDAP_OTHER;
335                                 errmsg = "controls internal error";
336                                 goto return_results;
337                         }
338
339                         if (( sc->sc_mask & tagmask ) == tagmask ) {
340                                 /* available extension */
341
342                                 if( !sc->sc_parse ) {
343                                         rc = LDAP_OTHER;
344                                         errmsg = "not yet implemented";
345                                         goto return_results;
346                                 }
347
348                                 rc = sc->sc_parse( conn, op, c, &errmsg );
349
350                                 if( rc != LDAP_SUCCESS ) goto return_results;
351
352                                 if ( sc->sc_mask & SLAP_CTRL_FRONTEND ) {
353                                         /* kludge to disable backend_control() check */
354                                         c->ldctl_iscritical = 0;
355
356                                 } else if ( tagmask == SLAP_CTRL_SEARCH &&
357                                         sc->sc_mask & SLAP_CTRL_FRONTEND_SEARCH )
358                                 {
359                                         /* kludge to disable backend_control() check */
360                                         c->ldctl_iscritical = 0;
361                                 }
362
363                         } else if( c->ldctl_iscritical ) {
364                                 /* unavailable CRITICAL control */
365                                 rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
366                                 errmsg = "critical extension is unavailable";
367                                 goto return_results;
368                         }
369
370                 } else if( c->ldctl_iscritical ) {
371                         /* unrecognized CRITICAL control */
372                         rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
373                         errmsg = "critical extension is not recognized";
374                         goto return_results;
375                 }
376         }
377
378 return_results:
379 #ifdef NEW_LOGGING
380         LDAP_LOG( OPERATION, RESULTS, 
381                 "get_ctrls: n=%d rc=%d err=%s\n", nctrls, rc, errmsg ? errmsg : "" );
382 #else
383         Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
384                 nctrls, rc, errmsg ? errmsg : "");
385 #endif
386
387         if( sendres && rc != LDAP_SUCCESS ) {
388                 if( rc == SLAPD_DISCONNECT ) {
389                         send_ldap_disconnect( conn, op, LDAP_PROTOCOL_ERROR, errmsg );
390                 } else {
391                         send_ldap_result( conn, op, rc,
392                                 NULL, errmsg, NULL, NULL );
393                 }
394         }
395
396         return rc;
397 }
398
399 static int parseManageDSAit (
400         Connection *conn,
401         Operation *op,
402         LDAPControl *ctrl,
403         const char **text )
404 {
405         if ( op->o_managedsait != SLAP_NO_CONTROL ) {
406                 *text = "manageDSAit control specified multiple times";
407                 return LDAP_PROTOCOL_ERROR;
408         }
409
410         if ( ctrl->ldctl_value.bv_len ) {
411                 *text = "manageDSAit control value not empty";
412                 return LDAP_PROTOCOL_ERROR;
413         }
414
415         op->o_managedsait = ctrl->ldctl_iscritical
416                 ? SLAP_CRITICAL_CONTROL
417                 : SLAP_NONCRITICAL_CONTROL;
418
419         return LDAP_SUCCESS;
420 }
421
422 #ifdef LDAP_CONTROL_SUBENTRIES
423 static int parseSubentries (
424         Connection *conn,
425         Operation *op,
426         LDAPControl *ctrl,
427         const char **text )
428 {
429         if ( op->o_subentries != SLAP_NO_CONTROL ) {
430                 *text = "subentries control specified multiple times";
431                 return LDAP_PROTOCOL_ERROR;
432         }
433
434         /* FIXME: should use BER library */
435         if( ( ctrl->ldctl_value.bv_len != 3 )
436                 && ( ctrl->ldctl_value.bv_val[0] != 0x01 )
437                 && ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
438         {
439                 *text = "subentries control value encoding is bogus";
440                 return LDAP_PROTOCOL_ERROR;
441         }
442
443         op->o_subentries = ctrl->ldctl_iscritical
444                 ? SLAP_CRITICAL_CONTROL
445                 : SLAP_NONCRITICAL_CONTROL;
446
447         op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
448
449         return LDAP_SUCCESS;
450 }
451 #endif
452
453 #ifdef LDAP_CONTROL_NOOP
454 static int parseNoOp (
455         Connection *conn,
456         Operation *op,
457         LDAPControl *ctrl,
458         const char **text )
459 {
460         if ( op->o_noop != SLAP_NO_CONTROL ) {
461                 *text = "noop control specified multiple times";
462                 return LDAP_PROTOCOL_ERROR;
463         }
464
465         if ( ctrl->ldctl_value.bv_len ) {
466                 *text = "noop control value not empty";
467                 return LDAP_PROTOCOL_ERROR;
468         }
469
470         op->o_noop = ctrl->ldctl_iscritical
471                 ? SLAP_CRITICAL_CONTROL
472                 : SLAP_NONCRITICAL_CONTROL;
473
474         return LDAP_SUCCESS;
475 }
476 #endif
477
478 #ifdef LDAP_CONTROL_PAGEDRESULTS
479 static int parsePagedResults (
480         Connection *conn,
481         Operation *op,
482         LDAPControl *ctrl,
483         const char **text )
484 {
485         ber_tag_t tag;
486         ber_int_t size;
487         BerElement *ber;
488         struct berval cookie = { 0, NULL };
489
490         if ( op->o_pagedresults != SLAP_NO_CONTROL ) {
491                 *text = "paged results control specified multiple times";
492                 return LDAP_PROTOCOL_ERROR;
493         }
494
495         if ( ctrl->ldctl_value.bv_len == 0 ) {
496                 *text = "paged results control value is empty";
497                 return LDAP_PROTOCOL_ERROR;
498         }
499
500         /* Parse the control value
501          *      realSearchControlValue ::= SEQUENCE {
502          *              size    INTEGER (0..maxInt),
503          *                              -- requested page size from client
504          *                              -- result set size estimate from server
505          *              cookie  OCTET STRING
506          */
507         ber = ber_init( &ctrl->ldctl_value );
508         if( ber == NULL ) {
509                 *text = "internal error";
510                 return LDAP_OTHER;
511         }
512
513         tag = ber_scanf( ber, "{im}", &size, &cookie );
514         (void) ber_free( ber, 1 );
515
516         if( tag == LBER_ERROR ) {
517                 *text = "paged results control could not be decoded";
518                 return LDAP_PROTOCOL_ERROR;
519         }
520
521         if( size < 0 ) {
522                 *text = "paged results control size invalid";
523                 return LDAP_PROTOCOL_ERROR;
524         }
525
526         if( cookie.bv_len ) {
527                 PagedResultsCookie reqcookie;
528                 if( cookie.bv_len != sizeof( reqcookie ) ) {
529                         /* bad cookie */
530                         *text = "paged results cookie is invalid";
531                         return LDAP_PROTOCOL_ERROR;
532                 }
533
534                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
535
536                 if( reqcookie > op->o_pagedresults_state.ps_cookie ) {
537                         /* bad cookie */
538                         *text = "paged results cookie is invalid";
539                         return LDAP_PROTOCOL_ERROR;
540
541                 } else if( reqcookie < op->o_pagedresults_state.ps_cookie ) {
542                         *text = "paged results cookie is invalid or old";
543                         return LDAP_UNWILLING_TO_PERFORM;
544                 }
545         } else {
546                 /* Initial request.  Initialize state. */
547                 op->o_pagedresults_state.ps_cookie = 0;
548                 op->o_pagedresults_state.ps_id = NOID;
549         }
550
551         op->o_pagedresults_size = size;
552
553         op->o_pagedresults = ctrl->ldctl_iscritical
554                 ? SLAP_CRITICAL_CONTROL
555                 : SLAP_NONCRITICAL_CONTROL;
556
557         return LDAP_SUCCESS;
558 }
559 #endif /* LDAP_CONTROL_PAGEDRESULTS */
560
561 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
562 int parseValuesReturnFilter (
563         Connection *conn,
564         Operation *op,
565         LDAPControl *ctrl,
566         const char **text )
567 {
568         int             rc;
569         BerElement      *ber;
570         struct berval   fstr = { 0, NULL };
571         const char *err_msg = "";
572
573         if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
574                 *text = "valuesreturnfilter control specified multiple times";
575                 return LDAP_PROTOCOL_ERROR;
576         }
577
578         ber = ber_init( &(ctrl->ldctl_value) );
579         if (ber == NULL) {
580                 *text = "internal error";
581                 return LDAP_OTHER;
582         }
583         
584         rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg);
585
586         if( rc != LDAP_SUCCESS ) {
587                 text = &err_msg;
588                 if( rc == SLAPD_DISCONNECT ) {
589                         send_ldap_disconnect( conn, op,
590                                 LDAP_PROTOCOL_ERROR, *text );
591                 } else {
592                         send_ldap_result( conn, op, rc,
593                                 NULL, *text, NULL, NULL );
594                 }
595                 if( fstr.bv_val != NULL) free( fstr.bv_val );
596                 if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); 
597
598         } else {
599                 vrFilter2bv( op->vrFilter, &fstr );
600         }
601
602 #ifdef NEW_LOGGING
603         LDAP_LOG( OPERATION, ARGS, 
604                 "parseValuesReturnFilter: conn %d       vrFilter: %s\n", 
605                 conn->c_connid, fstr.bv_len ? fstr.bv_val : "empty" , 0 );
606 #else
607         Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
608                 fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
609 #endif
610
611         op->o_valuesreturnfilter = ctrl->ldctl_iscritical
612                 ? SLAP_CRITICAL_CONTROL
613                 : SLAP_NONCRITICAL_CONTROL;
614
615         return LDAP_SUCCESS;
616 }
617 #endif
618
619 #ifdef LDAP_CLIENT_UPDATE
620 static int parseClientUpdate (
621         Connection *conn,
622         Operation *op,
623         LDAPControl *ctrl,
624         const char **text )
625 {
626         ber_tag_t tag;
627         BerElement *ber;
628         ber_int_t type;
629         ber_int_t interval;
630         ber_len_t len;
631         struct berval scheme = { 0, NULL };
632         struct berval cookie = { 0, NULL };
633
634         if ( op->o_clientupdate != SLAP_NO_CONTROL ) {
635                 *text = "LCUP client update control specified multiple times";
636                 return LDAP_PROTOCOL_ERROR;
637         }
638
639         if ( ctrl->ldctl_value.bv_len == 0 ) {
640                 *text = "LCUP client update control value is empty";
641                 return LDAP_PROTOCOL_ERROR;
642         }
643
644         /* Parse the control value
645          *      ClientUpdateControlValue ::= SEQUENCE {
646          *              updateType      ENUMERATED {
647          *                                      synchronizeOnly {0},
648          *                                      synchronizeAndPersist {1},
649          *                                      persistOnly {2} },
650          *              sendCookieInterval INTEGER OPTIONAL,
651          *              cookie          LCUPCookie OPTIONAL
652          *      }
653          */
654
655         ber = ber_init( &ctrl->ldctl_value );
656         if( ber == NULL ) {
657                 *text = "internal error";
658                 return LDAP_OTHER;
659         }
660
661         if ( (tag = ber_scanf( ber, "{i" /*}*/, &type )) == LBER_ERROR ) {
662                 *text = "LCUP client update control : decoding error";
663                 return LDAP_PROTOCOL_ERROR;
664         }
665
666         switch( type ) {
667         case LDAP_CUP_SYNC_ONLY:
668                 type = SLAP_LCUP_SYNC;
669                 break;
670         case LDAP_CUP_SYNC_AND_PERSIST:
671                 type = SLAP_LCUP_SYNC_AND_PERSIST;
672                 break;
673         case LDAP_CUP_PERSIST_ONLY:
674                 type = SLAP_LCUP_PERSIST;
675                 break;
676         default:
677                 *text = "LCUP client update control : unknown update type";
678                 return LDAP_PROTOCOL_ERROR;
679         }
680
681         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
682                 *text = "LCUP client update control : decoding error";
683                 return LDAP_PROTOCOL_ERROR;
684         }
685
686         if ( tag == LDAP_TAG_INTERVAL ) {
687                 if ( (tag = ber_scanf( ber, "i", &interval )) == LBER_ERROR ) {
688                         *text = "LCUP client update control : decoding error";
689                         return LDAP_PROTOCOL_ERROR;
690                 }
691                 
692                 if ( interval <= 0 ) {
693                         /* server chooses interval */
694                         interval = LDAP_CUP_DEFAULT_SEND_COOKIE_INTERVAL;
695                 }
696
697         } else {
698                 /* server chooses interval */
699                 interval = LDAP_CUP_DEFAULT_SEND_COOKIE_INTERVAL;
700         }
701
702         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
703                 *text = "LCUP client update control : decoding error";
704                 return LDAP_PROTOCOL_ERROR;
705         }
706
707         if ( tag == LDAP_TAG_COOKIE ) {
708                 if ( (tag = ber_scanf( ber, /*{*/ "{mm}}",
709                                         &scheme, &cookie )) == LBER_ERROR ) {
710                         *text = "LCUP client update control : decoding error";
711                         return LDAP_PROTOCOL_ERROR;
712                 }
713         }
714
715         /* TODO : Cookie Scheme Validation */
716 #if 0
717         if ( lcup_cookie_scheme_validate(scheme) != LDAP_SUCCESS ) {
718                 *text = "Unsupported LCUP cookie scheme";
719                 return LCUP_UNSUPPORTED_SCHEME;
720         }
721
722         if ( lcup_cookie_validate(scheme, cookie) != LDAP_SUCCESS ) {
723                 *text = "Invalid LCUP cookie";
724                 return LCUP_INVALID_COOKIE;
725         }
726 #endif
727
728         ber_dupbv( &op->o_clientupdate_state, &cookie );
729
730         (void) ber_free( ber, 1 );
731
732         op->o_clientupdate_type = (char) type;
733         op->o_clientupdate_interval = interval;
734
735         op->o_clientupdate = ctrl->ldctl_iscritical
736                 ? SLAP_CRITICAL_CONTROL
737                 : SLAP_NONCRITICAL_CONTROL;
738
739         return LDAP_SUCCESS;
740 }
741 #endif /* LDAP_CLIENT_UPDATE */