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