Back to index

openldap  2.4.31
autogroup.c
Go to the documentation of this file.
00001 /* autogroup.c - automatic group overlay */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2007-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2007 Michał Szulczyński.
00007  * Portions Copyright 2009 Howard Chu.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted only as authorized by the OpenLDAP
00012  * Public License.
00013  *
00014  * A copy of this license is available in the file LICENSE in the
00015  * top-level directory of the distribution or, alternatively, at
00016  * <http://www.OpenLDAP.org/license.html>.
00017  */
00018 /* ACKNOWLEDGEMENTS:
00019  * This work was initially developed by Michał Szulczyński for inclusion in
00020  * OpenLDAP Software.  Additional significant contributors include:
00021  *   Howard Chu
00022  *   Raphael Ouazana
00023  *   Norbert Pueschel
00024  *   Christian Manal
00025  */
00026 
00027 #include "portable.h"
00028 
00029 #include <stdio.h>
00030 
00031 #include <ac/string.h>
00032 
00033 #include "slap.h"
00034 #include "config.h"
00035 #include "lutil.h"
00036 
00037 #ifndef SLAPD_MEMBEROF_ATTR
00038 #define       SLAPD_MEMBEROF_ATTR  "memberOf"
00039 #endif
00040 
00041 /* Filter represents the memberURL of a group. */
00042 typedef struct autogroup_filter_t {
00043        struct berval               agf_dn;       /* The base DN in memberURL */
00044        struct berval               agf_ndn;
00045        struct berval               agf_filterstr;
00046        Filter                      *agf_filter;
00047        int                         agf_scope;
00048        AttributeName               *agf_anlist;
00049        struct autogroup_filter_t   *agf_next;
00050 } autogroup_filter_t;
00051 
00052 /* Description of group attributes. */
00053 typedef struct autogroup_def_t {
00054        ObjectClass          *agd_oc;
00055        AttributeDescription *agd_member_url_ad;
00056        AttributeDescription *agd_member_ad;
00057        struct autogroup_def_t      *agd_next;
00058 } autogroup_def_t;
00059 
00060 /* Represents the group entry. */
00061 typedef struct autogroup_entry_t {
00062        BerValue             age_dn;
00063        BerValue             age_ndn;
00064        autogroup_filter_t   *age_filter; /* List of filters made from memberURLs */
00065        autogroup_def_t             *age_def; /* Attribute definition */
00066        ldap_pvt_thread_mutex_t age_mutex;
00067        int                  age_mustrefresh; /* Defined in request to refresh in response */
00068        int                  age_modrdn_olddnmodified; /* Defined in request to refresh in response */
00069        struct autogroup_entry_t    *age_next;
00070 } autogroup_entry_t;
00071 
00072 /* Holds pointers to attribute definitions and groups. */
00073 typedef struct autogroup_info_t {
00074        autogroup_def_t             *agi_def;     /* Group attributes definitions. */
00075        autogroup_entry_t    *agi_entry;   /* Group entries.  */
00076        AttributeDescription *agi_memberof_ad;    /* memberOf attribute description  */
00077        ldap_pvt_thread_mutex_t agi_mutex;
00078 } autogroup_info_t;
00079 
00080 /* Search callback for adding groups initially. */
00081 typedef struct autogroup_sc_t {
00082        autogroup_info_t            *ags_info;    /* Group definitions and entries.  */
00083        autogroup_def_t             *ags_def;     /* Attributes definition of the group being added. */
00084 } autogroup_sc_t;
00085 
00086 /* Used for adding members, found when searching, to a group. */
00087 typedef struct autogroup_ga_t {
00088        autogroup_entry_t    *agg_group;   /* The group to which the members will be added. */
00089        autogroup_filter_t   *agg_filter;  /* Current filter */
00090        Entry                *agg_entry;   /* Used in autogroup_member_search_cb to modify 
00091                                           this entry with the search results. */
00092 
00093        Modifications        *agg_mod;     /* Used in autogroup_member_search_modify_cb to hold the 
00094                                           search results which will be added to the group. */
00095 
00096        Modifications        *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't 
00097                                           have to search for the last mod added. */
00098 } autogroup_ga_t;
00099 
00100 
00101 /* 
00102 **     dn, ndn       - the DN of the member to add
00103 **     age    - the group to which the member DN will be added
00104 */
00105 static int
00106 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
00107 {
00108        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00109        Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
00110        SlapReply     sreply = {REP_RESULT};
00111        BerValue      *vals, *nvals;
00112        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
00113        Operation     o = *op;
00114 
00115        assert( dn != NULL );
00116        assert( ndn != NULL );
00117        Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
00118               dn->bv_val, age->age_dn.bv_val, 0);
00119 
00120        vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
00121        nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
00122        ber_dupbv( vals, dn );
00123        BER_BVZERO( &vals[ 1 ] );
00124        ber_dupbv( nvals, ndn );
00125        BER_BVZERO( &nvals[ 1 ] );
00126 
00127        modlist->sml_op = LDAP_MOD_ADD;
00128        modlist->sml_desc = age->age_def->agd_member_ad;
00129        modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
00130        modlist->sml_values = vals;
00131        modlist->sml_nvalues = nvals;
00132        modlist->sml_numvals = 1;
00133        modlist->sml_flags = SLAP_MOD_INTERNAL;
00134        modlist->sml_next = NULL;
00135 
00136        o.o_tag = LDAP_REQ_MODIFY;
00137        o.o_callback = &cb;
00138        o.orm_modlist = modlist;
00139        o.o_req_dn = age->age_dn;
00140        o.o_req_ndn = age->age_ndn;
00141        o.o_permissive_modify = 1;
00142        o.o_managedsait = SLAP_CONTROL_CRITICAL;
00143        o.o_relax = SLAP_CONTROL_CRITICAL;
00144 
00145        o.o_bd->bd_info = (BackendInfo *)on->on_info;
00146        (void)op->o_bd->be_modify( &o, &sreply );
00147        o.o_bd->bd_info = (BackendInfo *)on;
00148 
00149        slap_mods_free( modlist, 1 );
00150 
00151        return sreply.sr_err;
00152 }
00153 
00154 /* 
00155 **     e      - the entry where to get the attribute values
00156 **     age    - the group to which the values will be added
00157 */
00158 static int
00159 autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
00160 {
00161        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00162        Modifications modlist;
00163        SlapReply     sreply = {REP_RESULT};
00164        Attribute     *attr;
00165        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
00166        Operation     o = *op;
00167 
00168        assert( e != NULL );
00169        Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
00170               e->e_name.bv_val, age->age_dn.bv_val, 0);
00171 
00172        attr = attrs_find( e->e_attrs, attrdesc );
00173        if (!attr) {
00174               // Nothing to add
00175               return LDAP_SUCCESS;
00176        }
00177 
00178        modlist.sml_op = LDAP_MOD_ADD;
00179        modlist.sml_desc = age->age_def->agd_member_ad;
00180        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
00181        modlist.sml_values = attr->a_vals;
00182        modlist.sml_nvalues = attr->a_nvals;
00183        modlist.sml_numvals = attr->a_numvals;
00184        modlist.sml_flags = SLAP_MOD_INTERNAL;
00185        modlist.sml_next = NULL;
00186 
00187        o.o_tag = LDAP_REQ_MODIFY;
00188        o.o_callback = &cb;
00189        o.orm_modlist = &modlist;
00190        o.o_req_dn = age->age_dn;
00191        o.o_req_ndn = age->age_ndn;
00192        o.o_permissive_modify = 1;
00193        o.o_managedsait = SLAP_CONTROL_CRITICAL;
00194        o.o_relax = SLAP_CONTROL_CRITICAL;
00195 
00196        o.o_bd->bd_info = (BackendInfo *)on->on_info;
00197        (void)op->o_bd->be_modify( &o, &sreply );
00198        o.o_bd->bd_info = (BackendInfo *)on;
00199 
00200        return sreply.sr_err;
00201 }
00202 
00203 /*
00204 ** dn,ndn     - the DN to be deleted
00205 ** age        - the group from which the DN will be deleted
00206 ** If we pass a NULL dn and ndn, all members are deleted from the group. 
00207 */
00208 static int
00209 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
00210 {
00211        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00212        Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
00213        SlapReply     sreply = {REP_RESULT};
00214        BerValue      *vals, *nvals;
00215        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
00216        Operation     o = *op;
00217 
00218        if ( dn == NULL || ndn == NULL ) {
00219               Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
00220                      age->age_dn.bv_val, 0 ,0);
00221 
00222               modlist->sml_values = NULL;
00223               modlist->sml_nvalues = NULL;
00224               modlist->sml_numvals = 0;
00225        } else {
00226               Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
00227                      dn->bv_val, age->age_dn.bv_val, 0);
00228 
00229               vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
00230               nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
00231               ber_dupbv( vals, dn );
00232               BER_BVZERO( &vals[ 1 ] );
00233               ber_dupbv( nvals, ndn );
00234               BER_BVZERO( &nvals[ 1 ] );
00235 
00236               modlist->sml_values = vals;
00237               modlist->sml_nvalues = nvals;
00238               modlist->sml_numvals = 1;
00239        }
00240 
00241 
00242        modlist->sml_op = LDAP_MOD_DELETE;
00243        modlist->sml_desc = age->age_def->agd_member_ad;
00244        modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
00245        modlist->sml_flags = SLAP_MOD_INTERNAL;
00246        modlist->sml_next = NULL;
00247 
00248        o.o_callback = &cb;
00249        o.o_tag = LDAP_REQ_MODIFY;
00250        o.orm_modlist = modlist;
00251        o.o_req_dn = age->age_dn;
00252        o.o_req_ndn = age->age_ndn;
00253        o.o_relax = SLAP_CONTROL_CRITICAL;
00254        o.o_managedsait = SLAP_CONTROL_CRITICAL;
00255        o.o_permissive_modify = 1;
00256 
00257        o.o_bd->bd_info = (BackendInfo *)on->on_info;
00258        (void)op->o_bd->be_modify( &o, &sreply );
00259        o.o_bd->bd_info = (BackendInfo *)on;
00260 
00261        slap_mods_free( modlist, 1 );
00262 
00263        return sreply.sr_err;
00264 }
00265 
00266 /*
00267 **      e       - the entry where to get the attribute values
00268 **      age     - the group from which the values will be deleted
00269 */
00270 static int
00271 autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
00272 {
00273         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
00274         Modifications   modlist;
00275         SlapReply       sreply = {REP_RESULT};
00276         Attribute       *attr;
00277         slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
00278         Operation       o = *op;
00279 
00280         assert( e != NULL );
00281         Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
00282                 e->e_name.bv_val, age->age_dn.bv_val, 0);
00283 
00284         attr = attrs_find( e->e_attrs, attrdesc );
00285         if (!attr) {
00286                 // Nothing to add
00287                 return LDAP_SUCCESS;
00288         }
00289 
00290         modlist.sml_op = LDAP_MOD_DELETE;
00291         modlist.sml_desc = age->age_def->agd_member_ad;
00292         modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
00293         modlist.sml_values = attr->a_vals;
00294         modlist.sml_nvalues = attr->a_nvals;
00295         modlist.sml_numvals = attr->a_numvals;
00296         modlist.sml_flags = SLAP_MOD_INTERNAL;
00297         modlist.sml_next = NULL;
00298 
00299         o.o_tag = LDAP_REQ_MODIFY;
00300         o.o_callback = &cb;
00301         o.orm_modlist = &modlist;
00302         o.o_req_dn = age->age_dn;
00303         o.o_req_ndn = age->age_ndn;
00304         o.o_permissive_modify = 1;
00305         o.o_managedsait = SLAP_CONTROL_CRITICAL;
00306         o.o_relax = SLAP_CONTROL_CRITICAL;
00307 
00308         o.o_bd->bd_info = (BackendInfo *)on->on_info;
00309         (void)op->o_bd->be_modify( &o, &sreply );
00310         o.o_bd->bd_info = (BackendInfo *)on;
00311 
00312         return sreply.sr_err;
00313 }
00314 
00315 /* 
00316 ** Callback used to add entries to a group, 
00317 ** which are going to be written in the database
00318 ** (used in bi_op_add)
00319 ** The group is passed in autogroup_ga_t->agg_group
00320 */
00321 static int
00322 autogroup_member_search_cb( Operation *op, SlapReply *rs )
00323 {
00324        assert( op->o_tag == LDAP_REQ_SEARCH );
00325 
00326        if ( rs->sr_type == REP_SEARCH ) {
00327               autogroup_ga_t              *agg = (autogroup_ga_t *)op->o_callback->sc_private;
00328               autogroup_entry_t    *age = agg->agg_group;
00329               autogroup_filter_t   *agf = agg->agg_filter;
00330               Modification         mod;
00331               const char           *text = NULL;
00332               char                 textbuf[1024];
00333               struct berval        *vals, *nvals;
00334               int                  numvals;
00335 
00336               Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
00337                      rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
00338 
00339               if ( agf->agf_anlist ) {
00340                      Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
00341                      if (attr) {
00342                             vals = attr->a_vals;
00343                             nvals = attr->a_nvals;
00344                             numvals = attr->a_numvals;
00345                      } else {
00346                             // Nothing to add
00347                             return 0;
00348                      }
00349               } else {
00350                      struct berval        lvals[ 2 ], lnvals[ 2 ];
00351                      lvals[ 0 ] = rs->sr_entry->e_name;
00352                      BER_BVZERO( &lvals[ 1 ] );
00353                      lnvals[ 0 ] = rs->sr_entry->e_nname;
00354                      BER_BVZERO( &lnvals[ 1 ] );
00355                      vals = lvals;
00356                      nvals = lnvals;
00357                      numvals = 1;
00358               }
00359 
00360               mod.sm_op = LDAP_MOD_ADD;
00361               mod.sm_desc = age->age_def->agd_member_ad;
00362               mod.sm_type = age->age_def->agd_member_ad->ad_cname;
00363               mod.sm_values = vals;
00364               mod.sm_nvalues = nvals;
00365               mod.sm_numvals = numvals;
00366 
00367               modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
00368        }
00369 
00370        return 0;
00371 }
00372 
00373 /* 
00374 ** Callback used to add entries to a group, which is already in the database.
00375 ** (used in on_response)
00376 ** The group is passed in autogroup_ga_t->agg_group
00377 ** NOTE: Very slow.
00378 */
00379 static int
00380 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
00381 {
00382        assert( op->o_tag == LDAP_REQ_SEARCH );
00383 
00384        if ( rs->sr_type == REP_SEARCH ) {
00385               autogroup_ga_t              *agg = (autogroup_ga_t *)op->o_callback->sc_private;
00386               autogroup_entry_t    *age = agg->agg_group;
00387               autogroup_filter_t   *agf = agg->agg_filter;
00388               Modifications        *modlist;
00389               struct berval        *vals, *nvals;
00390               int                  numvals;
00391 
00392               Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
00393                      rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
00394 
00395               if ( agf->agf_anlist ) {
00396                      Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
00397                      if (attr) {
00398                             vals = attr->a_vals;
00399                             nvals = attr->a_nvals;
00400                             numvals = attr->a_numvals;
00401                      } else {
00402                             // Nothing to add
00403                             return 0;
00404                      }
00405               } else {
00406                      struct berval        lvals[ 2 ], lnvals[ 2 ];
00407                      lvals[ 0 ] = rs->sr_entry->e_name;
00408                      BER_BVZERO( &lvals[ 1 ] );
00409                      lnvals[ 0 ] = rs->sr_entry->e_nname;
00410                      BER_BVZERO( &lnvals[ 1 ] );
00411                      vals = lvals;
00412                      nvals = lnvals;
00413                      numvals = 1;
00414               }
00415 
00416               if ( numvals ) {
00417                      modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
00418 
00419                      modlist->sml_op = LDAP_MOD_ADD;
00420                      modlist->sml_desc = age->age_def->agd_member_ad;
00421                      modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
00422 
00423                      ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
00424                      ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
00425                      modlist->sml_numvals = numvals;
00426 
00427                      modlist->sml_flags = SLAP_MOD_INTERNAL;
00428                      modlist->sml_next = NULL;
00429 
00430                      if ( agg->agg_mod == NULL ) {
00431                             agg->agg_mod = modlist;
00432                             agg->agg_mod_last = modlist;
00433                      } else {
00434                             agg->agg_mod_last->sml_next = modlist;
00435                             agg->agg_mod_last = modlist;
00436                      }
00437               }
00438 
00439        }
00440 
00441        return 0;
00442 }
00443 
00444 
00445 /*
00446 ** Adds all entries matching the passed filter to the specified group.
00447 ** If modify == 1, then we modify the group's entry in the database using be_modify.
00448 ** If modify == 0, then, we must supply a rw entry for the group, 
00449 **     because we only modify the entry, without calling be_modify.
00450 ** e   - the group entry, to which the members will be added
00451 ** age - the group
00452 ** agf - the filter
00453 */
00454 static int
00455 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
00456 {
00457        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
00458        Operation            o = *op;
00459        SlapReply            rs = { REP_SEARCH };
00460        slap_callback        cb = { 0 };
00461        slap_callback        null_cb = { NULL, slap_null_cb, NULL, NULL };
00462        autogroup_ga_t              agg;
00463 
00464        Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
00465               age->age_dn.bv_val, 0, 0);
00466 
00467        o.ors_attrsonly = 0;
00468        o.o_tag = LDAP_REQ_SEARCH;
00469 
00470        o.o_req_dn = agf->agf_dn;
00471        o.o_req_ndn = agf->agf_ndn;
00472 
00473        o.ors_filterstr = agf->agf_filterstr;
00474        o.ors_filter = agf->agf_filter;
00475 
00476        o.ors_scope = agf->agf_scope;
00477        o.ors_deref = LDAP_DEREF_NEVER;
00478        o.ors_limit = NULL;
00479        o.ors_tlimit = SLAP_NO_LIMIT;
00480        o.ors_slimit = SLAP_NO_LIMIT;
00481        o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
00482 
00483        agg.agg_group = age;
00484        agg.agg_filter = agf;
00485        agg.agg_mod = NULL;
00486        agg.agg_mod_last = NULL;
00487        agg.agg_entry = e;
00488        cb.sc_private = &agg;
00489 
00490        if ( modify == 1 ) {
00491               cb.sc_response = autogroup_member_search_modify_cb;
00492        } else {
00493               cb.sc_response = autogroup_member_search_cb;
00494        }
00495 
00496        cb.sc_cleanup = NULL;
00497        cb.sc_next = NULL;
00498 
00499        o.o_callback = &cb;
00500 
00501        o.o_bd->bd_info = (BackendInfo *)on->on_info;
00502        op->o_bd->be_search( &o, &rs );
00503        o.o_bd->bd_info = (BackendInfo *)on;      
00504 
00505        if ( modify == 1 && agg.agg_mod ) {
00506               rs_reinit( &rs, REP_RESULT );
00507 
00508               o = *op;
00509               o.o_callback = &null_cb;
00510               o.o_tag = LDAP_REQ_MODIFY;
00511               o.orm_modlist = agg.agg_mod;
00512               o.o_req_dn = age->age_dn;
00513               o.o_req_ndn = age->age_ndn;
00514               o.o_relax = SLAP_CONTROL_CRITICAL;
00515               o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
00516               o.o_permissive_modify = 1;
00517 
00518               o.o_bd->bd_info = (BackendInfo *)on->on_info;
00519               (void)op->o_bd->be_modify( &o, &rs );
00520               o.o_bd->bd_info = (BackendInfo *)on;      
00521 
00522               slap_mods_free(agg.agg_mod, 1);
00523        }
00524 
00525        return 0;
00526 }
00527 
00528 /* 
00529 ** Adds a group to the internal list from the passed entry.
00530 ** scan specifies whether to add all maching members to the group.
00531 ** modify specifies whether to modify the given group entry (when modify == 0),
00532 **     or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
00533 ** agi - pointer to the groups and the attribute definitions
00534 ** agd - the attribute definition of the added group
00535 ** e   - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
00536 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
00537 */
00538 static int
00539 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
00540 {
00541        autogroup_entry_t    **agep = &agi->agi_entry;
00542        autogroup_filter_t   *agf, *agf_prev = NULL;
00543        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
00544        LDAPURLDesc          *lud = NULL;
00545        Attribute            *a;
00546        BerValue             *bv, dn;
00547        int                  rc = 0, match = 1, null_entry = 0;
00548 
00549        if ( e == NULL ) {
00550               if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
00551                      LDAP_SUCCESS || e == NULL ) {
00552                      Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
00553                      return 1;
00554               }
00555 
00556               null_entry = 1;
00557        }
00558 
00559        Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
00560               e->e_name.bv_val, 0, 0);
00561 
00562        if ( agi->agi_entry != NULL ) {
00563               for ( ; *agep ; agep = &(*agep)->age_next ) {
00564                      dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
00565                      if ( match == 0 ) {
00566                             Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
00567                             return 1;
00568                      }
00569                      /* goto last */;
00570               }
00571        }
00572 
00573 
00574        *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
00575        ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
00576        (*agep)->age_def = agd;
00577        (*agep)->age_filter = NULL;
00578        (*agep)->age_mustrefresh = 0;
00579        (*agep)->age_modrdn_olddnmodified = 0;
00580 
00581        ber_dupbv( &(*agep)->age_dn, &e->e_name );
00582        ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
00583 
00584        a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
00585 
00586        if ( null_entry == 1 ) {
00587               a = attrs_dup( a );
00588               overlay_entry_release_ov( op, e, 0, on );
00589        }
00590 
00591        if( a == NULL ) {
00592               Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
00593        } else {
00594               for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
00595 
00596                      agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
00597 
00598                      if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
00599                             Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
00600                             /* FIXME: error? */
00601                             ch_free( agf ); 
00602                             continue;
00603                      }
00604 
00605                      agf->agf_scope = lud->lud_scope;
00606 
00607                      if ( lud->lud_dn == NULL ) {
00608                             BER_BVSTR( &dn, "" );
00609                      } else {
00610                             ber_str2bv( lud->lud_dn, 0, 0, &dn );
00611                      }
00612 
00613                      rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
00614                      if ( rc != LDAP_SUCCESS ) {
00615                             Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
00616                             /* FIXME: error? */
00617                             goto cleanup;
00618                      }
00619 
00620                      if ( lud->lud_filter != NULL ) {
00621                             ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
00622                             agf->agf_filter = str2filter( lud->lud_filter );
00623                      }                    
00624 
00625                      if ( lud->lud_attrs != NULL ) {
00626                             int i;
00627 
00628                             for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
00629                                    /* Just counting */;
00630                             }
00631 
00632                             if ( i > 1 ) {
00633                                    Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
00634                                           bv->bv_val, 0, 0);
00635                                    /* FIXME: error? */
00636                                    ldap_free_urldesc( lud );
00637                                    ch_free( agf ); 
00638                                    continue;
00639                             }
00640                                    
00641                             agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
00642 
00643                             if ( agf->agf_anlist == NULL ) {
00644                                    Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
00645                                           lud->lud_attrs[0], 0, 0 );         
00646                                    /* FIXME: error? */
00647                                    ldap_free_urldesc( lud );
00648                                    ch_free( agf ); 
00649                                    continue;
00650                             }
00651                      }
00652 
00653                      agf->agf_next = NULL;
00654 
00655 
00656                      if( (*agep)->age_filter == NULL ) {
00657                             (*agep)->age_filter = agf;
00658                      }
00659 
00660                      if( agf_prev != NULL ) {
00661                             agf_prev->agf_next = agf;
00662                      }
00663 
00664                      agf_prev = agf;
00665 
00666                      if ( scan == 1 ){
00667                             autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
00668                      }
00669 
00670                      Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
00671                             agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
00672 
00673                      ldap_free_urldesc( lud );
00674 
00675                      continue;
00676 
00677 
00678 cleanup:;
00679 
00680                      ldap_free_urldesc( lud );                        
00681                      ch_free( agf ); 
00682               }
00683        }
00684 
00685        if ( null_entry == 1 ) {
00686               attrs_free( a );
00687        }
00688        return rc;
00689 }
00690 
00691 /* 
00692 ** Used when opening the database to add all existing 
00693 ** groups from the database to our internal list.
00694 */
00695 static int
00696 autogroup_group_add_cb( Operation *op, SlapReply *rs )
00697 {
00698        assert( op->o_tag == LDAP_REQ_SEARCH );
00699 
00700        if ( rs->sr_type == REP_SEARCH ) {
00701               autogroup_sc_t              *ags = (autogroup_sc_t *)op->o_callback->sc_private;
00702 
00703               Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
00704                      rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
00705 
00706               autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
00707        }
00708 
00709        return 0;
00710 }
00711 
00712 
00713 /*
00714 ** When adding a group, we first strip any existing members,
00715 ** and add all which match the filters ourselfs.
00716 */
00717 static int
00718 autogroup_add_entry( Operation *op, SlapReply *rs)
00719 {
00720        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
00721        autogroup_info_t     *agi = (autogroup_info_t *)on->on_bi.bi_private;
00722        autogroup_def_t             *agd = agi->agi_def;
00723        autogroup_entry_t    *age;
00724        autogroup_filter_t   *agf;
00725        int                  rc = 0;
00726 
00727        Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
00728               op->ora_e->e_name.bv_val, 0, 0);
00729 
00730        ldap_pvt_thread_mutex_lock( &agi->agi_mutex );          
00731 
00732        /* Check if it's a group. */
00733        for ( ; agd ; agd = agd->agd_next ) {
00734               if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
00735                      Modification         mod;
00736                      const char           *text = NULL;
00737                      char                 textbuf[1024];
00738 
00739                      mod.sm_op = LDAP_MOD_DELETE;
00740                      mod.sm_desc = agd->agd_member_ad;
00741                      mod.sm_type = agd->agd_member_ad->ad_cname;
00742                      mod.sm_values = NULL;
00743                      mod.sm_nvalues = NULL;
00744 
00745                      /* We don't want any member attributes added by the user. */
00746                      modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
00747 
00748                      autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
00749                      ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
00750                      return SLAP_CB_CONTINUE;
00751               }
00752        }
00753 
00754        
00755        for ( age = agi->agi_entry; age ; age = age->age_next ) {
00756               ldap_pvt_thread_mutex_lock( &age->age_mutex );          
00757 
00758               /* Check if any of the filters are the suffix to the entry DN. 
00759                  If yes, we can test that filter against the entry. */
00760 
00761               for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
00762                      if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
00763                             rc = test_filter( op, op->ora_e, agf->agf_filter );
00764                             if ( rc == LDAP_COMPARE_TRUE ) {
00765                                    if ( agf->agf_anlist ) {
00766                                           autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
00767                                    } else {
00768                                           autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
00769                                    }
00770                                    break;
00771                             }
00772                      }
00773               }
00774               ldap_pvt_thread_mutex_unlock( &age->age_mutex );        
00775        }
00776 
00777        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
00778 
00779        return SLAP_CB_CONTINUE;
00780 }
00781 
00782 /*
00783 ** agi - internal group and attribute definitions list
00784 ** e   - the group to remove from the internal list
00785 */
00786 static int
00787 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
00788 {
00789        autogroup_entry_t    *age = agi->agi_entry,
00790                             *age_prev = NULL,
00791                             *age_next;
00792        int                  rc = 1;
00793 
00794        Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n", 
00795               age->age_dn.bv_val, 0, 0);
00796 
00797        for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
00798               age_next = age->age_next;
00799 
00800               if ( age == e ) {
00801                      autogroup_filter_t   *agf = age->age_filter,
00802                                                  *agf_next;
00803 
00804                      if ( age_prev != NULL ) {
00805                             age_prev->age_next = age_next;
00806                      } else {
00807                             agi->agi_entry = NULL;
00808                      }
00809 
00810                      ch_free( age->age_dn.bv_val );
00811                      ch_free( age->age_ndn.bv_val );
00812 
00813                      for( agf_next = agf ; agf_next ; agf = agf_next ){
00814                             agf_next = agf->agf_next;
00815 
00816                             filter_free( agf->agf_filter );
00817                             ch_free( agf->agf_filterstr.bv_val );
00818                             ch_free( agf->agf_dn.bv_val );
00819                             ch_free( agf->agf_ndn.bv_val );
00820                             anlist_free( agf->agf_anlist, 1, NULL );
00821                             ch_free( agf );
00822                      }
00823 
00824                      ldap_pvt_thread_mutex_unlock( &age->age_mutex );        
00825                      ldap_pvt_thread_mutex_destroy( &age->age_mutex );
00826                      ch_free( age );
00827 
00828                      rc = 0;       
00829                      return rc;
00830 
00831               }
00832        }
00833 
00834        Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
00835 
00836        return rc;
00837 
00838 }
00839 
00840 static int
00841 autogroup_delete_entry( Operation *op, SlapReply *rs)
00842 {
00843        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
00844        autogroup_info_t     *agi = (autogroup_info_t *)on->on_bi.bi_private;
00845        autogroup_entry_t    *age, *age_prev, *age_next;
00846        autogroup_filter_t   *agf;
00847        Entry                *e;
00848        int                  matched_group = 0, rc = 0;
00849 
00850        Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
00851 
00852        ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
00853 
00854        if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
00855               LDAP_SUCCESS || e == NULL ) {
00856               Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
00857               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );               
00858               return SLAP_CB_CONTINUE;
00859        }
00860 
00861        /* Check if the entry to be deleted is one of our groups. */
00862        for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
00863               age = age_next;
00864               ldap_pvt_thread_mutex_lock( &age->age_mutex );
00865               age_next = age->age_next;
00866 
00867               if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
00868                      int match = 1;
00869 
00870                      matched_group = 1;
00871 
00872                      dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
00873 
00874                      if ( match == 0 ) {
00875                             autogroup_delete_group( agi, age );
00876                             break;
00877                      }
00878               }
00879 
00880               ldap_pvt_thread_mutex_unlock( &age->age_mutex );               
00881        }
00882 
00883        if ( matched_group == 1 ) {
00884               overlay_entry_release_ov( op, e, 0, on );
00885               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
00886               return SLAP_CB_CONTINUE;
00887        }
00888 
00889        /* Check if the entry matches any of the groups.
00890           If yes, we can delete the entry from that group. */
00891 
00892        for ( age = agi->agi_entry ; age ; age = age->age_next ) {
00893               ldap_pvt_thread_mutex_lock( &age->age_mutex );          
00894 
00895               for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
00896                      if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
00897                             rc = test_filter( op, e, agf->agf_filter );
00898                             if ( rc == LDAP_COMPARE_TRUE ) {
00899                                    /* If the attribute is retrieved from the entry, we don't know what to delete
00900                                    ** So the group must be entirely refreshed
00901                                    ** But the refresh can't be done now because the entry is not deleted
00902                                    ** So the group is marked as mustrefresh
00903                                    */
00904                                    if ( agf->agf_anlist ) {
00905                                           age->age_mustrefresh = 1;
00906                                    } else {
00907                                           autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
00908                                    }
00909                                    break;
00910                             }
00911                      }
00912               }
00913               ldap_pvt_thread_mutex_unlock( &age->age_mutex );
00914        }
00915 
00916        overlay_entry_release_ov( op, e, 0, on );
00917        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
00918 
00919        return SLAP_CB_CONTINUE;
00920 }
00921 
00922 static int
00923 autogroup_response( Operation *op, SlapReply *rs )
00924 {
00925        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
00926        autogroup_info_t     *agi = (autogroup_info_t *)on->on_bi.bi_private;
00927        autogroup_def_t             *agd = agi->agi_def;
00928        autogroup_entry_t    *age;
00929        autogroup_filter_t   *agf;
00930        BerValue             new_dn, new_ndn, pdn;
00931        Entry                *e, *group;
00932        Attribute            *a, *ea;
00933        int                  is_olddn, is_newdn, is_value_refresh, dn_equal;
00934 
00935        /* Handle all cases where a refresh of the group is needed */
00936        if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
00937               if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
00938 
00939                      ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
00940 
00941                      for ( age = agi->agi_entry ; age ; age = age->age_next ) {
00942                             /* Request detected that the group must be refreshed */
00943 
00944                             ldap_pvt_thread_mutex_lock( &age->age_mutex );
00945 
00946                             if ( age->age_mustrefresh ) {
00947                                    autogroup_delete_member_from_group( op, NULL, NULL, age) ;
00948 
00949                                    for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
00950                                           autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
00951                                    }
00952                             }
00953 
00954                             ldap_pvt_thread_mutex_unlock( &age->age_mutex );
00955                      }
00956 
00957                      ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
00958               }
00959        } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
00960               if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
00961 
00962                      Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
00963 
00964                      ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                 
00965 
00966                      if ( op->oq_modrdn.rs_newSup ) {
00967                             pdn = *op->oq_modrdn.rs_newSup;
00968                      } else {
00969                             dnParent( &op->o_req_dn, &pdn );
00970                      }
00971                      build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
00972 
00973                      if ( op->oq_modrdn.rs_nnewSup ) {
00974                             pdn = *op->oq_modrdn.rs_nnewSup;
00975                      } else {
00976                             dnParent( &op->o_req_ndn, &pdn );
00977                      }
00978                      build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
00979 
00980                      Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
00981 
00982                      dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
00983 
00984                      if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
00985                             LDAP_SUCCESS || e == NULL ) {
00986                             Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
00987                             ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
00988                             return SLAP_CB_CONTINUE;
00989                      }
00990 
00991                      a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
00992 
00993 
00994                      if ( a == NULL ) {
00995                             Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
00996                             overlay_entry_release_ov( op, e, 0, on );
00997                             ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
00998                             return SLAP_CB_CONTINUE;
00999                      }
01000 
01001 
01002                      /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
01003                      for ( ; agd; agd = agd->agd_next ) {
01004 
01005                             if ( value_find_ex( slap_schema.si_ad_objectClass,
01006                                           SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
01007                                           SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
01008                                           a->a_nvals, &agd->agd_oc->soc_cname,
01009                                           op->o_tmpmemctx ) == 0 )
01010                             {             
01011                                    for ( age = agi->agi_entry ; age ; age = age->age_next ) {
01012                                           int match = 1;
01013 
01014                                           dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
01015                                           if ( match == 0 ) {
01016                                                  Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
01017                                                  ber_dupbv( &age->age_dn, &new_dn );
01018                                                  ber_dupbv( &age->age_ndn, &new_ndn );
01019 
01020                                                  op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
01021                                                  op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
01022                                                  overlay_entry_release_ov( op, e, 0, on );
01023                                                  ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
01024                                                  return SLAP_CB_CONTINUE;
01025                                           }
01026                                    }
01027 
01028                             }
01029                      }
01030 
01031                      /* For each group: 
01032                         1. check if the orginal entry's DN is in the group.
01033                         2. chceck if the any of the group filter's base DN is a suffix of the new DN 
01034 
01035                         If 1 and 2 are both false, we do nothing.
01036                         If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
01037                         If 1 is false, and 2 is true, we check the entry against the group's filters,
01038                             and add it's DN to the group.
01039                         If 1 is true, and 2 is false, we delete the entry's DN from the group.
01040                      */
01041                      for ( age = agi->agi_entry ; age ; age = age->age_next ) {
01042                             is_olddn = 0;
01043                             is_newdn = 0;
01044                             is_value_refresh = 0;
01045 
01046 
01047                             ldap_pvt_thread_mutex_lock( &age->age_mutex );
01048 
01049                             if ( age->age_filter && age->age_filter->agf_anlist ) {
01050                                    ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
01051                             }
01052                             else {
01053                                    ea = NULL;
01054                             }
01055 
01056                             if ( age->age_modrdn_olddnmodified ) {
01057                                    /* Resquest already marked this group to be updated */
01058                                    is_olddn = 1;
01059                                    is_value_refresh = 1;
01060                                    age->age_modrdn_olddnmodified = 0;
01061                             } else {
01062 
01063                                    if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
01064                                           LDAP_SUCCESS || group == NULL ) {
01065                                           Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
01066 
01067                                           op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
01068                                           op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
01069 
01070                                           overlay_entry_release_ov( op, e, 0, on );
01071                                           ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01072                                           ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01073                                           return SLAP_CB_CONTINUE;
01074                                    }
01075 
01076                                    a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
01077 
01078                                    if ( a != NULL ) {
01079                                           if ( value_find_ex( age->age_def->agd_member_ad,
01080                                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
01081                                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
01082                                                         a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
01083                                           {
01084                                                  is_olddn = 1;
01085                                           }
01086 
01087                                    }
01088 
01089                                    overlay_entry_release_ov( op, group, 0, on );
01090 
01091                             }
01092 
01093                             for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01094                                    if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
01095                                           /* TODO: should retest filter as it could imply conditions on the dn */
01096                                           is_newdn = 1;
01097                                           break;
01098                                    }
01099                             }
01100 
01101 
01102                             if ( is_value_refresh ) {
01103                                    if ( is_olddn != is_newdn ) {
01104                                           /* group refresh */
01105                                           autogroup_delete_member_from_group( op, NULL, NULL, age) ;
01106 
01107                                           for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01108                                                  autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
01109                                           }
01110                                    }
01111                                    ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01112                                    continue;
01113                             }
01114                             if ( is_olddn == 1 && is_newdn == 0 ) {
01115                                    if ( ea )
01116                                           autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
01117                                    else
01118                                           autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
01119                             } else
01120                             if ( is_olddn == 0 && is_newdn == 1 ) {
01121                                    for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
01122                                           if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
01123                                                  if ( ea )
01124                                                         autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
01125                                                  else
01126                                                         autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
01127                                                  break;
01128                                           }
01129                                    }
01130                             } else
01131                             if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
01132                                    if ( ea ) {
01133                                           /* group refresh */
01134                                           autogroup_delete_member_from_group( op, NULL, NULL, age) ;
01135 
01136                                           for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01137                                                  autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
01138                                           }
01139                                    }
01140                                    else {
01141                                           autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
01142                                           autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
01143                                    }
01144                             }
01145 
01146                             ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01147                      }
01148 
01149                      op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
01150                      op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
01151 
01152                      overlay_entry_release_ov( op, e, 0, on );
01153 
01154                      ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );               
01155               }
01156        }
01157 
01158        if ( op->o_tag == LDAP_REQ_MODIFY ) {
01159               if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
01160                      Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
01161 
01162                      ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                 
01163 
01164                      if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
01165                             LDAP_SUCCESS || e == NULL ) {
01166                             Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
01167                             ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01168                             return SLAP_CB_CONTINUE;
01169                      }
01170 
01171                      a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
01172 
01173 
01174                      if ( a == NULL ) {
01175                             Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
01176                             overlay_entry_release_ov( op, e, 0, on );
01177                             ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
01178                             return SLAP_CB_CONTINUE;
01179                      }
01180 
01181                      /* If we modify a group's memberURL, we have to delete all of it's members,
01182                         and add them anew, because we cannot tell from which memberURL a member was added. */
01183                      for ( ; agd; agd = agd->agd_next ) {
01184 
01185                             if ( value_find_ex( slap_schema.si_ad_objectClass,
01186                                           SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
01187                                           SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
01188                                           a->a_nvals, &agd->agd_oc->soc_cname,
01189                                           op->o_tmpmemctx ) == 0 )
01190                             {
01191                                    Modifications *m;
01192                                    int           match = 1;
01193 
01194                                    m = op->orm_modlist;
01195 
01196                                    for ( age = agi->agi_entry ; age ; age = age->age_next ) {
01197                                           ldap_pvt_thread_mutex_lock( &age->age_mutex );
01198 
01199                                           dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
01200 
01201                                           if ( match == 0 ) {
01202                                                  for ( ; m ; m = m->sml_next ) {
01203                                                         if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
01204                                                                autogroup_def_t      *group_agd = age->age_def;
01205                                                                Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n", 
01206                                                                       op->o_req_dn.bv_val, 0, 0);
01207 
01208                                                                overlay_entry_release_ov( op, e, 0, on );
01209 
01210                                                                autogroup_delete_member_from_group( op, NULL, NULL, age );
01211                                                                autogroup_delete_group( agi, age );
01212 
01213                                                                autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
01214 
01215                                                                overlay_entry_release_ov( op, e, 0, on );
01216                                                                ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01217                                                                return SLAP_CB_CONTINUE;
01218                                                         }
01219                                                  }
01220 
01221                                                  ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01222                                                  break;
01223                                           }
01224 
01225                                           ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01226                                    }
01227 
01228                                    overlay_entry_release_ov( op, e, 0, on );
01229                                    ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01230                                    return SLAP_CB_CONTINUE;
01231                             }
01232                      }
01233 
01234                      /* When modifing any of the attributes of an entry, we must
01235                         check if the entry is in any of our groups, and if
01236                         the modified entry maches any of the filters of that group.
01237 
01238                         If the entry exists in a group, but the modified attributes do
01239                             not match any of the group's filters, we delete the entry from that group.
01240                         If the entry doesn't exist in a group, but matches a filter, 
01241                             we add it to that group.
01242                      */
01243                      for ( age = agi->agi_entry ; age ; age = age->age_next ) {
01244                             is_olddn = 0;
01245                             is_newdn = 0;
01246 
01247 
01248                             ldap_pvt_thread_mutex_lock( &age->age_mutex );
01249 
01250                             if ( age->age_filter && age->age_filter->agf_anlist ) {
01251                                    ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
01252                             }
01253                             else {
01254                                    ea = NULL;
01255                             }
01256 
01257                             if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
01258                                    LDAP_SUCCESS || group == NULL ) {
01259                                    Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", 
01260                                           age->age_dn.bv_val, 0, 0);
01261 
01262                                    overlay_entry_release_ov( op, e, 0, on );
01263                                    ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01264                                    ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01265                                    return SLAP_CB_CONTINUE;
01266                             }
01267 
01268                             a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
01269 
01270                             if ( a != NULL ) {
01271                                    if ( value_find_ex( age->age_def->agd_member_ad,
01272                                                  SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
01273                                                  SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
01274                                                  a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
01275                                    {
01276                                           is_olddn = 1;
01277                                    }
01278 
01279                             }
01280 
01281                             overlay_entry_release_ov( op, group, 0, on );
01282 
01283                             for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01284                                    if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
01285                                           if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
01286                                                  is_newdn = 1;
01287                                                  break;
01288                                           }
01289                                    }
01290                             }
01291 
01292                             if ( is_olddn == 1 && is_newdn == 0 ) {
01293                                    if(ea)
01294                                           autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
01295                                    else
01296                                           autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
01297                             } else
01298                             if ( is_olddn == 0 && is_newdn == 1 ) {
01299                                    if(ea)
01300                                           autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
01301                                    else
01302                                           autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
01303                             } 
01304 
01305                             ldap_pvt_thread_mutex_unlock( &age->age_mutex );
01306                      }
01307 
01308                      overlay_entry_release_ov( op, e, 0, on );
01309 
01310                      ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01311               }
01312        }
01313 
01314        return SLAP_CB_CONTINUE;
01315 }
01316 
01317 /*
01318 ** Detect if filter contains a memberOf check for dn
01319 */
01320 static int
01321 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
01322 {
01323        int result = 0;
01324        if ( f == NULL ) return 0;
01325 
01326        switch ( f->f_choice & SLAPD_FILTER_MASK ) {
01327               case LDAP_FILTER_AND:
01328               case LDAP_FILTER_OR:
01329               case LDAP_FILTER_NOT:
01330                      for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
01331                             result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
01332                      }
01333                      break;
01334               case LDAP_FILTER_EQUALITY:
01335                      result = ( f->f_ava->aa_desc == memberof_ad &&
01336                                 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
01337                      break;
01338               default:
01339                      break;
01340        }
01341 
01342        return result;
01343 }
01344 
01345 /*
01346 ** When modifing a group, we must deny any modifications to the member attribute,
01347 ** because the group would be inconsistent.
01348 */
01349 static int
01350 autogroup_modify_entry( Operation *op, SlapReply *rs)
01351 {
01352        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
01353        autogroup_info_t            *agi = (autogroup_info_t *)on->on_bi.bi_private;
01354        autogroup_def_t             *agd = agi->agi_def;
01355        autogroup_entry_t    *age;
01356        Entry                *e;
01357        Attribute            *a;
01358 
01359        if ( get_manageDSAit( op ) ) {
01360               return SLAP_CB_CONTINUE;
01361        }
01362 
01363        Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
01364        ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                 
01365 
01366        if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
01367               LDAP_SUCCESS || e == NULL ) {
01368               Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
01369               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01370               return SLAP_CB_CONTINUE;
01371        }
01372 
01373        /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
01374        for ( age = agi->agi_entry; age ; age = age->age_next ) {
01375               autogroup_filter_t   *agf;
01376               for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01377                      if ( agf->agf_anlist ) {
01378                             Modifications *m;
01379                             for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
01380                                    if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
01381                                           if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
01382                                                  int rc = test_filter( op, e, agf->agf_filter );
01383                                                  if ( rc == LDAP_COMPARE_TRUE ) {
01384                                                         age->age_mustrefresh = 1;
01385                                                  }
01386                                           }
01387                                    }
01388                             }
01389                      }
01390 
01391                      if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
01392                             age->age_mustrefresh = 1;
01393                      }
01394               }
01395        }
01396 
01397        a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
01398 
01399        if ( a == NULL ) {
01400               Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
01401               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );        
01402               return SLAP_CB_CONTINUE;
01403        }
01404 
01405 
01406        for ( ; agd; agd = agd->agd_next ) {
01407 
01408               if ( value_find_ex( slap_schema.si_ad_objectClass,
01409                             SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
01410                             SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
01411                             a->a_nvals, &agd->agd_oc->soc_cname,
01412                             op->o_tmpmemctx ) == 0 )
01413               {
01414                      Modifications *m;
01415                      int           match = 1;
01416 
01417                      m = op->orm_modlist;
01418 
01419                      for ( age = agi->agi_entry ; age ; age = age->age_next ) {
01420                             dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
01421 
01422                             if ( match == 0 ) {
01423                                    for ( ; m ; m = m->sml_next ) {
01424                                           if ( m->sml_desc == age->age_def->agd_member_ad ) {
01425                                                  overlay_entry_release_ov( op, e, 0, on );
01426                                                  ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01427                                                  Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
01428                                                  send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
01429                                                  return LDAP_CONSTRAINT_VIOLATION;
01430                                           }
01431                                    }
01432                                    break;
01433                             }
01434                      }
01435 
01436                      overlay_entry_release_ov( op, e, 0, on );
01437                      ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01438                      return SLAP_CB_CONTINUE;
01439               }
01440        }
01441 
01442        overlay_entry_release_ov( op, e, 0, on );
01443        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );               
01444        return SLAP_CB_CONTINUE;
01445 }
01446 
01447 /*
01448 ** Detect if the olddn is part of a group and so if the group should be refreshed
01449 */
01450 static int
01451 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
01452 {
01453        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
01454        autogroup_info_t     *agi = (autogroup_info_t *)on->on_bi.bi_private;
01455        autogroup_entry_t    *age;
01456        Entry                *e;
01457 
01458        if ( get_manageDSAit( op ) ) {
01459               return SLAP_CB_CONTINUE;
01460        }
01461 
01462        Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
01463        ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                 
01464 
01465        if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
01466               LDAP_SUCCESS || e == NULL ) {
01467               Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
01468               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
01469               return SLAP_CB_CONTINUE;
01470        }
01471 
01472        /* Must check if a dn is modified */
01473        for ( age = agi->agi_entry; age ; age = age->age_next ) {
01474               autogroup_filter_t   *agf;
01475               for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
01476                      if ( agf->agf_anlist ) {
01477                             if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
01478                                    int rc = test_filter( op, e, agf->agf_filter );
01479                                    if ( rc == LDAP_COMPARE_TRUE ) {
01480                                           age->age_modrdn_olddnmodified = 1;
01481                                    }
01482                             }
01483                      }
01484               }
01485        }
01486 
01487        overlay_entry_release_ov( op, e, 0, on );
01488        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );               
01489        return SLAP_CB_CONTINUE;
01490 }
01491 
01492 /* 
01493 ** Builds a filter for searching for the 
01494 ** group entries, according to the objectClass. 
01495 */
01496 static int
01497 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
01498 {
01499        char   *ptr;
01500 
01501        Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
01502 
01503        op->ors_filterstr.bv_len = STRLENOF( "(=)" ) 
01504                      + slap_schema.si_ad_objectClass->ad_cname.bv_len
01505                      + agd->agd_oc->soc_cname.bv_len;
01506        ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
01507        *ptr++ = '(';
01508        ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
01509        *ptr++ = '=';
01510        ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
01511        *ptr++ = ')';
01512        *ptr = '\0';
01513 
01514        op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
01515 
01516        assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
01517 
01518        return 0;
01519 }
01520 
01521 enum {
01522        AG_ATTRSET = 1,
01523        AG_MEMBER_OF_AD,
01524        AG_LAST
01525 };
01526 
01527 static ConfigDriver  ag_cfgen;
01528 
01529 static ConfigTable agcfg[] = {
01530        { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
01531               3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
01532               "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
01533                      "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
01534                      "EQUALITY caseIgnoreMatch "
01535                      "SYNTAX OMsDirectoryString "
01536                      "X-ORDERED 'VALUES' )",
01537                      NULL, NULL },
01538        
01539        { "autogroup-memberof-ad", "memberOf attribute",
01540               2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
01541               "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
01542                      "DESC 'memberOf attribute' "
01543                      "SYNTAX OMsDirectoryString SINGLE-VALUE )",
01544                      NULL, NULL },
01545 
01546        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
01547 };
01548 
01549 static ConfigOCs agocs[] = {
01550        { "( OLcfgCtOc:2.1 "
01551               "NAME 'olcAutomaticGroups' "
01552               "DESC 'Automatic groups configuration' "
01553               "SUP olcOverlayConfig "
01554               "MAY ( "
01555                      "olcAGattrSet "
01556                      "$ olcAGmemberOfAd "
01557                   ")"
01558          ")",
01559               Cft_Overlay, agcfg, NULL, NULL },
01560        { NULL, 0, NULL }
01561 };
01562 
01563 
01564 static int
01565 ag_cfgen( ConfigArgs *c )
01566 {
01567        slap_overinst        *on = (slap_overinst *)c->bi;
01568        autogroup_info_t            *agi = (autogroup_info_t *)on->on_bi.bi_private;
01569        autogroup_def_t             *agd;
01570        autogroup_entry_t    *age;
01571 
01572        int rc = 0, i;
01573 
01574        Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
01575 
01576        if( agi == NULL ) {
01577               agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
01578               ldap_pvt_thread_mutex_init( &agi->agi_mutex );
01579               agi->agi_def = NULL;
01580               agi->agi_entry = NULL;
01581               on->on_bi.bi_private = (void *)agi;
01582        }
01583 
01584        agd = agi->agi_def;
01585        age = agi->agi_entry;
01586 
01587        if ( c->op == SLAP_CONFIG_EMIT ) {
01588 
01589               switch( c->type ){
01590               case AG_ATTRSET:
01591                      for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
01592                             struct berval bv;
01593                             char          *ptr = c->cr_msg;
01594        
01595                             assert(agd->agd_oc != NULL);
01596                             assert(agd->agd_member_url_ad != NULL);
01597                             assert(agd->agd_member_ad != NULL);
01598        
01599                             ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
01600                                    SLAP_X_ORDERED_FMT "%s %s %s", i,
01601                                    agd->agd_oc->soc_cname.bv_val,
01602                                    agd->agd_member_url_ad->ad_cname.bv_val,
01603                                    agd->agd_member_ad->ad_cname.bv_val );
01604        
01605                             bv.bv_val = c->cr_msg;
01606                             bv.bv_len = ptr - bv.bv_val;
01607                             value_add_one ( &c->rvalue_vals, &bv );
01608        
01609                      }
01610                      break;
01611 
01612               case AG_MEMBER_OF_AD:
01613                      if ( agi->agi_memberof_ad != NULL ){
01614                             value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
01615                      }
01616                      break;
01617 
01618               default:
01619                      assert( 0 );
01620                      return 1;
01621       }
01622 
01623               return rc;
01624 
01625        }else if ( c->op == LDAP_MOD_DELETE ) {
01626               if ( c->valx < 0) {
01627                      autogroup_def_t             *agd_next;
01628                      autogroup_entry_t    *age_next;
01629                      autogroup_filter_t   *agf = age->age_filter,
01630                                           *agf_next;
01631 
01632                      for ( agd_next = agd; agd_next; agd = agd_next ) {
01633                             agd_next = agd->agd_next;
01634 
01635                             ch_free( agd );
01636                      }
01637 
01638                      for ( age_next = age ; age_next ; age = age_next ) {
01639                             age_next = age->age_next;
01640 
01641                             ch_free( age->age_dn.bv_val );
01642                             ch_free( age->age_ndn.bv_val );
01643 
01644                             for( agf_next = agf ; agf_next ; agf = agf_next ){
01645                                    agf_next = agf->agf_next;
01646 
01647                                    filter_free( agf->agf_filter );
01648                                    ch_free( agf->agf_filterstr.bv_val );
01649                                    ch_free( agf->agf_dn.bv_val );
01650                                    ch_free( agf->agf_ndn.bv_val );
01651                                    anlist_free( agf->agf_anlist, 1, NULL );
01652                                    ch_free( agf );
01653                             }
01654 
01655                             ldap_pvt_thread_mutex_init( &age->age_mutex );
01656                             ch_free( age );
01657                      }
01658 
01659                      ch_free( agi );
01660                      on->on_bi.bi_private = NULL;
01661 
01662               } else {
01663                      autogroup_def_t             **agdp;
01664                      autogroup_entry_t    *age_next, *age_prev;
01665                      autogroup_filter_t   *agf,
01666                                           *agf_next;
01667 
01668                      for ( i = 0, agdp = &agi->agi_def;
01669                             i < c->valx; i++ ) 
01670                      {
01671                             if ( *agdp == NULL) {
01672                                    return 1;
01673                             }
01674                             agdp = &(*agdp)->agd_next;
01675                      }
01676 
01677                      agd = *agdp;
01678                      *agdp = agd->agd_next;
01679 
01680                      for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
01681                             age_next = age->age_next;
01682 
01683                             if( age->age_def == agd ) {
01684                                    agf = age->age_filter;
01685 
01686                                    ch_free( age->age_dn.bv_val );
01687                                    ch_free( age->age_ndn.bv_val );
01688 
01689                                    for ( agf_next = agf; agf_next ; agf = agf_next ) {
01690                                           agf_next = agf->agf_next;
01691                                           filter_free( agf->agf_filter );
01692                                           ch_free( agf->agf_filterstr.bv_val );
01693                                           ch_free( agf->agf_dn.bv_val );
01694                                           ch_free( agf->agf_ndn.bv_val );
01695                                           anlist_free( agf->agf_anlist, 1, NULL );
01696                                           ch_free( agf );
01697                                    }
01698 
01699                                    ldap_pvt_thread_mutex_destroy( &age->age_mutex );
01700                                    ch_free( age );
01701 
01702                                    age = age_prev;
01703 
01704                                    if( age_prev != NULL ) {
01705                                           age_prev->age_next = age_next;
01706                                    }
01707                             }
01708                      }
01709 
01710                      ch_free( agd );
01711                      agd = agi->agi_def;
01712 
01713               }
01714 
01715               return rc;
01716        }
01717 
01718        switch(c->type){
01719        case AG_ATTRSET: {
01720               autogroup_def_t             **agdp,
01721                                    *agd_next = NULL;
01722               ObjectClass          *oc = NULL;
01723               AttributeDescription *member_url_ad = NULL,
01724                                    *member_ad = NULL;
01725               const char           *text;
01726 
01727 
01728               oc = oc_find( c->argv[ 1 ] );
01729               if( oc == NULL ){
01730                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01731                             "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01732                             "unable to find ObjectClass \"%s\"",
01733                             c->argv[ 1 ] );
01734                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01735                             c->log, c->cr_msg, 0 );
01736                      return 1;
01737               }
01738 
01739 
01740               rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
01741               if( rc != LDAP_SUCCESS ) {
01742                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01743                             "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01744                             "unable to find AttributeDescription \"%s\"",
01745                             c->argv[ 2 ] );
01746                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01747                             c->log, c->cr_msg, 0 );            
01748                      return 1;
01749               }
01750 
01751               if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
01752                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01753                             "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01754                             "AttributeDescription \"%s\" ",
01755                             "must be of a subtype \"labeledURI\"",
01756                             c->argv[ 2 ] );
01757                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01758                             c->log, c->cr_msg, 0 );
01759                      return 1;
01760               }
01761 
01762               rc = slap_str2ad( c->argv[3], &member_ad, &text );
01763               if( rc != LDAP_SUCCESS ) {
01764                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01765                             "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01766                             "unable to find AttributeDescription \"%s\"",
01767                             c->argv[ 3 ] );
01768                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01769                             c->log, c->cr_msg, 0 );
01770                      return 1;
01771               }
01772 
01773               for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
01774                      /* The same URL attribute / member attribute pair
01775                      * cannot be repeated */
01776 
01777                      if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
01778                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
01779                                    "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01780                                    "URL attributeDescription \"%s\" already mapped",
01781                                    member_ad->ad_cname.bv_val );
01782                             Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01783                                    c->log, c->cr_msg, 0 );
01784 /*                          return 1; //warning*/
01785                      }
01786               }
01787 
01788               if ( c->valx > 0 ) {
01789                      int    i;
01790 
01791                      for ( i = 0, agdp = &agi->agi_def ;
01792                             i < c->valx; i++ )
01793                      {
01794                             if ( *agdp == NULL ) {
01795                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
01796                                           "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
01797                                           "invalid index {%d}",
01798                                           c->valx );
01799                                    Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01800                                           c->log, c->cr_msg, 0 );
01801 
01802                                    return 1;
01803                             }
01804                             agdp = &(*agdp)->agd_next;
01805                      }
01806                      agd_next = *agdp;
01807 
01808               } else {
01809                      for ( agdp = &agi->agi_def; *agdp;
01810                             agdp = &(*agdp)->agd_next )
01811                             /* goto last */;
01812               }
01813 
01814               *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
01815 
01816               (*agdp)->agd_oc = oc;
01817               (*agdp)->agd_member_url_ad = member_url_ad;
01818               (*agdp)->agd_member_ad = member_ad;
01819               (*agdp)->agd_next = agd_next;
01820 
01821               } break;
01822        
01823        case AG_MEMBER_OF_AD: {
01824               AttributeDescription *memberof_ad = NULL;
01825               const char     *text;
01826 
01827               rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
01828               if( rc != LDAP_SUCCESS ) {
01829                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01830                             "\"autogroup-memberof-ad <memberof-ad>\": "
01831                             "unable to find AttributeDescription \"%s\"",
01832                             c->argv[ 1 ] );
01833                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01834                             c->log, c->cr_msg, 0 );
01835                      return 1;
01836               }
01837 
01838               if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
01839                    && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
01840               {
01841                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01842                             "memberof attribute=\"%s\" must either "
01843                             "have DN (%s) or nameUID (%s) syntax",
01844                             c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
01845                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01846                             c->log, c->cr_msg, 0 );
01847                      return 1;
01848               }
01849 
01850               agi->agi_memberof_ad = memberof_ad;
01851 
01852               } break;
01853 
01854        default:
01855               rc = 1;
01856               break;
01857        }
01858 
01859        return rc;
01860 }
01861 
01862 extern int slapMode;
01863 
01864 /* 
01865 ** Do a search for all the groups in the
01866 ** database, and add them to out internal list.
01867 */
01868 static int
01869 autogroup_db_open(
01870        BackendDB     *be,
01871        ConfigReply   *cr )
01872 {
01873        slap_overinst               *on = (slap_overinst *) be->bd_info;
01874        autogroup_info_t            *agi = on->on_bi.bi_private;
01875        autogroup_def_t             *agd;
01876        autogroup_sc_t              ags;
01877        Operation            *op;
01878        slap_callback        cb = { 0 };
01879 
01880        void                        *thrctx = ldap_pvt_thread_pool_context();
01881        Connection                  conn = { 0 };
01882        OperationBuffer      opbuf;
01883 
01884        Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
01885 
01886        if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
01887               return 0;
01888        }
01889 
01890        connection_fake_init( &conn, &opbuf, thrctx );
01891        op = &opbuf.ob_op;
01892 
01893        op->ors_attrsonly = 0;
01894        op->o_tag = LDAP_REQ_SEARCH;
01895        op->o_dn = be->be_rootdn;
01896        op->o_ndn = be->be_rootndn;
01897 
01898        op->o_req_dn = be->be_suffix[0];
01899        op->o_req_ndn = be->be_nsuffix[0];
01900 
01901        op->ors_scope = LDAP_SCOPE_SUBTREE;
01902        op->ors_deref = LDAP_DEREF_NEVER;
01903        op->ors_limit = NULL;
01904        op->ors_tlimit = SLAP_NO_LIMIT;
01905        op->ors_slimit = SLAP_NO_LIMIT;
01906        op->ors_attrs =  slap_anlist_no_attrs;
01907 
01908        op->o_bd = be;
01909        op->o_bd->bd_info = (BackendInfo *)on->on_info;
01910 
01911        ags.ags_info = agi;
01912        cb.sc_private = &ags;
01913        cb.sc_response = autogroup_group_add_cb;
01914        cb.sc_cleanup = NULL;
01915        cb.sc_next = NULL;
01916 
01917        op->o_callback = &cb;
01918 
01919        for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
01920               SlapReply     rs = { REP_RESULT };
01921 
01922               autogroup_build_def_filter(agd, op);
01923 
01924               ags.ags_def = agd;
01925 
01926               op->o_bd->be_search( op, &rs );
01927 
01928               filter_free_x( op, op->ors_filter, 1 );
01929               op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
01930        }             
01931 
01932        if( ! agi->agi_memberof_ad ){
01933               int                  rc;
01934               const char           *text = NULL;
01935               
01936               rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
01937               if ( rc != LDAP_SUCCESS ) {
01938                      Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
01939                      "unable to find attribute=\"%s\": %s (%d)\n",
01940                      SLAPD_MEMBEROF_ATTR, text, rc );
01941                      return rc;
01942               }
01943        }
01944 
01945        return 0;
01946 }
01947 
01948 static int
01949 autogroup_db_close(
01950        BackendDB     *be,
01951        ConfigReply   *cr )
01952 {
01953        slap_overinst               *on = (slap_overinst *) be->bd_info;
01954 
01955        Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
01956 
01957        if ( on->on_bi.bi_private ) {
01958               autogroup_info_t            *agi = on->on_bi.bi_private;
01959               autogroup_entry_t    *age = agi->agi_entry,
01960                                    *age_next;
01961               autogroup_filter_t   *agf, *agf_next;
01962 
01963               for ( age_next = age; age_next; age = age_next ) {
01964                      age_next = age->age_next;
01965 
01966                      ch_free( age->age_dn.bv_val );
01967                      ch_free( age->age_ndn.bv_val );
01968 
01969                      agf = age->age_filter;
01970 
01971                      for ( agf_next = agf; agf_next; agf = agf_next ) {
01972                             agf_next = agf->agf_next;
01973 
01974                             filter_free( agf->agf_filter );
01975                             ch_free( agf->agf_filterstr.bv_val );
01976                             ch_free( agf->agf_dn.bv_val );
01977                             ch_free( agf->agf_ndn.bv_val );    
01978                             anlist_free( agf->agf_anlist, 1, NULL );
01979                             ch_free( agf );
01980                      }
01981 
01982                      ldap_pvt_thread_mutex_destroy( &age->age_mutex );
01983                      ch_free( age );
01984               }
01985        }
01986 
01987        return 0;
01988 }
01989 
01990 static int
01991 autogroup_db_destroy(
01992        BackendDB     *be,
01993        ConfigReply   *cr )
01994 {
01995        slap_overinst               *on = (slap_overinst *) be->bd_info;
01996 
01997        Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
01998 
01999        if ( on->on_bi.bi_private ) {
02000               autogroup_info_t            *agi = on->on_bi.bi_private;
02001               autogroup_def_t             *agd = agi->agi_def,
02002                                    *agd_next;
02003 
02004               for ( agd_next = agd; agd_next; agd = agd_next ) {
02005                      agd_next = agd->agd_next;
02006 
02007                      ch_free( agd );
02008               }
02009 
02010               ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
02011               ch_free( agi );
02012        }
02013 
02014        return 0;
02015 }
02016 
02017 static slap_overinst autogroup = { { NULL } };
02018 
02019 static
02020 int
02021 autogroup_initialize(void)
02022 {
02023        int           rc = 0;
02024        autogroup.on_bi.bi_type = "autogroup";
02025 
02026        autogroup.on_bi.bi_db_open = autogroup_db_open;
02027        autogroup.on_bi.bi_db_close = autogroup_db_close;
02028        autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
02029 
02030        autogroup.on_bi.bi_op_add = autogroup_add_entry;
02031        autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
02032        autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
02033        autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
02034 
02035        autogroup.on_response = autogroup_response;
02036 
02037        autogroup.on_bi.bi_cf_ocs = agocs;
02038 
02039        rc = config_register_schema( agcfg, agocs );
02040        if ( rc ) {
02041               return rc;
02042        }
02043 
02044        return overlay_register( &autogroup );
02045 }
02046 
02047 int
02048 init_module( int argc, char *argv[] )
02049 {
02050        return autogroup_initialize();
02051 }