r8037: a fairly major update to the internals of ldb. Changes are:
[nivanova/samba-autobuild/.git] / source4 / lib / ldb / common / ldb_match.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004-2005
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: ldb expression matching
29  *
30  *  Description: ldb expression matching 
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
39
40 /*
41   check if the scope matches in a search result
42 */
43 static int ldb_match_scope(const char *dn, const char *base, enum ldb_scope scope)
44 {
45         size_t dn_len, base_len;
46
47         if (base == NULL) {
48                 return 1;
49         }
50
51         base_len = strlen(base);
52         dn_len = strlen(dn);
53
54         if (scope != LDB_SCOPE_ONELEVEL && ldb_dn_cmp(dn, base) == 0) {
55                 return 1;
56         }
57
58         if (base_len+1 >= dn_len) {
59                 return 0;
60         }
61
62         switch (scope) {
63         case LDB_SCOPE_BASE:
64                 break;
65
66         case LDB_SCOPE_ONELEVEL:
67                 if (ldb_dn_cmp(dn + (dn_len - base_len), base) == 0 &&
68                     dn[dn_len - base_len - 1] == ',' &&
69                     strchr(dn, ',') == &dn[dn_len - base_len - 1]) {
70                         return 1;
71                 }
72                 break;
73                 
74         case LDB_SCOPE_SUBTREE:
75         default:
76                 if (ldb_dn_cmp(dn + (dn_len - base_len), base) == 0 &&
77                     dn[dn_len - base_len - 1] == ',') {
78                         return 1;
79                 }
80                 break;
81         }
82
83         return 0;
84 }
85
86
87 /*
88   match a leaf node
89 */
90 static int ldb_match_leaf(struct ldb_context *ldb, 
91                           struct ldb_message *msg,
92                           struct ldb_parse_tree *tree,
93                           const char *base,
94                           enum ldb_scope scope)
95 {
96         unsigned int i;
97         struct ldb_message_element *el;
98         const struct ldb_attrib_handler *h;
99
100         if (!ldb_match_scope(msg->dn, base, scope)) {
101                 return 0;
102         }
103
104         if (ldb_attr_cmp(tree->u.simple.attr, "dn") == 0) {
105                 if (strcmp(tree->u.simple.value.data, "*") == 0) {
106                         return 1;
107                 }
108                 return ldb_dn_cmp(msg->dn, tree->u.simple.value.data) == 0;
109         }
110
111         el = ldb_msg_find_element(msg, tree->u.simple.attr);
112         if (el == NULL) {
113                 return 0;
114         }
115
116         if (strcmp(tree->u.simple.value.data, "*") == 0) {
117                 return 1;
118         }
119
120         h = ldb_attrib_handler(ldb, el->name);
121
122         for (i=0;i<el->num_values;i++) {
123                 if (h->comparison_fn(ldb, &tree->u.simple.value, 
124                                      &el->values[i]) == 0) {
125                         return 1;
126                 }
127         }
128
129         return 0;
130 }
131
132
133 /*
134   bitwise-and comparator
135 */
136 static int ldb_comparator_and(struct ldb_val *v1, struct ldb_val *v2)
137 {
138         uint64_t i1, i2;
139         i1 = strtoull(v1->data, NULL, 0);
140         i2 = strtoull(v2->data, NULL, 0);
141         return ((i1 & i2) == i2);
142 }
143
144 /*
145   bitwise-or comparator
146 */
147 static int ldb_comparator_or(struct ldb_val *v1, struct ldb_val *v2)
148 {
149         uint64_t i1, i2;
150         i1 = strtoull(v1->data, NULL, 0);
151         i2 = strtoull(v2->data, NULL, 0);
152         return ((i1 & i2) != 0);
153 }
154
155
156 /*
157   extended match, handles things like bitops
158 */
159 static int ldb_match_extended(struct ldb_context *ldb, 
160                               struct ldb_message *msg,
161                               struct ldb_parse_tree *tree,
162                               const char *base,
163                               enum ldb_scope scope)
164 {
165         int i;
166         const struct {
167                 const char *oid;
168                 int (*comparator)(struct ldb_val *, struct ldb_val *);
169         } rules[] = {
170                 { LDB_OID_COMPARATOR_AND, ldb_comparator_and},
171                 { LDB_OID_COMPARATOR_OR, ldb_comparator_or}
172         };
173         int (*comp)(struct ldb_val *, struct ldb_val *) = NULL;
174         struct ldb_message_element *el;
175
176         if (tree->u.extended.dnAttributes) {
177                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
178                 return -1;
179         }
180         if (tree->u.extended.rule_id == NULL) {
181                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
182                 return -1;
183         }
184         if (tree->u.extended.attr == NULL) {
185                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
186                 return -1;
187         }
188
189         for (i=0;i<ARRAY_SIZE(rules);i++) {
190                 if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
191                         comp = rules[i].comparator;
192                         break;
193                 }
194         }
195         if (comp == NULL) {
196                 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
197                           tree->u.extended.rule_id);
198                 return -1;
199         }
200
201         /* find the message element */
202         el = ldb_msg_find_element(msg, tree->u.extended.attr);
203         if (el == NULL) {
204                 return 0;
205         }
206
207         for (i=0;i<el->num_values;i++) {
208                 int ret = comp(&el->values[i], &tree->u.extended.value);
209                 if (ret == -1 || ret == 1) return ret;
210         }
211
212         return 0;
213 }
214
215 /*
216   return 0 if the given parse tree matches the given message. Assumes
217   the message is in sorted order
218
219   return 1 if it matches, and 0 if it doesn't match
220
221   this is a recursive function, and does short-circuit evaluation
222  */
223 int ldb_match_message(struct ldb_context *ldb, 
224                       struct ldb_message *msg,
225                       struct ldb_parse_tree *tree,
226                       const char *base,
227                       enum ldb_scope scope)
228 {
229         unsigned int i;
230         int v;
231
232         switch (tree->operation) {
233         case LDB_OP_SIMPLE:
234                 break;
235
236         case LDB_OP_EXTENDED:
237                 return ldb_match_extended(ldb, msg, tree, base, scope);
238
239         case LDB_OP_NOT:
240                 return ! ldb_match_message(ldb, msg, tree->u.not.child, base, scope);
241
242         case LDB_OP_AND:
243                 for (i=0;i<tree->u.list.num_elements;i++) {
244                         v = ldb_match_message(ldb, msg, tree->u.list.elements[i],
245                                                base, scope);
246                         if (!v) return 0;
247                 }
248                 return 1;
249
250         case LDB_OP_OR:
251                 for (i=0;i<tree->u.list.num_elements;i++) {
252                         v = ldb_match_message(ldb, msg, tree->u.list.elements[i],
253                                               base, scope);
254                         if (v) return 1;
255                 }
256                 return 0;
257         }
258
259         return ldb_match_leaf(ldb, msg, tree, base, scope);
260 }