s4:dsdb/schema_convert_to_ol.c - quiet enum warning
[obnox/samba/samba-obnox.git] / source4 / dsdb / schema / schema_convert_to_ol.c
1 /* 
2    schema conversion routines
3
4    Copyright (C) Andrew Bartlett 2006-2008
5   
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18    
19 */
20
21 #include "includes.h"
22 #include "ldb.h"
23 #include "dsdb/samdb/samdb.h"
24 #include "system/locale.h"
25
26 #define SEPERATOR "\n  "
27
28 struct attr_map {
29         char *old_attr;
30         char *new_attr;
31 };
32
33 struct oid_map {
34         char *old_oid;
35         char *new_oid;
36 };
37
38 static char *print_schema_recursive(char *append_to_string, struct dsdb_schema *schema, const char *print_class,
39                                     enum dsdb_schema_convert_target target, 
40                                     const char **attrs_skip, const struct attr_map *attr_map, const struct oid_map *oid_map) 
41 {
42         char *out = append_to_string;
43         const struct dsdb_class *objectclass;
44         objectclass = dsdb_class_by_lDAPDisplayName(schema, print_class);
45         if (!objectclass) {
46                 DEBUG(0, ("Cannot find class %s in schema\n", print_class));
47                 return NULL;
48         }
49
50         do {
51                 TALLOC_CTX *mem_ctx = talloc_new(append_to_string);
52                 const char *name = objectclass->lDAPDisplayName;
53                 const char *oid = objectclass->governsID_oid;
54                 const char *subClassOf = objectclass->subClassOf;
55                 int objectClassCategory = objectclass->objectClassCategory;
56                 const char **must;
57                 const char **may;
58                 char *schema_entry = NULL;
59                 struct ldb_val objectclass_name_as_ldb_val = data_blob_string_const(objectclass->lDAPDisplayName);
60                 struct ldb_message_element objectclass_name_as_el = {
61                         .name = "objectClass",
62                         .num_values = 1,
63                         .values = &objectclass_name_as_ldb_val
64                 };
65                 unsigned int j;
66                 unsigned int attr_idx;
67                 
68                 if (!mem_ctx) {
69                         DEBUG(0, ("Failed to create new talloc context\n"));
70                         return NULL;
71                 }
72
73                 /* We have been asked to skip some attributes/objectClasses */
74                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
75                         continue;
76                 }
77
78                 /* We might have been asked to remap this oid, due to a conflict */
79                 for (j=0; oid_map && oid_map[j].old_oid; j++) {
80                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
81                                 oid =  oid_map[j].new_oid;
82                                 break;
83                         }
84                 }
85                 
86                 /* We might have been asked to remap this name, due to a conflict */
87                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
88                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
89                                 name =  attr_map[j].new_attr;
90                                 break;
91                         }
92                 }
93                 
94                 /* We might have been asked to remap this subClassOf, due to a conflict */
95                 for (j=0; subClassOf && attr_map && attr_map[j].old_attr; j++) {
96                         if (strcasecmp(subClassOf, attr_map[j].old_attr) == 0) {
97                                 subClassOf =  attr_map[j].new_attr;
98                                 break;
99                         }
100                 }
101                 
102                 may = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MAY);
103
104                 for (j=0; may && may[j]; j++) {
105                         /* We might have been asked to remap this name, due to a conflict */ 
106                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) { 
107                                 if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) { 
108                                         may[j] =  attr_map[attr_idx].new_attr; 
109                                         break;                          
110                                 }                                       
111                         }                                               
112                 }
113
114                 must = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MUST);
115
116                 for (j=0; must && must[j]; j++) {
117                         /* We might have been asked to remap this name, due to a conflict */ 
118                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) { 
119                                 if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) { 
120                                         must[j] =  attr_map[attr_idx].new_attr; 
121                                         break;                          
122                                 }                                       
123                         }                                               
124                 }
125
126                 schema_entry = schema_class_description(mem_ctx, target, 
127                                                         SEPERATOR,
128                                                         oid, 
129                                                         name,
130                                                         NULL, 
131                                                         subClassOf,
132                                                         objectClassCategory,
133                                                         must,
134                                                         may,
135                                                         NULL);
136                 if (schema_entry == NULL) {
137                         DEBUG(0, ("failed to generate schema description for %s\n", name));
138                         return NULL;
139                 }
140
141                 switch (target) {
142                 case TARGET_OPENLDAP:
143                         out = talloc_asprintf_append(out, "objectclass %s\n\n", schema_entry);
144                         break;
145                 case TARGET_FEDORA_DS:
146                         out = talloc_asprintf_append(out, "objectClasses: %s\n", schema_entry);
147                         break;
148                 default:
149                         DEBUG(0, ("Wrong type of target!\n"));
150                         return NULL;
151                 }
152                 talloc_free(mem_ctx);
153         } while (0);
154
155         
156         for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
157                 if (ldb_attr_cmp(objectclass->subClassOf, print_class) == 0 
158                     && ldb_attr_cmp(objectclass->lDAPDisplayName, print_class) != 0) {
159                         out = print_schema_recursive(out, schema, objectclass->lDAPDisplayName, 
160                                                      target, attrs_skip, attr_map, oid_map);
161                 }
162         }
163         return out;
164 }
165
166 /* Routine to linearise our internal schema into the format that
167    OpenLDAP and Fedora DS use for their backend.  
168
169    The 'mappings' are of a format like:
170
171 #Standard OpenLDAP attributes
172 labeledURI
173 #The memberOf plugin provides this attribute
174 memberOf
175 #These conflict with OpenLDAP builtins
176 attributeTypes:samba4AttributeTypes
177 2.5.21.5:1.3.6.1.4.1.7165.4.255.7
178
179 */
180
181
182 char *dsdb_convert_schema_to_openldap(struct ldb_context *ldb, char *target_str, const char *mappings) 
183 {
184         /* Read list of attributes to skip, OIDs to map */
185         TALLOC_CTX *mem_ctx = talloc_new(ldb);
186         char *line;
187         char *out;
188         const char **attrs_skip = NULL;
189         unsigned int num_skip = 0;
190         struct oid_map *oid_map = NULL;
191         unsigned int num_oid_maps = 0;
192         struct attr_map *attr_map = NULL;
193         unsigned int num_attr_maps = 0;
194         struct dsdb_attribute *attribute;
195         struct dsdb_schema *schema;
196         enum dsdb_schema_convert_target target;
197
198         char *next_line = talloc_strdup(mem_ctx, mappings);
199
200         if (!target_str || strcasecmp(target_str, "openldap") == 0) {
201                 target = TARGET_OPENLDAP;
202         } else if (strcasecmp(target_str, "fedora-ds") == 0) {
203                 target = TARGET_FEDORA_DS;
204         } else {
205                 DEBUG(0, ("Invalid target type for schema conversion %s\n", target_str));
206                 return NULL;
207         }
208
209         /* The mappings are line-seperated, and specify details such as OIDs to skip etc */
210         while (1) {
211                 line = next_line;
212                 next_line = strchr(line, '\n');
213                 if (!next_line) {
214                         break;
215                 }
216                 next_line[0] = '\0';
217                 next_line++;
218
219                 /* Blank Line */
220                 if (line[0] == '\0') {
221                         continue;
222                 }
223                 /* Comment */
224                 if (line[0] == '#') {
225                         continue;
226                 }
227
228                 if (isdigit(line[0])) {
229                         char *p = strchr(line, ':');
230                         if (!p) {
231                                 DEBUG(0, ("schema mapping file line has OID but no OID to map to: %s\n", line));
232                                 return NULL;
233                         }
234                         p[0] = '\0';
235                         p++;
236                         oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
237                         trim_string(line, " ", " ");
238                         oid_map[num_oid_maps].old_oid = talloc_strdup(oid_map, line);
239                         trim_string(p, " ", " ");
240                         oid_map[num_oid_maps].new_oid = p;
241                         num_oid_maps++;
242                         oid_map[num_oid_maps].old_oid = NULL;
243                 } else {
244                         char *p = strchr(line, ':');
245                         if (p) {
246                                 /* remap attribute/objectClass */
247                                 p[0] = '\0';
248                                 p++;
249                                 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
250                                 trim_string(line, " ", " ");
251                                 attr_map[num_attr_maps].old_attr = talloc_strdup(attr_map, line);
252                                 trim_string(p, " ", " ");
253                                 attr_map[num_attr_maps].new_attr = p;
254                                 num_attr_maps++;
255                                 attr_map[num_attr_maps].old_attr = NULL;
256                         } else {
257                                 /* skip attribute/objectClass */
258                                 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
259                                 trim_string(line, " ", " ");
260                                 attrs_skip[num_skip] = talloc_strdup(attrs_skip, line);
261                                 num_skip++;
262                                 attrs_skip[num_skip] = NULL;
263                         }
264                 }
265         }
266
267         schema = dsdb_get_schema(ldb, mem_ctx);
268         if (!schema) {
269                 DEBUG(0, ("No schema on ldb to convert!\n"));
270                 return NULL;
271         }
272
273         switch (target) {
274         case TARGET_OPENLDAP:
275                 out = talloc_strdup(mem_ctx, "");
276                 break;
277         case TARGET_FEDORA_DS:
278                 out = talloc_strdup(mem_ctx, "dn: cn=schema\n");
279                 break;
280         default:
281                 DEBUG(0, ("Wrong type of target!\n"));
282                 return NULL;
283         }
284
285         for (attribute=schema->attributes; attribute; attribute = attribute->next) {
286                 const char *name = attribute->lDAPDisplayName;
287                 const char *oid = attribute->attributeID_oid;
288                 const char *syntax = attribute->attributeSyntax_oid;
289                 const char *equality = NULL, *substring = NULL;
290                 bool single_value = attribute->isSingleValued;
291
292                 char *schema_entry = NULL;
293                 unsigned int j;
294
295                 /* We have been asked to skip some attributes/objectClasses */
296                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
297                         continue;
298                 }
299
300                 /* We might have been asked to remap this oid, due to a conflict */
301                 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
302                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
303                                 oid =  oid_map[j].new_oid;
304                                 break;
305                         }
306                 }
307                 
308                 if (attribute->syntax) {
309                         /* We might have been asked to remap this oid,
310                          * due to a conflict, or lack of
311                          * implementation */
312                         syntax = attribute->syntax->ldap_oid;
313                         /* We might have been asked to remap this oid, due to a conflict */
314                         for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
315                                 if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
316                                         syntax =  oid_map[j].new_oid;
317                                         break;
318                                 }
319                         }
320                         
321                         equality = attribute->syntax->equality;
322                         substring = attribute->syntax->substring;
323                 }
324
325                 /* We might have been asked to remap this name, due to a conflict */
326                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
327                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
328                                 name =  attr_map[j].new_attr;
329                                 break;
330                         }
331                 }
332                 
333                 schema_entry = schema_attribute_description(mem_ctx, 
334                                                             target, 
335                                                             SEPERATOR, 
336                                                             oid, 
337                                                             name, 
338                                                             equality, 
339                                                             substring, 
340                                                             syntax, 
341                                                             single_value, 
342                                                             false,
343                                                             NULL, NULL,
344                                                             NULL, NULL,
345                                                             false, false);
346
347                 if (schema_entry == NULL) {
348                         DEBUG(0, ("failed to generate attribute description for %s\n", name));
349                         return NULL;
350                 }
351
352                 switch (target) {
353                 case TARGET_OPENLDAP:
354                         out = talloc_asprintf_append(out, "attributetype %s\n\n", schema_entry);
355                         break;
356                 case TARGET_FEDORA_DS:
357                         out = talloc_asprintf_append(out, "attributeTypes: %s\n", schema_entry);
358                         break;
359                 default:
360                         DEBUG(0, ("Wrong type of target!\n"));
361                         return NULL;
362                 }
363         }
364
365         out = print_schema_recursive(out, schema, "top", target, attrs_skip, attr_map, oid_map);
366
367         return out;
368 }
369