1/*	$NetBSD$	*/
2
3/* attr.c - backend routines for dealing with attributes */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/attr.c,v 1.36.2.10 2010/04/13 20:23:23 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22
23#include <ac/socket.h>
24#include <ac/string.h>
25
26#include "slap.h"
27#include "back-bdb.h"
28#include "config.h"
29#include "lutil.h"
30
31/* Find the ad, return -1 if not found,
32 * set point for insertion if ins is non-NULL
33 */
34int
35bdb_attr_slot( struct bdb_info *bdb, AttributeDescription *ad, int *ins )
36{
37	unsigned base = 0, cursor = 0;
38	unsigned n = bdb->bi_nattrs;
39	int val = 0;
40
41	while ( 0 < n ) {
42		unsigned pivot = n >> 1;
43		cursor = base + pivot;
44
45		val = SLAP_PTRCMP( ad, bdb->bi_attrs[cursor]->ai_desc );
46		if ( val < 0 ) {
47			n = pivot;
48		} else if ( val > 0 ) {
49			base = cursor + 1;
50			n -= pivot + 1;
51		} else {
52			return cursor;
53		}
54	}
55	if ( ins ) {
56		if ( val > 0 )
57			++cursor;
58		*ins = cursor;
59	}
60	return -1;
61}
62
63static int
64ainfo_insert( struct bdb_info *bdb, AttrInfo *a )
65{
66	int x;
67	int i = bdb_attr_slot( bdb, a->ai_desc, &x );
68
69	/* Is it a dup? */
70	if ( i >= 0 )
71		return -1;
72
73	bdb->bi_attrs = ch_realloc( bdb->bi_attrs, ( bdb->bi_nattrs+1 ) *
74		sizeof( AttrInfo * ));
75	if ( x < bdb->bi_nattrs )
76		AC_MEMCPY( &bdb->bi_attrs[x+1], &bdb->bi_attrs[x],
77			( bdb->bi_nattrs - x ) * sizeof( AttrInfo *));
78	bdb->bi_attrs[x] = a;
79	bdb->bi_nattrs++;
80	return 0;
81}
82
83AttrInfo *
84bdb_attr_mask(
85	struct bdb_info	*bdb,
86	AttributeDescription *desc )
87{
88	int i = bdb_attr_slot( bdb, desc, NULL );
89	return i < 0 ? NULL : bdb->bi_attrs[i];
90}
91
92int
93bdb_attr_index_config(
94	struct bdb_info	*bdb,
95	const char		*fname,
96	int			lineno,
97	int			argc,
98	char		**argv,
99	struct		config_reply_s *c_reply)
100{
101	int rc = 0;
102	int	i;
103	slap_mask_t mask;
104	char **attrs;
105	char **indexes = NULL;
106
107	attrs = ldap_str2charray( argv[0], "," );
108
109	if( attrs == NULL ) {
110		fprintf( stderr, "%s: line %d: "
111			"no attributes specified: %s\n",
112			fname, lineno, argv[0] );
113		return LDAP_PARAM_ERROR;
114	}
115
116	if ( argc > 1 ) {
117		indexes = ldap_str2charray( argv[1], "," );
118
119		if( indexes == NULL ) {
120			fprintf( stderr, "%s: line %d: "
121				"no indexes specified: %s\n",
122				fname, lineno, argv[1] );
123			rc = LDAP_PARAM_ERROR;
124			goto done;
125		}
126	}
127
128	if( indexes == NULL ) {
129		mask = bdb->bi_defaultmask;
130
131	} else {
132		mask = 0;
133
134		for ( i = 0; indexes[i] != NULL; i++ ) {
135			slap_mask_t index;
136			rc = slap_str2index( indexes[i], &index );
137
138			if( rc != LDAP_SUCCESS ) {
139				if ( c_reply )
140				{
141					snprintf(c_reply->msg, sizeof(c_reply->msg),
142						"index type \"%s\" undefined", indexes[i] );
143
144					fprintf( stderr, "%s: line %d: %s\n",
145						fname, lineno, c_reply->msg );
146				}
147				rc = LDAP_PARAM_ERROR;
148				goto done;
149			}
150
151			mask |= index;
152		}
153	}
154
155	if( !mask ) {
156		if ( c_reply )
157		{
158			snprintf(c_reply->msg, sizeof(c_reply->msg),
159				"no indexes selected" );
160			fprintf( stderr, "%s: line %d: %s\n",
161				fname, lineno, c_reply->msg );
162		}
163		rc = LDAP_PARAM_ERROR;
164		goto done;
165	}
166
167	for ( i = 0; attrs[i] != NULL; i++ ) {
168		AttrInfo	*a;
169		AttributeDescription *ad;
170		const char *text;
171#ifdef LDAP_COMP_MATCH
172		ComponentReference* cr = NULL;
173		AttrInfo *a_cr = NULL;
174#endif
175
176		if( strcasecmp( attrs[i], "default" ) == 0 ) {
177			bdb->bi_defaultmask |= mask;
178			continue;
179		}
180
181#ifdef LDAP_COMP_MATCH
182		if ( is_component_reference( attrs[i] ) ) {
183			rc = extract_component_reference( attrs[i], &cr );
184			if ( rc != LDAP_SUCCESS ) {
185				if ( c_reply )
186				{
187					snprintf(c_reply->msg, sizeof(c_reply->msg),
188						"index component reference\"%s\" undefined",
189						attrs[i] );
190					fprintf( stderr, "%s: line %d: %s\n",
191						fname, lineno, c_reply->msg );
192				}
193				goto done;
194			}
195			cr->cr_indexmask = mask;
196			/*
197			 * After extracting a component reference
198			 * only the name of a attribute will be remaining
199			 */
200		} else {
201			cr = NULL;
202		}
203#endif
204		ad = NULL;
205		rc = slap_str2ad( attrs[i], &ad, &text );
206
207		if( rc != LDAP_SUCCESS ) {
208			if ( c_reply )
209			{
210				snprintf(c_reply->msg, sizeof(c_reply->msg),
211					"index attribute \"%s\" undefined",
212					attrs[i] );
213
214				fprintf( stderr, "%s: line %d: %s\n",
215					fname, lineno, c_reply->msg );
216			}
217			goto done;
218		}
219
220		if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
221			if (c_reply) {
222				snprintf(c_reply->msg, sizeof(c_reply->msg),
223					"index of attribute \"%s\" disallowed", attrs[i] );
224				fprintf( stderr, "%s: line %d: %s\n",
225					fname, lineno, c_reply->msg );
226			}
227			rc = LDAP_UNWILLING_TO_PERFORM;
228			goto done;
229		}
230
231		if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
232			ad->ad_type->sat_approx
233				&& ad->ad_type->sat_approx->smr_indexer
234				&& ad->ad_type->sat_approx->smr_filter ) )
235		{
236			if (c_reply) {
237				snprintf(c_reply->msg, sizeof(c_reply->msg),
238					"approx index of attribute \"%s\" disallowed", attrs[i] );
239				fprintf( stderr, "%s: line %d: %s\n",
240					fname, lineno, c_reply->msg );
241			}
242			rc = LDAP_INAPPROPRIATE_MATCHING;
243			goto done;
244		}
245
246		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
247			ad->ad_type->sat_equality
248				&& ad->ad_type->sat_equality->smr_indexer
249				&& ad->ad_type->sat_equality->smr_filter ) )
250		{
251			if (c_reply) {
252				snprintf(c_reply->msg, sizeof(c_reply->msg),
253					"equality index of attribute \"%s\" disallowed", attrs[i] );
254				fprintf( stderr, "%s: line %d: %s\n",
255					fname, lineno, c_reply->msg );
256			}
257			rc = LDAP_INAPPROPRIATE_MATCHING;
258			goto done;
259		}
260
261		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
262			ad->ad_type->sat_substr
263				&& ad->ad_type->sat_substr->smr_indexer
264				&& ad->ad_type->sat_substr->smr_filter ) )
265		{
266			if (c_reply) {
267				snprintf(c_reply->msg, sizeof(c_reply->msg),
268					"substr index of attribute \"%s\" disallowed", attrs[i] );
269				fprintf( stderr, "%s: line %d: %s\n",
270					fname, lineno, c_reply->msg );
271			}
272			rc = LDAP_INAPPROPRIATE_MATCHING;
273			goto done;
274		}
275
276		Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
277			ad->ad_cname.bv_val, mask, 0 );
278
279		a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
280
281#ifdef LDAP_COMP_MATCH
282		a->ai_cr = NULL;
283#endif
284		a->ai_desc = ad;
285
286		if ( bdb->bi_flags & BDB_IS_OPEN ) {
287			a->ai_indexmask = 0;
288			a->ai_newmask = mask;
289		} else {
290			a->ai_indexmask = mask;
291			a->ai_newmask = 0;
292		}
293
294#ifdef LDAP_COMP_MATCH
295		if ( cr ) {
296			a_cr = bdb_attr_mask( bdb, ad );
297			if ( a_cr ) {
298				/*
299				 * AttrInfo is already in AVL
300				 * just add the extracted component reference
301				 * in the AttrInfo
302				 */
303				rc = insert_component_reference( cr, &a_cr->ai_cr );
304				if ( rc != LDAP_SUCCESS) {
305					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
306					rc = LDAP_PARAM_ERROR;
307					goto done;
308				}
309				continue;
310			} else {
311				rc = insert_component_reference( cr, &a->ai_cr );
312				if ( rc != LDAP_SUCCESS) {
313					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
314					rc = LDAP_PARAM_ERROR;
315					goto done;
316				}
317			}
318		}
319#endif
320		rc = ainfo_insert( bdb, a );
321		if( rc ) {
322			if ( bdb->bi_flags & BDB_IS_OPEN ) {
323				AttrInfo *b = bdb_attr_mask( bdb, ad );
324				/* If there is already an index defined for this attribute
325				 * it must be replaced. Otherwise we end up with multiple
326				 * olcIndex values for the same attribute */
327				if ( b->ai_indexmask & BDB_INDEX_DELETING ) {
328					/* If we were editing this attr, reset it */
329					b->ai_indexmask &= ~BDB_INDEX_DELETING;
330					/* If this is leftover from a previous add, commit it */
331					if ( b->ai_newmask )
332						b->ai_indexmask = b->ai_newmask;
333					b->ai_newmask = a->ai_newmask;
334					ch_free( a );
335					rc = 0;
336					continue;
337				}
338			}
339			if (c_reply) {
340				snprintf(c_reply->msg, sizeof(c_reply->msg),
341					"duplicate index definition for attr \"%s\"",
342					attrs[i] );
343				fprintf( stderr, "%s: line %d: %s\n",
344					fname, lineno, c_reply->msg );
345			}
346
347			rc = LDAP_PARAM_ERROR;
348			goto done;
349		}
350	}
351
352done:
353	ldap_charray_free( attrs );
354	if ( indexes != NULL ) ldap_charray_free( indexes );
355
356	return rc;
357}
358
359static int
360bdb_attr_index_unparser( void *v1, void *v2 )
361{
362	AttrInfo *ai = v1;
363	BerVarray *bva = v2;
364	struct berval bv;
365	char *ptr;
366
367	slap_index2bvlen( ai->ai_indexmask, &bv );
368	if ( bv.bv_len ) {
369		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
370		ptr = ch_malloc( bv.bv_len+1 );
371		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
372		*bv.bv_val++ = ' ';
373		slap_index2bv( ai->ai_indexmask, &bv );
374		bv.bv_val = ptr;
375		ber_bvarray_add( bva, &bv );
376	}
377	return 0;
378}
379
380static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
381static AttrInfo aidef = { &addef };
382
383void
384bdb_attr_index_unparse( struct bdb_info *bdb, BerVarray *bva )
385{
386	int i;
387
388	if ( bdb->bi_defaultmask ) {
389		aidef.ai_indexmask = bdb->bi_defaultmask;
390		bdb_attr_index_unparser( &aidef, bva );
391	}
392	for ( i=0; i<bdb->bi_nattrs; i++ )
393		bdb_attr_index_unparser( bdb->bi_attrs[i], bva );
394}
395
396void
397bdb_attr_info_free( AttrInfo *ai )
398{
399#ifdef LDAP_COMP_MATCH
400	free( ai->ai_cr );
401#endif
402	free( ai );
403}
404
405void
406bdb_attr_index_destroy( struct bdb_info *bdb )
407{
408	int i;
409
410	for ( i=0; i<bdb->bi_nattrs; i++ )
411		bdb_attr_info_free( bdb->bi_attrs[i] );
412
413	free( bdb->bi_attrs );
414}
415
416void bdb_attr_index_free( struct bdb_info *bdb, AttributeDescription *ad )
417{
418	int i;
419
420	i = bdb_attr_slot( bdb, ad, NULL );
421	if ( i >= 0 ) {
422		bdb_attr_info_free( bdb->bi_attrs[i] );
423		bdb->bi_nattrs--;
424		for (; i<bdb->bi_nattrs; i++)
425			bdb->bi_attrs[i] = bdb->bi_attrs[i+1];
426	}
427}
428
429void bdb_attr_flush( struct bdb_info *bdb )
430{
431	int i;
432
433	for ( i=0; i<bdb->bi_nattrs; i++ ) {
434		if ( bdb->bi_attrs[i]->ai_indexmask & BDB_INDEX_DELETING ) {
435			int j;
436			bdb_attr_info_free( bdb->bi_attrs[i] );
437			bdb->bi_nattrs--;
438			for (j=i; j<bdb->bi_nattrs; j++)
439				bdb->bi_attrs[j] = bdb->bi_attrs[j+1];
440			i--;
441		}
442	}
443}
444