1/*	$NetBSD: slapi_ext.c,v 1.3 2021/08/14 16:15:02 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2003-2021 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* (C) Copyright PADL Software Pty Ltd. 2003
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that this notice is preserved
20 * and that due credit is given to PADL Software Pty Ltd. This software
21 * is provided ``as is'' without express or implied warranty.
22 */
23/* ACKNOWLEDGEMENTS:
24 * This work was initially developed by Luke Howard for inclusion
25 * in OpenLDAP Software.
26 */
27
28#include <sys/cdefs.h>
29__RCSID("$NetBSD: slapi_ext.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
30
31#include "portable.h"
32
33#include <ac/string.h>
34#include <ac/stdarg.h>
35#include <ac/ctype.h>
36#include <ac/unistd.h>
37
38#ifdef LDAP_SLAPI
39
40#include <slap.h>
41#include <slapi.h>
42
43/*
44 * Object extensions
45 *
46 * We only support two types -- connection and operation extensions.
47 * Define more types in slapi.h
48 */
49
50/* global state */
51struct slapi_registered_extension_set {
52	ldap_pvt_thread_mutex_t mutex;
53	struct slapi_registered_extension {
54		int active;
55		int count;
56		slapi_extension_constructor_fnptr *constructors;
57		slapi_extension_destructor_fnptr *destructors;
58	} extensions[SLAPI_X_EXT_MAX];
59} registered_extensions;
60
61/* per-object state */
62struct slapi_extension_block {
63	void **extensions;
64};
65
66static int get_extension_block(int objecttype, void *object, struct slapi_extension_block **eblock, void **parent)
67{
68	switch ((slapi_extension_t) objecttype) {
69	case SLAPI_X_EXT_CONNECTION:
70		*eblock = ((Connection *)object)->c_extensions;
71		*parent = NULL;
72		break;
73	case SLAPI_X_EXT_OPERATION:
74		*eblock = ((Operation *)object)->o_hdr->oh_extensions;
75		*parent = ((Operation *)object)->o_conn;
76		break;
77	default:
78		return -1;
79		break;
80	}
81
82	if ( *eblock == NULL ) {
83		return -1;
84	}
85
86	return 0;
87}
88
89static int map_extension_type(const char *objectname, slapi_extension_t *type)
90{
91	if ( strcasecmp( objectname, SLAPI_EXT_CONNECTION ) == 0 ) {
92		*type = SLAPI_X_EXT_CONNECTION;
93	} else if ( strcasecmp( objectname, SLAPI_EXT_OPERATION ) == 0 ) {
94		*type = SLAPI_X_EXT_OPERATION;
95	} else {
96		return -1;
97	}
98
99	return 0;
100}
101
102static void new_extension(struct slapi_extension_block *eblock,
103	int objecttype, void *object, void *parent,
104	int extensionhandle )
105{
106	slapi_extension_constructor_fnptr constructor;
107
108	assert( objecttype < SLAPI_X_EXT_MAX );
109	assert( extensionhandle < registered_extensions.extensions[objecttype].count );
110
111	assert( registered_extensions.extensions[objecttype].constructors != NULL );
112	constructor = registered_extensions.extensions[objecttype].constructors[extensionhandle];
113
114	assert( eblock->extensions[extensionhandle] == NULL );
115
116	if ( constructor != NULL ) {
117		eblock->extensions[extensionhandle] = (*constructor)( object, parent );
118	} else {
119		eblock->extensions[extensionhandle] = NULL;
120	}
121}
122
123static void free_extension(struct slapi_extension_block *eblock, int objecttype, void *object, void *parent, int extensionhandle )
124{
125	slapi_extension_destructor_fnptr destructor;
126
127	assert( objecttype < SLAPI_X_EXT_MAX );
128	assert( extensionhandle < registered_extensions.extensions[objecttype].count );
129
130	if ( eblock->extensions[extensionhandle] != NULL ) {
131		assert( registered_extensions.extensions[objecttype].destructors != NULL );
132		destructor = registered_extensions.extensions[objecttype].destructors[extensionhandle];
133		if ( destructor != NULL ) {
134			(*destructor)( eblock->extensions[extensionhandle], object, parent );
135		}
136		eblock->extensions[extensionhandle] = NULL;
137	}
138}
139
140void *slapi_get_object_extension(int objecttype, void *object, int extensionhandle)
141{
142	struct slapi_extension_block *eblock;
143	void *parent;
144
145	if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
146		return NULL;
147	}
148
149	if ( extensionhandle < registered_extensions.extensions[objecttype].count ) {
150		return eblock->extensions[extensionhandle];
151	}
152
153	return NULL;
154}
155
156void slapi_set_object_extension(int objecttype, void *object, int extensionhandle, void *extension)
157{
158	struct slapi_extension_block *eblock;
159	void *parent;
160
161	if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
162		return;
163	}
164
165	if ( extensionhandle < registered_extensions.extensions[objecttype].count ) {
166		/* free the old one */
167		free_extension( eblock, objecttype, object, parent, extensionhandle );
168
169		/* constructed by caller */
170		eblock->extensions[extensionhandle] = extension;
171	}
172}
173
174int slapi_register_object_extension(
175	const char *pluginname,
176	const char *objectname,
177	slapi_extension_constructor_fnptr constructor,
178	slapi_extension_destructor_fnptr destructor,
179	int *objecttype,
180	int *extensionhandle)
181{
182	int rc;
183	slapi_extension_t type;
184	struct slapi_registered_extension *re;
185
186	ldap_pvt_thread_mutex_lock( &registered_extensions.mutex );
187
188	rc = map_extension_type( objectname, &type );
189	if ( rc != 0 ) {
190		ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
191		return rc;
192	}
193
194	*objecttype = (int)type;
195
196	re = &registered_extensions.extensions[*objecttype];
197
198	*extensionhandle = re->count;
199
200	if ( re->active ) {
201		/* can't add new extensions after objects have been created */
202		ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
203		return -1;
204	}
205
206	re->count++;
207
208	if ( re->constructors == NULL ) {
209		re->constructors = (slapi_extension_constructor_fnptr *)slapi_ch_calloc( re->count,
210			sizeof( slapi_extension_constructor_fnptr ) );
211	} else {
212		re->constructors = (slapi_extension_constructor_fnptr *)slapi_ch_realloc( (char *)re->constructors,
213			re->count * sizeof( slapi_extension_constructor_fnptr ) );
214	}
215	re->constructors[*extensionhandle] = constructor;
216
217	if ( re->destructors == NULL ) {
218		re->destructors = (slapi_extension_destructor_fnptr *)slapi_ch_calloc( re->count,
219			sizeof( slapi_extension_destructor_fnptr ) );
220	} else {
221		re->destructors = (slapi_extension_destructor_fnptr *)slapi_ch_realloc( (char *)re->destructors,
222			re->count * sizeof( slapi_extension_destructor_fnptr ) );
223	}
224	re->destructors[*extensionhandle] = destructor;
225
226	ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
227
228	return 0;
229}
230
231int slapi_int_create_object_extensions(int objecttype, void *object)
232{
233	int i;
234	struct slapi_extension_block *eblock;
235	void **peblock;
236	void *parent;
237
238	switch ((slapi_extension_t) objecttype) {
239	case SLAPI_X_EXT_CONNECTION:
240		peblock = &(((Connection *)object)->c_extensions);
241		parent = NULL;
242		break;
243	case SLAPI_X_EXT_OPERATION:
244		peblock = &(((Operation *)object)->o_hdr->oh_extensions);
245		parent = ((Operation *)object)->o_conn;
246		break;
247	default:
248		return -1;
249		break;
250	}
251
252	*peblock = NULL;
253
254	ldap_pvt_thread_mutex_lock( &registered_extensions.mutex );
255	if ( registered_extensions.extensions[objecttype].active == 0 ) {
256		/*
257		 * once we've created some extensions, no new extensions can
258		 * be registered.
259		 */
260		registered_extensions.extensions[objecttype].active = 1;
261	}
262	ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
263
264	eblock = (struct slapi_extension_block *)slapi_ch_calloc( 1, sizeof(*eblock) );
265
266	if ( registered_extensions.extensions[objecttype].count ) {
267		eblock->extensions = (void **)slapi_ch_calloc( registered_extensions.extensions[objecttype].count, sizeof(void *) );
268		for ( i = 0; i < registered_extensions.extensions[objecttype].count; i++ ) {
269			new_extension( eblock, objecttype, object, parent, i );
270		}
271	} else {
272		eblock->extensions = NULL;
273	}
274
275	*peblock = eblock;
276
277	return 0;
278}
279
280int slapi_int_free_object_extensions(int objecttype, void *object)
281{
282	int i;
283	struct slapi_extension_block *eblock;
284	void **peblock;
285	void *parent;
286
287	switch ((slapi_extension_t) objecttype) {
288	case SLAPI_X_EXT_CONNECTION:
289		peblock = &(((Connection *)object)->c_extensions);
290		parent = NULL;
291		break;
292	case SLAPI_X_EXT_OPERATION:
293		peblock = &(((Operation *)object)->o_hdr->oh_extensions);
294		parent = ((Operation *)object)->o_conn;
295		break;
296	default:
297		return -1;
298		break;
299	}
300
301	eblock = (struct slapi_extension_block *)*peblock;
302
303	if ( eblock != NULL && eblock->extensions != NULL ) {
304		for ( i = registered_extensions.extensions[objecttype].count - 1; i >= 0; --i ) {
305			free_extension( eblock, objecttype, object, parent, i );
306		}
307
308		slapi_ch_free( (void **)&eblock->extensions );
309	}
310
311	slapi_ch_free( peblock );
312
313	return 0;
314}
315
316/* for reusable object types */
317int slapi_int_clear_object_extensions(int objecttype, void *object)
318{
319	int i;
320	struct slapi_extension_block *eblock;
321	void *parent;
322
323	if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
324		return -1;
325	}
326
327	if ( eblock->extensions == NULL ) {
328		/* no extensions */
329		return 0;
330	}
331
332	for ( i = registered_extensions.extensions[objecttype].count - 1; i >= 0; --i ) {
333		free_extension( eblock, objecttype, object, parent, i );
334	}
335
336	for ( i = 0; i < registered_extensions.extensions[objecttype].count; i++ ) {
337		new_extension( eblock, objecttype, object, parent, i );
338	}
339
340	return 0;
341}
342
343int slapi_int_init_object_extensions(void)
344{
345	memset( &registered_extensions, 0, sizeof( registered_extensions ) );
346
347	if ( ldap_pvt_thread_mutex_init( &registered_extensions.mutex ) != 0 ) {
348		return -1;
349	}
350
351	return 0;
352}
353
354#endif /* LDAP_SLAPI */
355