Back to index

openldap  2.4.31
mods.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in the file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00016  * All rights reserved.
00017  *
00018  * Redistribution and use in source and binary forms are permitted
00019  * provided that this notice is preserved and that due credit is given
00020  * to the University of Michigan at Ann Arbor. The name of the University
00021  * may not be used to endorse or promote products derived from this
00022  * software without specific prior written permission. This software
00023  * is provided ``as is'' without express or implied warranty.
00024  */
00025 
00026 #include "portable.h"
00027 
00028 #include <ac/string.h>
00029 
00030 #include "slap.h"
00031 #include "lutil.h"
00032 
00033 int
00034 modify_add_values(
00035        Entry         *e,
00036        Modification  *mod,
00037        int           permissive,
00038        const char    **text,
00039        char          *textbuf,
00040        size_t        textlen )
00041 {
00042        int           rc;
00043        const char    *op;
00044        Attribute     *a;
00045        Modification  pmod = *mod;
00046 
00047        switch ( mod->sm_op ) {
00048        case LDAP_MOD_ADD:
00049               op = "add";
00050               break;
00051        case LDAP_MOD_REPLACE:
00052               op = "replace";
00053               break;
00054        default:
00055               op = "?";
00056               assert( 0 );
00057        }
00058 
00059        /* FIXME: Catch old code that doesn't set sm_numvals.
00060         */
00061        if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
00062               unsigned i;
00063               for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
00064               assert( mod->sm_numvals == i );
00065        }
00066 
00067        /* check if values to add exist in attribute */
00068        a = attr_find( e->e_attrs, mod->sm_desc );
00069        if ( a != NULL ) {
00070               MatchingRule  *mr;
00071               struct berval *cvals;
00072               int           rc;
00073               unsigned i, p, flags;
00074 
00075               mr = mod->sm_desc->ad_type->sat_equality;
00076               if( mr == NULL || !mr->smr_match ) {
00077                      /* do not allow add of additional attribute
00078                             if no equality rule exists */
00079                      *text = textbuf;
00080                      snprintf( textbuf, textlen,
00081                             "modify/%s: %s: no equality matching rule",
00082                             op, mod->sm_desc->ad_cname.bv_val );
00083                      return LDAP_INAPPROPRIATE_MATCHING;
00084               }
00085 
00086               if ( permissive ) {
00087                      i = mod->sm_numvals;
00088                      pmod.sm_values = (BerVarray)ch_malloc(
00089                             (i + 1) * sizeof( struct berval ));
00090                      if ( pmod.sm_nvalues != NULL ) {
00091                             pmod.sm_nvalues = (BerVarray)ch_malloc(
00092                                    (i + 1) * sizeof( struct berval ));
00093                      }
00094               }
00095 
00096               /* no normalization is done in this routine nor
00097                * in the matching routines called by this routine. 
00098                * values are now normalized once on input to the
00099                * server (whether from LDAP or from the underlying
00100                * database).
00101                */
00102               if ( a->a_desc == slap_schema.si_ad_objectClass ) {
00103                      /* Needed by ITS#5517 */
00104                      flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
00105 
00106               } else {
00107                      flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
00108               }
00109               if ( mod->sm_nvalues ) {
00110                      flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
00111                             SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
00112                      cvals = mod->sm_nvalues;
00113               } else {
00114                      cvals = mod->sm_values;
00115               }
00116               for ( p = i = 0; i < mod->sm_numvals; i++ ) {
00117                      unsigned      slot;
00118 
00119                      rc = attr_valfind( a, flags, &cvals[i], &slot, NULL );
00120                      if ( rc == LDAP_SUCCESS ) {
00121                             if ( !permissive ) {
00122                                    /* value already exists */
00123                                    *text = textbuf;
00124                                    snprintf( textbuf, textlen,
00125                                           "modify/%s: %s: value #%u already exists",
00126                                           op, mod->sm_desc->ad_cname.bv_val, i );
00127                                    return LDAP_TYPE_OR_VALUE_EXISTS;
00128                             }
00129                      } else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
00130                             return rc;
00131                      }
00132 
00133                      if ( permissive && rc ) {
00134                             if ( pmod.sm_nvalues ) {
00135                                    pmod.sm_nvalues[p] = mod->sm_nvalues[i];
00136                             }
00137                             pmod.sm_values[p++] = mod->sm_values[i];
00138                      }
00139               }
00140 
00141               if ( permissive ) {
00142                      if ( p == 0 ) {
00143                             /* all new values match exist */
00144                             ch_free( pmod.sm_values );
00145                             if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
00146                             return LDAP_SUCCESS;
00147                      }
00148 
00149                      BER_BVZERO( &pmod.sm_values[p] );
00150                      if ( pmod.sm_nvalues ) {
00151                             BER_BVZERO( &pmod.sm_nvalues[p] );
00152                      }
00153               }
00154        }
00155 
00156        /* no - add them */
00157        if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
00158               rc = ordered_value_add( e, mod->sm_desc, a,
00159                      pmod.sm_values, pmod.sm_nvalues );
00160        } else {
00161               rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
00162        }
00163 
00164        if ( a != NULL && permissive ) {
00165               ch_free( pmod.sm_values );
00166               if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
00167        }
00168 
00169        if ( rc != 0 ) {
00170               /* this should return result of attr_merge */
00171               *text = textbuf;
00172               snprintf( textbuf, textlen,
00173                      "modify/%s: %s: merge error (%d)",
00174                      op, mod->sm_desc->ad_cname.bv_val, rc );
00175               return LDAP_OTHER;
00176        }
00177 
00178        return LDAP_SUCCESS;
00179 }
00180 
00181 int
00182 modify_delete_values(
00183        Entry  *e,
00184        Modification  *m,
00185        int    perm,
00186        const char    **text,
00187        char *textbuf, size_t textlen )
00188 {
00189        return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
00190 }
00191 
00192 int
00193 modify_delete_vindex(
00194        Entry  *e,
00195        Modification  *mod,
00196        int    permissive,
00197        const char    **text,
00198        char *textbuf, size_t textlen,
00199        int *idx )
00200 {
00201        Attribute     *a;
00202        MatchingRule  *mr = mod->sm_desc->ad_type->sat_equality;
00203        struct berval *cvals;
00204        int           *id2 = NULL;
00205        int           rc = 0;
00206        unsigned i, j, flags;
00207        char          dummy = '\0';
00208 
00209        /*
00210         * If permissive is set, then the non-existence of an 
00211         * attribute is not treated as an error.
00212         */
00213 
00214        /* delete the entire attribute */
00215        if ( mod->sm_values == NULL ) {
00216               rc = attr_delete( &e->e_attrs, mod->sm_desc );
00217 
00218               if( permissive ) {
00219                      rc = LDAP_SUCCESS;
00220               } else if( rc != LDAP_SUCCESS ) {
00221                      *text = textbuf;
00222                      snprintf( textbuf, textlen,
00223                             "modify/delete: %s: no such attribute",
00224                             mod->sm_desc->ad_cname.bv_val );
00225                      rc = LDAP_NO_SUCH_ATTRIBUTE;
00226               }
00227               return rc;
00228        }
00229 
00230        /* FIXME: Catch old code that doesn't set sm_numvals.
00231         */
00232        if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
00233               for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
00234               assert( mod->sm_numvals == i );
00235        }
00236        if ( !idx ) {
00237               id2 = ch_malloc( mod->sm_numvals * sizeof( int ));
00238               idx = id2;
00239        }
00240 
00241        if( mr == NULL || !mr->smr_match ) {
00242               /* disallow specific attributes from being deleted if
00243                      no equality rule */
00244               *text = textbuf;
00245               snprintf( textbuf, textlen,
00246                      "modify/delete: %s: no equality matching rule",
00247                      mod->sm_desc->ad_cname.bv_val );
00248               rc = LDAP_INAPPROPRIATE_MATCHING;
00249               goto return_result;
00250        }
00251 
00252        /* delete specific values - find the attribute first */
00253        if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
00254               if( permissive ) {
00255                      rc = LDAP_SUCCESS;
00256                      goto return_result;
00257               }
00258               *text = textbuf;
00259               snprintf( textbuf, textlen,
00260                      "modify/delete: %s: no such attribute",
00261                      mod->sm_desc->ad_cname.bv_val );
00262               rc = LDAP_NO_SUCH_ATTRIBUTE;
00263               goto return_result;
00264        }
00265 
00266        if ( a->a_desc == slap_schema.si_ad_objectClass ) {
00267               /* Needed by ITS#5517,ITS#5963 */
00268               flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
00269 
00270        } else {
00271               flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
00272        }
00273        if ( mod->sm_nvalues ) {
00274               flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
00275                      | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
00276               cvals = mod->sm_nvalues;
00277        } else {
00278               cvals = mod->sm_values;
00279        }
00280 
00281        /* Locate values to delete */
00282        for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
00283               unsigned sort;
00284               rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
00285               if ( rc == LDAP_SUCCESS ) {
00286                      idx[i] = sort;
00287               } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
00288                      if ( permissive ) {
00289                             idx[i] = -1;
00290                             continue;
00291                      }
00292                      *text = textbuf;
00293                      snprintf( textbuf, textlen,
00294                             "modify/delete: %s: no such value",
00295                             mod->sm_desc->ad_cname.bv_val );
00296                      goto return_result;
00297               } else {
00298                      *text = textbuf;
00299                      snprintf( textbuf, textlen,
00300                             "modify/delete: %s: matching rule failed",
00301                             mod->sm_desc->ad_cname.bv_val );
00302                      goto return_result;
00303               }
00304        }
00305 
00306        /* Delete the values */
00307        for ( i = 0; i < mod->sm_numvals; i++ ) {
00308               /* Skip permissive values that weren't found */
00309               if ( idx[i] < 0 )
00310                      continue;
00311               /* Skip duplicate delete specs */
00312               if ( a->a_vals[idx[i]].bv_val == &dummy )
00313                      continue;
00314               /* delete value and mark it as gone */
00315               free( a->a_vals[idx[i]].bv_val );
00316               a->a_vals[idx[i]].bv_val = &dummy;
00317               if( a->a_nvals != a->a_vals ) {
00318                      free( a->a_nvals[idx[i]].bv_val );
00319                      a->a_nvals[idx[i]].bv_val = &dummy;
00320               }
00321               a->a_numvals--;
00322        }
00323 
00324        /* compact array skipping dummies */
00325        for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
00326               /* skip dummies */
00327               if( a->a_vals[i].bv_val == &dummy ) {
00328                      assert( a->a_nvals[i].bv_val == &dummy );
00329                      continue;
00330               }
00331               if ( j != i ) {
00332                      a->a_vals[ j ] = a->a_vals[ i ];
00333                      if (a->a_nvals != a->a_vals) {
00334                             a->a_nvals[ j ] = a->a_nvals[ i ];
00335                      }
00336               }
00337               j++;
00338        }
00339 
00340        BER_BVZERO( &a->a_vals[j] );
00341        if (a->a_nvals != a->a_vals) {
00342               BER_BVZERO( &a->a_nvals[j] );
00343        }
00344 
00345        /* if no values remain, delete the entire attribute */
00346        if ( !a->a_numvals ) {
00347               if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
00348                      /* Can never happen */
00349                      *text = textbuf;
00350                      snprintf( textbuf, textlen,
00351                             "modify/delete: %s: no such attribute",
00352                             mod->sm_desc->ad_cname.bv_val );
00353                      rc = LDAP_NO_SUCH_ATTRIBUTE;
00354               }
00355        } else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
00356               /* For an ordered attribute, renumber the value indices */
00357               ordered_value_sort( a, 1 );
00358        }
00359 return_result:
00360        if ( id2 )
00361               ch_free( id2 );
00362        return rc;
00363 }
00364 
00365 int
00366 modify_replace_values(
00367        Entry  *e,
00368        Modification  *mod,
00369        int           permissive,
00370        const char    **text,
00371        char *textbuf, size_t textlen )
00372 {
00373        (void) attr_delete( &e->e_attrs, mod->sm_desc );
00374 
00375        if ( mod->sm_values ) {
00376               return modify_add_values( e, mod, permissive, text, textbuf, textlen );
00377        }
00378 
00379        return LDAP_SUCCESS;
00380 }
00381 
00382 int
00383 modify_increment_values(
00384        Entry  *e,
00385        Modification  *mod,
00386        int    permissive,
00387        const char    **text,
00388        char *textbuf, size_t textlen )
00389 {
00390        Attribute *a;
00391 
00392        a = attr_find( e->e_attrs, mod->sm_desc );
00393        if( a == NULL ) {
00394               if ( permissive ) {
00395                      Modification modReplace = *mod;
00396 
00397                      modReplace.sm_op = LDAP_MOD_REPLACE;
00398 
00399                      return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
00400               } else {
00401                      *text = textbuf;
00402                      snprintf( textbuf, textlen,
00403                             "modify/increment: %s: no such attribute",
00404                             mod->sm_desc->ad_cname.bv_val );
00405                      return LDAP_NO_SUCH_ATTRIBUTE;
00406               }
00407        }
00408 
00409        if ( !strcmp( a->a_desc->ad_type->sat_syntax_oid, SLAPD_INTEGER_SYNTAX )) {
00410               int i;
00411               char str[sizeof(long)*3 + 2]; /* overly long */
00412               long incr;
00413 
00414               if ( lutil_atol( &incr, mod->sm_values[0].bv_val ) != 0 ) {
00415                      *text = "modify/increment: invalid syntax of increment";
00416                      return LDAP_INVALID_SYNTAX;
00417               }
00418 
00419               /* treat zero and errors as a no-op */
00420               if( incr == 0 ) {
00421                      return LDAP_SUCCESS;
00422               }
00423 
00424               for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
00425                      char *tmp;
00426                      long value;
00427                      size_t strln;
00428                      if ( lutil_atol( &value, a->a_nvals[i].bv_val ) != 0 ) {
00429                             *text = "modify/increment: invalid syntax of original value";
00430                             return LDAP_INVALID_SYNTAX;
00431                      }
00432                      strln = snprintf( str, sizeof(str), "%ld", value+incr );
00433 
00434                      tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
00435                      if( tmp == NULL ) {
00436                             *text = "modify/increment: reallocation error";
00437                             return LDAP_OTHER;
00438                      }
00439                      a->a_nvals[i].bv_val = tmp;
00440                      a->a_nvals[i].bv_len = strln;
00441 
00442                      AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
00443               }
00444 
00445        } else {
00446               snprintf( textbuf, textlen,
00447                      "modify/increment: %s: increment not supported for value syntax %s",
00448                      mod->sm_desc->ad_cname.bv_val,
00449                      a->a_desc->ad_type->sat_syntax_oid );
00450               return LDAP_CONSTRAINT_VIOLATION;
00451        }
00452 
00453        return LDAP_SUCCESS;
00454 }
00455 
00456 void
00457 slap_mod_free(
00458        Modification  *mod,
00459        int                         freeit )
00460 {
00461        if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
00462        mod->sm_values = NULL;
00463 
00464        if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
00465        mod->sm_nvalues = NULL;
00466 
00467        if( freeit ) free( mod );
00468 }
00469 
00470 void
00471 slap_mods_free(
00472     Modifications    *ml,
00473     int                     freevals )
00474 {
00475        Modifications *next;
00476 
00477        for ( ; ml != NULL; ml = next ) {
00478               next = ml->sml_next;
00479 
00480               if ( freevals )
00481                      slap_mod_free( &ml->sml_mod, 0 );
00482               free( ml );
00483        }
00484 }
00485