Move ad2oLschema and oLschema2ldif into Samba4, out of LDB
[tprouty/samba.git] / source4 / utils / ad2oLschema.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett 2006
5
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
9    
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.
14
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.
19
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/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ad2oLschema
28  *
29  *  Description: utility to convert an AD schema into the format required by OpenLDAP
30  *
31  *  Author: Andrew Bartlett
32  */
33
34 #include "includes.h"
35 #include "ldb_includes.h"
36 #include "system/locale.h"
37 #include "lib/ldb/tools/cmdline.h"
38 #include "utils/schema_convert.h"
39 #include "param/param.h"
40 #include "lib/cmdline/popt_common.h"
41 #include "dsdb/samdb/samdb.h"
42
43 struct schema_conv {
44         int count;
45         int skipped;
46         int failures;
47 };
48
49 enum convert_target {
50         TARGET_OPENLDAP,
51         TARGET_FEDORA_DS
52 };
53         
54
55 static void usage(void)
56 {
57         printf("Usage: ad2oLschema <options>\n");
58         printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
59         printf("Options:\n");
60         printf("  -I inputfile     inputfile of mapped OIDs and skipped attributes/ObjectClasses");
61         printf("  -H url           LDB or LDAP server to read schmea from\n");
62         printf("  -O outputfile    outputfile otherwise STDOUT\n");
63         printf("  -o options       pass options like modules to activate\n");
64         printf("              e.g: -o modules:timestamps\n");
65         printf("\n");
66         printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
67         exit(1);
68 }
69
70 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
71                               TALLOC_CTX *mem_ctx, 
72                               struct ldb_result **attrs_res)
73 {
74         int ret;
75
76         /* Downlaod schema */
77         ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
78                          "objectClass=attributeSchema", 
79                          NULL, attrs_res);
80         if (ret != LDB_SUCCESS) {
81                 printf("Search failed: %s\n", ldb_errstring(ldb));
82                 return LDB_ERR_OPERATIONS_ERROR;
83         }
84         
85         return ret;
86 }
87
88 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn, 
89                               TALLOC_CTX *mem_ctx, 
90                               struct ldb_result *search_from,
91                               struct ldb_result *res_list)
92 {
93         int i;
94         int ret = 0;
95         for (i=0; i < search_from->count; i++) {
96                 struct ldb_result *res;
97                 const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i], 
98                                                                "lDAPDisplayname", NULL);
99
100                 ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
101                                         schemadn, LDB_SCOPE_SUBTREE, NULL,
102                                         "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
103                                         name, name);
104                 if (ret != LDB_SUCCESS) {
105                         printf("Search failed: %s\n", ldb_errstring(ldb));
106                         return ret;
107                 }
108                 
109                 res_list->msgs = talloc_realloc(res_list, res_list->msgs, 
110                                                 struct ldb_message *, res_list->count + 2);
111                 if (!res_list->msgs) {
112                         return LDB_ERR_OPERATIONS_ERROR;
113                 }
114                 res_list->msgs[res_list->count] = talloc_move(res_list, 
115                                                               &search_from->msgs[i]);
116                 res_list->count++;
117                 res_list->msgs[res_list->count] = NULL;
118
119                 if (res->count > 0) {
120                         ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list); 
121                 }
122                 if (ret != LDB_SUCCESS) {
123                         return ret;
124                 }
125         }
126         return ret;
127 }
128
129 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, 
130                                     TALLOC_CTX *mem_ctx, 
131                                     struct ldb_result **objectclasses_res)
132 {
133         TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
134         struct ldb_result *top_res, *ret_res;
135         int ret;
136         if (!local_ctx) {
137                 return LDB_ERR_OPERATIONS_ERROR;
138         }
139         
140         /* Downlaod 'top' */
141         ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
142                          "(&(objectClass=classSchema)(lDAPDisplayName=top))", 
143                          NULL, &top_res);
144         if (ret != LDB_SUCCESS) {
145                 printf("Search failed: %s\n", ldb_errstring(ldb));
146                 return LDB_ERR_OPERATIONS_ERROR;
147         }
148
149         talloc_steal(local_ctx, top_res);
150
151         if (top_res->count != 1) {
152                 return LDB_ERR_OPERATIONS_ERROR;
153         }
154
155         ret_res = talloc_zero(local_ctx, struct ldb_result);
156         if (!ret_res) {
157                 return LDB_ERR_OPERATIONS_ERROR;
158         }
159
160         ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res); 
161
162         if (ret != LDB_SUCCESS) {
163                 printf("Search failed: %s\n", ldb_errstring(ldb));
164                 return LDB_ERR_OPERATIONS_ERROR;
165         }
166
167         *objectclasses_res = talloc_move(mem_ctx, &ret_res);
168         return ret;
169 }
170
171 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
172                                      struct ldb_result **schema_res) 
173 {
174         const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
175         struct ldb_dn *schemadn;
176         struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
177         struct ldb_result *rootdse_res;
178         int ldb_ret;
179         
180         if (!basedn) {
181                 return NULL;
182         }
183         
184         /* Search for rootdse */
185         ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
186         if (ldb_ret != LDB_SUCCESS) {
187                 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE, 
188                                  "(&(objectClass=dMD)(cn=Schema))", 
189                                  NULL, schema_res);
190                 if (ldb_ret) {
191                         printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb));
192                         return NULL;
193                 }
194
195                 talloc_steal(mem_ctx, *schema_res);
196
197                 if ((*schema_res)->count != 1) {
198                         printf("Failed to find rootDSE");
199                         return NULL;
200                 }
201                 
202                 schemadn = talloc_steal(mem_ctx, (*schema_res)->msgs[0]->dn);
203                 return schemadn;
204                 
205         }
206         
207         talloc_steal(mem_ctx, rootdse_res);
208
209         if (rootdse_res->count != 1) {
210                 printf("Failed to find rootDSE");
211                 return NULL;
212         }
213         
214         /* Locate schema */
215         schemadn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
216         if (!schemadn) {
217                 return NULL;
218         }
219
220         ldb_ret = ldb_search(ldb, schemadn, LDB_SCOPE_BASE, 
221                              "(&(objectClass=dMD)(cn=Schema))", 
222                              NULL, schema_res);
223         if (ldb_ret) {
224                 printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb));
225                 return NULL;
226         }
227         
228         talloc_steal(mem_ctx, *schema_res);
229
230         if ((*schema_res)->count != 1) {
231                 printf("Failed to find rootDSE");
232                 return NULL;
233         }
234         talloc_free(rootdse_res);
235         return schemadn;
236 }
237
238 static bool merge_attr_list(TALLOC_CTX *mem_ctx, 
239                             struct ldb_message_element *attrs, struct ldb_message_element *new_attrs) 
240 {
241         struct ldb_val *values;
242         if (!new_attrs) {
243                 return true;
244         }
245
246         values = talloc_realloc(mem_ctx, 
247                                 attrs->values, struct ldb_val, attrs->num_values + new_attrs->num_values);
248         
249         attrs->values = values;
250
251         memcpy(&attrs->values[attrs->num_values], new_attrs->values, sizeof(*new_attrs->values) * new_attrs->num_values);
252         attrs->num_values = attrs->num_values + new_attrs->num_values;
253
254         /* Add sort and unique implementation here */
255
256         return true;
257 }
258
259 static bool find_aux_classes(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct ldb_dn *schema_dn,
260                              struct ldb_message_element *aux_class, struct ldb_message_element *must, 
261                              struct ldb_message_element *sys_must, struct ldb_message_element *may, 
262                              struct ldb_message_element *sys_may) 
263 {
264         int i, ret;
265         struct ldb_message *msg;
266         struct ldb_result *res;
267
268         for (i=0; aux_class && i < aux_class->num_values; i++) {
269                 ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
270                                          schema_dn, LDB_SCOPE_SUBTREE, NULL,
271                                          "(&(objectClass=classSchema)(lDAPDisplayName=%s))",
272                                          aux_class->values[i].data);
273                 if (ret != LDB_SUCCESS) {
274                         return false;
275                 }
276
277                 msg = res->msgs[0];
278
279                 if (!merge_attr_list(mem_ctx, must, ldb_msg_find_element(msg, "mustContain"))) {
280                         return false;
281                 }
282                 if (!merge_attr_list(mem_ctx, sys_must,  ldb_msg_find_element(msg, "systemMustContain"))) {
283                         return false;
284                 }
285                 if (!merge_attr_list(mem_ctx, may, ldb_msg_find_element(msg, "mayContain"))) {
286                         return false;
287                 }
288                 if (!merge_attr_list(mem_ctx, sys_may, ldb_msg_find_element(msg, "systemMayContain"))) {
289                         return false;
290                 }
291                 
292                 
293                 if (res->count == 0) {
294                         return false;
295                 }
296
297                 if (!find_aux_classes(mem_ctx, ldb, schema_dn,
298                                       ldb_msg_find_element(msg, "auxiliaryClass"), must, sys_must, may, sys_may)) {
299                         return false;
300                 }
301                 if (!find_aux_classes(mem_ctx, ldb, schema_dn,
302                                       ldb_msg_find_element(msg, "systemAuxiliaryClass"), must, sys_must, may, sys_may)) {
303                         return false;
304                 }
305         }
306         return true;
307 }
308
309
310 #define IF_NULL_FAIL_RET(x) do {     \
311                 if (!x) {               \
312                         ret.failures++; \
313                         return ret;     \
314                 }                       \
315         } while (0) 
316
317
318 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out) 
319 {
320         /* Read list of attributes to skip, OIDs to map */
321         TALLOC_CTX *mem_ctx = talloc_new(ldb);
322         char *line;
323         const char **attrs_skip = NULL;
324         int num_skip = 0;
325         struct oid_map {
326                 char *old_oid;
327                 char *new_oid;
328         } *oid_map = NULL;
329         int num_oid_maps = 0;
330         struct attr_map {
331                 char *old_attr;
332                 char *new_attr;
333         } *attr_map = NULL;
334         int num_attr_maps = 0;  
335         struct ldb_result *schema_res, *attrs_res, *objectclasses_res;
336         struct ldb_dn *schemadn;
337         struct schema_conv ret;
338         struct dsdb_schema *schema;
339         char *error_string;
340
341         int ldb_ret, i;
342
343         ret.count = 0;
344         ret.skipped = 0;
345         ret.failures = 0;
346
347         while ((line = afdgets(fileno(in), mem_ctx, 0))) {
348                 /* Blank Line */
349                 if (line[0] == '\0') {
350                         continue;
351                 }
352                 /* Comment */
353                 if (line[0] == '#') {
354                         continue;
355                 }
356                 if (isdigit(line[0])) {
357                         char *p = strchr(line, ':');
358                         IF_NULL_FAIL_RET(p);
359                         p[0] = '\0';
360                         p++;
361                         oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
362                         trim_string(line, " ", " ");
363                         oid_map[num_oid_maps].old_oid = talloc_move(oid_map, &line);
364                         trim_string(p, " ", " ");
365                         oid_map[num_oid_maps].new_oid = p;
366                         num_oid_maps++;
367                         oid_map[num_oid_maps].old_oid = NULL;
368                 } else {
369                         char *p = strchr(line, ':');
370                         if (p) {
371                                 /* remap attribute/objectClass */
372                                 p[0] = '\0';
373                                 p++;
374                                 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
375                                 trim_string(line, " ", " ");
376                                 attr_map[num_attr_maps].old_attr = talloc_move(attr_map, &line);
377                                 trim_string(p, " ", " ");
378                                 attr_map[num_attr_maps].new_attr = p;
379                                 num_attr_maps++;
380                                 attr_map[num_attr_maps].old_attr = NULL;
381                         } else {
382                                 /* skip attribute/objectClass */
383                                 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
384                                 trim_string(line, " ", " ");
385                                 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
386                                 num_skip++;
387                                 attrs_skip[num_skip] = NULL;
388                         }
389                 }
390         }
391
392         schemadn = find_schema_dn(ldb, mem_ctx, &schema_res);
393         if (!schemadn) {
394                 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
395                 ret.failures = 1;
396                 return ret;
397         }
398         
399         ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
400         if (ldb_ret != LDB_SUCCESS) {
401                 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
402                 ret.failures = 1;
403                 return ret;
404         }
405
406
407         ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
408         if (ldb_ret != LDB_SUCCESS) {
409                 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
410                 ret.failures = 1;
411                 return ret;
412         }
413         
414         ldb_ret = dsdb_schema_from_ldb_results(mem_ctx, ldb,
415                                                lp_iconv_convenience(cmdline_lp_ctx),
416                                                schema_res, attrs_res, objectclasses_res, &schema, &error_string);
417         if (ldb_ret != LDB_SUCCESS) {
418                 printf("Failed to load schema: %s\n", error_string);
419                 ret.failures = 1;
420                 return ret;
421         }
422
423         switch (target) {
424         case TARGET_OPENLDAP:
425                 break;
426         case TARGET_FEDORA_DS:
427                 fprintf(out, "dn: cn=schema\n");
428                 break;
429         }
430
431         for (i=0; i < attrs_res->count; i++) {
432                 struct ldb_message *msg = attrs_res->msgs[i];
433
434                 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
435                 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
436                 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
437                 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
438                 bool single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", false);
439                 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
440                 char *schema_entry = NULL;
441                 int j;
442
443                 if (!name) {
444                         printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn));
445                         ret.failures++;
446                         continue;
447                 }
448
449                 /* We have been asked to skip some attributes/objectClasses */
450                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
451                         ret.skipped++;
452                         continue;
453                 }
454
455                 /* We might have been asked to remap this oid, due to a conflict */
456                 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
457                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
458                                 oid =  oid_map[j].new_oid;
459                                 break;
460                         }
461                 }
462                 
463                 switch (target) {
464                 case TARGET_OPENLDAP:
465                         schema_entry = talloc_asprintf(mem_ctx, 
466                                                        "attributetype (\n"
467                                                        "  %s\n", oid);
468                         break;
469                 case TARGET_FEDORA_DS:
470                         schema_entry = talloc_asprintf(mem_ctx, 
471                                                        "attributeTypes: (\n"
472                                                        "  %s\n", oid);
473                         break;
474                 }
475                 IF_NULL_FAIL_RET(schema_entry);
476
477                 /* We might have been asked to remap this name, due to a conflict */
478                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
479                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
480                                 name =  attr_map[j].new_attr;
481                                 break;
482                         }
483                 }
484                 
485                 schema_entry = talloc_asprintf_append(schema_entry, 
486                                                       "  NAME '%s'\n", name);
487                 IF_NULL_FAIL_RET(schema_entry);
488
489                 if (description) {
490                         schema_entry = talloc_asprintf_append(schema_entry, 
491                                                               "  DESC %s\n", description);
492                         IF_NULL_FAIL_RET(schema_entry);
493                 }
494
495                 if (map) {
496                         const char *syntax_oid;
497                         if (map->equality) {
498                                 schema_entry = talloc_asprintf_append(schema_entry, 
499                                                                       "  EQUALITY %s\n", map->equality);
500                                 IF_NULL_FAIL_RET(schema_entry);
501                         }
502                         if (map->substring) {
503                                 schema_entry = talloc_asprintf_append(schema_entry, 
504                                                                       "  SUBSTR %s\n", map->substring);
505                                 IF_NULL_FAIL_RET(schema_entry);
506                         }
507                         syntax_oid = map->Standard_OID;
508                         /* We might have been asked to remap this oid,
509                          * due to a conflict, or lack of
510                          * implementation */
511                         for (j=0; syntax_oid && oid_map && oid_map[j].old_oid; j++) {
512                                 if (strcasecmp(syntax_oid, oid_map[j].old_oid) == 0) {
513                                         syntax_oid =  oid_map[j].new_oid;
514                                         break;
515                                 }
516                         }
517                         schema_entry = talloc_asprintf_append(schema_entry, 
518                                                               "  SYNTAX %s\n", syntax_oid);
519                         IF_NULL_FAIL_RET(schema_entry);
520                 }
521
522                 if (single_value) {
523                         schema_entry = talloc_asprintf_append(schema_entry, 
524                                                               "  SINGLE-VALUE\n");
525                         IF_NULL_FAIL_RET(schema_entry);
526                 }
527                 
528                 schema_entry = talloc_asprintf_append(schema_entry, 
529                                                       "  )");
530
531                 switch (target) {
532                 case TARGET_OPENLDAP:
533                         fprintf(out, "%s\n\n", schema_entry);
534                         break;
535                 case TARGET_FEDORA_DS:
536                         fprintf(out, "%s\n", schema_entry);
537                         break;
538                 }
539                 ret.count++;
540         }
541
542         for (i=0; i < objectclasses_res->count; i++) {
543                 struct ldb_message *msg = objectclasses_res->msgs[i];
544                 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
545                 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
546                 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
547                 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
548                 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
549                 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
550                 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
551                 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
552                 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
553                 struct ldb_message_element *aux_class = ldb_msg_find_element(msg, "auxiliaryClass");
554                 struct ldb_message_element *sys_aux_class = ldb_msg_find_element(msg, "systemAuxiliaryClass");
555                 char *schema_entry = NULL;
556                 int j;
557
558                 if (!name) {
559                         printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn));
560                         ret.failures++;
561                         continue;
562                 }
563
564                 /* We have been asked to skip some attributes/objectClasses */
565                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
566                         ret.skipped++;
567                         continue;
568                 }
569
570                 if (must == NULL) {
571                         must = talloc_zero(mem_ctx, struct ldb_message_element);
572                 }
573
574                 if (may == NULL) {
575                         may = talloc_zero(mem_ctx, struct ldb_message_element);
576                 }
577
578                 if (sys_must == NULL) {
579                         sys_must = talloc_zero(mem_ctx, struct ldb_message_element);
580                 }
581
582                 if (sys_may == NULL) {
583                         sys_may = talloc_zero(mem_ctx, struct ldb_message_element);
584                 }
585
586                 if (!find_aux_classes(mem_ctx, ldb, schemadn, aux_class, must, sys_must, may, sys_may)) {
587                         ret.failures++;
588                         continue;
589                 }
590
591                 if (!find_aux_classes(mem_ctx, ldb, schemadn, sys_aux_class, must, sys_must, may, sys_may)) {
592                         ret.failures++;
593                         continue;
594                 }
595
596                 /* We might have been asked to remap this oid, due to a conflict */
597                 for (j=0; oid_map && oid_map[j].old_oid; j++) {
598                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
599                                 oid =  oid_map[j].new_oid;
600                                 break;
601                         }
602                 }
603                 
604                 switch (target) {
605                 case TARGET_OPENLDAP:
606                         schema_entry = talloc_asprintf(mem_ctx, 
607                                                        "objectclass (\n"
608                                                        "  %s\n", oid);
609                         break;
610                 case TARGET_FEDORA_DS:
611                         schema_entry = talloc_asprintf(mem_ctx, 
612                                                        "objectClasses: (\n"
613                                                        "  %s\n", oid);
614                         break;
615                 }
616                 IF_NULL_FAIL_RET(schema_entry);
617                 if (!schema_entry) {
618                         ret.failures++;
619                         break;
620                 }
621
622                 /* We might have been asked to remap this name, due to a conflict */
623                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
624                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
625                                 name =  attr_map[j].new_attr;
626                                 break;
627                         }
628                 }
629                 
630                 schema_entry = talloc_asprintf_append(schema_entry, 
631                                                       "  NAME '%s'\n", name);
632                 IF_NULL_FAIL_RET(schema_entry);
633
634                 if (!schema_entry) return ret;
635
636                 if (description) {
637                         schema_entry = talloc_asprintf_append(schema_entry, 
638                                                               "  DESC %s\n", description);
639                         IF_NULL_FAIL_RET(schema_entry);
640                 }
641
642                 if (subClassOf) {
643                         schema_entry = talloc_asprintf_append(schema_entry, 
644                                                               "  SUP %s\n", subClassOf);
645                         IF_NULL_FAIL_RET(schema_entry);
646                 }
647                 
648                 switch (objectClassCategory) {
649                 case 1:
650                         schema_entry = talloc_asprintf_append(schema_entry, 
651                                                               "  STRUCTURAL\n");
652                         IF_NULL_FAIL_RET(schema_entry);
653                         break;
654                 case 2:
655                         schema_entry = talloc_asprintf_append(schema_entry, 
656                                                               "  ABSTRACT\n");
657                         IF_NULL_FAIL_RET(schema_entry);
658                         break;
659                 case 3:
660                         schema_entry = talloc_asprintf_append(schema_entry, 
661                                                               "  AUXILIARY\n");
662                         IF_NULL_FAIL_RET(schema_entry);
663                         break;
664                 }
665
666 #define APPEND_ATTRS(attributes) \
667                 do {                                            \
668                         int k;                                          \
669                         for (k=0; attributes && k < attributes->num_values; k++) { \
670                                 int attr_idx; \
671                                 const char *attr_name = (const char *)attributes->values[k].data;  \
672                                 /* We might have been asked to remap this name, due to a conflict */ \
673                                 for (attr_idx=0; attr_name && attr_map && attr_map[attr_idx].old_attr; attr_idx++) { \
674                                         if (strcasecmp(attr_name, attr_map[attr_idx].old_attr) == 0) { \
675                                                 attr_name =  attr_map[attr_idx].new_attr; \
676                                                 break;                  \
677                                         }                               \
678                                 }                                       \
679                                                                         \
680                                 schema_entry = talloc_asprintf_append(schema_entry, \
681                                                                       " %s", \
682                                                                       attr_name); \
683                                 IF_NULL_FAIL_RET(schema_entry);         \
684                                 if (k != (attributes->num_values - 1)) { \
685                                         schema_entry = talloc_asprintf_append(schema_entry, \
686                                                                               " $"); \
687                                         IF_NULL_FAIL_RET(schema_entry); \
688                                         if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
689                                                 schema_entry = talloc_asprintf_append(schema_entry, \
690                                                                                       "\n  "); \
691                                                 IF_NULL_FAIL_RET(schema_entry); \
692                                         }                               \
693                                 }                                       \
694                         }                                               \
695                 } while (0)
696
697                 if ((must && must->values) || (sys_must && sys_must->values)) {
698                         schema_entry = talloc_asprintf_append(schema_entry, 
699                                                               "  MUST (");
700                         IF_NULL_FAIL_RET(schema_entry);
701
702                         APPEND_ATTRS(must);
703                         if (must && must->values && sys_must && sys_must->values) {
704                                 schema_entry = talloc_asprintf_append(schema_entry, \
705                                                                       " $"); \
706                         }
707                         APPEND_ATTRS(sys_must);
708                         
709                         schema_entry = talloc_asprintf_append(schema_entry, 
710                                                               " )\n");
711                         IF_NULL_FAIL_RET(schema_entry);
712                 }
713
714                 if ((may && may->values) || (sys_may && sys_may->values)) {
715                         schema_entry = talloc_asprintf_append(schema_entry, 
716                                                               "  MAY (");
717                         IF_NULL_FAIL_RET(schema_entry);
718
719                         APPEND_ATTRS(may);
720                         if (may && may->values && sys_may && sys_may->values) {
721                                 schema_entry = talloc_asprintf_append(schema_entry, \
722                                                                       " $"); \
723                         }
724                         APPEND_ATTRS(sys_may);
725                         
726                         schema_entry = talloc_asprintf_append(schema_entry, 
727                                                               " )\n");
728                         IF_NULL_FAIL_RET(schema_entry);
729                 }
730
731                 schema_entry = talloc_asprintf_append(schema_entry, 
732                                                       "  )");
733
734                 switch (target) {
735                 case TARGET_OPENLDAP:
736                         fprintf(out, "%s\n\n", schema_entry);
737                         break;
738                 case TARGET_FEDORA_DS:
739                         fprintf(out, "%s\n", schema_entry);
740                         break;
741                 }
742                 ret.count++;
743         }
744
745         return ret;
746 }
747
748  int main(int argc, const char **argv)
749 {
750         TALLOC_CTX *ctx;
751         struct ldb_cmdline *options;
752         FILE *in = stdin;
753         FILE *out = stdout;
754         struct ldb_context *ldb;
755         struct schema_conv ret;
756         const char *target_str;
757         enum convert_target target;
758
759         ctx = talloc_new(NULL);
760         ldb = ldb_init(ctx, NULL);
761
762         options = ldb_cmdline_process(ldb, argc, argv, usage);
763
764         if (options->input) {
765                 in = fopen(options->input, "r");
766                 if (!in) {
767                         perror(options->input);
768                         exit(1);
769                 }
770         }
771         if (options->output) {
772                 out = fopen(options->output, "w");
773                 if (!out) {
774                         perror(options->output);
775                         exit(1);
776                 }
777         }
778
779         target_str = lp_parm_string(cmdline_lp_ctx, NULL, "convert", "target");
780
781         if (!target_str || strcasecmp(target_str, "openldap") == 0) {
782                 target = TARGET_OPENLDAP;
783         } else if (strcasecmp(target_str, "fedora-ds") == 0) {
784                 target = TARGET_FEDORA_DS;
785         } else {
786                 printf("Unsupported target: %s\n", target_str);
787                 exit(1);
788         }
789
790         ret = process_convert(ldb, target, in, out);
791
792         fclose(in);
793         fclose(out);
794
795         printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
796
797         return 0;
798 }