r382: More C++ friendliness fixes.
[kai/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 /*
36   see RFC2849 for the LDIF format definition
37 */
38
39 #include "includes.h"
40
41
42 /*
43   this base64 decoder was taken from jitterbug (written by tridge).
44   we might need to replace it with a new version
45 */
46 static int base64_decode(char *s)
47 {
48         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
49         int bit_offset, byte_offset, idx, i, n;
50         unsigned char *d = (unsigned char *)s;
51         char *p;
52
53         n=i=0;
54
55         while (*s && (p=strchr(b64,*s))) {
56                 idx = (int)(p - b64);
57                 byte_offset = (i*6)/8;
58                 bit_offset = (i*6)%8;
59                 d[byte_offset] &= ~((1<<(8-bit_offset))-1);
60                 if (bit_offset < 3) {
61                         d[byte_offset] |= (idx << (2-bit_offset));
62                         n = byte_offset+1;
63                 } else {
64                         d[byte_offset] |= (idx >> (bit_offset-2));
65                         d[byte_offset+1] = 0;
66                         d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
67                         n = byte_offset+2;
68                 }
69                 s++; i++;
70         }
71
72         if (*s && !p) {
73                 /* the only termination allowed */
74                 if (*s != '=') {
75                         return -1;
76                 }
77         }
78
79         /* null terminate */
80         d[n] = 0;
81         return n;
82 }
83
84
85 /*
86   encode as base64
87   caller frees
88 */
89 char *ldb_base64_encode(const char *buf, int len)
90 {
91         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
92         int bit_offset, byte_offset, idx, i;
93         const unsigned char *d = (const unsigned char *)buf;
94         int bytes = (len*8 + 5)/6;
95         char *out;
96
97         out = malloc(bytes+2);
98         if (!out) return NULL;
99
100         for (i=0;i<bytes;i++) {
101                 byte_offset = (i*6)/8;
102                 bit_offset = (i*6)%8;
103                 if (bit_offset < 3) {
104                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
105                 } else {
106                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
107                         if (byte_offset+1 < len) {
108                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
109                         }
110                 }
111                 out[i] = b64[idx];
112         }
113
114         out[i++] = '=';
115         out[i] = 0;
116
117         return out;
118 }
119
120 /*
121   see if a buffer should be base64 encoded
122 */
123 int ldb_should_b64_encode(const struct ldb_val *val)
124 {
125         int i;
126         unsigned char *p = val->data;
127
128         if (val->length == 0 || p[0] == ' ' || p[0] == ':') {
129                 return 1;
130         }
131
132         for (i=0; i<val->length; i++) {
133                 if (!isprint(p[i]) || p[i] == '\n') {
134                         return 1;
135                 }
136         }
137         return 0;
138 }
139
140 /* this macro is used to handle the return checking on fprintf_fn() */
141 #define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
142
143 /*
144   write a line folded string onto a file
145 */
146 static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private,
147                         const char *buf, size_t length, int start_pos)
148 {
149         int i;
150         int total=0, ret;
151
152         for (i=0;i<length;i++) {
153                 ret = fprintf_fn(private, "%c", buf[i]);
154                 CHECK_RET;
155                 if (i != (length-1) && (i + start_pos) % 77 == 0) {
156                         ret = fprintf_fn(private, "\n ");
157                         CHECK_RET;
158                 }
159         }
160
161         return total;
162 }
163
164 /*
165   encode as base64 to a file
166 */
167 static int base64_encode_f(int (*fprintf_fn)(void *, const char *, ...), void *private,
168                            const char *buf, int len, int start_pos)
169 {
170         char *b = ldb_base64_encode(buf, len);
171         int ret;
172
173         if (!b) {
174                 return -1;
175         }
176
177         ret = fold_string(fprintf_fn, private, b, strlen(b), start_pos);
178
179         free(b);
180         return ret;
181 }
182
183
184 static const struct {
185         const char *name;
186         enum ldb_changetype changetype;
187 } ldb_changetypes[] = {
188         {"add",    LDB_CHANGETYPE_ADD},
189         {"delete", LDB_CHANGETYPE_DELETE},
190         {"modify", LDB_CHANGETYPE_MODIFY},
191         {NULL, 0}
192 };
193
194 /*
195   write to ldif, using a caller supplied write method
196 */
197 int ldif_write(int (*fprintf_fn)(void *, const char *, ...), 
198                void *private,
199                const struct ldb_ldif *ldif)
200 {
201         int i, j;
202         int total=0, ret;
203         const struct ldb_message *msg;
204
205         msg = &ldif->msg;
206
207         ret = fprintf_fn(private, "dn: %s\n", msg->dn);
208         CHECK_RET;
209
210         if (ldif->changetype != LDB_CHANGETYPE_NONE) {
211                 for (i=0;ldb_changetypes[i].name;i++) {
212                         if (ldb_changetypes[i].changetype == ldif->changetype) {
213                                 break;
214                         }
215                 }
216                 if (!ldb_changetypes[i].name) {
217                         fprintf(stderr,"Invalid changetype\n");
218                         return -1;
219                 }
220                 ret = fprintf_fn(private, "changetype: %s\n", ldb_changetypes[i].name);
221                 CHECK_RET;
222         }
223
224         for (i=0;i<msg->num_elements;i++) {
225                 if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
226                         switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
227                         case LDB_FLAG_MOD_ADD:
228                                 fprintf_fn(private, "add: %s\n", 
229                                            msg->elements[i].name);
230                                 break;
231                         case LDB_FLAG_MOD_DELETE:
232                                 fprintf_fn(private, "delete: %s\n", 
233                                            msg->elements[i].name);
234                                 break;
235                         case LDB_FLAG_MOD_REPLACE:
236                                 fprintf_fn(private, "replace: %s\n", 
237                                            msg->elements[i].name);
238                                 break;
239                         }
240                 }
241
242                 for (j=0;j<msg->elements[i].num_values;j++) {
243                         if (ldb_should_b64_encode(&msg->elements[i].values[j])) {
244                                 ret = fprintf_fn(private, "%s:: ", 
245                                                  msg->elements[i].name);
246                                 CHECK_RET;
247                                 ret = base64_encode_f(fprintf_fn, private, 
248                                                       msg->elements[i].values[j].data, 
249                                                       msg->elements[i].values[j].length,
250                                                       strlen(msg->elements[i].name)+3);
251                                 CHECK_RET;
252                                 ret = fprintf_fn(private, "\n");
253                                 CHECK_RET;
254                         } else {
255                                 ret = fprintf_fn(private, "%s: ", msg->elements[i].name);
256                                 CHECK_RET;
257                                 ret = fold_string(fprintf_fn, private,
258                                                   msg->elements[i].values[j].data,
259                                                   msg->elements[i].values[j].length,
260                                                   strlen(msg->elements[i].name)+2);
261                                 CHECK_RET;
262                                 ret = fprintf_fn(private, "\n");
263                                 CHECK_RET;
264                         }
265                 }
266                 if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
267                         fprintf_fn(private, "-\n");
268                 }
269         }
270         ret = fprintf_fn(private,"\n");
271         CHECK_RET;
272
273         return total;
274 }
275
276 #undef CHECK_RET
277
278
279 /*
280   pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
281   this routine removes any RFC2849 continuations and comments
282
283   caller frees
284 */
285 static char *next_chunk(int (*fgetc_fn)(void *), void *private)
286 {
287         size_t alloc_size=0, chunk_size = 0;
288         char *chunk = NULL;
289         int c;
290         int in_comment = 0;
291
292         while ((c = fgetc_fn(private)) != EOF) {
293                 if (chunk_size+1 >= alloc_size) {
294                         char *c2;
295                         alloc_size += 1024;
296                         c2 = realloc_p(chunk, char, alloc_size);
297                         if (!c2) {
298                                 free(chunk);
299                                 errno = ENOMEM;
300                                 return NULL;
301                         }
302                         chunk = c2;
303                 }
304
305                 if (in_comment) {
306                         if (c == '\n') {
307                                 in_comment = 0;
308                         }
309                         continue;                       
310                 }
311                 
312                 /* handle continuation lines - see RFC2849 */
313                 if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
314                         chunk_size--;
315                         continue;
316                 }
317                 
318                 /* chunks are terminated by a double line-feed */
319                 if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
320                         chunk[chunk_size-1] = 0;
321                         return chunk;
322                 }
323
324                 if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
325                         in_comment = 1;
326                         continue;
327                 }
328
329                 /* ignore leading blank lines */
330                 if (chunk_size == 0 && c == '\n') {
331                         continue;
332                 }
333
334                 chunk[chunk_size++] = c;
335         }
336
337         if (chunk) {
338                 chunk[chunk_size] = 0;
339         }
340
341         return chunk;
342 }
343
344
345 /* simple ldif attribute parser */
346 static int next_attr(char **s, char **attr, struct ldb_val *value)
347 {
348         char *p;
349         int base64_encoded = 0;
350
351         if (strncmp(*s, "-\n", 2) == 0) {
352                 value->length = 0;
353                 *attr = "-";
354                 *s += 2;
355                 return 0;
356         }
357
358         p = strchr(*s, ':');
359         if (!p) {
360                 return -1;
361         }
362
363         *p++ = 0;
364
365         if (*p == ':') {
366                 base64_encoded = 1;
367                 p++;
368         }
369
370         *attr = *s;
371
372         while (isspace(*p)) {
373                 p++;
374         }
375
376         value->data = p;
377
378         p = strchr(p, '\n');
379
380         if (!p) {
381                 value->length = strlen((char *)value->data);
382                 *s = ((char *)value->data) + value->length;
383         } else {
384                 value->length = p - (char *)value->data;
385                 *s = p+1;
386                 *p = 0;
387         }
388
389         if (base64_encoded) {
390                 int len = base64_decode(value->data);
391                 if (len == -1) {
392                         /* it wasn't valid base64 data */
393                         return -1;
394                 }
395                 value->length = len;
396         }
397
398         return 0;
399 }
400
401
402 /*
403   free a message from a ldif_read
404 */
405 void ldif_read_free(struct ldb_ldif *ldif)
406 {
407         struct ldb_message *msg = &ldif->msg;
408         int i;
409         for (i=0;i<msg->num_elements;i++) {
410                 if (msg->elements[i].values) free(msg->elements[i].values);
411         }
412         if (msg->elements) free(msg->elements);
413         if (msg->private_data) free(msg->private_data);
414         free(ldif);
415 }
416
417 /*
418   add an empty element
419 */
420 static int msg_add_empty(struct ldb_message *msg, const char *name, unsigned flags)
421 {
422         struct ldb_message_element *el2, *el;
423
424         el2 = realloc_p(msg->elements, struct ldb_message_element, msg->num_elements+1);
425         if (!el2) {
426                 errno = ENOMEM;
427                 return -1;
428         }
429         
430         msg->elements = el2;
431
432         el = &msg->elements[msg->num_elements];
433         
434         el->name = name;
435         el->num_values = 0;
436         el->values = NULL;
437         el->flags = flags;
438
439         msg->num_elements++;
440
441         return 0;
442 }
443
444 /*
445  read from a LDIF source, creating a ldb_message
446 */
447 struct ldb_ldif *ldif_read(int (*fgetc_fn)(void *), void *private_data)
448 {
449         struct ldb_ldif *ldif;
450         struct ldb_message *msg;
451         char *attr=NULL, *chunk=NULL, *s;
452         struct ldb_val value;
453         unsigned flags = 0;
454
455         value.data = NULL;
456
457         ldif = malloc_p(struct ldb_ldif);
458         if (!ldif) return NULL;
459
460         ldif->changetype = LDB_CHANGETYPE_NONE;
461         msg = &ldif->msg;
462
463         msg->dn = NULL;
464         msg->elements = NULL;
465         msg->num_elements = 0;
466         msg->private_data = NULL;
467
468         chunk = next_chunk(fgetc_fn, private_data);
469         if (!chunk) {
470                 goto failed;
471         }
472
473         msg->private_data = chunk;
474         s = chunk;
475
476         if (next_attr(&s, &attr, &value) != 0) {
477                 goto failed;
478         }
479         
480         /* first line must be a dn */
481         if (strcmp(attr, "dn") != 0) {
482                 fprintf(stderr, "First line must be a dn not '%s'\n", attr);
483                 goto failed;
484         }
485
486         msg->dn = value.data;
487
488         while (next_attr(&s, &attr, &value) == 0) {
489                 struct ldb_message_element *el;
490                 int empty = 0;
491
492                 if (strcmp(attr, "changetype") == 0) {
493                         int i;
494                         for (i=0;ldb_changetypes[i].name;i++) {
495                                 if (strcmp((char *)value.data, ldb_changetypes[i].name) == 0) {
496                                         ldif->changetype = ldb_changetypes[i].changetype;
497                                         break;
498                                 }
499                         }
500                         if (!ldb_changetypes[i].name) {
501                                 fprintf(stderr,"Bad changetype '%s'\n",
502                                         (char *)value.data);
503                         }
504                         flags = 0;
505                         continue;
506                 }
507
508                 if (strcmp(attr, "add") == 0) {
509                         flags = LDB_FLAG_MOD_ADD;
510                         empty = 1;
511                 }
512                 if (strcmp(attr, "delete") == 0) {
513                         flags = LDB_FLAG_MOD_DELETE;
514                         empty = 1;
515                 }
516                 if (strcmp(attr, "replace") == 0) {
517                         flags = LDB_FLAG_MOD_REPLACE;
518                         empty = 1;
519                 }
520                 if (strcmp(attr, "-") == 0) {
521                         flags = 0;
522                         continue;
523                 }
524
525                 if (empty) {
526                         if (msg_add_empty(msg, (char *)value.data, flags) != 0) {
527                                 goto failed;
528                         }
529                         continue;
530                 }
531                 
532                 el = &msg->elements[msg->num_elements-1];
533
534                 if (msg->num_elements > 0 && strcmp(attr, el->name) == 0 &&
535                     flags == el->flags) {
536                         /* its a continuation */
537                         el->values = 
538                                 realloc_p(el->values, struct ldb_val, el->num_values+1);
539                         if (!el->values) {
540                                 goto failed;
541                         }
542                         el->values[el->num_values] = value;
543                         el->num_values++;
544                 } else {
545                         /* its a new attribute */
546                         msg->elements = realloc_p(msg->elements, 
547                                                   struct ldb_message_element, 
548                                                   msg->num_elements+1);
549                         if (!msg->elements) {
550                                 goto failed;
551                         }
552                         msg->elements[msg->num_elements].flags = flags;
553                         msg->elements[msg->num_elements].name = attr;
554                         el = &msg->elements[msg->num_elements];
555                         el->values = malloc_p(struct ldb_val);
556                         if (!el->values) {
557                                 goto failed;
558                         }
559                         el->num_values = 1;
560                         el->values[0] = value;
561                         msg->num_elements++;
562                 }
563         }
564
565         return ldif;
566
567 failed:
568         if (ldif) ldif_read_free(ldif);
569         return NULL;
570 }
571
572
573
574 /*
575   a wrapper around ldif_read() for reading from FILE*
576 */
577 struct ldif_read_file_state {
578         FILE *f;
579 };
580
581 static int fgetc_file(void *private)
582 {
583         struct ldif_read_file_state *state = private;
584         return fgetc(state->f);
585 }
586
587 struct ldb_ldif *ldif_read_file(FILE *f)
588 {
589         struct ldif_read_file_state state;
590         state.f = f;
591         return ldif_read(fgetc_file, &state);
592 }
593
594
595 /*
596   a wrapper around ldif_read() for reading from const char*
597 */
598 struct ldif_read_string_state {
599         const char *s;
600 };
601
602 static int fgetc_string(void *private)
603 {
604         struct ldif_read_string_state *state = private;
605         if (state->s[0] != 0) {
606                 return *state->s++;
607         }
608         return EOF;
609 }
610
611 struct ldb_ldif *ldif_read_string(const char *s)
612 {
613         struct ldif_read_string_state state;
614         state.s = s;
615         return ldif_read(fgetc_string, &state);
616 }
617
618
619 /*
620   wrapper around ldif_write() for a file
621 */
622 struct ldif_write_file_state {
623         FILE *f;
624 };
625
626 static int fprintf_file(void *private, const char *fmt, ...)
627 {
628         struct ldif_write_file_state *state = private;
629         int ret;
630         va_list ap;
631
632         va_start(ap, fmt);
633         ret = vfprintf(state->f, fmt, ap);
634         va_end(ap);
635         return ret;
636 }
637
638 int ldif_write_file(FILE *f, const struct ldb_ldif *ldif)
639 {
640         struct ldif_write_file_state state;
641         state.f = f;
642         return ldif_write(fprintf_file, &state, ldif);
643 }