The IRIX compiler does not like embedded unnamed unions
[Samba/gebeck_regimport.git] / source4 / utils / ad2oLschema.c
blob3c2ffe7a00c188231a7e1e4406b823515c0d0a94
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett 2006-2008
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
8 ** under the LGPL
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb
27 * Component: ad2oLschema
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
31 * Author: Andrew Bartlett
34 #include "includes.h"
35 #include "ldb_includes.h"
36 #include "system/locale.h"
37 #include "lib/ldb/tools/cmdline.h"
38 #include "param/param.h"
39 #include "lib/cmdline/popt_common.h"
40 #include "dsdb/samdb/samdb.h"
42 struct schema_conv {
43 int count;
44 int skipped;
45 int failures;
49 static void usage(void)
51 printf("Usage: ad2oLschema <options>\n");
52 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
53 printf("Options:\n");
54 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
55 printf(" -H url LDB or LDAP server to read schmea from\n");
56 printf(" -O outputfile outputfile otherwise STDOUT\n");
57 printf(" -o options pass options like modules to activate\n");
58 printf(" e.g: -o modules:timestamps\n");
59 printf("\n");
60 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
61 exit(1);
64 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
66 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
67 struct ldb_dn *schemadn;
68 struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
69 struct ldb_result *rootdse_res;
70 struct ldb_result *schema_res;
71 int ldb_ret;
73 if (!basedn) {
74 return NULL;
77 /* Search for rootdse */
78 ldb_ret = ldb_search(ldb, mem_ctx, &rootdse_res,
79 basedn, LDB_SCOPE_BASE, rootdse_attrs, NULL);
80 if (ldb_ret != LDB_SUCCESS) {
81 ldb_ret = ldb_search(ldb, mem_ctx, &schema_res, basedn, LDB_SCOPE_SUBTREE,
82 NULL, "(&(objectClass=dMD)(cn=Schema))");
83 if (ldb_ret) {
84 printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb));
85 return NULL;
88 if (schema_res->count != 1) {
89 talloc_free(schema_res);
90 printf("Failed to find rootDSE");
91 return NULL;
94 schemadn = talloc_steal(mem_ctx, schema_res->msgs[0]->dn);
95 talloc_free(schema_res);
96 return schemadn;
99 if (rootdse_res->count != 1) {
100 printf("Failed to find rootDSE");
101 talloc_free(rootdse_res);
102 return NULL;
105 /* Locate schema */
106 schemadn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
107 talloc_free(rootdse_res);
109 if (!schemadn) {
110 return NULL;
113 return schemadn;
117 static struct schema_conv process_convert(struct ldb_context *ldb, enum dsdb_schema_convert_target target, FILE *in, FILE *out)
119 /* Read list of attributes to skip, OIDs to map */
120 TALLOC_CTX *mem_ctx = talloc_new(ldb);
121 char *line;
122 const char **attrs_skip = NULL;
123 int num_skip = 0;
124 struct oid_map {
125 char *old_oid;
126 char *new_oid;
127 } *oid_map = NULL;
128 int num_oid_maps = 0;
129 struct attr_map {
130 char *old_attr;
131 char *new_attr;
132 } *attr_map = NULL;
133 int num_attr_maps = 0;
134 struct dsdb_class *objectclass;
135 struct dsdb_attribute *attribute;
136 struct ldb_dn *schemadn;
137 struct schema_conv ret;
138 struct dsdb_schema *schema;
139 const char *seperator;
140 char *error_string;
142 int ldb_ret;
144 ret.count = 0;
145 ret.skipped = 0;
146 ret.failures = 0;
148 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
149 /* Blank Line */
150 if (line[0] == '\0') {
151 continue;
153 /* Comment */
154 if (line[0] == '#') {
155 continue;
157 if (isdigit(line[0])) {
158 char *p = strchr(line, ':');
159 if (!p) {
160 ret.failures++;
161 return ret;
163 p[0] = '\0';
164 p++;
165 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
166 trim_string(line, " ", " ");
167 oid_map[num_oid_maps].old_oid = talloc_move(oid_map, &line);
168 trim_string(p, " ", " ");
169 oid_map[num_oid_maps].new_oid = p;
170 num_oid_maps++;
171 oid_map[num_oid_maps].old_oid = NULL;
172 } else {
173 char *p = strchr(line, ':');
174 if (p) {
175 /* remap attribute/objectClass */
176 p[0] = '\0';
177 p++;
178 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
179 trim_string(line, " ", " ");
180 attr_map[num_attr_maps].old_attr = talloc_move(attr_map, &line);
181 trim_string(p, " ", " ");
182 attr_map[num_attr_maps].new_attr = p;
183 num_attr_maps++;
184 attr_map[num_attr_maps].old_attr = NULL;
185 } else {
186 /* skip attribute/objectClass */
187 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
188 trim_string(line, " ", " ");
189 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
190 num_skip++;
191 attrs_skip[num_skip] = NULL;
196 schemadn = find_schema_dn(ldb, mem_ctx);
197 if (!schemadn) {
198 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
199 ret.failures = 1;
200 return ret;
203 ldb_ret = dsdb_schema_from_schema_dn(mem_ctx, ldb,
204 lp_iconv_convenience(cmdline_lp_ctx),
205 schemadn, &schema, &error_string);
206 if (ldb_ret != LDB_SUCCESS) {
207 printf("Failed to load schema: %s\n", error_string);
208 ret.failures = 1;
209 return ret;
212 switch (target) {
213 case TARGET_OPENLDAP:
214 seperator = "\n ";
215 break;
216 case TARGET_FEDORA_DS:
217 seperator = "\n ";
218 fprintf(out, "dn: cn=schema\n");
219 break;
222 for (attribute=schema->attributes; attribute; attribute = attribute->next) {
223 const char *name = attribute->lDAPDisplayName;
224 const char *description = attribute->adminDescription;
225 const char *oid = attribute->attributeID_oid;
226 const char *syntax = attribute->attributeSyntax_oid;
227 const char *equality = NULL, *substring = NULL;
228 bool single_value = attribute->isSingleValued;
230 const struct dsdb_syntax *map = find_syntax_map_by_ad_syntax(attribute->oMSyntax);
231 char *schema_entry = NULL;
232 int j;
234 /* We have been asked to skip some attributes/objectClasses */
235 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
236 ret.skipped++;
237 continue;
240 /* We might have been asked to remap this oid, due to a conflict */
241 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
242 if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
243 oid = oid_map[j].new_oid;
244 break;
248 if (map) {
249 /* We might have been asked to remap this oid,
250 * due to a conflict, or lack of
251 * implementation */
252 syntax = map->ldap_oid;
253 /* We might have been asked to remap this oid, due to a conflict */
254 for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
255 if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
256 syntax = oid_map[j].new_oid;
257 break;
261 equality = map->equality;
262 substring = map->substring;
265 /* We might have been asked to remap this name, due to a conflict */
266 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
267 if (strcasecmp(name, attr_map[j].old_attr) == 0) {
268 name = attr_map[j].new_attr;
269 break;
273 schema_entry = schema_attribute_description(mem_ctx, target, seperator, oid, name, description, equality, substring, syntax, single_value, false);
275 if (schema_entry == NULL) {
276 ret.failures++;
277 return ret;
280 switch (target) {
281 case TARGET_OPENLDAP:
282 fprintf(out, "attributetype %s\n\n", schema_entry);
283 break;
284 case TARGET_FEDORA_DS:
285 fprintf(out, "attributeTypes: %s\n", schema_entry);
286 break;
288 ret.count++;
291 /* This is already sorted to have 'top' and similar classes first */
292 for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
293 const char *name = objectclass->lDAPDisplayName;
294 const char *description = objectclass->adminDescription;
295 const char *oid = objectclass->governsID_oid;
296 const char *subClassOf = objectclass->subClassOf;
297 int objectClassCategory = objectclass->objectClassCategory;
298 char **must;
299 char **may;
300 char *schema_entry = NULL;
301 const char *objectclass_name_as_list[] = {
302 objectclass->lDAPDisplayName,
303 NULL
305 int j;
306 int attr_idx;
308 /* We have been asked to skip some attributes/objectClasses */
309 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
310 ret.skipped++;
311 continue;
314 /* We might have been asked to remap this oid, due to a conflict */
315 for (j=0; oid_map && oid_map[j].old_oid; j++) {
316 if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
317 oid = oid_map[j].new_oid;
318 break;
322 /* We might have been asked to remap this name, due to a conflict */
323 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
324 if (strcasecmp(name, attr_map[j].old_attr) == 0) {
325 name = attr_map[j].new_attr;
326 break;
330 may = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MAY);
332 for (j=0; may && may[j]; j++) {
333 /* We might have been asked to remap this name, due to a conflict */
334 for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
335 if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) {
336 may[j] = attr_map[attr_idx].new_attr;
337 break;
342 must = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MUST);
344 for (j=0; must && must[j]; j++) {
345 /* We might have been asked to remap this name, due to a conflict */
346 for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
347 if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) {
348 must[j] = attr_map[attr_idx].new_attr;
349 break;
354 schema_entry = schema_class_description(mem_ctx, target,
355 seperator,
356 oid,
357 name,
358 NULL,
359 description,
360 subClassOf,
361 objectClassCategory,
362 must,
363 may);
364 if (schema_entry == NULL) {
365 ret.failures++;
366 return ret;
369 switch (target) {
370 case TARGET_OPENLDAP:
371 fprintf(out, "objectclass %s\n\n", schema_entry);
372 break;
373 case TARGET_FEDORA_DS:
374 fprintf(out, "objectClasses: %s\n", schema_entry);
375 break;
377 ret.count++;
380 return ret;
383 int main(int argc, const char **argv)
385 TALLOC_CTX *ctx;
386 struct ldb_cmdline *options;
387 FILE *in = stdin;
388 FILE *out = stdout;
389 struct ldb_context *ldb;
390 struct schema_conv ret;
391 const char *target_str;
392 enum dsdb_schema_convert_target target;
394 ctx = talloc_new(NULL);
395 ldb = ldb_init(ctx, NULL);
397 options = ldb_cmdline_process(ldb, argc, argv, usage);
399 if (options->input) {
400 in = fopen(options->input, "r");
401 if (!in) {
402 perror(options->input);
403 exit(1);
406 if (options->output) {
407 out = fopen(options->output, "w");
408 if (!out) {
409 perror(options->output);
410 exit(1);
414 target_str = lp_parm_string(cmdline_lp_ctx, NULL, "convert", "target");
416 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
417 target = TARGET_OPENLDAP;
418 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
419 target = TARGET_FEDORA_DS;
420 } else {
421 printf("Unsupported target: %s\n", target_str);
422 exit(1);
425 ret = process_convert(ldb, target, in, out);
427 fclose(in);
428 fclose(out);
430 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
432 return 0;