]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
fix negative counters; prepare for imrpved count of sent data
[openldap] / servers / slapd / limits.c
1 /* limits.c - routines to handle regex-based size and time limits */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2004 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/regex.h>
22 #include <ac/string.h>
23
24 #include "slap.h"
25
26 /* define to get an error if requesting limit higher than hard */
27 #undef ABOVE_HARD_LIMIT_IS_ERROR
28
29 static char *
30 limits2str( unsigned i )
31 {
32         switch ( i ) {
33         case SLAP_LIMITS_UNDEFINED:
34                 return "UNDEFINED";
35
36         case SLAP_LIMITS_EXACT:
37                 return "EXACT";
38                         
39         case SLAP_LIMITS_ONE:
40                 return "ONELEVEL";      
41
42         case SLAP_LIMITS_SUBTREE:
43                 return "SUBTREE";
44
45         case SLAP_LIMITS_CHILDREN:
46                 return "CHILDREN";
47
48         case SLAP_LIMITS_REGEX:
49                 return "REGEX";
50
51         case SLAP_LIMITS_ANONYMOUS:
52                 return "ANONYMOUS";
53                 
54         case SLAP_LIMITS_USERS:
55                 return "USERS";
56                 
57         case SLAP_LIMITS_ANY:
58                 return "ANY";
59
60         default:
61                 return "UNKNOWN";
62         }
63 }
64
65 int
66 limits_get( 
67         Operation               *op,
68         struct berval           *ndn, 
69         struct slap_limits_set  **limit
70 )
71 {
72         struct slap_limits **lm;
73
74         assert( op );
75         assert( limit );
76
77         Debug( LDAP_DEBUG_TRACE, "==> limits_get: conn=%lu op=%lu dn=\"%s\"\n",
78                         op->o_connid, op->o_opid,
79                         BER_BVISNULL( ndn ) ? "[anonymous]" : ndn->bv_val );
80         /*
81          * default values
82          */
83         *limit = &op->o_bd->be_def_limit;
84
85         if ( op->o_bd->be_limits == NULL ) {
86                 return( 0 );
87         }
88
89         for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
90                 unsigned        style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
91                 unsigned        type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
92
93                 switch ( style ) {
94                 case SLAP_LIMITS_EXACT:
95                         if ( ndn->bv_len == 0 ) {
96                                 break;
97                         }
98
99                         if ( type == SLAP_LIMITS_TYPE_GROUP ) {
100                                 int     rc;
101
102                                 rc = backend_group( op, NULL,
103                                                 &lm[0]->lm_pat, ndn,
104                                                 lm[0]->lm_group_oc,
105                                                 lm[0]->lm_group_ad );
106                                 if ( rc == 0 ) {
107                                         *limit = &lm[0]->lm_limits;
108                                         Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
109                                                         "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
110                                                         lm[0]->lm_pat.bv_val,
111                                                         lm[0]->lm_group_oc->soc_cname.bv_val,
112                                                         lm[0]->lm_group_ad->ad_cname.bv_val );
113
114                                         return( 0 );
115                                 }
116                         } else {
117                         
118                                 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
119                                         *limit = &lm[0]->lm_limits;
120                                         Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=EXACT dn=\"%s\"\n",
121                                                         lm[0]->lm_pat.bv_val, 0, 0 );
122                                         return( 0 );
123                                 }
124                         }
125                         break;
126
127                 case SLAP_LIMITS_ONE:
128                 case SLAP_LIMITS_SUBTREE:
129                 case SLAP_LIMITS_CHILDREN: {
130                         size_t d;
131                         
132                         if ( ndn->bv_len == 0 ) {
133                                 break;
134                         }
135
136                         /* ndn shorter than dn_pat */
137                         if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
138                                 break;
139                         }
140                         d = ndn->bv_len - lm[0]->lm_pat.bv_len;
141
142                         /* allow exact match for SUBTREE only */
143                         if ( d == 0 ) {
144                                 if ( style != SLAP_LIMITS_SUBTREE ) {
145                                         break;
146                                 }
147                         } else {
148                                 /* check for unescaped rdn separator */
149                                 if ( !DN_SEPARATOR( ndn->bv_val[d-1] ) ) {
150                                         break;
151                                 }
152                         }
153
154                         /* in case of (sub)match ... */
155                         if ( lm[0]->lm_pat.bv_len == ( ndn->bv_len - d )
156                                         && strcmp( lm[0]->lm_pat.bv_val,
157                                                 &ndn->bv_val[d] ) == 0 )
158                         {
159                                 /* check for exactly one rdn in case of ONE */
160                                 if ( style == SLAP_LIMITS_ONE ) {
161                                         /*
162                                          * if ndn is more that one rdn
163                                          * below dn_pat, continue
164                                          */
165                                         if ( (size_t) dn_rdnlen( NULL, ndn )
166                                                         != d - 1 )
167                                         {
168                                                 break;
169                                         }
170                                 }
171
172                                 *limit = &lm[0]->lm_limits;
173                                 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
174                                                 limits2str( style ), lm[0]->lm_pat.bv_val, 0 );
175                                 return( 0 );
176                         }
177
178                         break;
179                 }
180
181                 case SLAP_LIMITS_REGEX:
182                         if ( ndn->bv_len == 0 ) {
183                                 break;
184                         }
185                         if ( regexec( &lm[0]->lm_regex, ndn->bv_val,
186                                                 0, NULL, 0 ) == 0 )
187                         {
188                                 *limit = &lm[0]->lm_limits;
189                                 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
190                                                 limits2str( style ), lm[0]->lm_pat.bv_val, 0 );
191                                 return( 0 );
192                         }
193                         break;
194
195                 case SLAP_LIMITS_ANONYMOUS:
196                         if ( ndn->bv_len == 0 ) {
197                                 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s\n",
198                                                 limits2str( style ), 0, 0 );
199                                 *limit = &lm[0]->lm_limits;
200                                 return( 0 );
201                         }
202                         break;
203
204                 case SLAP_LIMITS_USERS:
205                         if ( ndn->bv_len != 0 ) {
206                                 *limit = &lm[0]->lm_limits;
207                                 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=DN match=%s\n",
208                                                 limits2str( style ), 0, 0 );
209                                 return( 0 );
210                         }
211                         break;
212
213                 case SLAP_LIMITS_ANY:
214                         *limit = &lm[0]->lm_limits;
215                         return( 0 );
216
217                 default:
218                         assert( 0 );    /* unreachable */
219                         return( -1 );
220                 }
221         }
222
223         return( 0 );
224 }
225
226 static int
227 limits_add(
228         Backend                 *be,
229         unsigned                flags,
230         const char              *pattern,
231         ObjectClass             *group_oc,
232         AttributeDescription    *group_ad,
233         struct slap_limits_set  *limit
234 )
235 {
236         int                     i;
237         struct slap_limits      *lm;
238         unsigned                type, style;
239         
240         assert( be );
241         assert( limit );
242
243         type = flags & SLAP_LIMITS_TYPE_MASK;
244         style = flags & SLAP_LIMITS_MASK;
245
246         switch ( style ) {
247         case SLAP_LIMITS_ANONYMOUS:
248         case SLAP_LIMITS_USERS:
249         case SLAP_LIMITS_ANY:
250                 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
251                         if ( be->be_limits[ i ]->lm_flags == style ) {
252                                 return( -1 );
253                         }
254                 }
255                 break;
256         }
257
258
259         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
260
261         switch ( style ) {
262         case SLAP_LIMITS_UNDEFINED:
263                 style = SLAP_LIMITS_EXACT;
264                 /* continue to next cases */
265         case SLAP_LIMITS_EXACT:
266         case SLAP_LIMITS_ONE:
267         case SLAP_LIMITS_SUBTREE:
268         case SLAP_LIMITS_CHILDREN:
269                 lm->lm_flags = style | type;
270                 {
271                         int rc;
272                         struct berval bv;
273                         bv.bv_val = (char *) pattern;
274                         bv.bv_len = strlen( pattern );
275
276                         rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
277                         if ( rc != LDAP_SUCCESS ) {
278                                 ch_free( lm );
279                                 return( -1 );
280                         }
281                 }
282                 break;
283                 
284         case SLAP_LIMITS_REGEX:
285                 lm->lm_flags = style | type;
286                 ber_str2bv( pattern, 0, 1, &lm->lm_pat );
287                 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 
288                                         REG_EXTENDED | REG_ICASE ) ) {
289                         free( lm->lm_pat.bv_val );
290                         ch_free( lm );
291                         return( -1 );
292                 }
293                 break;
294
295         case SLAP_LIMITS_ANONYMOUS:
296         case SLAP_LIMITS_USERS:
297         case SLAP_LIMITS_ANY:
298                 lm->lm_flags = style | type;
299                 lm->lm_pat.bv_val = NULL;
300                 lm->lm_pat.bv_len = 0;
301                 break;
302         }
303
304         switch ( type ) {
305         case SLAP_LIMITS_TYPE_GROUP:
306                 assert( group_oc );
307                 assert( group_ad );
308                 lm->lm_group_oc = group_oc;
309                 lm->lm_group_ad = group_ad;
310                 break;
311         }
312
313         lm->lm_limits = *limit;
314
315         i = 0;
316         if ( be->be_limits != NULL ) {
317                 for ( ; be->be_limits[i]; i++ );
318         }
319
320         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
321                         sizeof( struct slap_limits * ) * ( i + 2 ) );
322         be->be_limits[i] = lm;
323         be->be_limits[i+1] = NULL;
324         
325         return( 0 );
326 }
327
328 int
329 limits_parse(
330         Backend     *be,
331         const char  *fname,
332         int         lineno,
333         int         argc,
334         char        **argv
335 )
336 {
337         int                     flags = SLAP_LIMITS_UNDEFINED;
338         char                    *pattern;
339         struct slap_limits_set  limit;
340         int                     i, rc = 0;
341         ObjectClass             *group_oc = NULL;
342         AttributeDescription    *group_ad = NULL;
343
344         assert( be );
345
346         if ( argc < 3 ) {
347                 Debug( LDAP_DEBUG_ANY,
348                         "%s : line %d: missing arg(s) in "
349                         "\"limits <pattern> <limits>\" line.\n%s",
350                         fname, lineno, "" );
351                 return( -1 );
352         }
353
354         limit = be->be_def_limit;
355
356         /*
357          * syntax:
358          *
359          * "limits" <pattern> <limit> [ ... ]
360          * 
361          * 
362          * <pattern>:
363          * 
364          * "anonymous"
365          * "users"
366          * [ "dn" [ "." { "exact" | "base" | "onelevel" | "subtree" | children"
367          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
368          *
369          * Note:
370          *      "exact" and "base" are the same (exact match);
371          *      "onelevel" means exactly one rdn below, NOT including pattern
372          *      "subtree" means any rdn below, including pattern
373          *      "children" means any rdn below, NOT including pattern
374          *      
375          *      "anonymous" may be deprecated in favour 
376          *      of the pattern = "anonymous" form
377          *
378          * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
379          *
380          * <limit>:
381          *
382          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
383          *
384          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
385          */
386         
387         pattern = argv[1];
388         if ( strcmp( pattern, "*" ) == 0) {
389                 flags = SLAP_LIMITS_ANY;
390
391         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
392                 flags = SLAP_LIMITS_ANONYMOUS;
393
394         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
395                 flags = SLAP_LIMITS_USERS;
396                 
397         } else if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
398                 pattern += STRLENOF( "dn" );
399                 if ( pattern[0] == '.' ) {
400                         pattern++;
401                         if ( strncasecmp( pattern, "exact", STRLENOF( "exact" )) == 0 ) {
402                                 flags = SLAP_LIMITS_EXACT;
403                                 pattern += STRLENOF( "exact" );
404
405                         } else if ( strncasecmp( pattern, "base", STRLENOF( "base" ) ) == 0 ) {
406                                 flags = SLAP_LIMITS_BASE;
407                                 pattern += STRLENOF( "base" );
408
409                         } else if ( strncasecmp( pattern, "one", STRLENOF( "one" ) ) == 0 ) {
410                                 flags = SLAP_LIMITS_ONE;
411                                 pattern += STRLENOF( "one" );
412                                 if ( strncasecmp( pattern, "level", STRLENOF( "level" ) ) == 0 ) {
413                                         pattern += STRLENOF( "level" );
414
415                                 } else {
416                                         Debug( LDAP_DEBUG_ANY,
417                                                 "%s : line %d: deprecated \"one\" style "
418                                                 "\"limits <pattern> <limits>\" line; "
419                                                 "use \"onelevel\" instead.\n", fname, lineno, 0 );
420                                 }
421
422                         } else if ( strncasecmp( pattern, "sub", STRLENOF( "sub" ) ) == 0 ) {
423                                 flags = SLAP_LIMITS_SUBTREE;
424                                 pattern += STRLENOF( "sub" );
425                                 if ( strncasecmp( pattern, "tree", STRLENOF( "tree" ) ) == 0 ) {
426                                         pattern += STRLENOF( "tree" );
427
428                                 } else {
429                                         Debug( LDAP_DEBUG_ANY,
430                                                 "%s : line %d: deprecated \"sub\" style "
431                                                 "\"limits <pattern> <limits>\" line; "
432                                                 "use \"subtree\" instead.\n", fname, lineno, 0 );
433                                 }
434
435                         } else if ( strncasecmp( pattern, "children", STRLENOF( "children" ) ) == 0 ) {
436                                 flags = SLAP_LIMITS_CHILDREN;
437                                 pattern += STRLENOF( "children" );
438
439                         } else if ( strncasecmp( pattern, "regex", STRLENOF( "regex" ) ) == 0 ) {
440                                 flags = SLAP_LIMITS_REGEX;
441                                 pattern += STRLENOF( "regex" );
442
443                         /* 
444                          * this could be deprecated in favour
445                          * of the pattern = "anonymous" form
446                          */
447                         } else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0 ) {
448                                 flags = SLAP_LIMITS_ANONYMOUS;
449                                 pattern = NULL;
450                         }
451                 }
452
453                 /* pre-check the data */
454                 switch ( flags ) {
455                 case SLAP_LIMITS_ANONYMOUS:
456                 case SLAP_LIMITS_USERS:
457
458                         /* no need for pattern */
459                         pattern = NULL;
460                         break;
461
462                 default:
463                         if ( pattern[0] != '=' ) {
464                                 Debug( LDAP_DEBUG_ANY,
465                                         "%s : line %d: missing '=' in "
466                                         "\"dn[.{exact|base|onelevel|subtree"
467                                         "|children|regex|anonymous}]"
468                                         "=<pattern>\" in "
469                                         "\"limits <pattern> <limits>\" "
470                                         "line.\n%s",
471                                         fname, lineno, "" );
472                                 return( -1 );
473                         }
474
475                         /* skip '=' (required) */
476                         pattern++;
477
478                         /* trim obvious cases */
479                         if ( strcmp( pattern, "*" ) == 0 ) {
480                                 flags = SLAP_LIMITS_ANY;
481                                 pattern = NULL;
482
483                         } else if ( flags == SLAP_LIMITS_REGEX
484                                         && strcmp( pattern, ".*" ) == 0 ) {
485                                 flags = SLAP_LIMITS_ANY;
486                                 pattern = NULL;
487                         }
488                 }
489
490         } else if (strncasecmp( pattern, "group", STRLENOF( "group" ) ) == 0 ) {
491                 pattern += STRLENOF( "group" );
492
493                 if ( pattern[0] == '/' ) {
494                         struct berval   oc, ad;
495
496                         oc.bv_val = pattern + 1;
497
498                         ad.bv_val = strchr(pattern, '/');
499                         if ( ad.bv_val != NULL ) {
500                                 const char      *text = NULL;
501                                 int             rc;
502
503                                 oc.bv_len = ad.bv_val - oc.bv_val;
504
505                                 ad.bv_val++;
506                                 ad.bv_len = strlen( ad.bv_val );
507                                 rc = slap_bv2ad( &ad, &group_ad, &text );
508                                 if ( rc != LDAP_SUCCESS ) {
509                                         goto no_ad;
510                                 }
511
512                                 pattern = ad.bv_val + ad.bv_len;
513
514                         } else {
515                                 oc.bv_len = strlen( oc.bv_val );
516
517                                 pattern = oc.bv_val + oc.bv_len;
518                         }
519
520                         group_oc = oc_bvfind( &oc );
521                         if ( group_oc == NULL ) {
522                                 goto no_oc;
523                         }
524                 }
525
526                 if ( group_oc == NULL ) {
527                         group_oc = oc_find( SLAPD_GROUP_CLASS );
528                         if ( group_oc == NULL ) {
529 no_oc:;
530                                 return( -1 );
531                         }
532                 }
533
534                 if ( group_ad == NULL ) {
535                         const char      *text = NULL;
536                         int             rc;
537                         
538                         rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
539
540                         if ( rc != LDAP_SUCCESS ) {
541 no_ad:;
542                                 return( -1 );
543                         }
544                 }
545
546                 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
547
548                 if ( pattern[0] != '=' ) {
549                         Debug( LDAP_DEBUG_ANY,
550                                 "%s : line %d: missing '=' in "
551                                 "\"group[/objectClass[/attributeType]]"
552                                 "=<pattern>\" in "
553                                 "\"limits <pattern> <limits>\" line.\n",
554                                 fname, lineno, 0 );
555                         return( -1 );
556                 }
557
558                 /* skip '=' (required) */
559                 pattern++;
560         }
561
562         /* get the limits */
563         for ( i = 2; i < argc; i++ ) {
564                 if ( limits_parse_one( argv[i], &limit ) ) {
565
566                         Debug( LDAP_DEBUG_ANY,
567                                 "%s : line %d: unknown limit values \"%s\" in "
568                                 "\"limits <pattern> <limits>\" line.\n",
569                         fname, lineno, argv[i] );
570
571                         return( 1 );
572                 }
573         }
574
575         /*
576          * sanity checks ...
577          *
578          * FIXME: add warnings?
579          */
580         if ( limit.lms_t_hard > 0 && 
581                         ( limit.lms_t_hard < limit.lms_t_soft 
582                           || limit.lms_t_soft == -1 ) ) {
583                 limit.lms_t_hard = limit.lms_t_soft;
584         }
585         
586         if ( limit.lms_s_hard > 0 && 
587                         ( limit.lms_s_hard < limit.lms_s_soft 
588                           || limit.lms_s_soft == -1 ) ) {
589                 limit.lms_s_hard = limit.lms_s_soft;
590         }
591
592         /*
593          * defaults ...
594          * 
595          * lms_t_hard:
596          *      -1      => no limits
597          *      0       => same as soft
598          *      > 0     => limit (in seconds)
599          *
600          * lms_s_hard:
601          *      -1      => no limits
602          *      0       0> same as soft
603          *      > 0     => limit (in entries)
604          *
605          * lms_s_pr_total:
606          *      -2      => disable the control
607          *      -1      => no limits
608          *      0       => same as soft
609          *      > 0     => limit (in entries)
610          *
611          * lms_s_pr:
612          *      -1      => no limits
613          *      0       => no limits?
614          *      > 0     => limit size (in entries)
615          */
616         if ( limit.lms_s_pr_total > 0 &&
617                         limit.lms_s_pr > limit.lms_s_pr_total ) {
618                 limit.lms_s_pr = limit.lms_s_pr_total;
619         }
620
621         rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
622         if ( rc ) {
623
624                 Debug( LDAP_DEBUG_ANY,
625                         "%s : line %d: unable to add limit in "
626                         "\"limits <pattern> <limits>\" line.\n",
627                 fname, lineno, 0 );
628         }
629
630         return( rc );
631 }
632
633 int
634 limits_parse_one(
635         const char              *arg,
636         struct slap_limits_set  *limit
637 )
638 {
639         assert( arg );
640         assert( limit );
641
642         if ( strncasecmp( arg, "time", STRLENOF( "time" ) ) == 0 ) {
643                 arg += STRLENOF( "time" );
644
645                 if ( arg[0] == '.' ) {
646                         arg++;
647                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
648                                 arg += STRLENOF( "soft=" );
649                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
650                                         limit->lms_t_soft = -1;
651
652                                 } else {
653                                         char    *next = NULL;
654                                         int     soft = strtol( arg, &next, 10 );
655
656                                         if ( next == arg || next[ 0 ] != '\0' ) {
657                                                 return( 1 );
658                                         }
659
660                                         if ( soft < -1 ) {
661                                                 return( 1 );
662                                         }
663
664                                         if ( soft == -1 ) {
665                                                 /* FIXME: use "unlimited" instead; issue warning? */
666                                         }
667
668                                         limit->lms_t_soft = soft;
669                                 }
670                                 
671                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
672                                 arg += STRLENOF( "hard=" );
673                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
674                                         limit->lms_t_hard = 0;
675
676                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
677                                         limit->lms_t_hard = -1;
678
679                                 } else {
680                                         char    *next = NULL;
681                                         int     hard = strtol( arg, &next, 10 );
682
683                                         if ( next == arg || next[ 0 ] != '\0' ) {
684                                                 return( 1 );
685                                         }
686
687                                         if ( hard < -1 ) {
688                                                 return( 1 );
689                                         }
690
691                                         if ( hard == -1 ) {
692                                                 /* FIXME: use "unlimited" instead */
693                                         }
694
695                                         if ( hard == 0 ) {
696                                                 /* FIXME: use "soft" instead */
697                                         }
698
699                                         limit->lms_t_hard = hard;
700                                 }
701                                 
702                         } else {
703                                 return( 1 );
704                         }
705                         
706                 } else if ( arg[0] == '=' ) {
707                         arg++;
708                         if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
709                                 limit->lms_t_soft = -1;
710
711                         } else {
712                                 char    *next = NULL;
713
714                                 limit->lms_t_soft = strtol( arg, &next, 10 );
715                                 if ( next == arg || limit->lms_t_soft < -1 ) {
716                                         return( 1 );
717                                 }
718                         }
719                         limit->lms_t_hard = 0;
720                         
721                 } else {
722                         return( 1 );
723                 }
724
725         } else if ( strncasecmp( arg, "size", STRLENOF( "size" ) ) == 0 ) {
726                 arg += STRLENOF( "size" );
727                 
728                 if ( arg[0] == '.' ) {
729                         arg++;
730                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
731                                 arg += STRLENOF( "soft=" );
732                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
733                                         limit->lms_s_soft = -1;
734
735                                 } else {
736                                         char    *next = NULL;
737                                         int     soft = strtol( arg, &next, 10 );
738
739                                         if ( next == arg || next[ 0 ] != '\0' ) {
740                                                 return( 1 );
741                                         }
742
743                                         if ( soft < -1 ) {
744                                                 return( 1 );
745                                         }
746
747                                         if ( soft == -1 ) {
748                                                 /* FIXME: use "unlimited" instead */
749                                         }
750
751                                         limit->lms_s_soft = soft;
752                                 }
753                                 
754                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
755                                 arg += STRLENOF( "hard=" );
756                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
757                                         limit->lms_s_hard = 0;
758
759                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
760                                         limit->lms_s_hard = -1;
761
762                                 } else {
763                                         char    *next = NULL;
764                                         int     hard = strtol( arg, &next, 10 );
765
766                                         if ( next == arg || next[ 0 ] != '\0' ) {
767                                                 return( 1 );
768                                         }
769
770                                         if ( hard < -1 ) {
771                                                 return( 1 );
772                                         }
773
774                                         if ( hard == -1 ) {
775                                                 /* FIXME: use "unlimited" instead */
776                                         }
777
778                                         if ( hard == 0 ) {
779                                                 /* FIXME: use "soft" instead */
780                                         }
781
782                                         limit->lms_s_hard = hard;
783                                 }
784                                 
785                         } else if ( strncasecmp( arg, "unchecked=", STRLENOF( "unchecked=" ) ) == 0 ) {
786                                 arg += STRLENOF( "unchecked=" );
787                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
788                                         limit->lms_s_unchecked = -1;
789
790                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
791                                         limit->lms_s_unchecked = 0;
792
793                                 } else {
794                                         char    *next = NULL;
795                                         int     unchecked = strtol( arg, &next, 10 );
796
797                                         if ( next == arg || next[ 0 ] != '\0' ) {
798                                                 return( 1 );
799                                         }
800
801                                         if ( unchecked < -1 ) {
802                                                 return( 1 );
803                                         }
804
805                                         if ( unchecked == -1 ) {
806                                                 /*  FIXME: use "unlimited" instead */
807                                         }
808
809                                         limit->lms_s_unchecked = unchecked;
810                                 }
811
812                         } else if ( strncasecmp( arg, "pr=", STRLENOF( "pr=" ) ) == 0 ) {
813                                 arg += STRLENOF( "pr=" );
814                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
815                                         limit->lms_s_pr_hide = 1;
816
817                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
818                                         limit->lms_s_pr = -1;
819
820                                 } else {
821                                         char    *next = NULL;
822                                         int     pr = strtol( arg, &next, 10 );
823
824                                         if ( next == arg || next[ 0 ] != '\0' ) {
825                                                 return( 1 );
826                                         }
827
828                                         if ( pr < -1 ) {
829                                                 return( 1 );
830                                         }
831
832                                         if ( pr == -1 ) {
833                                                 /* FIXME: use "unlimited" instead */
834                                         }
835
836                                         limit->lms_s_pr = pr;
837                                 }
838
839                         } else if ( strncasecmp( arg, "prtotal=", STRLENOF( "prtotal=" ) ) == 0 ) {
840                                 arg += STRLENOF( "prtotal=" );
841
842                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
843                                         limit->lms_s_pr_total = -1;
844
845                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
846                                         limit->lms_s_pr_total = -2;
847
848                                 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
849                                         limit->lms_s_pr_total = 0;
850
851                                 } else {
852                                         char    *next = NULL;
853                                         int     total;
854
855                                         total = strtol( arg, &next, 10 );
856                                         if ( next == arg || next[ 0 ] != '\0' ) {
857                                                 return( 1 );
858                                         }
859
860                                         if ( total < -1 ) {
861                                                 return( 1 );
862                                         }
863
864                                         if ( total == -1 ) {
865                                                 /* FIXME: use "unlimited" instead */
866                                         }
867
868                                         if ( total == 0 ) {
869                                                 /* FIXME: use "pr=disable" instead */
870                                         }
871
872                                         limit->lms_s_pr_total = total;
873                                 }
874
875                         } else {
876                                 return( 1 );
877                         }
878                         
879                 } else if ( arg[0] == '=' ) {
880                         arg++;
881                         if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
882                                 limit->lms_s_soft = -1;
883
884                         } else {
885                                 char    *next = NULL;
886
887                                 limit->lms_s_soft = strtol( arg, &next, 10 );
888                                 if ( next == arg || limit->lms_s_soft < -1 ) {
889                                         return( 1 );
890                                 }
891                         }
892                         limit->lms_s_hard = 0;
893                         
894                 } else {
895                         return( 1 );
896                 }
897         }
898
899         return 0;
900 }
901
902
903 int
904 limits_check( Operation *op, SlapReply *rs )
905 {
906         assert( op );
907         assert( rs );
908         /* FIXME: should this be always true? */
909         assert( op->o_tag == LDAP_REQ_SEARCH);
910
911         /* protocol only allows 0..maxInt;
912          *
913          * internal searches:
914          * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
915          * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
916          *   indicate searches that should return exactly N matches,
917          *   and handle errors thru a callback (see for instance
918          *   slap_sasl_match() and slap_sasl2dn())
919          */
920         if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
921                 return 0;
922         }
923
924         /* allow root to set no limit */
925         if ( be_isroot( op ) ) {
926                 op->ors_limit = NULL;
927
928                 if ( op->ors_tlimit == 0 ) {
929                         op->ors_tlimit = SLAP_NO_LIMIT;
930                 }
931
932                 if ( op->ors_slimit == 0 ) {
933                         op->ors_slimit = SLAP_NO_LIMIT;
934                 }
935
936         /* if not root, get appropriate limits */
937         } else {
938                 ( void ) limits_get( op, &op->o_ndn, &op->ors_limit );
939
940                 assert( op->ors_limit != NULL );
941
942                 /* if no limit is required, use soft limit */
943                 if ( op->ors_tlimit == 0 ) {
944                         op->ors_tlimit = op->ors_limit->lms_t_soft;
945
946                 /* limit required: check if legal */
947                 } else {
948                         if ( op->ors_limit->lms_t_hard == 0 ) {
949                                 if ( op->ors_limit->lms_t_soft > 0
950                                                 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
951                                         op->ors_tlimit = op->ors_limit->lms_t_soft;
952                                 }
953
954                         } else if ( op->ors_limit->lms_t_hard > 0 ) {
955 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
956                                 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
957                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
958
959                                 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
960                                         /* error if exceeding hard limit */
961                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
962                                         send_ldap_result( op, rs );
963                                         rs->sr_err = LDAP_SUCCESS;
964                                         return -1;
965                                 }
966 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
967                                 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
968                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
969                                 }
970 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
971                         }
972                 }
973
974                 /* else leave as is */
975
976                 /* don't even get to backend if candidate check is disabled */
977                 if ( op->ors_limit->lms_s_unchecked == 0 ) {
978                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
979                         send_ldap_result( op, rs );
980                         rs->sr_err = LDAP_SUCCESS;
981                         return -1;
982                 }
983
984                 /* if paged results is requested */     
985                 if ( get_pagedresults( op ) > SLAP_NO_CONTROL ) {
986                         int     slimit = -2;
987                         int     pr_total;
988
989                         /* paged results is not allowed */
990                         if ( op->ors_limit->lms_s_pr_total == -2 ) {
991                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
992                                 rs->sr_text = "pagedResults control not allowed";
993                                 send_ldap_result( op, rs );
994                                 rs->sr_err = LDAP_SUCCESS;
995                                 rs->sr_text = NULL;
996                                 return -1;
997                         }
998                         
999                         if ( op->ors_limit->lms_s_pr > 0 && op->o_pagedresults_size > op->ors_limit->lms_s_pr ) {
1000                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1001                                 rs->sr_text = "illegal pagedResults page size";
1002                                 send_ldap_result( op, rs );
1003                                 rs->sr_err = LDAP_SUCCESS;
1004                                 rs->sr_text = NULL;
1005                                 return -1;
1006                         }
1007
1008                         if ( op->ors_limit->lms_s_pr_total == 0 ) {
1009                                 if ( op->ors_limit->lms_s_hard == 0 ) {
1010                                         pr_total = op->ors_limit->lms_s_soft;
1011                                 } else {
1012                                         pr_total = op->ors_limit->lms_s_hard;
1013                                 }
1014                         } else {
1015                                 pr_total = op->ors_limit->lms_s_pr_total;
1016                         }
1017
1018                         if ( pr_total == -1 ) {
1019                                 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1020                                         slimit = -1;
1021
1022                                 } else {
1023                                         slimit = op->ors_slimit - op->o_pagedresults_state.ps_count;
1024                                 }
1025
1026 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1027                         } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
1028                                         && ( op->ors_slimit == SLAP_NO_LIMIT || op->ors_slimit > pr_total ) )
1029                         {
1030                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1031                                 send_ldap_result( op, rs );
1032                                 rs->sr_err = LDAP_SUCCESS;
1033                                 return -1;
1034 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1035         
1036                         } else {
1037                                 /* if no limit is required, use soft limit */
1038                                 int     total;
1039                                 int     slimit2;
1040
1041                                 /* first round of pagedResults: set count to any appropriate limit */
1042
1043                                 /* if the limit is set, check that it does not violate any server-side limit */
1044 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1045                                 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1046                                         slimit2 = op->ors_slimit = pr_total;
1047 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1048                                 if ( op->ors_slimit == SLAP_MAX_LIMIT || op->ors_slimit > pr_total ) {
1049                                         slimit2 = op->ors_slimit = pr_total;
1050 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1051
1052                                 } else if ( op->ors_slimit == 0 ) {
1053                                         slimit2 = pr_total;
1054
1055                                 } else {
1056                                         slimit2 = op->ors_slimit;
1057                                 }
1058
1059                                 total = slimit2 - op->o_pagedresults_state.ps_count;
1060
1061                                 if ( total >= 0 ) {
1062                                         if ( op->ors_limit->lms_s_pr > 0 ) {
1063                                                 /* use the smallest limit set by total/per page */
1064                                                 if ( total < op->ors_limit->lms_s_pr ) {
1065                                                         slimit = total;
1066         
1067                                                 } else {
1068                                                         /* use the perpage limit if any 
1069                                                          * NOTE: + 1 because the given value must be legal */
1070                                                         slimit = op->ors_limit->lms_s_pr + 1;
1071                                                 }
1072
1073                                         } else {
1074                                                 /* use the total limit if any */
1075                                                 slimit = total;
1076                                         }
1077
1078                                 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1079                                         /* use the perpage limit if any 
1080                                          * NOTE: + 1 because the given value must be legal */
1081                                         slimit = op->ors_limit->lms_s_pr + 1;
1082
1083                                 } else {
1084                                         /* use the standard hard/soft limit if any */
1085                                         slimit = op->ors_limit->lms_s_hard;
1086                                 }
1087                         }
1088                 
1089                         /* if got any limit, use it */
1090                         if ( slimit != -2 ) {
1091                                 if ( op->ors_slimit == 0 ) {
1092                                         op->ors_slimit = slimit;
1093
1094                                 } else if ( slimit > 0 ) {
1095                                         if ( op->ors_slimit - op->o_pagedresults_state.ps_count > slimit ) {
1096                                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1097                                                 send_ldap_result( op, rs );
1098                                                 rs->sr_err = LDAP_SUCCESS;
1099                                                 return -1;
1100                                         }
1101                                         op->ors_slimit = slimit;
1102                                 }
1103
1104                         } else {
1105                                 /* use the standard hard/soft limit if any */
1106                                 op->ors_slimit = pr_total;
1107                         }
1108
1109                 /* no limit requested: use soft, whatever it is */
1110                 } else if ( op->ors_slimit == 0 ) {
1111                         op->ors_slimit = op->ors_limit->lms_s_soft;
1112
1113                 /* limit requested: check if legal */
1114                 } else {
1115                         /* hard limit as soft (traditional behavior) */
1116                         if ( op->ors_limit->lms_s_hard == 0 ) {
1117                                 if ( op->ors_limit->lms_s_soft > 0
1118                                                 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1119                                         op->ors_slimit = op->ors_limit->lms_s_soft;
1120                                 }
1121
1122                         /* explicit hard limit: error if violated */
1123                         } else if ( op->ors_limit->lms_s_hard > 0 ) {
1124 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1125                                 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1126                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1127
1128                                 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1129                                         /* if limit exceeds hard, error */
1130                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1131                                         send_ldap_result( op, rs );
1132                                         rs->sr_err = LDAP_SUCCESS;
1133                                         return -1;
1134                                 }
1135 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1136                                 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1137                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1138                                 }
1139 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1140                         }
1141                 }
1142
1143                 /* else leave as is */
1144         }
1145
1146         return 0;
1147 }
1148