]> git.sur5r.net Git - openldap/blob - servers/slapd/modify.c
f388f2e37432fa25069cb83efe0475a5200f456e
[openldap] / servers / slapd / modify.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Copyright (c) 1995 Regents of the University of Michigan.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that this notice is preserved and that due credit is given
12  * to the University of Michigan at Ann Arbor. The name of the University
13  * may not be used to endorse or promote products derived from this
14  * software without specific prior written permission. This software
15  * is provided ``as is'' without express or implied warranty.
16  */
17
18 #include "portable.h"
19 #include "slapi_common.h"
20
21 #include <stdio.h>
22
23 #include <ac/socket.h>
24 #include <ac/string.h>
25 #include <ac/time.h>
26
27 #include "lutil.h"
28
29 #include "ldap_pvt.h"
30 #include "slap.h"
31 #include "slapi.h"
32
33 #ifdef LDAP_SLAPI
34 static LDAPMod **Modifications2LDAPMods (Modifications **modlist);
35 static Modifications *LDAPMods2Modifications (LDAPMod **mods);
36 static void FreeLDAPMods (LDAPMod **mods);
37 #endif /* LDAP_SLAPI */
38
39 int
40 do_modify(
41     Connection  *conn,
42     Operation   *op )
43 {
44         struct berval dn = { 0, NULL };
45         struct berval pdn = { 0, NULL };
46         struct berval ndn = { 0, NULL };
47         char            *last;
48         ber_tag_t       tag;
49         ber_len_t       len;
50         Modifications   *modlist = NULL;
51         Modifications   **modtail = &modlist;
52 #ifdef LDAP_SLAPI
53         LDAPMod         **modv = NULL;
54 #endif
55 #ifdef LDAP_DEBUG
56         Modifications *tmp;
57 #endif
58         Backend         *be;
59         int rc;
60         const char      *text;
61         int manageDSAit;
62         Slapi_PBlock *pb = op->o_pb;
63
64 #ifdef NEW_LOGGING
65         LDAP_LOG( OPERATION, ENTRY, "do_modify: enter\n", 0, 0, 0 );
66 #else
67         Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
68 #endif
69
70         /*
71          * Parse the modify request.  It looks like this:
72          *
73          *      ModifyRequest := [APPLICATION 6] SEQUENCE {
74          *              name    DistinguishedName,
75          *              mods    SEQUENCE OF SEQUENCE {
76          *                      operation       ENUMERATED {
77          *                              add     (0),
78          *                              delete  (1),
79          *                              replace (2)
80          *                      },
81          *                      modification    SEQUENCE {
82          *                              type    AttributeType,
83          *                              values  SET OF AttributeValue
84          *                      }
85          *              }
86          *      }
87          */
88
89         if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
90 #ifdef NEW_LOGGING
91                 LDAP_LOG( OPERATION, ERR, "do_modify: ber_scanf failed\n", 0, 0, 0 );
92 #else
93                 Debug( LDAP_DEBUG_ANY, "do_modify: ber_scanf failed\n", 0, 0, 0 );
94 #endif
95
96                 send_ldap_disconnect( conn, op,
97                         LDAP_PROTOCOL_ERROR, "decoding error" );
98                 return SLAPD_DISCONNECT;
99         }
100
101 #ifdef NEW_LOGGING
102         LDAP_LOG( OPERATION, ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
103 #else
104         Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
105 #endif
106
107
108         /* collect modifications & save for later */
109
110         for ( tag = ber_first_element( op->o_ber, &len, &last );
111             tag != LBER_DEFAULT;
112             tag = ber_next_element( op->o_ber, &len, last ) )
113         {
114                 ber_int_t mop;
115                 Modifications tmp, *mod;
116
117
118                 if ( ber_scanf( op->o_ber, "{i{m[W]}}", &mop,
119                     &tmp.sml_type, &tmp.sml_bvalues )
120                     == LBER_ERROR )
121                 {
122                         send_ldap_disconnect( conn, op,
123                                 LDAP_PROTOCOL_ERROR, "decoding modlist error" );
124                         rc = SLAPD_DISCONNECT;
125                         goto cleanup;
126                 }
127
128                 mod = (Modifications *) ch_malloc( sizeof(Modifications) );
129                 mod->sml_op = mop;
130                 mod->sml_type = tmp.sml_type;
131                 mod->sml_bvalues = tmp.sml_bvalues;
132                 mod->sml_desc = NULL;
133                 mod->sml_next = NULL;
134                 *modtail = mod;
135
136                 switch( mop ) {
137                 case LDAP_MOD_ADD:
138                         if ( mod->sml_bvalues == NULL ) {
139 #ifdef NEW_LOGGING
140                                 LDAP_LOG( OPERATION, ERR, 
141                                         "do_modify: modify/add operation (%ld) requires values\n",
142                                         (long)mop, 0, 0 );
143 #else
144                                 Debug( LDAP_DEBUG_ANY,
145                                         "do_modify: modify/add operation (%ld) requires values\n",
146                                         (long) mop, 0, 0 );
147 #endif
148
149                                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
150                                         NULL, "modify/add operation requires values",
151                                         NULL, NULL );
152                                 rc = LDAP_PROTOCOL_ERROR;
153                                 goto cleanup;
154                         }
155
156                         /* fall through */
157
158                 case LDAP_MOD_DELETE:
159                 case LDAP_MOD_REPLACE:
160                         break;
161
162                 default: {
163 #ifdef NEW_LOGGING
164                                 LDAP_LOG( OPERATION, ERR, 
165                                         "do_modify: invalid modify operation (%ld)\n", (long)mop, 0, 0 );
166 #else
167                                 Debug( LDAP_DEBUG_ANY,
168                                         "do_modify: invalid modify operation (%ld)\n",
169                                         (long) mop, 0, 0 );
170 #endif
171
172                                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
173                                         NULL, "unrecognized modify operation", NULL, NULL );
174                                 rc = LDAP_PROTOCOL_ERROR;
175                                 goto cleanup;
176                         }
177                 }
178
179                 modtail = &mod->sml_next;
180         }
181         *modtail = NULL;
182
183         if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
184 #ifdef NEW_LOGGING
185                 LDAP_LOG( OPERATION, ERR, "do_modify: get_ctrls failed\n", 0, 0, 0 );
186 #else
187                 Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
188 #endif
189
190                 goto cleanup;
191         }
192
193         rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn );
194         if( rc != LDAP_SUCCESS ) {
195 #ifdef NEW_LOGGING
196                 LDAP_LOG( OPERATION, INFO, "do_modify: conn %d  invalid dn (%s)\n",
197                         conn->c_connid, dn.bv_val, 0 );
198 #else
199                 Debug( LDAP_DEBUG_ANY,
200                         "do_modify: invalid dn (%s)\n", dn.bv_val, 0, 0 );
201 #endif
202                 send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
203                     "invalid DN", NULL, NULL );
204                 goto cleanup;
205         }
206
207         if( ndn.bv_len == 0 ) {
208 #ifdef NEW_LOGGING
209                 LDAP_LOG( OPERATION, ERR, 
210                         "do_modify: attempt to modify root DSE.\n",0, 0, 0 );
211 #else
212                 Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
213 #endif
214
215                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
216                         NULL, "modify upon the root DSE not supported", NULL, NULL );
217                 goto cleanup;
218
219         } else if ( bvmatch( &ndn, &global_schemandn ) ) {
220 #ifdef NEW_LOGGING
221                 LDAP_LOG( OPERATION, ERR,
222                         "do_modify: attempt to modify subschema subentry.\n" , 0, 0, 0  );
223 #else
224                 Debug( LDAP_DEBUG_ANY, "do_modify: subschema subentry!\n", 0, 0, 0 );
225 #endif
226
227                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
228                         NULL, "modification of subschema subentry not supported",
229                         NULL, NULL );
230                 goto cleanup;
231         }
232
233 #ifdef LDAP_DEBUG
234 #ifdef NEW_LOGGING
235         LDAP_LOG( OPERATION, DETAIL1, "do_modify: modifications:\n", 0, 0, 0  );
236 #else
237         Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
238 #endif
239
240         for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
241 #ifdef NEW_LOGGING
242                 LDAP_LOG( OPERATION, DETAIL1, "\t%s:  %s\n", 
243                         tmp->sml_op == LDAP_MOD_ADD ?
244                         "add" : (tmp->sml_op == LDAP_MOD_DELETE ?
245                         "delete" : "replace"), tmp->sml_type.bv_val, 0 );
246
247                 if ( tmp->sml_bvalues == NULL ) {
248                         LDAP_LOG( OPERATION, DETAIL1, "\t\tno values", 0, 0, 0 );
249                 } else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
250                         LDAP_LOG( OPERATION, DETAIL1, "\t\tzero values", 0, 0, 0 );
251                 } else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
252                         LDAP_LOG( OPERATION, DETAIL1, "\t\tone value", 0, 0, 0 );
253                 } else {
254                         LDAP_LOG( OPERATION, DETAIL1, "\t\tmultiple values", 0, 0, 0 );
255                 }
256
257 #else
258                 Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
259                         tmp->sml_op == LDAP_MOD_ADD
260                                 ? "add" : (tmp->sml_op == LDAP_MOD_DELETE
261                                         ? "delete" : "replace"), tmp->sml_type.bv_val, 0 );
262
263                 if ( tmp->sml_bvalues == NULL ) {
264                         Debug( LDAP_DEBUG_ARGS, "%s\n",
265                            "\t\tno values", NULL, NULL );
266                 } else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
267                         Debug( LDAP_DEBUG_ARGS, "%s\n",
268                            "\t\tzero values", NULL, NULL );
269                 } else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
270                         Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
271                            "\t\tone value", (long) tmp->sml_bvalues[0].bv_len, NULL );
272                 } else {
273                         Debug( LDAP_DEBUG_ARGS, "%s\n",
274                            "\t\tmultiple values", NULL, NULL );
275                 }
276 #endif
277         }
278 #endif
279
280         if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
281                 char abuf[BUFSIZ/2], *ptr = abuf;
282                 int len = 0;
283
284                 Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD dn=\"%s\"\n",
285                         op->o_connid, op->o_opid, dn.bv_val, 0, 0 );
286
287                 for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
288                         if (len + 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
289                                 Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD attr=%s\n",
290                                     op->o_connid, op->o_opid, abuf, 0, 0 );
291                                 len = 0;
292                                 ptr = abuf;
293                         }
294                         if (len) {
295                                 *ptr++ = ' ';
296                                 len++;
297                         }
298                         ptr = lutil_strcopy(ptr, tmp->sml_type.bv_val);
299                         len += tmp->sml_type.bv_len;
300                 }
301                 if (len) {
302                         Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD attr=%s\n",
303                                 op->o_connid, op->o_opid, abuf, 0, 0 );
304                 }
305         }
306
307         manageDSAit = get_manageDSAit( op );
308
309         /*
310          * We could be serving multiple database backends.  Select the
311          * appropriate one, or send a referral to our "referral server"
312          * if we don't hold it.
313          */
314         if ( (be = select_backend( &ndn, manageDSAit, 0 )) == NULL ) {
315                 BerVarray ref = referral_rewrite( default_referral,
316                         NULL, &pdn, LDAP_SCOPE_DEFAULT );
317
318                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
319                         NULL, NULL, ref ? ref : default_referral, NULL );
320
321                 ber_bvarray_free( ref );
322                 goto cleanup;
323         }
324
325         /* check restrictions */
326         rc = backend_check_restrictions( be, conn, op, NULL, &text ) ;
327         if( rc != LDAP_SUCCESS ) {
328                 send_ldap_result( conn, op, rc,
329                         NULL, text, NULL, NULL );
330                 goto cleanup;
331         }
332
333         /* check for referrals */
334         rc = backend_check_referrals( be, conn, op, &pdn, &ndn );
335         if ( rc != LDAP_SUCCESS ) {
336                 goto cleanup;
337         }
338
339         /* deref suffix alias if appropriate */
340         suffix_alias( be, &ndn );
341
342 #if defined( LDAP_SLAPI )
343         slapi_x_backend_set_pb( pb, be );
344         slapi_x_connection_set_pb( pb, conn );
345         slapi_x_operation_set_pb( pb, op );
346         slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
347         slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)(1) );
348         modv = Modifications2LDAPMods( &modlist );
349         slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
350
351         rc = doPluginFNs( be, SLAPI_PLUGIN_PRE_MODIFY_FN, pb );
352         if ( rc != 0 ) {
353                 /*
354                  * A preoperation plugin failure will abort the
355                  * entire operation.
356                  */
357 #ifdef NEW_LOGGING
358                 LDAP_LOG( OPERATION, INFO, "do_modify: modify preoperation plugin "
359                                 "failed\n", 0, 0, 0 );
360 #else
361                 Debug(LDAP_DEBUG_TRACE, "do_modify: modify preoperation plugin failed.\n",
362                                 0, 0, 0);
363 #endif
364                 if ( slapi_pblock_get( pb, SLAPI_RESULT_CODE, (void *)&rc ) != 0) {
365                         rc = LDAP_OPERATIONS_ERROR;
366                 }
367                 ldap_mods_free( modv, 1 );
368                 modv = NULL;
369                 goto cleanup;
370         }
371
372         /*
373          * It's possible that the preoperation plugin changed the
374          * modification array, so we need to convert it back to
375          * a Modification list.
376          *
377          * Calling Modifications2LDAPMods() destroyed modlist so
378          * we don't need to free it.
379          */
380         slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
381         modlist = LDAPMods2Modifications( modv );
382 #endif /* defined( LDAP_SLAPI ) */
383
384         /*
385          * do the modify if 1 && (2 || 3)
386          * 1) there is a modify function implemented in this backend;
387          * 2) this backend is master for what it holds;
388          * 3) it's a replica and the dn supplied is the update_ndn.
389          */
390         if ( be->be_modify ) {
391                 /* do the update here */
392                 int repl_user = be_isupdate( be, &op->o_ndn );
393 #ifndef SLAPD_MULTIMASTER
394                 /* Multimaster slapd does not have to check for replicator dn
395                  * because it accepts each modify request
396                  */
397                 if ( !be->be_update_ndn.bv_len || repl_user )
398 #endif
399                 {
400                         int update = be->be_update_ndn.bv_len;
401                         const char *text;
402                         char textbuf[SLAP_TEXT_BUFLEN];
403                         size_t textlen = sizeof textbuf;
404
405                         rc = slap_mods_check( modlist, update, &text,
406                                 textbuf, textlen );
407
408                         if( rc != LDAP_SUCCESS ) {
409                                 send_ldap_result( conn, op, rc,
410                                         NULL, text, NULL, NULL );
411                                 goto cleanup;
412                         }
413
414                         if ( !repl_user ) {
415                                 for( modtail = &modlist;
416                                         *modtail != NULL;
417                                         modtail = &(*modtail)->sml_next )
418                                 {
419                                         /* empty */
420                                 }
421
422                                 rc = slap_mods_opattrs( be, op, modlist, modtail, &text,
423                                         textbuf, textlen );
424                                 if( rc != LDAP_SUCCESS ) {
425                                         send_ldap_result( conn, op, rc,
426                                                 NULL, text,
427                                                 NULL, NULL );
428                                         goto cleanup;
429                                 }
430                         }
431
432                         if ( (*be->be_modify)( be, conn, op, &pdn, &ndn, modlist ) == 0
433 #ifdef SLAPD_MULTIMASTER
434                                 && !repl_user
435 #endif
436                         ) {
437                                 /* but we log only the ones not from a replicator user */
438                                 replog( be, op, &pdn, &ndn, modlist );
439                         }
440
441 #ifndef SLAPD_MULTIMASTER
442                 /* send a referral */
443                 } else {
444                         BerVarray defref = be->be_update_refs
445                                 ? be->be_update_refs : default_referral;
446                         BerVarray ref = referral_rewrite( defref,
447                                 NULL, &pdn, LDAP_SCOPE_DEFAULT );
448
449                         send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL, NULL,
450                                 ref ? ref : defref, NULL );
451
452                         ber_bvarray_free( ref );
453 #endif
454                 }
455         } else {
456                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
457                     NULL, "operation not supported within namingContext",
458                         NULL, NULL );
459         }
460
461 #if defined( LDAP_SLAPI )
462         if ( doPluginFNs( be, SLAPI_PLUGIN_POST_MODIFY_FN, pb ) != 0 ) {
463 #ifdef NEW_LOGGING
464                 LDAP_LOG( OPERATION, INFO, "do_modify: modify postoperation plugins "
465                                 "failed\n", 0, 0, 0 );
466 #else
467                 Debug(LDAP_DEBUG_TRACE, "do_modify: modify postoperation plugins "
468                                 "failed.\n", 0, 0, 0);
469 #endif
470         }
471 #endif /* defined( LDAP_SLAPI ) */
472
473 cleanup:
474         free( pdn.bv_val );
475         free( ndn.bv_val );
476         if ( modlist != NULL ) slap_mods_free( modlist );
477 #if defined( LDAP_SLAPI )
478         if ( modv != NULL ) FreeLDAPMods( modv );
479 #endif
480         return rc;
481 }
482
483 /*
484  * Do basic attribute type checking and syntax validation.
485  */
486 int slap_mods_check(
487         Modifications *ml,
488         int update,
489         const char **text,
490         char *textbuf,
491         size_t textlen )
492 {
493         int rc;
494
495         for( ; ml != NULL; ml = ml->sml_next ) {
496                 AttributeDescription *ad = NULL;
497
498                 /* convert to attribute description */
499                 rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
500
501                 if( rc != LDAP_SUCCESS ) {
502                         snprintf( textbuf, textlen, "%s: %s",
503                                 ml->sml_type.bv_val, *text );
504                         *text = textbuf;
505                         return rc;
506                 }
507
508                 ad = ml->sml_desc;
509
510                 if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
511                         && !slap_ad_is_binary( ad ))
512                 {
513                         /* attribute requires binary transfer */
514                         snprintf( textbuf, textlen,
515                                 "%s: requires ;binary transfer",
516                                 ml->sml_type.bv_val );
517                         *text = textbuf;
518                         return LDAP_UNDEFINED_TYPE;
519                 }
520
521                 if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
522                         && slap_ad_is_binary( ad ))
523                 {
524                         /* attribute requires binary transfer */
525                         snprintf( textbuf, textlen,
526                                 "%s: disallows ;binary transfer",
527                                 ml->sml_type.bv_val );
528                         *text = textbuf;
529                         return LDAP_UNDEFINED_TYPE;
530                 }
531
532                 if( slap_ad_is_tag_range( ad )) {
533                         /* attribute requires binary transfer */
534                         snprintf( textbuf, textlen,
535                                 "%s: inappropriate use of tag range option",
536                                 ml->sml_type.bv_val );
537                         *text = textbuf;
538                         return LDAP_UNDEFINED_TYPE;
539                 }
540
541                 if (!update && is_at_no_user_mod( ad->ad_type )) {
542                         /* user modification disallowed */
543                         snprintf( textbuf, textlen,
544                                 "%s: no user modification allowed",
545                                 ml->sml_type.bv_val );
546                         *text = textbuf;
547                         return LDAP_CONSTRAINT_VIOLATION;
548                 }
549
550                 if ( is_at_obsolete( ad->ad_type ) &&
551                         ( ml->sml_op == LDAP_MOD_ADD || ml->sml_bvalues != NULL ) )
552                 {
553                         /*
554                          * attribute is obsolete,
555                          * only allow replace/delete with no values
556                          */
557                         snprintf( textbuf, textlen,
558                                 "%s: attribute is obsolete",
559                                 ml->sml_type.bv_val );
560                         *text = textbuf;
561                         return LDAP_CONSTRAINT_VIOLATION;
562                 }
563
564                 /*
565                  * check values
566                  */
567                 if( ml->sml_bvalues != NULL ) {
568                         ber_len_t nvals;
569                         slap_syntax_validate_func *validate =
570                                 ad->ad_type->sat_syntax->ssyn_validate;
571                         slap_syntax_transform_func *pretty =
572                                 ad->ad_type->sat_syntax->ssyn_pretty;
573  
574                         if( !pretty && !validate ) {
575                                 *text = "no validator for syntax";
576                                 snprintf( textbuf, textlen,
577                                         "%s: no validator for syntax %s",
578                                         ml->sml_type.bv_val,
579                                         ad->ad_type->sat_syntax->ssyn_oid );
580                                 *text = textbuf;
581                                 return LDAP_INVALID_SYNTAX;
582                         }
583
584                         /*
585                          * check that each value is valid per syntax
586                          *      and pretty if appropriate
587                          */
588                         for( nvals = 0; ml->sml_bvalues[nvals].bv_val; nvals++ ) {
589                                 struct berval pval;
590                                 if( pretty ) {
591                                         rc = pretty( ad->ad_type->sat_syntax,
592                                                 &ml->sml_bvalues[nvals], &pval );
593                                 } else {
594                                         rc = validate( ad->ad_type->sat_syntax,
595                                                 &ml->sml_bvalues[nvals] );
596                                 }
597
598                                 if( rc != 0 ) {
599                                         snprintf( textbuf, textlen,
600                                                 "%s: value #%ld invalid per syntax",
601                                                 ml->sml_type.bv_val, (long) nvals );
602                                         *text = textbuf;
603                                         return LDAP_INVALID_SYNTAX;
604                                 }
605
606                                 if( pretty ) {
607                                         ber_memfree( ml->sml_bvalues[nvals].bv_val );
608                                         ml->sml_bvalues[nvals] = pval;
609                                 }
610                         }
611
612                         /*
613                          * a rough single value check... an additional check is needed
614                          * to catch add of single value to existing single valued attribute
615                          */
616                         if( ( ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE )
617                                 && nvals > 1 && is_at_single_value( ad->ad_type ))
618                         {
619                                 snprintf( textbuf, textlen,
620                                         "%s: multiple value provided",
621                                         ml->sml_type.bv_val );
622                                 *text = textbuf;
623                                 return LDAP_CONSTRAINT_VIOLATION;
624                         }
625                 }
626         }
627
628         return LDAP_SUCCESS;
629 }
630
631 int slap_mods_opattrs(
632         Backend *be,
633         Operation *op,
634         Modifications *mods,
635         Modifications **modtail,
636         const char **text,
637         char *textbuf, size_t textlen )
638 {
639         struct berval name, timestamp, csn;
640         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
641         char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
642         Modifications *mod;
643
644         int mop = op->o_tag == LDAP_REQ_ADD
645                 ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
646
647         assert( modtail != NULL );
648         assert( *modtail == NULL );
649
650         if( SLAP_LASTMOD(be) ) {
651                 struct tm *ltm;
652                 time_t now = slap_get_time();
653
654                 ldap_pvt_thread_mutex_lock( &gmtime_mutex );
655                 ltm = gmtime( &now );
656                 lutil_gentime( timebuf, sizeof(timebuf), ltm );
657
658                 csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
659                 ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
660                 csn.bv_val = csnbuf;
661
662                 timestamp.bv_val = timebuf;
663                 timestamp.bv_len = strlen(timebuf);
664
665                 if( op->o_dn.bv_len == 0 ) {
666                         name.bv_val = SLAPD_ANONYMOUS;
667                         name.bv_len = sizeof(SLAPD_ANONYMOUS)-1;
668                 } else {
669                         name = op->o_dn;
670                 }
671         }
672
673         if( op->o_tag == LDAP_REQ_ADD ) {
674                 struct berval tmpval;
675
676                 if( global_schemacheck ) {
677                         int rc = mods_structural_class( mods, &tmpval,
678                                 text, textbuf, textlen );
679                         if( rc != LDAP_SUCCESS ) {
680                                 return rc;
681                         }
682
683                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
684                         mod->sml_op = mop;
685                         mod->sml_type.bv_val = NULL;
686                         mod->sml_desc = slap_schema.si_ad_structuralObjectClass;
687                         mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
688                         ber_dupbv( &mod->sml_bvalues[0], &tmpval );
689                         mod->sml_bvalues[1].bv_val = NULL;
690                         assert( mod->sml_bvalues[0].bv_val );
691                         *modtail = mod;
692                         modtail = &mod->sml_next;
693                 }
694
695                 if( SLAP_LASTMOD(be) ) {
696                         char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
697
698                         tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
699                         tmpval.bv_val = uuidbuf;
700                 
701                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
702                         mod->sml_op = mop;
703                         mod->sml_type.bv_val = NULL;
704                         mod->sml_desc = slap_schema.si_ad_entryUUID;
705                         mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
706                         ber_dupbv( &mod->sml_bvalues[0], &tmpval );
707                         mod->sml_bvalues[1].bv_val = NULL;
708                         assert( mod->sml_bvalues[0].bv_val );
709                         *modtail = mod;
710                         modtail = &mod->sml_next;
711
712                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
713                         mod->sml_op = mop;
714                         mod->sml_type.bv_val = NULL;
715                         mod->sml_desc = slap_schema.si_ad_creatorsName;
716                         mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
717                         ber_dupbv( &mod->sml_bvalues[0], &name );
718                         mod->sml_bvalues[1].bv_val = NULL;
719                         assert( mod->sml_bvalues[0].bv_val );
720                         *modtail = mod;
721                         modtail = &mod->sml_next;
722
723                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
724                         mod->sml_op = mop;
725                         mod->sml_type.bv_val = NULL;
726                         mod->sml_desc = slap_schema.si_ad_createTimestamp;
727                         mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
728                         ber_dupbv( &mod->sml_bvalues[0], &timestamp );
729                         mod->sml_bvalues[1].bv_val = NULL;
730                         assert( mod->sml_bvalues[0].bv_val );
731                         *modtail = mod;
732                         modtail = &mod->sml_next;
733                 }
734         }
735
736         if( SLAP_LASTMOD(be) ) {
737                 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
738                 mod->sml_op = mop;
739                 mod->sml_type.bv_val = NULL;
740                 mod->sml_desc = slap_schema.si_ad_entryCSN;
741                 mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
742                 ber_dupbv( &mod->sml_bvalues[0], &csn );
743                 mod->sml_bvalues[1].bv_val = NULL;
744                 assert( mod->sml_bvalues[0].bv_val );
745                 *modtail = mod;
746                 modtail = &mod->sml_next;
747
748                 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
749                 mod->sml_op = mop;
750                 mod->sml_type.bv_val = NULL;
751                 mod->sml_desc = slap_schema.si_ad_modifiersName;
752                 mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
753                 ber_dupbv( &mod->sml_bvalues[0], &name );
754                 mod->sml_bvalues[1].bv_val = NULL;
755                 assert( mod->sml_bvalues[0].bv_val );
756                 *modtail = mod;
757                 modtail = &mod->sml_next;
758
759                 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
760                 mod->sml_op = mop;
761                 mod->sml_type.bv_val = NULL;
762                 mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
763                 mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
764                 ber_dupbv( &mod->sml_bvalues[0], &timestamp );
765                 mod->sml_bvalues[1].bv_val = NULL;
766                 assert( mod->sml_bvalues[0].bv_val );
767                 *modtail = mod;
768                 modtail = &mod->sml_next;
769         }
770
771         *modtail = NULL;
772         return LDAP_SUCCESS;
773 }
774
775 #ifdef LDAP_SLAPI
776 /*
777  * Synthesise an LDAPMod array from a Modifications list to pass
778  * to SLAPI. This synthesis is destructive and as such the 
779  * Modifications list may not be used after calling this 
780  * function.
781  * 
782  * This function must also be called before slap_mods_check().
783  */
784 static LDAPMod **Modifications2LDAPMods(Modifications **pmodlist)
785 {
786         Modifications *ml, *modlist;
787         LDAPMod **mods, *modp;
788         int i, j;
789
790         modlist = *pmodlist;
791
792         for( i = 0, ml = modlist; ml != NULL; i++, ml = ml->sml_next )
793                 ;
794
795         mods = (LDAPMod **)ch_malloc( (i + 1) * sizeof(LDAPMod *) );
796
797         for( i = 0, ml = modlist; ml != NULL; ml = ml->sml_next ) {
798                 modp = mods[i];
799                 modp->mod_op = ml->sml_op | LDAP_MOD_BVALUES;
800
801                 /* Take ownership of original type. */
802                 modp->mod_type = ml->sml_type.bv_val;
803                 ml->sml_type.bv_val = NULL;
804
805                 if ( ml->sml_bvalues != NULL ) {
806                         for( j = 0; ml->sml_bvalues[j].bv_val != NULL; j++ )
807                                 ;
808                         modp->mod_bvalues = (struct berval **)ch_malloc( (j + 1) *
809                                 sizeof(struct berval *) );
810                         for( j = 0; ml->sml_bvalues[j].bv_val != NULL; j++ ) {
811                                 /* Take ownership of original values. */
812                                 modp->mod_bvalues[j]->bv_len = ml->sml_bvalues[j].bv_len;
813                                 modp->mod_bvalues[j]->bv_val = ml->sml_bvalues[j].bv_val;
814                                 ml->sml_bvalues[j].bv_len = 0;
815                                 ml->sml_bvalues[j].bv_val = NULL;
816                         }
817                         modp->mod_bvalues[j] = NULL;
818                 } else {
819                         modp->mod_bvalues = NULL;
820                 }
821                 i++;
822         }
823
824         mods[i] = NULL;
825
826         slap_mods_free( modlist );
827         *pmodlist = NULL;
828
829         return mods;
830 }
831
832 /*
833  * Convert a potentially modified array of LDAPMods back to a
834  * Modification list. 
835  * 
836  * The returned Modification list contains pointers into the
837  * LDAPMods array; the latter MUST be freed with FreeLDAPMods()
838  * (see below).
839  */
840 static Modifications *LDAPMods2Modifications (LDAPMod **mods)
841 {
842         Modifications *modlist, **modtail;
843         LDAPMod **modp;
844
845         modtail = &modlist;
846
847         for( modp = mods; *modp != NULL; modp++ ) {
848                 Modifications *mod;
849                 int i;
850                 char **p;
851                 struct berval **bvp;
852
853                 mod = (Modifications *) ch_malloc( sizeof(Modifications) );
854                 mod->sml_op = (*modp)->mod_op & (~LDAP_MOD_BVALUES);
855                 mod->sml_type.bv_val = (*modp)->mod_type;
856                 mod->sml_type.bv_len = strlen( mod->sml_type.bv_val );
857                 mod->sml_desc = NULL;
858                 mod->sml_next = NULL;
859
860                 if ( (*modp)->mod_op & LDAP_MOD_BVALUES ) {
861                         for( i = 0, bvp = (*modp)->mod_bvalues; *bvp != NULL; bvp++, i++ )
862                                 ;
863                 } else {
864                         for( i = 0, p = (*modp)->mod_values; *p != NULL; p++, i++ )
865                                 ;
866                 }
867
868                 mod->sml_bvalues = (BerVarray) ch_malloc( (i + 1) * sizeof(struct berval) );
869
870                 /* NB: This implicitly trusts a plugin to return valid modifications. */
871                 if ( (*modp)->mod_op & LDAP_MOD_BVALUES ) {
872                         for( i = 0, bvp = (*modp)->mod_bvalues; *bvp != NULL; bvp++, i++ ) {
873                                 mod->sml_bvalues[i].bv_val = (*bvp)->bv_val;
874                                 mod->sml_bvalues[i].bv_len = (*bvp)->bv_len;
875                         }
876                 } else {
877                         for( i = 0, p = (*modp)->mod_values; *p != NULL; p++, i++ ) {
878                                 mod->sml_bvalues[i].bv_val = *p;
879                                 mod->sml_bvalues[i].bv_len = strlen( *p );
880                         }
881                 }
882                 mod->sml_bvalues[i].bv_val = NULL;
883
884                 *modtail = mod;
885                 modtail = &mod->sml_next;
886         }
887         
888         return modlist;
889 }
890
891 /*
892  * This function only frees the parts of the mods array that
893  * are not shared with the Modification list that was created
894  * by LDAPMods2Modifications. 
895  *
896  */
897 static void FreeLDAPMods (LDAPMod **mods)
898 {
899         int i;
900
901         if (mods == NULL)
902                 return;
903
904         for ( i = 0; mods[i] != NULL; i++ ) {
905                 /*
906                  * Don't free values themselves; they're owned by the
907                  * Modification list. Do free the containing array.
908                  */
909                 if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) {
910                         ch_free( mods[i]->mod_bvalues );
911                 } else {
912                         ch_free( mods[i]->mod_values );
913                 }
914                 /* Don't free type, for same reasons. */
915                 ch_free( mods[i] );
916         }
917         ch_free( mods );
918 }
919
920 #endif /* LDAP_SLAPI */
921