@@ -20,6 +20,7 @@ use trc::AddContext;
20
20
use utils:: {
21
21
config:: { Config , ConfigKey } ,
22
22
glob:: GlobPattern ,
23
+ Semver ,
23
24
} ;
24
25
25
26
#[ derive( Default ) ]
@@ -50,9 +51,8 @@ pub enum MatchType {
50
51
All ,
51
52
}
52
53
53
- pub ( crate ) struct ExternalConfig {
54
- pub id : String ,
55
- pub version : String ,
54
+ pub ( crate ) struct ExternalSpamRules {
55
+ pub version : Semver ,
56
56
pub keys : Vec < ConfigKey > ,
57
57
}
58
58
@@ -329,89 +329,91 @@ impl ConfigManager {
329
329
} )
330
330
}
331
331
332
- pub async fn update_config_resource (
333
- & self ,
334
- resource_id : & str ,
335
- overwrite : bool ,
336
- ) -> trc :: Result < Option < String > > {
337
- let external = self
338
- . fetch_config_resource ( resource_id )
339
- . await
340
- . map_err ( |reason | {
341
- trc :: EventType :: Config ( trc :: ConfigEvent :: FetchError )
342
- . caused_by ( trc :: location! ( ) )
343
- . details ( "Failed to fetch external configuration" )
344
- . ctx ( trc :: Key :: Reason , reason )
345
- } ) ? ;
346
-
347
- if self
348
- . get ( & external . id )
349
- . await ?
350
- . map_or ( true , |v| v != external . version )
351
- {
332
+ pub async fn update_spam_rules ( & self , overwrite : bool ) -> trc :: Result < Option < Semver > > {
333
+ let external = self . fetch_spam_rules ( ) . await . map_err ( |reason| {
334
+ trc :: EventType :: Config ( trc :: ConfigEvent :: FetchError )
335
+ . caused_by ( trc :: location! ( ) )
336
+ . details ( "Failed to update spam filter rules" )
337
+ . ctx ( trc :: Key :: Reason , reason )
338
+ } ) ? ;
339
+
340
+ if self . get ( "version.spam-filter" ) . await ? . map_or ( true , |v | {
341
+ v . as_str ( ) . try_into ( ) . map_or ( true , |v| external . version > v )
342
+ } ) {
343
+ // Delete previous STWT_* rules
344
+ for prefix in [
345
+ "spam-filter.rule.stwt_" ,
346
+ "spam-filter.dnsbl.server.stwt_" ,
347
+ "http-lookup.stwt_" ,
348
+ ] {
349
+ self . clear_prefix ( prefix ) . await ?;
350
+ }
351
+
352
352
self . set ( external. keys , overwrite) . await ?;
353
353
354
354
trc:: event!(
355
355
Config ( trc:: ConfigEvent :: ImportExternal ) ,
356
- Version = external. version. clone ( ) ,
357
- Id = resource_id . to_string ( ) ,
356
+ Version = external. version. to_string ( ) ,
357
+ Id = "spam-filter" ,
358
358
) ;
359
359
360
360
Ok ( Some ( external. version ) )
361
361
} else {
362
362
trc:: event!(
363
363
Config ( trc:: ConfigEvent :: AlreadyUpToDate ) ,
364
- Version = external. version,
365
- Id = resource_id . to_string ( ) ,
364
+ Version = external. version. to_string ( ) ,
365
+ Id = "spam-filter" ,
366
366
) ;
367
367
368
368
Ok ( None )
369
369
}
370
370
}
371
371
372
- pub ( crate ) async fn fetch_config_resource (
373
- & self ,
374
- resource_id : & str ,
375
- ) -> Result < ExternalConfig , String > {
376
- let config = String :: from_utf8 ( self . fetch_resource ( resource_id) . await ?)
372
+ pub ( crate ) async fn fetch_spam_rules ( & self ) -> Result < ExternalSpamRules , String > {
373
+ let config = String :: from_utf8 ( self . fetch_resource ( "spam-filter" ) . await ?)
377
374
. map_err ( |err| format ! ( "Configuration file has invalid UTF-8: {err}" ) ) ?;
378
375
let config = Config :: new ( config)
379
376
. map_err ( |err| format ! ( "Failed to parse external configuration: {err}" ) ) ?;
380
377
381
378
// Import configuration
382
- let mut external = ExternalConfig {
383
- id : String :: new ( ) ,
384
- version : String :: new ( ) ,
379
+ let mut external = ExternalSpamRules {
380
+ version : Semver :: default ( ) ,
385
381
keys : Vec :: new ( ) ,
386
382
} ;
383
+ let mut required_semver = Semver :: default ( ) ;
384
+ let server_semver: Semver = env ! ( "CARGO_PKG_VERSION" ) . try_into ( ) . unwrap ( ) ;
387
385
for ( key, value) in config. keys {
388
- if key. starts_with ( "version." ) {
389
- external. id . clone_from ( & key) ;
390
- external. version . clone_from ( & value) ;
386
+ if key == "version.spam-filter" {
387
+ external. version = value. as_str ( ) . try_into ( ) . unwrap_or_default ( ) ;
391
388
external. keys . push ( ConfigKey :: from ( ( key, value) ) ) ;
389
+ } else if key == "version.server" {
390
+ required_semver = value. as_str ( ) . try_into ( ) . unwrap_or_default ( ) ;
392
391
} else if key. starts_with ( "spam-filter." )
393
392
|| key. starts_with ( "http-lookup." )
394
393
|| ( key. starts_with ( "lookup." ) && !key. starts_with ( "lookup.default." ) )
395
394
|| key. starts_with ( "server.asn." )
396
- || key. starts_with ( "queue.quota." )
397
- || key. starts_with ( "queue.throttle." )
398
- || key. starts_with ( "session.throttle." )
399
395
{
400
396
external. keys . push ( ConfigKey :: from ( ( key, value) ) ) ;
401
397
} else {
402
398
trc:: event!(
403
399
Config ( trc:: ConfigEvent :: ExternalKeyIgnored ) ,
404
400
Key = key,
405
401
Value = value,
406
- Id = resource_id . to_string ( ) ,
402
+ Id = "spam-filter" ,
407
403
) ;
408
404
}
409
405
}
410
406
411
- if !external. version . is_empty ( ) {
407
+ if !required_semver. is_valid ( ) {
408
+ Err ( "External spam filter rules do not contain a valid server version" . to_string ( ) )
409
+ } else if required_semver > server_semver {
410
+ Err ( format ! (
411
+ "External spam filter rules require server version {required_semver}, but this is version {server_semver}" ,
412
+ ) )
413
+ } else if external. version . is_valid ( ) {
412
414
Ok ( external)
413
415
} else {
414
- Err ( "External configuration file does not contain a version key" . to_string ( ) )
416
+ Err ( "External spam filter rules do not contain a version key" . to_string ( ) )
415
417
}
416
418
}
417
419
0 commit comments