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