r792: - changed the ldb ldif_* functions to be in the ldb_ namespace
[samba.git] / source4 / lib / ldb / tools / ldbedit.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: ldbedit
29  *
30  *  Description: utility for ldb database editing
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36
37 /*
38   modify a database record so msg1 becomes msg2
39   returns the number of modified elements
40 */
41 static int modify_record(struct ldb_context *ldb, 
42                          struct ldb_message *msg1,
43                          struct ldb_message *msg2)
44 {
45         struct ldb_message mod;
46         struct ldb_message_element *el;
47         int i;
48         int count = 0;
49
50         mod.dn = msg1->dn;
51         mod.num_elements = 0;
52         mod.elements = NULL;
53
54         /* look in msg2 to find elements that need to be added
55            or modified */
56         for (i=0;i<msg2->num_elements;i++) {
57                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
58
59                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
60                         continue;
61                 }
62
63                 if (ldb_msg_add(ldb, &mod, 
64                                 &msg2->elements[i],
65                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
66                         return -1;
67                 }
68                 count++;
69         }
70
71         /* look in msg1 to find elements that need to be deleted */
72         for (i=0;i<msg1->num_elements;i++) {
73                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
74                 if (!el) {
75                         if (ldb_msg_add_empty(ldb, &mod, 
76                                               msg1->elements[i].name,
77                                               LDB_FLAG_MOD_DELETE) != 0) {
78                                 return -1;
79                         }
80                         count++;
81                 }
82         }
83
84         if (mod.num_elements == 0) {
85                 return 0;
86         }
87
88         if (ldb_modify(ldb, &mod) != 0) {
89                 fprintf(stderr, "failed to modify %s - %s\n", 
90                         msg1->dn, ldb_errstring(ldb));
91                 return -1;
92         }
93
94         return count;
95 }
96
97 /*
98   find dn in msgs[]
99 */
100 static struct ldb_message *msg_find(struct ldb_message **msgs, int count,
101                                     const char *dn)
102 {
103         int i;
104         for (i=0;i<count;i++) {
105                 if (ldb_dn_cmp(dn, msgs[i]->dn) == 0) {
106                         return msgs[i];
107                 }
108         }
109         return NULL;
110 }
111
112 /*
113   merge the changes in msgs2 into the messages from msgs1
114 */
115 static int merge_edits(struct ldb_context *ldb,
116                        struct ldb_message **msgs1, int count1,
117                        struct ldb_message **msgs2, int count2)
118 {
119         int i;
120         struct ldb_message *msg;
121         int ret = 0;
122         int adds=0, modifies=0, deletes=0;
123
124         /* do the adds and modifies */
125         for (i=0;i<count2;i++) {
126                 msg = msg_find(msgs1, count1, msgs2[i]->dn);
127                 if (!msg) {
128                         if (ldb_add(ldb, msgs2[i]) != 0) {
129                                 fprintf(stderr, "failed to add %s - %s\n",
130                                         msgs2[i]->dn, ldb_errstring(ldb));
131                                 return -1;
132                         }
133                         adds++;
134                 } else {
135                         if (modify_record(ldb, msg, msgs2[i]) > 0) {
136                                 modifies++;
137                         }
138                 }
139         }
140
141         /* do the deletes */
142         for (i=0;i<count1;i++) {
143                 msg = msg_find(msgs2, count2, msgs1[i]->dn);
144                 if (!msg) {
145                         if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
146                                 fprintf(stderr, "failed to delete %s - %s\n",
147                                         msgs1[i]->dn, ldb_errstring(ldb));
148                                 return -1;
149                         }
150                         deletes++;
151                 }
152         }
153
154         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
155
156         return ret;
157 }
158
159 /*
160   save a set of messages as ldif to a file
161 */
162 static int save_ldif(struct ldb_context *ldb, 
163                      FILE *f, struct ldb_message **msgs, int count)
164 {
165         int i;
166
167         fprintf(f, "# editing %d records\n", count);
168
169         for (i=0;i<count;i++) {
170                 struct ldb_ldif ldif;
171                 fprintf(f, "# record %d\n", i+1);
172
173                 ldif.changetype = LDB_CHANGETYPE_NONE;
174                 ldif.msg = *msgs[i];
175
176                 ldb_ldif_write_file(ldb, f, &ldif);
177         }
178
179         return 0;
180 }
181
182
183 /*
184   edit the ldb search results in msgs using the user selected editor
185 */
186 static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
187                    const char *editor)
188 {
189         int fd, ret;
190         FILE *f;
191         char template[] = "/tmp/ldbedit.XXXXXX";
192         char *cmd;
193         struct ldb_ldif *ldif;
194         struct ldb_message **msgs2 = NULL;
195         int count2 = 0;
196
197         /* write out the original set of messages to a temporary
198            file */
199         fd = mkstemp(template);
200
201         if (fd == -1) {
202                 perror(template);
203                 return -1;
204         }
205
206         f = fdopen(fd, "r+");
207
208         if (!f) {
209                 perror("fopen");
210                 close(fd);
211                 unlink(template);
212                 return -1;
213         }
214
215         if (save_ldif(ldb, f, msgs1, count1) != 0) {
216                 return -1;
217         }
218
219         fclose(f);
220
221         asprintf(&cmd, "%s %s", editor, template);
222
223         if (!cmd) {
224                 unlink(template);
225                 fprintf(stderr, "out of memory\n");
226                 return -1;
227         }
228
229         /* run the editor */
230         ret = system(cmd);
231         free(cmd);
232
233         if (ret != 0) {
234                 unlink(template);
235                 fprintf(stderr, "edit with %s failed\n", editor);
236                 return -1;
237         }
238
239         /* read the resulting ldif into msgs2 */
240         f = fopen(template, "r");
241         if (!f) {
242                 perror(template);
243                 return -1;
244         }
245
246         while ((ldif = ldb_ldif_read_file(ldb, f))) {
247                 msgs2 = ldb_realloc_p(ldb, msgs2, struct ldb_message *, count2+1);
248                 if (!msgs2) {
249                         fprintf(stderr, "out of memory");
250                         return -1;
251                 }
252                 msgs2[count2++] = &ldif->msg;
253         }
254
255         fclose(f);
256         unlink(template);
257
258         return merge_edits(ldb, msgs1, count1, msgs2, count2);
259 }
260
261 static void usage(void)
262 {
263         printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
264         printf("Options:\n");
265         printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
266         printf("  -s base|sub|one  choose search scope\n");
267         printf("  -b basedn        choose baseDN\n");
268         printf("  -a               edit all records (expression 'objectclass=*')\n");
269         printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
270         exit(1);
271 }
272
273  int main(int argc, char * const argv[])
274 {
275         struct ldb_context *ldb;
276         struct ldb_message **msgs;
277         int ret;
278         const char *expression = NULL;
279         const char *ldb_url;
280         const char *basedn = NULL;
281         int opt;
282         enum ldb_scope scope = LDB_SCOPE_SUBTREE;
283         const char *editor;
284         const char * const * attrs = NULL;
285
286         ldb_url = getenv("LDB_URL");
287
288         /* build the editor command to run -
289            use the same editor priorities as vipw */
290         editor = getenv("VISUAL");
291         if (!editor) {
292                 editor = getenv("EDITOR");
293         }
294         if (!editor) {
295                 editor = "vi";
296         }
297
298         while ((opt = getopt(argc, argv, "hab:e:H:s:")) != EOF) {
299                 switch (opt) {
300                 case 'b':
301                         basedn = optarg;
302                         break;
303
304                 case 'H':
305                         ldb_url = optarg;
306                         break;
307
308                 case 's':
309                         if (strcmp(optarg, "base") == 0) {
310                                 scope = LDB_SCOPE_BASE;
311                         } else if (strcmp(optarg, "sub") == 0) {
312                                 scope = LDB_SCOPE_SUBTREE;
313                         } else if (strcmp(optarg, "one") == 0) {
314                                 scope = LDB_SCOPE_ONELEVEL;
315                         }
316                         break;
317
318                 case 'e':
319                         editor = optarg;
320                         break;
321
322                 case 'a':
323                         expression = "(|(objectclass=*)(dn=*))";
324                         break;
325                         
326                 case 'h':
327                 default:
328                         usage();
329                         break;
330                 }
331         }
332
333         if (!ldb_url) {
334                 fprintf(stderr, "You must specify a ldb URL\n\n");
335                 usage();
336         }
337
338         argc -= optind;
339         argv += optind;
340
341         if (!expression) {
342                 if (argc == 0) {
343                         usage();
344                 }
345                 expression = argv[0];
346                 argc--;
347                 argv++;
348         }
349
350         if (argc > 0) {
351                 attrs = (const char * const *)argv;
352         }
353
354         ldb = ldb_connect(ldb_url, 0, NULL);
355
356         if (!ldb) {
357                 perror("ldb_connect");
358                 exit(1);
359         }
360
361         ldb_set_debug_stderr(ldb);
362
363         ret = ldb_search(ldb, basedn, scope, expression, attrs, &msgs);
364
365         if (ret == -1) {
366                 printf("search failed - %s\n", ldb_errstring(ldb));
367                 exit(1);
368         }
369
370         if (ret == 0) {
371                 printf("no matching records - cannot edit\n");
372                 return 0;
373         }
374
375         do_edit(ldb, msgs, ret, editor);
376
377         if (ret > 0) {
378                 ret = ldb_search_free(ldb, msgs);
379                 if (ret == -1) {
380                         fprintf(stderr, "search_free failed - %s\n", ldb_errstring(ldb));
381                         exit(1);
382                 }
383         }
384
385         ldb_close(ldb);
386         return 0;
387 }