]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/lastmod.c
last mod overlay (needs cleanup)
[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 } lastmod_info_t;
37
38 struct lastmod_schema_t {
39         ObjectClass             *lms_oc_lastmod;
40         AttributeDescription    *lms_ad_lastmodDN;
41         AttributeDescription    *lms_ad_lastmodType;
42 } lastmod_schema;
43
44 enum lastmodType_e {
45         LASTMOD_ADD = 0,
46         LASTMOD_DELETE,
47         LASTMOD_EXOP,
48         LASTMOD_MODIFY,
49         LASTMOD_MODRDN,
50         LASTMOD_UNKNOWN
51 };
52
53 struct berval lastmodType[] = {
54         BER_BVC( "add" ),
55         BER_BVC( "delete" ),
56         BER_BVC( "exop" ),
57         BER_BVC( "modify" ),
58         BER_BVC( "modrdn" ),
59         BER_BVC( "unknown" ),
60         BER_BVNULL
61 };
62
63 static struct m_s {
64         char    *name;
65         char    *schema;
66         slap_mask_t flags;
67         int     offset;
68 } moc[] = {
69         { "lastmod", "( 1.3.6.1.4.1.4203.666.3.100001 "
70                 "NAME 'lastmod' "
71                 "DESC 'OpenLDAP per-database last modification monitoring' "
72                 "SUP top STRUCTURAL "
73                 "MUST cn "
74                 "MAY ( "
75                         "lastmodDN "
76                         "$ lastmodType "
77                         "$ description "
78                         "$ seeAlso "
79                 ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
80                 offsetof(struct lastmod_schema_t, lms_oc_lastmod) },
81         { NULL }
82 }, mat[] = {
83         { "lastmodDN", "( 1.3.6.1.4.1.4203.666.1.100001 "
84                 "NAME 'lastmodDN' "
85                 "DESC 'DN of last modification' "
86                 "EQUALITY distinguishedNameMatch "
87                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
88                 "NO-USER-MODIFICATION "
89                 "USAGE directoryOperation )", SLAP_AT_HIDE,
90                 offsetof(struct lastmod_schema_t, lms_ad_lastmodDN) },
91         { "lastmodType", "( 1.3.6.1.4.1.4203.666.1.100002 "
92                 "NAME 'lastmodType' "
93                 "DESC 'Type of last modification' "
94                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
95                 "EQUALITY caseIgnoreMatch "
96                 "SINGLE-VALUE "
97                 "NO-USER-MODIFICATION "
98                 "USAGE directoryOperation )", SLAP_AT_HIDE,
99                 offsetof(struct lastmod_schema_t, lms_ad_lastmodType) },
100         { NULL }
101 };
102
103 static const struct berval *write_exop[] = {
104         &slap_EXOP_MODIFY_PASSWD,
105         NULL
106 };
107
108 static int
109 lastmod_search( Operation *op, SlapReply *rs )
110 {
111         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
112         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
113
114 #if 0
115         /* FIXME: can't restore the correct bd_info otherwise 
116          * backend_operational() screws things up */
117         op->o_bd->bd_info = (BackendInfo *)on->on_info;
118 #endif
119         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
120         rs->sr_attrs = op->ors_attrs;
121         rs->sr_flags = 0;
122         rs->sr_entry = lmi->lmi_e;
123         rs->sr_err = send_search_entry( op, rs );
124         rs->sr_entry = NULL;
125         rs->sr_flags = 0;
126         rs->sr_attrs = NULL;
127         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
128
129         send_ldap_result( op, rs );
130
131         return 0;
132 }
133
134 static int
135 lastmod_compare( Operation *op, SlapReply *rs )
136 {
137         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
138         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
139         Attribute               *a;
140
141         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
142         if ( get_assert( op ) &&
143                 ( test_filter( op, lmi->lmi_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
144         {
145                 rs->sr_err = LDAP_ASSERTION_FAILED;
146                 goto return_results;
147         }
148
149         rs->sr_err = access_allowed( op, lmi->lmi_e, op->oq_compare.rs_ava->aa_desc,
150                 &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
151         if ( ! rs->sr_err ) {
152                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
153                 goto return_results;
154         }
155
156         rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
157
158         for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
159                 a != NULL;
160                 a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ))
161         {
162                 rs->sr_err = LDAP_COMPARE_FALSE;
163
164                 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
165                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
166                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
167                         a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
168                 {
169                         rs->sr_err = LDAP_COMPARE_TRUE;
170                         break;
171                 }
172         }
173
174 return_results:;
175         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
176         send_ldap_result( op, rs );
177
178         if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
179                 rs->sr_err = LDAP_SUCCESS;
180         }
181
182         return rs->sr_err;
183 }
184
185 static int
186 lastmod_exop( Operation *op, SlapReply *rs )
187 {
188         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
189
190         /* Temporary */
191
192         op->o_bd->bd_info = (BackendInfo *)on->on_info;
193         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
194         rs->sr_text = "not allowed within namingContext";
195         send_ldap_result( op, rs );
196         rs->sr_text = NULL;
197         
198         return -1;
199 }
200
201 static int
202 lastmod_op_func( Operation *op, SlapReply *rs )
203 {
204         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
205         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
206         unsigned                i;
207
208         if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
209                 switch ( op->o_tag ) {
210                 case LDAP_REQ_SEARCH:
211                         if ( op->ors_scope != LDAP_SCOPE_BASE ) {
212                                 goto return_referral;
213                         }
214                         /* process */
215                         return lastmod_search( op, rs );
216
217                 case LDAP_REQ_COMPARE:
218                         return lastmod_compare( op, rs );
219
220                 case LDAP_REQ_EXTENDED:
221                         /* if write, reject; otherwise process */
222                         for ( i = 0; write_exop[ i ] != NULL; i++ ) {
223                                 if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
224                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
225                                         rs->sr_text = "not allowed within namingContext";
226                                         goto return_error;
227                                 }
228                         }
229                         return lastmod_exop( op, rs );
230
231                 default:
232                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
233                         rs->sr_text = "not allowed within namingContext";
234                         goto return_error;
235                 }
236         }
237
238         if ( dnIsSuffix( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
239                 goto return_referral;
240         }
241
242         return SLAP_CB_CONTINUE;
243
244 return_referral:;
245         op->o_bd->bd_info = (BackendInfo *)on->on_info;
246         rs->sr_ref = referral_rewrite( default_referral,
247                         NULL, &op->o_req_dn, op->ors_scope );
248
249         if (!rs->sr_ref) rs->sr_ref = default_referral;
250         rs->sr_err = LDAP_REFERRAL;
251         send_ldap_result( op, rs );
252
253         if (rs->sr_ref != default_referral)
254         ber_bvarray_free( rs->sr_ref );
255         rs->sr_ref = NULL;
256
257         return -1;
258
259 return_error:;
260         op->o_bd->bd_info = (BackendInfo *)on->on_info;
261         send_ldap_result( op, rs );
262         rs->sr_text = NULL;
263
264         return -1;
265 }
266
267 static int
268 lastmod_update( Operation *op, SlapReply *rs )
269 {
270         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
271         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
272         Attribute               *a;
273         Modifications           *ml = NULL;
274         struct berval           bv_modifyTimestamp = BER_BVNULL,
275                                 bv_nmodifyTimestamp = BER_BVNULL,
276                                 bv_modifiersName = BER_BVNULL,
277                                 bv_nmodifiersName = BER_BVNULL,
278                                 bv_name = BER_BVNULL,
279                                 bv_nname = BER_BVNULL;
280         enum lastmodType_e      lmt = LASTMOD_UNKNOWN;
281         Entry                   *e = NULL;
282         int                     rc = -1;
283
284         /* FIXME: timestamp? modifier? */
285         switch ( op->o_tag ) {
286         case LDAP_REQ_ADD:
287                 lmt = LASTMOD_ADD;
288                 e = op->ora_e;
289                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
290                 if ( a != NULL ) {
291                         ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
292                         ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
293                 }
294                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
295                 if ( a != NULL ) {
296                         ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
297                         if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
298                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
299                         } else {
300                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
301                         }
302                 }
303                 ber_dupbv( &bv_name, &e->e_name );
304                 ber_dupbv( &bv_nname, &e->e_nname );
305                 break;
306
307         case LDAP_REQ_DELETE:
308         {
309                 struct tm       *tm;
310 #ifdef HAVE_GMTIME_R
311                 struct tm       tm_buf;
312 #endif
313                 char            tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
314                 time_t          currtime;
315
316                 lmt = LASTMOD_DELETE;
317
318                 /* best guess */
319                 currtime = slap_get_time();
320
321 #ifndef HAVE_GMTIME_R
322                 ldap_pvt_thread_mutex_lock( &gmtime_mutex );
323                 tm = gmtime( &currtime );
324 #else /* HAVE_GMTIME_R */
325                 tm = gmtime_r( &currtime, &tm_buf );
326 #endif /* HAVE_GMTIME_R */
327                 lutil_gentime( tmbuf, sizeof( tmbuf ), tm );
328 #ifndef HAVE_GMTIME_R
329                 ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
330 #endif
331
332                 ber_str2bv( tmbuf, 0, 1, &bv_modifyTimestamp );
333                 ber_dupbv( &bv_nmodifyTimestamp, &bv_modifyTimestamp );
334
335                 /* best guess */
336                 ber_dupbv( &bv_modifiersName, &op->o_dn );
337                 ber_dupbv( &bv_nmodifiersName, &op->o_ndn );
338
339                 ber_dupbv( &bv_name, &op->o_req_dn );
340                 ber_dupbv( &bv_nname, &op->o_req_ndn );
341                 break;
342         }
343
344         case LDAP_REQ_MODIFY:
345                 lmt = LASTMOD_MODIFY;
346
347                 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
348                         if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) {
349                                 ber_dupbv( &bv_modifiersName, &ml->sml_values[0] );
350                                 ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] );
351
352                                 if ( !BER_BVISNULL( &bv_modifyTimestamp ) ) {
353                                         break;
354                                 }
355
356                         } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) == 0 ) {
357                                 ber_dupbv( &bv_modifyTimestamp, &ml->sml_values[0] );
358                                 if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
359                                         ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_nvalues[0] );
360                                 } else {
361                                         ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] );
362                                 }
363
364                                 if ( !BER_BVISNULL( &bv_modifiersName ) ) {
365                                         break;
366                                 }
367                         }
368                 }
369
370                 ber_dupbv( &bv_name, &op->o_req_dn );
371                 ber_dupbv( &bv_nname, &op->o_req_ndn );
372                 break;
373
374         case LDAP_REQ_MODRDN:
375                 lmt = LASTMOD_MODRDN;
376                 e = NULL;
377                 if ( on->on_info->oi_orig->bi_entry_get_rw ) {
378                         BackendInfo     *bi = op->o_bd->bd_info;
379                         struct berval   dn = BER_BVNULL;
380
381                         if ( op->orr_nnewSup && !BER_BVISNULL( op->orr_nnewSup ) ) {
382                                 build_new_dn( &dn, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
383
384                         } else {
385                                 struct berval   pdn;
386
387                                 dnParent( &op->o_req_ndn, &pdn );
388                                 build_new_dn( &dn, &pdn, &op->orr_nnewrdn, NULL );
389                         }
390
391                         op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
392                         if ( (*op->o_bd->bd_info->bi_entry_get_rw)( op, &dn, NULL, NULL, 0, &e ) == LDAP_SUCCESS ) {
393                                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
394                                 if ( a != NULL ) {
395                                         ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
396                                         ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
397                                 }
398                                 a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
399                                 if ( a != NULL ) {
400                                         ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
401                                         if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
402                                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
403                                         } else {
404                                                 ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
405                                         }
406                                 }
407
408                                 ber_dupbv( &bv_name, &e->e_name );
409                                 ber_dupbv( &bv_nname, &e->e_nname );
410
411                                 (*op->o_bd->bd_info->bi_entry_release_rw)( op, e, 0 );
412                         }
413
414                         ch_free( dn.bv_val );
415                         op->o_bd->bd_info = bi;
416                 }
417                 break;
418
419         default:
420                 return -1;
421         }
422         
423         ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
424
425 #if 0
426         fprintf( stderr, "### lastmodDN: %s %s\n", bv_name.bv_val, bv_nname.bv_val );
427 #endif
428
429         a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodDN );
430         if ( a == NULL ) {
431                 goto error_return;
432         }
433         ch_free( a->a_vals[0].bv_val );
434         a->a_vals[0] = bv_name;
435         ch_free( a->a_nvals[0].bv_val );
436         a->a_nvals[0] = bv_nname;
437
438 #if 0
439         fprintf( stderr, "### lastmodType: %s %s\n", lastmodType[ lmt ].bv_val, lastmodType[ lmt ].bv_val );
440 #endif
441
442         a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodType );
443         if ( a == NULL ) {
444                 goto error_return;
445         } 
446         ch_free( a->a_vals[0].bv_val );
447         ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
448         ch_free( a->a_nvals[0].bv_val );
449         ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
450
451 #if 0
452         fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val );
453 #endif
454
455         a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifiersName );
456         if ( a == NULL ) {
457                 goto error_return;
458         } 
459         ch_free( a->a_vals[0].bv_val );
460         a->a_vals[0] = bv_modifiersName;
461         ch_free( a->a_nvals[0].bv_val );
462         a->a_nvals[0] = bv_nmodifiersName;
463
464 #if 0
465         fprintf( stderr, "### modifyTimestamp: %s %s\n", bv_nmodifyTimestamp.bv_val, bv_modifyTimestamp.bv_val );
466 #endif
467
468         a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifyTimestamp );
469         if ( a == NULL ) {
470                 goto error_return;
471         } 
472         ch_free( a->a_vals[0].bv_val );
473         a->a_vals[0] = bv_modifyTimestamp;
474         ch_free( a->a_nvals[0].bv_val );
475         a->a_nvals[0] = bv_nmodifyTimestamp;
476
477         rc = 0;
478
479 error_return:;
480         ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
481         
482         return rc;
483 }
484
485 static int
486 lastmod_response( Operation *op, SlapReply *rs )
487 {
488         unsigned int    i;
489
490         if ( rs->sr_err != LDAP_SUCCESS ) {
491                 return SLAP_CB_CONTINUE;
492         }
493
494         switch ( op->o_tag ) {
495         case LDAP_REQ_ADD:
496         case LDAP_REQ_MODIFY:
497         case LDAP_REQ_MODRDN:
498         case LDAP_REQ_DELETE:
499                 /* FIXME: exop? */
500                 break;
501
502         case LDAP_REQ_EXTENDED:
503                 /* if write, process */
504                 for ( i = 0; write_exop[ i ] != NULL; i++ ) {
505                         if ( ber_bvcmp( write_exop[ i ], &op->oq_extended.rs_reqoid ) == 0 ) {
506                                 goto process;
507                         }
508                 }
509                 /* fall thru */
510
511         default:
512                 return SLAP_CB_CONTINUE;
513         }
514
515 process:;
516         if ( rs->sr_err == LDAP_SUCCESS ) {
517                 /* FIXME: ignore errors */
518                 (void)lastmod_update( op, rs );
519         }
520
521         return SLAP_CB_CONTINUE;
522 }
523
524 static int
525 lastmod_db_init(
526         BackendDB *be
527 )
528 {
529         slap_overinst           *on = (slap_overinst *) be->bd_info;
530         lastmod_info_t          *lmi;
531
532         if ( lastmod_schema.lms_oc_lastmod == NULL ) {
533                 int                     i;
534                 const char              *text;
535
536                 /* schema integration */
537                 for ( i = 0; mat[i].name; i++ ) {
538                         LDAPAttributeType       *at;
539                         int                     code;
540                         const char              *err;
541                         AttributeDescription    **ad;
542         
543                         at = ldap_str2attributetype( mat[i].schema, &code,
544                                 &err, LDAP_SCHEMA_ALLOW_ALL );
545                         if ( !at ) {
546 #ifdef NEW_LOGGING
547                                 LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
548                                         "in AttributeType '%s' %s before %s\n",
549                                         mat[i].name, ldap_scherr2str(code), err );
550 #else
551                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
552                                         "in AttributeType '%s' %s before %s\n",
553                                         mat[i].name, ldap_scherr2str(code), err );
554 #endif
555                                 return -1;
556                         }
557         
558                         if ( at->at_oid == NULL ) {
559 #ifdef NEW_LOGGING
560                                 LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
561                                         "null OID for attributeType '%s'\n",
562                                         mat[i].name, 0, 0 );
563 #else
564                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
565                                         "null OID for attributeType '%s'\n",
566                                         mat[i].name, 0, 0 );
567 #endif
568                                 return -1;
569                         }
570         
571                         code = at_add(at, &err);
572                         if ( code ) {
573 #ifdef NEW_LOGGING
574                                 LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
575                                         "%s in attributeType '%s'\n",
576                                         scherr2str(code), mat[i].name, 0 );
577 #else
578                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
579                                         "%s in attributeType '%s'\n",
580                                         scherr2str(code), mat[i].name, 0 );
581 #endif
582                                 return -1;
583                         }
584                         ldap_memfree(at);
585         
586                         ad = ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset]));
587                         ad[0] = NULL;
588                         if ( slap_str2ad( mat[i].name, ad, &text ) ) {
589 #ifdef NEW_LOGGING
590                                 LDAP_LOG( OPERATION, CRIT,
591                                         "lastmod_init: %s\n", text, 0, 0 );
592 #else
593                                 Debug( LDAP_DEBUG_ANY,
594                                         "lastmod_init: %s\n", text, 0, 0 );
595 #endif
596                                 return -1;
597                         }
598         
599                         (*ad)->ad_type->sat_flags |= mat[i].flags;
600                 }
601
602                 for ( i = 0; moc[i].name; i++ ) {
603                         LDAPObjectClass         *oc;
604                         int                     code;
605                         const char              *err;
606                         ObjectClass             *Oc;
607         
608                         oc = ldap_str2objectclass(moc[i].schema, &code, &err,
609                                         LDAP_SCHEMA_ALLOW_ALL );
610                         if ( !oc ) {
611 #ifdef NEW_LOGGING
612                                 LDAP_LOG( OPERATION, CRIT,
613                                         "unable to parse lastmod objectClass '%s': "
614                                         "%s before %s\n" , moc[i].name,
615                                         ldap_scherr2str(code), err );
616 #else
617                                 Debug( LDAP_DEBUG_ANY,
618                                         "unable to parse lastmod objectClass '%s': "
619                                         "%s before %s\n" , moc[i].name,
620                                         ldap_scherr2str(code), err );
621 #endif
622                                 return -1;
623                         }
624
625                         if ( oc->oc_oid == NULL ) {
626 #ifdef NEW_LOGGING
627                                 LDAP_LOG( OPERATION, CRIT,
628                                         "objectClass '%s' has no OID\n" ,
629                                         moc[i].name, 0, 0 );
630 #else
631                                 Debug( LDAP_DEBUG_ANY,
632                                         "objectClass '%s' has no OID\n" ,
633                                         moc[i].name, 0, 0 );
634 #endif
635                                 return -1;
636                         }
637
638                         code = oc_add(oc, 0, &err);
639                         if ( code ) {
640 #ifdef NEW_LOGGING
641                                 LDAP_LOG( OPERATION, CRIT,
642                                         "objectClass '%s': %s \"%s\"\n" ,
643                                         moc[i].name, scherr2str(code), err );
644 #else
645                                 Debug( LDAP_DEBUG_ANY,
646                                         "objectClass '%s': %s \"%s\"\n" ,
647                                         moc[i].name, scherr2str(code), err );
648 #endif
649                                 return -1;
650                         }
651         
652                         ldap_memfree(oc);
653         
654                         Oc = oc_find( moc[i].name );
655                         if ( Oc == NULL ) {
656 #ifdef NEW_LOGGING
657                                 LDAP_LOG( OPERATION, CRIT, "lastmod_init: "
658                                                 "unable to find objectClass %s "
659                                                 "(just added)\n", moc[i].name, 0, 0 );
660 #else
661                                 Debug( LDAP_DEBUG_ANY, "lastmod_init: "
662                                                 "unable to find objectClass %s "
663                                                 "(just added)\n", moc[i].name, 0, 0 );
664 #endif
665                                 return -1;
666                         }
667
668                         Oc->soc_flags |= moc[i].flags;
669
670                         ((ObjectClass **)&(((char *)&lastmod_schema)[moc[i].offset]))[0] = Oc;
671                 }
672         }
673
674         lmi = (lastmod_info_t *)ch_malloc(sizeof(lastmod_info_t));
675         memset(lmi, 0, sizeof(lastmod_info_t));
676         on->on_bi.bi_private = lmi;
677
678         return 0;
679 }
680
681 static int
682 lastmod_db_config(
683     BackendDB   *be,
684     const char  *fname,
685     int         lineno,
686     int         argc,
687     char        **argv
688 )
689 {
690         slap_overinst           *on = (slap_overinst *) be->bd_info;
691         lastmod_info_t          *lmi = (lastmod_info_t *)on->on_bi.bi_private;
692
693         if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) {
694                 if ( lmi->lmi_rdnvalue.bv_val ) {
695                         /* already defined! */
696                         ch_free( lmi->lmi_rdnvalue.bv_val );
697                 }
698
699                 ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue );
700
701         } else {
702                 return SLAP_CONF_UNKNOWN;
703         }
704
705         return 0;
706 }
707
708 static int
709 lastmod_db_open(
710         BackendDB *be
711 )
712 {
713         slap_overinst   *on = (slap_overinst *) be->bd_info;
714         lastmod_info_t  *lmi = (lastmod_info_t *)on->on_bi.bi_private;
715         char            buf[ 8192 ];
716         struct tm               *tms;
717 #ifdef HAVE_GMTIME_R
718         struct tm               tm_buf;
719 #endif
720         static char             tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
721
722         if ( !SLAP_LASTMOD( be ) ) {
723                 fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" );
724                 return -1;
725         }
726
727         /*
728          * Start
729          */
730 #ifndef HAVE_GMTIME_R
731         ldap_pvt_thread_mutex_lock( &gmtime_mutex );
732         tms = gmtime( &starttime );
733 #else /* HAVE_GMTIME_R */
734         tms = gmtime_r( &starttime, &tm_buf );
735 #endif /* HAVE_GMTIME_R */
736         lutil_gentime( tmbuf, sizeof(tmbuf), tms );
737 #ifndef HAVE_GMTIME_R
738         ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
739 #endif
740
741         if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
742                 ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue );
743         }
744
745         snprintf( buf, sizeof( buf ),
746                         "dn: cn=%s,%s\n"
747                         "objectClass: %s\n"
748                         "structuralObjectClass: %s\n"
749                         "cn: %s\n"
750                         "description: This object contains the last modification to this database\n"
751                         "%s: cn=%s,%s\n"
752                         "%s: %s\n"
753                         "createTimestamp: %s\n"
754                         "creatorsName: %s\n"
755                         "modifyTimestamp: %s\n"
756                         "modifiersName: %s\n"
757                         "hasSubordinates: FALSE\n",
758                         lmi->lmi_rdnvalue.bv_val, be->be_suffix[0].bv_val,
759                         lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
760                         lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
761                         lmi->lmi_rdnvalue.bv_val,
762                         lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val, lmi->lmi_rdnvalue.bv_val, be->be_suffix[0].bv_val,
763                         lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val,
764                         tmbuf,
765                         BER_BVISNULL( &be->be_rootdn ) ? "" : be->be_rootdn.bv_val,
766                         tmbuf,
767                         BER_BVISNULL( &be->be_rootdn ) ? "" : be->be_rootdn.bv_val );
768
769 #if 0
770         fprintf( stderr, "# entry:\n%s\n", buf );
771         fflush( stderr );
772 #endif
773
774         lmi->lmi_e = str2entry( buf );
775         if ( lmi->lmi_e == NULL ) {
776                 return -1;
777         }
778
779         ldap_pvt_thread_mutex_init( &lmi->lmi_entry_mutex );
780
781         return 0;
782 }
783
784 static int
785 lastmod_db_destroy(
786         BackendDB *be
787 )
788 {
789         slap_overinst   *on = (slap_overinst *) be->bd_info;
790         lastmod_info_t  *lmi = (lastmod_info_t *)on->on_bi.bi_private;
791
792         if ( lmi ) {
793                 if ( !BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
794                         ch_free( lmi->lmi_rdnvalue.bv_val );
795                 }
796
797                 if ( lmi->lmi_e ) {
798                         entry_free( lmi->lmi_e );
799
800                         ldap_pvt_thread_mutex_destroy( &lmi->lmi_entry_mutex );
801                 }
802
803                 ch_free( lmi );
804         }
805
806         return 0;
807 }
808
809 /* This overlay is set up for dynamic loading via moduleload. For static
810  * configuration, you'll need to arrange for the slap_overinst to be
811  * initialized and registered by some other function inside slapd.
812  */
813
814 static slap_overinst            lastmod;
815
816 int
817 lastmod_init()
818 {
819         memset( &lastmod, 0, sizeof( slap_overinst ) );
820         lastmod.on_bi.bi_type = "lastmod";
821         lastmod.on_bi.bi_db_init = lastmod_db_init;
822         lastmod.on_bi.bi_db_config = lastmod_db_config;
823         lastmod.on_bi.bi_db_destroy = lastmod_db_destroy;
824         lastmod.on_bi.bi_db_open = lastmod_db_open;
825
826         lastmod.on_bi.bi_op_add = lastmod_op_func;
827         lastmod.on_bi.bi_op_compare = lastmod_op_func;
828         lastmod.on_bi.bi_op_delete = lastmod_op_func;
829         lastmod.on_bi.bi_op_modify = lastmod_op_func;
830         lastmod.on_bi.bi_op_modrdn = lastmod_op_func;
831         lastmod.on_bi.bi_op_search = lastmod_op_func;
832         lastmod.on_bi.bi_extended = lastmod_op_func;
833
834         lastmod.on_response = lastmod_response;
835
836         return overlay_register( &lastmod );
837 }
838
839 #if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC
840 int
841 init_module(int argc, char *argv[])
842 {
843         return lastmod_init();
844 }
845 #endif /* SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC */
846
847 #endif /* defined(SLAPD_OVER_LASTMOD) */