Back to index

openldap  2.4.31
limits.c
Go to the documentation of this file.
00001 /* limits.c - routines to handle regex-based size and time limits */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 
00021 #include <ac/ctype.h>
00022 #include <ac/regex.h>
00023 #include <ac/string.h>
00024 
00025 #include "slap.h"
00026 #include "lutil.h"
00027 
00028 /* define to get an error if requesting limit higher than hard */
00029 #undef ABOVE_HARD_LIMIT_IS_ERROR
00030 
00031 static const struct berval lmpats[] = {
00032        BER_BVC( "base" ),
00033        BER_BVC( "base" ),
00034        BER_BVC( "onelevel" ),
00035        BER_BVC( "subtree" ),
00036        BER_BVC( "children" ),
00037        BER_BVC( "regex" ),
00038        BER_BVC( "anonymous" ),
00039        BER_BVC( "users" ),
00040        BER_BVC( "*" )
00041 };
00042 
00043 #ifdef LDAP_DEBUG
00044 static const char *const dn_source[2] = { "DN", "DN.THIS" };
00045 static const char *const lmpats_out[] = {
00046        "UNDEFINED",
00047        "EXACT",
00048        "ONELEVEL",
00049        "SUBTREE",
00050        "CHILDREN",
00051        "REGEX",
00052        "ANONYMOUS",
00053        "USERS",
00054        "ANY"
00055 };
00056 
00057 static const char *
00058 limits2str( unsigned i )
00059 {
00060        return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] ))
00061               ? lmpats_out[i] : "UNKNOWN";
00062 }
00063 #endif /* LDAP_DEBUG */
00064 
00065 static int
00066 limits_get( 
00067        Operation            *op,
00068        struct slap_limits_set      **limit
00069 )
00070 {
00071        static struct berval empty_dn = BER_BVC( "" );
00072        struct slap_limits **lm;
00073        struct berval        *ndns[2];
00074 
00075        assert( op != NULL );
00076        assert( limit != NULL );
00077 
00078        ndns[0] = &op->o_ndn;
00079        ndns[1] = &op->o_req_ndn;
00080 
00081        Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
00082                      op->o_log_prefix,
00083                      BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
00084                      BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
00085        /*
00086         * default values
00087         */
00088        *limit = &op->o_bd->be_def_limit;
00089 
00090        if ( op->o_bd->be_limits == NULL ) {
00091               return( 0 );
00092        }
00093 
00094        for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
00095               unsigned      style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
00096               unsigned      type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
00097               unsigned      isthis = type == SLAP_LIMITS_TYPE_THIS;
00098               struct berval *ndn = ndns[isthis];
00099 
00100               if ( style == SLAP_LIMITS_ANY )
00101                      goto found_any;
00102 
00103               if ( BER_BVISEMPTY( ndn ) ) {
00104                      if ( style == SLAP_LIMITS_ANONYMOUS )
00105                             goto found_nodn;
00106                      if ( !isthis )
00107                             continue;
00108                      ndn = &empty_dn;
00109               }
00110 
00111               switch ( style ) {
00112               case SLAP_LIMITS_EXACT:
00113                      if ( type == SLAP_LIMITS_TYPE_GROUP ) {
00114                             int    rc = backend_group( op, NULL,
00115                                           &lm[0]->lm_pat, ndn,
00116                                           lm[0]->lm_group_oc,
00117                                           lm[0]->lm_group_ad );
00118                             if ( rc == 0 ) {
00119                                    goto found_group;
00120                             }
00121                      } else {
00122                             if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
00123                                    goto found_dn;
00124                             }
00125                      }
00126                      break;
00127 
00128               case SLAP_LIMITS_ONE:
00129               case SLAP_LIMITS_SUBTREE:
00130               case SLAP_LIMITS_CHILDREN: {
00131                      ber_len_t d;
00132                      
00133                      /* ndn shorter than lm_pat */
00134                      if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
00135                             break;
00136                      }
00137                      d = ndn->bv_len - lm[0]->lm_pat.bv_len;
00138 
00139                      if ( d == 0 ) {
00140                             /* allow exact match for SUBTREE only */
00141                             if ( style != SLAP_LIMITS_SUBTREE ) {
00142                                    break;
00143                             }
00144                      } else {
00145                             /* check for unescaped rdn separator */
00146                             if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
00147                                    break;
00148                             }
00149                      }
00150 
00151                      /* check that ndn ends with lm_pat */
00152                      if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
00153                             break;
00154                      }
00155 
00156                      /* in case of ONE, require exactly one rdn below lm_pat */
00157                      if ( style == SLAP_LIMITS_ONE ) {
00158                             if ( dn_rdnlen( NULL, ndn ) != d - 1 ) {
00159                                    break;
00160                             }
00161                      }
00162 
00163                      goto found_dn;
00164               }
00165 
00166               case SLAP_LIMITS_REGEX:
00167                      if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
00168                             goto found_dn;
00169                      }
00170                      break;
00171 
00172               case SLAP_LIMITS_ANONYMOUS:
00173                      break;
00174 
00175               case SLAP_LIMITS_USERS:
00176               found_nodn:
00177                      Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
00178                             dn_source[isthis], limits2str( style ), 0 );
00179               found_any:
00180                      *limit = &lm[0]->lm_limits;
00181                      return( 0 );
00182 
00183               found_dn:
00184                      Debug( LDAP_DEBUG_TRACE,
00185                             "<== limits_get: type=%s match=%s dn=\"%s\"\n",
00186                             dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
00187                      *limit = &lm[0]->lm_limits;
00188                      return( 0 );
00189 
00190               found_group:
00191                      Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
00192                             "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
00193                             lm[0]->lm_pat.bv_val,
00194                             lm[0]->lm_group_oc->soc_cname.bv_val,
00195                             lm[0]->lm_group_ad->ad_cname.bv_val );
00196                      *limit = &lm[0]->lm_limits;
00197                      return( 0 );
00198 
00199               default:
00200                      assert( 0 );  /* unreachable */
00201                      return( -1 );
00202               }
00203        }
00204 
00205        return( 0 );
00206 }
00207 
00208 static int
00209 limits_add(
00210        Backend               *be,
00211        unsigned             flags,
00212        const char           *pattern,
00213        ObjectClass          *group_oc,
00214        AttributeDescription *group_ad,
00215        struct slap_limits_set      *limit
00216 )
00217 {
00218        int                  i;
00219        struct slap_limits   *lm;
00220        unsigned             type, style;
00221        
00222        assert( be != NULL );
00223        assert( limit != NULL );
00224 
00225        type = flags & SLAP_LIMITS_TYPE_MASK;
00226        style = flags & SLAP_LIMITS_MASK;
00227 
00228        switch ( style ) {
00229        case SLAP_LIMITS_ANONYMOUS:
00230        case SLAP_LIMITS_USERS:
00231        case SLAP_LIMITS_ANY:
00232               /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
00233               for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
00234                      if ( be->be_limits[ i ]->lm_flags == style ) {
00235                             return( -1 );
00236                      }
00237               }
00238               break;
00239        }
00240 
00241 
00242        lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
00243 
00244        switch ( style ) {
00245        case SLAP_LIMITS_UNDEFINED:
00246               style = SLAP_LIMITS_EXACT;
00247               /* continue to next cases */
00248        case SLAP_LIMITS_EXACT:
00249        case SLAP_LIMITS_ONE:
00250        case SLAP_LIMITS_SUBTREE:
00251        case SLAP_LIMITS_CHILDREN:
00252               {
00253                      int rc;
00254                      struct berval bv;
00255 
00256                      ber_str2bv( pattern, 0, 0, &bv );
00257 
00258                      rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
00259                      if ( rc != LDAP_SUCCESS ) {
00260                             ch_free( lm );
00261                             return( -1 );
00262                      }
00263               }
00264               break;
00265               
00266        case SLAP_LIMITS_REGEX:
00267               ber_str2bv( pattern, 0, 1, &lm->lm_pat );
00268               if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 
00269                                    REG_EXTENDED | REG_ICASE ) ) {
00270                      free( lm->lm_pat.bv_val );
00271                      ch_free( lm );
00272                      return( -1 );
00273               }
00274               break;
00275 
00276        case SLAP_LIMITS_ANONYMOUS:
00277        case SLAP_LIMITS_USERS:
00278        case SLAP_LIMITS_ANY:
00279               BER_BVZERO( &lm->lm_pat );
00280               break;
00281        }
00282 
00283        switch ( type ) {
00284        case SLAP_LIMITS_TYPE_GROUP:
00285               assert( group_oc != NULL );
00286               assert( group_ad != NULL );
00287               lm->lm_group_oc = group_oc;
00288               lm->lm_group_ad = group_ad;
00289               break;
00290        }
00291 
00292        lm->lm_flags = style | type;
00293        lm->lm_limits = *limit;
00294 
00295        i = 0;
00296        if ( be->be_limits != NULL ) {
00297               for ( ; be->be_limits[i]; i++ );
00298        }
00299 
00300        be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
00301                      sizeof( struct slap_limits * ) * ( i + 2 ) );
00302        be->be_limits[i] = lm;
00303        be->be_limits[i+1] = NULL;
00304        
00305        return( 0 );
00306 }
00307 
00308 #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
00309 
00310 int
00311 limits_parse(
00312        Backend     *be,
00313        const char  *fname,
00314        int         lineno,
00315        int         argc,
00316        char        **argv
00317 )
00318 {
00319        int                  flags = SLAP_LIMITS_UNDEFINED;
00320        char                 *pattern;
00321        struct slap_limits_set      limit;
00322        int                  i, rc = 0;
00323        ObjectClass          *group_oc = NULL;
00324        AttributeDescription *group_ad = NULL;
00325 
00326        assert( be != NULL );
00327 
00328        if ( argc < 3 ) {
00329               Debug( LDAP_DEBUG_ANY,
00330                      "%s : line %d: missing arg(s) in "
00331                      "\"limits <pattern> <limits>\" line.\n%s",
00332                      fname, lineno, "" );
00333               return( -1 );
00334        }
00335 
00336        limit = be->be_def_limit;
00337 
00338        /*
00339         * syntax:
00340         *
00341         * "limits" <pattern> <limit> [ ... ]
00342         * 
00343         * 
00344         * <pattern>:
00345         * 
00346         * "anonymous"
00347         * "users"
00348         * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
00349         *     "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
00350         *     "=" ] <dn pattern>
00351         *
00352         * Note:
00353         *     "this" is the baseobject, "self" (the default) is the bound DN
00354         *     "exact" and "base" are the same (exact match);
00355         *     "onelevel" means exactly one rdn below, NOT including pattern
00356         *     "subtree" means any rdn below, including pattern
00357         *     "children" means any rdn below, NOT including pattern
00358         *     
00359         *     "anonymous" may be deprecated in favour 
00360         *     of the pattern = "anonymous" form
00361         *
00362         * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
00363         *
00364         * <limit>:
00365         *
00366         * "time" [ "." { "soft" | "hard" } ] "=" <integer>
00367         *
00368         * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
00369         */
00370        
00371        pattern = argv[1];
00372        if ( strcmp( pattern, "*" ) == 0) {
00373               flags = SLAP_LIMITS_ANY;
00374 
00375        } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
00376               flags = SLAP_LIMITS_ANONYMOUS;
00377 
00378        } else if ( strcasecmp( pattern, "users" ) == 0 ) {
00379               flags = SLAP_LIMITS_USERS;
00380               
00381        } else if ( STRSTART( pattern, "dn" ) ) {
00382               pattern += STRLENOF( "dn" );
00383               flags = SLAP_LIMITS_TYPE_SELF;
00384               if ( pattern[0] == '.' ) {
00385                      pattern++;
00386                      if ( STRSTART( pattern, "this" ) ) {
00387                             flags = SLAP_LIMITS_TYPE_THIS;
00388                             pattern += STRLENOF( "this" );
00389                      } else if ( STRSTART( pattern, "self" ) ) {
00390                             pattern += STRLENOF( "self" );
00391                      } else {
00392                             goto got_dn_dot;
00393                      }
00394               }
00395               if ( pattern[0] == '.' ) {
00396                      pattern++;
00397               got_dn_dot:
00398                      if ( STRSTART( pattern, "exact" ) ) {
00399                             flags |= SLAP_LIMITS_EXACT;
00400                             pattern += STRLENOF( "exact" );
00401 
00402                      } else if ( STRSTART( pattern, "base" ) ) {
00403                             flags |= SLAP_LIMITS_BASE;
00404                             pattern += STRLENOF( "base" );
00405 
00406                      } else if ( STRSTART( pattern, "one" ) ) {
00407                             flags |= SLAP_LIMITS_ONE;
00408                             pattern += STRLENOF( "one" );
00409                             if ( STRSTART( pattern, "level" ) ) {
00410                                    pattern += STRLENOF( "level" );
00411 
00412                             } else {
00413                                    Debug( LDAP_DEBUG_ANY,
00414                                           "%s : line %d: deprecated \"one\" style "
00415                                           "\"limits <pattern> <limits>\" line; "
00416                                           "use \"onelevel\" instead.\n", fname, lineno, 0 );
00417                             }
00418 
00419                      } else if ( STRSTART( pattern, "sub" ) ) {
00420                             flags |= SLAP_LIMITS_SUBTREE;
00421                             pattern += STRLENOF( "sub" );
00422                             if ( STRSTART( pattern, "tree" ) ) {
00423                                    pattern += STRLENOF( "tree" );
00424 
00425                             } else {
00426                                    Debug( LDAP_DEBUG_ANY,
00427                                           "%s : line %d: deprecated \"sub\" style "
00428                                           "\"limits <pattern> <limits>\" line; "
00429                                           "use \"subtree\" instead.\n", fname, lineno, 0 );
00430                             }
00431 
00432                      } else if ( STRSTART( pattern, "children" ) ) {
00433                             flags |= SLAP_LIMITS_CHILDREN;
00434                             pattern += STRLENOF( "children" );
00435 
00436                      } else if ( STRSTART( pattern, "regex" ) ) {
00437                             flags |= SLAP_LIMITS_REGEX;
00438                             pattern += STRLENOF( "regex" );
00439 
00440                      /* 
00441                       * this could be deprecated in favour
00442                       * of the pattern = "anonymous" form
00443                       */
00444                      } else if ( STRSTART( pattern, "anonymous" )
00445                                    && flags == SLAP_LIMITS_TYPE_SELF )
00446                      {
00447                             flags = SLAP_LIMITS_ANONYMOUS;
00448                             pattern = NULL;
00449 
00450                      } else {
00451                             /* force error below */
00452                             if ( *pattern == '=' )
00453                                    --pattern;
00454                      }
00455               }
00456 
00457               /* pre-check the data */
00458               if ( pattern != NULL ) {
00459                      if ( pattern[0] != '=' ) {
00460                             Debug( LDAP_DEBUG_ANY,
00461                                    "%s : line %d: %s in "
00462                                    "\"dn[.{this|self}][.{exact|base"
00463                                    "|onelevel|subtree|children|regex"
00464                                    "|anonymous}]=<pattern>\" in "
00465                                    "\"limits <pattern> <limits>\" line.\n",
00466                                    fname, lineno,
00467                                    isalnum( (unsigned char)pattern[0] )
00468                                    ? "unknown DN modifier" : "missing '='" );
00469                             return( -1 );
00470                      }
00471 
00472                      /* skip '=' (required) */
00473                      pattern++;
00474 
00475                      /* trim obvious cases */
00476                      if ( strcmp( pattern, "*" ) == 0 ) {
00477                             flags = SLAP_LIMITS_ANY;
00478                             pattern = NULL;
00479 
00480                      } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX
00481                                    && strcmp( pattern, ".*" ) == 0 ) {
00482                             flags = SLAP_LIMITS_ANY;
00483                             pattern = NULL;
00484                      }
00485               }
00486 
00487        } else if (STRSTART( pattern, "group" ) ) {
00488               pattern += STRLENOF( "group" );
00489 
00490               if ( pattern[0] == '/' ) {
00491                      struct berval oc, ad;
00492 
00493                      oc.bv_val = pattern + 1;
00494                      pattern = strchr( pattern, '=' );
00495                      if ( pattern == NULL ) {
00496                             return -1;
00497                      }
00498 
00499                      ad.bv_val = strchr( oc.bv_val, '/' );
00500                      if ( ad.bv_val != NULL ) {
00501                             const char    *text = NULL;
00502 
00503                             oc.bv_len = ad.bv_val - oc.bv_val;
00504 
00505                             ad.bv_val++;
00506                             ad.bv_len = pattern - ad.bv_val;
00507                             rc = slap_bv2ad( &ad, &group_ad, &text );
00508                             if ( rc != LDAP_SUCCESS ) {
00509                                    goto no_ad;
00510                             }
00511 
00512                      } else {
00513                             oc.bv_len = pattern - oc.bv_val;
00514                      }
00515 
00516                      group_oc = oc_bvfind( &oc );
00517                      if ( group_oc == NULL ) {
00518                             goto no_oc;
00519                      }
00520               }
00521 
00522               if ( group_oc == NULL ) {
00523                      group_oc = oc_find( SLAPD_GROUP_CLASS );
00524                      if ( group_oc == NULL ) {
00525 no_oc:;
00526                             return( -1 );
00527                      }
00528               }
00529 
00530               if ( group_ad == NULL ) {
00531                      const char    *text = NULL;
00532                      
00533                      rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
00534 
00535                      if ( rc != LDAP_SUCCESS ) {
00536 no_ad:;
00537                             return( -1 );
00538                      }
00539               }
00540 
00541               flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
00542 
00543               if ( pattern[0] != '=' ) {
00544                      Debug( LDAP_DEBUG_ANY,
00545                             "%s : line %d: missing '=' in "
00546                             "\"group[/objectClass[/attributeType]]"
00547                             "=<pattern>\" in "
00548                             "\"limits <pattern> <limits>\" line.\n",
00549                             fname, lineno, 0 );
00550                      return( -1 );
00551               }
00552 
00553               /* skip '=' (required) */
00554               pattern++;
00555        }
00556 
00557        /* get the limits */
00558        for ( i = 2; i < argc; i++ ) {
00559               if ( limits_parse_one( argv[i], &limit ) ) {
00560 
00561                      Debug( LDAP_DEBUG_ANY,
00562                             "%s : line %d: unknown limit values \"%s\" in "
00563                             "\"limits <pattern> <limits>\" line.\n",
00564                      fname, lineno, argv[i] );
00565 
00566                      return( 1 );
00567               }
00568        }
00569 
00570        /*
00571         * sanity checks ...
00572         *
00573         * FIXME: add warnings?
00574         */
00575        if ( limit.lms_t_hard > 0 && 
00576                      ( limit.lms_t_hard < limit.lms_t_soft 
00577                        || limit.lms_t_soft == -1 ) ) {
00578               limit.lms_t_hard = limit.lms_t_soft;
00579        }
00580        
00581        if ( limit.lms_s_hard > 0 && 
00582                      ( limit.lms_s_hard < limit.lms_s_soft 
00583                        || limit.lms_s_soft == -1 ) ) {
00584               limit.lms_s_hard = limit.lms_s_soft;
00585        }
00586 
00587        /*
00588         * defaults ...
00589         * 
00590         * lms_t_hard:
00591         *     -1     => no limits
00592         *     0      => same as soft
00593         *     > 0    => limit (in seconds)
00594         *
00595         * lms_s_hard:
00596         *     -1     => no limits
00597         *     0      0> same as soft
00598         *     > 0    => limit (in entries)
00599         *
00600         * lms_s_pr_total:
00601         *     -2     => disable the control
00602         *     -1     => no limits
00603         *     0      => same as soft
00604         *     > 0    => limit (in entries)
00605         *
00606         * lms_s_pr:
00607         *     -1     => no limits
00608         *     0      => no limits?
00609         *     > 0    => limit size (in entries)
00610         */
00611        if ( limit.lms_s_pr_total > 0 &&
00612                      limit.lms_s_pr > limit.lms_s_pr_total ) {
00613               limit.lms_s_pr = limit.lms_s_pr_total;
00614        }
00615 
00616        rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
00617        if ( rc ) {
00618 
00619               Debug( LDAP_DEBUG_ANY,
00620                      "%s : line %d: unable to add limit in "
00621                      "\"limits <pattern> <limits>\" line.\n",
00622               fname, lineno, 0 );
00623        }
00624 
00625        return( rc );
00626 }
00627 
00628 int
00629 limits_parse_one(
00630        const char           *arg,
00631        struct slap_limits_set      *limit
00632 )
00633 {
00634        assert( arg != NULL );
00635        assert( limit != NULL );
00636 
00637        if ( STRSTART( arg, "time" ) ) {
00638               arg += STRLENOF( "time" );
00639 
00640               if ( arg[0] == '.' ) {
00641                      arg++;
00642                      if ( STRSTART( arg, "soft=" ) ) {
00643                             arg += STRLENOF( "soft=" );
00644                             if ( strcasecmp( arg, "unlimited" ) == 0
00645                                    || strcasecmp( arg, "none" ) == 0 )
00646                             {
00647                                    limit->lms_t_soft = -1;
00648 
00649                             } else {
00650                                    int    soft;
00651 
00652                                    if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
00653                                           return( 1 );
00654                                    }
00655 
00656                                    if ( soft == -1 ) {
00657                                           /* FIXME: use "unlimited" instead; issue warning? */
00658                                    }
00659 
00660                                    limit->lms_t_soft = soft;
00661                             }
00662                             
00663                      } else if ( STRSTART( arg, "hard=" ) ) {
00664                             arg += STRLENOF( "hard=" );
00665                             if ( strcasecmp( arg, "soft" ) == 0 ) {
00666                                    limit->lms_t_hard = 0;
00667 
00668                             } else if ( strcasecmp( arg, "unlimited" ) == 0
00669                                           || strcasecmp( arg, "none" ) == 0 )
00670                             {
00671                                    limit->lms_t_hard = -1;
00672 
00673                             } else {
00674                                    int    hard;
00675 
00676                                    if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
00677                                           return( 1 );
00678                                    }
00679 
00680                                    if ( hard == -1 ) {
00681                                           /* FIXME: use "unlimited" instead */
00682                                    }
00683 
00684                                    if ( hard == 0 ) {
00685                                           /* FIXME: use "soft" instead */
00686                                    }
00687 
00688                                    limit->lms_t_hard = hard;
00689                             }
00690                             
00691                      } else {
00692                             return( 1 );
00693                      }
00694                      
00695               } else if ( arg[0] == '=' ) {
00696                      arg++;
00697                      if ( strcasecmp( arg, "unlimited" ) == 0
00698                             || strcasecmp( arg, "none" ) == 0 )
00699                      {
00700                             limit->lms_t_soft = -1;
00701 
00702                      } else {
00703                             if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0 
00704                                    || limit->lms_t_soft < -1 )
00705                             {
00706                                    return( 1 );
00707                             }
00708                      }
00709                      limit->lms_t_hard = 0;
00710                      
00711               } else {
00712                      return( 1 );
00713               }
00714 
00715        } else if ( STRSTART( arg, "size" ) ) {
00716               arg += STRLENOF( "size" );
00717               
00718               if ( arg[0] == '.' ) {
00719                      arg++;
00720                      if ( STRSTART( arg, "soft=" ) ) {
00721                             arg += STRLENOF( "soft=" );
00722                             if ( strcasecmp( arg, "unlimited" ) == 0
00723                                    || strcasecmp( arg, "none" ) == 0 )
00724                             {
00725                                    limit->lms_s_soft = -1;
00726 
00727                             } else {
00728                                    int    soft;
00729 
00730                                    if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
00731                                           return( 1 );
00732                                    }
00733 
00734                                    if ( soft == -1 ) {
00735                                           /* FIXME: use "unlimited" instead */
00736                                    }
00737 
00738                                    limit->lms_s_soft = soft;
00739                             }
00740                             
00741                      } else if ( STRSTART( arg, "hard=" ) ) {
00742                             arg += STRLENOF( "hard=" );
00743                             if ( strcasecmp( arg, "soft" ) == 0 ) {
00744                                    limit->lms_s_hard = 0;
00745 
00746                             } else if ( strcasecmp( arg, "unlimited" ) == 0
00747                                           || strcasecmp( arg, "none" ) == 0 )
00748                             {
00749                                    limit->lms_s_hard = -1;
00750 
00751                             } else {
00752                                    int    hard;
00753 
00754                                    if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
00755                                           return( 1 );
00756                                    }
00757 
00758                                    if ( hard == -1 ) {
00759                                           /* FIXME: use "unlimited" instead */
00760                                    }
00761 
00762                                    if ( hard == 0 ) {
00763                                           /* FIXME: use "soft" instead */
00764                                    }
00765 
00766                                    limit->lms_s_hard = hard;
00767                             }
00768                             
00769                      } else if ( STRSTART( arg, "unchecked=" ) ) {
00770                             arg += STRLENOF( "unchecked=" );
00771                             if ( strcasecmp( arg, "unlimited" ) == 0
00772                                    || strcasecmp( arg, "none" ) == 0 )
00773                             {
00774                                    limit->lms_s_unchecked = -1;
00775 
00776                             } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
00777                                    limit->lms_s_unchecked = 0;
00778 
00779                             } else {
00780                                    int    unchecked;
00781 
00782                                    if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
00783                                           return( 1 );
00784                                    }
00785 
00786                                    if ( unchecked == -1 ) {
00787                                           /*  FIXME: use "unlimited" instead */
00788                                    }
00789 
00790                                    limit->lms_s_unchecked = unchecked;
00791                             }
00792 
00793                      } else if ( STRSTART( arg, "pr=" ) ) {
00794                             arg += STRLENOF( "pr=" );
00795                             if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
00796                                    limit->lms_s_pr_hide = 1;
00797 
00798                             } else if ( strcasecmp( arg, "unlimited" ) == 0
00799                                           || strcasecmp( arg, "none" ) == 0 )
00800                             {
00801                                    limit->lms_s_pr = -1;
00802 
00803                             } else {
00804                                    int    pr;
00805 
00806                                    if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
00807                                           return( 1 );
00808                                    }
00809 
00810                                    if ( pr == -1 ) {
00811                                           /* FIXME: use "unlimited" instead */
00812                                    }
00813 
00814                                    limit->lms_s_pr = pr;
00815                             }
00816 
00817                      } else if ( STRSTART( arg, "prtotal=" ) ) {
00818                             arg += STRLENOF( "prtotal=" );
00819 
00820                             if ( strcasecmp( arg, "unlimited" ) == 0
00821                                    || strcasecmp( arg, "none" ) == 0 )
00822                             {
00823                                    limit->lms_s_pr_total = -1;
00824 
00825                             } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
00826                                    limit->lms_s_pr_total = -2;
00827 
00828                             } else if ( strcasecmp( arg, "hard" ) == 0 ) {
00829                                    limit->lms_s_pr_total = 0;
00830 
00831                             } else {
00832                                    int    total;
00833 
00834                                    if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
00835                                           return( 1 );
00836                                    }
00837 
00838                                    if ( total == -1 ) {
00839                                           /* FIXME: use "unlimited" instead */
00840                                    }
00841 
00842                                    if ( total == 0 ) {
00843                                           /* FIXME: use "pr=disable" instead */
00844                                    }
00845 
00846                                    limit->lms_s_pr_total = total;
00847                             }
00848 
00849                      } else {
00850                             return( 1 );
00851                      }
00852                      
00853               } else if ( arg[0] == '=' ) {
00854                      arg++;
00855                      if ( strcasecmp( arg, "unlimited" ) == 0
00856                             || strcasecmp( arg, "none" ) == 0 )
00857                      {
00858                             limit->lms_s_soft = -1;
00859 
00860                      } else {
00861                             if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
00862                                    || limit->lms_s_soft < -1 )
00863                             {
00864                                    return( 1 );
00865                             }
00866                      }
00867                      limit->lms_s_hard = 0;
00868                      
00869               } else {
00870                      return( 1 );
00871               }
00872        }
00873 
00874        return 0;
00875 }
00876 
00877 /* Helper macros for limits_unparse() and limits_unparse_one():
00878  * Write to ptr, but not past bufEnd.  Move ptr past the new text.
00879  * Return (success && enough room ? 0 : -1).
00880  */
00881 #define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \
00882        (WHATSLEFT <= (bv).bv_len ? -1 : \
00883         ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0))
00884 #define ptr_APPEND_LIT(str) /* Append a string literal */ \
00885        (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \
00886         ((void) (ptr = lutil_strcopy( ptr, str )), 0))
00887 #define ptr_APPEND_FMT(args) /* Append formatted text */ \
00888        (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0))
00889 #define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg ))
00890 #define WHATSLEFT ((ber_len_t) (bufEnd - ptr))
00891 
00892 /* Caller must provide an adequately sized buffer in bv */
00893 int
00894 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
00895 {
00896        struct berval btmp;
00897        char *ptr, *bufEnd;                /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
00898        ber_len_t tmpLen;                  /* Used by ptr_APPEND_FMT*() */
00899        unsigned type, style;
00900        int rc = 0;
00901 
00902        if ( !bv || !bv->bv_val ) return -1;
00903 
00904        ptr = bv->bv_val;
00905        bufEnd = ptr + buflen;
00906        type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
00907 
00908        if ( type == SLAP_LIMITS_TYPE_GROUP ) {
00909               rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"",
00910                      lim->lm_group_oc->soc_cname.bv_val,
00911                      lim->lm_group_ad->ad_cname.bv_val,
00912                      lim->lm_pat.bv_val ));
00913        } else {
00914               style = lim->lm_flags & SLAP_LIMITS_MASK;
00915               switch( style ) {
00916               case SLAP_LIMITS_ANONYMOUS:
00917               case SLAP_LIMITS_USERS:
00918               case SLAP_LIMITS_ANY:
00919                      rc = ptr_APPEND_BV( lmpats[style] );
00920                      break;
00921               case SLAP_LIMITS_UNDEFINED:
00922               case SLAP_LIMITS_EXACT:
00923               case SLAP_LIMITS_ONE:
00924               case SLAP_LIMITS_SUBTREE:
00925               case SLAP_LIMITS_CHILDREN:
00926               case SLAP_LIMITS_REGEX:
00927                      rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"",
00928                             type == SLAP_LIMITS_TYPE_SELF ? "" : "this.",
00929                             lmpats[style].bv_val, lim->lm_pat.bv_val ));
00930                      break;
00931               }
00932        }
00933        if ( rc == 0 ) {
00934               bv->bv_len = ptr - bv->bv_val;
00935               btmp.bv_val = ptr;
00936               btmp.bv_len = 0;
00937               rc = limits_unparse_one( &lim->lm_limits,
00938                      SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
00939                      &btmp, WHATSLEFT );
00940               if ( rc == 0 )
00941                      bv->bv_len += btmp.bv_len;
00942        }
00943        return rc;
00944 }
00945 
00946 /* Caller must provide an adequately sized buffer in bv */
00947 int
00948 limits_unparse_one(
00949        struct slap_limits_set      *lim,
00950        int                         which,
00951        struct berval *bv,
00952        ber_len_t            buflen )
00953 {
00954        char *ptr, *bufEnd;                /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
00955        ber_len_t tmpLen;                  /* Used by ptr_APPEND_FMT*() */
00956 
00957        if ( !bv || !bv->bv_val ) return -1;
00958 
00959        ptr = bv->bv_val;
00960        bufEnd = ptr + buflen;
00961 
00962        if ( which & SLAP_LIMIT_SIZE ) {
00963               if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
00964 
00965                      /* If same as global limit, drop it */
00966                      if ( lim != &frontendDB->be_def_limit &&
00967                             lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
00968                      {
00969                             goto s_hard;
00970                      /* If there's also a hard limit, fully qualify this one */
00971                      } else if ( lim->lms_s_hard ) {
00972                             if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1;
00973 
00974                      /* If doing both size & time, qualify this */
00975                      } else if ( which & SLAP_LIMIT_TIME ) {
00976                             if ( ptr_APPEND_LIT( " size=" ) ) return -1;
00977                      }
00978 
00979                      if ( lim->lms_s_soft == -1
00980                                    ? ptr_APPEND_LIT( "unlimited " )
00981                                    : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
00982                             return -1;
00983               }
00984 s_hard:
00985               if ( lim->lms_s_hard ) {
00986                      if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1;
00987                      if ( lim->lms_s_hard == -1
00988                                    ? ptr_APPEND_LIT( "unlimited " )
00989                                    : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) )
00990                             return -1;
00991               }
00992               if ( lim->lms_s_unchecked != -1 ) {
00993                      if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1;
00994                      if ( lim->lms_s_unchecked == 0
00995                                    ? ptr_APPEND_LIT( "disabled " )
00996                                    : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) )
00997                             return -1;
00998               }
00999               if ( lim->lms_s_pr_hide ) {
01000                      if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
01001               }
01002               if ( lim->lms_s_pr ) {
01003                      if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1;
01004                      if ( lim->lms_s_pr == -1
01005                                    ? ptr_APPEND_LIT( "unlimited " )
01006                                    : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) )
01007                             return -1;
01008               }
01009               if ( lim->lms_s_pr_total ) {
01010                      if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1;
01011                      if ( lim->lms_s_pr_total  == -1 ? ptr_APPEND_LIT( "unlimited " )
01012                             : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " )
01013                             : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) )
01014                             return -1;
01015               }
01016        }
01017 
01018        if ( which & SLAP_LIMIT_TIME ) {
01019               if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
01020 
01021                      /* If same as global limit, drop it */
01022                      if ( lim != &frontendDB->be_def_limit &&
01023                             lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
01024                      {
01025                             goto t_hard;
01026 
01027                      /* If there's also a hard limit, fully qualify this one */
01028                      } else if ( lim->lms_t_hard ) {
01029                             if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1;
01030 
01031                      /* If doing both size & time, qualify this */
01032                      } else if ( which & SLAP_LIMIT_SIZE ) {
01033                             if ( ptr_APPEND_LIT( " time=" ) ) return -1;
01034                      }
01035 
01036                      if ( lim->lms_t_soft == -1
01037                                    ? ptr_APPEND_LIT( "unlimited " )
01038                                    : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
01039                             return -1;
01040               }
01041 t_hard:
01042               if ( lim->lms_t_hard ) {
01043                      if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1;
01044                      if ( lim->lms_t_hard == -1
01045                                    ? ptr_APPEND_LIT( "unlimited " )
01046                                    : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) )
01047                             return -1;
01048               }
01049        }
01050        if ( ptr != bv->bv_val ) {
01051               ptr--;
01052               *ptr = '\0';
01053               bv->bv_len = ptr - bv->bv_val;
01054        }
01055 
01056        return 0;
01057 }
01058 
01059 int
01060 limits_check( Operation *op, SlapReply *rs )
01061 {
01062        assert( op != NULL );
01063        assert( rs != NULL );
01064        /* FIXME: should this be always true? */
01065        assert( op->o_tag == LDAP_REQ_SEARCH);
01066 
01067        /* protocol only allows 0..maxInt;
01068         *
01069         * internal searches:
01070         * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
01071         * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
01072         *   indicate searches that should return exactly N matches,
01073         *   and handle errors thru a callback (see for instance
01074         *   slap_sasl_match() and slap_sasl2dn())
01075         */
01076        if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
01077               return 0;
01078        }
01079 
01080        /* allow root to set no limit */
01081        if ( be_isroot( op ) ) {
01082               op->ors_limit = NULL;
01083 
01084               if ( op->ors_tlimit == 0 ) {
01085                      op->ors_tlimit = SLAP_NO_LIMIT;
01086               }
01087 
01088               if ( op->ors_slimit == 0 ) {
01089                      op->ors_slimit = SLAP_NO_LIMIT;
01090               }
01091 
01092               /* if paged results and slimit are requested */  
01093               if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
01094                      op->ors_slimit != SLAP_NO_LIMIT ) {
01095                      PagedResultsState *ps = op->o_pagedresults_state;
01096                      int total = op->ors_slimit - ps->ps_count;
01097                      if ( total > 0 ) {
01098                             op->ors_slimit = total;
01099                      } else {
01100                             op->ors_slimit = 0;
01101                      }
01102               }
01103 
01104        /* if not root, get appropriate limits */
01105        } else {
01106               ( void ) limits_get( op, &op->ors_limit );
01107 
01108               assert( op->ors_limit != NULL );
01109 
01110               /* if no limit is required, use soft limit */
01111               if ( op->ors_tlimit == 0 ) {
01112                      op->ors_tlimit = op->ors_limit->lms_t_soft;
01113 
01114               /* limit required: check if legal */
01115               } else {
01116                      if ( op->ors_limit->lms_t_hard == 0 ) {
01117                             if ( op->ors_limit->lms_t_soft > 0
01118                                           && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
01119                                    op->ors_tlimit = op->ors_limit->lms_t_soft;
01120                             }
01121 
01122                      } else if ( op->ors_limit->lms_t_hard > 0 ) {
01123 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
01124                             if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
01125                                    op->ors_tlimit = op->ors_limit->lms_t_hard;
01126 
01127                             } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
01128                                    /* error if exceeding hard limit */
01129                                    rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01130                                    send_ldap_result( op, rs );
01131                                    rs->sr_err = LDAP_SUCCESS;
01132                                    return -1;
01133                             }
01134 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01135                             if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
01136                                    op->ors_tlimit = op->ors_limit->lms_t_hard;
01137                             }
01138 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01139                      }
01140               }
01141 
01142               /* else leave as is */
01143 
01144               /* don't even get to backend if candidate check is disabled */
01145               if ( op->ors_limit->lms_s_unchecked == 0 ) {
01146                      rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01147                      send_ldap_result( op, rs );
01148                      rs->sr_err = LDAP_SUCCESS;
01149                      return -1;
01150               }
01151 
01152               /* if paged results is requested */       
01153               if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
01154                      int    slimit = -2;
01155                      int    pr_total;
01156                      PagedResultsState *ps = op->o_pagedresults_state;
01157 
01158                      /* paged results is not allowed */
01159                      if ( op->ors_limit->lms_s_pr_total == -2 ) {
01160                             rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01161                             rs->sr_text = "pagedResults control not allowed";
01162                             send_ldap_result( op, rs );
01163                             rs->sr_err = LDAP_SUCCESS;
01164                             rs->sr_text = NULL;
01165                             return -1;
01166                      }
01167                      
01168                      if ( op->ors_limit->lms_s_pr > 0
01169                             && ps->ps_size > op->ors_limit->lms_s_pr )
01170                      {
01171                             rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01172                             rs->sr_text = "illegal pagedResults page size";
01173                             send_ldap_result( op, rs );
01174                             rs->sr_err = LDAP_SUCCESS;
01175                             rs->sr_text = NULL;
01176                             return -1;
01177                      }
01178 
01179                      if ( op->ors_limit->lms_s_pr_total == 0 ) {
01180                             if ( op->ors_limit->lms_s_hard == 0 ) {
01181                                    pr_total = op->ors_limit->lms_s_soft;
01182                             } else {
01183                                    pr_total = op->ors_limit->lms_s_hard;
01184                             }
01185                      } else {
01186                             pr_total = op->ors_limit->lms_s_pr_total;
01187                      }
01188 
01189                      if ( pr_total == -1 ) {
01190                             if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
01191                                    slimit = -1;
01192 
01193                             } else {
01194                                    slimit = op->ors_slimit - ps->ps_count;
01195                             }
01196 
01197 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
01198                      } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
01199                                    && ( op->ors_slimit == SLAP_NO_LIMIT
01200                                           || op->ors_slimit > pr_total ) )
01201                      {
01202                             rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01203                             send_ldap_result( op, rs );
01204                             rs->sr_err = LDAP_SUCCESS;
01205                             return -1;
01206 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01207        
01208                      } else {
01209                             /* if no limit is required, use soft limit */
01210                             int    total;
01211                             int    slimit2;
01212 
01213                             /* first round of pagedResults:
01214                              * set count to any appropriate limit */
01215 
01216                             /* if the limit is set, check that it does
01217                              * not violate any server-side limit */
01218 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
01219                             if ( op->ors_slimit == SLAP_MAX_LIMIT )
01220 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01221                             if ( op->ors_slimit == SLAP_MAX_LIMIT
01222                                    || op->ors_slimit > pr_total )
01223 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01224                             {
01225                                    slimit2 = op->ors_slimit = pr_total;
01226 
01227                             } else if ( op->ors_slimit == 0 ) {
01228                                    slimit2 = pr_total;
01229 
01230                             } else {
01231                                    slimit2 = op->ors_slimit;
01232                             }
01233 
01234                             total = slimit2 - ps->ps_count;
01235 
01236                             if ( total >= 0 ) {
01237                                    if ( op->ors_limit->lms_s_pr > 0 ) {
01238                                           /* use the smallest limit set by total/per page */
01239                                           if ( total < op->ors_limit->lms_s_pr ) {
01240                                                  slimit = total;
01241        
01242                                           } else {
01243                                                  /* use the perpage limit if any 
01244                                                   * NOTE: + 1 because given value must be legal */
01245                                                  slimit = op->ors_limit->lms_s_pr + 1;
01246                                           }
01247 
01248                                    } else {
01249                                           /* use the total limit if any */
01250                                           slimit = total;
01251                                    }
01252 
01253                             } else if ( op->ors_limit->lms_s_pr > 0 ) {
01254                                    /* use the perpage limit if any 
01255                                     * NOTE: + 1 because the given value must be legal */
01256                                    slimit = op->ors_limit->lms_s_pr + 1;
01257 
01258                             } else {
01259                                    /* use the standard hard/soft limit if any */
01260                                    slimit = op->ors_limit->lms_s_hard;
01261                             }
01262                      }
01263               
01264                      /* if got any limit, use it */
01265                      if ( slimit != -2 ) {
01266                             if ( op->ors_slimit == 0 ) {
01267                                    op->ors_slimit = slimit;
01268 
01269                             } else if ( slimit > 0 ) {
01270                                    if ( op->ors_slimit - ps->ps_count > slimit ) {
01271                                           rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01272                                           send_ldap_result( op, rs );
01273                                           rs->sr_err = LDAP_SUCCESS;
01274                                           return -1;
01275                                    }
01276                                    op->ors_slimit = slimit;
01277 
01278                             } else if ( slimit == 0 ) {
01279                                    op->ors_slimit = 0;
01280                             }
01281 
01282                      } else {
01283                             /* use the standard hard/soft limit if any */
01284                             op->ors_slimit = pr_total;
01285                      }
01286 
01287               /* no limit requested: use soft, whatever it is */
01288               } else if ( op->ors_slimit == 0 ) {
01289                      op->ors_slimit = op->ors_limit->lms_s_soft;
01290 
01291               /* limit requested: check if legal */
01292               } else {
01293                      /* hard limit as soft (traditional behavior) */
01294                      if ( op->ors_limit->lms_s_hard == 0 ) {
01295                             if ( op->ors_limit->lms_s_soft > 0
01296                                           && op->ors_slimit > op->ors_limit->lms_s_soft ) {
01297                                    op->ors_slimit = op->ors_limit->lms_s_soft;
01298                             }
01299 
01300                      /* explicit hard limit: error if violated */
01301                      } else if ( op->ors_limit->lms_s_hard > 0 ) {
01302 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
01303                             if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
01304                                    op->ors_slimit = op->ors_limit->lms_s_hard;
01305 
01306                             } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
01307                                    /* if limit exceeds hard, error */
01308                                    rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
01309                                    send_ldap_result( op, rs );
01310                                    rs->sr_err = LDAP_SUCCESS;
01311                                    return -1;
01312                             }
01313 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01314                             if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
01315                                    op->ors_slimit = op->ors_limit->lms_s_hard;
01316                             }
01317 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
01318                      }
01319               }
01320 
01321               /* else leave as is */
01322        }
01323 
01324        return 0;
01325 }
01326 
01327 void
01328 limits_free_one( 
01329        struct slap_limits   *lm )
01330 {
01331        if ( ( lm->lm_flags & SLAP_LIMITS_MASK ) == SLAP_LIMITS_REGEX )
01332               regfree( &lm->lm_regex );
01333 
01334        if ( !BER_BVISNULL( &lm->lm_pat ) )
01335               ch_free( lm->lm_pat.bv_val );
01336 
01337        ch_free( lm );
01338 }
01339 
01340 void
01341 limits_destroy( 
01342        struct slap_limits   **lm )
01343 {
01344        int           i;
01345 
01346        if ( lm == NULL ) {
01347               return;
01348        }
01349 
01350        for ( i = 0; lm[ i ]; i++ ) {
01351               limits_free_one( lm[ i ] );
01352        }
01353 
01354        ch_free( lm );
01355 }