Part 2: adding the actual delete command.

This still misses the documentation, I'll send that later.

diff --git a/aldap.c b/aldap.c
index 39ced8f..a460f9a 100644
--- a/aldap.c
+++ b/aldap.c
@@ -247,6 +247,34 @@ fail:
        return (-1);
 }
 
+int
+aldap_delete(struct aldap *ldap, const char *dn)
+{
+       struct ber_element *root = NULL, *elm;
+
+       if ((root = ber_add_sequence(NULL)) == NULL)
+               goto fail;
+
+       elm = ber_printf_elements(root, "dst", ++ldap->msgid, dn, BER_CLASS_APP,
+           LDAP_REQ_DELETE_30);
+
+       if (elm == NULL)
+               goto fail;
+
+       LDAP_DEBUG("aldap_delete", root);
+
+       if (aldap_send(ldap, root) == -1) {
+               root = NULL;
+               goto fail;
+       }
+       return (ldap->msgid);
+fail:
+       ber_free_elements(root);
+
+       ldap->err = ALDAP_ERR_OPERATION_FAILED;
+       return (-1);
+}
+
 int
 aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
     char **attrs, int typesonly, int sizelimit, int timelimit,
@@ -449,7 +477,7 @@ parsefail:
 }
 
 struct aldap_page_control *
-aldap_parse_page_control(struct ber_element *control, size_t len) 
+aldap_parse_page_control(struct ber_element *control, size_t len)
 {
        char *oid, *s;
        char *encoded;
diff --git a/aldap.h b/aldap.h
index 81fe770..1ac86ec 100644
--- a/aldap.h
+++ b/aldap.h
@@ -225,6 +225,7 @@ int                  aldap_req_starttls(struct aldap *);
 int     aldap_bind(struct aldap *, char *, char *);
 int     aldap_unbind(struct aldap *);
 int     aldap_search(struct aldap *, char *, enum scope, char *, char **, int, 
int, int, struct aldap_page_control *);
+int     aldap_delete(struct aldap *, const char *);
 int     aldap_get_errno(struct aldap *, const char **);
 
 int     aldap_get_resultcode(struct aldap_message *);
diff --git a/ldapclient.c b/ldapclient.c
index 9f29997..503bcd1 100644
--- a/ldapclient.c
+++ b/ldapclient.c
@@ -81,6 +81,12 @@ struct ldapc_search {
        char                    **ls_attr;
 };
 
+struct ldapc_delete {
+       char                    *ld_file;
+       int                      ld_recursive;
+       int                      ld_sizelimit;
+};
+
 struct ldapc_app {
        const char              *name;
        const char              *optstring;
@@ -90,12 +96,16 @@ struct ldapc_app {
        struct ldapc             ldap;
        union {
                struct ldapc_search ls;
+               struct ldapc_delete ld;
        };
 };
 
 __dead void     usage(void);
 int             ldapc_connect(struct ldapc *);
 int             ldapc_search(int, char *[]);
+int             ldapc_delete(int, char *[]);
+int             ldapc_do_delete(char *);
+int             ldapc_children(char *, char **[]);
 int             ldapc_printattr(struct ldapc *, const char *,
                    const struct ber_octetstring *);
 void            ldapc_disconnect(struct ldapc *);
@@ -107,7 +117,9 @@ const char  *url_decode(char *);
 struct ldapc_app ldapc_apps[] = {
        {"search", "Lb:s:l:z:", "[-L] [-b basedn] [-s scope] [-l timelimit] "
            "[-z sizelimit] [filter] [attributes ...]", "stdio", ldapc_search,
-           {0}, {0}}
+           {0}, {0}},
+       {"delete", "f:rz:", "[-r] [-f file] [-z sizelimit]", "rpath stdio",
+           ldapc_delete, {0}, {0}}
 };
 struct ldapc_app *ldapc_app = NULL;
 
@@ -167,6 +179,12 @@ main(int argc, char *argv[])
                        ldapc_app->ldap.ldap_binddn = optarg;
                        ldapc_app->ldap.ldap_flags |= F_NEEDAUTH;
                        break;
+               case 'f':
+                       if (strcmp(ldapc_app->name, "delete") == 0) {
+                               ldapc_app->ld.ld_file = optarg;
+                               break;
+                       }
+                       usage();
                case 'H':
                        url = optarg;
                        break;
@@ -185,6 +203,12 @@ main(int argc, char *argv[])
                                break;
                        }
                        usage();
+               case 'r':
+                       if (strcmp(ldapc_app->name, "delete") == 0) {
+                               ldapc_app->ld.ld_recursive = 1;
+                               break;
+                       }
+                       usage();
                case 's':
                        if (strcmp(ldapc_app->name, "search") == 0) {
                                if (strcasecmp("base", optarg) == 0)
@@ -228,6 +252,12 @@ main(int argc, char *argv[])
                                if (errstr != NULL)
                                        errx(1, "sizelimit %s", errstr);
                                break;
+                       } else if (strcmp(ldapc_app->name, "delete") == 0) {
+                               ldapc_app->ld.ld_sizelimit = strtonum(optarg, 0,
+                                   INT_MAX, &errstr);
+                               if (errstr != NULL)
+                                       errx(1, "sizelimit %s", errstr);
+                               break;
                        }
                        usage();
                default:
@@ -319,6 +349,190 @@ main(int argc, char *argv[])
        return (0);
 }
 
+int
+ldapc_delete(int argc, char *argv[])
+{
+       FILE                            *file;
+       struct ldapc                    *ldap;
+       struct ldapc_delete             *ld;
+       int                              i;
+       ssize_t                          dnlen;
+       size_t                           dnsize = 0;
+       char                            *dn = NULL;
+
+       ldap = &ldapc_app->ldap;
+       ld = &ldapc_app->ld;
+
+       if (ld->ld_file != NULL) {
+               if ((file = fopen(ld->ld_file, "r")) == NULL) {
+                       log_warn("Failed to open %s", ld->ld_file);
+                       goto fail;
+               }
+               if (pledge("stdio", NULL) == -1)
+                       err(1, "pledge");
+
+               while ((dnlen = getline(&dn, &dnsize, file)) != -1) {
+                       if (dn[dnlen - 1] == '\n')
+                               dn[dnlen - 1] = '\0';
+                       if (ldapc_do_delete(dn) == -1)
+                               goto fail;
+               }
+               if (ferror(file)) {
+                       log_warn("Error reading file %s", ld->ld_file);
+                       goto fail;
+               }
+               fclose(file);
+       }
+       for (i = 0; i < argc; i++) {
+               if (ldapc_do_delete(argv[i]) == -1)
+                       goto fail;
+       }
+
+       return (0);
+fail:
+       ldapc_disconnect(ldap);
+
+       return (-1);
+}
+
+int
+ldapc_do_delete(char *dn)
+{
+       struct ldapc            *ldap;
+       struct ldapc_delete     *ld;
+       struct aldap_message    *m;
+       const char              *errstr;
+       char                    **children = NULL;
+       int                      nchildren = 0;
+       int                      code;
+
+       ldap = &ldapc_app->ldap;
+       ld = &ldapc_app->ld;
+
+       if (aldap_delete(ldap->ldap_al, dn) == -1) {
+               aldap_get_errno(ldap->ldap_al, &errstr);
+               log_warnx("LDAP delete failed: %s", errstr);
+               return (-1);
+       }
+       if ((m = aldap_parse(ldap->ldap_al)) == NULL)
+               return (-1);
+
+       if (ldap->ldap_al->msgid != m->msgid) {
+               log_warnx("LDAP delete unexpected message id: %d (expected %d)",
+                   m->msgid, ldap->ldap_al->msgid);
+               goto fail;
+       }
+       if (m->message_type != LDAP_RES_DELETE) {
+               log_warnx("LDAP delete unexpected message "
+                   "type: %d (expected %d)", m->message_type,
+                   LDAP_RES_DELETE);
+               goto fail;
+       }
+       code = aldap_get_resultcode(m);
+       if (code == LDAP_NOT_ALLOWED_ON_NONLEAF && ld->ld_recursive) {
+               log_debug("deleting children of %s", dn);
+               nchildren = ldapc_children(dn, &children);
+               if (nchildren == 0)
+                       log_warnx("Unexpected empty set of children for %s",
+                           dn);
+               if (nchildren < 1)
+                       goto fail;
+               while (nchildren > 0) {
+                       if (ldapc_do_delete(children[nchildren - 1]) == -1)
+                               goto fail;
+                       free(children[--nchildren]);
+               }
+               free(children);
+               children = NULL;
+               return ldapc_do_delete(dn);
+       }
+       if (code != LDAP_SUCCESS) {
+               log_warnx("LDAP delete failed: %s(%d)",
+                   ldapc_resultcode(code), code);
+               goto fail;
+       }
+       log_debug("deleted entry %s", dn);
+       aldap_freemsg(m);
+       return (0);
+fail:
+       while (nchildren > 0)
+               free(children[--nchildren]);
+       free(children);
+       aldap_freemsg(m);
+       return (-1);
+}
+
+int
+ldapc_children(char *dn, char *(*children[]))
+{
+       struct ldapc                    *ldap;
+       struct ldapc_delete             *ld;
+       struct aldap_page_control       *pg = NULL;
+       struct aldap_message            *m;
+       char                            *noattr[] = {"1.1", NULL};
+       const char                      *errstr;
+       char                            **tchildren;
+       int                              nchildren = 0;
+       int                              childrenlen = 0;
+       int                              code;
+
+       ldap = &ldapc_app->ldap;
+       ld = &ldapc_app->ld;
+       *children = NULL;
+
+       do {
+               if (aldap_search(ldap->ldap_al, dn, LDAP_SCOPE_ONELEVEL,
+                   "(objectClass=*)", noattr, 0, ld->ld_sizelimit, 0,
+                   pg) == -1) {
+                       aldap_get_errno(ldap->ldap_al, &errstr);
+                       log_warnx("LDAP delete failed: %s", errstr);
+                       return (-1);
+               }
+               if (pg != NULL) {
+                       aldap_freepage(pg);
+                       pg = NULL;
+               }
+
+               while ((m = aldap_parse(ldap->ldap_al)) != NULL) {
+                       if (ldap->ldap_al->msgid != m->msgid)
+                               goto fail;
+                       if ((code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
+                               log_warnx("LDAP delete failed: %s(%d)",
+                                   ldapc_resultcode(code), code);
+                               goto fail;
+                       }
+                       if (m->message_type == LDAP_RES_SEARCH_RESULT) {
+                               if (m->page != NULL && m->page->cookie_len != 0)
+                                       pg = m->page;
+                               aldap_freemsg(m);
+                               break;
+                       }
+                       if (m->message_type != LDAP_RES_SEARCH_ENTRY)
+                               goto fail;
+                       if (childrenlen <= nchildren) {
+                               childrenlen += 50;
+                               tchildren = realloc(*children, childrenlen);
+                               if (tchildren == NULL)
+                                       goto fail;
+                               *children = tchildren;
+                       }
+                       (*children)[nchildren] = strdup(aldap_get_dn(m));
+                       if ((*children)[nchildren] == NULL)
+                               goto fail;
+                       nchildren++;
+                       aldap_freemsg(m);
+               }
+       } while (pg != NULL);
+
+       return nchildren;
+fail:
+       aldap_freemsg(m);
+       while (nchildren > 0)
+               free((*children)[--nchildren]);
+       free(*children);
+       return (-1);
+}
+
 int
 ldapc_search(int argc, char *argv[])
 {

Reply via email to