1 /* -*- show-trailing-whitespace: t; indent-tabs: t -*-
2  * Copyright (c) 2003,2004,2005,2006 David Lichteblau
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #include "common.h"
19 
20 LDAPObjectClass *
schema_get_objectclass(tschema * schema,char * name)21 schema_get_objectclass(tschema *schema, char *name)
22 {
23 	return g_hash_table_lookup(schema->classes, name);
24 }
25 
26 LDAPAttributeType *
schema_get_attributetype(tschema * schema,char * name)27 schema_get_attributetype(tschema *schema, char *name)
28 {
29 	return g_hash_table_lookup(schema->types, name);
30 }
31 
32 char *
objectclass_name(LDAPObjectClass * cls)33 objectclass_name(LDAPObjectClass *cls)
34 {
35 	char **names = cls->oc_names;
36 	if (names && *names)
37 		return *names;
38 	return cls->oc_oid;
39 }
40 
41 char *
attributetype_name(LDAPAttributeType * at)42 attributetype_name(LDAPAttributeType *at)
43 {
44 	char **names = at->at_names;
45 	if (names && *names)
46 		return *names;
47 	return at->at_oid;
48 }
49 
50 static void
add_objectclass(GHashTable * classes,LDAPObjectClass * cls)51 add_objectclass(GHashTable *classes, LDAPObjectClass *cls)
52 {
53 	int i;
54 	char **names = cls->oc_names;
55 
56 	g_hash_table_insert(classes, cls->oc_oid, cls);
57 	if (names)
58 		for (i = 0; names[i]; i++)
59 			g_hash_table_insert(classes, names[i], cls);
60 }
61 
62 static void
add_attributetype(GHashTable * types,LDAPAttributeType * at)63 add_attributetype(GHashTable *types, LDAPAttributeType *at)
64 {
65 	int i;
66 	char **names = at->at_names;
67 
68 	g_hash_table_insert(types, at->at_oid, at);
69 	if (names)
70 		for (i = 0; names[i]; i++)
71 			g_hash_table_insert(types, names[i], at);
72 }
73 
74 static gboolean
strcaseequal(gconstpointer v,gconstpointer w)75 strcaseequal(gconstpointer v, gconstpointer w)
76 {
77 	return strcasecmp((char *) v, (char *) w) == 0;
78 }
79 
80 /* From GLIB - Library of useful routines for C programming, g_str_hash()
81  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
82  */
83 static guint
strcasehash(gconstpointer v)84 strcasehash(gconstpointer v)
85 {
86 	const signed char *p = v;
87 	guint32 h = *p;
88 
89 	if (h)
90 		for (p += 1; *p != '\0'; p++)
91 			h = (h << 5) - h + tolower(*p);
92 
93 	return h;
94 }
95 
96 static gboolean
aux_class_entry_p(gpointer key,gpointer value,gpointer data)97 aux_class_entry_p(gpointer key, gpointer value, gpointer data)
98 {
99 	LDAPObjectClass *class = value;
100 	return !!strcmp(key, class->oc_oid);
101 }
102 
103 static gboolean
aux_type_entry_p(gpointer key,gpointer value,gpointer data)104 aux_type_entry_p(gpointer key, gpointer value, gpointer data)
105 {
106 	LDAPAttributeType *at = value;
107 	return !!strcmp(key, at->at_oid);
108 }
109 
110 static void
free_class(gpointer key,gpointer value,gpointer data)111 free_class(gpointer key, gpointer value, gpointer data)
112 {
113 	ldap_objectclass_free(value);
114 }
115 
116 static void
free_type(gpointer key,gpointer value,gpointer data)117 free_type(gpointer key, gpointer value, gpointer data)
118 {
119 	ldap_attributetype_free(value);
120 }
121 
122 void
schema_free(tschema * schema)123 schema_free(tschema *schema)
124 {
125 	g_hash_table_foreach_steal(schema->classes, aux_class_entry_p, 0);
126 	g_hash_table_foreach_steal(schema->types, aux_type_entry_p, 0);
127 
128 	g_hash_table_foreach(schema->classes, free_class, 0);
129 	g_hash_table_foreach(schema->types, free_type, 0);
130 
131 	g_hash_table_destroy(schema->classes);
132 	g_hash_table_destroy(schema->types);
133 	free(schema);
134 }
135 
136 tschema *
schema_new(LDAP * ld)137 schema_new(LDAP *ld)
138 {
139 	LDAPMessage *result, *entry;
140 	char **values;
141 	char *subschema_dn;
142 	int code;
143 	const char *errp;
144 	char *attrs[2] = {"subschemaSubentry", 0};
145 	tschema *schema;
146 
147 	if (ldap_search_s(ld, "", LDAP_SCOPE_BASE, 0, attrs, 0, &result)) {
148 		ldap_perror(ld, "ldap_search");
149 		return 0;
150 	}
151 	if ( !(entry = ldap_first_entry(ld, result))) {
152 		ldap_perror(ld, "ldap_first_entry");
153 		return 0;
154 	}
155 	values = ldap_get_values(ld, entry, "subschemaSubentry");
156 	if (!values) {
157 		fputs("subschemaSubentry attribute not found.", stderr);
158 		ldap_msgfree(result);
159 		return 0;
160 	}
161 	subschema_dn = xdup(*values);
162 	ldap_value_free(values);
163 	ldap_msgfree(result);
164 
165 	entry = get_entry(ld, subschema_dn, &result);
166 	free(subschema_dn);
167 	values = ldap_get_values(ld, entry, "objectClasses");
168 
169 	schema = xalloc(sizeof(tschema));
170 	schema->classes = g_hash_table_new(strcasehash, strcaseequal);
171 	schema->types = g_hash_table_new(strcasehash, strcaseequal);
172 
173 	if (values) {
174 		char **ptr = values;
175 		for (ptr = values; *ptr; ptr++) {
176 			LDAPObjectClass *cls
177 				= ldap_str2objectclass(*ptr, &code, &errp, 0);
178 			if (cls)
179 				add_objectclass(schema->classes, cls);
180                         else
181                                 fprintf(stderr,
182                                         "Warning: Cannot parse class: %s\n",
183                                         ldap_scherr2str(code));
184 		}
185 		ldap_value_free(values);
186 	}
187 	values = ldap_get_values(ld, entry, "attributeTypes");
188 	if (values) {
189 		char **ptr = values;
190 		for (ptr = values; *ptr; ptr++) {
191 			LDAPAttributeType *at
192 				= ldap_str2attributetype(
193 					*ptr, &code, &errp, 0);
194 			if (at)
195                                 add_attributetype(schema->types, at);
196                         else
197                                 fprintf(stderr,
198                                         "Warning: Cannot parse type: %s\n",
199                                         ldap_scherr2str(code));
200 		}
201 		ldap_value_free(values);
202 	}
203 	ldap_msgfree(result);
204 	return schema;
205 }
206 
207 tentroid *
entroid_new(tschema * schema)208 entroid_new(tschema *schema)
209 {
210 	tentroid *result = xalloc(sizeof(tentroid));
211 	result->schema = schema;
212 	result->classes = g_ptr_array_new();
213 	result->must = g_ptr_array_new();
214 	result->may = g_ptr_array_new();
215 	result->structural = 0;
216 	result->comment = g_string_sized_new(0);
217 	result->error = g_string_sized_new(0);
218 	return result;
219 }
220 
221 void
entroid_reset(tentroid * entroid)222 entroid_reset(tentroid *entroid)
223 {
224 	g_ptr_array_set_size(entroid->classes, 0);
225 	g_ptr_array_set_size(entroid->must, 0);
226 	g_ptr_array_set_size(entroid->may, 0);
227 	entroid->structural = 0;
228 	g_string_truncate(entroid->comment, 0);
229 	g_string_truncate(entroid->error, 0);
230 }
231 
232 void
entroid_free(tentroid * entroid)233 entroid_free(tentroid *entroid)
234 {
235 	g_ptr_array_free(entroid->classes, 1);
236 	g_ptr_array_free(entroid->must, 1);
237 	g_ptr_array_free(entroid->may, 1);
238 	g_string_free(entroid->comment, 1);
239 	g_string_free(entroid->error, 1);
240 	free(entroid);
241 }
242 
243 LDAPObjectClass *
entroid_get_objectclass(tentroid * entroid,char * name)244 entroid_get_objectclass(tentroid *entroid, char *name)
245 {
246 	LDAPObjectClass *cls = schema_get_objectclass(entroid->schema, name);
247 	if (!cls) {
248 		g_string_assign(entroid->error,
249 				"Error: Object class not found: ");
250 		g_string_append(entroid->error, name);
251 		g_string_append_c(entroid->error, '\n');
252 	}
253 	return cls;
254 }
255 
256 LDAPAttributeType *
entroid_get_attributetype(tentroid * entroid,char * name)257 entroid_get_attributetype(tentroid *entroid, char *name)
258 {
259 	LDAPAttributeType *at
260 		= schema_get_attributetype(entroid->schema, name);
261 	if (!at) {
262 		g_string_assign(entroid->error,
263 				"Error: Attribute type not found: ");
264 		g_string_append(entroid->error, name);
265 		g_string_append_c(entroid->error, '\n');
266 	}
267 	return at;
268 }
269 
270 LDAPObjectClass *
entroid_request_class(tentroid * entroid,char * name)271 entroid_request_class(tentroid *entroid, char *name)
272 {
273 	LDAPObjectClass *cls = entroid_get_objectclass(entroid, name);
274 	if (cls)
275 		adjoin_ptr(entroid->classes, cls);
276 	return cls;
277 }
278 
279 int
entroid_remove_ad(tentroid * entroid,char * ad)280 entroid_remove_ad(tentroid *entroid, char *ad)
281 {
282 	LDAPAttributeType *at;
283 	char *name;
284 	char *s = strchr(ad, ';');
285 	int found;
286 
287 	if (s) {
288 		int n = s - ad;
289 		name = xalloc(n);
290 		memcpy(name, ad, n);
291 	} else
292 		name = ad;
293 
294 	if ( !(at = entroid_get_attributetype(entroid, name)))
295 		return 0;
296 	found = g_ptr_array_remove(entroid->must, at);
297 	found |= g_ptr_array_remove(entroid->may, at);
298 
299 	if (name != ad)
300 		free(name);
301 	return found;
302 }
303 
304 static int
compute_entroid_1(tentroid * entroid,LDAPObjectClass * cls)305 compute_entroid_1(tentroid *entroid, LDAPObjectClass *cls)
306 {
307 	char **ptr;
308 
309 	for (ptr = cls->oc_sup_oids; ptr && *ptr; ptr++)
310 		if (!entroid_request_class(entroid, *ptr))
311 			return -1;
312 	if (cls->oc_kind == LDAP_SCHEMA_STRUCTURAL) {
313 		char *str;
314 		if (entroid->structural)
315 			str = "### WARNING: extra structural object class: ";
316 		else {
317 			str = "# structural object class: ";
318 			entroid->structural = cls;
319 		}
320 		g_string_append(entroid->comment, str);
321 		g_string_append(entroid->comment, objectclass_name(cls));
322 		g_string_append_c(entroid->comment, '\n');
323 	}
324 	for (ptr = cls->oc_at_oids_must; ptr && *ptr; ptr++) {
325 		LDAPAttributeType *at
326 			= entroid_get_attributetype(entroid, *ptr);
327 		if (!at) return -1;
328 		g_ptr_array_remove(entroid->may, at);
329 		adjoin_ptr(entroid->must, at);
330 	}
331 	for (ptr = cls->oc_at_oids_may; ptr && *ptr; ptr++) {
332 		int i;
333 		LDAPAttributeType *at
334 			= entroid_get_attributetype(entroid, *ptr);
335 		if (!at) return -1;
336 		for (i = 0; i < entroid->must->len; i++)
337 			if (at == g_ptr_array_index(entroid->must, i))
338 				break;
339 		if (i >= entroid->must->len)
340 			g_ptr_array_add(entroid->may, at);
341 	}
342 	return 0;
343 }
344 
345 /*
346  * Add all superclasses to entroid->classes; add required and optional
347  * attributes to entroid->must, entroid->may.  Set entroid->structural
348  * to the structural objectclass, if any.  Extra trace output for user
349  * display in entroid->comment;
350  *
351  * Return 0 on success, -1 else.
352  * Error message, if any, in entroid->error.
353  */
354 int
compute_entroid(tentroid * entroid)355 compute_entroid(tentroid *entroid)
356 {
357 	int i;
358 	for (i = 0; i < entroid->classes->len; i++) {
359 		LDAPObjectClass *cls = g_ptr_array_index(entroid->classes, i);
360 		if (compute_entroid_1(entroid, cls) == -1)
361 			return -1;
362 	}
363 	if (!entroid->structural)
364 		g_string_append(entroid->comment,
365 				"### WARNING:"
366 				" no structural object class specified!\n");
367 	return 0;
368 }
369