1/* generic.c
2
3   Subroutines that support the generic object. */
4
5/*
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 *   Internet Systems Consortium, Inc.
22 *   950 Charter Street
23 *   Redwood City, CA 94063
24 *   <info@isc.org>
25 *   http://www.isc.org/
26 *
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
33 */
34
35#include <omapip/omapip_p.h>
36
37OMAPI_OBJECT_ALLOC (omapi_generic,
38		    omapi_generic_object_t, omapi_type_generic)
39
40isc_result_t omapi_generic_new (omapi_object_t **gen,
41				const char *file, int line)
42{
43	/* Backwards compatibility. */
44	return omapi_generic_allocate ((omapi_generic_object_t **)gen,
45				       file, line);
46}
47
48isc_result_t omapi_generic_set_value (omapi_object_t *h,
49				      omapi_object_t *id,
50				      omapi_data_string_t *name,
51				      omapi_typed_data_t *value)
52{
53	omapi_generic_object_t *g;
54	omapi_value_t *new;
55	omapi_value_t **va;
56	u_int8_t *ca;
57	int vm_new;
58	int i, vfree = -1;
59	isc_result_t status;
60
61	if (h -> type != omapi_type_generic)
62		return ISC_R_INVALIDARG;
63	g = (omapi_generic_object_t *)h;
64
65	/* See if there's already a value with this name attached to
66	   the generic object, and if so, replace the current value
67	   with the new one. */
68	for (i = 0; i < g -> nvalues; i++) {
69		if (!omapi_data_string_cmp (name, g -> values [i] -> name)) {
70			/* There's an inconsistency here: the standard
71			   behaviour of a set_values method when
72			   passed a matching name and a null value is
73			   to delete the value associated with that
74			   name (where possible).  In the generic
75			   object, we remember the name/null pair,
76			   because generic objects are generally used
77			   to pass messages around, and this is the
78			   way that remote entities delete values from
79			   local objects.  If the get_value method of
80			   a generic object is called for a name that
81			   maps to a name/null pair, ISC_R_NOTFOUND is
82			   returned. */
83			new = (omapi_value_t *)0;
84			status = (omapi_value_new (&new, MDL));
85			if (status != ISC_R_SUCCESS)
86				return status;
87			omapi_data_string_reference (&new -> name, name, MDL);
88			if (value)
89				omapi_typed_data_reference (&new -> value,
90							    value, MDL);
91
92			omapi_value_dereference (&(g -> values [i]), MDL);
93			status = (omapi_value_reference
94				  (&(g -> values [i]), new, MDL));
95			omapi_value_dereference (&new, MDL);
96			g -> changed [i] = 1;
97			return status;
98		}
99		/* Notice a free slot if we pass one. */
100		else if (vfree == -1 && !g -> values [i])
101			vfree = i;
102	}
103
104	/* If the name isn't already attached to this object, see if an
105	   inner object has it. */
106	if (h -> inner && h -> inner -> type -> set_value) {
107		status = ((*(h -> inner -> type -> set_value))
108			  (h -> inner, id, name, value));
109		if (status != ISC_R_NOTFOUND)
110			return status;
111	}
112
113	/* Okay, so it's a value that no inner object knows about, and
114	   (implicitly, since the outer object set_value method would
115	   have called this object's set_value method) it's an object that
116	   no outer object knows about, it's this object's responsibility
117	   to remember it - that's what generic objects do. */
118
119	/* Arrange for there to be space for the pointer to the new
120           name/value pair if necessary: */
121	if (vfree == -1) {
122		vfree = g -> nvalues;
123		if (vfree == g -> va_max) {
124			if (g -> va_max)
125				vm_new = 2 * g -> va_max;
126			else
127				vm_new = 10;
128			va = dmalloc (vm_new * sizeof *va, MDL);
129			if (!va)
130				return ISC_R_NOMEMORY;
131			ca = dmalloc (vm_new * sizeof *ca, MDL);
132			if (!ca) {
133				dfree (va, MDL);
134				return ISC_R_NOMEMORY;
135			}
136			if (g -> va_max) {
137				memcpy (va, g -> values,
138					g -> va_max * sizeof *va);
139				memcpy (ca, g -> changed,
140					g -> va_max * sizeof *ca);
141			}
142			memset (va + g -> va_max, 0,
143				(vm_new - g -> va_max) * sizeof *va);
144			memset (ca + g -> va_max, 0,
145				(vm_new - g -> va_max) * sizeof *ca);
146			if (g -> values)
147				dfree (g -> values, MDL);
148			if (g -> changed)
149				dfree (g -> changed, MDL);
150			g -> values = va;
151			g -> changed = ca;
152			g -> va_max = vm_new;
153		}
154	}
155	status = omapi_value_new (&g -> values [vfree], MDL);
156	if (status != ISC_R_SUCCESS)
157		return status;
158	omapi_data_string_reference (&g -> values [vfree] -> name,
159				     name, MDL);
160	if (value)
161		omapi_typed_data_reference
162			(&g -> values [vfree] -> value, value, MDL);
163	g -> changed [vfree] = 1;
164	if (vfree == g -> nvalues)
165		g -> nvalues++;
166	return ISC_R_SUCCESS;
167}
168
169isc_result_t omapi_generic_get_value (omapi_object_t *h,
170				      omapi_object_t *id,
171				      omapi_data_string_t *name,
172				      omapi_value_t **value)
173{
174	int i;
175	omapi_generic_object_t *g;
176
177	if (h -> type != omapi_type_generic)
178		return ISC_R_INVALIDARG;
179	g = (omapi_generic_object_t *)h;
180
181	/* Look up the specified name in our list of objects. */
182	for (i = 0; i < g -> nvalues; i++) {
183		if (!g -> values[i])
184			continue;
185		if (!omapi_data_string_cmp (name, g -> values [i] -> name)) {
186			/* If this is a name/null value pair, this is the
187			   same as if there were no value that matched
188			   the specified name, so return ISC_R_NOTFOUND. */
189			if (!g -> values [i] -> value)
190				return ISC_R_NOTFOUND;
191			/* Otherwise, return the name/value pair. */
192			return omapi_value_reference (value,
193						      g -> values [i], MDL);
194		}
195	}
196
197	if (h -> inner && h -> inner -> type -> get_value)
198		return (*(h -> inner -> type -> get_value))
199			(h -> inner, id, name, value);
200	return ISC_R_NOTFOUND;
201}
202
203isc_result_t omapi_generic_destroy (omapi_object_t *h,
204				    const char *file, int line)
205{
206	omapi_generic_object_t *g;
207	int i;
208
209	if (h -> type != omapi_type_generic)
210		return ISC_R_UNEXPECTED;
211	g = (omapi_generic_object_t *)h;
212
213	if (g -> values) {
214		for (i = 0; i < g -> nvalues; i++) {
215			if (g -> values [i])
216				omapi_value_dereference (&g -> values [i],
217							 file, line);
218		}
219		dfree (g -> values, file, line);
220		dfree (g -> changed, file, line);
221		g -> values = (omapi_value_t **)0;
222		g -> changed = (u_int8_t *)0;
223		g -> va_max = 0;
224	}
225
226	return ISC_R_SUCCESS;
227}
228
229isc_result_t omapi_generic_signal_handler (omapi_object_t *h,
230					   const char *name, va_list ap)
231{
232	if (h -> type != omapi_type_generic)
233		return ISC_R_INVALIDARG;
234
235	if (h -> inner && h -> inner -> type -> signal_handler)
236		return (*(h -> inner -> type -> signal_handler)) (h -> inner,
237								  name, ap);
238	return ISC_R_NOTFOUND;
239}
240
241/* Write all the published values associated with the object through the
242   specified connection. */
243
244isc_result_t omapi_generic_stuff_values (omapi_object_t *c,
245					 omapi_object_t *id,
246					 omapi_object_t *g)
247{
248	omapi_generic_object_t *src;
249	int i;
250	isc_result_t status;
251
252	if (g -> type != omapi_type_generic)
253		return ISC_R_INVALIDARG;
254	src = (omapi_generic_object_t *)g;
255
256	for (i = 0; i < src -> nvalues; i++) {
257		if (src -> values [i] && src -> values [i] -> name -> len &&
258		    src -> changed [i]) {
259			status = (omapi_connection_put_uint16
260				  (c, src -> values [i] -> name -> len));
261			if (status != ISC_R_SUCCESS)
262				return status;
263			status = (omapi_connection_copyin
264				  (c, src -> values [i] -> name -> value,
265				   src -> values [i] -> name -> len));
266			if (status != ISC_R_SUCCESS)
267				return status;
268
269			status = (omapi_connection_write_typed_data
270				  (c, src -> values [i] -> value));
271			if (status != ISC_R_SUCCESS)
272				return status;
273		}
274	}
275
276	if (g -> inner && g -> inner -> type -> stuff_values)
277		return (*(g -> inner -> type -> stuff_values)) (c, id,
278								g -> inner);
279	return ISC_R_SUCCESS;
280}
281
282/* Clear the changed flags on the object.   This has the effect that if
283   generic_stuff is called, any attributes that still have a cleared changed
284   flag aren't sent to the peer.   This also deletes any values that are
285   null, presuming that these have now been properly handled. */
286
287isc_result_t omapi_generic_clear_flags (omapi_object_t *o)
288{
289	int i;
290	omapi_generic_object_t *g;
291
292	if (o -> type != omapi_type_generic)
293		return ISC_R_INVALIDARG;
294	g = (omapi_generic_object_t *)o;
295
296	for (i = 0; i < g -> nvalues; i++) {
297		g -> changed [i] = 0;
298		if (g -> values [i] &&
299		    !g -> values [i] -> value)
300			omapi_value_dereference (&g -> values [i], MDL);
301	}
302	return ISC_R_SUCCESS;
303}
304