]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/collect.c
ITS#5339
[openldap] / servers / slapd / overlays / collect.c
1 /* collect.c - Demonstration of overlay code */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2008 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_COLLECT
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/socket.h>
30
31 #include "slap.h"
32 #include "config.h"
33
34 /* This is a cheap hack to implement a collective attribute.
35  *
36  * This demonstration overlay looks for a specified attribute in an
37  * ancestor of a given entry and adds that attribute to the given
38  * entry when it is returned in a search response. It takes no effect
39  * for any other operations. If the ancestor does not exist, there
40  * is no effect. If no attribute was configured, there is no effect.
41  */
42
43 typedef struct collect_info {
44         struct collect_info *ci_next;
45         struct berval ci_dn;
46         AttributeDescription *ci_ad;
47 } collect_info;
48
49 static int
50 collect_cf( ConfigArgs *c )
51 {
52         slap_overinst *on = (slap_overinst *)c->bi;
53         int rc = 1;
54
55         switch( c->op ) {
56         case SLAP_CONFIG_EMIT:
57                 {
58                 collect_info *ci;
59                 for ( ci = on->on_bi.bi_private; ci; ci = ci->ci_next ) {
60                         struct berval bv;
61                         int len;
62
63                         bv.bv_len = ci->ci_dn.bv_len + STRLENOF("\"\" ") +
64                                 ci->ci_ad->ad_cname.bv_len;
65                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
66                         len = snprintf( bv.bv_val, bv.bv_len + 1, "\"%s\" %s",
67                                 ci->ci_dn.bv_val, ci->ci_ad->ad_cname.bv_val );
68                         assert( len == bv.bv_len );
69                         ber_bvarray_add( &c->rvalue_vals, &bv );
70                         rc = 0;
71                 }
72                 }
73                 break;
74         case LDAP_MOD_DELETE:
75                 if ( c->valx == -1 ) {
76                 /* Delete entire attribute */
77                         collect_info *ci;
78                         while (( ci = on->on_bi.bi_private )) {
79                                 on->on_bi.bi_private = ci->ci_next;
80                                 ch_free( ci->ci_dn.bv_val );
81                                 ch_free( ci );
82                         }
83                 } else {
84                 /* Delete just one value */
85                         collect_info **cip, *ci;
86                         int i;
87                         cip = (collect_info **)&on->on_bi.bi_private;
88                         for ( i=0; i <= c->valx; i++, cip = &ci->ci_next ) ci = *cip;
89                         *cip = ci->ci_next;
90                         ch_free( ci->ci_dn.bv_val );
91                         ch_free( ci );
92                 }
93                 rc = 0;
94                 break;
95         case SLAP_CONFIG_ADD:
96         case LDAP_MOD_ADD:
97                 {
98                 collect_info *ci;
99                 struct berval bv, dn;
100                 const char *text;
101                 AttributeDescription *ad = NULL;
102
103                 ber_str2bv( c->argv[1], 0, 0, &bv );
104                 if ( dnNormalize( 0, NULL, NULL, &bv, &dn, NULL ) ) {
105                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid DN: \"%s\"",
106                                 c->argv[0], c->argv[1] );
107                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
108                                 "%s: %s\n", c->log, c->cr_msg, 0 );
109                         return ARG_BAD_CONF;
110                 }
111                 if ( slap_str2ad( c->argv[2], &ad, &text ) ) {
112                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s attribute description unknown: \"%s\"",
113                                 c->argv[0], c->argv[2] );
114                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
115                                 "%s: %s\n", c->log, c->cr_msg, 0 );
116                         return ARG_BAD_CONF;
117                 }
118
119                 /* The on->on_bi.bi_private pointer can be used for
120                  * anything this instance of the overlay needs.
121                  */
122                 ci = ch_malloc( sizeof( collect_info ));
123                 ci->ci_ad = ad;
124                 ci->ci_dn = dn;
125                 ci->ci_next = on->on_bi.bi_private;
126                 on->on_bi.bi_private = ci;
127                 rc = 0;
128                 }
129         }
130         return rc;
131 }
132
133 static ConfigTable collectcfg[] = {
134         { "collectinfo", "dn> <attribute", 3, 3, 0,
135           ARG_MAGIC, collect_cf,
136           "( OLcfgOvAt:19.1 NAME 'olcCollectInfo' "
137           "DESC 'DN of entry and attribute to distribute' "
138           "SYNTAX OMsDirectoryString )", NULL, NULL },
139         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
140 };
141
142 static ConfigOCs collectocs[] = {
143         { "( OLcfgOvOc:19.1 "
144           "NAME 'olcCollectConfig' "
145           "DESC 'Collective Attribute configuration' "
146           "SUP olcOverlayConfig "
147           "MAY olcCollectInfo )",
148           Cft_Overlay, collectcfg },
149         { NULL, 0, NULL }
150 };
151
152 static int
153 collect_destroy(
154         BackendDB *be,
155         ConfigReply *cr
156 )
157 {
158         slap_overinst *on = (slap_overinst *)be->bd_info;
159         collect_info *ci;
160
161         while (( ci = on->on_bi.bi_private )) {
162                 on->on_bi.bi_private = ci->ci_next;
163                 ch_free( ci->ci_dn.bv_val );
164                 ch_free( ci );
165         }
166         return 0;
167 }
168
169 static int
170 collect_response( Operation *op, SlapReply *rs )
171 {
172         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
173         collect_info *ci = on->on_bi.bi_private;
174
175         /* If we've been configured and the current response is
176          * a search entry
177          */
178         if ( ci && rs->sr_type == REP_SEARCH ) {
179                 int rc;
180
181                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
182
183                 for (; ci; ci=ci->ci_next ) {
184                         BerVarray vals = NULL;
185
186                         /* Is our configured entry an ancestor of this one? */
187                         if ( !dnIsSuffix( &rs->sr_entry->e_nname, &ci->ci_dn ))
188                                 continue;
189
190                         /* Extract the values of the desired attribute from
191                          * the ancestor entry
192                          */
193                         rc = backend_attribute( op, NULL, &ci->ci_dn, ci->ci_ad, &vals, ACL_READ );
194
195                         /* If there are any values, merge them into the
196                          * current entry
197                          */
198                         if ( vals ) {
199                                 /* The current entry may live in a cache, so
200                                  * don't modify it directly. Make a copy and
201                                  * work with that instead.
202                                  */
203                                 if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE )) {
204                                         rs->sr_entry = entry_dup( rs->sr_entry );
205                                         rs->sr_flags |= REP_ENTRY_MODIFIABLE |
206                                                 REP_ENTRY_MUSTBEFREED;
207                                 }
208                                 attr_merge( rs->sr_entry, ci->ci_ad, vals, NULL );
209                                 ber_bvarray_free_x( vals, op->o_tmpmemctx );
210                         }
211                 }
212         }
213         /* Default is to just fall through to the normal processing */
214         return SLAP_CB_CONTINUE;
215 }
216
217 static slap_overinst collect;
218
219 int collect_initialize() {
220         int code;
221
222         collect.on_bi.bi_type = "collect";
223         collect.on_bi.bi_db_destroy = collect_destroy;
224         collect.on_response = collect_response;
225
226         collect.on_bi.bi_cf_ocs = collectocs;
227         code = config_register_schema( collectcfg, collectocs );
228         if ( code ) return code;
229
230         return overlay_register( &collect );
231 }
232
233 #if SLAPD_OVER_COLLECT == SLAPD_MOD_DYNAMIC
234 int init_module(int argc, char *argv[]) {
235         return collect_initialize();
236 }
237 #endif
238
239 #endif /* SLAPD_OVER_COLLECT */