]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/compare.c
05ea183e0bac81fc3242eb3d2173007e713d1962
[openldap] / servers / slapd / back-meta / compare.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2006 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "slap.h"
31 #include "../back-ldap/back-ldap.h"
32 #include "back-meta.h"
33
34 int
35 meta_back_compare( Operation *op, SlapReply *rs )
36 {
37         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
38         metaconn_t              *mc = NULL;
39         char                    *match = NULL,
40                                 *err = NULL;
41         struct berval           mmatch = BER_BVNULL;
42         int                     ncandidates = 0,
43                                 last = 0,
44                                 i,
45                                 count = 0,
46                                 rc,
47                                 cres = LDAP_SUCCESS,
48                                 rres = LDAP_SUCCESS,
49                                 *msgid;
50         dncookie                dc;
51
52         SlapReply               *candidates = meta_back_candidates_get( op );
53
54         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
55         if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
56                 return rs->sr_err;
57         }
58         
59         msgid = ch_calloc( sizeof( int ), mi->mi_ntargets );
60         if ( msgid == NULL ) {
61                 send_ldap_error( op, rs, LDAP_OTHER, NULL );
62                 rc = LDAP_OTHER;
63                 goto done;
64         }
65
66         /*
67          * start an asynchronous compare for each candidate target
68          */
69         dc.conn = op->o_conn;
70         dc.rs = rs;
71         dc.ctx = "compareDN";
72
73         for ( i = 0; i < mi->mi_ntargets; i++ ) {
74                 struct berval           mdn = BER_BVNULL;
75                 struct berval           mapped_attr = op->orc_ava->aa_desc->ad_cname;
76                 struct berval           mapped_value = op->orc_ava->aa_value;
77                 metatarget_t            *mt = mi->mi_targets[ i ];
78                 LDAPControl             **ctrls = NULL;
79
80                 if ( ! META_IS_CANDIDATE( &candidates[ i ] ) ) {
81                         msgid[ i ] = -1;
82                         continue;
83                 }
84
85                 /*
86                  * Rewrite the compare dn, if needed
87                  */
88                 dc.target = mt;
89
90                 switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
91                 case LDAP_UNWILLING_TO_PERFORM:
92                         rc = 1;
93                         goto finish;
94
95                 default:
96                         break;
97                 }
98
99                 /*
100                  * if attr is objectClass, try to remap the value
101                  */
102                 if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) {
103                         ldap_back_map( &mt->mt_rwmap.rwm_oc,
104                                         &op->orc_ava->aa_value,
105                                         &mapped_value, BACKLDAP_MAP );
106
107                         if ( BER_BVISNULL( &mapped_value ) || mapped_value.bv_val[0] == '\0' ) {
108                                 continue;
109                         }
110                 /*
111                  * else try to remap the attribute
112                  */
113                 } else {
114                         ldap_back_map( &mt->mt_rwmap.rwm_at,
115                                 &op->orc_ava->aa_desc->ad_cname,
116                                 &mapped_attr, BACKLDAP_MAP );
117                         if ( BER_BVISNULL( &mapped_attr ) || mapped_attr.bv_val[0] == '\0' ) {
118                                 continue;
119                         }
120
121                         if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
122                         {
123                                 dc.ctx = "compareAttrDN";
124
125                                 switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) )
126                                 {
127                                 case LDAP_UNWILLING_TO_PERFORM:
128                                         rc = 1;
129                                         goto finish;
130
131                                 default:
132                                         break;
133                                 }
134                         }
135                 }
136                 
137                 ctrls = op->o_ctrls;
138                 if ( ldap_back_proxy_authz_ctrl( &mc->mc_conns[ i ].msc_bound_ndn,
139                         mt->mt_version, &mt->mt_idassert, op, rs, &ctrls ) != LDAP_SUCCESS )
140                 {
141                         continue;
142                 }
143
144                 /*
145                  * the compare op is spawned across the targets and the first
146                  * that returns determines the result; a constraint on unicity
147                  * of the result ought to be enforced
148                  */
149                  rc = ldap_compare_ext( mc->mc_conns[ i ].msc_ld, mdn.bv_val,
150                                 mapped_attr.bv_val, &mapped_value,
151                                 ctrls, NULL, &msgid[ i ] );
152
153                 (void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
154
155                 if ( mdn.bv_val != op->o_req_dn.bv_val ) {
156                         free( mdn.bv_val );
157                         BER_BVZERO( &mdn );
158                 }
159
160                 if ( mapped_attr.bv_val != op->orc_ava->aa_desc->ad_cname.bv_val ) {
161                         free( mapped_attr.bv_val );
162                         BER_BVZERO( &mapped_attr );
163                 }
164
165                 if ( mapped_value.bv_val != op->orc_ava->aa_value.bv_val ) {
166                         free( mapped_value.bv_val );
167                         BER_BVZERO( &mapped_value );
168                 }
169
170                 if ( rc != LDAP_SUCCESS ) {
171                         /* FIXME: what should we do with the error? */
172                         continue;
173                 }
174
175                 ++ncandidates;
176         }
177
178         /*
179          * wait for replies
180          */
181         for ( rc = 0, count = 0; ncandidates > 0; ) {
182
183                 /*
184                  * FIXME: should we check for abandon?
185                  */
186                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
187                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
188                         int                     lrc;
189                         LDAPMessage             *res = NULL;
190                         struct timeval          tv;
191
192                         LDAP_BACK_TV_SET( &tv );
193
194                         if ( msgid[ i ] == -1 ) {
195                                 continue;
196                         }
197
198                         lrc = ldap_result( msc->msc_ld, msgid[ i ],
199                                         LDAP_MSG_ALL, &tv, &res );
200
201                         switch ( lrc ) {
202                         case 0:
203                                 assert( res == NULL );
204                                 continue;
205
206                         case -1:
207                                 /* we do not retry in this case;
208                                  * only for unique operations... */
209                                 ldap_get_option( msc->msc_ld,
210                                         LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
211                                 rres = slap_map_api2result( rs );
212                                 rres = rc;
213                                 rc = -1;
214                                 goto finish;
215
216                         default:
217                                 /* only touch when activity actually took place... */
218                                 /* NOTE: no mutex because there's only a loose requirement
219                                  * to bump it up... */
220                                 if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
221                                         msc->msc_time = op->o_time;
222                                 }
223                                 break;
224                         }
225
226                         if ( lrc == LDAP_RES_COMPARE ) {
227                                 if ( count > 0 ) {
228                                         rres = LDAP_OTHER;
229                                         rc = -1;
230                                         goto finish;
231                                 }
232
233                                 /* FIXME: matched? referrals? response controls? */
234                                 rc = ldap_parse_result( msc->msc_ld, res,
235                                                 &rs->sr_err,
236                                                 NULL, NULL, NULL, NULL, 1 );
237                                 if ( rc != LDAP_SUCCESS ) {
238                                         rres = rc;
239                                         rc = -1;
240                                         goto finish;
241                                 }
242                                 
243                                 switch ( rs->sr_err ) {
244                                 case LDAP_COMPARE_TRUE:
245                                 case LDAP_COMPARE_FALSE:
246
247                                         /*
248                                          * true or false, got it;
249                                          * sending to cache ...
250                                          */
251                                         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
252                                                 ( void )meta_dncache_update_entry( &mi->mi_cache, &op->o_req_ndn, i );
253                                         }
254
255                                         count++;
256                                         rc = 0;
257                                         break;
258
259                                 default:
260                                         rres = slap_map_api2result( rs );
261
262                                         if ( err != NULL ) {
263                                                 free( err );
264                                         }
265                                         ldap_get_option( msc->msc_ld,
266                                                 LDAP_OPT_ERROR_STRING, &err );
267
268                                         if ( match != NULL ) {
269                                                 free( match );
270                                         }
271                                         ldap_get_option( msc->msc_ld,
272                                                 LDAP_OPT_MATCHED_DN, &match );
273                                         
274                                         last = i;
275                                         break;
276                                 }
277                                 msgid[ i ] = -1;
278                                 --ncandidates;
279
280                         } else {
281                                 msgid[ i ] = -1;
282                                 --ncandidates;
283                                 if ( res ) {
284                                         ldap_msgfree( res );
285                                 }
286                                 break;
287                         }
288                 }
289         }
290
291 finish:;
292
293         /*
294          * Rewrite the matched portion of the search base, if required
295          * 
296          * FIXME: only the last one gets caught!
297          */
298         if ( count == 1 ) {
299                 if ( match != NULL ) {
300                         free( match );
301                         match = NULL;
302                 }
303                 
304                 /*
305                  * the result of the compare is assigned to the res code
306                  * that will be returned
307                  */
308                 rres = cres;
309                 
310                 /*
311                  * At least one compare failed with matched portion,
312                  * and none was successful
313                  */
314         } else if ( match != NULL && match[ 0 ] != '\0' ) {
315                 struct berval matched, pmatched;
316
317                 ber_str2bv( match, 0, 0, &matched );
318
319                 dc.ctx = "matchedDN";
320                 ldap_back_dn_massage( &dc, &matched, &mmatch );
321                 if ( dnPretty( NULL, &mmatch, &pmatched, NULL ) == LDAP_SUCCESS ) {
322                         if ( mmatch.bv_val != match ) {
323                                 free( mmatch.bv_val );
324                         }
325                         mmatch = pmatched;
326                 }
327         }
328
329         if ( rres != LDAP_SUCCESS ) {
330                 rs->sr_err = rres;
331         }
332         rs->sr_matched = mmatch.bv_val;
333         send_ldap_result( op, rs );
334         rs->sr_matched = NULL;
335
336         if ( match != NULL ) {
337                 if ( mmatch.bv_val != match ) {
338                         free( mmatch.bv_val );
339                 }
340                 free( match );
341         }
342
343         if ( msgid ) {
344                 free( msgid );
345         }
346
347 done:;
348         meta_back_release_conn( op, mc );
349
350         return rc;
351 }
352