Add the magic 'TO' header.
Checks both To: and Cc: as an OR, without needing to specify both in separate rules. FossilOrigin-Name: e8cde0ba2d9b19089d80aeea455d6d6024cda477e206f567a379fd1b1d596815
This commit is contained in:
parent
2952e2cbd7
commit
f0221aac0a
4 changed files with 57 additions and 37 deletions
17
config.yml
17
config.yml
|
|
@ -3,13 +3,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# Default, no logging. Relative to homedir.
|
# Default, no logging. Relative to homedir.
|
||||||
#logfile: sieb.log
|
logfile: sieb.log
|
||||||
|
|
||||||
## Rules tried before global filtering
|
## Rules tried before global filtering
|
||||||
early_rules:
|
early_rules:
|
||||||
-
|
-
|
||||||
headers:
|
headers:
|
||||||
to: mahlon@(laika|ravn)
|
TO: mahlon@(laika|ravn)
|
||||||
received: .*sendgrid.*
|
received: .*sendgrid.*
|
||||||
filter:
|
filter:
|
||||||
- [ reformail, -A, "X-Sieb: This matched." ]
|
- [ reformail, -A, "X-Sieb: This matched." ]
|
||||||
|
|
@ -17,7 +17,7 @@ early_rules:
|
||||||
|
|
||||||
## Filter message before additional rules.
|
## Filter message before additional rules.
|
||||||
filter:
|
filter:
|
||||||
- [ reformail, -A, "X-Sieb: That shit totally matched" ]
|
- [ reformail, -A, "X-Sieb: Processed!" ]
|
||||||
|
|
||||||
## Ordered, top down, first match wins.
|
## Ordered, top down, first match wins.
|
||||||
## Headers are lowercased. Multiple matches are AND'ed.
|
## Headers are lowercased. Multiple matches are AND'ed.
|
||||||
|
|
@ -25,12 +25,11 @@ filter:
|
||||||
## Delivery default is ~/Maildir, any set value is an auto-created maildir under
|
## Delivery default is ~/Maildir, any set value is an auto-created maildir under
|
||||||
## that path.
|
## that path.
|
||||||
##
|
##
|
||||||
# rules:
|
rules:
|
||||||
# -
|
-
|
||||||
# headers:
|
headers:
|
||||||
# x-what: pcre-matcher
|
x-sieb: global
|
||||||
# poonie: pcre-matcher
|
deliver: .whoas
|
||||||
# deliver: .whatever
|
|
||||||
|
|
||||||
# # Magic "TO" which means To: OR Cc:
|
# # Magic "TO" which means To: OR Cc:
|
||||||
# -
|
# -
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ const CONFFILES = @[
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
### FIXME: generate default config?
|
||||||
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
# T Y P E S
|
# T Y P E S
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ proc newMessage*( dir: Maildir ): Message =
|
||||||
result.path = joinPath( result.dir.tmp, result.basename )
|
result.path = joinPath( result.dir.tmp, result.basename )
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug "Opening new message at {result.path}".fmt
|
debug "Opening new message at:\n {result.path}".fmt
|
||||||
result.stream = openFileStream( result.path, fmWrite )
|
result.stream = openFileStream( result.path, fmWrite )
|
||||||
result.path.setFilePermissions( OWNERFILEPERMS )
|
result.path.setFilePermissions( OWNERFILEPERMS )
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
|
|
@ -130,8 +130,8 @@ proc save*( msg: Message, dir=msg.dir ) =
|
||||||
## maildir, but can be provided a different one.
|
## maildir, but can be provided a different one.
|
||||||
msg.stream.close
|
msg.stream.close
|
||||||
let newpath = joinPath( dir.new, msg.basename )
|
let newpath = joinPath( dir.new, msg.basename )
|
||||||
debug "Delivering message to {newpath}".fmt
|
|
||||||
msg.path.moveFile( newpath )
|
msg.path.moveFile( newpath )
|
||||||
|
debug "Delivered message to:\n {newpath}".fmt
|
||||||
msg.dir = dir
|
msg.dir = dir
|
||||||
msg.path = newpath
|
msg.path = newpath
|
||||||
|
|
||||||
|
|
@ -139,8 +139,8 @@ proc save*( msg: Message, dir=msg.dir ) =
|
||||||
proc delete*( msg: Message ) =
|
proc delete*( msg: Message ) =
|
||||||
## Remove a message from disk.
|
## Remove a message from disk.
|
||||||
msg.stream.close
|
msg.stream.close
|
||||||
debug "Removing message at {msg.path}".fmt
|
|
||||||
msg.path.removeFile
|
msg.path.removeFile
|
||||||
|
debug "Removed message at:\n {msg.path}".fmt
|
||||||
msg.path = ""
|
msg.path = ""
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,7 +157,7 @@ proc writeStdin*( msg: Message ): Message =
|
||||||
msg.stream.write( buf )
|
msg.stream.write( buf )
|
||||||
msg.stream.flush
|
msg.stream.flush
|
||||||
msg.stream.close
|
msg.stream.close
|
||||||
debug "Wrote {total} bytes from stdin".fmt
|
debug "Wrote {total} bytes".fmt
|
||||||
|
|
||||||
|
|
||||||
proc filter*( orig_msg: Message, cmd: seq[string] ): Message =
|
proc filter*( orig_msg: Message, cmd: seq[string] ): Message =
|
||||||
|
|
@ -249,26 +249,44 @@ proc parseHeaders*( msg: Message ) =
|
||||||
( header, value ) = ( matches[0].toLower, matches[1] )
|
( header, value ) = ( matches[0].toLower, matches[1] )
|
||||||
|
|
||||||
|
|
||||||
# FIXME: magic TO
|
proc evalRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool =
|
||||||
proc walkRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool =
|
|
||||||
## Evaluate each rule against the Message, returning true
|
## Evaluate each rule against the Message, returning true
|
||||||
## if there was a valid match found.
|
## if there was a valid match found.
|
||||||
msg.parseHeaders
|
|
||||||
result = false
|
result = false
|
||||||
|
msg.parseHeaders
|
||||||
|
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
var match = false
|
var match = false
|
||||||
|
|
||||||
|
if rule.headers.len > 0: debug "Evaluating rule..."
|
||||||
block thisRule:
|
block thisRule:
|
||||||
for header, regexp in rule.headers:
|
for header, regexp in rule.headers:
|
||||||
let header_chk = header.toLower
|
let header_chk = header.toLower
|
||||||
var hmatch = false
|
var
|
||||||
|
hmatch = false
|
||||||
|
headerlbl = header_chk
|
||||||
|
recipients: seq[ string ]
|
||||||
|
values: seq[ string ]
|
||||||
|
|
||||||
debug " checking header \"{header}\"".fmt
|
# TO checks both To: and Cc: simultaneously.
|
||||||
if msg.headers.hasKey( header_chk ):
|
#
|
||||||
for val in msg.headers[ header_chk ]:
|
if header == "TO":
|
||||||
|
headerlbl = "to|cc"
|
||||||
|
recipients = msg.headers.getOrDefault( "to", @[] ) &
|
||||||
|
msg.headers.getOrDefault( "cc", @[] )
|
||||||
|
|
||||||
|
debug " checking header \"{headerlbl}\"".fmt
|
||||||
|
if recipients.len > 0:
|
||||||
|
values = recipients
|
||||||
|
elif msg.headers.hasKey( header_chk ):
|
||||||
|
values = msg.headers[ header_chk ]
|
||||||
|
else:
|
||||||
|
debug " nonexistent header, skipping others"
|
||||||
|
break thisRule
|
||||||
|
|
||||||
|
for val in values:
|
||||||
try:
|
try:
|
||||||
hmatch = val.match( regexp.re )
|
hmatch = val.match( regexp.re({reStudy,reIgnoreCase}) )
|
||||||
if hmatch:
|
if hmatch:
|
||||||
debug " match on \"{regexp}\"".fmt
|
debug " match on \"{regexp}\"".fmt
|
||||||
break # a single multi-header is sufficient
|
break # a single multi-header is sufficient
|
||||||
|
|
@ -280,11 +298,7 @@ proc walkRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool =
|
||||||
if hmatch:
|
if hmatch:
|
||||||
match = true
|
match = true
|
||||||
else:
|
else:
|
||||||
debug " no match, skipping others"
|
debug " no match for \"{regexp}\", skipping others".fmt
|
||||||
break thisRule
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug " nonexistent header, skipping others"
|
|
||||||
break thisRule
|
break thisRule
|
||||||
|
|
||||||
result = match
|
result = match
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ import
|
||||||
lib/util
|
lib/util
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: logfile
|
||||||
|
# TODO: timer/performance
|
||||||
|
# TODO: more performant debug
|
||||||
|
|
||||||
# Without this, we got nuthin'!
|
# Without this, we got nuthin'!
|
||||||
if not existsEnv( "HOME" ):
|
if not existsEnv( "HOME" ):
|
||||||
deferral "Unable to determine HOME from environment."
|
deferral "Unable to determine HOME from environment."
|
||||||
|
|
@ -25,14 +29,14 @@ var msg = default.newMessage.writeStdin
|
||||||
|
|
||||||
# If there are "early rules", parse the message now and walk those.
|
# If there are "early rules", parse the message now and walk those.
|
||||||
if conf.early_rules.len > 0:
|
if conf.early_rules.len > 0:
|
||||||
if msg.walkRules( conf.early_rules, default ): quit( 0 )
|
if msg.evalRules( conf.early_rules, default ): quit( 0 )
|
||||||
|
|
||||||
# Apply any configured global filtering.
|
# Apply any configured global filtering.
|
||||||
for filter in conf.filter: msg = msg.filter( filter )
|
for filter in conf.filter: msg = msg.filter( filter )
|
||||||
|
|
||||||
# Walk the rules, and if nothing hits, deliver to fallthrough.
|
# Walk the rules, and if nothing hits, deliver to fallthrough.
|
||||||
if conf.rules.len > 0:
|
if conf.rules.len > 0:
|
||||||
if not msg.walkRules( conf.rules, default ): msg.save
|
if not msg.evalRules( conf.rules, default ): msg.save
|
||||||
else:
|
else:
|
||||||
msg.save
|
msg.save
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue