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