show-deleted: Do not indicate an error if an object is missing.
[vlendec/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / show_deleted.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2005
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
7    Copyright (C) Matthias Dieter Wallnöfer 2010
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24  *  Name: ldb
25  *
26  *  Component: ldb deleted objects control module
27  *
28  *  Description: this module hides deleted and recylced objects, and returns
29  *  them if the right control is there
30  *
31  *  Author: Stefan Metzmacher
32  */
33
34 #include "includes.h"
35 #include <ldb_module.h>
36 #include "dsdb/samdb/samdb.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
38
39 struct show_deleted_state {
40         bool need_refresh;
41         bool recycle_bin_enabled;
42 };
43
44 static int show_deleted_search(struct ldb_module *module, struct ldb_request *req)
45 {
46         struct ldb_context *ldb;
47         struct ldb_control *show_del, *show_rec;
48         struct ldb_request *down_req;
49         struct ldb_parse_tree *new_tree = req->op.search.tree;
50         struct show_deleted_state *state;
51         int ret;
52         const char *attr_filter = NULL;
53
54         /* do not manipulate our control entries */
55         if (ldb_dn_is_special(req->op.search.base)) {
56                 return ldb_next_request(module, req);
57         }
58
59         ldb = ldb_module_get_ctx(module);
60
61         state = talloc_get_type(ldb_module_get_private(module), struct show_deleted_state);
62
63         /* note that state may be NULL during initialisation */
64         if (state != NULL && state->need_refresh) {
65                 /* Do not move this assignment, it can cause recursion loops! */
66                 state->need_refresh = false;
67                 ret = dsdb_recyclebin_enabled(module, &state->recycle_bin_enabled);
68                 if (ret != LDB_SUCCESS) {
69                         state->recycle_bin_enabled = false;
70                         /*
71                          * We can fail to find the feature object
72                          * during provision. Ignore any such error and
73                          * assume the recycle bin cannot be enabled at
74                          * this point in time.
75                          */
76                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
77                                 state->need_refresh = true;
78                                 return LDB_ERR_UNWILLING_TO_PERFORM;
79                         }
80                 }
81         }
82
83         /* This is the logic from MS-ADTS 3.1.1.3.4.1.14 that
84            determines if objects are visible
85
86            Extended control name                     Deleted-objects      Tombstones        Recycled-objects
87            LDAP_SERVER_SHOW_DELETED_OID              Visible              Visible           Not Visible
88            LDAP_SERVER_SHOW_RECYCLED_OID             Visible              Visible           Visible
89
90            Note that if the recycle bin is disabled, then the
91            isRecycled attribute is ignored, and objects are either
92            "normal" or "tombstone".
93
94            When the recycle bin is enabled, then objects are in one of
95            3 states, "normal", "deleted" or "recycled"
96         */
97
98         /* check if there's a show deleted control */
99         show_del = ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID);
100         /* check if there's a show recycled control */
101         show_rec = ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID);
102
103
104         if (state == NULL || !state->recycle_bin_enabled) {
105                 /* when recycle bin is not enabled, then all we look
106                    at is the isDeleted attribute. We hide objects with this
107                    attribute set to TRUE when the client has not specified either
108                    SHOW_DELETED or SHOW_RECYCLED
109                 */
110                 if (show_del != NULL || show_rec != NULL) {
111                         attr_filter = NULL;
112                 } else {
113                         attr_filter = "isDeleted";
114                 }
115         } else {
116                 /* the recycle bin is enabled
117                  */
118                 if (show_rec != NULL) {
119                         attr_filter = NULL;
120                 } else if (show_del != NULL) {
121                         /* we want deleted but not recycled objects */
122                         attr_filter = "isRecycled";
123                 } else {
124                         /* we don't want deleted or recycled objects,
125                          * which we get by filtering on isDeleted */
126                         attr_filter = "isDeleted";
127                 }
128         }
129
130
131         if (attr_filter != NULL) {
132                 new_tree = talloc(req, struct ldb_parse_tree);
133                 if (!new_tree) {
134                         return ldb_oom(ldb);
135                 }
136                 new_tree->operation = LDB_OP_AND;
137                 new_tree->u.list.num_elements = 2;
138                 new_tree->u.list.elements = talloc_array(new_tree, struct ldb_parse_tree *, 2);
139                 if (!new_tree->u.list.elements) {
140                         return ldb_oom(ldb);
141                 }
142
143                 new_tree->u.list.elements[0] = talloc(new_tree->u.list.elements, struct ldb_parse_tree);
144                 new_tree->u.list.elements[0]->operation = LDB_OP_NOT;
145                 new_tree->u.list.elements[0]->u.isnot.child =
146                         talloc(new_tree->u.list.elements, struct ldb_parse_tree);
147                 if (!new_tree->u.list.elements[0]->u.isnot.child) {
148                         return ldb_oom(ldb);
149                 }
150                 new_tree->u.list.elements[0]->u.isnot.child->operation = LDB_OP_EQUALITY;
151                 new_tree->u.list.elements[0]->u.isnot.child->u.equality.attr = attr_filter;
152                 new_tree->u.list.elements[0]->u.isnot.child->u.equality.value = data_blob_string_const("TRUE");
153                 new_tree->u.list.elements[1] = req->op.search.tree;
154         }
155
156         ret = ldb_build_search_req_ex(&down_req, ldb, req,
157                                       req->op.search.base,
158                                       req->op.search.scope,
159                                       new_tree,
160                                       req->op.search.attrs,
161                                       req->controls,
162                                       req, dsdb_next_callback,
163                                       req);
164         LDB_REQ_SET_LOCATION(down_req);
165         if (ret != LDB_SUCCESS) {
166                 return ret;
167         }
168
169         /* mark the controls as done */
170         if (show_del != NULL) {
171                 show_del->critical = 0;
172         }
173         if (show_rec != NULL) {
174                 show_rec->critical = 0;
175         }
176
177         /* perform the search */
178         return ldb_next_request(module, down_req);
179 }
180
181 static int show_deleted_init(struct ldb_module *module)
182 {
183         struct ldb_context *ldb;
184         int ret;
185         struct show_deleted_state *state;
186
187         state = talloc_zero(module, struct show_deleted_state);
188         if (state == NULL) {
189                 return ldb_module_oom(module);
190         }
191         state->need_refresh = true;
192
193         ldb = ldb_module_get_ctx(module);
194
195         ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_DELETED_OID);
196         if (ret != LDB_SUCCESS) {
197                 ldb_debug(ldb, LDB_DEBUG_ERROR,
198                         "show_deleted: Unable to register control with rootdse!\n");
199                 return ldb_operr(ldb);
200         }
201
202         ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_RECYCLED_OID);
203         if (ret != LDB_SUCCESS) {
204                 ldb_debug(ldb, LDB_DEBUG_ERROR,
205                         "show_deleted: Unable to register control with rootdse!\n");
206                 return ldb_operr(ldb);
207         }
208
209         ret = ldb_next_init(module);
210
211         ldb_module_set_private(module, state);
212
213         return ret;
214 }
215
216 static const struct ldb_module_ops ldb_show_deleted_module_ops = {
217         .name              = "show_deleted",
218         .search            = show_deleted_search,
219         .init_context      = show_deleted_init
220 };
221
222 int ldb_show_deleted_module_init(const char *version)
223 {
224         LDB_MODULE_CHECK_VERSION(version);
225         return ldb_register_module(&ldb_show_deleted_module_ops);
226 }