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