]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/cache-search.c
Proxy cache patch (by Jong, Apurva, & Kurt)
[openldap] / servers / slapd / back-meta / cache-search.c
1 /*
2  * Copyright (c) 2003 by International Business Machines, Inc.
3  *
4  * International Business Machines, Inc. (hereinafter called IBM) grants
5  * permission under its copyrights to use, copy, modify, and distribute this
6  * Software with or without fee, provided that the above copyright notice and
7  * all paragraphs of this notice appear in all copies, and that the name of IBM
8  * not be used in connection with the marketing of any product incorporating
9  * the Software or modifications thereof, without specific, written prior
10  * permission.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
13  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14  * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
15  * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
17  * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
18  *
19  * 
20  * This software is based on the backends back-ldap and back-meta, implemented
21  * by Howard Chu <hyc@highlandsun.com>, Mark Valence
22  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
23  * contributors. 
24  *
25  * The original copyright statements follow. 
26  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
27  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
28  *
29  * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
30  *
31  * This work has been developed to fulfill the requirements
32  * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
33  * to the OpenLDAP Foundation in the hope that it may be useful
34  * to the Open Source community, but WITHOUT ANY WARRANTY.
35  *
36  * Permission is granted to anyone to use this software for any purpose
37  * on any computer system, and to alter it and redistribute it, subject
38  * to the following restrictions:
39  *
40  * 1. The author and SysNet s.n.c. are not responsible for the consequences
41  *    of use of this software, no matter how awful, even if they arise from 
42  *    flaws in it.
43  *
44  * 2. The origin of this software must not be misrepresented, either by
45  *    explicit claim or by omission.  Since few users ever read sources,
46  *    credits should appear in the documentation.
47  *
48  * 3. Altered versions must be plainly marked as such, and must not be
49  *    misrepresented as being the original software.  Since few users
50  *    ever read sources, credits should appear in the documentation.
51  *    SysNet s.n.c. cannot be responsible for the consequences of the
52  *    alterations.
53  *
54  * 4. This notice may not be removed or altered.
55  *
56  *
57  * This software is based on the backend back-ldap, implemented
58  * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
59  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
60  * contributors. The contribution of the original software to the present
61  * implementation is acknowledged in this copyright statement.
62  *
63  * A special acknowledgement goes to Howard for the overall architecture
64  * (and for borrowing large pieces of code), and to Mark, who implemented
65  * from scratch the attribute/objectclass mapping.
66  *
67  * The original copyright statement follows.
68  *
69  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
70  *
71  * Permission is granted to anyone to use this software for any purpose
72  * on any computer system, and to alter it and redistribute it, subject
73  * to the following restrictions:
74  *
75  * 1. The author is not responsible for the consequences of use of this
76  *    software, no matter how awful, even if they arise from flaws in it.
77  *
78  * 2. The origin of this software must not be misrepresented, either by
79  *    explicit claim or by omission.  Since few users ever read sources,
80  *    credits should appear in the documentation.
81  *
82  * 3. Altered versions must be plainly marked as such, and must not be
83  *    misrepresented as being the original software.  Since few users
84  *    ever read sources, credits should appear in the
85  *    documentation.
86  *
87  * 4. This notice may not be removed or altered.
88  *                
89  */
90 #include "portable.h"
91
92 #include <stdio.h>
93
94 #include <ac/socket.h>
95 #include <ac/string.h>
96 #include <ac/time.h>
97
98 #include "ldap_pvt.h"
99 #include "lutil.h"
100 #include "slap.h"
101 #include "../back-ldap/back-ldap.h"
102 #include "back-meta.h"
103 #undef ldap_debug       /* silence a warning in ldap-int.h */
104 #include "ldap_log.h"
105 #include "../../../libraries/libldap/ldap-int.h"
106
107 #ifdef LDAP_CACHING 
108 static Entry* 
109 meta_create_entry(
110         Backend         *be,
111         struct metaconn *lc,
112         int             target,
113         LDAPMessage     *e,
114         struct exception* result
115 ); 
116
117 static int
118 is_one_level_rdn(
119         const char      *rdn,
120         int             from
121 );
122
123 static struct metaconn*  
124 metaConnect(
125         Operation               *op, 
126         SlapReply               *rs,
127         int                     op_type, 
128         struct berval           *nbase, 
129         struct exception        *result 
130 );
131
132 static void
133 add_filter_attrs(
134         AttributeName** newattrs, 
135         AttributeName* attrs, 
136         AttributeName* filter_attrs
137 );
138
139 static int 
140 handleLdapResult(
141         struct metaconn* lc, 
142         Operation* op, 
143         SlapReply *rs,
144         int* msgid, Backend* be, 
145         AttributeName* attrs, 
146         int attrsonly, 
147         int candidates, 
148         int cacheable, 
149         Entry*** entry_array, 
150         int curr_limit, 
151         int slimit,
152         struct exception* result
153 );
154
155 static Entry* 
156 get_result_entry(
157         Backend* be,
158         struct metaconn* lc, 
159         struct metasingleconn* lsc, 
160         int* msgid,
161         int i, 
162         struct timeval* tv, 
163         struct exception* result
164 ); 
165
166 static void
167 rewriteSession(
168         struct rewrite_info* info, 
169         const char* rewriteContext, 
170         const char* string, 
171         const void* cookie,  
172         char** base, 
173         struct exception* result
174 );
175
176 static int 
177 get_attr_set(
178         AttributeName* attrs, 
179         query_manager* qm, 
180         int num
181 );
182
183 static int 
184 attrscmp(
185         AttributeName* attrs_in, 
186         AttributeName* attrs
187 );
188
189 static char* 
190 cache_entries(
191         Operation       *op,
192         SlapReply       *rs,
193         Entry** entry_array, 
194         cache_manager* cm, 
195         struct exception* result
196 ); 
197
198 static int 
199 is_temp_answerable(
200         int attr_set, 
201         struct berval* tempstr, 
202         query_manager* qm, 
203         int template_id
204 );
205
206 static void*
207 consistency_check(
208         void    *op
209 ); 
210
211 static int
212 cache_back_sentry(
213         Operation* op, 
214         SlapReply *rs
215 );
216
217
218 int
219 meta_back_cache_search(
220         Operation       *op,
221         SlapReply       *rs )
222         /*
223         Backend         *be,
224         Connection      *conn,
225         Operation       *op,
226         struct berval   *base,
227         struct berval   *nbase,
228         int             scope,
229         int             deref,
230         int             slimit,
231         int             tlimit,
232         Filter          *filt,
233         struct berval   *filterstr,
234         AttributeName   *attributes,
235         int             attrsonly
236 ) */
237 {
238         struct metainfo         *li = ( struct metainfo * )op->o_bd->be_private;
239         struct metaconn         *lc;
240         struct metasingleconn   *lsc;
241         cache_manager*          cm = li->cm; 
242         query_manager*          qm = cm->qm; 
243
244         Operation               *oper;
245
246         time_t                  curr_time; 
247
248         int count, rc = 0, *msgid = NULL; 
249         char *mbase = NULL;
250         char *cbase = NULL; 
251         char *uuid; 
252             
253         int i = -1, last = 0, candidates = 0, op_type;
254
255         struct berval   mfilter;
256         struct berval   cachebase = { 0L, NULL };  
257         struct berval   ncachebase = { 0L, NULL };  
258         struct berval   cache_suffix; 
259         struct berval   tempstr = { 0L, NULL }; 
260
261         AttributeName   *filter_attrs = NULL; 
262         AttributeName   *new_attrs = NULL; 
263         AttributeName   *attrs = NULL; 
264
265         Entry           *e;
266         Entry           **entry_array = NULL;
267
268         Query           query; 
269
270         int             attr_set = -1; 
271         int             template_id = -1; 
272         int             answerable = 0; 
273         int             cacheable = 0; 
274         int             num_entries = 0;
275         int             curr_limit;
276         int             fattr_cnt=0; 
277         int             oc_attr_absent = 1;
278
279         struct exception result[1]; 
280
281         Filter* filter = str2filter(op->ors_filterstr.bv_val); 
282         slap_callback cb = {cache_back_sentry, NULL}; 
283
284         cb.sc_private = op->o_bd; 
285
286         if (op->ors_attrs) {
287                 for ( count=0; op->ors_attrs[ count ].an_name.bv_val; count++ ) {
288                         if ( op->ors_attrs[count].an_desc == slap_schema.si_ad_objectClass )
289                                 oc_attr_absent = 0;
290                 }
291                 attrs = (AttributeName*)malloc( ( count + 1 + oc_attr_absent )
292                                                                 *sizeof(AttributeName));
293                 for ( count=0; op->ors_attrs[ count ].an_name.bv_val; count++ ) {
294                         ber_dupbv(&attrs[ count ].an_name,
295                                                 &op->ors_attrs[ count ].an_name);
296                         attrs[count].an_desc = op->ors_attrs[count].an_desc; 
297                 }
298                 attrs[ count ].an_name.bv_val = NULL;
299                 attrs[ count ].an_name.bv_len = 0;
300         }
301
302         result->type = SUCCESS; 
303         result->rc = 0; 
304         ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
305         cm->threads++; 
306 #ifdef NEW_LOGGING
307         LDAP_LOG( BACK_META, DETAIL1, "Threads++ = %d\n", cm->threads, 0, 0 );
308 #else /* !NEW_LOGGING */
309         Debug( LDAP_DEBUG_ANY, "Threads++ = %d\n", cm->threads, 0, 0 );
310 #endif /* !NEW_LOGGING */
311         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
312         
313         ldap_pvt_thread_mutex_lock(&cm->cc_mutex); 
314         if (!cm->cc_thread_started) {
315                 oper = (Operation*)malloc(sizeof(Operation)); 
316                 *oper = *op; 
317                 cm->cc_thread_started = 1; 
318                 ldap_pvt_thread_create(&(cm->cc_thread), 1, consistency_check, (void*)oper); 
319         }       
320         ldap_pvt_thread_mutex_unlock(&cm->cc_mutex); 
321
322         filter2template(filter, &tempstr, &filter_attrs, &fattr_cnt, result);  
323         if (result->type != SUCCESS) 
324                 goto Catch; 
325
326 #ifdef NEW_LOGGING
327         LDAP_LOG( BACK_META, DETAIL1, "query template of incoming query = %s\n",
328                                         tempstr.bv_val, 0, 0 );
329 #else /* !NEW_LOGGING */
330         Debug( LDAP_DEBUG_ANY, "query template of incoming query = %s\n",
331                                         tempstr.bv_val, 0, 0 );
332 #endif /* !NEW_LOGGING */
333         curr_limit = cm->num_entries_limit ;
334
335         /* find attr set */     
336         attr_set = get_attr_set(attrs, qm, cm->numattrsets); 
337     
338         query.filter = filter; 
339         query.attrs = attrs; 
340         query.base = op->o_req_dn; 
341         query.scope = op->ors_scope; 
342
343         /* check for query containment */
344         if (attr_set > -1) {
345                 for (i=0; i<cm->numtemplates; i++) {
346                         /* find if template i can potentially answer tempstr */
347                         if (!is_temp_answerable(attr_set, &tempstr, qm, i)) 
348                                 continue; 
349                         if (attr_set == qm->templates[i].attr_set_index) {
350                                 cacheable = 1; 
351                                 template_id = i; 
352                         }
353 #ifdef NEW_LOGGING
354                         LDAP_LOG( BACK_META, DETAIL2,
355                                         "Entering QC, querystr = %s\n",
356                                         op->ors_filterstr.bv_val, 0, 0 );
357 #else /* !NEW_LOGGING */
358                         Debug( LDAP_DEBUG_NONE, "Entering QC, querystr = %s\n",
359                                         op->ors_filterstr.bv_val, 0, 0 );
360 #endif /* !NEW_LOGGING */
361                         answerable = (*(qm->qcfunc))(qm, &query, i);
362
363                         if (answerable)
364                                 break;
365                 }
366         }
367
368         if ( attrs && oc_attr_absent ) {
369                 for ( count = 0; attrs[count].an_name.bv_val; count++) ;
370                 attrs[ count ].an_name.bv_val = "objectClass";
371                 attrs[ count ].an_name.bv_len = strlen( "objectClass" );
372                 attrs[ count ].an_desc = slap_schema.si_ad_objectClass;
373                 attrs[ count + 1 ].an_name.bv_val = NULL;
374                 attrs[ count + 1 ].an_name.bv_len = 0;
375         }
376
377         if (answerable) {
378                 Operation       op_tmp;
379
380 #ifdef NEW_LOGGING
381                 LDAP_LOG( BACK_META, DETAIL1, "QUERY ANSWERABLE\n", 0, 0, 0 );
382 #else /* !NEW_LOGGING */
383                 Debug( LDAP_DEBUG_ANY, "QUERY ANSWERABLE\n", 0, 0, 0 );
384 #endif /* !NEW_LOGGING */
385                 rewriteSession(li->rwinfo, "cacheBase", op->o_req_dn.bv_val,
386                                         op->o_conn, &cbase, result); 
387                 if (result->type != SUCCESS) { 
388                         ldap_pvt_thread_rdwr_runlock(&qm->templates[i].t_rwlock); 
389                         goto Catch; 
390                 }
391                 if ( cbase == NULL ) {
392                         cachebase = op->o_req_dn;
393                 } else {
394                         cachebase.bv_val = cbase;
395                         cachebase.bv_len = strlen(cbase);
396                 }
397                 dnNormalize(0, NULL, NULL, &cachebase, &ncachebase,
398                                 op->o_tmpmemctx); 
399
400                 /* FIXME: safe default? */
401                 op_tmp = *op;
402
403                 op_tmp.o_bd = li->glue_be;
404                 op_tmp.o_req_dn = cachebase;
405                 op_tmp.o_req_ndn = ncachebase;
406
407                 op_tmp.o_caching_on = 0; 
408                 op_tmp.o_callback = &cb; 
409
410                 li->glue_be->be_search(&op_tmp, rs);
411                 free( ncachebase.bv_val );
412                 if ( cachebase.bv_val != op->o_req_dn.bv_val ) {
413                         /* free only if rewritten */
414                         free( cachebase.bv_val );
415                 }
416
417                 ldap_pvt_thread_rdwr_runlock(&qm->templates[i].t_rwlock); 
418         } else {
419                 Operation       op_tmp;
420                 op_tmp = *op;
421 #ifdef NEW_LOGGING
422                 LDAP_LOG( BACK_META, DETAIL1, "QUERY NOT ANSWERABLE\n",
423                                         0, 0, 0 );
424 #else /* !NEW_LOGGING */
425                 Debug( LDAP_DEBUG_ANY, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
426 #endif /* !NEW_LOGGING */
427
428                 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
429                         op_type = META_OP_REQUIRE_SINGLE;
430                 } else {
431                         op_type = META_OP_ALLOW_MULTIPLE;
432                 }
433
434                 lc = metaConnect(&op_tmp, rs, op_type,
435                                 &op->o_req_ndn, result);
436
437                 if (result->type != SUCCESS) 
438                         goto Catch; 
439
440                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
441                 if (cm->num_cached_queries >= cm->max_queries) {
442                         cacheable = 0; 
443                 }
444                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
445                 
446                 if (cacheable) {
447                         add_filter_attrs(&new_attrs, attrs, filter_attrs);
448                 } else {
449                         new_attrs = attrs; 
450                 }
451
452                 free(filter_attrs); 
453         
454                 /*
455                  * Array of message id of each target
456                  */
457                 msgid = ch_calloc( sizeof( int ), li->ntargets );
458                 if ( msgid == NULL ) {
459                         result->type = CONN_ERR; 
460                         goto Catch; 
461                 }
462
463                 /*
464                 if (slimit > 0 &&  (slimit <= cm->num_entries_limit))  
465                         slimit = cm->num_entries_limit; 
466                 */
467
468                 /*
469                  * Inits searches
470                  */
471
472                 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
473                         char    *realbase = ( char * )op->o_req_dn.bv_val;
474                         int     realscope = op->ors_scope;
475                         ber_len_t suffixlen;
476                         char    *mapped_filter, **mapped_attrs;
477
478                         /* FIXME: Check for more than one targets */
479                         if ( meta_back_is_candidate(
480                                         &li->targets[i]->suffix,
481                                         &op->o_req_ndn ))
482                                 lsc->candidate = META_CANDIDATE; 
483
484                         if ( lsc->candidate != META_CANDIDATE ) 
485                                 continue;
486
487                         if ( op->ors_deref != -1 ) {
488                                 ldap_set_option( lsc->ld, LDAP_OPT_DEREF,
489                                                 ( void * )&op->ors_deref);
490                         }
491                         if ( op->ors_tlimit != -1 ) {
492                                 ldap_set_option( lsc->ld, LDAP_OPT_TIMELIMIT,
493                                                 ( void * )&op->ors_tlimit);
494                         }
495                         if ( op->ors_slimit != -1 ) {
496                                 ldap_set_option( lsc->ld, LDAP_OPT_SIZELIMIT,
497                                                 ( void * )&op->ors_slimit);
498                         }
499
500                         /*
501                          * modifies the base according to the scope, if required
502                          */
503                         suffixlen = li->targets[ i ]->suffix.bv_len;
504                         if ( suffixlen > op->o_req_ndn.bv_len ) {
505                                 switch ( op->ors_scope ) {
506                                 case LDAP_SCOPE_SUBTREE:
507                                         /*
508                                          * make the target suffix the new base
509                                          * FIXME: this is very forgiving,
510                                          * because illegal bases may be turned
511                                          * into the suffix of the target.
512                                          */
513                                         if ( dnIsSuffix(
514                                                 &li->targets[ i ]->suffix,
515                                                 &op->o_req_ndn ) ) {
516                                                 realbase =
517                                                 li->targets[i]->suffix.bv_val;
518                                         } else {
519                                                 /*
520                                                  * this target is no longer
521                                                  * candidate
522                                                  */
523                                                 lsc->candidate =
524                                                         META_NOT_CANDIDATE;
525                                                 continue;
526                                         }
527                                         break;
528
529                                 case LDAP_SCOPE_ONELEVEL:
530                                         if ( is_one_level_rdn(
531                                                 li->targets[ i ]->suffix.bv_val,
532                                                 suffixlen - op->o_req_ndn.bv_len - 1 )
533                                                 && dnIsSuffix(
534                                                 &li->targets[ i ]->suffix,
535                                                 &op->o_req_ndn ) ) {
536                                                 /*
537                                                  * if there is exactly one
538                                                  * level, make the target suffix
539                                                  * the new base, and make scope
540                                                  * "base"
541                                                  */
542                                                 realbase =
543                                                 li->targets[i]->suffix.bv_val;
544                                                 realscope = LDAP_SCOPE_BASE;
545                                                 break;
546                                         } /* else continue with the next case */
547
548                                 case LDAP_SCOPE_BASE:
549                                         /*
550                                          * this target is no longer candidate
551                                          */
552                                         lsc->candidate = META_NOT_CANDIDATE;
553                                         continue;
554                                 }
555                         }
556
557                         /*
558                          * Rewrite the search base, if required
559                          */
560
561                         rewriteSession(li->targets[i]->rwmap.rwm_rw,
562                                         "searchBase",
563                                         realbase, op->o_conn, &mbase, result); 
564
565                         if (result->type != SUCCESS)
566                                 goto Catch; 
567
568                         if ( mbase == NULL ) {
569                                 mbase = realbase;
570                         }
571
572                         /*
573                          * Rewrite the search filter, if required
574                          */
575                         rewriteSession( li->targets[i]->rwmap.rwm_rw,
576                                         "searchFilter",
577                                         op->ors_filterstr.bv_val, op->o_conn,
578                                         &mfilter.bv_val, result);
579                         if (result->type != SUCCESS) 
580                                 goto Catch; 
581
582                         if ( mfilter.bv_val != NULL && mfilter.bv_val[ 0 ]
583                                                                 != '\0') {
584                                 mfilter.bv_len = strlen( mfilter.bv_val );
585                         } else {
586                                 if ( mfilter.bv_val != NULL ) {
587                                         free( mfilter.bv_val );
588                                 }
589                                 mfilter = op->ors_filterstr;
590                         }
591
592 #if 0
593                         /*
594                          * Maps attributes in filter
595                          */
596                         mapped_filter = ldap_back_map_filter(
597                                         &li->targets[i]->rwmap.rwm_at,
598                                         &li->targets[i]->rwmap.rwm_oc,
599                                         &mfilter, 0 );
600                         if ( mapped_filter == NULL ) {
601                                 mapped_filter = ( char * )mfilter.bv_val;
602                         } else {
603                                 if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
604                                         free( mfilter.bv_val );
605                                 }
606                         }
607                         mfilter.bv_val = NULL;
608                         mfilter.bv_len = 0;
609 #else
610                         mapped_filter = (char *) mfilter.bv_val;
611 #endif
612
613                         /*
614                          * Maps required attributes
615                          */
616                         if ( ldap_back_map_attrs(
617                                         &li->targets[ i ]->rwmap.rwm_at,
618                                         new_attrs, 0, &mapped_attrs ) ) {
619                                 goto Catch;
620                         }
621
622                         /*
623                          * Starts the search
624                          */
625                         msgid[ i ] = ldap_search( lsc->ld, mbase, realscope,
626                                                 mapped_filter, mapped_attrs,
627                                                 op->ors_attrsonly );
628
629                         if ( msgid[ i ] == -1 ) {
630                                 result->type = CONN_ERR; 
631                                 goto Catch; 
632                                 /*
633                                 lsc->candidate = META_NOT_CANDIDATE;
634                                 continue;
635                                 */
636                         }
637
638                         if ( mapped_attrs ) {
639                                 free( mapped_attrs );
640                                 mapped_attrs = NULL;
641                         }
642
643                         if ( mapped_filter != op->ors_filterstr.bv_val ) {
644                                 free( mapped_filter );
645                                 mapped_filter = NULL;
646                         }
647
648                         if ( mbase != realbase ) {
649                                 free( mbase );
650                                 mbase = NULL;
651                         }
652
653                         ++candidates;
654                 }
655
656                 num_entries = handleLdapResult(lc, &op_tmp, rs, msgid,
657                                 op->o_bd, attrs,
658                                 op->ors_attrsonly, candidates, 
659                                 cacheable, &entry_array,
660                                 curr_limit, op->ors_slimit, result); 
661
662                 if (result->type != SUCCESS) 
663                         goto Catch; 
664                 if (cacheable && (num_entries <= curr_limit)) {
665
666 #ifdef NEW_LOGGING
667                         LDAP_LOG( BACK_META, DETAIL1,
668                                         "QUERY CACHEABLE\n", 0, 0, 0 );
669 #else /* !NEW_LOGGING */
670                         Debug( LDAP_DEBUG_ANY, "QUERY CACHEABLE\n", 0, 0, 0 );
671 #endif /* !NEW_LOGGING */
672                         op_tmp.o_bd = li->glue_be;
673                         uuid = cache_entries(&op_tmp, rs, entry_array, cm, result); 
674 #ifdef NEW_LOGGING
675                         LDAP_LOG( BACK_META, DETAIL1,
676                                         "Added query %s UUID %s ENTRIES %d\n",
677                                         op->ors_filterstr.bv_val,
678                                         uuid, num_entries );
679 #else /* !NEW_LOGGING */
680                         Debug( LDAP_DEBUG_ANY,
681                                         "Added query %s UUID %s ENTRIES %d\n",
682                                         op->ors_filterstr.bv_val,
683                                         uuid, num_entries );
684 #endif /* !NEW_LOGGING */
685             
686                         if (result->type != SUCCESS) 
687                                 goto Catch; 
688                         (*(qm->addfunc))(qm, &query, template_id, uuid, result); 
689                         if (result->type != SUCCESS) 
690                                 goto Catch; 
691                         filter = 0; 
692                         attrs = 0; 
693
694                         /* FIXME : launch do_syncrepl() threads around here
695                          *
696                          * entryUUID and entryCSN need also to be requested by :
697                          */
698                         /*
699                         msgid[ i ] = ldap_search( lsc->ld, mbase, realscope,
700                                                 mapped_filter, mapped_attrs, op->ors_attrsonly );
701                         */
702                         /* Also, mbase, realscope, mapped_filter, mapped_attrs need
703                          * be managed as arrays. Each element needs to be retained by this point.
704                          */
705
706                 } else {
707 #ifdef NEW_LOGGING
708                         LDAP_LOG( BACK_META, DETAIL1,
709                                         "QUERY NOT CACHEABLE no\n",
710                                         0, 0, 0);
711 #else /* !NEW_LOGGING */
712                         Debug( LDAP_DEBUG_ANY, "QUERY NOT CACHEABLE no\n",
713                                         0, 0, 0);
714 #endif /* !NEW_LOGGING */
715                 }
716         }
717
718 Catch:;
719         switch (result->type) {
720                 case SUCCESS: 
721                         rc = 0; 
722                         break;
723
724                 case FILTER_ERR: 
725 #ifdef NEW_LOGGING
726                         LDAP_LOG( BACK_META, DETAIL1,
727                                         "Invalid template error\n", 0, 0, 0 );
728 #else /* !NEW_LOGGING */
729                         Debug( LDAP_DEBUG_ANY, "Invalid template error\n",
730                                         0, 0, 0 );
731 #endif /* !NEW_LOGGING */
732                         break; 
733
734                 case CONN_ERR: 
735                         rc = -1; 
736 #ifdef NEW_LOGGING
737                         LDAP_LOG( BACK_META, DETAIL1,
738                                 "Could not connect to a remote server\n",
739                                 0, 0, 0 );
740 #else /* !NEW_LOGGING */
741                         Debug( LDAP_DEBUG_ANY,
742                                 "Could not connect to a remote server\n",
743                                 0, 0, 0 );
744 #endif /* !NEW_LOGGING */
745                         send_ldap_error(op, rs, LDAP_OTHER,
746                                         "Connection error" );
747                         break;
748                         
749                 case RESULT_ERR: 
750                         rc = -1; 
751 #ifdef NEW_LOGGING
752                         LDAP_LOG( BACK_META, DETAIL1,
753                                 "Error in handling ldap_result\n", 0, 0, 0 );
754 #else /* !NEW_LOGGING */
755                         Debug( LDAP_DEBUG_ANY,
756                                 "Error in handling ldap_result\n", 0, 0, 0 );
757 #endif /* !NEW_LOGGING */
758                         break; 
759
760                 case REWRITING_ERR: 
761                         rc = -1; 
762                         if (result->rc == REWRITE_REGEXEC_UNWILLING) {
763                                 send_ldap_error( op, rs,
764                                                 LDAP_UNWILLING_TO_PERFORM,
765                                                 "Unwilling to perform" );
766                         } else {
767                                 send_ldap_error( op, rs, LDAP_OTHER,
768                                                 "Rewrite error" );
769                         }
770                         break;
771
772                 case MERGE_ERR: 
773                         rc = -1; 
774 #ifdef NEW_LOGGING
775                         LDAP_LOG( BACK_META, DETAIL1,
776                                 "Error in merging entry \n", 0, 0, 0 );
777 #else /* !NEW_LOGGING */
778                         Debug( LDAP_DEBUG_ANY,
779                                 "Error in merging entry \n", 0, 0, 0 );
780 #endif /* !NEW_LOGGING */
781                         break;
782
783                 case REMOVE_ERR: 
784                         rc = -1; 
785 #ifdef NEW_LOGGING
786                         LDAP_LOG( BACK_META, DETAIL1,
787                                         "Error in removing query \n",
788                                         0, 0, 0 );
789 #else /* !NEW_LOGGING */
790                         Debug( LDAP_DEBUG_ANY, "Error in removing query \n",
791                                         0, 0, 0 );
792 #endif /* !NEW_LOGGING */
793                         break;
794
795                 default:
796                         /* assert(0); */
797                         break;
798         }
799
800
801         if ( msgid ) {
802                 ch_free( msgid );
803         }
804         if (entry_array)  {
805                 for (i=0; (e = entry_array[i]); i++) {
806                         entry_free(e); 
807                 }
808                 free(entry_array);
809         }
810         if (filter) 
811                 filter_free(filter);
812
813         if (new_attrs) {
814                 if (new_attrs != attrs) 
815                         free(new_attrs); 
816         }
817
818         if (attrs)
819                 free(attrs); 
820
821         if (tempstr.bv_val ) {
822                 free(tempstr.bv_val);
823         }
824         ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
825         cm->threads--; 
826 #ifdef NEW_LOGGING
827         LDAP_LOG( BACK_META, DETAIL1, "Threads-- = %d\n", cm->threads, 0, 0 ); 
828 #else /* !NEW_LOGGING */
829         Debug( LDAP_DEBUG_ANY, "Threads-- = %d\n", cm->threads, 0, 0 ); 
830 #endif /* !NEW_LOGGING */
831         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
832         return rc;
833 }
834
835
836 static Entry* 
837 meta_create_entry (
838         Backend                 *be,
839         struct metaconn         *lc,
840         int                     target,
841         LDAPMessage             *e,
842         struct exception*       result
843 )
844 {
845         struct metainfo         *li = ( struct metainfo * )be->be_private;
846         struct berval           a, mapped;
847         Entry*                  ent;
848         BerElement              ber = *e->lm_ber;
849         Attribute               *attr, *soc_attr, **attrp;
850         struct berval   dummy = { 0, NULL };
851         struct berval   *bv, bdn;
852         const char              *text;
853         char*                   ename = NULL; 
854         struct berval   sc;
855         char*                   textbuf;
856         size_t                  textlen;
857
858         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
859                 result->type = CREATE_ENTRY_ERR;        
860                 return NULL; 
861         }
862         ent = (Entry*)malloc(sizeof(Entry)); 
863
864         /*
865          * Rewrite the dn of the result, if needed
866          */
867         rewriteSession( li->targets[ target ]->rwmap.rwm_rw, "searchResult",
868                         bdn.bv_val, lc->conn, &ent->e_name.bv_val, result );  
869
870         if (result->type != SUCCESS) {
871                 return NULL; 
872         }
873         if ( ent->e_name.bv_val == NULL ) {
874                 ber_dupbv(&(ent->e_name), &bdn);
875         } else {
876 #ifdef NEW_LOGGING
877                 LDAP_LOG( BACK_META, DETAIL1,
878                         "[rw] searchResult[%d]: \"%s\" -> \"%s\"\n",
879                         target, bdn.bv_val, ent->e_name.bv_val );
880 #else /* !NEW_LOGGING */
881                 Debug( LDAP_DEBUG_ARGS, "rw> searchResult[%d]: \"%s\""
882                         " -> \"%s\"\n",
883                         target, bdn.bv_val, ent->e_name.bv_val );
884 #endif /* !NEW_LOGGING */
885                 ent->e_name.bv_len = strlen( ent->e_name.bv_val );
886         }
887                 
888         /*
889          * Note: this may fail if the target host(s) schema differs
890          * from the one known to the meta, and a DN with unknown
891          * attributes is returned.
892          * 
893          * FIXME: should we log anything, or delegate to dnNormalize?
894          */
895         dnNormalize( 0, NULL, NULL, &ent->e_name, &ent->e_nname, NULL ); 
896
897         /*
898         if ( dnNormalize( 0, NULL, NULL, &ent->e_name, &ent->e_nname )
899                 != LDAP_SUCCESS )
900         {
901                 return LDAP_INVALID_DN_SYNTAX;
902         }
903         */
904
905         /*
906          * cache dn
907          */
908         if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
909                 meta_dncache_update_entry( &li->cache, &ent->e_nname, target );
910         }
911
912         ent->e_id = 0;
913         ent->e_attrs = 0;
914         ent->e_private = 0;
915         ent->e_bv.bv_val = 0; 
916
917         attrp = &ent->e_attrs;
918
919         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
920                 ldap_back_map( &li->targets[ target ]->rwmap.rwm_at, 
921                                 &a, &mapped, 1 );
922                 if ( mapped.bv_val == NULL ) {
923                         continue;
924                 }
925                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
926                 if ( attr == NULL ) {
927                         continue;
928                 }
929                 attr->a_flags = 0;
930                 attr->a_next = 0;
931                 attr->a_desc = NULL;
932                 attr->a_nvals = NULL;
933                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text ) != LDAP_SUCCESS) {
934                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
935                                         != LDAP_SUCCESS) {
936 #ifdef NEW_LOGGING
937                                 LDAP_LOG( BACK_META, DETAIL1,
938                                         "slap_bv2undef_ad(%s): %s\n",
939                                         mapped.bv_val, text, 0 );
940 #else /* !NEW_LOGGING */
941                                 Debug( LDAP_DEBUG_ANY,
942                                         "slap_bv2undef_ad(%s): "
943                                         "%s\n%s", mapped.bv_val, text, "" );
944 #endif /* !NEW_LOGGING */
945                                 ch_free( attr );
946                                 continue;
947                         }
948                 }
949
950                 /* no subschemaSubentry */
951                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
952                         ch_free(attr);
953                         continue;
954                 }
955
956                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
957                                 || attr->a_vals == NULL ) {
958                         attr->a_vals = &dummy;
959 #if 0
960                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass ||
961                                 attr->a_desc ==
962                                 slap_schema.si_ad_structuralObjectClass) {
963 #else
964                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass ) {
965 #endif
966                         int i, last;
967                         for ( last = 0; attr->a_vals[ last ].bv_val; ++last )
968                                 ;
969                         for ( i = 0, bv = attr->a_vals; bv->bv_val; bv++,i++ ) {
970                                 ldap_back_map( &li->targets[ target]->rwmap.rwm_oc,
971                                                 bv, &mapped, 1 );
972                                 if ( mapped.bv_val == NULL ) {
973                                         free( bv->bv_val );
974                                         bv->bv_val = NULL;
975                                         if ( --last < 0 ) {
976                                                 break;
977                                         }
978                                         *bv = attr->a_vals[ last ];
979                                         attr->a_vals[ last ].bv_val = NULL;
980                                         i--;
981                                 } else if ( mapped.bv_val != bv->bv_val ) {
982                                         free( bv->bv_val );
983                                         ber_dupbv( bv, &mapped );
984                                 }
985                         }
986
987                         structural_class( attr->a_vals, &sc, NULL, &text, textbuf, textlen );
988                         soc_attr = (Attribute*) ch_malloc( sizeof( Attribute ));
989                         soc_attr->a_desc = slap_schema.si_ad_structuralObjectClass;
990                         soc_attr->a_vals = (BerVarray) ch_malloc( 2* sizeof( BerValue ));
991                         ber_dupbv( &soc_attr->a_vals[0], &sc );
992                         soc_attr->a_vals[1].bv_len = 0;
993                         soc_attr->a_vals[1].bv_val = NULL;
994                         soc_attr->a_nvals = (BerVarray) ch_malloc( 2* sizeof( BerValue ));
995                         ber_dupbv( &soc_attr->a_nvals[0], &sc );
996                         soc_attr->a_nvals[1].bv_len = 0;
997                         soc_attr->a_nvals[1].bv_val = NULL;
998
999                         *attrp = soc_attr;
1000                         attrp = &soc_attr->a_next;
1001
1002                 /*
1003                  * It is necessary to try to rewrite attributes with
1004                  * dn syntax because they might be used in ACLs as
1005                  * members of groups; since ACLs are applied to the
1006                  * rewritten stuff, no dn-based subecj clause could
1007                  * be used at the ldap backend side (see
1008                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
1009                  * The problem can be overcome by moving the dn-based
1010                  * ACLs to the target directory server, and letting
1011                  * everything pass thru the ldap backend.
1012                  */
1013                 } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid,
1014                                 SLAPD_DN_SYNTAX ) == 0 ) {
1015                         int i;
1016                         for ( i = 0, bv = attr->a_vals; bv->bv_val; bv++,i++ ) {
1017                                 char *newval;
1018                                 rewriteSession(li->targets[ target ]->rwmap.rwm_rw,
1019                                                 "searchResult", bv->bv_val,
1020                                                 lc->conn, &newval, result); 
1021                                 if (result->type != SUCCESS) {
1022                                         /* FIXME : Handle error */
1023                                         result->type = SUCCESS; 
1024                                 } else {
1025                                         /* left as is */
1026                                         if ( newval == NULL ) {
1027                                                 break;
1028                                         }
1029 #ifdef NEW_LOGGING
1030                                         LDAP_LOG( BACK_META, DETAIL1,
1031                                                 "[rw] searchResult on "
1032                                                 "attr=%s: \"%s\" -> \"%s\"\n",
1033                                                 attr->a_desc->ad_type->
1034                                                 sat_cname.bv_val,
1035                                                 bv->bv_val, newval );
1036 #else /* !NEW_LOGGING */
1037                                         Debug( LDAP_DEBUG_ARGS,
1038                                                 "rw> searchResult on attr=%s:"
1039                                                 " \"%s\" -> \"%s\"\n",
1040                                                 attr->a_desc->ad_type->
1041                                                 sat_cname.bv_val,
1042                                                 bv->bv_val, newval );
1043 #endif /* !NEW_LOGGING */
1044                                         free( bv->bv_val );
1045                                         bv->bv_val = newval;
1046                                         bv->bv_len = strlen( newval );
1047                                 }
1048                         }
1049                 }
1050                 *attrp = attr;
1051                 attrp = &attr->a_next;
1052         }
1053
1054         return ent; 
1055 }
1056
1057 static int
1058 is_one_level_rdn(
1059         const char      *rdn,
1060         int             from
1061 )
1062 {
1063         for ( ; from--; ) {
1064                 if ( DN_SEPARATOR( rdn[ from ] ) ) {
1065                         return 0;
1066                 }
1067         }
1068         return 1;
1069 }
1070
1071 static struct metaconn*  
1072 metaConnect(
1073         Operation*              op, 
1074         SlapReply               *rs,
1075         int                     op_type, 
1076         struct berval           *nbase, 
1077         struct exception        *result)
1078 {
1079         struct metaconn         *lc; 
1080
1081         result->type = SUCCESS; 
1082         lc = meta_back_getconn( op, rs, op_type, nbase, NULL );
1083         if (!lc) {
1084                 result->type = CONN_ERR; 
1085                 return 0; 
1086         }
1087         return lc; 
1088 }
1089
1090 static void
1091 add_filter_attrs(
1092         AttributeName** new_attrs, 
1093         AttributeName* attrs, 
1094         AttributeName* filter_attrs )
1095 {
1096         struct berval all_user = { sizeof(LDAP_ALL_USER_ATTRIBUTES) -1,
1097                                    LDAP_ALL_USER_ATTRIBUTES };
1098
1099         struct berval all_op = { sizeof(LDAP_ALL_OPERATIONAL_ATTRIBUTES) -1,
1100                                         LDAP_ALL_OPERATIONAL_ATTRIBUTES}; 
1101
1102         int alluser = 0; 
1103         int allop = 0; 
1104         int i; 
1105         int count; 
1106
1107         /* duplicate attrs */
1108         if (attrs == NULL) {
1109                 count = 1; 
1110         } else { 
1111                 for (count=0; attrs[count].an_name.bv_val; count++) 
1112                         ;
1113         }
1114         *new_attrs = (AttributeName*)(malloc((count+1)*sizeof(AttributeName))); 
1115         if (attrs == NULL) { 
1116                 (*new_attrs)[0].an_name.bv_val = "*"; 
1117                 (*new_attrs)[0].an_name.bv_len = 1; 
1118                 (*new_attrs)[1].an_name.bv_val = NULL;
1119                 (*new_attrs)[1].an_name.bv_len = 0; 
1120                 alluser = 1; 
1121                 allop = 0; 
1122         } else {  
1123                 for (i=0; i<count; i++) {
1124                         (*new_attrs)[i].an_name = attrs[i].an_name; 
1125                         (*new_attrs)[i].an_desc = attrs[i].an_desc;  
1126                 }
1127                 (*new_attrs)[count].an_name.bv_val = NULL; 
1128                 (*new_attrs)[count].an_name.bv_len = 0; 
1129                 alluser = an_find(*new_attrs, &all_user); 
1130                 allop = an_find(*new_attrs, &all_op); 
1131         }
1132
1133         for ( i=0; filter_attrs[i].an_name.bv_val; i++ ) {
1134                 if ( an_find(*new_attrs, &filter_attrs[i].an_name ))
1135                         continue; 
1136                 if ( is_at_operational(filter_attrs[i].an_desc->ad_type) ) {
1137                         if (allop) 
1138                                 continue; 
1139                 } else if (alluser) 
1140                         continue; 
1141                 *new_attrs = (AttributeName*)(realloc(*new_attrs,
1142                                         (count+2)*sizeof(AttributeName))); 
1143                 (*new_attrs)[count].an_name.bv_val =
1144                                 filter_attrs[i].an_name.bv_val; 
1145                 (*new_attrs)[count].an_name.bv_len =
1146                                 filter_attrs[i].an_name.bv_len; 
1147                 (*new_attrs)[count].an_desc = filter_attrs[i].an_desc; 
1148                 (*new_attrs)[count+1].an_name.bv_val = NULL; 
1149                 (*new_attrs)[count+1].an_name.bv_len = 0; 
1150                 count++; 
1151         }
1152 }
1153
1154 static int 
1155 handleLdapResult(
1156         struct metaconn* lc,
1157         Operation* op, 
1158         SlapReply *rs,
1159         int* msgid, Backend* be, 
1160         AttributeName* attrs, 
1161         int attrsonly, 
1162         int candidates, 
1163         int cacheable, 
1164         Entry*** entry_array, 
1165         int curr_limit, 
1166         int slimit,
1167         struct exception* result)
1168 {
1169         Entry  *entry;
1170         char *match = NULL, *err = NULL, *cache_ename = NULL;
1171         int sres; 
1172         int mres = LDAP_SUCCESS; 
1173         int num_entries = 0, count, i, rc;     
1174         struct timeval tv = {0, 0}; 
1175         struct metasingleconn* lsc; 
1176         struct metainfo         *li = ( struct metainfo * )be->be_private;
1177         result->rc = 0; 
1178         result->type = SUCCESS; 
1179
1180         for ( count = 0, rc = 0; candidates > 0; ) {
1181                 int ab, gotit = 0;
1182
1183                 /* check for abandon */
1184                 ab = op->o_abandon;
1185
1186                 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); lsc++, i++ ) {
1187                         if ( lsc->candidate != META_CANDIDATE ) {
1188                                 continue;
1189                         }
1190
1191                         if ( ab ) {
1192                                 ldap_abandon( lsc->ld, msgid[ i ] );
1193                                 result->type = ABANDON_ERR;
1194                                 break; 
1195                         }
1196
1197                         if ( slimit > 0 && num_entries == slimit ) {
1198                                 result->type = SLIMIT_ERR; 
1199                                 break; 
1200                         }
1201
1202                         if ((entry = get_result_entry(be, lc, lsc,
1203                                                 msgid, i, &tv, result))) {
1204                                 rs->sr_entry = entry;
1205                                 rs->sr_attrs = op->ors_attrs; 
1206                                 send_search_entry( op, rs );
1207                                 rs->sr_entry = NULL;
1208                                 rs->sr_attrs = NULL; 
1209                                 if ((cacheable) &&
1210                                                 (num_entries < curr_limit))  {
1211                                         rewriteSession( li->rwinfo,
1212                                                         "cacheResult",
1213                                                         entry->e_name.bv_val,
1214                                                         lc->conn,
1215                                                         &cache_ename, result );  
1216                                         free(entry->e_name.bv_val); 
1217                                         if (result->type != SUCCESS) {
1218                                                 return 0; 
1219                                         }
1220                                         ber_str2bv(cache_ename,
1221                                                 strlen(cache_ename),
1222                                                 0, &entry->e_name); 
1223                                         ber_dupbv(&entry->e_nname,
1224                                                 &entry->e_name); 
1225                                         *entry_array = (Entry**)realloc(
1226                                                         *entry_array,
1227                                                         (( num_entries+2 ) *
1228                                                          sizeof( Entry* )));
1229                                         (*entry_array)[num_entries] = entry;    
1230                                         (*entry_array)[num_entries+1] = NULL;
1231                                 }
1232                                 num_entries++; 
1233                                 gotit = 1; 
1234                         } else if (result->type == REWRITING_ERR) {
1235                                 return 0; 
1236                         } else if (result->type == TIMEOUT_ERR) {
1237                                 result->type = SUCCESS; 
1238                                 continue;  
1239                         } else if (result->type == CREATE_ENTRY_ERR) {
1240                                 break; 
1241                         } else if (result->rc == -1) {
1242                                 break; 
1243                         } else {
1244                                 rs->sr_err = result->rc;
1245                                 sres = ldap_back_map_result(rs);
1246                                 if (mres == LDAP_SUCCESS &&
1247                                                 sres != LDAP_SUCCESS) {
1248                                         mres = sres; 
1249                                         ldap_get_option(lsc->ld,
1250                                                 LDAP_OPT_ERROR_STRING, &err);
1251                                         ldap_get_option(lsc->ld,
1252                                                 LDAP_OPT_MATCHED_DN, &match);
1253                                 }
1254                                 lsc->candidate = META_NOT_CANDIDATE; 
1255                                 candidates--; 
1256                                 result->type = SUCCESS; 
1257                         }
1258                 }
1259                 switch (result->type) {
1260                 case RESULT_ERR: 
1261 #ifdef NEW_LOGGING
1262                         LDAP_LOG( BACK_META, DETAIL1,
1263                                         "ldap_result error, rc = -1\n",
1264                                         0, 0, 0 );
1265 #else /* !NEW_LOGGING */
1266                         Debug( LDAP_DEBUG_ANY, "ldap_result error, rc = -1\n",
1267                                         0, 0, 0 );
1268 #endif /* !NEW_LOGGING */
1269                         rs->sr_err = LDAP_OTHER;
1270                         send_ldap_result( op, rs );
1271                         return 0; 
1272
1273                 case CREATE_ENTRY_ERR: 
1274 #ifdef NEW_LOGGING
1275                         LDAP_LOG( BACK_META, DETAIL1,
1276                                         "Error in parsing result \n",
1277                                         0, 0, 0 );
1278 #else /* !NEW_LOGGING */
1279                         Debug( LDAP_DEBUG_ANY, "Error in parsing result \n",
1280                                         0, 0, 0 );
1281 #endif /* !NEW_LOGGING */
1282                         rs->sr_err = LDAP_OTHER;
1283                         send_ldap_result( op, rs );
1284                         result->type = RESULT_ERR; 
1285                         return 0; 
1286
1287                 case SLIMIT_ERR: 
1288 #ifdef NEW_LOGGING
1289                         LDAP_LOG( BACK_META, DETAIL1, "Size limit exceeded \n",
1290                                         0, 0, 0 );
1291 #else /* !NEW_LOGGING */
1292                         Debug( LDAP_DEBUG_ANY, "Size limit exceeded \n",
1293                                         0, 0, 0 );
1294 #endif /* !NEW_LOGGING */
1295                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1296                         send_ldap_result( op, rs );
1297                         result->type = RESULT_ERR; 
1298                         return 0;
1299
1300                 case ABANDON_ERR: 
1301 #ifdef NEW_LOGGING
1302                         LDAP_LOG( BACK_META, DETAIL1,
1303                                         "search operation abandoned \n",
1304                                         0, 0, 0 );
1305 #else /* !NEW_LOGGING */
1306                         Debug( LDAP_DEBUG_ANY, "search operation abandoned \n",
1307                                         0, 0, 0 );
1308 #endif /* !NEW_LOGGING */
1309                         result->type = RESULT_ERR; 
1310                         return 0; 
1311
1312                 default:
1313                         /* assert( 0 ); */
1314                         break;
1315                 }
1316                 if ( gotit == 0 ) {
1317                         tv.tv_sec = 0;
1318                         tv.tv_usec = 100000;
1319                         ldap_pvt_thread_yield();
1320                 } else {
1321                         tv.tv_sec = 0;
1322                         tv.tv_usec = 0;
1323                 }
1324         }
1325
1326         rs->sr_err = mres;
1327         rs->sr_text = err;
1328         rs->sr_matched = match;
1329
1330         send_ldap_result( op, rs );
1331
1332         rs->sr_text = NULL;
1333         rs->sr_matched = NULL;
1334
1335         if (err) 
1336                 free(err); 
1337
1338         if (match) 
1339                 free(match); 
1340     
1341         result->type = (mres == LDAP_SUCCESS) ? SUCCESS : RESULT_ERR; 
1342         return num_entries; 
1343 }
1344
1345 static Entry* 
1346 get_result_entry(
1347         Backend* be, 
1348         struct metaconn* lc, 
1349         struct metasingleconn* lsc, 
1350         int* msgid,
1351         int i, 
1352         struct timeval* tv, 
1353         struct exception* result)
1354 {
1355         Entry* entry; 
1356         LDAPMessage     *res, *e; 
1357         int rc; 
1358         int sres = LDAP_SUCCESS; 
1359
1360         rc = ldap_result( lsc->ld, msgid[ i ],
1361                         0, tv, &res );
1362
1363         if ( rc == 0 ) {
1364                 result->type = TIMEOUT_ERR; 
1365                 return NULL; 
1366         } else if ( rc == -1 ) {
1367                 result->rc = -1; 
1368                 result->type = RESULT_ERR; 
1369                 return NULL; 
1370         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
1371                 e = ldap_first_entry( lsc->ld, res );
1372                 entry = meta_create_entry(be, lc, i, e, result);  
1373                 if (!entry) {
1374                         return NULL; 
1375                 }    
1376                 ldap_msgfree( res );
1377                 result->type = SUCCESS; 
1378                 return entry; 
1379         } else {
1380                 sres = ldap_result2error( lsc->ld,
1381                                 res, 1 );
1382                 result->rc = sres; 
1383                 result->type = RESULT_ERR; 
1384                 return NULL; 
1385         }
1386 }       
1387
1388 static void
1389 rewriteSession(
1390         struct rewrite_info* info, 
1391         const char* rewriteContext, 
1392         const char* string, 
1393         const void* cookie,  
1394         char** base, 
1395         struct exception* result)
1396 {
1397         int rc = rewrite_session(info, rewriteContext, string, cookie, base); 
1398         if (rc != REWRITE_REGEXEC_OK) {
1399                 result->rc = rc; 
1400                 result->type = REWRITING_ERR; 
1401
1402                 if (strcmp(rewriteContext, "searchBase") == 0) 
1403 #ifdef NEW_LOGGING
1404                         LDAP_LOG( BACK_META, DETAIL1,
1405                                 "Problem in rewriting search base\n", 0, 0, 0 );
1406 #else /* !NEW_LOGGING */
1407                         Debug( LDAP_DEBUG_ANY,
1408                                 "Problem in rewriting search base\n", 0, 0, 0 );
1409 #endif /* !NEW_LOGGING */
1410                 if (strcmp(rewriteContext, "searchFilter") == 0) 
1411 #ifdef NEW_LOGGING
1412                         LDAP_LOG( BACK_META, DETAIL1,
1413                                 "Problem in rewriting search filter\n",
1414                                 0, 0, 0 );
1415 #else /* !NEW_LOGGING */
1416                         Debug( LDAP_DEBUG_ANY,
1417                                 "Problem in rewriting search filter\n",
1418                                 0, 0, 0 );
1419 #endif /* !NEW_LOGGING */
1420                 if (strcmp(rewriteContext, "searchResult") == 0) 
1421 #ifdef NEW_LOGGING
1422                         LDAP_LOG( BACK_META, DETAIL1,
1423                                 "Problem in rewriting DN, or DN syntax "
1424                                 "attributes of search result\n", 0, 0, 0 );
1425 #else /* !NEW_LOGGING */
1426                         Debug( LDAP_DEBUG_ANY,
1427                                 "Problem in rewriting DN, or DN syntax "
1428                                 "attributes of search result\n", 0, 0, 0 );
1429 #endif /* !NEW_LOGGING */
1430                 if (strcmp(rewriteContext, "cacheBase") == 0) 
1431 #ifdef NEW_LOGGING
1432                         LDAP_LOG( BACK_META, DETAIL1,
1433                                 "Problem in rewriting search base with "
1434                                 "cache base\n", 0, 0, 0 );
1435 #else /* !NEW_LOGGING */
1436                         Debug( LDAP_DEBUG_ANY,
1437                                 "Problem in rewriting search base with "
1438                                 "cache base\n", 0, 0, 0 );
1439 #endif /* !NEW_LOGGING */
1440                 if (strcmp(rewriteContext, "cacheResult") == 0) 
1441 #ifdef NEW_LOGGING
1442                         LDAP_LOG( BACK_META, DETAIL1,
1443                                 "Problem in rewriting DN for cached entries\n",
1444                                 0, 0, 0 );
1445 #else /* !NEW_LOGGING */
1446                         Debug( LDAP_DEBUG_ANY,
1447                                 "Problem in rewriting DN for cached entries\n",
1448                                 0, 0, 0 );
1449 #endif /* !NEW_LOGGING */
1450                 if (strcmp(rewriteContext, "cacheReturn") == 0) 
1451 #ifdef NEW_LOGGING
1452                         LDAP_LOG( BACK_META, DETAIL1,
1453                                 "Problem in rewriting DN for answerable "
1454                                 "entries\n", 0, 0, 0 );
1455 #else /* !NEW_LOGGING */
1456                         Debug( LDAP_DEBUG_ANY,
1457                                 "Problem in rewriting DN for answerable "
1458                                 "entries\n", 0, 0, 0 );
1459 #endif /* !NEW_LOGGING */
1460         } else {
1461                 result->type = SUCCESS;
1462         }
1463 }
1464
1465 static int 
1466 get_attr_set(
1467         AttributeName* attrs, 
1468         query_manager* qm, 
1469         int num )
1470 {
1471         int i; 
1472         for (i=0; i<num; i++) {
1473                 if (attrscmp(attrs, qm->attr_sets[i].attrs)) 
1474                         return i;
1475         }
1476         return -1; 
1477 }
1478
1479 static int 
1480 attrscmp(
1481         AttributeName* attrs_in, 
1482         AttributeName* attrs)
1483 {
1484         int i, count1, count2; 
1485         if ( attrs_in == NULL ) {
1486                 return (attrs ? 0 : 1); 
1487         } 
1488         if ( attrs == NULL ) 
1489                 return 0; 
1490         
1491         for ( count1=0;
1492               attrs_in && attrs_in[count1].an_name.bv_val != NULL;
1493               count1++ )
1494                 ;
1495         for ( count2=0;
1496               attrs && attrs[count2].an_name.bv_val != NULL;
1497               count2++) 
1498                 ;
1499         if ( count1 != count2 )
1500                 return 0; 
1501
1502         for ( i=0; i<count1; i++ ) {
1503                 if ( !an_find(attrs, &attrs_in[i].an_name ))
1504                         return 0; 
1505         }
1506         return 1; 
1507 }
1508
1509 static char* 
1510 cache_entries(
1511         Operation       *op,
1512         SlapReply       *rs,
1513         Entry** entry_array, 
1514         cache_manager* cm, 
1515         struct exception* result)
1516 {
1517         int             i; 
1518         int             return_val; 
1519         int             cache_size; 
1520         Entry           *e; 
1521         struct berval   query_uuid; 
1522         struct berval   crp_uuid; 
1523         char            uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ], *crpid; 
1524         char            crpuuid[40]; 
1525         query_manager   *qm = cm->qm;
1526     
1527         result->type = SUCCESS; 
1528         query_uuid.bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf)); 
1529         query_uuid.bv_val = ch_strdup(uuidbuf);
1530
1531 #ifdef NEW_LOGGING
1532         LDAP_LOG( BACK_META, DETAIL1, "UUID for query being added = %s\n",
1533                         uuidbuf, 0, 0 );
1534 #else /* !NEW_LOGGING */
1535         Debug( LDAP_DEBUG_ANY, "UUID for query being added = %s\n",
1536                         uuidbuf, 0, 0 );
1537 #endif /* !NEW_LOGGING */
1538         
1539         for ( i=0; ( entry_array && (e=entry_array[i]) ); i++ ) {
1540 #ifdef NEW_LOGGING
1541                 LDAP_LOG( BACK_META, DETAIL2, "LOCKING REMOVE MUTEX\n",
1542                                 0, 0, 0 );
1543 #else /* !NEW_LOGGING */
1544                 Debug( LDAP_DEBUG_NONE, "LOCKING REMOVE MUTEX\n", 0, 0, 0 );
1545 #endif /* !NEW_LOGGING */
1546                 ldap_pvt_thread_mutex_lock(&cm->remove_mutex); 
1547 #ifdef NEW_LOGGING
1548                 LDAP_LOG( BACK_META, DETAIL2, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1549 #else /* !NEW_LOGGING */
1550                 Debug( LDAP_DEBUG_NONE, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1551 #endif /* !NEW_LOGGING */
1552                 if ( cm->cache_size > (cm->thresh_hi) ) {
1553                         while(cm->cache_size > (cm->thresh_lo)) {
1554                                 crpid = cache_replacement(qm);
1555                                 if (crpid == NULL) {
1556                                         result->type = REMOVE_ERR; 
1557                                 } else {
1558                                         strcpy(crpuuid, crpid); 
1559                                         crp_uuid.bv_val = crpuuid; 
1560                                         crp_uuid.bv_len = strlen(crpuuid); 
1561 #ifdef NEW_LOGGING
1562                                         LDAP_LOG( BACK_META, DETAIL1,
1563                                                 "Removing query UUID %s\n",
1564                                                 crpuuid, 0, 0 );
1565 #else /* !NEW_LOGGING */
1566                                         Debug( LDAP_DEBUG_ANY,
1567                                                 "Removing query UUID %s\n",
1568                                                 crpuuid, 0, 0 );
1569 #endif /* !NEW_LOGGING */
1570                                         return_val = remove_query_data(op, rs,
1571                                                         &crp_uuid, result); 
1572 #ifdef NEW_LOGGING
1573                                         LDAP_LOG( BACK_META, DETAIL1,
1574                                                 "QUERY REMOVED, SIZE=%d\n",
1575                                                 return_val, 0, 0);
1576 #else /* !NEW_LOGGING */
1577                                         Debug( LDAP_DEBUG_ANY,
1578                                                 "QUERY REMOVED, SIZE=%d\n",
1579                                                 return_val, 0, 0);
1580 #endif /* !NEW_LOGGING */
1581                                         ldap_pvt_thread_mutex_lock(
1582                                                         &cm->cache_mutex ); 
1583                                         cm->total_entries -= result->rc; 
1584                                         cm->num_cached_queries--; 
1585 #ifdef NEW_LOGGING
1586                                         LDAP_LOG( BACK_META, DETAIL1,
1587                                                 "STORED QUERIES = %lu\n",
1588                                                 cm->num_cached_queries, 0, 0 );
1589 #else /* !NEW_LOGGING */
1590                                         Debug( LDAP_DEBUG_ANY,
1591                                                 "STORED QUERIES = %lu\n",
1592                                                 cm->num_cached_queries, 0, 0 );
1593 #endif /* !NEW_LOGGING */
1594                                         ldap_pvt_thread_mutex_unlock(
1595                                                         &cm->cache_mutex );
1596                                         cm->cache_size = (return_val >
1597                                                 cm->cache_size) ?
1598                                                 0 : (cm->cache_size-return_val);
1599 #ifdef NEW_LOGGING
1600                                         LDAP_LOG( BACK_META, DETAIL1,
1601                                                 "QUERY REMOVED, CACHE SIZE="
1602                                                 "%lu bytes %d entries\n",
1603                                                 cm->cache_size,
1604                                                 cm->total_entries, 0 );
1605 #else /* !NEW_LOGGING */
1606                                         Debug( LDAP_DEBUG_ANY,
1607                                                 "QUERY REMOVED, CACHE SIZE="
1608                                                 "%lu bytes %d entries\n",
1609                                                 cm->cache_size,
1610                                                 cm->total_entries, 0 );
1611 #endif /* !NEW_LOGGING */
1612                                 }
1613                         }
1614                 }
1615
1616                 rs->sr_entry = e;
1617                 return_val = merge_entry(op, rs, &query_uuid, result);
1618                 rs->sr_entry = NULL;
1619                 cm->cache_size += return_val;
1620 #ifdef NEW_LOGGING
1621                 LDAP_LOG( BACK_META, DETAIL1,
1622                         "ENTRY ADDED/MERGED, CACHE SIZE=%lu bytes\n",
1623                         cm->cache_size, 0, 0 );
1624 #else /* !NEW_LOGGING */
1625                 Debug( LDAP_DEBUG_ANY,
1626                         "ENTRY ADDED/MERGED, CACHE SIZE=%lu bytes\n",
1627                         cm->cache_size, 0, 0 );
1628 #endif /* !NEW_LOGGING */
1629 #ifdef NEW_LOGGING
1630                 LDAP_LOG( BACK_META, DETAIL2, "UNLOCKING REMOVE MUTEX\n",
1631                                 0, 0, 0 );
1632 #else /* !NEW_LOGGING */
1633                 Debug( LDAP_DEBUG_NONE, "UNLOCKING REMOVE MUTEX\n", 0, 0, 0 );
1634 #endif /* !NEW_LOGGING */
1635                 ldap_pvt_thread_mutex_unlock(&cm->remove_mutex); 
1636 #ifdef NEW_LOGGING
1637                 LDAP_LOG( BACK_META, DETAIL2, "UNLOCKED REMOVE MUTEX\n",
1638                                 0, 0, 0 );
1639 #else /* !NEW_LOGGING */
1640                 Debug( LDAP_DEBUG_NONE, "UNLOCKED REMOVE MUTEX\n", 0, 0, 0 );
1641 #endif /* !NEW_LOGGING */
1642                 if (result->type != SUCCESS) 
1643                         return 0; 
1644                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
1645                 cm->total_entries += result->rc; 
1646 #ifdef NEW_LOGGING
1647                 LDAP_LOG( BACK_META, DETAIL1,
1648                         "ENTRY ADDED/MERGED, SIZE=%d, CACHED ENTRIES=%d\n",
1649                         return_val, cm->total_entries, 0 );
1650 #else /* !NEW_LOGGING */
1651                 Debug( LDAP_DEBUG_ANY,
1652                         "ENTRY ADDED/MERGED, SIZE=%d, CACHED ENTRIES=%d\n",
1653                         return_val, cm->total_entries, 0 );
1654 #endif /* !NEW_LOGGING */
1655                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
1656         }
1657         ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
1658         cm->num_cached_queries++; 
1659 #ifdef NEW_LOGGING
1660         LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1661                         cm->num_cached_queries, 0, 0 );
1662 #else /* !NEW_LOGGING */
1663         Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1664                         cm->num_cached_queries, 0, 0 );
1665 #endif /* !NEW_LOGGING */
1666         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
1667
1668         return query_uuid.bv_val; 
1669 }
1670
1671 static int 
1672 is_temp_answerable(
1673         int attr_set, 
1674         struct berval* tempstr, 
1675         query_manager* qm, 
1676         int template_id )
1677 {
1678         int i; 
1679         int* id_array; 
1680         char* str;
1681         int result = 0; 
1682         i = qm->templates[template_id].attr_set_index; 
1683         str = qm->templates[template_id].querystr; 
1684
1685         if (attr_set == i) {
1686                 result = 1; 
1687         } else { 
1688                 id_array = qm->attr_sets[attr_set].ID_array;    
1689
1690                 while (*id_array != -1) {
1691                         if (*id_array == i) 
1692                                 result = 1; 
1693                         id_array++; 
1694                 }
1695         }
1696         if (!result) 
1697                 return 0; 
1698         if (strcasecmp(str, tempstr->bv_val) == 0)  
1699                 return 1; 
1700         return 0; 
1701 }
1702
1703 static void* 
1704 consistency_check(void* operation)
1705 {
1706         Operation* op = (Operation*)operation; 
1707
1708         SlapReply rs = {REP_RESULT}; 
1709
1710         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
1711         cache_manager*  cm = li->cm; 
1712         query_manager* qm = cm->qm; 
1713         CachedQuery* query, *query_prev; 
1714         time_t curr_time; 
1715         struct berval uuid;  
1716         struct exception result; 
1717         int i, return_val; 
1718         QueryTemplate* templ;
1719
1720
1721         op->o_bd = li->glue_be;
1722       
1723         for(;;) {
1724                 ldap_pvt_thread_sleep(cm->cc_period);     
1725                 for (i=0; qm->templates[i].querystr; i++) {
1726                         templ = qm->templates + i; 
1727                         query = templ->query_last; 
1728                         curr_time = slap_get_time(); 
1729                         ldap_pvt_thread_mutex_lock(&cm->remove_mutex); 
1730                         while (query && (query->expiry_time < curr_time)) {
1731                                 ldap_pvt_thread_mutex_lock(&qm->lru_mutex); 
1732                                 remove_query(qm, query); 
1733                                 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 
1734 #ifdef NEW_LOGGING
1735                                 LDAP_LOG( BACK_META, DETAIL1, "Lock CR index = %d\n",
1736                                                 i, 0, 0 );
1737 #else /* !NEW_LOGGING */
1738                                 Debug( LDAP_DEBUG_ANY, "Lock CR index = %d\n",
1739                                                 i, 0, 0 );
1740 #endif /* !NEW_LOGGING */
1741                                 ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);  
1742                                 remove_from_template(query, templ); 
1743 #ifdef NEW_LOGGING
1744                                 LDAP_LOG( BACK_META, DETAIL1,
1745                                                 "TEMPLATE %d QUERIES-- %d\n",
1746                                                 i, templ->no_of_queries, 0 );
1747 #else /* !NEW_LOGGING */
1748                                 Debug( LDAP_DEBUG_ANY, "TEMPLATE %d QUERIES-- %d\n",
1749                                                 i, templ->no_of_queries, 0 );
1750 #endif /* !NEW_LOGGING */
1751 #ifdef NEW_LOGGING
1752                                 LDAP_LOG( BACK_META, DETAIL1, "Unlock CR index = %d\n",
1753                                                 i, 0, 0 );
1754 #else /* !NEW_LOGGING */
1755                                 Debug( LDAP_DEBUG_ANY, "Unlock CR index = %d\n",
1756                                                 i, 0, 0 );
1757 #endif /* !NEW_LOGGING */
1758                                 ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);  
1759                                 uuid.bv_val = query->q_uuid; 
1760                                 uuid.bv_len = strlen(query->q_uuid); 
1761                                 return_val = remove_query_data(op, &rs, &uuid, &result); 
1762 #ifdef NEW_LOGGING
1763                                 LDAP_LOG( BACK_META, DETAIL1,
1764                                                 "STALE QUERY REMOVED, SIZE=%d\n",
1765                                                 return_val, 0, 0 );
1766 #else /* !NEW_LOGGING */
1767                                 Debug( LDAP_DEBUG_ANY, "STALE QUERY REMOVED, SIZE=%d\n",
1768                                                         return_val, 0, 0 );
1769 #endif /* !NEW_LOGGING */
1770                                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 
1771                                 cm->total_entries -= result.rc; 
1772                                 cm->num_cached_queries--; 
1773 #ifdef NEW_LOGGING
1774                                 LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1775                                                 cm->num_cached_queries, 0, 0 );
1776 #else /* !NEW_LOGGING */
1777                                 Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1778                                                 cm->num_cached_queries, 0, 0 );
1779 #endif /* !NEW_LOGGING */
1780                                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 
1781                                 cm->cache_size = (return_val > cm->cache_size) ?
1782                                                         0: (cm->cache_size-return_val);
1783 #ifdef NEW_LOGGING
1784                                 LDAP_LOG( BACK_META, DETAIL1,
1785                                         "STALE QUERY REMOVED, CACHE SIZE=%lu bytes %d "
1786                                         "entries\n", cm->cache_size,
1787                                         cm->total_entries, 0 );
1788 #else /* !NEW_LOGGING */
1789                                 Debug( LDAP_DEBUG_ANY,
1790                                         "STALE QUERY REMOVED, CACHE SIZE=%lu bytes %d "
1791                                         "entries\n", cm->cache_size,
1792                                         cm->total_entries, 0 );
1793 #endif /* !NEW_LOGGING */
1794                                 query_prev = query; 
1795                                 query = query->prev; 
1796                                 free_query(query_prev); 
1797                         }
1798                         ldap_pvt_thread_mutex_unlock(&cm->remove_mutex); 
1799                 }
1800         }
1801 }
1802
1803 static int
1804 cache_back_sentry(
1805         Operation* op, 
1806         SlapReply *rs )
1807
1808         slap_callback           *cb = op->o_callback; 
1809         /*struct metainfo       *li = ( struct metainfo * )op->o_bd->be_private;*/
1810         Backend* be = (Backend*)(cb->sc_private);
1811         struct metainfo         *li = ( struct metainfo * )be->be_private;
1812  
1813         char                    *ename = NULL;
1814         struct exception        result;
1815         struct berval           dn;
1816         struct berval           ndn;
1817  
1818         if (rs->sr_type == REP_SEARCH) {
1819                 dn = rs->sr_entry->e_name; 
1820                 ndn = rs->sr_entry->e_nname; 
1821
1822                 rewriteSession( li->rwinfo, "cacheReturn",
1823                                 rs->sr_entry->e_name.bv_val, op->o_conn,
1824                                 &ename, &result );  
1825                 ber_str2bv(ename, strlen(ename), 0, &rs->sr_entry->e_name); 
1826                 /* FIXME: should we normalize this? */
1827                 ber_dupbv(&rs->sr_entry->e_nname, &rs->sr_entry->e_name); 
1828
1829                 op->o_callback = NULL; 
1830
1831                 send_search_entry( op, rs );
1832          
1833                 rs->sr_entry->e_name = dn; 
1834                 rs->sr_entry->e_nname = ndn; 
1835
1836                 op->o_callback = cb; 
1837                 return 0; 
1838         } else if (rs->sr_type == REP_RESULT) { 
1839                 op->o_callback = NULL; 
1840                 send_ldap_result( op, rs ); 
1841                 return 0; 
1842         }
1843 }
1844 #endif