]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/lastmod.c
6c832f41aae43d8637b546d654acdbdad7889850
[openldap] / servers / slapd / overlays / lastmod.c
1 /* lastmod.c - returns last modification info */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENTS:
16  * This work was initially developed by Pierangelo Masarati for inclusion in
17  * OpenLDAP Software.
18  */
19
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_LASTMOD
23
24 #include <stdio.h>
25
26 #include <ac/string.h>
27 #include <ac/socket.h>
28
29 #include "slap.h"
30 #include "lutil.h"
31
32 typedef struct lastmod_info_t {
33         struct berval           lmi_rdnvalue;
34         Entry                   *lmi_e;
35         ldap_pvt_thread_mutex_t lmi_entry_mutex;
36         int                     lmi_enabled;
37 } lastmod_info_t;
38
39 struct lastmod_schema_t {
40         ObjectClass             *lms_oc_lastmod;
41         AttributeDescription    *lms_ad_lastmodDN;
42         AttributeDescription    *lms_ad_lastmodType;
43         AttributeDescription    *lms_ad_lastmodEnabled;
44 } lastmod_schema;
45
46 enum lastmodType_e {
47         LASTMOD_ADD = 0,
48         LASTMOD_DELETE,
49         LASTMOD_EXOP,
50         LASTMOD_MODIFY,
51         LASTMOD_MODRDN,
52         LASTMOD_UNKNOWN
53 };
54
55 struct berval lastmodType[] = {
56         BER_BVC( "add" ),
57         BER_BVC( "delete" ),
58         BER_BVC( "exop" ),
59         BER_BVC( "modify" ),
60         BER_BVC( "modrdn" ),
61         BER_BVC( "unknown" ),
62         BER_BVNULL
63 };
64
65 static struct m_s {
66         char                    *name;
67         char                    *schema;
68         slap_mask_t             flags;
69         int                     offset;
70 } moc[] = {
71         { "lastmod", "( 1.3.6.1.4.1.4203.666.3.13"
72                 "NAME 'lastmod' "
73                 "DESC 'OpenLDAP per-database last modification monitoring' "
74                 "STRUCTURAL "
75                 "SUP top "
76                 "MUST cn "
77                 "MAY ( "
78                         "lastmodDN "
79                         "$ lastmodType "
80                         "$ description "
81                         "$ seeAlso "
82                 ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
83                 offsetof( struct lastmod_schema_t, lms_oc_lastmod ) },
84         { NULL }
85 }, mat[] = {
86         { "lastmodDN", "( 1.3.6.1.4.1.4203.666.1.28"
87                 "NAME 'lastmodDN' "
88                 "DESC 'DN of last modification' "
89                 "EQUALITY distinguishedNameMatch "
90                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
91                 "NO-USER-MODIFICATION "
92                 "USAGE directoryOperation )", SLAP_AT_HIDE,
93                 offsetof( struct lastmod_schema_t, lms_ad_lastmodDN ) },
94         { "lastmodType", "( 1.3.6.1.4.1.4203.666.1.29"
95                 "NAME 'lastmodType' "
96                 "DESC 'Type of last modification' "
97                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
98                 "EQUALITY caseIgnoreMatch "
99                 "SINGLE-VALUE "
100                 "NO-USER-MODIFICATION "
101                 "USAGE directoryOperation )", SLAP_AT_HIDE,
102                 offsetof( struct lastmod_schema_t, lms_ad_lastmodType ) },
103         { "lastmodEnabled", "( 1.3.6.1.4.1.4203.666.1.30"
104                 "NAME 'lastmodEnabled' "
105                 "DESC 'Lastmod overlay state' "
106                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
107                 "EQUALITY booleanMatch "
108                 "SINGLE-VALUE )", 0,
109                 offsetof( struct lastmod_schema_t, lms_ad_lastmodEnabled ) },
110         { NULL }
111 };
112
113 static const struct berval *write_exop[] = {
114         &slap_EXOP_MODIFY_PASSWD,
115         NULL
116 };
117
118 static int
119 lastmod_search( Operation *op, SlapReply *rs )
120 {
121         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
122         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
123         int                     rc;
124
125         /* if we get here, it must be a success */
126         rs->sr_err = LDAP_SUCCESS;
127
128         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
129
130         rc = test_filter( op, lmi->lmi_e, op->oq_search.rs_filter );
131         if ( rc == LDAP_COMPARE_TRUE ) {
132                 rs->sr_attrs = op->ors_attrs;
133                 rs->sr_flags = 0;
134                 rs->sr_entry = lmi->lmi_e;
135                 rs->sr_err = send_search_entry( op, rs );
136                 rs->sr_entry = NULL;
137                 rs->sr_flags = 0;
138                 rs->sr_attrs = NULL;
139         }
140
141         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
142
143         send_ldap_result( op, rs );
144
145         return 0;
146 }
147
148 static int
149 lastmod_compare( Operation *op, SlapReply *rs )
150 {
151         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
152         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
153         Attribute               *a;
154
155         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
156
157         if ( get_assert( op ) &&
158                 ( test_filter( op, lmi->lmi_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
159         {
160                 rs->sr_err = LDAP_ASSERTION_FAILED;
161                 goto return_results;
162         }
163
164         rs->sr_err = access_allowed( op, lmi->lmi_e, op->oq_compare.rs_ava->aa_desc,
165                 &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
166         if ( ! rs->sr_err ) {
167                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
168                 goto return_results;
169         }
170
171         rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
172
173         for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
174                 a != NULL;
175                 a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
176         {
177                 rs->sr_err = LDAP_COMPARE_FALSE;
178
179                 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
180                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
181                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
182                         a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
183                 {
184                         rs->sr_err = LDAP_COMPARE_TRUE;
185                         break;
186                 }
187         }
188
189 return_results:;
190
191         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
192
193         send_ldap_result( op, rs );
194
195         if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
196                 rs->sr_err = LDAP_SUCCESS;
197         }
198
199         return rs->sr_err;
200 }
201
202 static int
203 lastmod_exop( Operation *op, SlapReply *rs )
204 {
205         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
206
207         /* Temporary */
208
209         op->o_bd->bd_info = (BackendInfo *)on->on_info;
210         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
211         rs->sr_text = "not allowed within namingContext";
212         send_ldap_result( op, rs );
213         rs->sr_text = NULL;
214         
215         return -1;
216 }
217
218 static int
219 lastmod_modify( Operation *op, SlapReply *rs )
220 {
221         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
222         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
223         Modifications           *ml;
224
225         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
226
227         if ( !acl_check_modlist( op, lmi->lmi_e, op->orm_modlist ) ) {
228                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
229                 goto cleanup;
230         }
231
232         for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
233                 Attribute       *a;
234
235                 if ( ml->sml_desc != lastmod_schema.lms_ad_lastmodEnabled ) {
236                         continue;
237                 }
238
239                 if ( ml->sml_op != LDAP_MOD_REPLACE ) {
240                         rs->sr_text = "unsupported mod type";
241                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
242                         goto cleanup;
243                 }
244                 
245                 a = attr_find( lmi->lmi_e->e_attrs, ml->sml_desc );
246
247                 if ( a == NULL ) {
248                         rs->sr_text = "lastmod overlay internal error";
249                         rs->sr_err = LDAP_OTHER;
250                         goto cleanup;
251                 }
252
253                 ch_free( a->a_vals[ 0 ].bv_val );
254                 ber_dupbv( &a->a_vals[ 0 ], &ml->sml_values[ 0 ] );
255                 if ( a->a_nvals ) {
256                         ch_free( a->a_nvals[ 0 ].bv_val );
257                         if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
258                                 ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_nvalues[ 0 ] );
259                         } else {
260                                 ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_values[ 0 ] );
261                         }
262                 }
263
264                 if ( strcmp( ml->sml_values[ 0 ].bv_val, "TRUE" ) == 0 ) {
265                         lmi->lmi_enabled = 1;
266                 } else if ( strcmp( ml->sml_values[ 0 ].bv_val, "FALSE" ) == 0 ) {
267                         lmi->lmi_enabled = 0;
268                 } else {
269                         assert( 0 );
270                 }
271         }
272
273         rs->sr_err = LDAP_SUCCESS;
274
275 cleanup:;
276         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
277
278         send_ldap_result( op, rs );
279         rs->sr_text = NULL;
280
281         return rs->sr_err;
282 }
283
284 static int
285 lastmod_op_func( Operation *op, SlapReply *rs )
286 {
287         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
288         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
289         unsigned                i;
290         Modifications           *ml;
291
292         if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
293                 switch ( op->o_tag ) {
294                 case LDAP_REQ_SEARCH:
295                         if ( op->ors_scope != LDAP_SCOPE_BASE ) {
296                                 goto return_referral;
297                         }
298                         /* process */
299                         return lastmod_search( op, rs );
300
301                 case LDAP_REQ_COMPARE:
302                         return lastmod_compare( op, rs );
303
304                 case LDAP_REQ_EXTENDED:
305                         /* if write, reject; otherwise process */
306                         for ( i = 0; write_exop[ i ] != NULL; i++ ) {
307                                 if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
308                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
309                                         rs->sr_text = "not allowed within namingContext";
310                                         goto return_error;
311                                 }
312                         }
313                         return lastmod_exop( op, rs );
314
315                 case LDAP_REQ_MODIFY:
316                         /* if global overlay, modlist is not checked yet */
317                         if ( op->orm_modlist->sml_desc == NULL ) {
318                                 char textbuf[SLAP_TEXT_BUFLEN];
319                                 size_t textlen = sizeof textbuf;
320
321                                 rs->sr_err = slap_mods_check( op->orm_modlist, 0, &rs->sr_text,
322                                                 textbuf, textlen, NULL );
323
324                                 if ( rs->sr_err ) {
325                                         goto return_error;
326                                 }
327                         }
328
329                         /* allow only changes to overlay status */
330                         for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
331                                 if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifiersName ) != 0
332                                                 && ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) != 0
333                                                 && ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) != 0
334                                                 && ad_cmp( ml->sml_desc, lastmod_schema.lms_ad_lastmodEnabled ) != 0 )
335                                 {
336                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
337                                         rs->sr_text = "not allowed within namingContext";
338                                         goto return_error;
339                                 }
340                         }
341                         return lastmod_modify( op, rs );
342
343                 default:
344                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
345                         rs->sr_text = "not allowed within namingContext";
346                         goto return_error;
347                 }
348         }
349
350         if ( dnIsSuffix( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
351                 goto return_referral;
352         }
353
354         return SLAP_CB_CONTINUE;
355
356 return_referral:;
357         op->o_bd->bd_info = (BackendInfo *)on->on_info;
358         rs->sr_ref = referral_rewrite( SLAPD_GLOBAL(default_referral),
359                         NULL, &op->o_req_dn, op->ors_scope );
360
361         if ( !rs->sr_ref ) {
362                 rs->sr_ref = SLAPD_GLOBAL( default_referral );
363         }
364         rs->sr_err = LDAP_REFERRAL;
365         send_ldap_result( op, rs );
366
367         if ( rs->sr_ref != SLAPD_GLOBAL( default_referral )) {
368                 ber_bvarray_free( rs->sr_ref );
369         }
370         rs->sr_ref = NULL;
371
372         return -1;
373
374 return_error:;
375         op->o_bd->bd_info = (BackendInfo *)on->on_info;
376         send_ldap_result( op, rs );
377         rs->sr_text = NULL;
378
379         return -1;
380 }
381
382 static int
383 best_guess( Operation *op,
384                 struct berval *bv_modifyTimestamp, struct berval *bv_nmodifyTimestamp,
385                 struct berval *bv_modifiersName, struct berval *bv_nmodifiersName )
386 {
387         if ( bv_modifyTimestamp ) {
388                 struct tm       *tm;
389 #ifdef HAVE_GMTIME_R
390                 struct tm       tm_buf;
391 #endif
392                 char            tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
393                 time_t          currtime;
394
395                 /* best guess */
396 #if 0
397                 currtime = slap_get_time();
398 #endif
399                 /* maybe we better use the time the operation was initiated */
400                 currtime = op->o_time;
401
402 #ifndef HAVE_GMTIME_R
403                 ldap_pvt_thread_mutex_lock( &SLAPD_GLOBAL( gmtime_mutex ));
404                 tm = gmtime( &currtime );
405 #else /* HAVE_GMTIME_R */
406                 tm = gmtime_r( &currtime, &tm_buf );
407 #endif /* HAVE_GMTIME_R */
408                 lutil_gentime( tmbuf, sizeof( tmbuf ), tm );
409 #ifndef HAVE_GMTIME_R
410                 ldap_pvt_thread_mutex_unlock( &SLAPD_GLOBAL(gmtime_mutex) );
411 #endif
412
413                 ber_str2bv( tmbuf, 0, 1, bv_modifyTimestamp );
414                 ber_dupbv( bv_nmodifyTimestamp, bv_modifyTimestamp );
415         }
416
417         if ( bv_modifiersName ) {
418                 /* best guess */
419                 ber_dupbv( bv_modifiersName, &op->o_dn );
420                 ber_dupbv( bv_nmodifiersName, &op->o_ndn );
421         }
422
423         return 0;
424 }
425
426 static int
427 lastmod_update( Operation *op, SlapReply *rs )
428 {
429         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
430         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
431         Attribute               *a;
432         Modifications           *ml = NULL;
433         struct berval           bv_modifyTimestamp = BER_BVNULL,
434                                 bv_nmodifyTimestamp = BER_BVNULL,
435                                 bv_modifiersName = BER_BVNULL,
436                                 bv_nmodifiersName = BER_BVNULL,
437                                 bv_name = BER_BVNULL,
438                                 bv_nname = BER_BVNULL;
439         enum lastmodType_e      lmt = LASTMOD_UNKNOWN;
440         Entry                   *e = NULL;
441         int                     rc = -1;
442
443         /* FIXME: timestamp? modifier? */
444         switch ( op->o_tag ) {
445         case LDAP_REQ_ADD:
446                 lmt = LASTMOD_ADD;
447                 e = op->ora_e;
448                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
449                 if ( a != NULL ) {
450                         ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
451                         ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
452                 }
453                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
454                 if ( a != NULL ) {
455                         ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
456                         if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
457                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
458                         } else {
459                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
460                         }
461                 }
462                 ber_dupbv( &bv_name, &e->e_name );
463                 ber_dupbv( &bv_nname, &e->e_nname );
464                 break;
465
466         case LDAP_REQ_DELETE:
467                 lmt = LASTMOD_DELETE;
468
469                 best_guess( op, &bv_modifyTimestamp, &bv_nmodifyTimestamp,
470                                 &bv_modifiersName, &bv_nmodifiersName );
471
472                 ber_dupbv( &bv_name, &op->o_req_dn );
473                 ber_dupbv( &bv_nname, &op->o_req_ndn );
474                 break;
475
476         case LDAP_REQ_EXTENDED:
477                 lmt = LASTMOD_EXOP;
478
479                 /* actually, password change is wrapped around a backend 
480                  * call to modify, so it never shows up as an exop... */
481                 best_guess( op, &bv_modifyTimestamp, &bv_nmodifyTimestamp,
482                                 &bv_modifiersName, &bv_nmodifiersName );
483
484                 ber_dupbv( &bv_name, &op->o_req_dn );
485                 ber_dupbv( &bv_nname, &op->o_req_ndn );
486                 break;
487
488         case LDAP_REQ_MODIFY:
489                 lmt = LASTMOD_MODIFY;
490
491                 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
492                         if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) {
493                                 ber_dupbv( &bv_modifiersName, &ml->sml_values[0] );
494                                 ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] );
495
496                                 if ( !BER_BVISNULL( &bv_modifyTimestamp ) ) {
497                                         break;
498                                 }
499
500                         } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) == 0 ) {
501                                 ber_dupbv( &bv_modifyTimestamp, &ml->sml_values[0] );
502                                 if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
503                                         ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_nvalues[0] );
504                                 } else {
505                                         ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] );
506                                 }
507
508                                 if ( !BER_BVISNULL( &bv_modifiersName ) ) {
509                                         break;
510                                 }
511                         }
512                 }
513
514                 /* if rooted at global overlay, opattrs are not yet in place */
515                 if ( BER_BVISNULL( &bv_modifiersName ) ) {
516                         best_guess( op, NULL, NULL, &bv_modifiersName, &bv_nmodifiersName );
517                 }
518
519                 /* if rooted at global overlay, opattrs are not yet in place */
520                 if ( BER_BVISNULL( &bv_modifyTimestamp ) ) {
521                         best_guess( op, &bv_modifyTimestamp, &bv_nmodifyTimestamp, NULL, NULL );
522                 }
523
524                 ber_dupbv( &bv_name, &op->o_req_dn );
525                 ber_dupbv( &bv_nname, &op->o_req_ndn );
526                 break;
527
528         case LDAP_REQ_MODRDN:
529                 lmt = LASTMOD_MODRDN;
530                 e = NULL;
531
532                 if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) {
533                         build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL );
534                         build_new_dn( &bv_nname, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
535
536                 } else {
537                         struct berval   pdn;
538
539                         dnParent( &op->o_req_dn, &pdn );
540                         build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL );
541
542                         dnParent( &op->o_req_ndn, &pdn );
543                         build_new_dn( &bv_nname, &pdn, &op->orr_nnewrdn, NULL );
544                 }
545
546                 if ( on->on_info->oi_orig->bi_entry_get_rw ) {
547                         BackendInfo     *bi = op->o_bd->bd_info;
548                         int             rc;
549
550                         op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
551                         rc = (*op->o_bd->bd_info->bi_entry_get_rw)( op, &bv_name, NULL, NULL, 0, &e );
552                         if ( rc == LDAP_SUCCESS ) {
553                                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
554                                 if ( a != NULL ) {
555                                         ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
556                                         ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
557                                 }
558                                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
559                                 if ( a != NULL ) {
560                                         ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
561                                         if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
562                                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
563                                         } else {
564                                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
565                                         }
566                                 }
567
568                                 assert( dn_match( &bv_name, &e->e_name ) );
569                                 assert( dn_match( &bv_nname, &e->e_nname ) );
570
571                                 (*op->o_bd->bd_info->bi_entry_release_rw)( op, e, 0 );
572                         }
573
574                         op->o_bd->bd_info = bi;
575
576                 }
577
578                 /* if !bi_entry_get_rw || bi_entry_get_rw failed for any reason... */
579                 if ( e == NULL ) {
580                         best_guess( op, &bv_modifyTimestamp, &bv_nmodifyTimestamp,
581                                         &bv_modifiersName, &bv_nmodifiersName );
582                 }
583
584                 break;
585
586         default:
587                 return -1;
588         }
589         
590         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
591
592 #if 0
593         fprintf( stderr, "### lastmodDN: %s %s\n", bv_name.bv_val, bv_nname.bv_val );
594 #endif
595
596         a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodDN );
597         if ( a == NULL ) {
598                 goto error_return;
599         }
600         ch_free( a->a_vals[0].bv_val );
601         a->a_vals[0] = bv_name;
602         ch_free( a->a_nvals[0].bv_val );
603         a->a_nvals[0] = bv_nname;
604
605 #if 0
606         fprintf( stderr, "### lastmodType: %s %s\n", lastmodType[ lmt ].bv_val, lastmodType[ lmt ].bv_val );
607 #endif
608
609         a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodType );
610         if ( a == NULL ) {
611                 goto error_return;
612         } 
613         ch_free( a->a_vals[0].bv_val );
614         ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
615         ch_free( a->a_nvals[0].bv_val );
616         ber_dupbv( &a->a_nvals[0], &lastmodType[ lmt ] );
617
618 #if 0
619         fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val );
620 #endif
621
622         a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifiersName );
623         if ( a == NULL ) {
624                 goto error_return;
625         } 
626         ch_free( a->a_vals[0].bv_val );
627         a->a_vals[0] = bv_modifiersName;
628         ch_free( a->a_nvals[0].bv_val );
629         a->a_nvals[0] = bv_nmodifiersName;
630
631 #if 0
632         fprintf( stderr, "### modifyTimestamp: %s %s\n", bv_nmodifyTimestamp.bv_val, bv_modifyTimestamp.bv_val );
633 #endif
634
635         a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifyTimestamp );
636         if ( a == NULL ) {
637                 goto error_return;
638         } 
639         ch_free( a->a_vals[0].bv_val );
640         a->a_vals[0] = bv_modifyTimestamp;
641         ch_free( a->a_nvals[0].bv_val );
642         a->a_nvals[0] = bv_nmodifyTimestamp;
643
644         rc = 0;
645
646 error_return:;
647         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
648         
649         return rc;
650 }
651
652 static int
653 lastmod_response( Operation *op, SlapReply *rs )
654 {
655         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
656         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
657         unsigned int            i;
658
659         /* don't record failed operations */
660         switch ( rs->sr_err ) {
661         case LDAP_SUCCESS:
662                 /* FIXME: other cases? */
663                 break;
664
665         default:
666                 return SLAP_CB_CONTINUE;
667         }
668
669         /* record only write operations */
670         switch ( op->o_tag ) {
671         case LDAP_REQ_ADD:
672         case LDAP_REQ_MODIFY:
673         case LDAP_REQ_MODRDN:
674         case LDAP_REQ_DELETE:
675                 break;
676
677         case LDAP_REQ_EXTENDED:
678                 /* if write, process */
679                 for ( i = 0; write_exop[ i ] != NULL; i++ ) {
680                         if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
681                                 goto process;
682                         }
683                 }
684                 /* fall thru */
685
686         default:
687                 return SLAP_CB_CONTINUE;
688         }
689
690         /* skip if disabled */
691         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
692         if ( !lmi->lmi_enabled ) {
693                 ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
694                 return SLAP_CB_CONTINUE;
695         }
696         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
697
698 process:;
699         (void)lastmod_update( op, rs );
700
701         return SLAP_CB_CONTINUE;
702 }
703
704 static int
705 lastmod_db_init(
706         BackendDB *be
707 )
708 {
709         slap_overinst           *on = (slap_overinst *)be->bd_info;
710         lastmod_info_t          *lmi;
711
712         if ( lastmod_schema.lms_oc_lastmod == NULL ) {
713                 int             i;
714                 const char      *text;
715
716                 /* schema integration */
717                 for ( i = 0; mat[i].name; i++ ) {
718                         LDAPAttributeType       *at;
719                         int                     code;
720                         const char              *err;
721                         AttributeDescription    **ad;
722         
723                         at = ldap_str2attributetype( mat[i].schema, &code,
724                                 &err, LDAP_SCHEMA_ALLOW_ALL );
725                         if ( !at ) {
726                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
727                                         "in AttributeType '%s' %s before %s\n",
728                                         mat[i].name, ldap_scherr2str(code), err );
729                                 return -1;
730                         }
731         
732                         if ( at->at_oid == NULL ) {
733                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
734                                         "null OID for attributeType '%s'\n",
735                                         mat[i].name, 0, 0 );
736                                 return -1;
737                         }
738         
739                         code = at_add(at, &err);
740                         if ( code ) {
741                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
742                                         "%s in attributeType '%s'\n",
743                                         scherr2str(code), mat[i].name, 0 );
744                                 return -1;
745                         }
746                         ldap_memfree(at);
747         
748                         ad = ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset]));
749                         ad[0] = NULL;
750                         if ( slap_str2ad( mat[i].name, ad, &text ) ) {
751                                 Debug( LDAP_DEBUG_ANY,
752                                         "lastmod_init: %s\n", text, 0, 0 );
753                                 return -1;
754                         }
755         
756                         (*ad)->ad_type->sat_flags |= mat[i].flags;
757                 }
758
759                 for ( i = 0; moc[i].name; i++ ) {
760                         LDAPObjectClass         *oc;
761                         int                     code;
762                         const char              *err;
763                         ObjectClass             *Oc;
764         
765                         oc = ldap_str2objectclass(moc[i].schema, &code, &err,
766                                         LDAP_SCHEMA_ALLOW_ALL );
767                         if ( !oc ) {
768                                 Debug( LDAP_DEBUG_ANY,
769                                         "unable to parse lastmod objectClass '%s': "
770                                         "%s before %s\n" , moc[i].name,
771                                         ldap_scherr2str(code), err );
772                                 return -1;
773                         }
774
775                         if ( oc->oc_oid == NULL ) {
776                                 Debug( LDAP_DEBUG_ANY,
777                                         "objectClass '%s' has no OID\n" ,
778                                         moc[i].name, 0, 0 );
779                                 return -1;
780                         }
781
782                         code = oc_add(oc, 0, &err);
783                         if ( code ) {
784                                 Debug( LDAP_DEBUG_ANY,
785                                         "objectClass '%s': %s \"%s\"\n" ,
786                                         moc[i].name, scherr2str(code), err );
787                                 return -1;
788                         }
789         
790                         ldap_memfree(oc);
791         
792                         Oc = oc_find( moc[i].name );
793                         if ( Oc == NULL ) {
794                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
795                                                 "unable to find objectClass %s "
796                                                 "(just added)\n", moc[i].name, 0, 0 );
797                                 return -1;
798                         }
799
800                         Oc->soc_flags |= moc[i].flags;
801
802                         ((ObjectClass **)&(((char *)&lastmod_schema)[moc[i].offset]))[0] = Oc;
803                 }
804         }
805
806         lmi = (lastmod_info_t *)ch_malloc( sizeof( lastmod_info_t ) );
807
808         memset( lmi, 0, sizeof( lastmod_info_t ) );
809         lmi->lmi_enabled = 1;
810         
811         on->on_bi.bi_private = lmi;
812
813         return 0;
814 }
815
816 static int
817 lastmod_db_config(
818         BackendDB       *be,
819         const char      *fname,
820         int             lineno,
821         int             argc,
822         char    **argv
823 )
824 {
825         slap_overinst           *on = (slap_overinst *)be->bd_info;
826         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
827
828         if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) {
829                 if ( lmi->lmi_rdnvalue.bv_val ) {
830                         /* already defined! */
831                         ch_free( lmi->lmi_rdnvalue.bv_val );
832                 }
833
834                 ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue );
835
836         } else if ( strcasecmp( argv[ 0 ], "lastmod-enabled" ) == 0 ) {
837                 if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
838                         lmi->lmi_enabled = 1;
839
840                 } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
841                         lmi->lmi_enabled = 0;
842
843                 } else {
844                         return -1;
845                 }
846
847         } else {
848                 return SLAP_CONF_UNKNOWN;
849         }
850
851         return 0;
852 }
853
854 static int
855 lastmod_db_open(
856         BackendDB *be
857 )
858 {
859         slap_overinst   *on = (slap_overinst *) be->bd_info;
860         lastmod_info_t  *lmi = (lastmod_info_t *)on->on_bi.bi_private;
861         char            buf[ 8192 ];
862         struct tm               *tms;
863 #ifdef HAVE_GMTIME_R
864         struct tm               tm_buf;
865 #endif
866         static char             tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
867
868         if ( !SLAP_LASTMOD( be ) ) {
869                 fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" );
870                 return -1;
871         }
872
873         /*
874          * Start
875          */
876 #ifndef HAVE_GMTIME_R
877         ldap_pvt_thread_mutex_lock( &SLAPD_GLOBAL( gmtime_mutex ));
878         tms = gmtime( &SLAPD_GLOBAL( starttime ));
879 #else /* HAVE_GMTIME_R */
880         tms = gmtime_r( &SLAPD_GLOBAL( starttime ), &tm_buf );
881 #endif /* HAVE_GMTIME_R */
882         lutil_gentime( tmbuf, sizeof(tmbuf), tms );
883 #ifndef HAVE_GMTIME_R
884         ldap_pvt_thread_mutex_unlock( &SLAPD_GLOBAL( gmtime_mutex ));
885 #endif
886
887         if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
888                 ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue );
889         }
890
891         snprintf( buf, sizeof( buf ),
892                         "dn: cn=%s%s%s\n"
893                         "objectClass: %s\n"
894                         "structuralObjectClass: %s\n"
895                         "cn: %s\n"
896                         "description: This object contains the last modification to this database\n"
897                         "%s: cn=%s%s%s\n"
898                         "%s: %s\n"
899                         "%s: %s\n"
900                         "createTimestamp: %s\n"
901                         "creatorsName: %s\n"
902                         "modifyTimestamp: %s\n"
903                         "modifiersName: %s\n"
904                         "hasSubordinates: FALSE\n",
905                         lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
906                         lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
907                         lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
908                         lmi->lmi_rdnvalue.bv_val,
909                         lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val,
910                                 lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
911                         lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val,
912                         lastmod_schema.lms_ad_lastmodEnabled->ad_cname.bv_val, lmi->lmi_enabled ? "TRUE" : "FALSE",
913                         tmbuf,
914                         BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
915                         tmbuf,
916                         BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val );
917
918 #if 0
919         fprintf( stderr, "# entry:\n%s\n", buf );
920 #endif
921
922         lmi->lmi_e = str2entry( buf );
923         if ( lmi->lmi_e == NULL ) {
924                 return -1;
925         }
926
927         ldap_pvt_thread_mutex_init( &lmi->lmi_entry_mutex );
928
929         return 0;
930 }
931
932 static int
933 lastmod_db_destroy(
934         BackendDB *be
935 )
936 {
937         slap_overinst   *on = (slap_overinst *)be->bd_info;
938         lastmod_info_t  *lmi = (lastmod_info_t *)on->on_bi.bi_private;
939
940         if ( lmi ) {
941                 if ( !BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
942                         ch_free( lmi->lmi_rdnvalue.bv_val );
943                 }
944
945                 if ( lmi->lmi_e ) {
946                         entry_free( lmi->lmi_e );
947
948                         ldap_pvt_thread_mutex_destroy( &lmi->lmi_entry_mutex );
949                 }
950
951                 ch_free( lmi );
952         }
953
954         return 0;
955 }
956
957 /* This overlay is set up for dynamic loading via moduleload. For static
958  * configuration, you'll need to arrange for the slap_overinst to be
959  * initialized and registered by some other function inside slapd.
960  */
961
962 static slap_overinst            lastmod;
963
964 int
965 lastmod_init()
966 {
967         lastmod.on_bi.bi_type = "lastmod";
968         lastmod.on_bi.bi_db_init = lastmod_db_init;
969         lastmod.on_bi.bi_db_config = lastmod_db_config;
970         lastmod.on_bi.bi_db_destroy = lastmod_db_destroy;
971         lastmod.on_bi.bi_db_open = lastmod_db_open;
972
973         lastmod.on_bi.bi_op_add = lastmod_op_func;
974         lastmod.on_bi.bi_op_compare = lastmod_op_func;
975         lastmod.on_bi.bi_op_delete = lastmod_op_func;
976         lastmod.on_bi.bi_op_modify = lastmod_op_func;
977         lastmod.on_bi.bi_op_modrdn = lastmod_op_func;
978         lastmod.on_bi.bi_op_search = lastmod_op_func;
979         lastmod.on_bi.bi_extended = lastmod_op_func;
980
981         lastmod.on_response = lastmod_response;
982
983         return overlay_register( &lastmod );
984 }
985
986 #if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC
987 int
988 init_module( int argc, char *argv[] )
989 {
990         return lastmod_init();
991 }
992 #endif /* SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC */
993
994 #endif /* defined(SLAPD_OVER_LASTMOD) */