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