ldb: detect eof on ldif files
[samba.git] / source4 / lib / ldb / tools / ldbedit.c
index 1a684c5c2db4d0c035efd6ffa33d0aaaa536288c..aaf6d80352d804b8a652f51786799cbd38b4cc89 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    ldb database library
 
    Copyright (C) Andrew Tridgell  2004
@@ -6,7 +6,7 @@
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
      ** under the LGPL
-   
+
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
  *  Author: Andrew Tridgell
  */
 
-#include "ldb_includes.h"
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "ldb.h"
 #include "tools/cmdline.h"
+#include "tools/ldbutil.h"
 
 static struct ldb_cmdline *options;
 
 /*
-  debug routine 
+  debug routine
 */
-static void ldif_write_msg(struct ldb_context *ldb, 
-                          FILE *f, 
+static void ldif_write_msg(struct ldb_context *ldb,
+                          FILE *f,
                           enum ldb_changetype changetype,
                           struct ldb_message *msg)
 {
@@ -54,33 +59,38 @@ static void ldif_write_msg(struct ldb_context *ldb,
   modify a database record so msg1 becomes msg2
   returns the number of modified elements
 */
-static int modify_record(struct ldb_context *ldb, 
+static int modify_record(struct ldb_context *ldb,
                         struct ldb_message *msg1,
-                        struct ldb_message *msg2)
+                        struct ldb_message *msg2,
+                        struct ldb_control **req_ctrls)
 {
+       int ret;
        struct ldb_message *mod;
 
-       mod = ldb_msg_diff(ldb, msg1, msg2);
-       if (mod == NULL) {
+       if (ldb_msg_difference(ldb, ldb, msg1, msg2, &mod) != LDB_SUCCESS) {
                fprintf(stderr, "Failed to calculate message differences\n");
                return -1;
        }
 
-       if (mod->num_elements == 0) {
-               return 0;
+       ret = mod->num_elements;
+       if (ret == 0) {
+               goto done;
        }
 
        if (options->verbose > 0) {
                ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
        }
 
-       if (ldb_modify(ldb, mod) != 0) {
-               fprintf(stderr, "failed to modify %s - %s\n", 
+       if (ldb_modify_ctrl(ldb, mod, req_ctrls) != LDB_SUCCESS) {
+               fprintf(stderr, "failed to modify %s - %s\n",
                        ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb));
-               return -1;
+               ret = -1;
+               goto done;
        }
 
-       return mod->num_elements;
+done:
+       talloc_free(mod);
+       return ret;
 }
 
 /*
@@ -88,10 +98,10 @@ static int modify_record(struct ldb_context *ldb,
 */
 static struct ldb_message *msg_find(struct ldb_context *ldb,
                                    struct ldb_message **msgs,
-                                   int count,
+                                   unsigned int count,
                                    struct ldb_dn *dn)
 {
-       int i;
+       unsigned int i;
        for (i=0;i<count;i++) {
                if (ldb_dn_compare(dn, msgs[i]->dn) == 0) {
                        return msgs[i];
@@ -104,15 +114,20 @@ static struct ldb_message *msg_find(struct ldb_context *ldb,
   merge the changes in msgs2 into the messages from msgs1
 */
 static int merge_edits(struct ldb_context *ldb,
-                      struct ldb_message **msgs1, int count1,
-                      struct ldb_message **msgs2, int count2)
+                      struct ldb_message **msgs1, unsigned int count1,
+                      struct ldb_message **msgs2, unsigned int count2)
 {
-       int i;
+       unsigned int i;
        struct ldb_message *msg;
-       int ret = 0;
-       int adds=0, modifies=0, deletes=0;
+       int ret;
+       unsigned int adds=0, modifies=0, deletes=0;
+       struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
+       if (options->controls != NULL && req_ctrls == NULL) {
+               fprintf(stderr, "parsing controls failed: %s\n", ldb_errstring(ldb));
+               return -1;
+       }
 
-       if (ldb_transaction_start(ldb) != 0) {
+       if (ldb_transaction_start(ldb) != LDB_SUCCESS) {
                fprintf(stderr, "Failed to start transaction: %s\n", ldb_errstring(ldb));
                return -1;
        }
@@ -124,16 +139,20 @@ static int merge_edits(struct ldb_context *ldb,
                        if (options->verbose > 0) {
                                ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
                        }
-                       if (ldb_add(ldb, msgs2[i]) != 0) {
+                       if (ldb_add_ctrl(ldb, msgs2[i], req_ctrls) != LDB_SUCCESS) {
                                fprintf(stderr, "failed to add %s - %s\n",
                                        ldb_dn_get_linearized(msgs2[i]->dn),
                                        ldb_errstring(ldb));
+                               ldb_transaction_cancel(ldb);
                                return -1;
                        }
                        adds++;
                } else {
-                       if (modify_record(ldb, msg, msgs2[i]) > 0) {
-                               modifies++;
+                       ret = modify_record(ldb, msg, msgs2[i], req_ctrls);
+                       if (ret != -1) {
+                               modifies += (unsigned int) ret;
+                       } else {
+                               return -1;
                        }
                }
        }
@@ -145,39 +164,40 @@ static int merge_edits(struct ldb_context *ldb,
                        if (options->verbose > 0) {
                                ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
                        }
-                       if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
+                       if (ldb_delete_ctrl(ldb, msgs1[i]->dn, req_ctrls) != LDB_SUCCESS) {
                                fprintf(stderr, "failed to delete %s - %s\n",
                                        ldb_dn_get_linearized(msgs1[i]->dn),
                                        ldb_errstring(ldb));
+                               ldb_transaction_cancel(ldb);
                                return -1;
                        }
                        deletes++;
                }
        }
 
-       if (ldb_transaction_commit(ldb) != 0) {
+       if (ldb_transaction_commit(ldb) != LDB_SUCCESS) {
                fprintf(stderr, "Failed to commit transaction: %s\n", ldb_errstring(ldb));
                return -1;
        }
 
-       printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
+       printf("# %u adds  %u modifies  %u deletes\n", adds, modifies, deletes);
 
-       return ret;
+       return 0;
 }
 
 /*
   save a set of messages as ldif to a file
 */
-static int save_ldif(struct ldb_context *ldb, 
-                    FILE *f, struct ldb_message **msgs, int count)
+static int save_ldif(struct ldb_context *ldb,
+                    FILE *f, struct ldb_message **msgs, unsigned int count)
 {
-       int i;
+       unsigned int i;
 
-       fprintf(f, "# editing %d records\n", count);
+       fprintf(f, "# editing %u records\n", count);
 
        for (i=0;i<count;i++) {
                struct ldb_ldif ldif;
-               fprintf(f, "# record %d\n", i+1);
+               fprintf(f, "# record %u\n", i+1);
 
                ldif.changetype = LDB_CHANGETYPE_NONE;
                ldif.msg = msgs[i];
@@ -192,8 +212,8 @@ static int save_ldif(struct ldb_context *ldb,
 /*
   edit the ldb search results in msgs using the user selected editor
 */
-static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
-                  const char *editor)
+static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1,
+                  unsigned int count1, const char *editor)
 {
        int fd, ret;
        FILE *f;
@@ -201,7 +221,7 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
        char *cmd;
        struct ldb_ldif *ldif;
        struct ldb_message **msgs2 = NULL;
-       int count2 = 0;
+       unsigned int count2 = 0;
 
        /* write out the original set of messages to a temporary
           file */
@@ -261,23 +281,32 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
                msgs2[count2++] = ldif->msg;
        }
 
+       /* the feof() test works here, even for the last line of the
+        * file, as we parse ldif files character by character, and
+        * feof() is only true if we have failed to read a character
+        * from the file. So if the last line is bad, we don't get
+        * feof() set, so we know the record was bad. Only if we
+        * attempt to go to the next record will we get feof() and
+        * thus consider that the ldif has ended without errors
+        */
+       if (!feof(f)) {
+               fprintf(stderr, "Error parsing ldif - aborting\n");
+               fclose(f);
+               unlink(file_template);
+               return -1;
+       }
+
        fclose(f);
        unlink(file_template);
 
        return merge_edits(ldb, msgs1, count1, msgs2, count2);
 }
 
-static void usage(void)
+static void usage(struct ldb_context *ldb)
 {
        printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
-       printf("Options:\n");
-       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
-       printf("  -s base|sub|one  choose search scope\n");
-       printf("  -b basedn        choose baseDN\n");
-       printf("  -a               edit all records (expression 'objectclass=*')\n");
-       printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
-       printf("  -v               verbose mode\n");
-       exit(1);
+       ldb_cmdline_help(ldb, "ldbedit", stdout);
+       exit(LDB_ERR_OPERATIONS_ERROR);
 }
 
 int main(int argc, const char **argv)
@@ -288,13 +317,18 @@ int main(int argc, const char **argv)
        int ret;
        const char *expression = "(|(objectClass=*)(distinguishedName=*))";
        const char * const * attrs = NULL;
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       struct ldb_control **req_ctrls;
 
-       ldb = ldb_init(NULL, NULL);
+       ldb = ldb_init(mem_ctx, NULL);
+       if (ldb == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        options = ldb_cmdline_process(ldb, argc, argv, usage);
 
        /* the check for '=' is for compatibility with ldapsearch */
-       if (options->argc > 0 && 
+       if (options->argc > 0 &&
            strchr(options->argv[0], '=')) {
                expression = options->argv[0];
                options->argv++;
@@ -307,33 +341,32 @@ int main(int argc, const char **argv)
 
        if (options->basedn != NULL) {
                basedn = ldb_dn_new(ldb, ldb, options->basedn);
-               if ( ! ldb_dn_validate(basedn)) {
-                       printf("Invalid Base DN format\n");
-                       exit(1);
+               if (basedn == NULL) {
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
        }
 
-       ret = ldb_search(ldb, ldb, &result, basedn, options->scope, attrs, "%s", expression);
+       req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
+       if (options->controls != NULL &&  req_ctrls== NULL) {
+               printf("parsing controls failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldb_search_ctrl(ldb, ldb, &result, basedn, options->scope, attrs, req_ctrls, "%s", expression);
        if (ret != LDB_SUCCESS) {
                printf("search failed - %s\n", ldb_errstring(ldb));
-               exit(1);
+               return ret;
        }
 
        if (result->count == 0) {
                printf("no matching records - cannot edit\n");
-               return 0;
+               talloc_free(mem_ctx);
+               return LDB_SUCCESS;
        }
 
-       do_edit(ldb, result->msgs, result->count, options->editor);
+       ret = do_edit(ldb, result->msgs, result->count, options->editor);
 
-       if (result) {
-               ret = talloc_free(result);
-               if (ret == -1) {
-                       fprintf(stderr, "talloc_free failed\n");
-                       exit(1);
-               }
-       }
+       talloc_free(mem_ctx);
 
-       talloc_free(ldb);
-       return 0;
+       return ret == 0 ? LDB_SUCCESS : LDB_ERR_OPERATIONS_ERROR;
 }