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