Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into 4-0-abartlet
[tprouty/samba.git] / source4 / utils / oLschema2ldif.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005
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: oLschema2ldif
28  *
29  *  Description: utility to convert an OpenLDAP schema into AD LDIF
30  *
31  *  Author: Simo Sorce
32  */
33
34 #include "includes.h"
35 #include "ldb_includes.h"
36 #include "tools/cmdline.h"
37 #include "dsdb/samdb/samdb.h"
38
39 #define SCHEMA_UNKNOWN 0
40 #define SCHEMA_NAME 1
41 #define SCHEMA_SUP 2
42 #define SCHEMA_STRUCTURAL 3
43 #define SCHEMA_ABSTRACT 4
44 #define SCHEMA_AUXILIARY 5
45 #define SCHEMA_MUST 6
46 #define SCHEMA_MAY 7
47 #define SCHEMA_SINGLE_VALUE 8
48 #define SCHEMA_EQUALITY 9
49 #define SCHEMA_ORDERING 10
50 #define SCHEMA_SUBSTR 11
51 #define SCHEMA_SYNTAX 12
52 #define SCHEMA_DESC 13
53
54 struct schema_conv {
55         int count;
56         int failures;
57 };
58
59 struct schema_token {
60         int type;
61         char *value;
62 };
63
64 struct ldb_context *ldb_ctx;
65 struct ldb_dn *basedn;
66
67 static int check_braces(const char *string)
68 {
69         int b;
70         char *c;
71
72         b = 0;
73         if ((c = strchr(string, '(')) == NULL) {
74                 return -1;
75         }
76         b++;
77         c++;
78         while (b) {
79                 c = strpbrk(c, "()");
80                 if (c == NULL) return 1;
81                 if (*c == '(') b++;
82                 if (*c == ')') b--;
83                 c++;
84         }
85         return 0;
86 }
87
88 static char *skip_spaces(char *string) {
89         return (string + strspn(string, " \t\n"));
90 }
91
92 static int add_multi_string(struct ldb_message *msg, const char *attr, char *values)
93 {
94         char *c;
95         char *s;
96         int n;
97
98         c = skip_spaces(values);
99         while (*c) {
100                 n = strcspn(c, " \t$");
101                 s = talloc_strndup(msg, c, n);
102                 if (ldb_msg_add_string(msg, attr, s) != 0) {
103                         return -1;
104                 }
105                 c += n;
106                 c += strspn(c, " \t$");
107         }
108
109         return 0;
110 }
111
112 #define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0)
113 #define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0)
114
115 static char *get_def_value(TALLOC_CTX *ctx, char **string)
116 {
117         char *c = *string;
118         char *value;
119         int n;
120
121         if (*c == '\'') {
122                 c++;
123                 n = strcspn(c, "\'");
124                 value = talloc_strndup(ctx, c, n);
125                 c += n;
126                 c++; /* skip closing \' */
127         } else {
128                 n = strcspn(c, " \t\n");
129                 value = talloc_strndup(ctx, c, n);
130                 c += n;
131         }
132         *string = c;
133
134         return value;
135 }
136
137 static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string)
138 {
139         char *c = skip_spaces(*string);
140         char *type;
141         struct schema_token *token;
142         int n;
143
144         token = talloc(ctx, struct schema_token);
145
146         n = strcspn(c, " \t\n");
147         type = talloc_strndup(token, c, n);
148         c += n;
149         c = skip_spaces(c);
150
151         if (strcasecmp("NAME", type) == 0) {
152                 talloc_free(type);
153                 token->type = SCHEMA_NAME;
154                 /* we do not support aliases so we get only the first name given and skip others */
155                 if (*c == '(') {
156                         char *s = strchr(c, ')');
157                         if (s == NULL) return NULL;
158                         s = skip_spaces(s);
159                         *string = s;
160
161                         c++;
162                         c = skip_spaces(c);
163                 }
164
165                 token->value = get_def_value(ctx, &c);
166
167                 if (*string < c) { /* single name */
168                         c = skip_spaces(c);
169                         *string = c;
170                 }
171                 return token;
172         }
173         if (strcasecmp("SUP", type) == 0) {
174                 talloc_free(type);
175                 token->type = SCHEMA_SUP;
176
177                 if (*c == '(') {
178                         c++;
179                         n = strcspn(c, ")");
180                         token->value = talloc_strndup(ctx, c, n);
181                         c += n;
182                         c++;
183                 } else {
184                         token->value = get_def_value(ctx, &c);
185                 }
186
187                 c = skip_spaces(c);
188                 *string = c;
189                 return token;
190         }
191
192         if (strcasecmp("STRUCTURAL", type) == 0) {
193                 talloc_free(type);
194                 token->type = SCHEMA_STRUCTURAL;
195                 *string = c;
196                 return token;
197         }
198
199         if (strcasecmp("ABSTRACT", type) == 0) {
200                 talloc_free(type);
201                 token->type = SCHEMA_ABSTRACT;
202                 *string = c;
203                 return token;
204         }
205
206         if (strcasecmp("AUXILIARY", type) == 0) {
207                 talloc_free(type);
208                 token->type = SCHEMA_AUXILIARY;
209                 *string = c;
210                 return token;
211         }
212
213         if (strcasecmp("MUST", type) == 0) {
214                 talloc_free(type);
215                 token->type = SCHEMA_MUST;
216
217                 if (*c == '(') {
218                         c++;
219                         n = strcspn(c, ")");
220                         token->value = talloc_strndup(ctx, c, n);
221                         c += n;
222                         c++;
223                 } else {
224                         token->value = get_def_value(ctx, &c);
225                 }
226
227                 c = skip_spaces(c);
228                 *string = c;
229                 return token;
230         }
231
232         if (strcasecmp("MAY", type) == 0) {
233                 talloc_free(type);
234                 token->type = SCHEMA_MAY;
235
236                 if (*c == '(') {
237                         c++;
238                         n = strcspn(c, ")");
239                         token->value = talloc_strndup(ctx, c, n);
240                         c += n;
241                         c++;
242                 } else {
243                         token->value = get_def_value(ctx, &c);
244                 }
245
246                 c = skip_spaces(c);
247                 *string = c;
248                 return token;
249         }
250
251         if (strcasecmp("SINGLE-VALUE", type) == 0) {
252                 talloc_free(type);
253                 token->type = SCHEMA_SINGLE_VALUE;
254                 *string = c;
255                 return token;
256         }
257
258         if (strcasecmp("EQUALITY", type) == 0) {
259                 talloc_free(type);
260                 token->type = SCHEMA_EQUALITY;
261
262                 token->value = get_def_value(ctx, &c);
263
264                 c = skip_spaces(c);
265                 *string = c;
266                 return token;
267         }
268
269         if (strcasecmp("ORDERING", type) == 0) {
270                 talloc_free(type);
271                 token->type = SCHEMA_ORDERING;
272
273                 token->value = get_def_value(ctx, &c);
274
275                 c = skip_spaces(c);
276                 *string = c;
277                 return token;
278         }
279
280         if (strcasecmp("SUBSTR", type) == 0) {
281                 talloc_free(type);
282                 token->type = SCHEMA_SUBSTR;
283
284                 token->value = get_def_value(ctx, &c);
285
286                 c = skip_spaces(c);
287                 *string = c;
288                 return token;
289         }
290
291         if (strcasecmp("SYNTAX", type) == 0) {
292                 talloc_free(type);
293                 token->type = SCHEMA_SYNTAX;
294
295                 token->value = get_def_value(ctx, &c);
296
297                 c = skip_spaces(c);
298                 *string = c;
299                 return token;
300         }
301
302         if (strcasecmp("DESC", type) == 0) {
303                 talloc_free(type);
304                 token->type = SCHEMA_DESC;
305
306                 token->value = get_def_value(ctx, &c);
307
308                 c = skip_spaces(c);
309                 *string = c;
310                 return token;
311         }
312
313         token->type = SCHEMA_UNKNOWN;
314         token->value = type;
315         if (*c == ')') {
316                 *string = c;
317                 return token;
318         }
319         if (*c == '\'') {
320                 c = strchr(++c, '\'');
321                 c++;
322         } else {
323                 c += strcspn(c, " \t\n");
324         }
325         c = skip_spaces(c);
326         *string = c;
327
328         return token;
329 }
330
331 static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry)
332 {
333         TALLOC_CTX *ctx;
334         struct ldb_message *msg;
335         struct schema_token *token;
336         char *c, *s;
337         int n;
338
339         ctx = talloc_new(mem_ctx);
340         msg = ldb_msg_new(ctx);
341
342         ldb_msg_add_string(msg, "objectClass", "top");
343
344         c = talloc_strdup(ctx, entry);
345         if (!c) return NULL;
346
347         c = skip_spaces(c);
348
349         switch (*c) {
350         case 'a':
351                 if (strncmp(c, "attributetype", 13) == 0) {
352                         c += 13;
353                         MSG_ADD_STRING("objectClass", "attributeSchema");
354                         break;
355                 }
356                 goto failed;
357         case 'o':
358                 if (strncmp(c, "objectclass", 11) == 0) {
359                         c += 11;
360                         MSG_ADD_STRING("objectClass", "classSchema");
361                         break;
362                 }
363                 goto failed;
364         default:
365                 goto failed;
366         }
367
368         c = strchr(c, '(');
369         if (c == NULL) goto failed;
370         c++;
371
372         c = skip_spaces(c);
373
374         /* get attributeID */
375         n = strcspn(c, " \t");
376         s = talloc_strndup(msg, c, n);
377         MSG_ADD_STRING("attributeID", s);
378         c += n;
379         c = skip_spaces(c);     
380
381         while (*c != ')') {
382                 token = get_next_schema_token(msg, &c);
383                 if (!token) goto failed;
384
385                 switch (token->type) {
386                 case SCHEMA_NAME:
387                         MSG_ADD_STRING("cn", token->value);
388                         MSG_ADD_STRING("name", token->value);
389                         MSG_ADD_STRING("lDAPDisplayName", token->value);
390                         msg->dn = ldb_dn_copy(msg, basedn);
391                         ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Schema,CN=Configuration", token->value);
392                         break;
393
394                 case SCHEMA_SUP:
395                         MSG_ADD_M_STRING("subClassOf", token->value);
396                         break;
397
398                 case SCHEMA_STRUCTURAL:
399                         MSG_ADD_STRING("objectClassCategory", "1");
400                         break;
401
402                 case SCHEMA_ABSTRACT:
403                         MSG_ADD_STRING("objectClassCategory", "2");
404                         break;
405
406                 case SCHEMA_AUXILIARY:
407                         MSG_ADD_STRING("objectClassCategory", "3");
408                         break;
409
410                 case SCHEMA_MUST:
411                         MSG_ADD_M_STRING("mustContain", token->value);
412                         break;
413
414                 case SCHEMA_MAY:
415                         MSG_ADD_M_STRING("mayContain", token->value);
416                         break;
417
418                 case SCHEMA_SINGLE_VALUE:
419                         MSG_ADD_STRING("isSingleValued", "TRUE");
420                         break;
421
422                 case SCHEMA_EQUALITY:
423                         /* TODO */
424                         break;
425
426                 case SCHEMA_ORDERING:
427                         /* TODO */
428                         break;
429
430                 case SCHEMA_SUBSTR:
431                         /* TODO */
432                         break;
433
434                 case SCHEMA_SYNTAX:
435                 {
436                         const struct dsdb_syntax *map = 
437                                 find_syntax_map_by_standard_oid(token->value);
438                         if (!map) {
439                                 break;
440                         }
441                         MSG_ADD_STRING("attributeSyntax", map->attributeSyntax_oid);
442                         break;
443                 }
444                 case SCHEMA_DESC:
445                         MSG_ADD_STRING("description", token->value);
446                         break;
447
448                 default:
449                         fprintf(stderr, "Unknown Definition: %s\n", token->value);
450                 }
451         }
452
453         talloc_steal(mem_ctx, msg);
454         talloc_free(ctx);
455         return msg;
456
457 failed:
458         talloc_free(ctx);
459         return NULL;
460 }
461
462 static struct schema_conv process_file(FILE *in, FILE *out)
463 {
464         TALLOC_CTX *ctx;
465         struct schema_conv ret;
466         char *entry;
467         int c, t, line;
468         struct ldb_ldif ldif;
469
470         ldif.changetype = LDB_CHANGETYPE_NONE;
471
472         ctx = talloc_new(NULL);
473
474         ret.count = 0;
475         ret.failures = 0;
476         line = 0;
477
478         while ((c = fgetc(in)) != EOF) {
479                 line++;
480                 /* fprintf(stderr, "Parsing line %d\n", line); */
481                 if (c == '#') {
482                         do {
483                                 c = fgetc(in);
484                         } while (c != EOF && c != '\n');
485                         continue;
486                 }
487                 if (c == '\n') {
488                         continue;
489                 }
490
491                 t = 0;
492                 entry = talloc_array(ctx, char, 1024);
493                 if (entry == NULL) exit(-1);
494
495                 do { 
496                         if (c == '\n') {
497                                 entry[t] = '\0';        
498                                 if (check_braces(entry) == 0) {
499                                         ret.count++;
500                                         ldif.msg = process_entry(ctx, entry);
501                                         if (ldif.msg == NULL) {
502                                                 ret.failures++;
503                                                 fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
504                                                 break;
505                                         }
506                                         ldb_ldif_write_file(ldb_ctx, out, &ldif);
507                                         break;
508                                 }
509                                 line++;
510                         } else {
511                                 entry[t] = c;
512                                 t++;
513                         }
514                         if ((t % 1023) == 0) {
515                                 entry = talloc_realloc(ctx, entry, char, t + 1024);
516                                 if (entry == NULL) exit(-1);
517                         }
518                 } while ((c = fgetc(in)) != EOF); 
519
520                 if (c != '\n') {
521                         entry[t] = '\0';
522                         if (check_braces(entry) == 0) {
523                                 ret.count++;
524                                 ldif.msg = process_entry(ctx, entry);
525                                 if (ldif.msg == NULL) {
526                                         ret.failures++;
527                                         fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
528                                         break;
529                                 }
530                                 ldb_ldif_write_file(ldb_ctx, out, &ldif);
531                         } else {
532                                 fprintf(stderr, "malformed entry on line %d\n", line);
533                                 ret.failures++;
534                         }
535                 }
536         
537                 if (c == EOF) break;
538         }
539
540         return ret;
541 }
542
543 static void usage(void)
544 {
545         printf("Usage: oLschema2ldif -H NONE <options>\n");
546         printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
547         printf("Options:\n");
548         printf("  -I inputfile     inputfile of OpenLDAP style schema otherwise STDIN\n");
549         printf("  -O outputfile    outputfile otherwise STDOUT\n");
550         printf("  -o options       pass options like modules to activate\n");
551         printf("              e.g: -o modules:timestamps\n");
552         printf("\n");
553         printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
554         exit(1);
555 }
556
557  int main(int argc, const char **argv)
558 {
559         TALLOC_CTX *ctx;
560         struct schema_conv ret;
561         struct ldb_cmdline *options;
562         FILE *in = stdin;
563         FILE *out = stdout;
564         ctx = talloc_new(NULL);
565         ldb_ctx = ldb_init(ctx, NULL);
566
567         setenv("LDB_URL", "NONE", 1);
568         options = ldb_cmdline_process(ldb_ctx, argc, argv, usage);
569
570         if (options->basedn == NULL) {
571                 perror("Base DN not specified");
572                 exit(1);
573         } else {
574                 basedn = ldb_dn_new(ctx, ldb_ctx, options->basedn);
575                 if ( ! ldb_dn_validate(basedn)) {
576                         perror("Malformed Base DN");
577                         exit(1);
578                 }
579         }
580
581         if (options->input) {
582                 in = fopen(options->input, "r");
583                 if (!in) {
584                         perror(options->input);
585                         exit(1);
586                 }
587         }
588         if (options->output) {
589                 out = fopen(options->output, "w");
590                 if (!out) {
591                         perror(options->output);
592                         exit(1);
593                 }
594         }
595
596         ret = process_file(in, out);
597
598         fclose(in);
599         fclose(out);
600
601         printf("Converted %d records with %d failures\n", ret.count, ret.failures);
602
603         return 0;
604 }