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