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