diff options
author | Andrew Bartlett <abartlet@samba.org> | 2008-07-02 21:30:08 +1000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2008-07-02 21:30:08 +1000 |
commit | 7e851ada55bcdbd1ad19587e5dd6779c74c361e1 (patch) | |
tree | c06603ced759b72c7a7e8057470c86352d5e73fb /source4/lib | |
parent | fe174cc1415d5c55e7d8d2bb861a66f2203e68fe (diff) | |
download | samba-7e851ada55bcdbd1ad19587e5dd6779c74c361e1.tar.gz samba-7e851ada55bcdbd1ad19587e5dd6779c74c361e1.tar.xz samba-7e851ada55bcdbd1ad19587e5dd6779c74c361e1.zip |
Move ad2oLschema and oLschema2ldif into Samba4, out of LDB
LDB does not know about nor process the AD schema, so it makes no
sense to have this tool there. I've been changing it anyway, to use a
common schema manipulation library, and will enhance these links in
the future.
Andrew Bartlett
(This used to be commit c7704805b9a3541e4c8768278c8289b0aa6ed5e3)
Diffstat (limited to 'source4/lib')
-rw-r--r-- | source4/lib/ldb/config.mk | 27 | ||||
-rw-r--r-- | source4/lib/ldb/tools/ad2oLschema.c | 800 | ||||
-rw-r--r-- | source4/lib/ldb/tools/convert.c | 173 | ||||
-rw-r--r-- | source4/lib/ldb/tools/convert.h | 10 | ||||
-rw-r--r-- | source4/lib/ldb/tools/oLschema2ldif.c | 603 |
5 files changed, 0 insertions, 1613 deletions
diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk index 6821c058f2f..fe3b71d1d54 100644 --- a/source4/lib/ldb/config.mk +++ b/source4/lib/ldb/config.mk @@ -147,32 +147,5 @@ PRIVATE_DEPENDENCIES = \ ldbtest_OBJ_FILES = $(ldbsrcdir)/tools/ldbtest.o -################################################ -# Start BINARY oLschema2ldif -[BINARY::oLschema2ldif] -INSTALLDIR = BINDIR -PRIVATE_DEPENDENCIES = \ - LIBLDB_CMDLINE -# End BINARY oLschema2ldif -################################################ - - -oLschema2ldif_OBJ_FILES = $(addprefix $(ldbsrcdir)/tools/, convert.o oLschema2ldif.o) - -MANPAGES += $(ldbsrcdir)/man/oLschema2ldif.1 - -################################################ -# Start BINARY ad2oLschema -[BINARY::ad2oLschema] -INSTALLDIR = BINDIR -PRIVATE_DEPENDENCIES = \ - LIBLDB_CMDLINE -# End BINARY ad2oLschema -################################################ - -ad2oLschema_OBJ_FILES = $(addprefix $(ldbsrcdir)/tools/, convert.o ad2oLschema.o) - -MANPAGES += $(ldbsrcdir)/man/ad2oLschema.1 - mkinclude tools/config.mk mkinclude ldb_ildap/config.mk diff --git a/source4/lib/ldb/tools/ad2oLschema.c b/source4/lib/ldb/tools/ad2oLschema.c deleted file mode 100644 index df6fc91688d..00000000000 --- a/source4/lib/ldb/tools/ad2oLschema.c +++ /dev/null @@ -1,800 +0,0 @@ -/* - ldb database library - - Copyright (C) Andrew Bartlett 2006 - - ** NOTE! The following LGPL license applies to the ldb - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ - -/* - * Name: ldb - * - * Component: ad2oLschema - * - * Description: utility to convert an AD schema into the format required by OpenLDAP - * - * Author: Andrew Bartlett - */ - -#include "includes.h" -#include "ldb_includes.h" -#include "system/locale.h" -#include "tools/cmdline.h" -#include "tools/convert.h" -#include "param/param.h" -#include "lib/cmdline/popt_common.h" - -struct schema_conv { - int count; - int skipped; - int failures; -}; - -enum convert_target { - TARGET_OPENLDAP, - TARGET_FEDORA_DS -}; - - -static void usage(void) -{ - printf("Usage: ad2oLschema <options>\n"); - printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n"); - printf("Options:\n"); - printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses"); - printf(" -H url LDB or LDAP server to read schmea from\n"); - printf(" -O outputfile outputfile otherwise STDOUT\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); - printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n"); - exit(1); -} - -static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, - TALLOC_CTX *mem_ctx, - struct ldb_result **attrs_res) -{ - TALLOC_CTX *local_ctx = talloc_new(mem_ctx); - int ret; - const char *attrs[] = { - "lDAPDisplayName", - "isSingleValued", - "attributeID", - "attributeSyntax", - "description", - NULL - }; - - if (!local_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* Downlaod schema */ - ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, - "objectClass=attributeSchema", - attrs, attrs_res); - if (ret != LDB_SUCCESS) { - printf("Search failed: %s\n", ldb_errstring(ldb)); - return LDB_ERR_OPERATIONS_ERROR; - } - - return ret; -} - -static const char *oc_attrs[] = { - "lDAPDisplayName", - "mayContain", - "mustContain", - "systemMayContain", - "systemMustContain", - "objectClassCategory", - "governsID", - "description", - "subClassOf", - "systemAuxiliaryClass", - "auxiliaryClass", - NULL -}; - -static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn, - TALLOC_CTX *mem_ctx, - struct ldb_result *search_from, - struct ldb_result *res_list) -{ - int i; - int ret = 0; - for (i=0; i < search_from->count; i++) { - struct ldb_result *res; - const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i], - "lDAPDisplayname", NULL); - - ret = ldb_search_exp_fmt(ldb, mem_ctx, &res, - schemadn, LDB_SCOPE_SUBTREE, oc_attrs, - "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))", - name, name); - if (ret != LDB_SUCCESS) { - printf("Search failed: %s\n", ldb_errstring(ldb)); - return ret; - } - - res_list->msgs = talloc_realloc(res_list, res_list->msgs, - struct ldb_message *, res_list->count + 2); - if (!res_list->msgs) { - return LDB_ERR_OPERATIONS_ERROR; - } - res_list->msgs[res_list->count] = talloc_move(res_list, - &search_from->msgs[i]); - res_list->count++; - res_list->msgs[res_list->count] = NULL; - - if (res->count > 0) { - ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list); - } - if (ret != LDB_SUCCESS) { - return ret; - } - } - return ret; -} - -static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, - TALLOC_CTX *mem_ctx, - struct ldb_result **objectclasses_res) -{ - TALLOC_CTX *local_ctx = talloc_new(mem_ctx); - struct ldb_result *top_res, *ret_res; - int ret; - if (!local_ctx) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* Downlaod 'top' */ - ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, - "(&(objectClass=classSchema)(lDAPDisplayName=top))", - oc_attrs, &top_res); - if (ret != LDB_SUCCESS) { - printf("Search failed: %s\n", ldb_errstring(ldb)); - return LDB_ERR_OPERATIONS_ERROR; - } - - talloc_steal(local_ctx, top_res); - - if (top_res->count != 1) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret_res = talloc_zero(local_ctx, struct ldb_result); - if (!ret_res) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res); - - if (ret != LDB_SUCCESS) { - printf("Search failed: %s\n", ldb_errstring(ldb)); - return LDB_ERR_OPERATIONS_ERROR; - } - - *objectclasses_res = talloc_move(mem_ctx, &ret_res); - return ret; -} - -static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) -{ - const char *rootdse_attrs[] = {"schemaNamingContext", NULL}; - const char *no_attrs[] = { NULL }; - struct ldb_dn *schemadn; - struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL); - struct ldb_result *rootdse_res; - struct ldb_result *schema_res; - int ldb_ret; - if (!basedn) { - return NULL; - } - - /* Search for rootdse */ - ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res); - if (ldb_ret != LDB_SUCCESS) { - ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE, - "(&(objectClass=dMD)(cn=Schema))", - no_attrs, &schema_res); - if (ldb_ret) { - printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb)); - return NULL; - } - - talloc_steal(mem_ctx, schema_res); - - if (schema_res->count != 1) { - printf("Failed to find rootDSE"); - return NULL; - } - - schemadn = talloc_steal(mem_ctx, schema_res->msgs[0]->dn); - talloc_free(schema_res); - return schemadn; - - } - - talloc_steal(mem_ctx, rootdse_res); - - if (rootdse_res->count != 1) { - printf("Failed to find rootDSE"); - return NULL; - } - - /* Locate schema */ - schemadn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext"); - if (!schemadn) { - return NULL; - } - - talloc_free(rootdse_res); - return schemadn; -} - -static bool merge_attr_list(TALLOC_CTX *mem_ctx, - struct ldb_message_element *attrs, struct ldb_message_element *new_attrs) -{ - struct ldb_val *values; - if (!new_attrs) { - return true; - } - - values = talloc_realloc(mem_ctx, - attrs->values, struct ldb_val, attrs->num_values + new_attrs->num_values); - - attrs->values = values; - - memcpy(&attrs->values[attrs->num_values], new_attrs->values, sizeof(*new_attrs->values) * new_attrs->num_values); - attrs->num_values = attrs->num_values + new_attrs->num_values; - - /* Add sort and unique implementation here */ - - return true; -} - -static bool find_aux_classes(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct ldb_dn *schema_dn, - struct ldb_message_element *aux_class, struct ldb_message_element *must, - struct ldb_message_element *sys_must, struct ldb_message_element *may, - struct ldb_message_element *sys_may) -{ - int i, ret; - struct ldb_message *msg; - struct ldb_result *res; - - for (i=0; aux_class && i < aux_class->num_values; i++) { - ret = ldb_search_exp_fmt(ldb, mem_ctx, &res, - schema_dn, LDB_SCOPE_SUBTREE, oc_attrs, - "(&(objectClass=classSchema)(lDAPDisplayName=%s))", - aux_class->values[i].data); - if (ret != LDB_SUCCESS) { - return false; - } - - msg = res->msgs[0]; - - if (!merge_attr_list(mem_ctx, must, ldb_msg_find_element(msg, "mustContain"))) { - return false; - } - if (!merge_attr_list(mem_ctx, sys_must, ldb_msg_find_element(msg, "systemMustContain"))) { - return false; - } - if (!merge_attr_list(mem_ctx, may, ldb_msg_find_element(msg, "mayContain"))) { - return false; - } - if (!merge_attr_list(mem_ctx, sys_may, ldb_msg_find_element(msg, "systemMayContain"))) { - return false; - } - - - if (res->count == 0) { - return false; - } - - if (!find_aux_classes(mem_ctx, ldb, schema_dn, - ldb_msg_find_element(msg, "auxiliaryClass"), must, sys_must, may, sys_may)) { - return false; - } - if (!find_aux_classes(mem_ctx, ldb, schema_dn, - ldb_msg_find_element(msg, "systemAuxiliaryClass"), must, sys_must, may, sys_may)) { - return false; - } - } - return true; -} - - -#define IF_NULL_FAIL_RET(x) do { \ - if (!x) { \ - ret.failures++; \ - return ret; \ - } \ - } while (0) - - -static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out) -{ - /* Read list of attributes to skip, OIDs to map */ - TALLOC_CTX *mem_ctx = talloc_new(ldb); - char *line; - const char **attrs_skip = NULL; - int num_skip = 0; - struct oid_map { - char *old_oid; - char *new_oid; - } *oid_map = NULL; - int num_oid_maps = 0; - struct attr_map { - char *old_attr; - char *new_attr; - } *attr_map = NULL; - int num_attr_maps = 0; - struct ldb_result *attrs_res, *objectclasses_res; - struct ldb_dn *schemadn; - struct schema_conv ret; - - int ldb_ret, i; - - ret.count = 0; - ret.skipped = 0; - ret.failures = 0; - - while ((line = afdgets(fileno(in), mem_ctx, 0))) { - /* Blank Line */ - if (line[0] == '\0') { - continue; - } - /* Comment */ - if (line[0] == '#') { - continue; - } - if (isdigit(line[0])) { - char *p = strchr(line, ':'); - IF_NULL_FAIL_RET(p); - p[0] = '\0'; - p++; - oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2); - trim_string(line, " ", " "); - oid_map[num_oid_maps].old_oid = talloc_move(oid_map, &line); - trim_string(p, " ", " "); - oid_map[num_oid_maps].new_oid = p; - num_oid_maps++; - oid_map[num_oid_maps].old_oid = NULL; - } else { - char *p = strchr(line, ':'); - if (p) { - /* remap attribute/objectClass */ - p[0] = '\0'; - p++; - attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2); - trim_string(line, " ", " "); - attr_map[num_attr_maps].old_attr = talloc_move(attr_map, &line); - trim_string(p, " ", " "); - attr_map[num_attr_maps].new_attr = p; - num_attr_maps++; - attr_map[num_attr_maps].old_attr = NULL; - } else { - /* skip attribute/objectClass */ - attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2); - trim_string(line, " ", " "); - attrs_skip[num_skip] = talloc_move(attrs_skip, &line); - num_skip++; - attrs_skip[num_skip] = NULL; - } - } - } - - schemadn = find_schema_dn(ldb, mem_ctx); - if (!schemadn) { - printf("Failed to find schema DN: %s\n", ldb_errstring(ldb)); - ret.failures = 1; - return ret; - } - - ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res); - if (ldb_ret != LDB_SUCCESS) { - printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb)); - ret.failures = 1; - return ret; - } - - switch (target) { - case TARGET_OPENLDAP: - break; - case TARGET_FEDORA_DS: - fprintf(out, "dn: cn=schema\n"); - break; - } - - for (i=0; i < attrs_res->count; i++) { - struct ldb_message *msg = attrs_res->msgs[i]; - - const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL); - const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL); - const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL); - const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL); - bool single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", false); - const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax); - char *schema_entry = NULL; - int j; - - if (!name) { - printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn)); - ret.failures++; - continue; - } - - /* We have been asked to skip some attributes/objectClasses */ - if (attrs_skip && str_list_check_ci(attrs_skip, name)) { - ret.skipped++; - continue; - } - - /* We might have been asked to remap this oid, due to a conflict */ - for (j=0; oid && oid_map && oid_map[j].old_oid; j++) { - if (strcasecmp(oid, oid_map[j].old_oid) == 0) { - oid = oid_map[j].new_oid; - break; - } - } - - switch (target) { - case TARGET_OPENLDAP: - schema_entry = talloc_asprintf(mem_ctx, - "attributetype (\n" - " %s\n", oid); - break; - case TARGET_FEDORA_DS: - schema_entry = talloc_asprintf(mem_ctx, - "attributeTypes: (\n" - " %s\n", oid); - break; - } - IF_NULL_FAIL_RET(schema_entry); - - /* We might have been asked to remap this name, due to a conflict */ - for (j=0; name && attr_map && attr_map[j].old_attr; j++) { - if (strcasecmp(name, attr_map[j].old_attr) == 0) { - name = attr_map[j].new_attr; - break; - } - } - - schema_entry = talloc_asprintf_append(schema_entry, - " NAME '%s'\n", name); - IF_NULL_FAIL_RET(schema_entry); - - if (description) { - schema_entry = talloc_asprintf_append(schema_entry, - " DESC %s\n", description); - IF_NULL_FAIL_RET(schema_entry); - } - - if (map) { - const char *syntax_oid; - if (map->equality) { - schema_entry = talloc_asprintf_append(schema_entry, - " EQUALITY %s\n", map->equality); - IF_NULL_FAIL_RET(schema_entry); - } - if (map->substring) { - schema_entry = talloc_asprintf_append(schema_entry, - " SUBSTR %s\n", map->substring); - IF_NULL_FAIL_RET(schema_entry); - } - syntax_oid = map->Standard_OID; - /* We might have been asked to remap this oid, - * due to a conflict, or lack of - * implementation */ - for (j=0; syntax_oid && oid_map && oid_map[j].old_oid; j++) { - if (strcasecmp(syntax_oid, oid_map[j].old_oid) == 0) { - syntax_oid = oid_map[j].new_oid; - break; - } - } - schema_entry = talloc_asprintf_append(schema_entry, - " SYNTAX %s\n", syntax_oid); - IF_NULL_FAIL_RET(schema_entry); - } - - if (single_value) { - schema_entry = talloc_asprintf_append(schema_entry, - " SINGLE-VALUE\n"); - IF_NULL_FAIL_RET(schema_entry); - } - - schema_entry = talloc_asprintf_append(schema_entry, - " )"); - - switch (target) { - case TARGET_OPENLDAP: - fprintf(out, "%s\n\n", schema_entry); - break; - case TARGET_FEDORA_DS: - fprintf(out, "%s\n", schema_entry); - break; - } - ret.count++; - } - - ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res); - if (ldb_ret != LDB_SUCCESS) { - printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb)); - ret.failures = 1; - return ret; - } - - for (i=0; i < objectclasses_res->count; i++) { - struct ldb_message *msg = objectclasses_res->msgs[i]; - const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL); - const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL); - const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL); - const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL); - int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0); - struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain"); - struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain"); - struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain"); - struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain"); - struct ldb_message_element *aux_class = ldb_msg_find_element(msg, "auxiliaryClass"); - struct ldb_message_element *sys_aux_class = ldb_msg_find_element(msg, "systemAuxiliaryClass"); - char *schema_entry = NULL; - int j; - - if (!name) { - printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn)); - ret.failures++; - continue; - } - - /* We have been asked to skip some attributes/objectClasses */ - if (attrs_skip && str_list_check_ci(attrs_skip, name)) { - ret.skipped++; - continue; - } - - if (must == NULL) { - must = talloc_zero(mem_ctx, struct ldb_message_element); - } - - if (may == NULL) { - may = talloc_zero(mem_ctx, struct ldb_message_element); - } - - if (sys_must == NULL) { - sys_must = talloc_zero(mem_ctx, struct ldb_message_element); - } - - if (sys_may == NULL) { - sys_may = talloc_zero(mem_ctx, struct ldb_message_element); - } - - if (!find_aux_classes(mem_ctx, ldb, schemadn, aux_class, must, sys_must, may, sys_may)) { - ret.failures++; - continue; - } - - if (!find_aux_classes(mem_ctx, ldb, schemadn, sys_aux_class, must, sys_must, may, sys_may)) { - ret.failures++; - continue; - } - - /* We might have been asked to remap this oid, due to a conflict */ - for (j=0; oid_map && oid_map[j].old_oid; j++) { - if (strcasecmp(oid, oid_map[j].old_oid) == 0) { - oid = oid_map[j].new_oid; - break; - } - } - - switch (target) { - case TARGET_OPENLDAP: - schema_entry = talloc_asprintf(mem_ctx, - "objectclass (\n" - " %s\n", oid); - break; - case TARGET_FEDORA_DS: - schema_entry = talloc_asprintf(mem_ctx, - "objectClasses: (\n" - " %s\n", oid); - break; - } - IF_NULL_FAIL_RET(schema_entry); - if (!schema_entry) { - ret.failures++; - break; - } - - /* We might have been asked to remap this name, due to a conflict */ - for (j=0; name && attr_map && attr_map[j].old_attr; j++) { - if (strcasecmp(name, attr_map[j].old_attr) == 0) { - name = attr_map[j].new_attr; - break; - } - } - - schema_entry = talloc_asprintf_append(schema_entry, - " NAME '%s'\n", name); - IF_NULL_FAIL_RET(schema_entry); - - if (!schema_entry) return ret; - - if (description) { - schema_entry = talloc_asprintf_append(schema_entry, - " DESC %s\n", description); - IF_NULL_FAIL_RET(schema_entry); - } - - if (subClassOf) { - schema_entry = talloc_asprintf_append(schema_entry, - " SUP %s\n", subClassOf); - IF_NULL_FAIL_RET(schema_entry); - } - - switch (objectClassCategory) { - case 1: - schema_entry = talloc_asprintf_append(schema_entry, - " STRUCTURAL\n"); - IF_NULL_FAIL_RET(schema_entry); - break; - case 2: - schema_entry = talloc_asprintf_append(schema_entry, - " ABSTRACT\n"); - IF_NULL_FAIL_RET(schema_entry); - break; - case 3: - schema_entry = talloc_asprintf_append(schema_entry, - " AUXILIARY\n"); - IF_NULL_FAIL_RET(schema_entry); - break; - } - -#define APPEND_ATTRS(attributes) \ - do { \ - int k; \ - for (k=0; attributes && k < attributes->num_values; k++) { \ - int attr_idx; \ - const char *attr_name = (const char *)attributes->values[k].data; \ - /* We might have been asked to remap this name, due to a conflict */ \ - for (attr_idx=0; attr_name && attr_map && attr_map[attr_idx].old_attr; attr_idx++) { \ - if (strcasecmp(attr_name, attr_map[attr_idx].old_attr) == 0) { \ - attr_name = attr_map[attr_idx].new_attr; \ - break; \ - } \ - } \ - \ - schema_entry = talloc_asprintf_append(schema_entry, \ - " %s", \ - attr_name); \ - IF_NULL_FAIL_RET(schema_entry); \ - if (k != (attributes->num_values - 1)) { \ - schema_entry = talloc_asprintf_append(schema_entry, \ - " $"); \ - IF_NULL_FAIL_RET(schema_entry); \ - if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \ - schema_entry = talloc_asprintf_append(schema_entry, \ - "\n "); \ - IF_NULL_FAIL_RET(schema_entry); \ - } \ - } \ - } \ - } while (0) - - if ((must && must->values) || (sys_must && sys_must->values)) { - schema_entry = talloc_asprintf_append(schema_entry, - " MUST ("); - IF_NULL_FAIL_RET(schema_entry); - - APPEND_ATTRS(must); - if (must && must->values && sys_must && sys_must->values) { - schema_entry = talloc_asprintf_append(schema_entry, \ - " $"); \ - } - APPEND_ATTRS(sys_must); - - schema_entry = talloc_asprintf_append(schema_entry, - " )\n"); - IF_NULL_FAIL_RET(schema_entry); - } - - if ((may && may->values) || (sys_may && sys_may->values)) { - schema_entry = talloc_asprintf_append(schema_entry, - " MAY ("); - IF_NULL_FAIL_RET(schema_entry); - - APPEND_ATTRS(may); - if (may && may->values && sys_may && sys_may->values) { - schema_entry = talloc_asprintf_append(schema_entry, \ - " $"); \ - } - APPEND_ATTRS(sys_may); - - schema_entry = talloc_asprintf_append(schema_entry, - " )\n"); - IF_NULL_FAIL_RET(schema_entry); - } - - schema_entry = talloc_asprintf_append(schema_entry, - " )"); - - switch (target) { - case TARGET_OPENLDAP: - fprintf(out, "%s\n\n", schema_entry); - break; - case TARGET_FEDORA_DS: - fprintf(out, "%s\n", schema_entry); - break; - } - ret.count++; - } - - return ret; -} - - int main(int argc, const char **argv) -{ - TALLOC_CTX *ctx; - struct ldb_cmdline *options; - FILE *in = stdin; - FILE *out = stdout; - struct ldb_context *ldb; - struct schema_conv ret; - const char *target_str; - enum convert_target target; - - ctx = talloc_new(NULL); - ldb = ldb_init(ctx, NULL); - - options = ldb_cmdline_process(ldb, argc, argv, usage); - - if (options->input) { - in = fopen(options->input, "r"); - if (!in) { - perror(options->input); - exit(1); - } - } - if (options->output) { - out = fopen(options->output, "w"); - if (!out) { - perror(options->output); - exit(1); - } - } - - target_str = lp_parm_string(cmdline_lp_ctx, NULL, "convert", "target"); - - if (!target_str || strcasecmp(target_str, "openldap") == 0) { - target = TARGET_OPENLDAP; - } else if (strcasecmp(target_str, "fedora-ds") == 0) { - target = TARGET_FEDORA_DS; - } else { - printf("Unsupported target: %s\n", target_str); - exit(1); - } - - ret = process_convert(ldb, target, in, out); - - fclose(in); - fclose(out); - - printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures); - - return 0; -} diff --git a/source4/lib/ldb/tools/convert.c b/source4/lib/ldb/tools/convert.c deleted file mode 100644 index 5a5cf2a94c5..00000000000 --- a/source4/lib/ldb/tools/convert.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - ldb database library - - Copyright (C) Simo Sorce 2005 - - ** NOTE! The following LGPL license applies to the ldb - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ - -#include "convert.h" -#include "ldb_includes.h" - -/* Shared map for converting syntax between formats */ -static const struct syntax_map syntax_map[] = { - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.12", - .AD_OID = "2.5.5.1", - .equality = "distinguishedNameMatch", - .comment = "Object(DS-DN) == a DN" - }, -#if 0 - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.38", - .AD_OID = "2.5.5.2", - .equality = "objectIdentifierMatch", - .comment = "OID String" - }, -#else - { - .Standard_OID = "1.2.840.113556.1.4.905", - .AD_OID = "2.5.5.2", - .equality = "caseIgnoreMatch", - .comment = "OID as a Case Insensitive String" - }, -#endif - { - .Standard_OID = "1.2.840.113556.1.4.905", - .AD_OID = "2.5.5.4", - .equality = "caseIgnoreMatch", - .substring = "caseIgnoreSubstringsMatch", - .comment = "Case Insensitive String" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.26", - .AD_OID = "2.5.5.5", - .equality = "caseExactIA5Match", - .comment = "Printable String" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.36", - .AD_OID = "2.5.5.6", - .equality = "numericStringMatch", - .substring = "numericStringSubstringsMatch", - .comment = "Numeric String" - }, - { - .Standard_OID = "1.2.840.113556.1.4.903", - .AD_OID = "2.5.5.7", - .equality = "distinguishedNameMatch", - .comment = "OctetString: Binary+DN" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.7", - .AD_OID = "2.5.5.8", - .equality = "booleanMatch", - .comment = "Boolean" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.27", - .AD_OID = "2.5.5.9", - .equality = "integerMatch", - .comment = "Integer" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.40", - .AD_OID = "2.5.5.10", - .equality = "octetStringMatch", - .comment = "Octet String" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.24", - .AD_OID = "2.5.5.11", - .equality = "generalizedTimeMatch", - .comment = "Generalized Time" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.53", - .AD_OID = "2.5.5.11", - .equality = "generalizedTimeMatch", - .comment = "UTC Time" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.15", - .AD_OID = "2.5.5.12", - .equality = "caseIgnoreMatch", - .substring = "caseIgnoreSubstringsMatch", - .comment = "Directory String" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.43", - .AD_OID = "2.5.5.13", - .comment = "Presentation Address" - }, - { - .Standard_OID = "Not Found Yet", - .AD_OID = "2.5.5.14", - .equality = "distinguishedNameMatch", - .comment = "OctetString: String+DN" - }, - { - .Standard_OID = "1.2.840.113556.1.4.907", - .AD_OID = "2.5.5.15", - .equality = "octetStringMatch", - .comment = "NT Security Descriptor" - }, - { - .Standard_OID = "1.2.840.113556.1.4.906", - .AD_OID = "2.5.5.16", - .equality = "integerMatch", - .comment = "Large Integer" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.40", - .AD_OID = "2.5.5.17", - .equality = "octetStringMatch", - .comment = "Octet String - Security Identifier (SID)" - }, - { - .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.26", - .AD_OID = "2.5.5.5", - .equality = "caseExactIA5Match", - .comment = "IA5 String" - }, - { .Standard_OID = NULL - } -}; - - -const struct syntax_map *find_syntax_map_by_ad_oid(const char *ad_oid) -{ - int i; - for (i=0; syntax_map[i].Standard_OID; i++) { - if (strcasecmp(ad_oid, syntax_map[i].AD_OID) == 0) { - return &syntax_map[i]; - } - } - return NULL; -} - -const struct syntax_map *find_syntax_map_by_standard_oid(const char *standard_oid) -{ - int i; - for (i=0; syntax_map[i].Standard_OID; i++) { - if (strcasecmp(standard_oid, syntax_map[i].Standard_OID) == 0) { - return &syntax_map[i]; - } - } - return NULL; -} diff --git a/source4/lib/ldb/tools/convert.h b/source4/lib/ldb/tools/convert.h deleted file mode 100644 index de379343a68..00000000000 --- a/source4/lib/ldb/tools/convert.h +++ /dev/null @@ -1,10 +0,0 @@ -struct syntax_map { - const char *Standard_OID; - const char *AD_OID; - const char *equality; - const char *substring; - const char *comment; -}; - -const struct syntax_map *find_syntax_map_by_ad_oid(const char *ad_oid); -const struct syntax_map *find_syntax_map_by_standard_oid(const char *standard_oid); diff --git a/source4/lib/ldb/tools/oLschema2ldif.c b/source4/lib/ldb/tools/oLschema2ldif.c deleted file mode 100644 index 3c31f37c553..00000000000 --- a/source4/lib/ldb/tools/oLschema2ldif.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - ldb database library - - Copyright (C) Simo Sorce 2005 - - ** NOTE! The following LGPL license applies to the ldb - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ - -/* - * Name: ldb - * - * Component: oLschema2ldif - * - * Description: utility to convert an OpenLDAP schema into AD LDIF - * - * Author: Simo Sorce - */ - -#include "ldb_includes.h" -#include "tools/cmdline.h" -#include "tools/convert.h" - -#define SCHEMA_UNKNOWN 0 -#define SCHEMA_NAME 1 -#define SCHEMA_SUP 2 -#define SCHEMA_STRUCTURAL 3 -#define SCHEMA_ABSTRACT 4 -#define SCHEMA_AUXILIARY 5 -#define SCHEMA_MUST 6 -#define SCHEMA_MAY 7 -#define SCHEMA_SINGLE_VALUE 8 -#define SCHEMA_EQUALITY 9 -#define SCHEMA_ORDERING 10 -#define SCHEMA_SUBSTR 11 -#define SCHEMA_SYNTAX 12 -#define SCHEMA_DESC 13 - -struct schema_conv { - int count; - int failures; -}; - -struct schema_token { - int type; - char *value; -}; - -struct ldb_context *ldb_ctx; -struct ldb_dn *basedn; - -static int check_braces(const char *string) -{ - int b; - char *c; - - b = 0; - if ((c = strchr(string, '(')) == NULL) { - return -1; - } - b++; - c++; - while (b) { - c = strpbrk(c, "()"); - if (c == NULL) return 1; - if (*c == '(') b++; - if (*c == ')') b--; - c++; - } - return 0; -} - -static char *skip_spaces(char *string) { - return (string + strspn(string, " \t\n")); -} - -static int add_multi_string(struct ldb_message *msg, const char *attr, char *values) -{ - char *c; - char *s; - int n; - - c = skip_spaces(values); - while (*c) { - n = strcspn(c, " \t$"); - s = talloc_strndup(msg, c, n); - if (ldb_msg_add_string(msg, attr, s) != 0) { - return -1; - } - c += n; - c += strspn(c, " \t$"); - } - - return 0; -} - -#define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0) -#define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0) - -static char *get_def_value(TALLOC_CTX *ctx, char **string) -{ - char *c = *string; - char *value; - int n; - - if (*c == '\'') { - c++; - n = strcspn(c, "\'"); - value = talloc_strndup(ctx, c, n); - c += n; - c++; /* skip closing \' */ - } else { - n = strcspn(c, " \t\n"); - value = talloc_strndup(ctx, c, n); - c += n; - } - *string = c; - - return value; -} - -static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string) -{ - char *c = skip_spaces(*string); - char *type; - struct schema_token *token; - int n; - - token = talloc(ctx, struct schema_token); - - n = strcspn(c, " \t\n"); - type = talloc_strndup(token, c, n); - c += n; - c = skip_spaces(c); - - if (strcasecmp("NAME", type) == 0) { - talloc_free(type); - token->type = SCHEMA_NAME; - /* we do not support aliases so we get only the first name given and skip others */ - if (*c == '(') { - char *s = strchr(c, ')'); - if (s == NULL) return NULL; - s = skip_spaces(s); - *string = s; - - c++; - c = skip_spaces(c); - } - - token->value = get_def_value(ctx, &c); - - if (*string < c) { /* single name */ - c = skip_spaces(c); - *string = c; - } - return token; - } - if (strcasecmp("SUP", type) == 0) { - talloc_free(type); - token->type = SCHEMA_SUP; - - if (*c == '(') { - c++; - n = strcspn(c, ")"); - token->value = talloc_strndup(ctx, c, n); - c += n; - c++; - } else { - token->value = get_def_value(ctx, &c); - } - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("STRUCTURAL", type) == 0) { - talloc_free(type); - token->type = SCHEMA_STRUCTURAL; - *string = c; - return token; - } - - if (strcasecmp("ABSTRACT", type) == 0) { - talloc_free(type); - token->type = SCHEMA_ABSTRACT; - *string = c; - return token; - } - - if (strcasecmp("AUXILIARY", type) == 0) { - talloc_free(type); - token->type = SCHEMA_AUXILIARY; - *string = c; - return token; - } - - if (strcasecmp("MUST", type) == 0) { - talloc_free(type); - token->type = SCHEMA_MUST; - - if (*c == '(') { - c++; - n = strcspn(c, ")"); - token->value = talloc_strndup(ctx, c, n); - c += n; - c++; - } else { - token->value = get_def_value(ctx, &c); - } - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("MAY", type) == 0) { - talloc_free(type); - token->type = SCHEMA_MAY; - - if (*c == '(') { - c++; - n = strcspn(c, ")"); - token->value = talloc_strndup(ctx, c, n); - c += n; - c++; - } else { - token->value = get_def_value(ctx, &c); - } - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("SINGLE-VALUE", type) == 0) { - talloc_free(type); - token->type = SCHEMA_SINGLE_VALUE; - *string = c; - return token; - } - - if (strcasecmp("EQUALITY", type) == 0) { - talloc_free(type); - token->type = SCHEMA_EQUALITY; - - token->value = get_def_value(ctx, &c); - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("ORDERING", type) == 0) { - talloc_free(type); - token->type = SCHEMA_ORDERING; - - token->value = get_def_value(ctx, &c); - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("SUBSTR", type) == 0) { - talloc_free(type); - token->type = SCHEMA_SUBSTR; - - token->value = get_def_value(ctx, &c); - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("SYNTAX", type) == 0) { - talloc_free(type); - token->type = SCHEMA_SYNTAX; - - token->value = get_def_value(ctx, &c); - - c = skip_spaces(c); - *string = c; - return token; - } - - if (strcasecmp("DESC", type) == 0) { - talloc_free(type); - token->type = SCHEMA_DESC; - - token->value = get_def_value(ctx, &c); - - c = skip_spaces(c); - *string = c; - return token; - } - - token->type = SCHEMA_UNKNOWN; - token->value = type; - if (*c == ')') { - *string = c; - return token; - } - if (*c == '\'') { - c = strchr(++c, '\''); - c++; - } else { - c += strcspn(c, " \t\n"); - } - c = skip_spaces(c); - *string = c; - - return token; -} - -static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry) -{ - TALLOC_CTX *ctx; - struct ldb_message *msg; - struct schema_token *token; - char *c, *s; - int n; - - ctx = talloc_new(mem_ctx); - msg = ldb_msg_new(ctx); - - ldb_msg_add_string(msg, "objectClass", "top"); - - c = talloc_strdup(ctx, entry); - if (!c) return NULL; - - c = skip_spaces(c); - - switch (*c) { - case 'a': - if (strncmp(c, "attributetype", 13) == 0) { - c += 13; - MSG_ADD_STRING("objectClass", "attributeSchema"); - break; - } - goto failed; - case 'o': - if (strncmp(c, "objectclass", 11) == 0) { - c += 11; - MSG_ADD_STRING("objectClass", "classSchema"); - break; - } - goto failed; - default: - goto failed; - } - - c = strchr(c, '('); - if (c == NULL) goto failed; - c++; - - c = skip_spaces(c); - - /* get attributeID */ - n = strcspn(c, " \t"); - s = talloc_strndup(msg, c, n); - MSG_ADD_STRING("attributeID", s); - c += n; - c = skip_spaces(c); - - while (*c != ')') { - token = get_next_schema_token(msg, &c); - if (!token) goto failed; - - switch (token->type) { - case SCHEMA_NAME: - MSG_ADD_STRING("cn", token->value); - MSG_ADD_STRING("name", token->value); - MSG_ADD_STRING("lDAPDisplayName", token->value); - msg->dn = ldb_dn_copy(msg, basedn); - ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Schema,CN=Configuration", token->value); - break; - - case SCHEMA_SUP: - MSG_ADD_M_STRING("subClassOf", token->value); - break; - - case SCHEMA_STRUCTURAL: - MSG_ADD_STRING("objectClassCategory", "1"); - break; - - case SCHEMA_ABSTRACT: - MSG_ADD_STRING("objectClassCategory", "2"); - break; - - case SCHEMA_AUXILIARY: - MSG_ADD_STRING("objectClassCategory", "3"); - break; - - case SCHEMA_MUST: - MSG_ADD_M_STRING("mustContain", token->value); - break; - - case SCHEMA_MAY: - MSG_ADD_M_STRING("mayContain", token->value); - break; - - case SCHEMA_SINGLE_VALUE: - MSG_ADD_STRING("isSingleValued", "TRUE"); - break; - - case SCHEMA_EQUALITY: - /* TODO */ - break; - - case SCHEMA_ORDERING: - /* TODO */ - break; - - case SCHEMA_SUBSTR: - /* TODO */ - break; - - case SCHEMA_SYNTAX: - { - const struct syntax_map *map = - find_syntax_map_by_standard_oid(token->value); - if (!map) { - break; - } - MSG_ADD_STRING("attributeSyntax", map->AD_OID); - break; - } - case SCHEMA_DESC: - MSG_ADD_STRING("description", token->value); - break; - - default: - fprintf(stderr, "Unknown Definition: %s\n", token->value); - } - } - - talloc_steal(mem_ctx, msg); - talloc_free(ctx); - return msg; - -failed: - talloc_free(ctx); - return NULL; -} - -static struct schema_conv process_file(FILE *in, FILE *out) -{ - TALLOC_CTX *ctx; - struct schema_conv ret; - char *entry; - int c, t, line; - struct ldb_ldif ldif; - - ldif.changetype = LDB_CHANGETYPE_NONE; - - ctx = talloc_new(NULL); - - ret.count = 0; - ret.failures = 0; - line = 0; - - while ((c = fgetc(in)) != EOF) { - line++; - /* fprintf(stderr, "Parsing line %d\n", line); */ - if (c == '#') { - do { - c = fgetc(in); - } while (c != EOF && c != '\n'); - continue; - } - if (c == '\n') { - continue; - } - - t = 0; - entry = talloc_array(ctx, char, 1024); - if (entry == NULL) exit(-1); - - do { - if (c == '\n') { - entry[t] = '\0'; - if (check_braces(entry) == 0) { - ret.count++; - ldif.msg = process_entry(ctx, entry); - if (ldif.msg == NULL) { - ret.failures++; - fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line); - break; - } - ldb_ldif_write_file(ldb_ctx, out, &ldif); - break; - } - line++; - } else { - entry[t] = c; - t++; - } - if ((t % 1023) == 0) { - entry = talloc_realloc(ctx, entry, char, t + 1024); - if (entry == NULL) exit(-1); - } - } while ((c = fgetc(in)) != EOF); - - if (c != '\n') { - entry[t] = '\0'; - if (check_braces(entry) == 0) { - ret.count++; - ldif.msg = process_entry(ctx, entry); - if (ldif.msg == NULL) { - ret.failures++; - fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line); - break; - } - ldb_ldif_write_file(ldb_ctx, out, &ldif); - } else { - fprintf(stderr, "malformed entry on line %d\n", line); - ret.failures++; - } - } - - if (c == EOF) break; - } - - return ret; -} - -static void usage(void) -{ - printf("Usage: oLschema2ldif -H NONE <options>\n"); - printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n"); - printf("Options:\n"); - printf(" -I inputfile inputfile of OpenLDAP style schema otherwise STDIN\n"); - printf(" -O outputfile outputfile otherwise STDOUT\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); - printf("Converts records from an openLdap formatted schema to an ldif schema\n\n"); - exit(1); -} - - int main(int argc, const char **argv) -{ - TALLOC_CTX *ctx; - struct schema_conv ret; - struct ldb_cmdline *options; - FILE *in = stdin; - FILE *out = stdout; - ctx = talloc_new(NULL); - ldb_ctx = ldb_init(ctx, NULL); - - setenv("LDB_URL", "NONE", 1); - options = ldb_cmdline_process(ldb_ctx, argc, argv, usage); - - if (options->basedn == NULL) { - perror("Base DN not specified"); - exit(1); - } else { - basedn = ldb_dn_new(ctx, ldb_ctx, options->basedn); - if ( ! ldb_dn_validate(basedn)) { - perror("Malformed Base DN"); - exit(1); - } - } - - if (options->input) { - in = fopen(options->input, "r"); - if (!in) { - perror(options->input); - exit(1); - } - } - if (options->output) { - out = fopen(options->output, "w"); - if (!out) { - perror(options->output); - exit(1); - } - } - - ret = process_file(in, out); - - fclose(in); - fclose(out); - - printf("Converted %d records with %d failures\n", ret.count, ret.failures); - - return 0; -} |