make a more recent snapshot of ldb available to interested
[samba.git] / source4 / lib / ldb / ldb_tdb / ldb_ldif.c
1  /* 
2    Unix SMB/CIFS implementation.
3
4    ldif utilities for ldb
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25
26 /*
27   this base64 decoder was taken from jitterbug (written by tridge).
28   we might need to replace it with a new version
29 */
30 static int base64_decode(char *s)
31 {
32         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
33         int bit_offset, byte_offset, idx, i, n;
34         unsigned char *d = (unsigned char *)s;
35         char *p;
36
37         n=i=0;
38
39         while (*s && (p=strchr(b64,*s))) {
40                 idx = (int)(p - b64);
41                 byte_offset = (i*6)/8;
42                 bit_offset = (i*6)%8;
43                 d[byte_offset] &= ~((1<<(8-bit_offset))-1);
44                 if (bit_offset < 3) {
45                         d[byte_offset] |= (idx << (2-bit_offset));
46                         n = byte_offset+1;
47                 } else {
48                         d[byte_offset] |= (idx >> (bit_offset-2));
49                         d[byte_offset+1] = 0;
50                         d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
51                         n = byte_offset+2;
52                 }
53                 s++; i++;
54         }
55
56         if (*s && !p) {
57                 /* the only termination allowed */
58                 if (*s != '=') {
59                         return -1;
60                 }
61         }
62
63         /* null terminate */
64         d[n] = 0;
65         return n;
66 }
67
68
69 /*
70   encode as base64
71   caller frees
72 */
73 char *ldb_base64_encode(const char *buf, int len)
74 {
75         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
76         int bit_offset, byte_offset, idx, i;
77         unsigned char *d = (unsigned char *)buf;
78         int bytes = (len*8 + 5)/6;
79         char *out;
80
81         out = malloc(bytes+2);
82         if (!out) return NULL;
83
84         for (i=0;i<bytes;i++) {
85                 byte_offset = (i*6)/8;
86                 bit_offset = (i*6)%8;
87                 if (bit_offset < 3) {
88                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
89                 } else {
90                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
91                         if (byte_offset+1 < len) {
92                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
93                         }
94                 }
95                 out[i] = b64[idx];
96         }
97
98         out[i++] = '=';
99         out[i] = 0;
100
101         return out;
102 }
103
104 /*
105   see if a buffer should be base64 encoded
106 */
107 int ldb_should_b64_encode(const struct ldb_val *val)
108 {
109         int i;
110         unsigned char *p = val->data;
111
112         if (val->length == 0 || p[0] == ' ' || p[0] == ':') {
113                 return 1;
114         }
115
116         for (i=0; i<val->length; i++) {
117                 if (!isprint(p[i]) || p[i] == '\n') {
118                         return 1;
119                 }
120         }
121         return 0;
122 }
123
124
125 /*
126   encode as base64 to a file
127 */
128 static int base64_encode_f(FILE *f, const char *buf, int len, int start_pos)
129 {
130         int i;
131         char *b = ldb_base64_encode(buf, len);
132
133         if (!b) {
134                 return -1;
135         }
136
137         for (i=0;b[i];i++) {
138                 fputc(b[i], f);
139                 if (b[i+1] && (i + start_pos) % 77 == 0) {
140                         fputc('\n', f);
141                         fputc(' ', f);
142                 }
143         }
144         free(b);
145         return 0;
146 }
147
148 /*
149   write a line folded string onto a file
150 */
151 static void fold_string(FILE *f, const char *buf, size_t length, int start_pos)
152 {
153         int i;
154
155         for (i=0;i<length;i++) {
156                 fputc(buf[i], f);
157                 if (i != (length-1) && (i + start_pos) % 77 == 0) {
158                         fputc('\n', f);
159                         fputc(' ', f);
160                 }
161         }
162 }
163
164
165 /*
166   pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
167   this routine removes any RFC2849 continuations and comments
168
169   caller frees
170 */
171 static char *next_chunk(FILE *f)
172 {
173         size_t alloc_size=0, chunk_size = 0;
174         char *chunk = NULL;
175         int c;
176         int in_comment = 0;
177
178         while ((c = fgetc(f)) != EOF) {
179                 if (chunk_size == alloc_size) {
180                         char *c2;
181                         alloc_size += 1024;
182                         c2 = realloc_p(chunk, char, alloc_size);
183                         if (!c2) {
184                                 free(chunk);
185                                 errno = ENOMEM;
186                                 return NULL;
187                         }
188                         chunk = c2;
189                 }
190
191                 if (in_comment) {
192                         if (c == '\n') {
193                                 in_comment = 0;
194                         }
195                         continue;                       
196                 }
197                 
198                 /* handle continuation lines - see RFC2849 */
199                 if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
200                         chunk_size--;
201                         continue;
202                 }
203                 
204                 /* chunks are terminated by a double line-feed */
205                 if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
206                         chunk[chunk_size-1] = 0;
207                         return chunk;
208                 }
209
210                 if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
211                         in_comment = 1;
212                         continue;
213                 }
214
215                 chunk[chunk_size++] = c;
216         }
217
218         return chunk;
219 }
220
221
222 /* simple ldif attribute parser */
223 static int next_attr(char **s, char **attr, struct ldb_val *value)
224 {
225         char *p;
226         int base64_encoded = 0;
227
228         p = strchr(*s, ':');
229         if (!p) {
230                 return -1;
231         }
232
233         *p++ = 0;
234
235         if (*p == ':') {
236                 base64_encoded = 1;
237                 p++;
238         }
239
240         *attr = *s;
241
242         while (isspace(*p)) {
243                 p++;
244         }
245
246         value->data = p;
247
248         p = strchr(p, '\n');
249
250         if (!p) {
251                 value->length = strlen((char *)value->data);
252                 *s = ((char *)value->data) + value->length;
253         } else {
254                 value->length = p - (char *)value->data;
255                 *s = p+1;
256                 *p = 0;
257         }
258
259         if (base64_encoded) {
260                 int len = base64_decode(value->data);
261                 if (len == -1) {
262                         /* it wasn't valid base64 data */
263                         return -1;
264                 }
265                 value->length = len;
266         }
267
268         return 0;
269 }
270
271
272 /*
273   free a message from a ldif_read
274 */
275 void ldif_read_free(struct ldb_message *msg)
276 {
277         if (msg->elements) free(msg->elements);
278         if (msg->private) free(msg->private);
279         free(msg);
280 }
281
282 /*
283  read from a LDIF file, creating a ldb_message
284 */
285 struct ldb_message *ldif_read(FILE *f)
286 {
287         struct ldb_message *msg;
288         char *attr=NULL, *chunk=NULL, *s;
289         struct ldb_val value;
290
291         value.data = NULL;
292
293         msg = malloc_p(struct ldb_message);
294         if (!msg) return NULL;
295
296         msg->dn = NULL;
297         msg->elements = NULL;
298         msg->num_elements = 0;
299         msg->private = NULL;
300
301         chunk = next_chunk(f);
302         if (!chunk) {
303                 goto failed;
304         }
305
306         msg->private = chunk;
307         s = chunk;
308
309         if (next_attr(&s, &attr, &value) != 0) {
310                 goto failed;
311         }
312         
313         /* first line must be a dn */
314         if (strcmp(attr, "dn") != 0) {
315                 fprintf(stderr, "First line must be a dn not '%s'\n", attr);
316                 goto failed;
317         }
318
319         msg->dn = value.data;
320
321         while (next_attr(&s, &attr, &value) == 0) {
322                 msg->elements = realloc_p(msg->elements, 
323                                           struct ldb_message_element, 
324                                           msg->num_elements+1);
325                 if (!msg->elements) {
326                         goto failed;
327                 }
328                 msg->elements[msg->num_elements].flags = 0;
329                 msg->elements[msg->num_elements].name = attr;
330                 msg->elements[msg->num_elements].value = value;
331                 msg->num_elements++;
332         }
333
334         return msg;
335
336 failed:
337         if (msg) ldif_read_free(msg);
338         return NULL;
339 }
340
341
342 /*
343   write to a ldif file 
344 */
345 void ldif_write(FILE *f, const struct ldb_message *msg)
346 {
347         int i;
348         fprintf(f, "dn: %s\n", msg->dn);
349         for (i=0;i<msg->num_elements;i++) {
350                 if (ldb_should_b64_encode(&msg->elements[i].value)) {
351                         fprintf(f, "%s:: ", msg->elements[i].name);
352                         base64_encode_f(f, 
353                                         msg->elements[i].value.data, 
354                                         msg->elements[i].value.length,
355                                         strlen(msg->elements[i].name)+3);
356                         fprintf(f, "\n");
357                 } else {
358                         fprintf(f, "%s: ", msg->elements[i].name);
359                         fold_string(f, msg->elements[i].value.data,                                 
360                                     msg->elements[i].value.length,
361                                     strlen(msg->elements[i].name)+2);
362                         fprintf(f, "\n");
363                 }
364         }
365         fprintf(f,"\n");
366 }