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