make a more recent snapshot of ldb available to interested
[kamenim/samba.git] / source4 / lib / ldb / common / ldb_ldif.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library 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 GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldif routines
29  *
30  *  Description: ldif pack/unpack routines
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36
37
38 /*
39   this base64 decoder was taken from jitterbug (written by tridge).
40   we might need to replace it with a new version
41 */
42 static int base64_decode(char *s)
43 {
44         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45         int bit_offset, byte_offset, idx, i, n;
46         unsigned char *d = (unsigned char *)s;
47         char *p;
48
49         n=i=0;
50
51         while (*s && (p=strchr(b64,*s))) {
52                 idx = (int)(p - b64);
53                 byte_offset = (i*6)/8;
54                 bit_offset = (i*6)%8;
55                 d[byte_offset] &= ~((1<<(8-bit_offset))-1);
56                 if (bit_offset < 3) {
57                         d[byte_offset] |= (idx << (2-bit_offset));
58                         n = byte_offset+1;
59                 } else {
60                         d[byte_offset] |= (idx >> (bit_offset-2));
61                         d[byte_offset+1] = 0;
62                         d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
63                         n = byte_offset+2;
64                 }
65                 s++; i++;
66         }
67
68         if (*s && !p) {
69                 /* the only termination allowed */
70                 if (*s != '=') {
71                         return -1;
72                 }
73         }
74
75         /* null terminate */
76         d[n] = 0;
77         return n;
78 }
79
80
81 /*
82   encode as base64
83   caller frees
84 */
85 char *ldb_base64_encode(const char *buf, int len)
86 {
87         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
88         int bit_offset, byte_offset, idx, i;
89         unsigned char *d = (unsigned char *)buf;
90         int bytes = (len*8 + 5)/6;
91         char *out;
92
93         out = malloc(bytes+2);
94         if (!out) return NULL;
95
96         for (i=0;i<bytes;i++) {
97                 byte_offset = (i*6)/8;
98                 bit_offset = (i*6)%8;
99                 if (bit_offset < 3) {
100                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
101                 } else {
102                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
103                         if (byte_offset+1 < len) {
104                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
105                         }
106                 }
107                 out[i] = b64[idx];
108         }
109
110         out[i++] = '=';
111         out[i] = 0;
112
113         return out;
114 }
115
116 /*
117   see if a buffer should be base64 encoded
118 */
119 int ldb_should_b64_encode(const struct ldb_val *val)
120 {
121         int i;
122         unsigned char *p = val->data;
123
124         if (val->length == 0 || p[0] == ' ' || p[0] == ':') {
125                 return 1;
126         }
127
128         for (i=0; i<val->length; i++) {
129                 if (!isprint(p[i]) || p[i] == '\n') {
130                         return 1;
131                 }
132         }
133         return 0;
134 }
135
136 /* this macro is used to handle the return checking on fprintf_fn() */
137 #define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
138
139 /*
140   write a line folded string onto a file
141 */
142 static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private,
143                         const char *buf, size_t length, int start_pos)
144 {
145         int i;
146         int total=0, ret;
147
148         for (i=0;i<length;i++) {
149                 ret = fprintf_fn(private, "%c", buf[i]);
150                 CHECK_RET;
151                 if (i != (length-1) && (i + start_pos) % 77 == 0) {
152                         ret = fprintf_fn(private, "\n ");
153                         CHECK_RET;
154                 }
155         }
156
157         return total;
158 }
159
160 /*
161   encode as base64 to a file
162 */
163 static int base64_encode_f(int (*fprintf_fn)(void *, const char *, ...), void *private,
164                            const char *buf, int len, int start_pos)
165 {
166         char *b = ldb_base64_encode(buf, len);
167         int ret;
168
169         if (!b) {
170                 return -1;
171         }
172
173         ret = fold_string(fprintf_fn, private, b, strlen(b), start_pos);
174
175         free(b);
176         return ret;
177 }
178
179 /*
180   write to ldif, using a caller supplied write method
181 */
182 int ldif_write(int (*fprintf_fn)(void *, const char *, ...), 
183                void *private,
184                const struct ldb_message *msg)
185 {
186         int i;
187         int total=0, ret;
188
189         ret = fprintf_fn(private, "dn: %s\n", msg->dn);
190         CHECK_RET;
191
192         for (i=0;i<msg->num_elements;i++) {
193                 if (ldb_should_b64_encode(&msg->elements[i].value)) {
194                         ret = fprintf_fn(private, "%s:: ", msg->elements[i].name);
195                         CHECK_RET;
196                         ret = base64_encode_f(fprintf_fn, private, 
197                                               msg->elements[i].value.data, 
198                                               msg->elements[i].value.length,
199                                               strlen(msg->elements[i].name)+3);
200                         CHECK_RET;
201                         ret = fprintf_fn(private, "\n");
202                         CHECK_RET;
203                 } else {
204                         ret = fprintf_fn(private, "%s: ", msg->elements[i].name);
205                         CHECK_RET;
206                         ret = fold_string(fprintf_fn, private,
207                                           msg->elements[i].value.data,
208                                           msg->elements[i].value.length,
209                                           strlen(msg->elements[i].name)+2);
210                         CHECK_RET;
211                         ret = fprintf_fn(private, "\n");
212                         CHECK_RET;
213                 }
214         }
215         ret = fprintf_fn(private,"\n");
216         CHECK_RET;
217
218         return total;
219 }
220
221 #undef CHECK_RET
222
223
224 /*
225   pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
226   this routine removes any RFC2849 continuations and comments
227
228   caller frees
229 */
230 static char *next_chunk(int (*fgetc_fn)(void *), void *private)
231 {
232         size_t alloc_size=0, chunk_size = 0;
233         char *chunk = NULL;
234         int c;
235         int in_comment = 0;
236
237         while ((c = fgetc_fn(private)) != EOF) {
238                 if (chunk_size == alloc_size) {
239                         char *c2;
240                         alloc_size += 1024;
241                         c2 = realloc_p(chunk, char, alloc_size);
242                         if (!c2) {
243                                 free(chunk);
244                                 errno = ENOMEM;
245                                 return NULL;
246                         }
247                         chunk = c2;
248                 }
249
250                 if (in_comment) {
251                         if (c == '\n') {
252                                 in_comment = 0;
253                         }
254                         continue;                       
255                 }
256                 
257                 /* handle continuation lines - see RFC2849 */
258                 if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
259                         chunk_size--;
260                         continue;
261                 }
262                 
263                 /* chunks are terminated by a double line-feed */
264                 if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
265                         chunk[chunk_size-1] = 0;
266                         return chunk;
267                 }
268
269                 if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
270                         in_comment = 1;
271                         continue;
272                 }
273
274                 /* ignore leading blank lines */
275                 if (chunk_size == 0 && c == '\n') {
276                         continue;
277                 }
278
279                 chunk[chunk_size++] = c;
280         }
281
282         return chunk;
283 }
284
285
286 /* simple ldif attribute parser */
287 static int next_attr(char **s, char **attr, struct ldb_val *value)
288 {
289         char *p;
290         int base64_encoded = 0;
291
292         p = strchr(*s, ':');
293         if (!p) {
294                 return -1;
295         }
296
297         *p++ = 0;
298
299         if (*p == ':') {
300                 base64_encoded = 1;
301                 p++;
302         }
303
304         *attr = *s;
305
306         while (isspace(*p)) {
307                 p++;
308         }
309
310         value->data = p;
311
312         p = strchr(p, '\n');
313
314         if (!p) {
315                 value->length = strlen((char *)value->data);
316                 *s = ((char *)value->data) + value->length;
317         } else {
318                 value->length = p - (char *)value->data;
319                 *s = p+1;
320                 *p = 0;
321         }
322
323         if (base64_encoded) {
324                 int len = base64_decode(value->data);
325                 if (len == -1) {
326                         /* it wasn't valid base64 data */
327                         return -1;
328                 }
329                 value->length = len;
330         }
331
332         return 0;
333 }
334
335
336 /*
337   free a message from a ldif_read
338 */
339 void ldif_read_free(struct ldb_message *msg)
340 {
341         if (msg->elements) free(msg->elements);
342         if (msg->private) free(msg->private);
343         free(msg);
344 }
345
346 /*
347  read from a LDIF source, creating a ldb_message
348 */
349 struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private)
350 {
351         struct ldb_message *msg;
352         char *attr=NULL, *chunk=NULL, *s;
353         struct ldb_val value;
354
355         value.data = NULL;
356
357         msg = malloc_p(struct ldb_message);
358         if (!msg) return NULL;
359
360         msg->dn = NULL;
361         msg->elements = NULL;
362         msg->num_elements = 0;
363         msg->private = NULL;
364
365         chunk = next_chunk(fgetc_fn, private);
366         if (!chunk) {
367                 goto failed;
368         }
369
370         msg->private = chunk;
371         s = chunk;
372
373         if (next_attr(&s, &attr, &value) != 0) {
374                 goto failed;
375         }
376         
377         /* first line must be a dn */
378         if (strcmp(attr, "dn") != 0) {
379                 fprintf(stderr, "First line must be a dn not '%s'\n", attr);
380                 goto failed;
381         }
382
383         msg->dn = value.data;
384
385         while (next_attr(&s, &attr, &value) == 0) {
386                 msg->elements = realloc_p(msg->elements, 
387                                           struct ldb_message_element, 
388                                           msg->num_elements+1);
389                 if (!msg->elements) {
390                         goto failed;
391                 }
392                 msg->elements[msg->num_elements].flags = 0;
393                 msg->elements[msg->num_elements].name = attr;
394                 msg->elements[msg->num_elements].value = value;
395                 msg->num_elements++;
396         }
397
398         return msg;
399
400 failed:
401         if (msg) ldif_read_free(msg);
402         return NULL;
403 }
404
405
406
407 /*
408   a wrapper around ldif_read() for reading from FILE*
409 */
410 struct ldif_read_file_state {
411         FILE *f;
412 };
413
414 static int fgetc_file(void *private)
415 {
416         struct ldif_read_file_state *state = private;
417         return fgetc(state->f);
418 }
419
420 struct ldb_message *ldif_read_file(FILE *f)
421 {
422         struct ldif_read_file_state state;
423         state.f = f;
424         return ldif_read(fgetc_file, &state);
425 }
426
427
428 /*
429   a wrapper around ldif_read() for reading from const char*
430 */
431 struct ldif_read_string_state {
432         const char *s;
433 };
434
435 static int fgetc_string(void *private)
436 {
437         struct ldif_read_string_state *state = private;
438         if (state->s[0] != 0) {
439                 return *state->s++;
440         }
441         return EOF;
442 }
443
444 struct ldb_message *ldif_read_string(const char *s)
445 {
446         struct ldif_read_string_state state;
447         state.s = s;
448         return ldif_read(fgetc_string, &state);
449 }
450
451
452 /*
453   wrapper around ldif_write() for a file
454 */
455 struct ldif_write_file_state {
456         FILE *f;
457 };
458
459 static int fprintf_file(void *private, const char *fmt, ...)
460 {
461         struct ldif_write_file_state *state = private;
462         int ret;
463         va_list ap;
464
465         va_start(ap, fmt);
466         ret = vfprintf(state->f, fmt, ap);
467         va_end(ap);
468         return ret;
469 }
470
471 int ldif_write_file(FILE *f, const struct ldb_message *msg)
472 {
473         struct ldif_write_file_state state;
474         state.f = f;
475         return ldif_write(fprintf_file, &state, msg);
476 }