r9391: Convert all the code to use struct ldb_dn to ohandle ldap like distinguished...
[kai/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 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_private.h"
38 #include "ldb/tools/cmdline.h"
39
40 #ifdef _SAMBA_BUILD_
41 #include "system/filesys.h"
42 #endif
43
44 static struct ldb_cmdline *options;
45
46 /*
47   debug routine 
48 */
49 static void ldif_write_msg(struct ldb_context *ldb, 
50                            FILE *f, 
51                            enum ldb_changetype changetype,
52                            struct ldb_message *msg)
53 {
54         struct ldb_ldif ldif;
55         ldif.changetype = changetype;
56         ldif.msg = msg;
57         ldb_ldif_write_file(ldb, f, &ldif);
58 }
59
60 /*
61   modify a database record so msg1 becomes msg2
62   returns the number of modified elements
63 */
64 static int modify_record(struct ldb_context *ldb, 
65                          struct ldb_message *msg1,
66                          struct ldb_message *msg2)
67 {
68         struct ldb_message *mod;
69
70         mod = ldb_msg_diff(ldb, msg1, msg2);
71         if (mod == NULL) {
72                 fprintf(stderr, "Failed to calculate message differences\n");
73                 return -1;
74         }
75
76         if (mod->num_elements == 0) {
77                 return 0;
78         }
79
80         if (options->verbose > 0) {
81                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
82         }
83
84         if (ldb_modify(ldb, mod) != 0) {
85                 fprintf(stderr, "failed to modify %s - %s\n", 
86                         ldb_dn_linearize(ldb, msg1->dn), ldb_errstring(ldb));
87                 return -1;
88         }
89
90         return mod->num_elements;
91 }
92
93 /*
94   find dn in msgs[]
95 */
96 static struct ldb_message *msg_find(struct ldb_context *ldb,
97                                     struct ldb_message **msgs,
98                                     int count,
99                                     const struct ldb_dn *dn)
100 {
101         int i;
102         for (i=0;i<count;i++) {
103                 if (ldb_dn_compare(ldb, dn, msgs[i]->dn) == 0) {
104                         return msgs[i];
105                 }
106         }
107         return NULL;
108 }
109
110 /*
111   merge the changes in msgs2 into the messages from msgs1
112 */
113 static int merge_edits(struct ldb_context *ldb,
114                        struct ldb_message **msgs1, int count1,
115                        struct ldb_message **msgs2, int count2)
116 {
117         int i;
118         struct ldb_message *msg;
119         int ret = 0;
120         int adds=0, modifies=0, deletes=0;
121
122         /* do the adds and modifies */
123         for (i=0;i<count2;i++) {
124                 msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
125                 if (!msg) {
126                         if (options->verbose > 0) {
127                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
128                         }
129                         if (ldb_add(ldb, msgs2[i]) != 0) {
130                                 fprintf(stderr, "failed to add %s - %s\n",
131                                         ldb_dn_linearize(ldb, msgs2[i]->dn),
132                                         ldb_errstring(ldb));
133                                 return -1;
134                         }
135                         adds++;
136                 } else {
137                         if (modify_record(ldb, msg, msgs2[i]) > 0) {
138                                 modifies++;
139                         }
140                 }
141         }
142
143         /* do the deletes */
144         for (i=0;i<count1;i++) {
145                 msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
146                 if (!msg) {
147                         if (options->verbose > 0) {
148                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
149                         }
150                         if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
151                                 fprintf(stderr, "failed to delete %s - %s\n",
152                                         ldb_dn_linearize(ldb, msgs1[i]->dn),
153                                         ldb_errstring(ldb));
154                                 return -1;
155                         }
156                         deletes++;
157                 }
158         }
159
160         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
161
162         return ret;
163 }
164
165 /*
166   save a set of messages as ldif to a file
167 */
168 static int save_ldif(struct ldb_context *ldb, 
169                      FILE *f, struct ldb_message **msgs, int count)
170 {
171         int i;
172
173         fprintf(f, "# editing %d records\n", count);
174
175         for (i=0;i<count;i++) {
176                 struct ldb_ldif ldif;
177                 fprintf(f, "# record %d\n", i+1);
178
179                 ldif.changetype = LDB_CHANGETYPE_NONE;
180                 ldif.msg = msgs[i];
181
182                 ldb_ldif_write_file(ldb, f, &ldif);
183         }
184
185         return 0;
186 }
187
188
189 /*
190   edit the ldb search results in msgs using the user selected editor
191 */
192 static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
193                    const char *editor)
194 {
195         int fd, ret;
196         FILE *f;
197         char template[] = "/tmp/ldbedit.XXXXXX";
198         char *cmd;
199         struct ldb_ldif *ldif;
200         struct ldb_message **msgs2 = NULL;
201         int count2 = 0;
202
203         /* write out the original set of messages to a temporary
204            file */
205         fd = mkstemp(template);
206
207         if (fd == -1) {
208                 perror(template);
209                 return -1;
210         }
211
212         f = fdopen(fd, "r+");
213
214         if (!f) {
215                 perror("fopen");
216                 close(fd);
217                 unlink(template);
218                 return -1;
219         }
220
221         if (save_ldif(ldb, f, msgs1, count1) != 0) {
222                 return -1;
223         }
224
225         fclose(f);
226
227         asprintf(&cmd, "%s %s", editor, template);
228
229         if (!cmd) {
230                 unlink(template);
231                 fprintf(stderr, "out of memory\n");
232                 return -1;
233         }
234
235         /* run the editor */
236         ret = system(cmd);
237         free(cmd);
238
239         if (ret != 0) {
240                 unlink(template);
241                 fprintf(stderr, "edit with %s failed\n", editor);
242                 return -1;
243         }
244
245         /* read the resulting ldif into msgs2 */
246         f = fopen(template, "r");
247         if (!f) {
248                 perror(template);
249                 return -1;
250         }
251
252         while ((ldif = ldb_ldif_read_file(ldb, f))) {
253                 msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
254                 if (!msgs2) {
255                         fprintf(stderr, "out of memory");
256                         return -1;
257                 }
258                 msgs2[count2++] = ldif->msg;
259         }
260
261         fclose(f);
262         unlink(template);
263
264         return merge_edits(ldb, msgs1, count1, msgs2, count2);
265 }
266
267 static void usage(void)
268 {
269         printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
270         printf("Options:\n");
271         printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
272         printf("  -s base|sub|one  choose search scope\n");
273         printf("  -b basedn        choose baseDN\n");
274         printf("  -a               edit all records (expression 'objectclass=*')\n");
275         printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
276         printf("  -v               verbose mode)\n");
277         exit(1);
278 }
279
280  int main(int argc, const char **argv)
281 {
282         struct ldb_context *ldb;
283         struct ldb_message **msgs;
284         struct ldb_dn *basedn = NULL;
285         int ret;
286         const char *expression = "(|(objectclass=*)(dn=*))";
287         const char * const * attrs = NULL;
288
289         ldb = ldb_init(NULL);
290
291         options = ldb_cmdline_process(ldb, argc, argv, usage);
292
293         /* the check for '=' is for compatibility with ldapsearch */
294         if (options->argc > 0 && 
295             strchr(options->argv[0], '=')) {
296                 expression = options->argv[0];
297                 options->argv++;
298                 options->argc--;
299         }
300
301         if (options->argc > 0) {
302                 attrs = (const char * const *)(options->argv);
303         }
304
305         if (options->basedn != NULL) {
306                 basedn = ldb_dn_explode(ldb, options->basedn);
307                 if (basedn == NULL) {
308                         printf("Invalid Base DN format\n");
309                         exit(1);
310                 }
311         }
312
313         ret = ldb_search(ldb, basedn, options->scope, expression, attrs, &msgs);
314         if (ret == -1) {
315                 printf("search failed - %s\n", ldb_errstring(ldb));
316                 exit(1);
317         }
318
319         if (ret == 0) {
320                 printf("no matching records - cannot edit\n");
321                 return 0;
322         }
323
324         do_edit(ldb, msgs, ret, options->editor);
325
326         if (ret > 0) {
327                 ret = talloc_free(msgs);
328                 if (ret == -1) {
329                         fprintf(stderr, "talloc_free failed\n");
330                         exit(1);
331                 }
332         }
333
334         talloc_free(ldb);
335         return 0;
336 }