s3-build: fix the build of vfs_notify_fam.
[samba.git] / source3 / 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 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/include/includes.h"
36 #include "ldb/tools/cmdline.h"
37 #include "ldb/tools/convert.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_string_compose(msg, basedn,
391                                                         "CN=%s,CN=Schema,CN=Configuration",
392                                                         token->value);
393                         break;
394
395                 case SCHEMA_SUP:
396                         MSG_ADD_M_STRING("subClassOf", token->value);
397                         break;
398
399                 case SCHEMA_STRUCTURAL:
400                         MSG_ADD_STRING("objectClassCategory", "1");
401                         break;
402
403                 case SCHEMA_ABSTRACT:
404                         MSG_ADD_STRING("objectClassCategory", "2");
405                         break;
406
407                 case SCHEMA_AUXILIARY:
408                         MSG_ADD_STRING("objectClassCategory", "3");
409                         break;
410
411                 case SCHEMA_MUST:
412                         MSG_ADD_M_STRING("mustContain", token->value);
413                         break;
414
415                 case SCHEMA_MAY:
416                         MSG_ADD_M_STRING("mayContain", token->value);
417                         break;
418
419                 case SCHEMA_SINGLE_VALUE:
420                         MSG_ADD_STRING("isSingleValued", "TRUE");
421                         break;
422
423                 case SCHEMA_EQUALITY:
424                         /* TODO */
425                         break;
426
427                 case SCHEMA_ORDERING:
428                         /* TODO */
429                         break;
430
431                 case SCHEMA_SUBSTR:
432                         /* TODO */
433                         break;
434
435                 case SCHEMA_SYNTAX:
436                 {
437                         const struct syntax_map *map = 
438                                 find_syntax_map_by_standard_oid(token->value);
439                         if (!map) {
440                                 break;
441                         }
442                         MSG_ADD_STRING("attributeSyntax", map->AD_OID);
443                         break;
444                 }
445                 case SCHEMA_DESC:
446                         MSG_ADD_STRING("description", token->value);
447                         break;
448
449                 default:
450                         fprintf(stderr, "Unknown Definition: %s\n", token->value);
451                 }
452         }
453
454         talloc_steal(mem_ctx, msg);
455         talloc_free(ctx);
456         return msg;
457
458 failed:
459         talloc_free(ctx);
460         return NULL;
461 }
462
463 static struct schema_conv process_file(FILE *in, FILE *out)
464 {
465         TALLOC_CTX *ctx;
466         struct schema_conv ret;
467         char *entry;
468         int c, t, line;
469         struct ldb_ldif ldif;
470
471         ldif.changetype = LDB_CHANGETYPE_NONE;
472
473         ctx = talloc_new(NULL);
474
475         ret.count = 0;
476         ret.failures = 0;
477         line = 0;
478
479         while ((c = fgetc(in)) != EOF) {
480                 line++;
481                 /* fprintf(stderr, "Parsing line %d\n", line); */
482                 if (c == '#') {
483                         do {
484                                 c = fgetc(in);
485                         } while (c != EOF && c != '\n');
486                         continue;
487                 }
488                 if (c == '\n') {
489                         continue;
490                 }
491
492                 t = 0;
493                 entry = talloc_array(ctx, char, 1024);
494                 if (entry == NULL) exit(-1);
495
496                 do { 
497                         if (c == '\n') {
498                                 entry[t] = '\0';        
499                                 if (check_braces(entry) == 0) {
500                                         ret.count++;
501                                         ldif.msg = process_entry(ctx, entry);
502                                         if (ldif.msg == NULL) {
503                                                 ret.failures++;
504                                                 fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
505                                                 break;
506                                         }
507                                         ldb_ldif_write_file(ldb_ctx, out, &ldif);
508                                         break;
509                                 }
510                                 line++;
511                         } else {
512                                 entry[t] = c;
513                                 t++;
514                         }
515                         if ((t % 1023) == 0) {
516                                 entry = talloc_realloc(ctx, entry, char, t + 1024);
517                                 if (entry == NULL) exit(-1);
518                         }
519                 } while ((c = fgetc(in)) != EOF); 
520
521                 if (c != '\n') {
522                         entry[t] = '\0';
523                         if (check_braces(entry) == 0) {
524                                 ret.count++;
525                                 ldif.msg = process_entry(ctx, entry);
526                                 if (ldif.msg == NULL) {
527                                         ret.failures++;
528                                         fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
529                                         break;
530                                 }
531                                 ldb_ldif_write_file(ldb_ctx, out, &ldif);
532                         } else {
533                                 fprintf(stderr, "malformed entry on line %d\n", line);
534                                 ret.failures++;
535                         }
536                 }
537         
538                 if (c == EOF) break;
539         }
540
541         return ret;
542 }
543
544 static void usage(void)
545 {
546         printf("Usage: oLschema2ldif -H NONE <options>\n");
547         printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
548         printf("Options:\n");
549         printf("  -I inputfile     inputfile of OpenLDAP style schema otherwise STDIN\n");
550         printf("  -O outputfile    outputfile otherwise STDOUT\n");
551         printf("  -o options       pass options like modules to activate\n");
552         printf("              e.g: -o modules:timestamps\n");
553         printf("\n");
554         printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
555         exit(1);
556 }
557
558  int main(int argc, const char **argv)
559 {
560         TALLOC_CTX *ctx;
561         struct schema_conv ret;
562         struct ldb_cmdline *options;
563         FILE *in = stdin;
564         FILE *out = stdout;
565         ldb_global_init();
566
567         ctx = talloc_new(NULL);
568         ldb_ctx = ldb_init(ctx);
569
570         setenv("LDB_URL", "NONE", 1);
571         options = ldb_cmdline_process(ldb_ctx, argc, argv, usage);
572
573         if (options->basedn == NULL) {
574                 perror("Base DN not specified");
575                 exit(1);
576         } else {
577                 basedn = ldb_dn_explode(ctx, options->basedn);
578                 if (basedn == NULL) {
579                         perror("Malformed Base DN");
580                         exit(1);
581                 }
582         }
583
584         if (options->input) {
585                 in = fopen(options->input, "r");
586                 if (!in) {
587                         perror(options->input);
588                         exit(1);
589                 }
590         }
591         if (options->output) {
592                 out = fopen(options->output, "w");
593                 if (!out) {
594                         perror(options->output);
595                         exit(1);
596                 }
597         }
598
599         ret = process_file(in, out);
600
601         fclose(in);
602         fclose(out);
603
604         printf("Converted %d records with %d failures\n", ret.count, ret.failures);
605
606         return 0;
607 }