r5305: removed libcli/ldap/ldap.h from includes.h
[kamenim/samba.git] / source4 / libcli / ldap / ldap_ldif.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    LDAP protocol helper functions for SAMBA
4    
5    Copyright (C) Andrew Tridgell  2004
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Stefan Metzmacher 2004
8    Copyright (C) Simo Sorce 2004
9     
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program 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
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23    
24 */
25
26 #include "includes.h"
27 #include "system/iconv.h"
28 #include "libcli/ldap/ldap.h"
29
30 /****************************************************************************
31  *
32  * LDIF parser
33  *
34  * Shamelessly stolen and adapted from ldb.
35  *
36  ***************************************************************************/
37
38 /*
39   pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
40   this routine removes any RFC2849 continuations and comments
41
42   caller frees
43 */
44 static char *next_chunk(TALLOC_CTX *mem_ctx,
45                         int (*fgetc_fn)(void *), void *private_data)
46 {
47         size_t alloc_size=0, chunk_size = 0;
48         char *chunk = NULL;
49         int c;
50         int in_comment = 0;
51
52         while ((c = fgetc_fn(private_data)) != EOF) {
53                 if (chunk_size+1 >= alloc_size) {
54                         char *c2;
55                         alloc_size += 1024;
56                         c2 = talloc_realloc(mem_ctx, chunk, char, alloc_size);
57                         if (!c2) {
58                                 errno = ENOMEM;
59                                 return NULL;
60                         }
61                         chunk = c2;
62                 }
63
64                 if (in_comment) {
65                         if (c == '\n') {
66                                 in_comment = 0;
67                         }
68                         continue;                       
69                 }
70                 
71                 /* handle continuation lines - see RFC2849 */
72                 if (c == ' ' && chunk_size > 1 &&
73                     chunk[chunk_size-1] == '\n') {
74                         chunk_size--;
75                         continue;
76                 }
77                 
78                 /* chunks are terminated by a double line-feed */
79                 if (c == '\n' && chunk_size > 0 &&
80                     chunk[chunk_size-1] == '\n') {
81                         chunk[chunk_size-1] = 0;
82                         return chunk;
83                 }
84
85                 if (c == '#' &&
86                     (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
87                         in_comment = 1;
88                         continue;
89                 }
90
91                 /* ignore leading blank lines */
92                 if (chunk_size == 0 && c == '\n') {
93                         continue;
94                 }
95
96                 chunk[chunk_size++] = c;
97         }
98
99         if (chunk) {
100                 chunk[chunk_size] = 0;
101         }
102
103         return chunk;
104 }
105
106 /* simple ldif attribute parser */
107 static int next_attr(char **s, const char **attr, struct ldap_val *value)
108 {
109         char *p;
110         int base64_encoded = 0;
111
112         if (strncmp(*s, "-\n", 2) == 0) {
113                 value->length = 0;
114                 *attr = "-";
115                 *s += 2;
116                 return 0;
117         }
118
119         p = strchr(*s, ':');
120         if (!p) {
121                 return -1;
122         }
123
124         *p++ = 0;
125
126         if (*p == ':') {
127                 base64_encoded = 1;
128                 p++;
129         }
130
131         *attr = *s;
132
133         while (isspace(*p)) {
134                 p++;
135         }
136
137         value->data = p;
138
139         p = strchr(p, '\n');
140
141         if (!p) {
142                 value->length = strlen((char *)value->data);
143                 *s = ((char *)value->data) + value->length;
144         } else {
145                 value->length = p - (char *)value->data;
146                 *s = p+1;
147                 *p = 0;
148         }
149
150         if (base64_encoded) {
151                 DATA_BLOB blob = base64_decode_data_blob(value->data);
152                 memcpy(value->data, blob.data, blob.length);
153                 value->length = blob.length;
154                 ((char *)value->data)[value->length] = '\0';
155         }
156
157         return 0;
158 }
159
160 BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldap_val *value,
161                          struct ldap_attribute *attrib)
162 {
163         attrib->values = talloc_realloc(mem_ctx, 
164                                           attrib->values,
165                                           DATA_BLOB,
166                                           attrib->num_values+1);
167         if (attrib->values == NULL)
168                 return False;
169
170         attrib->values[attrib->num_values] =
171                 data_blob_talloc(mem_ctx, value->data, value->length);
172         attrib->num_values += 1;
173         return True;
174 }
175
176 BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
177                                        const struct ldap_attribute *attrib,
178                                        struct ldap_attribute **attribs,
179                                        int *num_attribs)
180 {
181         *attribs = talloc_realloc(mem_ctx,
182                                     *attribs,
183                                     struct ldap_attribute,
184                                     *num_attribs+1);
185
186         if (*attribs == NULL)
187                 return False;
188
189         (*attribs)[*num_attribs] = *attrib;
190         *num_attribs += 1;
191         return True;
192 }
193
194 static BOOL fill_add_attributes(struct ldap_message *msg, char **chunk)
195 {
196         struct ldap_AddRequest *r = &msg->r.AddRequest;
197         const char *attr_name;
198         struct ldap_val value;
199
200         r->num_attributes = 0;
201         r->attributes = NULL;
202
203         while (next_attr(chunk, &attr_name, &value) == 0) {
204                 int i;
205                 struct ldap_attribute *attrib = NULL;
206                 
207                 for (i=0; i<r->num_attributes; i++) {
208                         if (strequal(r->attributes[i].name, attr_name)) {
209                                 attrib = &r->attributes[i];
210                                 break;
211                         }
212                 }
213
214                 if (attrib == NULL) {
215                         r->attributes = talloc_realloc(msg->mem_ctx,
216                                                          r->attributes,
217                                                          struct ldap_attribute,
218                                                          r->num_attributes+1);
219                         if (r->attributes == NULL)
220                                 return False;
221
222                         attrib = &(r->attributes[r->num_attributes]);
223                         r->num_attributes += 1;
224                         ZERO_STRUCTP(attrib);
225                         attrib->name = talloc_strdup(msg->mem_ctx,
226                                                      attr_name);
227                 }
228
229                 if (!add_value_to_attrib(msg->mem_ctx, &value, attrib))
230                         return False;
231         }
232         return True;
233 }
234
235 BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
236                                     struct ldap_mod *mod,
237                                     struct ldap_mod **mods,
238                                     int *num_mods)
239 {
240         *mods = talloc_realloc(mem_ctx, *mods, struct ldap_mod, (*num_mods)+1);
241
242         if (*mods == NULL)
243                 return False;
244
245         (*mods)[*num_mods] = *mod;
246         *num_mods += 1;
247         return True;
248 }
249
250 static BOOL fill_mods(struct ldap_message *msg, char **chunk)
251 {
252         struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
253         const char *attr_name;
254         struct ldap_val value;
255
256         r->num_mods = 0;
257         r->mods = NULL;
258
259         while (next_attr(chunk, &attr_name, &value) == 0) {
260
261                 struct ldap_mod mod;
262                 mod.type = LDAP_MODIFY_NONE;
263
264                 mod.attrib.name = talloc_strdup(msg->mem_ctx, value.data);
265
266                 if (strequal(attr_name, "add"))
267                         mod.type = LDAP_MODIFY_ADD;
268
269                 if (strequal(attr_name, "delete"))
270                         mod.type = LDAP_MODIFY_DELETE;
271
272                 if (strequal(attr_name, "replace"))
273                         mod.type = LDAP_MODIFY_REPLACE;
274
275                 if (mod.type == LDAP_MODIFY_NONE) {
276                         DEBUG(2, ("ldif modification type %s unsupported\n",
277                                   attr_name));
278                         return False;
279                 }
280
281                 mod.attrib.num_values = 0;
282                 mod.attrib.values = NULL;
283
284                 while (next_attr(chunk, &attr_name, &value) == 0) {
285                         if (strequal(attr_name, "-"))
286                                 break;
287                         if (!strequal(attr_name, mod.attrib.name)) {
288                                 DEBUG(3, ("attrib name %s does not "
289                                           "match %s\n", attr_name,
290                                           mod.attrib.name));
291                                 return False;
292                         }
293                         if (!add_value_to_attrib(msg->mem_ctx, &value,
294                                                  &mod.attrib)) {
295                                 DEBUG(3, ("Could not add value\n"));
296                                 return False;
297                         }
298                 }
299
300                 if (!add_mod_to_array_talloc(msg->mem_ctx, &mod, &r->mods,
301                                              &r->num_mods))
302                         return False;
303         }
304
305         return True;
306 }
307
308 /*
309  read from a LDIF source, creating a ldap_message
310 */
311 static struct ldap_message *ldif_read(TALLOC_CTX *mem_ctx, int (*fgetc_fn)(void *),
312                                       void *private_data)
313 {
314         struct ldap_message *msg;
315         const char *attr=NULL;
316         const char *dn;
317         char *chunk=NULL, *s;
318         struct ldap_val value;
319
320         value.data = NULL;
321
322         msg = new_ldap_message(mem_ctx);
323         if (msg == NULL)
324                 return NULL;
325
326         chunk = next_chunk(msg->mem_ctx, fgetc_fn, private_data);
327         if (!chunk) {
328                 goto failed;
329         }
330
331         s = chunk;
332
333         if (next_attr(&s, &attr, &value) != 0) {
334                 goto failed;
335         }
336         
337         /* first line must be a dn */
338         if (!strequal(attr, "dn")) {
339                 DEBUG(5, ("Error: First line of ldif must be a dn not '%s'\n",
340                           attr));
341                 goto failed;
342         }
343
344         dn = talloc_strdup(msg->mem_ctx, value.data);
345
346         if (next_attr(&s, &attr, &value) != 0) {
347                 goto failed;
348         }
349
350         if (!strequal(attr, "changetype")) {
351                 DEBUG(5, ("Error: Second line of ldif must be a changetype "
352                           "not '%s'\n",  attr));
353                 goto failed;
354         }
355
356         if (strequal(value.data, "delete")) {
357                 msg->type = LDAP_TAG_DelRequest;
358                 msg->r.DelRequest.dn = dn;
359                 return msg;
360         }
361
362         if (strequal(value.data, "add")) {
363                 msg->type = LDAP_TAG_AddRequest;
364
365                 msg->r.AddRequest.dn = dn;
366
367                 if (!fill_add_attributes(msg, &s))
368                         goto failed;
369
370                 return msg;
371         }
372
373         if (strequal(value.data, "modify")) {
374                 msg->type = LDAP_TAG_ModifyRequest;
375
376                 msg->r.ModifyRequest.dn = dn;
377
378                 if (!fill_mods(msg, &s))
379                         goto failed;
380
381                 return msg;
382         }
383
384         DEBUG(3, ("changetype %s not supported\n", (char *)value.data));
385
386 failed:
387         talloc_free(msg);
388         return NULL;
389 }
390
391 /*
392   a wrapper around ldif_read() for reading from const char*
393 */
394 struct ldif_read_string_state {
395         const char *s;
396 };
397
398 static int fgetc_string(void *private_data)
399 {
400         struct ldif_read_string_state *state = private_data;
401         if (state->s[0] != 0) {
402                 return *state->s++;
403         }
404         return EOF;
405 }
406
407 struct ldap_message *ldap_ldif2msg(TALLOC_CTX *mem_ctx, const char *s)
408 {
409         struct ldif_read_string_state state;
410         state.s = s;
411         return ldif_read(mem_ctx, fgetc_string, &state);
412 }
413