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