Merge branch 'master' of ssh://git.samba.org/data/git/samba into wspp-schema
[kai/samba.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                 const char *objectclass_name_as_list[] = {
60                         objectclass->lDAPDisplayName,
61                         NULL
62                 };
63                 int j;
64                 int attr_idx;
65                 
66                 if (!mem_ctx) {
67                         DEBUG(0, ("Failed to create new talloc context\n"));
68                         return NULL;
69                 }
70
71                 /* We have been asked to skip some attributes/objectClasses */
72                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
73                         continue;
74                 }
75
76                 /* We might have been asked to remap this oid, due to a conflict */
77                 for (j=0; oid_map && oid_map[j].old_oid; j++) {
78                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
79                                 oid =  oid_map[j].new_oid;
80                                 break;
81                         }
82                 }
83                 
84                 /* We might have been asked to remap this name, due to a conflict */
85                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
86                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
87                                 name =  attr_map[j].new_attr;
88                                 break;
89                         }
90                 }
91                 
92                 may = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MAY);
93
94                 for (j=0; may && may[j]; j++) {
95                         /* We might have been asked to remap this name, due to a conflict */ 
96                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) { 
97                                 if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) { 
98                                         may[j] =  attr_map[attr_idx].new_attr; 
99                                         break;                          
100                                 }                                       
101                         }                                               
102                 }
103
104                 must = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MUST);
105
106                 for (j=0; must && must[j]; j++) {
107                         /* We might have been asked to remap this name, due to a conflict */ 
108                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) { 
109                                 if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) { 
110                                         must[j] =  attr_map[attr_idx].new_attr; 
111                                         break;                          
112                                 }                                       
113                         }                                               
114                 }
115
116                 schema_entry = schema_class_description(mem_ctx, target, 
117                                                         SEPERATOR,
118                                                         oid, 
119                                                         name,
120                                                         NULL, 
121                                                         subClassOf,
122                                                         objectClassCategory,
123                                                         must,
124                                                         may,
125                                                         NULL);
126                 if (schema_entry == NULL) {
127                         DEBUG(0, ("failed to generate schema description for %s\n", name));
128                         return NULL;
129                 }
130
131                 switch (target) {
132                 case TARGET_OPENLDAP:
133                         out = talloc_asprintf_append(out, "objectclass %s\n\n", schema_entry);
134                         break;
135                 case TARGET_FEDORA_DS:
136                         out = talloc_asprintf_append(out, "objectClasses: %s\n", schema_entry);
137                         break;
138                 }
139                 talloc_free(mem_ctx);
140         } while (0);
141
142         
143         for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
144                 if (ldb_attr_cmp(objectclass->subClassOf, print_class) == 0 
145                     && ldb_attr_cmp(objectclass->lDAPDisplayName, print_class) != 0) {
146                         out = print_schema_recursive(out, schema, objectclass->lDAPDisplayName, 
147                                                      target, attrs_skip, attr_map, oid_map);
148                 }
149         }
150         return out;
151 }
152
153 /* Routine to linearise our internal schema into the format that
154    OpenLDAP and Fedora DS use for their backend.  
155
156    The 'mappings' are of a format like:
157
158 #Standard OpenLDAP attributes
159 labeledURI
160 #The memberOf plugin provides this attribute
161 memberOf
162 #These conflict with OpenLDAP builtins
163 attributeTypes:samba4AttributeTypes
164 2.5.21.5:1.3.6.1.4.1.7165.4.255.7
165
166 */
167
168
169 char *dsdb_convert_schema_to_openldap(struct ldb_context *ldb, char *target_str, const char *mappings) 
170 {
171         /* Read list of attributes to skip, OIDs to map */
172         TALLOC_CTX *mem_ctx = talloc_new(ldb);
173         char *line;
174         char *out;
175         const char **attrs_skip = NULL;
176         int num_skip = 0;
177         struct oid_map *oid_map = NULL;
178         int num_oid_maps = 0;
179         struct attr_map *attr_map = NULL;
180         int num_attr_maps = 0;  
181         struct dsdb_attribute *attribute;
182         struct dsdb_schema *schema;
183         enum dsdb_schema_convert_target target;
184
185         char *next_line = talloc_strdup(mem_ctx, mappings);
186
187         if (!target_str || strcasecmp(target_str, "openldap") == 0) {
188                 target = TARGET_OPENLDAP;
189         } else if (strcasecmp(target_str, "fedora-ds") == 0) {
190                 target = TARGET_FEDORA_DS;
191         } else {
192                 DEBUG(0, ("Invalid target type for schema conversion %s\n", target_str));
193                 return NULL;
194         }
195
196         /* The mappings are line-seperated, and specify details such as OIDs to skip etc */
197         while (1) {
198                 line = next_line;
199                 next_line = strchr(line, '\n');
200                 if (!next_line) {
201                         break;
202                 }
203                 next_line[0] = '\0';
204                 next_line++;
205
206                 /* Blank Line */
207                 if (line[0] == '\0') {
208                         continue;
209                 }
210                 /* Comment */
211                 if (line[0] == '#') {
212                         continue;
213                 }
214
215                 if (isdigit(line[0])) {
216                         char *p = strchr(line, ':');
217                         if (!p) {
218                                 DEBUG(0, ("schema mapping file line has OID but no OID to map to: %s\n", line));
219                                 return NULL;
220                         }
221                         p[0] = '\0';
222                         p++;
223                         oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
224                         trim_string(line, " ", " ");
225                         oid_map[num_oid_maps].old_oid = talloc_strdup(oid_map, line);
226                         trim_string(p, " ", " ");
227                         oid_map[num_oid_maps].new_oid = p;
228                         num_oid_maps++;
229                         oid_map[num_oid_maps].old_oid = NULL;
230                 } else {
231                         char *p = strchr(line, ':');
232                         if (p) {
233                                 /* remap attribute/objectClass */
234                                 p[0] = '\0';
235                                 p++;
236                                 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
237                                 trim_string(line, " ", " ");
238                                 attr_map[num_attr_maps].old_attr = talloc_strdup(attr_map, line);
239                                 trim_string(p, " ", " ");
240                                 attr_map[num_attr_maps].new_attr = p;
241                                 num_attr_maps++;
242                                 attr_map[num_attr_maps].old_attr = NULL;
243                         } else {
244                                 /* skip attribute/objectClass */
245                                 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
246                                 trim_string(line, " ", " ");
247                                 attrs_skip[num_skip] = talloc_strdup(attrs_skip, line);
248                                 num_skip++;
249                                 attrs_skip[num_skip] = NULL;
250                         }
251                 }
252         }
253
254         schema = dsdb_get_schema(ldb);
255         if (!schema) {
256                 DEBUG(0, ("No schema on ldb to convert!\n"));
257                 return NULL;
258         }
259
260         switch (target) {
261         case TARGET_OPENLDAP:
262                 out = talloc_strdup(mem_ctx, "");
263                 break;
264         case TARGET_FEDORA_DS:
265                 out = talloc_strdup(mem_ctx, "dn: cn=schema\n");
266                 break;
267         }
268
269         for (attribute=schema->attributes; attribute; attribute = attribute->next) {
270                 const char *name = attribute->lDAPDisplayName;
271                 const char *oid = attribute->attributeID_oid;
272                 const char *syntax = attribute->attributeSyntax_oid;
273                 const char *equality = NULL, *substring = NULL;
274                 bool single_value = attribute->isSingleValued;
275
276                 char *schema_entry = NULL;
277                 int j;
278
279                 /* We have been asked to skip some attributes/objectClasses */
280                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
281                         continue;
282                 }
283
284                 /* We might have been asked to remap this oid, due to a conflict */
285                 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
286                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
287                                 oid =  oid_map[j].new_oid;
288                                 break;
289                         }
290                 }
291                 
292                 if (attribute->syntax) {
293                         /* We might have been asked to remap this oid,
294                          * due to a conflict, or lack of
295                          * implementation */
296                         syntax = attribute->syntax->ldap_oid;
297                         /* We might have been asked to remap this oid, due to a conflict */
298                         for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
299                                 if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
300                                         syntax =  oid_map[j].new_oid;
301                                         break;
302                                 }
303                         }
304                         
305                         equality = attribute->syntax->equality;
306                         substring = attribute->syntax->substring;
307                 }
308
309                 /* We might have been asked to remap this name, due to a conflict */
310                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
311                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
312                                 name =  attr_map[j].new_attr;
313                                 break;
314                         }
315                 }
316                 
317                 schema_entry = schema_attribute_description(mem_ctx, 
318                                                             target, 
319                                                             SEPERATOR, 
320                                                             oid, 
321                                                             name, 
322                                                             equality, 
323                                                             substring, 
324                                                             syntax, 
325                                                             single_value, 
326                                                             false,
327                                                             NULL, NULL,
328                                                             NULL, NULL,
329                                                             false, false);
330
331                 if (schema_entry == NULL) {
332                         DEBUG(0, ("failed to generate attribute description for %s\n", name));
333                         return NULL;
334                 }
335
336                 switch (target) {
337                 case TARGET_OPENLDAP:
338                         out = talloc_asprintf_append(out, "attributetype %s\n\n", schema_entry);
339                         break;
340                 case TARGET_FEDORA_DS:
341                         out = talloc_asprintf_append(out, "attributeTypes: %s\n", schema_entry);
342                         break;
343                 }
344         }
345
346         out = print_schema_recursive(out, schema, "top", target, attrs_skip, attr_map, oid_map);
347
348         return out;
349 }
350