docs: remove whitespace in example samba.ldif (fix bug #8789)
[Samba/gebeck_regimport.git] / source4 / utils / oLschema2ldif.c
blobae69db19bc3620c396e8d41f9f9375c5f8e6c730
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
8 ** under the LGPL
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb
27 * Component: oLschema2ldif
29 * Description: utility to convert an OpenLDAP schema into AD LDIF
31 * Author: Simo Sorce
34 #include "includes.h"
35 #include "ldb.h"
36 #include "tools/cmdline.h"
37 #include "dsdb/samdb/samdb.h"
38 #include "../lib/crypto/sha256.h"
39 #include "../librpc/gen_ndr/ndr_misc.h"
40 #include "lib/cmdline/popt_common.h"
42 #define SCHEMA_UNKNOWN 0
43 #define SCHEMA_NAME 1
44 #define SCHEMA_SUP 2
45 #define SCHEMA_STRUCTURAL 3
46 #define SCHEMA_ABSTRACT 4
47 #define SCHEMA_AUXILIARY 5
48 #define SCHEMA_MUST 6
49 #define SCHEMA_MAY 7
50 #define SCHEMA_SINGLE_VALUE 8
51 #define SCHEMA_EQUALITY 9
52 #define SCHEMA_ORDERING 10
53 #define SCHEMA_SUBSTR 11
54 #define SCHEMA_SYNTAX 12
55 #define SCHEMA_DESC 13
57 struct schema_conv {
58 int count;
59 int failures;
62 struct schema_token {
63 int type;
64 char *value;
67 struct ldb_context *ldb_ctx;
68 struct ldb_dn *basedn;
70 static int check_braces(const char *string)
72 int b;
73 char *c;
75 b = 0;
76 if ((c = strchr(string, '(')) == NULL) {
77 return -1;
79 b++;
80 c++;
81 while (b) {
82 c = strpbrk(c, "()");
83 if (c == NULL) return 1;
84 if (*c == '(') b++;
85 if (*c == ')') b--;
86 c++;
88 return 0;
91 static char *skip_spaces(char *string) {
92 return (string + strspn(string, " \t\n"));
95 static int add_multi_string(struct ldb_message *msg, const char *attr, char *values)
97 char *c;
98 char *s;
99 int n;
101 c = skip_spaces(values);
102 while (*c) {
103 n = strcspn(c, " \t$");
104 s = talloc_strndup(msg, c, n);
105 if (ldb_msg_add_string(msg, attr, s) != 0) {
106 return -1;
108 c += n;
109 c += strspn(c, " \t$");
112 return 0;
115 #define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0)
116 #define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0)
118 static char *get_def_value(TALLOC_CTX *ctx, char **string)
120 char *c = *string;
121 char *value;
122 int n;
124 if (*c == '\'') {
125 c++;
126 n = strcspn(c, "\'");
127 value = talloc_strndup(ctx, c, n);
128 c += n;
129 c++; /* skip closing \' */
130 } else {
131 n = strcspn(c, " \t\n");
132 value = talloc_strndup(ctx, c, n);
133 c += n;
135 *string = c;
137 return value;
140 static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string)
142 char *c = skip_spaces(*string);
143 char *type;
144 struct schema_token *token;
145 int n;
147 token = talloc(ctx, struct schema_token);
149 n = strcspn(c, " \t\n");
150 type = talloc_strndup(token, c, n);
151 c += n;
152 c = skip_spaces(c);
154 if (strcasecmp("NAME", type) == 0) {
155 talloc_free(type);
156 token->type = SCHEMA_NAME;
157 /* we do not support aliases so we get only the first name given and skip others */
158 if (*c == '(') {
159 char *s = strchr(c, ')');
160 if (s == NULL) return NULL;
161 s = skip_spaces(s);
162 *string = s;
164 c++;
165 c = skip_spaces(c);
168 token->value = get_def_value(ctx, &c);
170 if (*string < c) { /* single name */
171 c = skip_spaces(c);
172 *string = c;
174 return token;
176 if (strcasecmp("SUP", type) == 0) {
177 talloc_free(type);
178 token->type = SCHEMA_SUP;
180 if (*c == '(') {
181 c++;
182 n = strcspn(c, ")");
183 token->value = talloc_strndup(ctx, c, n);
184 c += n;
185 c++;
186 } else {
187 token->value = get_def_value(ctx, &c);
190 c = skip_spaces(c);
191 *string = c;
192 return token;
195 if (strcasecmp("STRUCTURAL", type) == 0) {
196 talloc_free(type);
197 token->type = SCHEMA_STRUCTURAL;
198 *string = c;
199 return token;
202 if (strcasecmp("ABSTRACT", type) == 0) {
203 talloc_free(type);
204 token->type = SCHEMA_ABSTRACT;
205 *string = c;
206 return token;
209 if (strcasecmp("AUXILIARY", type) == 0) {
210 talloc_free(type);
211 token->type = SCHEMA_AUXILIARY;
212 *string = c;
213 return token;
216 if (strcasecmp("MUST", type) == 0) {
217 talloc_free(type);
218 token->type = SCHEMA_MUST;
220 if (*c == '(') {
221 c++;
222 n = strcspn(c, ")");
223 token->value = talloc_strndup(ctx, c, n);
224 c += n;
225 c++;
226 } else {
227 token->value = get_def_value(ctx, &c);
230 c = skip_spaces(c);
231 *string = c;
232 return token;
235 if (strcasecmp("MAY", type) == 0) {
236 talloc_free(type);
237 token->type = SCHEMA_MAY;
239 if (*c == '(') {
240 c++;
241 n = strcspn(c, ")");
242 token->value = talloc_strndup(ctx, c, n);
243 c += n;
244 c++;
245 } else {
246 token->value = get_def_value(ctx, &c);
249 c = skip_spaces(c);
250 *string = c;
251 return token;
254 if (strcasecmp("SINGLE-VALUE", type) == 0) {
255 talloc_free(type);
256 token->type = SCHEMA_SINGLE_VALUE;
257 *string = c;
258 return token;
261 if (strcasecmp("EQUALITY", type) == 0) {
262 talloc_free(type);
263 token->type = SCHEMA_EQUALITY;
265 token->value = get_def_value(ctx, &c);
267 c = skip_spaces(c);
268 *string = c;
269 return token;
272 if (strcasecmp("ORDERING", type) == 0) {
273 talloc_free(type);
274 token->type = SCHEMA_ORDERING;
276 token->value = get_def_value(ctx, &c);
278 c = skip_spaces(c);
279 *string = c;
280 return token;
283 if (strcasecmp("SUBSTR", type) == 0) {
284 talloc_free(type);
285 token->type = SCHEMA_SUBSTR;
287 token->value = get_def_value(ctx, &c);
289 c = skip_spaces(c);
290 *string = c;
291 return token;
294 if (strcasecmp("SYNTAX", type) == 0) {
295 talloc_free(type);
296 token->type = SCHEMA_SYNTAX;
298 token->value = get_def_value(ctx, &c);
300 c = skip_spaces(c);
301 *string = c;
302 return token;
305 if (strcasecmp("DESC", type) == 0) {
306 talloc_free(type);
307 token->type = SCHEMA_DESC;
309 token->value = get_def_value(ctx, &c);
311 c = skip_spaces(c);
312 *string = c;
313 return token;
316 token->type = SCHEMA_UNKNOWN;
317 token->value = type;
318 if (*c == ')') {
319 *string = c;
320 return token;
322 if (*c == '\'') {
323 c = strchr(++c, '\'');
324 c++;
325 } else {
326 c += strcspn(c, " \t\n");
328 c = skip_spaces(c);
329 *string = c;
331 return token;
334 static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry)
336 TALLOC_CTX *ctx;
337 struct ldb_message *msg;
338 struct schema_token *token;
339 char *c, *s;
340 int n;
342 SHA256_CTX sha256_context;
343 uint8_t digest[SHA256_DIGEST_LENGTH];
345 struct GUID guid;
347 bool isAttribute = false;
348 bool single_valued = false;
350 ctx = talloc_new(mem_ctx);
351 msg = ldb_msg_new(ctx);
353 ldb_msg_add_string(msg, "objectClass", "top");
355 c = talloc_strdup(ctx, entry);
356 if (!c) return NULL;
358 c = skip_spaces(c);
360 switch (*c) {
361 case 'a':
362 if (strncmp(c, "attributetype", 13) == 0) {
363 c += 13;
364 MSG_ADD_STRING("objectClass", "attributeSchema");
365 isAttribute = true;
366 break;
368 goto failed;
369 case 'o':
370 if (strncmp(c, "objectclass", 11) == 0) {
371 c += 11;
372 MSG_ADD_STRING("objectClass", "classSchema");
373 break;
375 goto failed;
376 default:
377 goto failed;
380 c = strchr(c, '(');
381 if (c == NULL) goto failed;
382 c++;
384 c = skip_spaces(c);
386 /* get attributeID */
387 n = strcspn(c, " \t");
388 s = talloc_strndup(msg, c, n);
389 if (isAttribute) {
390 MSG_ADD_STRING("attributeID", s);
391 } else {
392 MSG_ADD_STRING("governsID", s);
395 samba_SHA256_Init(&sha256_context);
396 samba_SHA256_Update(&sha256_context, (uint8_t*)s, strlen(s));
397 samba_SHA256_Final(digest, &sha256_context);
399 memcpy(&guid, digest, sizeof(struct GUID));
401 if (dsdb_msg_add_guid(msg, &guid, "schemaIdGuid") != 0) {
402 goto failed;
405 c += n;
406 c = skip_spaces(c);
408 while (*c != ')') {
409 token = get_next_schema_token(msg, &c);
410 if (!token) goto failed;
412 switch (token->type) {
413 case SCHEMA_NAME:
414 MSG_ADD_STRING("cn", token->value);
415 MSG_ADD_STRING("name", token->value);
416 MSG_ADD_STRING("lDAPDisplayName", token->value);
417 msg->dn = ldb_dn_copy(msg, basedn);
418 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Schema,CN=Configuration", token->value);
419 break;
421 case SCHEMA_SUP:
422 MSG_ADD_M_STRING("subClassOf", token->value);
423 break;
425 case SCHEMA_STRUCTURAL:
426 MSG_ADD_STRING("objectClassCategory", "1");
427 break;
429 case SCHEMA_ABSTRACT:
430 MSG_ADD_STRING("objectClassCategory", "2");
431 break;
433 case SCHEMA_AUXILIARY:
434 MSG_ADD_STRING("objectClassCategory", "3");
435 break;
437 case SCHEMA_MUST:
438 MSG_ADD_M_STRING("mustContain", token->value);
439 break;
441 case SCHEMA_MAY:
442 MSG_ADD_M_STRING("mayContain", token->value);
443 break;
445 case SCHEMA_SINGLE_VALUE:
446 single_valued = true;
447 break;
449 case SCHEMA_EQUALITY:
450 /* TODO */
451 break;
453 case SCHEMA_ORDERING:
454 /* TODO */
455 break;
457 case SCHEMA_SUBSTR:
458 /* TODO */
459 break;
461 case SCHEMA_SYNTAX:
463 char *syntax_oid;
464 const struct dsdb_syntax *map;
465 char *oMSyntax;
467 n = strcspn(token->value, "{");
468 syntax_oid = talloc_strndup(ctx, token->value, n);
470 map = find_syntax_map_by_standard_oid(syntax_oid);
471 if (!map) {
472 break;
475 MSG_ADD_STRING("attributeSyntax", map->attributeSyntax_oid);
477 oMSyntax = talloc_asprintf(msg, "%d", map->oMSyntax);
478 MSG_ADD_STRING("oMSyntax", oMSyntax);
480 break;
482 case SCHEMA_DESC:
483 MSG_ADD_STRING("description", token->value);
484 break;
486 default:
487 fprintf(stderr, "Unknown Definition: %s\n", token->value);
491 if (isAttribute) {
492 MSG_ADD_STRING("isSingleValued", single_valued ? "TRUE" : "FALSE");
493 } else {
494 MSG_ADD_STRING("defaultObjectCategory", ldb_dn_get_linearized(msg->dn));
497 talloc_steal(mem_ctx, msg);
498 talloc_free(ctx);
499 return msg;
501 failed:
502 talloc_free(ctx);
503 return NULL;
506 static struct schema_conv process_file(FILE *in, FILE *out)
508 TALLOC_CTX *ctx;
509 struct schema_conv ret;
510 char *entry;
511 int c, t, line;
512 struct ldb_ldif ldif;
514 ldif.changetype = LDB_CHANGETYPE_NONE;
516 ctx = talloc_new(NULL);
518 ret.count = 0;
519 ret.failures = 0;
520 line = 0;
522 while ((c = fgetc(in)) != EOF) {
523 line++;
524 /* fprintf(stderr, "Parsing line %d\n", line); */
525 if (c == '#') {
526 do {
527 c = fgetc(in);
528 } while (c != EOF && c != '\n');
529 continue;
531 if (c == '\n') {
532 continue;
535 t = 0;
536 entry = talloc_array(ctx, char, 1024);
537 if (entry == NULL) exit(-1);
539 do {
540 if (c == '\n') {
541 entry[t] = '\0';
542 if (check_braces(entry) == 0) {
543 ret.count++;
544 ldif.msg = process_entry(ctx, entry);
545 if (ldif.msg == NULL) {
546 ret.failures++;
547 fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
548 break;
550 ldb_ldif_write_file(ldb_ctx, out, &ldif);
551 break;
553 line++;
554 } else {
555 entry[t] = c;
556 t++;
558 if ((t % 1023) == 0) {
559 entry = talloc_realloc(ctx, entry, char, t + 1024);
560 if (entry == NULL) exit(-1);
562 } while ((c = fgetc(in)) != EOF);
564 if (c != '\n') {
565 entry[t] = '\0';
566 if (check_braces(entry) == 0) {
567 ret.count++;
568 ldif.msg = process_entry(ctx, entry);
569 if (ldif.msg == NULL) {
570 ret.failures++;
571 fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
572 break;
574 ldb_ldif_write_file(ldb_ctx, out, &ldif);
575 } else {
576 fprintf(stderr, "malformed entry on line %d\n", line);
577 ret.failures++;
581 if (c == EOF) break;
584 return ret;
587 static struct options {
588 const char *basedn;
589 const char *input;
590 const char *output;
591 } options;
593 static struct poptOption popt_options[] = {
594 POPT_AUTOHELP
595 { "basedn", 'b', POPT_ARG_STRING, &options.basedn, 0, "base DN", "DN" },
596 { "input", 'I', POPT_ARG_STRING, &options.input, 0,
597 "inputfile of OpenLDAP style schema otherwise STDIN", "inputfile"},
598 { "output", 'O', POPT_ARG_STRING, &options.output, 0,
599 "outputfile otherwise STDOUT", "outputfile"},
600 POPT_COMMON_VERSION
601 { NULL }
605 static void usage(void)
607 poptContext pc;
608 printf("Usage: oLschema2ldif <options>\n");
609 printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
610 printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
611 pc = poptGetContext("oLschema2ldif", 0, NULL, popt_options,
612 POPT_CONTEXT_KEEP_FIRST);
613 poptPrintHelp(pc, stdout, 0);
614 exit(1);
618 int main(int argc, const char **argv)
620 TALLOC_CTX *ctx;
621 struct schema_conv ret;
622 FILE *in = stdin;
623 FILE *out = stdout;
624 poptContext pc;
625 int opt;
627 ctx = talloc_new(NULL);
628 ldb_ctx = ldb_init(ctx, NULL);
630 setenv("LDB_URL", "NONE", 1);
632 pc = poptGetContext(argv[0], argc, argv, popt_options,
633 POPT_CONTEXT_KEEP_FIRST);
635 while((opt = poptGetNextOpt(pc)) != -1) {
636 fprintf(stderr, "Invalid option %s: %s\n",
637 poptBadOption(pc, 0), poptStrerror(opt));
638 usage();
641 if (options.basedn == NULL) {
642 printf("Base DN not specified\n");
643 usage();
644 exit(1);
645 } else {
646 basedn = ldb_dn_new(ctx, ldb_ctx, options.basedn);
647 if ( ! ldb_dn_validate(basedn)) {
648 printf("Malformed Base DN\n");
649 usage();
650 exit(1);
654 if (options.input) {
655 in = fopen(options.input, "r");
656 if (!in) {
657 perror(options.input);
658 usage();
659 exit(1);
662 if (options.output) {
663 out = fopen(options.output, "w");
664 if (!out) {
665 perror(options.output);
666 usage();
667 exit(1);
671 ret = process_file(in, out);
673 fclose(in);
674 fclose(out);
676 printf("Converted %d records with %d failures\n", ret.count, ret.failures);
678 return 0;