]> git.sur5r.net Git - openldap/blob - servers/slapd/slapi/slapi_ops.c
assert expects int. (int)<nonnull ptr/long> can be 0. Use assert(arg!=0/NULL).
[openldap] / servers / slapd / slapi / slapi_ops.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2002-2005 The OpenLDAP Foundation.
5  * Portions Copyright 1997,2002-2003 IBM Corporation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by IBM Corporation for use in
18  * IBM products and subsequently ported to OpenLDAP Software by
19  * Steve Omrani.  Additional significant contributors include:
20  *   Luke Howard
21  */
22
23 #include "portable.h"
24
25 #include <ac/string.h>
26 #include <ac/stdarg.h>
27 #include <ac/ctype.h>
28 #include <ac/unistd.h>
29
30 #include <slap.h>
31 #include <lber_pvt.h>
32 #include <slapi.h>
33
34 /*
35  * use a fake listener when faking a connection,
36  * so it can be used in ACLs
37  */
38 static struct slap_listener slap_unknown_listener = {
39         BER_BVC("unknown"),     /* FIXME: use a URI form? (e.g. slapi://) */
40         BER_BVC("UNKNOWN")
41 };
42
43 static void
44 internal_result_v3(
45         Operation       *op, 
46         SlapReply       *rs )
47 {
48 #ifdef notdef
49         /* XXX needs review after internal API change */
50         /* rs->sr_nentries appears to always be 0 */
51         if (op->o_tag == LDAP_REQ_SEARCH)
52                 slapi_pblock_set( (Slapi_PBlock *)op->o_pb,
53                         SLAPI_NENTRIES, (void *)rs->sr_nentries );
54 #endif
55
56         return;
57 }
58
59 static int
60 internal_search_entry(
61         Operation       *op,
62         SlapReply       *rs )
63 {
64         int nentries = 0, i = 0;
65         Slapi_Entry **head = NULL, **tp;
66         Slapi_Entry *entry;
67
68         entry = slapi_entry_dup( rs->sr_entry );
69         if ( entry == NULL ) {
70                 return 1;
71         }
72
73         slapi_pblock_get( (Slapi_PBlock *)op->o_pb,
74                         SLAPI_NENTRIES, &nentries );
75         slapi_pblock_get( (Slapi_PBlock *)op->o_pb,
76                         SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &head );
77         
78         i = nentries + 1;
79         if ( nentries == 0 ) {
80                 tp = (Slapi_Entry **)slapi_ch_malloc( 2 * sizeof(Slapi_Entry *) );
81                 if ( tp == NULL ) {
82                         slapi_entry_free( entry );
83                         return 1;
84                 }
85
86                 tp[ 0 ] = entry;
87         } else {
88                 tp = (Slapi_Entry **)slapi_ch_realloc( (char *)head,
89                                 sizeof(Slapi_Entry *) * ( i + 1 ) );
90                 if ( tp == NULL ) {
91                         slapi_entry_free( entry );
92                         return 1;
93                 }
94                 tp[ i - 1 ] = entry;
95         }
96         tp[ i ] = NULL;
97                   
98         slapi_pblock_set( (Slapi_PBlock *)op->o_pb,
99                         SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, (void *)tp );
100         slapi_pblock_set( (Slapi_PBlock *)op->o_pb,
101                         SLAPI_NENTRIES, (void *)i );
102
103         return LDAP_SUCCESS;
104 }
105
106 static void
107 internal_result_ext(
108         Operation       *op,    
109         SlapReply       *sr )
110 {
111         return;
112 }
113
114 static int
115 internal_search_reference(
116         Operation       *op,    
117         SlapReply       *sr )
118 {
119         return LDAP_SUCCESS;
120 }
121
122 Connection *
123 slapi_int_init_connection(
124         char *DN, 
125         int OpType ) 
126
127         Connection *pConn;
128         ber_len_t max = sockbuf_max_incoming;
129
130         pConn = (Connection *) slapi_ch_calloc(1, sizeof(Connection));
131         if (pConn == NULL) {
132                 return (Connection *)NULL;
133         }
134
135         LDAP_STAILQ_INIT( &pConn->c_pending_ops );
136
137         pConn->c_pending_ops.stqh_first =
138                 (Operation *) slapi_ch_calloc( 1, sizeof(Operation) );
139         if ( pConn->c_pending_ops.stqh_first == NULL ) { 
140                 slapi_ch_free( (void **)&pConn );
141                 return (Connection *)NULL;
142         }
143
144         pConn->c_pending_ops.stqh_first->o_pb = 
145                 (Slapi_PBlock *) slapi_pblock_new();
146         if ( pConn->c_pending_ops.stqh_first->o_pb == NULL ) {
147                 slapi_ch_free( (void **)&pConn->c_pending_ops.stqh_first );
148                 slapi_ch_free( (void **)&pConn );
149                 return (Connection *)NULL;
150         }
151
152         /* connection object */
153         pConn->c_authmech.bv_val = NULL;
154         pConn->c_authmech.bv_len = 0;
155         pConn->c_dn.bv_val = NULL;
156         pConn->c_dn.bv_len = 0;
157         pConn->c_ndn.bv_val = NULL;
158         pConn->c_ndn.bv_len = 0;
159
160         pConn->c_listener = &slap_unknown_listener;
161         ber_dupbv( &pConn->c_peer_domain, (struct berval *)&slap_unknown_bv );
162         ber_dupbv( &pConn->c_peer_name, (struct berval *)&slap_unknown_bv );
163
164         LDAP_STAILQ_INIT( &pConn->c_ops );
165
166         pConn->c_sasl_bind_mech.bv_val = NULL;
167         pConn->c_sasl_bind_mech.bv_len = 0;
168         pConn->c_sasl_authctx = NULL;
169         pConn->c_sasl_sockctx = NULL;
170         pConn->c_sasl_extra = NULL;
171
172         pConn->c_sb = ber_sockbuf_alloc( );
173
174         ber_sockbuf_ctrl( pConn->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
175
176         pConn->c_currentber = NULL;
177
178         /* should check status of thread calls */
179         ldap_pvt_thread_mutex_init( &pConn->c_mutex );
180         ldap_pvt_thread_mutex_init( &pConn->c_write_mutex );
181         ldap_pvt_thread_cond_init( &pConn->c_write_cv );
182
183         ldap_pvt_thread_mutex_lock( &pConn->c_mutex );
184
185         pConn->c_n_ops_received = 0;
186         pConn->c_n_ops_executing = 0;
187         pConn->c_n_ops_pending = 0;
188         pConn->c_n_ops_completed = 0;
189
190         pConn->c_n_get = 0;
191         pConn->c_n_read = 0;
192         pConn->c_n_write = 0;
193
194         pConn->c_protocol = LDAP_VERSION3; 
195
196         pConn->c_activitytime = pConn->c_starttime = slap_get_time();
197
198         /*
199          * A real connection ID is required, because syncrepl associates
200          * pending CSNs with unique ( connection, operation ) tuples.
201          * Setting a fake connection ID will cause slap_get_commit_csn()
202          * to return a stale value.
203          */
204         connection_assign_nextid( pConn );
205
206         pConn->c_conn_state  = 0x01;    /* SLAP_C_ACTIVE */
207         pConn->c_struct_state = 0x02;   /* SLAP_C_USED */
208
209         pConn->c_ssf = pConn->c_transport_ssf = 0;
210         pConn->c_tls_ssf = 0;
211
212         backend_connection_init( pConn );
213
214         pConn->c_send_ldap_result = internal_result_v3;
215         pConn->c_send_search_entry = internal_search_entry;
216         pConn->c_send_ldap_extended = internal_result_ext;
217         pConn->c_send_search_reference = internal_search_reference;
218
219         /* operation object */
220         pConn->c_pending_ops.stqh_first->o_tag = OpType;
221         pConn->c_pending_ops.stqh_first->o_protocol = LDAP_VERSION3; 
222         pConn->c_pending_ops.stqh_first->o_authmech.bv_val = NULL; 
223         pConn->c_pending_ops.stqh_first->o_authmech.bv_len = 0; 
224         pConn->c_pending_ops.stqh_first->o_time = slap_get_time();
225         pConn->c_pending_ops.stqh_first->o_do_not_cache = 1;
226         pConn->c_pending_ops.stqh_first->o_threadctx = ldap_pvt_thread_pool_context();
227         pConn->c_pending_ops.stqh_first->o_tmpmemctx = NULL;
228         pConn->c_pending_ops.stqh_first->o_tmpmfuncs = &ch_mfuncs;
229         pConn->c_pending_ops.stqh_first->o_conn = pConn;
230         pConn->c_pending_ops.stqh_first->o_connid = pConn->c_connid;
231
232         ldap_pvt_thread_mutex_unlock( &pConn->c_mutex );
233
234         return pConn;
235 }
236
237 void slapi_int_connection_destroy( Connection **pConn )
238 {
239         Connection *conn = *pConn;
240         Operation *op;
241
242         if ( pConn == NULL ) {
243                 return;
244         }
245
246         op = (Operation *)conn->c_pending_ops.stqh_first;
247
248         slap_graduate_commit_csn( op );
249
250         if ( op->o_req_dn.bv_val != NULL ) {
251                 slapi_ch_free( (void **)&op->o_req_dn.bv_val );
252         }
253         if ( op->o_req_ndn.bv_val != NULL ) {
254                 slapi_ch_free( (void **)&op->o_req_ndn.bv_val );
255         }
256
257         if ( conn->c_sb != NULL ) {
258                 ber_sockbuf_free( conn->c_sb );
259         }
260         if ( op != NULL ) {
261                 slapi_ch_free( (void **)&op );
262         }
263         slapi_ch_free( (void **)pConn );
264 }
265
266 /*
267  * Function : values2obj
268  * Convert an array of strings into a BerVarray.
269  * the strings.
270  */
271 static int
272 values2obj_copy(
273         char **ppValue,
274         BerVarray *bvobj )
275 {
276         int i;
277         BerVarray tmpberval;
278
279         if ( ppValue == NULL ) {
280                 *bvobj = NULL;
281                 return LDAP_SUCCESS;
282         }
283
284         for ( i = 0; ppValue[i] != NULL; i++ )
285                 ; /* EMPTY */
286
287         tmpberval = (BerVarray)slapi_ch_malloc( (i+1) * (sizeof(struct berval)) );
288         if ( tmpberval == NULL ) {
289                 return LDAP_NO_MEMORY;
290         }
291         for ( i = 0; ppValue[i] != NULL; i++ ) {
292                 size_t len = strlen( ppValue[i] );
293
294                 tmpberval[i].bv_val = slapi_ch_malloc( len + 1 );
295                 AC_MEMCPY( tmpberval[i].bv_val, ppValue[i], len + 1 );
296                 tmpberval[i].bv_len = len;
297         }
298         tmpberval[i].bv_val = NULL;
299         tmpberval[i].bv_len = 0;
300
301         *bvobj = tmpberval;
302
303         return LDAP_SUCCESS;
304 }
305
306 static int
307 bvptr2obj_copy(
308         struct berval   **bvptr, 
309         BerVarray       *bvobj )
310 {
311         int             i;
312         BerVarray       tmpberval;
313
314         if ( bvptr == NULL ) {
315                 *bvobj = NULL;
316                 return LDAP_SUCCESS;
317         }
318
319         for ( i = 0; bvptr[i] != NULL; i++ )
320                 ; /* EMPTY */
321
322         tmpberval = (BerVarray)slapi_ch_malloc( (i + 1) * sizeof(struct berval));
323         if ( tmpberval == NULL ) {
324                 return LDAP_NO_MEMORY;
325         } 
326
327         for ( i = 0; bvptr[i] != NULL; i++ ) {
328                 tmpberval[i].bv_val = slapi_ch_malloc( bvptr[i]->bv_len );
329                 tmpberval[i].bv_len = bvptr[i]->bv_len;
330                 AC_MEMCPY( tmpberval[i].bv_val, bvptr[i]->bv_val, bvptr[i]->bv_len );
331         }
332
333         tmpberval[i].bv_val = NULL;
334         tmpberval[i].bv_len = 0;
335
336         *bvobj = tmpberval;
337
338         return LDAP_SUCCESS;
339 }
340
341 /*
342  * Function : slapi_int_ldapmod_to_entry 
343  * convert a dn plus an array of LDAPMod struct ptrs to an entry structure
344  * with a link list of the correspondent attributes.
345  * Return value : LDAP_SUCCESS
346  *                LDAP_NO_MEMORY
347  *                LDAP_OTHER
348 */
349 static Entry *
350 slapi_int_ldapmod_to_entry(
351         Connection *pConn,
352         char *ldn, 
353         LDAPMod **mods )
354 {
355         struct berval           dn = BER_BVNULL;
356         Entry                   *pEntry=NULL;
357         LDAPMod                 *pMod;
358         struct berval           *bv;
359         Operation               *op;
360
361         Modifications           *modlist = NULL;
362         Modifications           **modtail = &modlist;
363         Modifications           tmp;
364
365         int                     rc = LDAP_SUCCESS;
366         int                     i;
367
368         const char              *text = NULL;
369
370         op = (Operation *)pConn->c_pending_ops.stqh_first;
371
372         pEntry = (Entry *) ch_calloc( 1, sizeof(Entry) );
373         if ( pEntry == NULL) {
374                 rc = LDAP_NO_MEMORY;
375                 goto cleanup;
376         } 
377
378         dn.bv_val = slapi_ch_strdup(ldn);
379         dn.bv_len = strlen(ldn);
380
381         rc = dnPrettyNormal( NULL, &dn, &pEntry->e_name, &pEntry->e_nname, NULL );
382         if ( rc != LDAP_SUCCESS ) {
383                 goto cleanup;
384         }
385
386         if ( rc == LDAP_SUCCESS ) {
387                 for ( i = 0, pMod = mods[0]; rc == LDAP_SUCCESS && pMod != NULL; pMod = mods[++i]) {
388                         Modifications *mod;
389
390                         if ( (pMod->mod_op & LDAP_MOD_BVALUES) != 0 ) {
391                                 /*
392                                  * Convert an array of pointers to bervals to
393                                  * an array of bervals. Note that we need to copy the
394                                  * values too, as the slap_mods_check() will free the
395                                  * original values after prettying; the modifications
396                                  * being passed in may not have been allocated on the
397                                  * heap.
398                                  */
399                                 rc = bvptr2obj_copy( pMod->mod_bvalues, &bv );
400                                 if ( rc != LDAP_SUCCESS ) goto cleanup;
401                                 tmp.sml_type.bv_val = pMod->mod_type;
402                                 tmp.sml_type.bv_len = strlen( pMod->mod_type );
403                                 tmp.sml_values = bv;
404                                 tmp.sml_nvalues = NULL;
405                 
406                                 mod  = (Modifications *) ch_malloc( sizeof(Modifications) );
407
408                                 mod->sml_op = LDAP_MOD_ADD;
409                                 mod->sml_flags = 0;
410                                 mod->sml_next = NULL;
411                                 mod->sml_desc = NULL;
412                                 mod->sml_type = tmp.sml_type;
413                                 mod->sml_values = tmp.sml_values;
414                                 mod->sml_nvalues = tmp.sml_nvalues;
415
416                                 *modtail = mod;
417                                 modtail = &mod->sml_next;
418
419                         } else {
420                                 /* attr values are in string format, need to be converted */
421                                 /* to an array of bervals */ 
422                                 if ( pMod->mod_values == NULL ) {
423                                         rc = LDAP_OTHER;
424                                 } else {
425                                         rc = values2obj_copy( pMod->mod_values, &bv );
426                                         if ( rc != LDAP_SUCCESS ) goto cleanup;
427                                         tmp.sml_type.bv_val = pMod->mod_type;
428                                         tmp.sml_type.bv_len = strlen( pMod->mod_type );
429                                         tmp.sml_values = bv;
430                                         tmp.sml_nvalues = NULL;
431                 
432                                         mod  = (Modifications *) ch_malloc( sizeof(Modifications) );
433
434                                         mod->sml_op = LDAP_MOD_ADD;
435                                         mod->sml_flags = 0;
436                                         mod->sml_next = NULL;
437                                         mod->sml_desc = NULL;
438                                         mod->sml_type = tmp.sml_type;
439                                         mod->sml_values = tmp.sml_values;
440                                         mod->sml_nvalues = tmp.sml_nvalues;
441
442                                         *modtail = mod;
443                                         modtail = &mod->sml_next;
444                                 }
445                         }
446                 } /* for each LDAPMod */
447         }
448
449         op->o_bd = select_backend( &pEntry->e_nname, 0, 0 );
450         if ( op->o_bd == NULL ) {
451                 rc = LDAP_PARTIAL_RESULTS;
452         } else {
453                 int repl_user = be_isupdate_dn( op->o_bd, &op->o_bd->be_rootdn );
454                 if ( !op->o_bd->be_update_ndn.bv_len || repl_user ) {
455                         int     update = !BER_BVISNULL( &op->o_bd->be_update_ndn );
456                         char    textbuf[ SLAP_TEXT_BUFLEN ];
457                         size_t  textlen = sizeof( textbuf );
458
459                         rc = slap_mods_check( modlist, &text, 
460                                 textbuf, textlen, NULL );
461                         if ( rc != LDAP_SUCCESS) {
462                                 goto cleanup;
463                         }
464
465                         if ( !update ) {
466                                 rc = slap_mods_no_user_mod_check( op, modlist,
467                                         &text, textbuf, textlen );
468                                 if ( rc != LDAP_SUCCESS) {
469                                         goto cleanup;
470                                 }
471                         }
472
473                         if ( !repl_user ) {
474                                 rc = slap_mods_opattrs( op, modlist, modtail,
475                                         &text, textbuf, textlen, 1 );
476                                 if ( rc != LDAP_SUCCESS) {
477                                         goto cleanup;
478                                 }
479                         }
480
481                         rc = slap_mods2entry( modlist, &pEntry, repl_user,
482                                               0, &text, textbuf, textlen );
483                         if (rc != LDAP_SUCCESS) {
484                                 goto cleanup;
485                         }
486
487                 } else {
488                         rc = LDAP_REFERRAL;
489                 }
490         }
491
492 cleanup:;
493         if ( dn.bv_val )
494                 slapi_ch_free( (void **)&dn.bv_val );
495         if ( modlist != NULL )
496                 slap_mods_free( modlist );
497         if ( rc != LDAP_SUCCESS ) {
498                 if ( pEntry != NULL ) {
499                         slapi_entry_free( pEntry );
500                 }
501                 pEntry = NULL;
502         }
503
504         return( pEntry );
505 }
506
507 /* Function : slapi_delete_internal
508  *
509  * Description : Plugin functions call this routine to delete an entry 
510  *               in the backend directly
511  * Return values : LDAP_SUCCESS
512  *                 LDAP_PARAM_ERROR
513  *                 LDAP_NO_MEMORY
514  *                 LDAP_OTHER
515  *                 LDAP_UNWILLING_TO_PERFORM
516 */
517 Slapi_PBlock *
518 slapi_delete_internal(
519         char *ldn, 
520         LDAPControl **controls, 
521         int log_change )
522 {
523 #ifdef LDAP_SLAPI
524         Connection              *pConn = NULL;
525         Operation               *op = NULL;
526         Slapi_PBlock            *pPB = NULL;
527         SlapReply               rs = { REP_RESULT };
528         struct berval           dn = BER_BVNULL;
529
530         int                     manageDsaIt = SLAP_CONTROL_NONE;
531         int                     isCritical;
532
533         if ( ldn == NULL ) {
534                 rs.sr_err = LDAP_PARAM_ERROR; 
535                 goto cleanup;
536         }
537
538         pConn = slapi_int_init_connection( NULL, LDAP_REQ_DELETE );
539         if (pConn == NULL) {
540                 rs.sr_err = LDAP_NO_MEMORY;
541                 goto cleanup;
542         }
543
544         op = (Operation *)pConn->c_pending_ops.stqh_first;
545         pPB = (Slapi_PBlock *)op->o_pb;
546         op->o_ctrls = controls;
547
548         dn.bv_val = slapi_ch_strdup(ldn);
549         dn.bv_len = strlen(ldn);
550         rs.sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, NULL );
551         if ( rs.sr_err != LDAP_SUCCESS )
552                 goto cleanup;
553
554         if ( slapi_control_present( controls, 
555                         SLAPI_CONTROL_MANAGEDSAIT_OID, NULL, &isCritical) ) {
556                 manageDsaIt = isCritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL; 
557         }
558
559         op->o_bd = select_backend( &op->o_req_ndn, manageDsaIt, 1 );
560         if ( op->o_bd == NULL ) {
561                 rs.sr_err = LDAP_PARTIAL_RESULTS;
562                 goto cleanup;
563         }
564
565         op->o_dn = pConn->c_dn = op->o_bd->be_rootdn;
566         op->o_ndn = pConn->c_ndn = op->o_bd->be_rootndn;
567
568         if ( op->o_bd->be_delete ) {
569                 int repl_user = be_isupdate( op );
570                 if ( !op->o_bd->be_update_ndn.bv_len || repl_user ) {
571                         slap_callback cb = { NULL, slap_replog_cb, NULL, NULL };
572                         if ( log_change ) op->o_callback = &cb;
573                         if ( (*op->o_bd->be_delete)( op, &rs ) ) {
574                                 rs.sr_err = LDAP_OTHER;
575                         }
576                 } else {
577                         rs.sr_err = LDAP_REFERRAL;
578                 }
579         } else {
580                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
581         }
582
583 cleanup:
584         if ( pPB != NULL ) {
585                 slapi_pblock_set( pPB, SLAPI_PLUGIN_INTOP_RESULT, (void *)rs.sr_err );
586         }
587         if ( dn.bv_val ) {
588                 slapi_ch_free( (void **)&dn.bv_val );
589         }
590
591         slapi_int_connection_destroy( &pConn );
592
593         return pPB;
594 #else
595         return NULL;
596 #endif /* LDAP_SLAPI */
597 }
598
599 #ifdef LDAP_SLAPI
600 static Slapi_PBlock * 
601 slapi_int_add_entry_locked(
602         Connection *pConn,
603         Slapi_Entry **e, 
604         LDAPControl **controls, 
605         int log_changes ) 
606 {
607         Operation               *op = NULL;
608         Slapi_PBlock            *pPB = NULL;
609
610         int                     manageDsaIt = SLAP_CONTROL_NONE;
611         int                     isCritical;
612         SlapReply               rs = { REP_RESULT };
613
614         if ( *e == NULL ) {
615                 rs.sr_err = LDAP_PARAM_ERROR;
616                 goto cleanup;
617         }
618
619         if ( slapi_control_present( controls, LDAP_CONTROL_MANAGEDSAIT,
620                                 NULL, &isCritical ) ) {
621                 manageDsaIt = isCritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL; 
622         }
623
624         op = (Operation *)pConn->c_pending_ops.stqh_first;
625         pPB = (Slapi_PBlock *)op->o_pb;
626         op->o_ctrls = controls;
627
628         op->o_bd = select_backend( &((*e)->e_nname), manageDsaIt, 1 );
629         if ( op->o_bd == NULL ) {
630                 rs.sr_err = LDAP_PARTIAL_RESULTS;
631                 goto cleanup;
632         }
633
634         op->o_dn = pConn->c_dn = op->o_bd->be_rootdn;
635         op->o_ndn = pConn->c_ndn = op->o_bd->be_rootndn;
636         op->oq_add.rs_e = *e;
637
638         if ( op->o_bd->be_add ) {
639                 int repl_user = be_isupdate( op );
640                 if ( !op->o_bd->be_update_ndn.bv_len || repl_user ) {
641                         slap_callback cb = { NULL, slap_replog_cb, NULL, NULL };
642                         if ( log_changes ) op->o_callback = &cb;
643                         if ( (*op->o_bd->be_add)( op, &rs ) == 0 ) {
644                                 be_entry_release_w( op, *e );
645                                 *e = NULL;
646                         }
647                 } else {
648                         rs.sr_err = LDAP_REFERRAL;
649                 }
650         } else {
651                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
652         }
653
654 cleanup:
655         if ( pPB != NULL ) {
656                 slapi_pblock_set( pPB, SLAPI_PLUGIN_INTOP_RESULT, (void *)rs.sr_err );
657         }
658
659         return( pPB );
660 }
661 #endif /* LDAP_SLAPI */
662
663 Slapi_PBlock * 
664 slapi_add_entry_internal(
665         Slapi_Entry *e, 
666         LDAPControl **controls, 
667         int log_changes ) 
668 {
669 #ifdef LDAP_SLAPI
670         Slapi_PBlock            *pb = NULL;
671         Slapi_Entry             *entry = NULL;
672         Connection              *pConn = NULL;
673
674         pConn = slapi_int_init_connection( NULL, LDAP_REQ_ADD );
675         if ( pConn == NULL ) {
676                 return NULL;
677         }
678
679         /*
680          * We make a copy to avoid an entry that may be freed later
681          * by the caller being placed in the cache.
682          */
683         entry = slapi_entry_dup( e );
684         pb = slapi_int_add_entry_locked( pConn, &entry, controls, log_changes );
685         if ( entry != NULL ) {
686                 slapi_entry_free( entry );
687         }
688
689         slapi_int_connection_destroy( &pConn );
690
691         return pb;
692 #else
693         return NULL;
694 #endif
695 }
696
697 Slapi_PBlock *
698 slapi_add_internal(
699         char *dn, 
700         LDAPMod **mods, 
701         LDAPControl **controls, 
702         int log_changes  ) 
703 {
704 #ifdef LDAP_SLAPI
705         LDAPMod                 *pMod = NULL;
706         Connection              *pConn = NULL;
707         Slapi_PBlock            *pb = NULL;
708         Entry                   *pEntry = NULL;
709         int                     i, rc = LDAP_SUCCESS;
710
711         if ( mods == NULL || *mods == NULL || dn == NULL || *dn == '\0' ) {
712                 rc = LDAP_PARAM_ERROR ;
713         }
714
715         if ( rc == LDAP_SUCCESS ) {
716                 for ( i = 0, pMod = mods[0]; pMod != NULL; pMod = mods[++i] ) {
717                         if ( (pMod->mod_op & LDAP_MOD_OP ) != LDAP_MOD_ADD ) {
718                                 rc = LDAP_OTHER;
719                                 break;
720                         }
721                 }
722         }
723
724         if ( rc == LDAP_SUCCESS ) {
725                 pConn = slapi_int_init_connection( NULL, LDAP_REQ_ADD );
726                 if ( pConn != NULL ) {
727                         pEntry = slapi_int_ldapmod_to_entry( pConn, dn, mods );
728                         if ( pEntry == NULL ) {
729                                 rc = LDAP_OTHER;
730                         }
731                 }
732         }
733
734         if ( rc != LDAP_SUCCESS ) {
735                 pb = slapi_pblock_new();
736                 slapi_pblock_set( pb, SLAPI_PLUGIN_INTOP_RESULT, (void *)rc );
737         } else {
738                 pb = slapi_int_add_entry_locked( pConn, &pEntry, controls, log_changes );
739         }
740
741         if ( pEntry != NULL ) {
742                 slapi_entry_free( pEntry );
743         }
744
745         slapi_int_connection_destroy( &pConn );
746
747         return pb;
748 #else
749         return NULL;
750 #endif /* LDAP_SLAPI */
751 }
752
753 /* Function : slapi_modrdn_internal
754  *
755  * Description : Plugin functions call this routine to modify the rdn 
756  *                               of an entry in the backend directly
757  * Return values : LDAP_SUCCESS
758  *                 LDAP_PARAM_ERROR
759  *                 LDAP_NO_MEMORY
760  *                 LDAP_OTHER
761  *                 LDAP_UNWILLING_TO_PERFORM
762  *
763  * NOTE: This function does not support the "newSuperior" option from LDAP V3.
764  */
765 Slapi_PBlock *
766 slapi_modrdn_internal(
767         char *olddn, 
768         char *lnewrdn, 
769         int deloldrdn, 
770         LDAPControl **controls, 
771         int log_change )
772 {
773 #ifdef LDAP_SLAPI
774         struct berval           dn = BER_BVNULL;
775         struct berval           newrdn = BER_BVNULL;
776         Connection              *pConn = NULL;
777         Operation               *op = NULL;
778         Slapi_PBlock            *pPB = NULL;
779         int                     manageDsaIt = SLAP_CONTROL_NONE;
780         int                     isCritical;
781         SlapReply               rs = { REP_RESULT };
782
783         pConn = slapi_int_init_connection( NULL,  LDAP_REQ_MODRDN );
784         if ( pConn == NULL) {
785                 rs.sr_err = LDAP_NO_MEMORY;
786                 goto cleanup;
787         }
788
789         op = (Operation *)pConn->c_pending_ops.stqh_first;
790         pPB = (Slapi_PBlock *)op->o_pb;
791         op->o_ctrls = controls;
792
793         if ( slapi_control_present( controls, 
794                         SLAPI_CONTROL_MANAGEDSAIT_OID, NULL, &isCritical ) ) {
795                 manageDsaIt = isCritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
796         }
797
798         op->o_bd = select_backend( &op->o_req_ndn, manageDsaIt, 1 );
799         if ( op->o_bd == NULL ) {
800                 rs.sr_err =  LDAP_PARTIAL_RESULTS;
801                 goto cleanup;
802         }
803
804         op->o_dn = pConn->c_dn = op->o_bd->be_rootdn;
805         op->o_ndn = pConn->c_ndn = op->o_bd->be_rootndn;
806
807         dn.bv_val = slapi_ch_strdup( olddn );
808         dn.bv_len = strlen( olddn );
809
810         rs.sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, NULL );
811         if ( rs.sr_err != LDAP_SUCCESS ) {
812                 goto cleanup;
813         }
814
815         if ( op->o_req_dn.bv_len == 0 ) {
816                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
817                 goto cleanup;
818         }
819
820         newrdn.bv_val = slapi_ch_strdup( lnewrdn );
821         newrdn.bv_len = strlen( lnewrdn );
822
823         rs.sr_err = dnPrettyNormal( NULL, &newrdn, &op->oq_modrdn.rs_newrdn, &op->oq_modrdn.rs_nnewrdn, NULL );
824         if ( rs.sr_err != LDAP_SUCCESS ) {
825                 goto cleanup;
826         }
827
828         if ( rdn_validate( &op->oq_modrdn.rs_nnewrdn ) != LDAP_SUCCESS ) {
829                 goto cleanup;
830         }
831
832         op->oq_modrdn.rs_newSup = NULL;
833         op->oq_modrdn.rs_nnewSup = NULL;
834         op->oq_modrdn.rs_deleteoldrdn = deloldrdn;
835
836         if ( op->o_bd->be_modrdn ) {
837                 int repl_user = be_isupdate( op );
838                 if ( !op->o_bd->be_update_ndn.bv_len || repl_user ) {
839                         slap_callback cb = { NULL, slap_replog_cb, NULL, NULL };
840                         if ( log_change ) op->o_callback = &cb;
841                         if ( (*op->o_bd->be_modrdn)( op, &rs ) ) {
842                                 rs.sr_err = LDAP_OTHER;
843                         }
844                 } else {
845                         rs.sr_err = LDAP_REFERRAL;
846                 }
847         } else {
848                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
849         }
850
851 cleanup:
852
853         if ( pPB != NULL ) {
854                 slapi_pblock_set( pPB, SLAPI_PLUGIN_INTOP_RESULT, (void *)rs.sr_err );
855         }
856         
857         if ( dn.bv_val )
858                 slapi_ch_free( (void **)&dn.bv_val );
859
860         if ( newrdn.bv_val )
861                 slapi_ch_free( (void **)&newrdn.bv_val );
862         if ( op->oq_modrdn.rs_newrdn.bv_val )
863                 slapi_ch_free( (void **)&op->oq_modrdn.rs_newrdn.bv_val );
864         if ( op->oq_modrdn.rs_nnewrdn.bv_val )
865                 slapi_ch_free( (void **)&op->oq_modrdn.rs_nnewrdn.bv_val );
866
867         slapi_int_connection_destroy( &pConn );
868
869         return pPB;
870 #else
871         return NULL;
872 #endif /* LDAP_SLAPI */
873 }
874
875 /* Function : slapi_modify_internal
876  *
877  * Description: Plugin functions call this routine to modify an entry 
878  *                              in the backend directly
879  * Return values : LDAP_SUCCESS
880  *                 LDAP_PARAM_ERROR
881  *                 LDAP_NO_MEMORY
882  *                 LDAP_OTHER
883  *                 LDAP_UNWILLING_TO_PERFORM
884 */
885 Slapi_PBlock *
886 slapi_modify_internal(
887         char *ldn,      
888         LDAPMod **mods, 
889         LDAPControl **controls, 
890         int log_change )
891 {
892 #ifdef LDAP_SLAPI
893         int                     i;
894         Connection              *pConn = NULL;
895         Operation               *op = NULL;
896         Slapi_PBlock            *pPB = NULL;
897
898         struct berval dn = BER_BVNULL;
899
900         int                     manageDsaIt = SLAP_CONTROL_NONE;
901         int                     isCritical;
902         struct berval           *bv;
903         LDAPMod                 *pMod;
904
905         Modifications           *modlist = NULL;
906         Modifications           **modtail = &modlist;
907         Modifications           tmp;
908
909         SlapReply               rs = { REP_RESULT };
910
911         if ( mods == NULL || *mods == NULL || ldn == NULL ) {
912                 rs.sr_err = LDAP_PARAM_ERROR ;
913                 goto cleanup;
914         }
915
916         pConn = slapi_int_init_connection( NULL,  LDAP_REQ_MODIFY );
917         if ( pConn == NULL ) {
918                 rs.sr_err = LDAP_NO_MEMORY;
919                 goto cleanup;
920         }
921
922         op = (Operation *)pConn->c_pending_ops.stqh_first;
923         pPB = (Slapi_PBlock *)op->o_pb;
924         op->o_ctrls = controls;
925
926         dn.bv_val = slapi_ch_strdup( ldn );
927         dn.bv_len = strlen( ldn );
928         rs.sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, NULL );
929         if ( rs.sr_err != LDAP_SUCCESS ) {
930                 goto cleanup;
931         }
932
933         if ( slapi_control_present( controls, 
934                         SLAPI_CONTROL_MANAGEDSAIT_OID, NULL, &isCritical ) ) {
935                 manageDsaIt = isCritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
936         }
937
938         op->o_bd = select_backend( &op->o_req_ndn, manageDsaIt, 1 );
939         if ( op->o_bd == NULL ) {
940                 rs.sr_err = LDAP_PARTIAL_RESULTS;
941                 goto cleanup;
942         }
943
944         op->o_dn = pConn->c_dn = op->o_bd->be_rootdn;
945         op->o_ndn = pConn->c_ndn = op->o_bd->be_rootndn;
946
947         for ( i = 0, pMod = mods[0];
948                 rs.sr_err == LDAP_SUCCESS && pMod != NULL; 
949                 pMod = mods[++i] )
950         {
951                 Modifications *mod;
952
953                 if ( (pMod->mod_op & LDAP_MOD_BVALUES) != 0 ) {
954                         /*
955                          * attr values are in berval format
956                          * convert an array of pointers to bervals
957                          * to an array of bervals
958                          */
959                         rs.sr_err = bvptr2obj_copy( pMod->mod_bvalues, &bv );
960                         if ( rs.sr_err != LDAP_SUCCESS )
961                                 goto cleanup;
962                         tmp.sml_type.bv_val = pMod->mod_type;
963                         tmp.sml_type.bv_len = strlen( pMod->mod_type );
964                         tmp.sml_values = bv;
965                         tmp.sml_nvalues = NULL;
966
967                         mod  = (Modifications *)ch_malloc( sizeof(Modifications) );
968
969                         mod->sml_op = pMod->mod_op & LDAP_MOD_OP;
970                         mod->sml_flags = 0;
971                         mod->sml_next = NULL;
972                         mod->sml_desc = NULL;
973                         mod->sml_type = tmp.sml_type;
974                         mod->sml_values = tmp.sml_values;
975                         mod->sml_nvalues = tmp.sml_nvalues;
976                 } else { 
977                         rs.sr_err = values2obj_copy( pMod->mod_values, &bv );
978                         if ( rs.sr_err != LDAP_SUCCESS )
979                                 goto cleanup;
980                         tmp.sml_type.bv_val = pMod->mod_type;
981                         tmp.sml_type.bv_len = strlen( pMod->mod_type );
982                         tmp.sml_values = bv;
983                         tmp.sml_nvalues = NULL;
984
985                         mod  = (Modifications *) ch_malloc( sizeof(Modifications) );
986
987                         mod->sml_op = pMod->mod_op & LDAP_MOD_OP;
988                         mod->sml_flags = 0;
989                         mod->sml_next = NULL;
990                         mod->sml_desc = NULL;
991                         mod->sml_type = tmp.sml_type;
992                         mod->sml_values = tmp.sml_values;
993                         mod->sml_nvalues = tmp.sml_nvalues;
994                 }
995                 *modtail = mod;
996                 modtail = &mod->sml_next;
997
998                 switch( pMod->mod_op & LDAP_MOD_OP ) {
999                 case LDAP_MOD_ADD:
1000                 if ( mod->sml_values == NULL ) {
1001                         rs.sr_err = LDAP_PROTOCOL_ERROR;
1002                         goto cleanup;
1003                 }
1004
1005                 /* fall through */
1006                 case LDAP_MOD_DELETE:
1007                 case LDAP_MOD_REPLACE:
1008                 case LDAP_MOD_INCREMENT:
1009                 break;
1010
1011                 default:
1012                         rs.sr_err = LDAP_PROTOCOL_ERROR;
1013                         goto cleanup;
1014                 }
1015         } 
1016         *modtail = NULL;
1017
1018         if ( op->o_req_ndn.bv_len == 0 ) {
1019                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
1020                 goto cleanup;
1021         }
1022
1023         op->oq_modify.rs_modlist = modlist;
1024
1025         if ( op->o_bd->be_modify ) {
1026                 int repl_user = be_isupdate( op );
1027                 if ( !op->o_bd->be_update_ndn.bv_len || repl_user ) {
1028                         int             update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn );
1029                         const char      *text = NULL;
1030                         char            textbuf[ SLAP_TEXT_BUFLEN ];
1031                         size_t          textlen = sizeof( textbuf );
1032                         slap_callback   cb = { NULL, slap_replog_cb, NULL, NULL };
1033
1034                         rs.sr_err = slap_mods_check( modlist,
1035                                 &text, textbuf, textlen, NULL );
1036                         if ( rs.sr_err != LDAP_SUCCESS ) {
1037                                 goto cleanup;
1038                         }
1039
1040                         if ( !update ) {
1041                                 rs.sr_err = slap_mods_no_user_mod_check( op, modlist,
1042                                         &text, textbuf, textlen );
1043                                 if ( rs.sr_err != LDAP_SUCCESS ) {
1044                                         goto cleanup;
1045                                 }
1046                         }
1047
1048                         if ( !repl_user ) {
1049                                 rs.sr_err = slap_mods_opattrs( op, modlist,
1050                                                 modtail, &text, textbuf, 
1051                                                 textlen, 1 );
1052                                 if ( rs.sr_err != LDAP_SUCCESS ) {
1053                                         goto cleanup;
1054                                 }
1055                         }
1056                         if ( log_change ) op->o_callback = &cb;
1057                         if ( (*op->o_bd->be_modify)( op, &rs ) ) {
1058                                 rs.sr_err = LDAP_OTHER;
1059                         }
1060                 } else {
1061                         rs.sr_err = LDAP_REFERRAL;
1062                 }
1063         } else {
1064                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
1065         }
1066
1067 cleanup:
1068
1069         if ( pPB != NULL ) 
1070                 slapi_pblock_set( pPB, SLAPI_PLUGIN_INTOP_RESULT, (void *)rs.sr_err );
1071
1072         if ( dn.bv_val )
1073                 slapi_ch_free( (void **)&dn.bv_val );
1074
1075         if ( modlist != NULL )
1076                 slap_mods_free( modlist );
1077
1078         slapi_int_connection_destroy( &pConn );
1079
1080         return pPB;
1081 #else
1082         return NULL;
1083 #endif /* LDAP_SLAPI */
1084 }
1085
1086 Slapi_PBlock *
1087 slapi_search_internal(
1088         char *ldn, 
1089         int scope, 
1090         char *filStr, 
1091         LDAPControl **controls, 
1092         char **attrs, 
1093         int attrsonly ) 
1094 {       
1095 #ifdef LDAP_SLAPI
1096         Connection              *c;
1097         Operation               *op = NULL;
1098         Slapi_PBlock            *pPB = NULL;            
1099         struct berval           dn = BER_BVNULL;
1100         Filter                  *filter=NULL;
1101         struct berval           fstr = BER_BVNULL;
1102         AttributeName           *an = NULL;
1103         const char              *text = NULL;
1104
1105         int                     manageDsaIt = SLAP_CONTROL_NONE; 
1106         int                     isCritical;
1107         int                     i;
1108
1109         SlapReply               rs = { REP_RESULT };
1110
1111         c = slapi_int_init_connection( NULL, LDAP_REQ_SEARCH );
1112         if ( c == NULL ) {
1113                 rs.sr_err = LDAP_NO_MEMORY;
1114                 goto cleanup;
1115         }
1116
1117         op = (Operation *)c->c_pending_ops.stqh_first;
1118         pPB = (Slapi_PBlock *)op->o_pb;
1119         op->o_ctrls = controls;
1120
1121         if ( ldn != NULL ) {
1122                 dn.bv_val = slapi_ch_strdup(ldn);
1123                 dn.bv_len = strlen(ldn);
1124         }
1125
1126         rs.sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, NULL );
1127         if ( rs.sr_err != LDAP_SUCCESS ) {
1128                 goto cleanup;
1129         }
1130
1131         if ( scope != LDAP_SCOPE_BASE && 
1132                         scope != LDAP_SCOPE_ONELEVEL && 
1133                         scope != LDAP_SCOPE_SUBTREE ) {
1134                 rs.sr_err = LDAP_PROTOCOL_ERROR;
1135                 goto cleanup;
1136         }
1137
1138         filter = slapi_str2filter(filStr);
1139         if ( filter == NULL ) {
1140                 rs.sr_err = LDAP_PROTOCOL_ERROR;
1141                 goto cleanup;
1142         }
1143
1144         filter2bv( filter, &fstr );
1145
1146         for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
1147                 ; /* count the number of attributes */
1148         }
1149
1150         if (i > 0) {
1151                 an = (AttributeName *)slapi_ch_calloc( (i + 1), sizeof(AttributeName) );
1152                 for (i = 0; attrs[i] != 0; i++) {
1153                         an[i].an_desc = NULL;
1154                         an[i].an_oc = NULL;
1155                         an[i].an_oc_exclude = 0;
1156                         an[i].an_name.bv_val = slapi_ch_strdup(attrs[i]);
1157                         an[i].an_name.bv_len = strlen(attrs[i]);
1158                         slap_bv2ad( &an[i].an_name, &an[i].an_desc, &text );
1159                 }
1160                 an[i].an_name.bv_val = NULL;
1161         }
1162
1163         memset( &rs, 0, sizeof(rs) );
1164         rs.sr_type = REP_RESULT;
1165         rs.sr_err = LDAP_SUCCESS;
1166         rs.sr_entry = NULL; /* paranoia */
1167
1168         if ( scope == LDAP_SCOPE_BASE ) {
1169                 rs.sr_entry = NULL;
1170
1171                 if ( op->o_req_ndn.bv_len == 0 ) {
1172                         rs.sr_err = root_dse_info( c, &rs.sr_entry, &rs.sr_text );
1173                 }
1174
1175                 if( rs.sr_err != LDAP_SUCCESS ) {
1176                         send_ldap_result( op, &rs );
1177                         goto cleanup;
1178                 } else if ( rs.sr_entry != NULL ) {
1179                         rs.sr_err = test_filter( op, rs.sr_entry, filter );
1180
1181                         if ( rs.sr_err == LDAP_COMPARE_TRUE ) {
1182                                 rs.sr_type = REP_SEARCH;
1183                                 rs.sr_err = LDAP_SUCCESS;
1184                                 rs.sr_attrs = an;
1185                                 rs.sr_operational_attrs = NULL;
1186                                 rs.sr_flags = REP_ENTRY_MODIFIABLE;
1187
1188                                 send_search_entry( op, &rs );
1189                         }
1190
1191                         entry_free( rs.sr_entry );
1192
1193                         rs.sr_type = REP_RESULT;
1194                         rs.sr_err = LDAP_SUCCESS;
1195
1196                         send_ldap_result( op, &rs );
1197
1198                         goto cleanup;
1199                 }
1200         }
1201
1202         if ( !op->o_req_ndn.bv_len && default_search_nbase.bv_len ) {
1203                 slapi_ch_free( (void **)&op->o_req_dn.bv_val );
1204                 slapi_ch_free( (void **)&op->o_req_ndn.bv_val );
1205
1206                 ber_dupbv( &op->o_req_dn, &default_search_base );
1207                 ber_dupbv( &op->o_req_ndn, &default_search_nbase );
1208         }
1209
1210         if ( slapi_control_present( controls,
1211                         LDAP_CONTROL_MANAGEDSAIT, NULL, &isCritical ) ) {
1212                 manageDsaIt = isCritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1213         }
1214
1215         op->o_bd = select_backend( &op->o_req_ndn, manageDsaIt, 1 );
1216         if ( op->o_bd == NULL ) {
1217                 if ( manageDsaIt > SLAP_CONTROL_NONE  ) {
1218                         rs.sr_err = LDAP_NO_SUCH_OBJECT;
1219                 } else {
1220                         rs.sr_err = LDAP_PARTIAL_RESULTS;
1221                 }
1222                 goto cleanup;
1223         } 
1224
1225         op->o_dn = c->c_dn = op->o_bd->be_rootdn;
1226         op->o_ndn = c->c_ndn = op->o_bd->be_rootndn;
1227
1228         op->oq_search.rs_scope = scope;
1229         op->oq_search.rs_deref = 0;
1230         op->oq_search.rs_slimit = SLAP_NO_LIMIT;
1231         op->oq_search.rs_tlimit = SLAP_NO_LIMIT;
1232         op->oq_search.rs_attrsonly = attrsonly;
1233         op->oq_search.rs_attrs = an;
1234         op->oq_search.rs_filter = filter;
1235         op->oq_search.rs_filterstr = fstr;
1236
1237         if ( op->o_bd->be_search ) {
1238                 if ( (*op->o_bd->be_search)( op, &rs ) != 0 ) {
1239                         rs.sr_err = LDAP_OTHER;
1240                 }
1241         } else {
1242                 rs.sr_err = LDAP_UNWILLING_TO_PERFORM;
1243         }
1244
1245 cleanup:
1246
1247         if ( pPB != NULL )
1248                 slapi_pblock_set( pPB, SLAPI_PLUGIN_INTOP_RESULT, (void *)rs.sr_err );
1249
1250         if ( dn.bv_val )
1251                 slapi_ch_free( (void **)&dn.bv_val );
1252         if ( filter )
1253                 slapi_filter_free( filter, 1 );
1254         if ( fstr.bv_val )
1255                 slapi_ch_free( (void **)&fstr.bv_val );
1256         if ( an != NULL )
1257                 slapi_ch_free( (void **)&an );
1258
1259         slapi_int_connection_destroy( &c );
1260
1261         return pPB;
1262 #else
1263         return NULL;
1264 #endif /* LDAP_SLAPI */
1265 }
1266