Checkpoint.

- Use an optional logger (global) tied into debug()
- Defer string interpolation for logging until we know we need it

FossilOrigin-Name: 0b3e7b516a0fed2cd11fd49a5ce76fde2db36bffa30914b63f9940a4d79bb5f8
This commit is contained in:
Mahlon E. Smith 2023-06-23 16:27:37 +00:00
parent f0221aac0a
commit d63a73a22a
6 changed files with 48 additions and 27 deletions

View file

@ -11,7 +11,7 @@ dependencies:
development: ${FILES} development: ${FILES}
# can use gdb with this... # can use gdb with this...
nim --debugInfo --assertions:on --linedir:on -d:testing -d:nimTypeNames --nimcache:.cache c ${FILES} nim --verbosity:2 --debugInfo --assertions:on --linedir:on -d:debug -d:nimTypeNames --nimcache:.cache c ${FILES}
mv src/sieb . mv src/sieb .
debugger: ${FILES} debugger: ${FILES}

View file

@ -9,7 +9,7 @@ logfile: sieb.log
early_rules: early_rules:
- -
headers: headers:
TO: mahlon@(laika|ravn) TO: mhlon@(laika|ravn)
received: .*sendgrid.* received: .*sendgrid.*
filter: filter:
- [ reformail, -A, "X-Sieb: This matched." ] - [ reformail, -A, "X-Sieb: This matched." ]

View file

@ -29,9 +29,6 @@ const CONFFILES = @[
] ]
### FIXME: generate default config?
############################################################# #############################################################
# T Y P E S # T Y P E S
############################################################# #############################################################

View file

@ -79,7 +79,7 @@ proc newMaildir*( path: string ): Maildir =
result.tmp = joinPath( path, "tmp" ) result.tmp = joinPath( path, "tmp" )
if not dirExists( path ): if not dirExists( path ):
debug "Creating new maildir at {path}".fmt "Creating new maildir at $#".debug( path )
try: try:
for p in [ result.path, result.cur, result.new, result.tmp ]: for p in [ result.path, result.cur, result.new, result.tmp ]:
p.createDir p.createDir
@ -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:\n {result.path}".fmt "Opening new message at:\n $#".debug( result.path )
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:
@ -131,7 +131,7 @@ proc save*( msg: Message, dir=msg.dir ) =
msg.stream.close msg.stream.close
let newpath = joinPath( dir.new, msg.basename ) let newpath = joinPath( dir.new, msg.basename )
msg.path.moveFile( newpath ) msg.path.moveFile( newpath )
debug "Delivered message to:\n {newpath}".fmt "Delivered message to:\n $#".debug( newpath )
msg.dir = dir msg.dir = dir
msg.path = newpath msg.path = newpath
@ -140,7 +140,7 @@ proc delete*( msg: Message ) =
## Remove a message from disk. ## Remove a message from disk.
msg.stream.close msg.stream.close
msg.path.removeFile msg.path.removeFile
debug "Removed message at:\n {msg.path}".fmt "Removed message at:\n $#".debug( msg.path )
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".fmt "Wrote $# bytes".debug( total )
proc filter*( orig_msg: Message, cmd: seq[string] ): Message = proc filter*( orig_msg: Message, cmd: seq[string] ): Message =
@ -172,7 +172,7 @@ proc filter*( orig_msg: Message, cmd: seq[string] ): Message =
options = FILTERPROCOPTS options = FILTERPROCOPTS
) )
debug "Running filter: {cmd}".fmt "Running filter: $#".debug( cmd )
# let process = cmd.startProcess( options = FILTERPROCOPTS ) # let process = cmd.startProcess( options = FILTERPROCOPTS )
# Read from the original message, write to the filter # Read from the original message, write to the filter
@ -195,24 +195,24 @@ proc filter*( orig_msg: Message, cmd: seq[string] ): Message =
new_msg.stream.flush new_msg.stream.flush
let exitcode = process.waitForExit let exitcode = process.waitForExit
debug "Filter exited: {exitcode}".fmt "Filter exited: $#".debug( exitcode )
process.close process.close
if exitcode == 0: if exitcode == 0:
new_msg.stream.close new_msg.stream.close
orig_msg.delete orig_msg.delete
result = new_msg result = new_msg
else: else:
debug "Unable to filter message: non-zero exit code".fmt "Unable to filter message: non-zero exit code".debug
except OSError as err: except OSError as err:
debug "Unable to filter message: {err.msg}".fmt "Unable to filter message: $#".debug( err.msg )
proc parseHeaders*( msg: Message ) = proc parseHeaders*( msg: Message ) =
## Walk the RFC2822 headers, placing them into memory. ## Walk the RFC2822 headers, placing them into memory.
## This 'unwraps' multiline headers, and allows for duplicate headers. ## This 'unwraps' multiline headers, and allows for duplicate headers.
debug "Parsing message headers." let preparsed = not msg.headers.isNil
msg.headers = initTable[ string, seq[string] ]() msg.headers = initTable[ string, seq[string] ]()
msg.open msg.open
@ -248,6 +248,10 @@ proc parseHeaders*( msg: Message ) =
msg.headers[ header ] = @[ value ] msg.headers[ header ] = @[ value ]
( header, value ) = ( matches[0].toLower, matches[1] ) ( header, value ) = ( matches[0].toLower, matches[1] )
"Parsed message headers.".debug
if msg.headers.hasKey( "message-id" ):
"Message-ID is \"$#\"".debug( msg.headers[ "message-id" ] )
proc evalRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool = proc evalRules*( 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
@ -258,7 +262,7 @@ proc evalRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool =
for rule in rules: for rule in rules:
var match = false var match = false
if rule.headers.len > 0: debug "Evaluating rule..." if rule.headers.len > 0: "Evaluating rule...".debug
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
@ -275,37 +279,38 @@ proc evalRules*( msg: var Message, rules: seq[Rule], default: Maildir ): bool =
recipients = msg.headers.getOrDefault( "to", @[] ) & recipients = msg.headers.getOrDefault( "to", @[] ) &
msg.headers.getOrDefault( "cc", @[] ) msg.headers.getOrDefault( "cc", @[] )
debug " checking header \"{headerlbl}\"".fmt " checking header \"$#\"".debug( headerlbl )
if recipients.len > 0: if recipients.len > 0:
values = recipients values = recipients
elif msg.headers.hasKey( header_chk ): elif msg.headers.hasKey( header_chk ):
values = msg.headers[ header_chk ] values = msg.headers[ header_chk ]
else: else:
debug " nonexistent header, skipping others" " nonexistent header, skipping others".debug
break thisRule break thisRule
for val in values: for val in values:
try: try:
hmatch = val.match( regexp.re({reStudy,reIgnoreCase}) ) hmatch = val.match( regexp.re({reStudy,reIgnoreCase}) )
if hmatch: if hmatch:
debug " match on \"{regexp}\"".fmt " match on \"$#\"".debug( regexp )
break # a single multi-header is sufficient break # a single multi-header is sufficient
except RegexError as err: except RegexError as err:
debug " invalid regexp \"{regexp}\" ({err.msg}), skipping".fmt.replace( "\n", " " ) let errmsg = err.msg.replace( "\n", " " )
" invalid regexp \"$#\" ($#), skipping".debug( regexp, errmsg )
break thisRule break thisRule
# Did any of the (possibly) multi-header values match? # Did any of the (possibly) multi-header values match?
if hmatch: if hmatch:
match = true match = true
else: else:
debug " no match for \"{regexp}\", skipping others".fmt " no match for \"$#\", skipping others".debug( regexp )
break thisRule break thisRule
result = match result = match
if result: if result:
debug "Rule match!" "Rule match!".debug
for filter in rule.filter: msg = msg.filter( filter ) for filter in rule.filter: msg = msg.filter( filter )
var deliver: Maildir var deliver: Maildir

View file

@ -12,6 +12,9 @@ import
std/terminal, std/terminal,
std/strutils std/strutils
import
logging
############################################################# #############################################################
# C O N S T A N T S # C O N S T A N T S
@ -68,9 +71,13 @@ proc deferral*( msg: string ) =
quit( 1 ) quit( 1 )
proc debug*( msg: string ) = proc debug*( msg: string, args: varargs[string, `$`] ) =
## Emit +msg+ if debug mode is enabled. ## Emit +msg+ if debug mode is enabled, coercing arguments into a string for
if defined( testing ): echo msg ## formatting.
if defined( debug ) or not logger.closed:
var str = msg % args
if defined( debug ): echo str
if not logger.closed: str.log
proc parse_cmdline*: Opts = proc parse_cmdline*: Opts =
@ -84,7 +91,7 @@ proc parse_cmdline*: Opts =
) )
# always set debug mode if development build. # always set debug mode if development build.
result.debug = defined( testing ) result.debug = defined( debug )
for kind, key, val in getopt(): for kind, key, val in getopt():
case kind case kind

View file

@ -5,13 +5,18 @@ import
import import
lib/config, lib/config,
lib/logging,
lib/message, lib/message,
lib/util lib/util
# /home/mahlon/repo/sieb/src/sieb.nim(30) sieb
# /home/mahlon/.choosenim/toolchains/nim-1.6.10/lib/system/io.nim(759) open
# Error: unhandled exception: cannot open: /home/mahlon/ [IOError]
# TODO: logfile
# TODO: timer/performance # TODO: timer/performance
# TODO: more performant debug # TODO: more performant debug
# TODO: generate default config?
# Without this, we got nuthin'! # Without this, we got nuthin'!
if not existsEnv( "HOME" ): if not existsEnv( "HOME" ):
@ -23,6 +28,13 @@ let
conf = get_config( opts.config ) conf = get_config( opts.config )
default = newMaildir( joinPath( home, "Maildir" ) ) default = newMaildir( joinPath( home, "Maildir" ) )
if conf.logfile != "":
createLogger( conf.logfile )
# FIXME: at exit?
# ... if logger not nil logger close
# Create a new message under Maildir/tmp, and stream stdin to it. # Create a new message under Maildir/tmp, and stream stdin to it.
var msg = default.newMessage.writeStdin var msg = default.newMessage.writeStdin